mirror of
https://github.com/Stremio/stremio-shell-ng.git
synced 2026-03-11 17:15:49 +00:00
Tray icon; win visibility WIP
This commit is contained in:
parent
e6cb1d8fa6
commit
381f6c4a8d
6 changed files with 125 additions and 11 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -500,6 +500,7 @@ dependencies = [
|
||||||
name = "stremio-shell-ng"
|
name = "stremio-shell-ng"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
"embed-resource",
|
"embed-resource",
|
||||||
"mpv",
|
"mpv",
|
||||||
"native-windows-derive",
|
"native-windows-derive",
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
once_cell = "1.3.1"
|
once_cell = "1.3.1"
|
||||||
native-windows-gui = { version = "1.0.4", features = ["high-dpi", "notice"] }
|
native-windows-gui = { version = "1.0.4", features = ["high-dpi", "notice", "tray-notification", "menu"] }
|
||||||
native-windows-derive = "1.0.3"
|
native-windows-derive = "1.0.3"
|
||||||
winapi = { version = "0.3.9", features = [
|
winapi = { version = "0.3.9", features = [
|
||||||
"libloaderapi", "handleapi", "wincon", "winuser"
|
"libloaderapi", "handleapi", "wincon", "winuser"
|
||||||
|
|
@ -18,5 +18,6 @@ serde_json = "1.0"
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
open = "1"
|
open = "1"
|
||||||
urlencoding = "2.1.0"
|
urlencoding = "2.1.0"
|
||||||
|
bitflags = "1.2.1"
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
embed-resource = "1.3"
|
embed-resource = "1.3"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate bitflags;
|
||||||
use native_windows_gui::{self as nwg, NativeUi};
|
use native_windows_gui::{self as nwg, NativeUi};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use winapi::um::wincon::GetConsoleWindow;
|
use winapi::um::wincon::GetConsoleWindow;
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,28 @@ use std::cell::RefCell;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use winapi::shared::windef::HWND__;
|
||||||
use winapi::um::winuser::{
|
use winapi::um::winuser::{
|
||||||
GetSystemMetrics, GetWindowLongA, SetWindowLongA, GWL_EXSTYLE, GWL_STYLE, SM_CXSCREEN,
|
GetActiveWindow, GetSystemMetrics, GetWindowLongA, IsIconic, IsZoomed, SetWindowLongA,
|
||||||
SM_CYSCREEN, WS_CAPTION, WS_EX_CLIENTEDGE, WS_EX_DLGMODALFRAME, WS_EX_STATICEDGE,
|
GWL_EXSTYLE, GWL_STYLE, SM_CXSCREEN, SM_CYSCREEN, WS_CAPTION, WS_EX_CLIENTEDGE,
|
||||||
WS_EX_WINDOWEDGE, WS_THICKFRAME,
|
WS_EX_DLGMODALFRAME, WS_EX_STATICEDGE, WS_EX_WINDOWEDGE, WS_THICKFRAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::stremio_app::ipc::{RPCRequest, RPCResponse, RPCResponseData, RPCResponseDataTransport};
|
use crate::stremio_app::ipc::{RPCRequest, RPCResponse, RPCResponseData, RPCResponseDataTransport};
|
||||||
use crate::stremio_app::stremio_player::Player;
|
use crate::stremio_app::stremio_player::Player;
|
||||||
use crate::stremio_app::stremio_wevbiew::WebView;
|
use crate::stremio_app::stremio_wevbiew::WebView;
|
||||||
|
use crate::stremio_app::systray::SystemTray;
|
||||||
|
|
||||||
#[derive(Default)]
|
bitflags! {
|
||||||
|
struct WindowState: u8 {
|
||||||
|
const MINIMIZED = 0x01;
|
||||||
|
const MAXIMIZED = 0x02;
|
||||||
|
const FULL_SCREEN = 0x04;
|
||||||
|
const ACTIVE = 0x08;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
pub struct WindowStyle {
|
pub struct WindowStyle {
|
||||||
pub full_screen: bool,
|
pub full_screen: bool,
|
||||||
pub pos: (i32, i32),
|
pub pos: (i32, i32),
|
||||||
|
|
@ -23,6 +34,34 @@ pub struct WindowStyle {
|
||||||
pub style: i32,
|
pub style: i32,
|
||||||
pub ex_style: i32,
|
pub ex_style: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WindowStyle {
|
||||||
|
pub fn get_window_state(self, hwnd: *mut HWND__) -> u32 {
|
||||||
|
let mut visibility: WindowState = WindowState::empty();
|
||||||
|
visibility |= if 0 != unsafe { IsIconic(hwnd) } {
|
||||||
|
WindowState::MINIMIZED
|
||||||
|
} else {
|
||||||
|
WindowState::empty()
|
||||||
|
};
|
||||||
|
visibility |= if 0 != unsafe { IsZoomed(hwnd) } {
|
||||||
|
WindowState::MAXIMIZED
|
||||||
|
} else {
|
||||||
|
WindowState::empty()
|
||||||
|
};
|
||||||
|
visibility |= if hwnd == unsafe { GetActiveWindow() } {
|
||||||
|
WindowState::ACTIVE
|
||||||
|
} else {
|
||||||
|
WindowState::empty()
|
||||||
|
};
|
||||||
|
visibility |= if self.full_screen {
|
||||||
|
WindowState::FULL_SCREEN
|
||||||
|
} else {
|
||||||
|
WindowState::empty()
|
||||||
|
};
|
||||||
|
visibility.bits() as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, NwgUi)]
|
#[derive(Default, NwgUi)]
|
||||||
pub struct MainWindow {
|
pub struct MainWindow {
|
||||||
pub webui_url: String,
|
pub webui_url: String,
|
||||||
|
|
@ -32,9 +71,12 @@ pub struct MainWindow {
|
||||||
#[nwg_resource(source_embed: Some(&data.embed), source_embed_str: Some("MAINICON"))]
|
#[nwg_resource(source_embed: Some(&data.embed), source_embed_str: Some("MAINICON"))]
|
||||||
pub window_icon: nwg::Icon,
|
pub window_icon: nwg::Icon,
|
||||||
#[nwg_control(icon: Some(&data.window_icon), title: "Stremio", flags: "MAIN_WINDOW|VISIBLE")]
|
#[nwg_control(icon: Some(&data.window_icon), title: "Stremio", flags: "MAIN_WINDOW|VISIBLE")]
|
||||||
#[nwg_events( OnWindowClose: [Self::on_quit], OnInit: [Self::on_init], OnPaint: [Self::on_paint], OnMinMaxInfo: [Self::on_min_max(SELF, EVT_DATA)] )]
|
#[nwg_events( OnWindowClose: [Self::on_quit(SELF, EVT_DATA)], OnInit: [Self::on_init], OnPaint: [Self::on_paint], OnMinMaxInfo: [Self::on_min_max(SELF, EVT_DATA)], OnWindowMaximize: [Self::on_maximize] )]
|
||||||
pub window: nwg::Window,
|
pub window: nwg::Window,
|
||||||
#[nwg_partial(parent: window)]
|
#[nwg_partial(parent: window)]
|
||||||
|
#[nwg_events((tray_exit, OnMenuItemSelected): [Self::on_quit_notice], (tray_show_hide, OnMenuItemSelected): [Self::on_show_hide]) ]
|
||||||
|
pub tray: SystemTray,
|
||||||
|
#[nwg_partial(parent: window)]
|
||||||
pub webview: WebView,
|
pub webview: WebView,
|
||||||
#[nwg_partial(parent: window)]
|
#[nwg_partial(parent: window)]
|
||||||
pub player: Player,
|
pub player: Player,
|
||||||
|
|
@ -73,6 +115,8 @@ impl MainWindow {
|
||||||
.set_size(dimensions.0 as u32, dimensions.1 as u32);
|
.set_size(dimensions.0 as u32, dimensions.1 as u32);
|
||||||
self.window.set_position(x, y);
|
self.window.set_position(x, y);
|
||||||
|
|
||||||
|
self.tray.tray_show_hide.set_checked(true);
|
||||||
|
|
||||||
let player_channel = self.player.channel.borrow();
|
let player_channel = self.player.channel.borrow();
|
||||||
let (player_tx, player_rx) = player_channel
|
let (player_tx, player_rx) = player_channel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -250,6 +294,8 @@ impl MainWindow {
|
||||||
});
|
});
|
||||||
saved_style.full_screen = true;
|
saved_style.full_screen = true;
|
||||||
}
|
}
|
||||||
|
let visibility = saved_style.clone().get_window_state(hwnd);
|
||||||
|
|
||||||
let web_channel = self.webview.channel.borrow();
|
let web_channel = self.webview.channel.borrow();
|
||||||
let (web_tx, _) = web_channel
|
let (web_tx, _) = web_channel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -258,19 +304,52 @@ impl MainWindow {
|
||||||
web_tx_app
|
web_tx_app
|
||||||
.send(RPCResponse::visibility_change(
|
.send(RPCResponse::visibility_change(
|
||||||
true,
|
true,
|
||||||
1,
|
visibility,
|
||||||
saved_style.full_screen,
|
saved_style.full_screen,
|
||||||
))
|
))
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn on_quit_notice(&self) {
|
fn on_quit_notice(&self) {
|
||||||
self.on_quit();
|
nwg::stop_thread_dispatch();
|
||||||
}
|
}
|
||||||
fn on_hide_splash_notice(&self) {
|
fn on_hide_splash_notice(&self) {
|
||||||
self.splash_frame.set_visible(false);
|
self.splash_frame.set_visible(false);
|
||||||
}
|
}
|
||||||
fn on_quit(&self) {
|
fn on_maximize(&self) {}
|
||||||
nwg::stop_thread_dispatch();
|
fn on_show_hide(&self) {
|
||||||
|
self.window.set_visible(!self.window.visible());
|
||||||
|
self.tray.tray_show_hide.set_checked(self.window.visible());
|
||||||
|
let web_channel = self.webview.channel.borrow();
|
||||||
|
let (web_tx, _) = web_channel
|
||||||
|
.as_ref()
|
||||||
|
.expect("Cannont obtain communication channel for the Web UI");
|
||||||
|
let web_tx_app = web_tx.clone();
|
||||||
|
let saved_style = self.saved_window_style.borrow();
|
||||||
|
web_tx_app
|
||||||
|
.send(RPCResponse::visibility_change(
|
||||||
|
self.window.visible(),
|
||||||
|
1,
|
||||||
|
saved_style.full_screen,
|
||||||
|
))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
fn on_quit(&self, data: &nwg::EventData) {
|
||||||
|
if let Some(hwnd) = self.window.handle.hwnd() {
|
||||||
|
if let nwg::EventData::OnWindowClose(data) = data {
|
||||||
|
data.close(false);
|
||||||
|
}
|
||||||
|
self.window.set_visible(false);
|
||||||
|
let web_channel = self.webview.channel.borrow();
|
||||||
|
let (web_tx, _) = web_channel
|
||||||
|
.as_ref()
|
||||||
|
.expect("Cannont obtain communication channel for the Web UI");
|
||||||
|
let web_tx_app = web_tx.clone();
|
||||||
|
let saved_style = self.saved_window_style.borrow();
|
||||||
|
let visibility = saved_style.clone().get_window_state(hwnd);
|
||||||
|
web_tx_app
|
||||||
|
.send(RPCResponse::visibility_change(false, visibility, false))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,6 @@ pub mod stremio_server;
|
||||||
pub use stremio_server::StremioServer;
|
pub use stremio_server::StremioServer;
|
||||||
pub mod ipc;
|
pub mod ipc;
|
||||||
pub use ipc::{Channel, RPCRequest, RPCResponse, RPCResponseData, RPCResponseDataTransport};
|
pub use ipc::{Channel, RPCRequest, RPCResponse, RPCResponseData, RPCResponseDataTransport};
|
||||||
|
pub mod systray;
|
||||||
|
pub use systray::SystemTray;
|
||||||
|
|
||||||
|
|
|
||||||
28
src/stremio_app/systray.rs
Normal file
28
src/stremio_app/systray.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
use native_windows_derive::NwgPartial;
|
||||||
|
use native_windows_gui as nwg;
|
||||||
|
|
||||||
|
#[derive(Default, NwgPartial)]
|
||||||
|
pub struct SystemTray {
|
||||||
|
#[nwg_resource]
|
||||||
|
pub embed: nwg::EmbedResource,
|
||||||
|
#[nwg_resource(source_embed: Some(&data.embed), source_embed_str: Some("MAINICON"))]
|
||||||
|
pub tray_icon: nwg::Icon,
|
||||||
|
#[nwg_control(icon: Some(&data.tray_icon), tip: Some("Stremio"))]
|
||||||
|
#[nwg_events(MousePressLeftUp: [Self::show_menu], OnContextMenu: [Self::show_menu])]
|
||||||
|
pub tray: nwg::TrayNotification,
|
||||||
|
#[nwg_control(popup: true)]
|
||||||
|
pub tray_menu: nwg::Menu,
|
||||||
|
#[nwg_control(parent: tray_menu, text: "&Show Window")]
|
||||||
|
pub tray_show_hide: nwg::MenuItem,
|
||||||
|
#[nwg_control(parent: tray_menu, text: "Always on &Top")]
|
||||||
|
pub tray_topmost: nwg::MenuItem,
|
||||||
|
#[nwg_control(parent: tray_menu, text: "E&xit")]
|
||||||
|
pub tray_exit: nwg::MenuItem,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemTray {
|
||||||
|
fn show_menu(&self) {
|
||||||
|
let (x, y) = nwg::GlobalCursor::position();
|
||||||
|
self.tray_menu.popup(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue