zng_ext_window/
cmd.rs

1//! Commands that control the scoped window.
2
3use zng_app::{
4    event::{CommandHandle, CommandInfoExt, CommandNameExt, command},
5    shortcut::{CommandShortcutExt, shortcut},
6    update::EventUpdate,
7    window::{WINDOW, WindowId},
8};
9use zng_var::Var;
10use zng_view_api::window::WindowState;
11use zng_wgt::{CommandIconExt as _, ICONS, wgt_fn};
12
13use crate::{WINDOWS, WindowVars};
14
15pub use zng_view_api::window::ResizeDirection;
16
17command! {
18    /// Represents the window **close** action.
19    pub static CLOSE_CMD = {
20        l10n!: true,
21        name: "Close",
22        info: "Close the window",
23        shortcut: [shortcut!(ALT+F4), shortcut!(CTRL+'W')],
24        icon: wgt_fn!(|_| ICONS.get(["window-close", "close"])),
25    };
26
27    /// Represents the window **minimize** action.
28    pub static MINIMIZE_CMD = {
29        l10n!: true,
30        name: "Minimize",
31        info: "Minimize the window",
32        icon: wgt_fn!(|_| ICONS.get(["window-minimize"])),
33    };
34
35    /// Represents the window **maximize** action.
36    pub static MAXIMIZE_CMD = {
37        l10n!: true,
38        name: "Maximize",
39        info: "Maximize the window",
40        icon: wgt_fn!(|_| ICONS.get(["window-maximize"])),
41    };
42
43    /// Represents the window **toggle fullscreen** action.
44    ///
45    /// # Behavior
46    ///
47    /// This command is about the *windowed* fullscreen state ([`WindowState::Fullscreen`]),
48    /// use the [`EXCLUSIVE_FULLSCREEN_CMD`] to toggle *exclusive* video mode fullscreen.
49    pub static FULLSCREEN_CMD = {
50        l10n!: true,
51        name: "Fullscreen",
52        info: "Toggle fullscreen mode on the window",
53        shortcut: {
54            let a = if cfg!(target_os = "macos") {
55                shortcut!(CTRL|SHIFT+'F')
56            } else {
57                shortcut!(F11)
58            };
59            [a, shortcut!(ZoomToggle)]
60        },
61        icon: wgt_fn!(|_| ICONS.get(["window-windowed-fullscreen", "window-fullscreen", "fullscreen"])),
62    };
63
64    /// Represents the window **toggle fullscreen** action.
65    ///
66    /// # Behavior
67    ///
68    /// This command is about the *exclusive* fullscreen state ([`WindowState::Exclusive`]),
69    /// use the [`FULLSCREEN_CMD`] to toggle *windowed* fullscreen.
70    pub static EXCLUSIVE_FULLSCREEN_CMD = {
71        l10n!: true,
72        name: "Exclusive Fullscreen",
73        info: "Toggle exclusive fullscreen mode on the window",
74        icon: wgt_fn!(|_| ICONS.get(["window-exclusive-fullscreen", "window-fullscreen", "fullscreen"])),
75    };
76
77    /// Represents the window **restore** action.
78    ///
79    /// Restores the window to its previous non-minimized state or normal state.
80    pub static RESTORE_CMD = {
81        l10n!: true,
82        name: "Restore",
83        info: "Restores the window to its previous non-minimized state or normal state",
84        icon: wgt_fn!(|_| ICONS.get(["window-restore"])),
85    };
86
87    /// Represents the **close IME** action.
88    ///
89    /// If any IME preview is active close it without committing.
90    pub static CANCEL_IME_CMD;
91
92    /// Represents the window **drag-move** and **drag-resize** actions.
93    ///
94    /// There's no guarantee that this will work unless the left mouse button was pressed immediately before this command is called.
95    ///
96    /// # Parameter
97    ///
98    /// If this command is called without parameter the window will drag-move, if it is called with a [`ResizeDirection`] the
99    /// window will drag-resize.
100    pub static DRAG_MOVE_RESIZE_CMD;
101
102    /// Represents the window **open title bar context menu** action.
103    ///
104    /// # Parameter
105    ///
106    /// This command supports an optional parameter, it can be a [`DipPoint`] or [`PxPoint`] that defines
107    /// the menu position.
108    ///
109    /// [`DipPoint`]: zng_layout::unit::DipPoint
110    /// [`PxPoint`]: zng_layout::unit::PxPoint
111    pub static OPEN_TITLE_BAR_CONTEXT_MENU_CMD;
112}
113
114pub(super) struct WindowCommands {
115    maximize_handle: CommandHandle,
116    minimize_handle: CommandHandle,
117    restore_handle: CommandHandle,
118
119    fullscreen_handle: CommandHandle,
120    exclusive_handle: CommandHandle,
121
122    close_handle: CommandHandle,
123}
124impl WindowCommands {
125    pub fn new(window_id: WindowId) -> Self {
126        WindowCommands {
127            maximize_handle: MAXIMIZE_CMD.scoped(window_id).subscribe(false),
128            minimize_handle: MINIMIZE_CMD.scoped(window_id).subscribe(false),
129            restore_handle: RESTORE_CMD.scoped(window_id).subscribe(false),
130            fullscreen_handle: FULLSCREEN_CMD.scoped(window_id).subscribe(true),
131            exclusive_handle: EXCLUSIVE_FULLSCREEN_CMD.scoped(window_id).subscribe(true),
132            close_handle: CLOSE_CMD.scoped(window_id).subscribe(true),
133        }
134    }
135
136    pub fn event(&mut self, window_vars: &WindowVars, update: &EventUpdate) {
137        let scope = WINDOW.id();
138        if let Some(args) = MAXIMIZE_CMD.scoped(scope).on(update) {
139            args.handle_enabled(&self.maximize_handle, |_| {
140                window_vars.state().set(WindowState::Maximized);
141            });
142        } else if let Some(args) = MINIMIZE_CMD.scoped(scope).on(update) {
143            args.handle_enabled(&self.minimize_handle, |_| {
144                window_vars.state().set(WindowState::Minimized);
145            });
146        } else if let Some(args) = RESTORE_CMD.scoped(scope).on(update) {
147            args.handle_enabled(&self.restore_handle, |_| {
148                window_vars.state().set(window_vars.restore_state().get());
149            });
150        } else if let Some(args) = CLOSE_CMD.scoped(scope).on(update) {
151            args.handle_enabled(&self.close_handle, |_| {
152                let _ = WINDOWS.close(scope);
153            });
154        } else if let Some(args) = FULLSCREEN_CMD.scoped(scope).on(update) {
155            args.handle_enabled(&self.fullscreen_handle, |_| {
156                if let WindowState::Fullscreen = window_vars.state().get() {
157                    window_vars.state().set(window_vars.restore_state().get());
158                } else {
159                    window_vars.state().set(WindowState::Fullscreen);
160                }
161            });
162        } else if let Some(args) = EXCLUSIVE_FULLSCREEN_CMD.scoped(scope).on(update) {
163            args.handle_enabled(&self.exclusive_handle, |_| {
164                if let WindowState::Exclusive = window_vars.state().get() {
165                    window_vars.state().set(window_vars.restore_state().get());
166                } else {
167                    window_vars.state().set(WindowState::Exclusive);
168                }
169            });
170        }
171    }
172
173    pub fn init(&mut self, window_vars: &WindowVars) {
174        self.update_state(window_vars.state().get());
175    }
176
177    pub fn update(&mut self, window_vars: &WindowVars) {
178        if let Some(state) = window_vars.state().get_new() {
179            self.update_state(state);
180        }
181    }
182
183    fn update_state(&mut self, state: WindowState) {
184        self.restore_handle.set_enabled(state != WindowState::Normal);
185        self.maximize_handle.set_enabled(state != WindowState::Maximized);
186        self.minimize_handle.set_enabled(state != WindowState::Minimized);
187    }
188}