Added close behaviour option for specific window (#3754)
Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
1 parent
c3c7e5f705
commit
9573af115a
10 files changed
+172
-28
No files matched your search
@@ -5,12 +5,15 @@
|
||||
//! own context, root elements, etc.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus::{desktop::Config, desktop::WindowCloseBehaviour};
|
||||
use dioxus::{desktop::Config, desktop::DefaultWindowCloseBehaviour};
|
||||
|
||||
fn main() {
|
||||
dioxus::LaunchBuilder::desktop()
|
||||
// We can choose the close behavior of the last window to hide. See WindowCloseBehaviour for more options.
|
||||
.with_cfg(Config::new().with_close_behaviour(WindowCloseBehaviour::LastWindowHides))
|
||||
// We can choose the close behavior of the last window to hide. See DefaultWindowCloseBehaviour for more options.
|
||||
.with_cfg(
|
||||
Config::new()
|
||||
.with_default_window_close_behaviour(DefaultWindowCloseBehaviour::LastWindowHides),
|
||||
)
|
||||
.launch(app);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
//! Multiwindow with tray icon example
|
||||
//!
|
||||
//! This example shows how to implement a simple multiwindow application and tray icon using dioxus.
|
||||
//! This works by spawning a new window when the user clicks a button. We have to build a new virtualdom which has its
|
||||
//! own context, root elements, etc.
|
||||
|
||||
use dioxus::desktop::{
|
||||
trayicon::{default_tray_icon, init_tray_icon},
|
||||
Config, WindowCloseBehaviour,
|
||||
};
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus::LaunchBuilder::desktop()
|
||||
// We can choose the close behavior of this window to hide. See WindowCloseBehaviour for more options.
|
||||
.with_cfg(Config::new().with_window_close_behaviour(WindowCloseBehaviour::WindowHides))
|
||||
.launch(app);
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
// async should not be needed, check if issue 3542 has been resolved
|
||||
let onclick = move |_| async {
|
||||
let dom = VirtualDom::new(popup);
|
||||
dioxus::desktop::window().new_window(dom, Default::default());
|
||||
};
|
||||
|
||||
init_tray_icon(default_tray_icon(), None);
|
||||
|
||||
rsx! {
|
||||
button { onclick, "New Window" }
|
||||
}
|
||||
}
|
||||
|
||||
fn popup() -> Element {
|
||||
rsx! {
|
||||
div { "This is a popup window!" }
|
||||
}
|
||||
}
|
||||
@@ -8,12 +8,15 @@
|
||||
use dioxus::desktop::tao::event::Event as WryEvent;
|
||||
use dioxus::desktop::tao::event::WindowEvent;
|
||||
use dioxus::desktop::use_wry_event_handler;
|
||||
use dioxus::desktop::{Config, WindowCloseBehaviour};
|
||||
use dioxus::desktop::{Config, DefaultWindowCloseBehaviour};
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus::LaunchBuilder::desktop()
|
||||
.with_cfg(Config::new().with_close_behaviour(WindowCloseBehaviour::CloseWindow))
|
||||
.with_cfg(
|
||||
Config::new()
|
||||
.with_default_window_close_behaviour(DefaultWindowCloseBehaviour::WindowsCloses),
|
||||
)
|
||||
.launch(app)
|
||||
}
|
||||
|
||||
|
||||
+56
-11
@@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
config::{Config, WindowCloseBehaviour},
|
||||
config::{Config, DefaultWindowCloseBehaviour, WindowCloseBehaviour},
|
||||
edits::EditWebsocket,
|
||||
event_handlers::WindowEventHandlers,
|
||||
file_upload::{DesktopFileUploadForm, FileDialogRequest, NativeFileEngine},
|
||||
@@ -36,7 +36,7 @@ pub(crate) struct App {
|
||||
// Stuff we need mutable access to
|
||||
pub(crate) control_flow: ControlFlow,
|
||||
pub(crate) is_visible_before_start: bool,
|
||||
pub(crate) window_behavior: WindowCloseBehaviour,
|
||||
pub(crate) default_window_close_behavior: DefaultWindowCloseBehaviour,
|
||||
pub(crate) webviews: HashMap<WindowId, WebviewInstance>,
|
||||
pub(crate) float_all: bool,
|
||||
pub(crate) show_devtools: bool,
|
||||
@@ -65,7 +65,7 @@ impl App {
|
||||
.unwrap_or_else(|| EventLoopBuilder::<UserWindowEvent>::with_user_event().build());
|
||||
|
||||
let app = Self {
|
||||
window_behavior: cfg.last_window_close_behavior,
|
||||
default_window_close_behavior: cfg.default_window_close_behaviour,
|
||||
is_visible_before_start: true,
|
||||
webviews: HashMap::new(),
|
||||
control_flow: ControlFlow::Wait,
|
||||
@@ -188,10 +188,55 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_window_close_behaviour(
|
||||
&mut self,
|
||||
id: WindowId,
|
||||
behaviour: Option<WindowCloseBehaviour>,
|
||||
) {
|
||||
if let Some(webview) = self.webviews.get_mut(&id) {
|
||||
webview.close_behaviour = behaviour
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_close_requested(&mut self, id: WindowId) {
|
||||
use DefaultWindowCloseBehaviour::*;
|
||||
use WindowCloseBehaviour::*;
|
||||
|
||||
match self.window_behavior {
|
||||
let mut remove = false;
|
||||
|
||||
if let Some(webview) = self.webviews.get(&id) {
|
||||
if let Some(close_behaviour) = &webview.close_behaviour {
|
||||
match close_behaviour {
|
||||
WindowExitsApp => {
|
||||
self.control_flow = ControlFlow::Exit;
|
||||
return;
|
||||
}
|
||||
WindowHides => {
|
||||
hide_window(&webview.desktop_context.window);
|
||||
return;
|
||||
}
|
||||
WindowCloses => {
|
||||
remove = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// needed in case of `default_window_close_behavior WindowsHides | LastWindowHides` since they may not remove a window on `WindowCloses`
|
||||
if remove {
|
||||
#[cfg(debug_assertions)]
|
||||
self.persist_window_state();
|
||||
|
||||
self.webviews.remove(&id);
|
||||
if matches!(self.default_window_close_behavior, LastWindowExitsApp)
|
||||
&& self.webviews.is_empty()
|
||||
{
|
||||
self.control_flow = ControlFlow::Exit
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
match self.default_window_close_behavior {
|
||||
LastWindowExitsApp => {
|
||||
#[cfg(debug_assertions)]
|
||||
self.persist_window_state();
|
||||
@@ -206,13 +251,13 @@ impl App {
|
||||
self.webviews.remove(&id);
|
||||
}
|
||||
|
||||
LastWindowHides => {
|
||||
WindowsHides | LastWindowHides => {
|
||||
if let Some(webview) = self.webviews.get(&id) {
|
||||
hide_last_window(&webview.desktop_context.window);
|
||||
hide_window(&webview.desktop_context.window);
|
||||
}
|
||||
}
|
||||
|
||||
CloseWindow => {
|
||||
WindowsCloses => {
|
||||
self.webviews.remove(&id);
|
||||
}
|
||||
}
|
||||
@@ -222,8 +267,8 @@ impl App {
|
||||
self.webviews.remove(&id);
|
||||
|
||||
if matches!(
|
||||
self.window_behavior,
|
||||
WindowCloseBehaviour::LastWindowExitsApp
|
||||
self.default_window_close_behavior,
|
||||
DefaultWindowCloseBehaviour::LastWindowExitsApp
|
||||
) && self.webviews.is_empty()
|
||||
{
|
||||
self.control_flow = ControlFlow::Exit
|
||||
@@ -678,13 +723,13 @@ struct PreservedWindowState {
|
||||
url: Option<String>,
|
||||
}
|
||||
|
||||
/// Hide the last window when using LastWindowHides.
|
||||
/// Hides a window.
|
||||
///
|
||||
/// On macOS, if we use `set_visibility(false)` on the window, it will hide the window but not show
|
||||
/// it again when the user switches back to the app. `NSApplication::hide:` has the correct behaviour,
|
||||
/// so we need to special case it.
|
||||
#[allow(unused)]
|
||||
fn hide_last_window(window: &Window) {
|
||||
fn hide_window(window: &Window) {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use tao::platform::windows::WindowExtWindows;
|
||||
|
||||
@@ -17,16 +17,30 @@ type CustomEventHandler = Box<
|
||||
),
|
||||
>;
|
||||
|
||||
/// The behaviour of the application when the last window is closed.
|
||||
/// The closing behaviour of the application when the last window is closed, you can overwrite this behaviour for specific window with WindowCloseBehaviour.
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum WindowCloseBehaviour {
|
||||
/// Default behaviour, closing the last window exits the app
|
||||
pub enum DefaultWindowCloseBehaviour {
|
||||
/// Default behaviour, closing the last window will exit the app, others will close,
|
||||
LastWindowExitsApp,
|
||||
/// Closing the last window will not actually close it, just hide it
|
||||
/// Closing the last window will hide it, others will close
|
||||
LastWindowHides,
|
||||
/// Closing the last window will close it but the app will keep running so that new windows can be opened
|
||||
CloseWindow,
|
||||
/// Every window will hide
|
||||
WindowsHides,
|
||||
/// Every window will close
|
||||
WindowsCloses,
|
||||
}
|
||||
|
||||
/// The closing behaviour of specific application window.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum WindowCloseBehaviour {
|
||||
/// Closing the window will exit the app
|
||||
WindowExitsApp,
|
||||
/// Window will hide
|
||||
WindowHides,
|
||||
/// Window will close
|
||||
WindowCloses,
|
||||
}
|
||||
|
||||
/// The state of the menu builder. We need to keep track of if the state is default
|
||||
@@ -61,7 +75,8 @@ pub struct Config {
|
||||
pub(crate) custom_index: Option<String>,
|
||||
pub(crate) root_name: String,
|
||||
pub(crate) background_color: Option<(u8, u8, u8, u8)>,
|
||||
pub(crate) last_window_close_behavior: WindowCloseBehaviour,
|
||||
pub(crate) default_window_close_behaviour: DefaultWindowCloseBehaviour,
|
||||
pub(crate) window_close_behaviour: Option<WindowCloseBehaviour>,
|
||||
pub(crate) custom_event_handler: Option<CustomEventHandler>,
|
||||
pub(crate) disable_file_drop_handler: bool,
|
||||
}
|
||||
@@ -107,7 +122,8 @@ impl Config {
|
||||
custom_index: None,
|
||||
root_name: "main".to_string(),
|
||||
background_color: None,
|
||||
last_window_close_behavior: WindowCloseBehaviour::LastWindowExitsApp,
|
||||
default_window_close_behaviour: DefaultWindowCloseBehaviour::LastWindowExitsApp,
|
||||
window_close_behaviour: None,
|
||||
custom_event_handler: None,
|
||||
disable_file_drop_handler: false,
|
||||
}
|
||||
@@ -170,8 +186,17 @@ impl Config {
|
||||
}
|
||||
|
||||
/// Sets the behaviour of the application when the last window is closed.
|
||||
pub fn with_close_behaviour(mut self, behaviour: WindowCloseBehaviour) -> Self {
|
||||
self.last_window_close_behavior = behaviour;
|
||||
pub fn with_default_window_close_behaviour(
|
||||
mut self,
|
||||
behaviour: DefaultWindowCloseBehaviour,
|
||||
) -> Self {
|
||||
self.default_window_close_behaviour = behaviour;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the behaviour of the application when the last window is closed.
|
||||
pub fn with_window_close_behaviour(mut self, behaviour: WindowCloseBehaviour) -> Self {
|
||||
self.window_close_behaviour = Some(behaviour);
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
query::QueryEngine,
|
||||
shortcut::{HotKey, HotKeyState, ShortcutHandle, ShortcutRegistryError},
|
||||
webview::PendingWebview,
|
||||
AssetRequest, Config, WryEventHandler,
|
||||
AssetRequest, Config, WindowCloseBehaviour, WryEventHandler,
|
||||
};
|
||||
use dioxus_core::{prelude::Callback, VirtualDom};
|
||||
use std::{
|
||||
@@ -181,6 +181,26 @@ impl DesktopService {
|
||||
.send_event(UserWindowEvent::CloseWindow(id));
|
||||
}
|
||||
|
||||
/// Change close behaviour of this window
|
||||
pub fn change_close_behaviour(&self, behaviour: Option<WindowCloseBehaviour>) {
|
||||
let _ = self
|
||||
.shared
|
||||
.proxy
|
||||
.send_event(UserWindowEvent::CloseBehaviour(self.id(), behaviour));
|
||||
}
|
||||
|
||||
/// Change close behaviour of a specific window, given its ID
|
||||
pub fn change_window_close_behaviour(
|
||||
&self,
|
||||
id: WindowId,
|
||||
behaviour: Option<WindowCloseBehaviour>,
|
||||
) {
|
||||
let _ = self
|
||||
.shared
|
||||
.proxy
|
||||
.send_event(UserWindowEvent::CloseBehaviour(id, behaviour));
|
||||
}
|
||||
|
||||
/// change window to fullscreen
|
||||
pub fn set_fullscreen(&self, fullscreen: bool) {
|
||||
if let Some(handle) = &self.window.current_monitor() {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tao::window::WindowId;
|
||||
|
||||
use crate::WindowCloseBehaviour;
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum UserWindowEvent {
|
||||
@@ -17,6 +19,8 @@ pub enum UserWindowEvent {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
TrayMenuEvent(tray_icon::menu::MenuEvent),
|
||||
|
||||
CloseBehaviour(WindowId, Option<WindowCloseBehaviour>),
|
||||
|
||||
/// Poll the virtualdom
|
||||
Poll(WindowId),
|
||||
|
||||
|
||||
@@ -103,6 +103,9 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, mut desktop_config:
|
||||
IpcMethod::BrowserOpen => app.handle_browser_open(msg),
|
||||
IpcMethod::Other(_) => {}
|
||||
},
|
||||
UserWindowEvent::CloseBehaviour(window_id, window_close_behaviour) => {
|
||||
app.change_window_close_behaviour(window_id, window_close_behaviour)
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ pub mod trayicon;
|
||||
|
||||
// Public exports
|
||||
pub use assets::AssetRequest;
|
||||
pub use config::{Config, WindowCloseBehaviour};
|
||||
pub use config::{Config, DefaultWindowCloseBehaviour, WindowCloseBehaviour};
|
||||
pub use desktop_context::{
|
||||
window, DesktopContext, DesktopService, PendingDesktopContext, WeakDesktopContext,
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::element::DesktopElement;
|
||||
use crate::file_upload::DesktopFileDragEvent;
|
||||
use crate::menubar::DioxusMenu;
|
||||
use crate::PendingDesktopContext;
|
||||
use crate::WindowCloseBehaviour;
|
||||
use crate::{
|
||||
app::SharedContext,
|
||||
assets::AssetHandlerRegistry,
|
||||
@@ -160,6 +161,7 @@ pub(crate) struct WebviewInstance {
|
||||
pub edits: WebviewEdits,
|
||||
pub desktop_context: DesktopContext,
|
||||
pub waker: Waker,
|
||||
pub close_behaviour: Option<WindowCloseBehaviour>,
|
||||
|
||||
// Wry assumes the webcontext is alive for the lifetime of the webview.
|
||||
// We need to keep the webcontext alive, otherwise the webview will crash
|
||||
@@ -431,6 +433,7 @@ impl WebviewInstance {
|
||||
desktop_context,
|
||||
_menu: menu,
|
||||
_web_context: web_context,
|
||||
close_behaviour: cfg.window_close_behaviour,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user