1 parent
281087469a
commit
292f757d5d
8 files changed
+169
No files matched your search
@@ -235,6 +235,7 @@ cocoa = "0.26"
|
|||||||
core-foundation = "0.10.0"
|
core-foundation = "0.10.0"
|
||||||
objc = { version = "0.2.7", features = ["exception"] }
|
objc = { version = "0.2.7", features = ["exception"] }
|
||||||
objc_id = "0.1.1"
|
objc_id = "0.1.1"
|
||||||
|
tray-icon = "0.19"
|
||||||
|
|
||||||
# our release profile should be fast to compile and fast to run
|
# our release profile should be fast to compile and fast to run
|
||||||
# when we ship our CI builds, we turn on LTO which improves perf leftover by turning on incremental
|
# when we ship our CI builds, we turn on LTO which improves perf leftover by turning on incremental
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ global-hotkey = "0.5.0"
|
|||||||
rfd = { version = "0.14", default-features = false, features = ["xdg-portal", "tokio"] }
|
rfd = { version = "0.14", default-features = false, features = ["xdg-portal", "tokio"] }
|
||||||
muda = "0.11.3"
|
muda = "0.11.3"
|
||||||
|
|
||||||
|
[target.'cfg(any(target_os = "windows",target_os = "macos",target_os = "linux"))'.dependencies]
|
||||||
|
tray-icon = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(target_os = "ios")'.dependencies]
|
[target.'cfg(target_os = "ios")'.dependencies]
|
||||||
objc = "0.2.7"
|
objc = "0.2.7"
|
||||||
|
|||||||
@@ -85,6 +85,10 @@ impl App {
|
|||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
app.set_menubar_receiver();
|
app.set_menubar_receiver();
|
||||||
|
|
||||||
|
// Wire up the tray icon receiver - this way any component can key into the menubar actions
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
app.set_tray_icon_receiver();
|
||||||
|
|
||||||
// Allow hotreloading to work - but only in debug mode
|
// Allow hotreloading to work - but only in debug mode
|
||||||
#[cfg(all(feature = "devtools", debug_assertions))]
|
#[cfg(all(feature = "devtools", debug_assertions))]
|
||||||
app.connect_hotreload();
|
app.connect_hotreload();
|
||||||
@@ -134,6 +138,29 @@ impl App {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
pub fn handle_tray_menu_event(&mut self, event: tray_icon::menu::MenuEvent) {
|
||||||
|
_ = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
pub fn handle_tray_icon_event(&mut self, event: tray_icon::TrayIconEvent) {
|
||||||
|
if let tray_icon::TrayIconEvent::Click {
|
||||||
|
id: _,
|
||||||
|
position: _,
|
||||||
|
rect: _,
|
||||||
|
button,
|
||||||
|
button_state: _,
|
||||||
|
} = event
|
||||||
|
{
|
||||||
|
if button == tray_icon::MouseButton::Left {
|
||||||
|
for webview in self.webviews.values() {
|
||||||
|
webview.desktop_context.window.set_visible(true);
|
||||||
|
webview.desktop_context.window.set_focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "devtools", debug_assertions))]
|
#[cfg(all(feature = "devtools", debug_assertions))]
|
||||||
pub fn connect_hotreload(&self) {
|
pub fn connect_hotreload(&self) {
|
||||||
@@ -360,6 +387,27 @@ impl App {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
fn set_tray_icon_receiver(&self) {
|
||||||
|
let receiver = self.shared.proxy.clone();
|
||||||
|
|
||||||
|
// The event loop becomes the menu receiver
|
||||||
|
// This means we don't need to poll the receiver on every tick - we just get the events as they come in
|
||||||
|
// This is a bit more efficient than the previous implementation, but if someone else sets a handler, the
|
||||||
|
// receiver will become inert.
|
||||||
|
tray_icon::TrayIconEvent::set_event_handler(Some(move |t| {
|
||||||
|
// todo: should we unset the event handler when the app shuts down?
|
||||||
|
_ = receiver.send_event(UserWindowEvent::TrayIconEvent(t));
|
||||||
|
}));
|
||||||
|
|
||||||
|
// for whatever reason they had to make it separate
|
||||||
|
let receiver = self.shared.proxy.clone();
|
||||||
|
tray_icon::menu::MenuEvent::set_event_handler(Some(move |t| {
|
||||||
|
// todo: should we unset the event handler when the app shuts down?
|
||||||
|
_ = receiver.send_event(UserWindowEvent::TrayMenuEvent(t));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
/// Do our best to preserve state about the window when the event loop is destroyed
|
/// Do our best to preserve state about the window when the event loop is destroyed
|
||||||
///
|
///
|
||||||
/// This will attempt to save the window position, size, and monitor into the environment before
|
/// This will attempt to save the window position, size, and monitor into the environment before
|
||||||
|
|||||||
@@ -54,6 +54,40 @@ pub fn use_muda_event_handler(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Register an event handler that runs when a tray icon menu event is processed.
|
||||||
|
#[cfg_attr(
|
||||||
|
docsrs,
|
||||||
|
doc(cfg(any(target_os = "windows", target_os = "linux", target_os = "macos")))
|
||||||
|
)]
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
pub fn use_tray_menu_event_handler(
|
||||||
|
mut handler: impl FnMut(&tray_icon::menu::MenuEvent) + 'static,
|
||||||
|
) -> WryEventHandler {
|
||||||
|
use_wry_event_handler(move |event, _| {
|
||||||
|
if let Event::UserEvent(UserWindowEvent::TrayMenuEvent(event)) = event {
|
||||||
|
handler(event);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register an event handler that runs when a tray icon event is processed.
|
||||||
|
/// This is only for tray icon and not it's menus.
|
||||||
|
/// If you want to register tray icon menus handler use `use_tray_menu_event_handler` instead.
|
||||||
|
#[cfg_attr(
|
||||||
|
docsrs,
|
||||||
|
doc(cfg(any(target_os = "windows", target_os = "linux", target_os = "macos")))
|
||||||
|
)]
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
pub fn use_tray_icon_event_handler(
|
||||||
|
mut handler: impl FnMut(&tray_icon::TrayIconEvent) + 'static,
|
||||||
|
) -> WryEventHandler {
|
||||||
|
use_wry_event_handler(move |event, _| {
|
||||||
|
if let Event::UserEvent(UserWindowEvent::TrayIconEvent(event)) = event {
|
||||||
|
handler(event);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Provide a callback to handle asset loading yourself.
|
/// Provide a callback to handle asset loading yourself.
|
||||||
///
|
///
|
||||||
/// The callback takes a path as requested by the web view, and it should return `Some(response)`
|
/// The callback takes a path as requested by the web view, and it should return `Some(response)`
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ pub enum UserWindowEvent {
|
|||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
MudaMenuEvent(muda::MenuEvent),
|
MudaMenuEvent(muda::MenuEvent),
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
TrayIconEvent(tray_icon::TrayIconEvent),
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
TrayMenuEvent(tray_icon::menu::MenuEvent),
|
||||||
|
|
||||||
/// Poll the virtualdom
|
/// Poll the virtualdom
|
||||||
Poll(WindowId),
|
Poll(WindowId),
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Conf
|
|||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
UserWindowEvent::MudaMenuEvent(evnt) => app.handle_menu_event(evnt),
|
UserWindowEvent::MudaMenuEvent(evnt) => app.handle_menu_event(evnt),
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
UserWindowEvent::TrayMenuEvent(evnt) => app.handle_tray_menu_event(evnt),
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
UserWindowEvent::TrayIconEvent(evnt) => app.handle_tray_icon_event(evnt),
|
||||||
|
|
||||||
#[cfg(all(feature = "devtools", debug_assertions))]
|
#[cfg(all(feature = "devtools", debug_assertions))]
|
||||||
UserWindowEvent::HotReloadEvent(msg) => app.handle_hot_reload_msg(msg),
|
UserWindowEvent::HotReloadEvent(msg) => app.handle_hot_reload_msg(msg),
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ pub use wry;
|
|||||||
#[cfg(not(any(target_os = "ios", target_os = "android")))]
|
#[cfg(not(any(target_os = "ios", target_os = "android")))]
|
||||||
pub use muda;
|
pub use muda;
|
||||||
|
|
||||||
|
// Tray icon
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
pub mod trayicon;
|
||||||
|
|
||||||
// Public exports
|
// Public exports
|
||||||
pub use assets::AssetRequest;
|
pub use assets::AssetRequest;
|
||||||
pub use config::{Config, WindowCloseBehaviour};
|
pub use config::{Config, WindowCloseBehaviour};
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
//! tray icon
|
||||||
|
|
||||||
|
use dioxus_core::{
|
||||||
|
prelude::{provide_context, try_consume_context},
|
||||||
|
use_hook,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "ios", target_os = "android")))]
|
||||||
|
pub use tray_icon::*;
|
||||||
|
|
||||||
|
/// tray icon menu type trait
|
||||||
|
#[cfg(not(any(target_os = "ios", target_os = "android")))]
|
||||||
|
pub type DioxusTrayMenu = tray_icon::menu::Menu;
|
||||||
|
#[cfg(any(target_os = "ios", target_os = "android"))]
|
||||||
|
pub type DioxusTrayMenu = ();
|
||||||
|
|
||||||
|
/// tray icon icon type trait
|
||||||
|
#[cfg(not(any(target_os = "ios", target_os = "android")))]
|
||||||
|
pub type DioxusTrayIcon = tray_icon::Icon;
|
||||||
|
#[cfg(any(target_os = "ios", target_os = "android"))]
|
||||||
|
pub type DioxusTrayIcon = ();
|
||||||
|
|
||||||
|
/// tray icon type trait
|
||||||
|
#[cfg(not(any(target_os = "ios", target_os = "android")))]
|
||||||
|
pub type DioxusTray = tray_icon::TrayIcon;
|
||||||
|
#[cfg(any(target_os = "ios", target_os = "android"))]
|
||||||
|
pub type DioxusTray = ();
|
||||||
|
|
||||||
|
/// initializes a tray icon
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn init_tray_icon(menu: DioxusTrayMenu, icon: Option<DioxusTrayIcon>) -> DioxusTray {
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
{
|
||||||
|
let builder = tray_icon::TrayIconBuilder::new()
|
||||||
|
.with_menu(Box::new(menu))
|
||||||
|
.with_menu_on_left_click(false)
|
||||||
|
.with_icon(match icon {
|
||||||
|
Some(value) => value,
|
||||||
|
None => tray_icon::Icon::from_rgba(
|
||||||
|
include_bytes!("./assets/default_icon.bin").to_vec(),
|
||||||
|
460,
|
||||||
|
460,
|
||||||
|
)
|
||||||
|
.expect("image parse failed"),
|
||||||
|
});
|
||||||
|
|
||||||
|
provide_context(builder.build().expect("tray icon builder failed"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a default tray icon menu
|
||||||
|
pub fn default_tray_icon() -> DioxusTrayMenu {
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
{
|
||||||
|
use tray_icon::menu::{Menu, PredefinedMenuItem};
|
||||||
|
let tray_menu = Menu::new();
|
||||||
|
tray_menu
|
||||||
|
.append_items(&[&PredefinedMenuItem::quit(None)])
|
||||||
|
.unwrap();
|
||||||
|
tray_menu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides a hook to the tray icon
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
pub fn use_tray_icon() -> Option<tray_icon::TrayIcon> {
|
||||||
|
use_hook(try_consume_context)
|
||||||
|
}
|
||||||
Reference in new issue
Block a user