zng_wgt_window/
lib.rs

1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
3//!
4//! Window widget, properties, properties and nodes.
5//!
6//! # Crate
7//!
8#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11
12// used by fallback_chrome
13zng_wgt::enable_widget_macros!();
14
15use zng_ext_input::focus::{DirectionalNav, FocusScopeOnFocus, TabNav};
16use zng_ext_window::{
17    FrameImageReadyArgs, HeadlessMonitor, RenderMode, StartPosition, WINDOW_Ext as _, WindowChangedArgs, WindowCloseArgs,
18    WindowCloseRequestedArgs, WindowOpenArgs, WindowRoot,
19};
20use zng_var::types::ContextualizedVar;
21use zng_wgt::{is_mobile, prelude::*};
22use zng_wgt_fill::background_color;
23use zng_wgt_input::focus::{
24    FOCUS_HIGHLIGHT_OFFSETS_VAR, FOCUS_HIGHLIGHT_WIDTHS_VAR, directional_nav, focus_highlight, focus_scope, focus_scope_behavior, tab_nav,
25};
26use zng_wgt_text::{FONT_SIZE_VAR, font_color, lang};
27
28pub mod events;
29mod window_properties;
30
31pub use self::window_properties::*;
32
33mod fallback_chrome;
34pub use fallback_chrome::fallback_chrome;
35
36/// A window container.
37///
38/// The instance type is [`WindowRoot`], it can be given to the [`WINDOWS`](zng_ext_window::WINDOWS) service
39/// to open a system window that is kept in sync with the window properties set in the widget.
40///
41/// See [`run_window`] for more details.
42///
43/// [`WindowRoot`]: zng_ext_window::WindowRoot
44/// [`run_window`]: zng_ext_window::AppRunWindowExt::run_window
45#[widget($crate::Window)]
46pub struct Window(zng_wgt_container::Container);
47impl Window {
48    fn widget_intrinsic(&mut self) {
49        widget_set! {
50            self;
51
52            // set the root font size
53            font_size = FONT_SIZE_VAR.boxed();
54
55            // optimization, actualize mapping context-vars early, see `context_var!` docs.
56            zng_wgt_text::font_palette = zng_wgt_text::FONT_PALETTE_VAR;
57
58            // set layout direction.
59            lang = zng_ext_l10n::LANG_VAR;
60
61            font_color = light_dark(rgb(0.08, 0.08, 0.08), rgb(0.92, 0.92, 0.92));
62            background_color = light_dark(rgb(0.9, 0.9, 0.9), rgb(0.1, 0.1, 0.1));
63            focus_highlight = {
64                offsets: FOCUS_HIGHLIGHT_OFFSETS_VAR,
65                widths: FOCUS_HIGHLIGHT_WIDTHS_VAR,
66                sides: light_dark(colors::BLACK, rgb(200, 200, 200)).rgba_map(BorderSides::dashed),
67            };
68            clear_color = light_dark(rgb(0.9, 0.9, 0.9), rgb(0.1, 0.1, 0.1));
69            focus_scope = true;
70            tab_nav = TabNav::Cycle;
71            directional_nav = DirectionalNav::Cycle;
72            focus_scope_behavior = FocusScopeOnFocus::LastFocused;
73            config_block_window_load = true;
74            save_state = SaveState::enabled();
75
76            padding = ContextualizedVar::new(|| WINDOW.vars().safe_padding().map(|p| SideOffsets::from(*p)));
77
78            when #is_mobile {
79                // users tap the main background to dismiss `TextInput!` soft keyboard
80                focus_scope_behavior = FocusScopeOnFocus::Widget;
81                font_size = FONT_SIZE_VAR.map(|f| f.clone() * 1.5.fct()).boxed();
82            }
83
84            when #needs_fallback_chrome {
85                custom_chrome_adorner_fn = wgt_fn!(|_| { fallback_chrome() });
86                custom_chrome_padding_fn = ContextualizedVar::new(|| {
87                    let vars = WINDOW.vars();
88                    expr_var! {
89                        let title_padding = SideOffsets::new(28, 0, 0, 0);
90                        let chrome_padding = if matches!(#{vars.state()}, zng_ext_window::WindowState::Maximized) {
91                            title_padding
92                        } else {
93                            title_padding + SideOffsets::new_all(5)
94                        };
95                        // safe_padding is 0 in GNOME+Wayland, but better be safe :D
96                        let safe_padding = SideOffsets::from(*#{vars.safe_padding()});
97                        chrome_padding + safe_padding
98                    }
99                });
100            }
101        }
102
103        self.widget_builder().push_build_action(|wgt| {
104            wgt.push_intrinsic(NestGroup::EVENT, "layers", zng_wgt_layer::layers_node);
105        });
106    }
107
108    /// Build a [`WindowRoot`].
109    ///
110    /// [`WindowRoot`]: zng_ext_window::WindowRoot
111    pub fn widget_build(&mut self) -> WindowRoot {
112        let mut wgt = self.widget_take();
113        WindowRoot::new(
114            wgt.capture_value_or_else(property_id!(Self::id), WidgetId::new_unique),
115            wgt.capture_value_or_default::<StartPosition>(property_id!(Self::start_position)),
116            wgt.capture_value_or_default(property_id!(Self::kiosk)),
117            wgt.capture_value_or_else(property_id!(Self::allow_transparency), || true),
118            wgt.capture_value_or_default::<Option<RenderMode>>(property_id!(Self::render_mode)),
119            wgt.capture_value_or_default::<HeadlessMonitor>(property_id!(Self::headless_monitor)),
120            wgt.capture_value_or_default(property_id!(Self::start_focused)),
121            wgt.build(),
122        )
123    }
124}
125
126/// Defines how the window is positioned when it first opens.
127#[property(LAYOUT, capture, widget_impl(Window))]
128pub fn start_position(position: impl IntoValue<StartPosition>) {}
129
130/// If the window is steals keyboard focus on open.
131///
132/// By default the operating system decides if the window will receive focus after opening, usually it is focused
133/// only if the process that started the window already has focus. Enabling this ensures that focus
134/// is moved to the new window, potentially stealing the focus from other apps and disrupting the user.
135#[property(CONTEXT, capture, widget_impl(Window))]
136pub fn start_focused(enabled: impl IntoValue<bool>) {}
137
138/// Lock-in kiosk mode.
139///
140/// In kiosk mode the only window states allowed are fullscreen or fullscreen exclusive, and
141/// all subsequent windows opened are child of the kiosk window.
142///
143/// Note that this does not configure the operating system,
144/// you still need to setup a kiosk environment. This just stops the
145/// app itself from accidentally exiting fullscreen.
146#[property(CONTEXT, capture, widget_impl(Window))]
147pub fn kiosk(kiosk: impl IntoValue<bool>) {}
148
149/// If semi-transparent content is see-through, mixing with the operating system pixels behind the window.
150///
151/// Note that to actually see behind the window you must set the [`clear_color`] and [`background_color`] to a transparent color.
152/// The composition is a simple alpha blend, effects like blur do not apply to the pixels behind the window.
153///
154/// [`clear_color`]: fn@clear_color
155/// [`background_color`]: fn@background_color
156#[property(CONTEXT, capture, widget_impl(Window))]
157pub fn allow_transparency(allow: impl IntoValue<bool>) {}
158
159/// Render performance mode overwrite for this window, if set to `None` the [`WINDOWS.default_render_mode`] is used.
160///
161/// The `view-process` will try to match the mode, if it is not available a fallback mode is selected,
162/// see [`RenderMode`] for more details about each mode and fallbacks.
163///
164/// [`WINDOWS.default_render_mode`]: zng_ext_window::WINDOWS::default_render_mode
165/// [`RenderMode`]: crate::RenderMode
166#[property(CONTEXT, capture, widget_impl(Window))]
167pub fn render_mode(mode: impl IntoValue<Option<RenderMode>>) {}
168
169/// Event just after the window opens.
170///
171/// This event notifies once per window, after the window content is inited.
172///
173/// This property is the same as [`on_pre_window_open`].
174///
175/// [`on_pre_window_open`]: fn@events::on_pre_window_open
176#[property(EVENT, widget_impl(Window))]
177pub fn on_open(child: impl UiNode, handler: impl WidgetHandler<WindowOpenArgs>) -> impl UiNode {
178    events::on_pre_window_open(child, handler)
179}
180
181/// Event just after the window loads.
182///
183/// This event notifies once per window, after the first layout and all [`WindowLoadingHandle`]
184/// have expired or dropped.
185///
186/// This property is the same as [`on_pre_window_load`].
187///
188/// [`WindowLoadingHandle`]: zng_ext_window::WindowLoadingHandle
189/// [`on_pre_window_load`]: fn@events::on_pre_window_load
190#[property(EVENT, widget_impl(Window))]
191pub fn on_load(child: impl UiNode, handler: impl WidgetHandler<WindowOpenArgs>) -> impl UiNode {
192    events::on_pre_window_load(child, handler)
193}
194
195/// On window close requested.
196///
197/// This event notifies every time an attempt to close the window is made. Close can be cancelled by stopping propagation
198/// on the event args, the window only closes after all handlers receive this event and propagation is not stopped.
199///
200/// This property is the same as [`on_window_close_requested`].
201///
202/// [`on_window_close_requested`]: fn@events::on_window_close_requested
203#[property(EVENT, widget_impl(Window))]
204pub fn on_close_requested(child: impl UiNode, handler: impl WidgetHandler<WindowCloseRequestedArgs>) -> impl UiNode {
205    events::on_window_close_requested(child, handler)
206}
207
208/// On window close.
209///
210/// The window will deinit after this event.
211///
212/// This property is the same as [`on_pre_window_close`].
213///
214/// [`on_pre_window_close`]: fn@events::on_pre_window_close
215#[property(EVENT, widget_impl(Window))]
216pub fn on_close(child: impl UiNode, handler: impl WidgetHandler<WindowCloseArgs>) -> impl UiNode {
217    events::on_pre_window_close(child, handler)
218}
219
220/// On window position changed.
221///
222/// This event notifies every time the window position changes. You can also track the window
223/// position using the [`actual_position`] variable.
224///
225/// This property is the same as [`on_pre_window_moved`].
226///
227/// [`actual_position`]: zng_ext_window::WindowVars::actual_position
228/// [`on_pre_window_moved`]: fn@events::on_pre_window_moved
229#[property(EVENT, widget_impl(Window))]
230pub fn on_moved(child: impl UiNode, handler: impl WidgetHandler<WindowChangedArgs>) -> impl UiNode {
231    events::on_pre_window_moved(child, handler)
232}
233
234/// On window size changed.
235///
236/// This event notifies every time the window content area size changes. You can also track
237/// the window size using the [`actual_size`] variable.
238///
239/// This property is the same as [`on_pre_window_resized`].
240///
241/// [`actual_size`]: zng_ext_window::WindowVars::actual_size
242/// [`on_pre_window_resized`]: fn@events::on_pre_window_resized
243#[property(EVENT, widget_impl(Window))]
244pub fn on_resized(child: impl UiNode, handler: impl WidgetHandler<WindowChangedArgs>) -> impl UiNode {
245    events::on_pre_window_resized(child, handler)
246}
247
248/// On window state changed.
249///
250/// This event notifies every time the window state changes.
251///
252/// Note that you can also track the window
253/// state by setting [`state`] to a read-write variable.
254///
255/// This property is the same as [`on_pre_window_state_changed`].
256///
257/// [`state`]: fn@state
258/// [`on_pre_window_state_changed`]: fn@events::on_pre_window_state_changed
259#[property(EVENT, widget_impl(Window))]
260pub fn on_state_changed(child: impl UiNode, handler: impl WidgetHandler<WindowChangedArgs>) -> impl UiNode {
261    events::on_pre_window_state_changed(child, handler)
262}
263
264/// On window maximized.
265///
266/// This event notifies every time the window state changes to maximized.
267///
268/// This property is the same as [`on_pre_window_maximized`].
269///
270/// [`on_pre_window_maximized`]: fn@events::on_pre_window_maximized
271#[property(EVENT, widget_impl(Window))]
272pub fn on_maximized(child: impl UiNode, handler: impl WidgetHandler<WindowChangedArgs>) -> impl UiNode {
273    events::on_pre_window_maximized(child, handler)
274}
275
276/// On window exited the maximized state.
277///
278/// This event notifies every time the window state changes to a different state from maximized.
279///
280/// This property is the same as [`on_pre_window_unmaximized`].
281///
282/// [`on_pre_window_unmaximized`]: fn@events::on_pre_window_unmaximized
283#[property(EVENT, widget_impl(Window))]
284pub fn on_unmaximized(child: impl UiNode, handler: impl WidgetHandler<WindowChangedArgs>) -> impl UiNode {
285    events::on_pre_window_unmaximized(child, handler)
286}
287
288/// On window minimized.
289///
290/// This event notifies every time the window state changes to minimized.
291///
292/// This property is the same as [`on_pre_window_maximized`].
293///
294/// [`on_pre_window_maximized`]: fn@events::on_pre_window_maximized
295#[property(EVENT, widget_impl(Window))]
296pub fn on_minimized(child: impl UiNode, handler: impl WidgetHandler<WindowChangedArgs>) -> impl UiNode {
297    events::on_pre_window_minimized(child, handler)
298}
299
300/// On window exited the minimized state.
301///
302/// This event notifies every time the window state changes to a different state from minimized.
303///
304/// This property is the same as [`on_pre_window_unminimized`].
305///
306/// [`on_pre_window_unminimized`]: fn@events::on_pre_window_unminimized
307#[property(EVENT, widget_impl(Window))]
308pub fn on_unminimized(child: impl UiNode, handler: impl WidgetHandler<WindowChangedArgs>) -> impl UiNode {
309    events::on_pre_window_unminimized(child, handler)
310}
311
312/// On window state changed to [`Normal`].
313///
314/// This event notifies every time the window state changes to [`Normal`].
315///
316/// This property is the same as [`on_pre_window_restored`].
317///
318/// [`Normal`]: zng_ext_window::WindowState::Normal
319/// [`on_pre_window_restored`]: fn@events::on_pre_window_restored
320#[property(EVENT, widget_impl(Window))]
321pub fn on_restored(child: impl UiNode, handler: impl WidgetHandler<WindowChangedArgs>) -> impl UiNode {
322    events::on_pre_window_restored(child, handler)
323}
324
325/// On window enter one of the fullscreen states.
326///
327/// This event notifies every time the window state changes to [`Fullscreen`] or [`Exclusive`].
328///
329/// This property is the same as [`on_pre_window_fullscreen`].
330///
331/// [`Fullscreen`]: zng_ext_window::WindowState::Fullscreen
332/// [`Exclusive`]: zng_ext_window::WindowState::Exclusive
333/// [`on_pre_window_fullscreen`]: fn@events::on_pre_window_fullscreen
334#[property(EVENT, widget_impl(Window))]
335pub fn on_fullscreen(child: impl UiNode, handler: impl WidgetHandler<WindowChangedArgs>) -> impl UiNode {
336    events::on_pre_window_fullscreen(child, handler)
337}
338
339/// On window is no longer fullscreen.
340///
341/// This event notifies every time the window state changes to one that is not fullscreen.
342///
343/// This property is the same as [`on_pre_window_exited_fullscreen`].
344///
345/// [`on_pre_window_exited_fullscreen`]: fn@events::on_pre_window_exited_fullscreen
346#[property(EVENT, widget_impl(Window))]
347pub fn on_exited_fullscreen(child: impl UiNode, handler: impl WidgetHandler<WindowChangedArgs>) -> impl UiNode {
348    events::on_pre_window_exited_fullscreen(child, handler)
349}
350
351/// On window frame rendered.
352///
353/// If [`frame_capture_mode`](fn@frame_capture_mode) is set the image will be available in the event args.
354#[property(EVENT, widget_impl(Window))]
355pub fn on_frame_image_ready(child: impl UiNode, handler: impl WidgetHandler<FrameImageReadyArgs>) -> impl UiNode {
356    events::on_pre_frame_image_ready(child, handler)
357}
358
359/// Imaginary monitor used by the window when it runs in [headless mode](zng_app::window::WindowMode::is_headless).
360#[property(LAYOUT, capture, widget_impl(Window))]
361pub fn headless_monitor(monitor: impl IntoValue<HeadlessMonitor>) {}