zng_ext_window/vars.rs
1use core::fmt;
2use std::sync::Arc;
3
4use crate::{AutoSize, CursorSource, MonitorQuery, WindowIcon, WindowInstanceState};
5use zng_app::{
6 widget::{WidgetId, base::Parallel, info::access::AccessEnabled},
7 window::{MonitorId, WINDOW, WindowId},
8};
9use zng_color::LightDark;
10use zng_layout::unit::{
11 Dip, DipPoint, DipRect, DipSideOffsets, DipSize, DipToPx, Factor, FactorUnits, Length, LengthUnits, Point, PxPoint, PxSize, Size,
12};
13use zng_state_map::{StateId, static_id};
14use zng_txt::Txt;
15use zng_unique_id::IdSet;
16use zng_var::{Var, VarValue, merge_var, var, var_from};
17use zng_view_api::{
18 config::{ColorScheme, ColorsConfig},
19 window::{CursorIcon, FocusIndicator, RenderMode, VideoMode, WindowButton, WindowState, WindowStateAll},
20};
21
22#[cfg(feature = "image")]
23use crate::FrameCaptureMode;
24#[cfg(feature = "image")]
25use zng_ext_image::ImageEntry;
26
27pub(crate) struct WindowVarsData {
28 pub(crate) instance_state: Var<WindowInstanceState>,
29
30 pub(crate) chrome: Var<bool>,
31 pub(crate) icon: Var<WindowIcon>,
32 #[cfg(feature = "image")]
33 pub(crate) actual_icon: Var<Option<ImageEntry>>,
34 pub(crate) cursor: Var<CursorSource>,
35 #[cfg(feature = "image")]
36 pub(crate) actual_cursor_img: Var<Option<(ImageEntry, PxPoint)>>,
37 pub(crate) title: Var<Txt>,
38
39 pub(crate) state: Var<WindowState>,
40 pub(crate) focus_indicator: Var<Option<FocusIndicator>>,
41
42 pub(crate) position: Var<Point>,
43 pub(crate) monitor: Var<MonitorQuery>,
44 pub(crate) video_mode: Var<VideoMode>,
45
46 pub(crate) size: Var<Size>,
47 pub(crate) auto_size: Var<AutoSize>,
48 pub(crate) auto_size_origin: Var<Point>,
49 pub(crate) min_size: Var<Size>,
50 pub(crate) max_size: Var<Size>,
51
52 pub(crate) font_size: Var<Length>,
53
54 pub(crate) actual_position: Var<DipPoint>,
55 pub(crate) global_position: Var<PxPoint>,
56 pub(crate) actual_monitor: Var<Option<MonitorId>>,
57 pub(crate) actual_size: Var<DipSize>,
58 pub(crate) actual_min_size: Var<DipSize>,
59 pub(crate) actual_max_size: Var<DipSize>,
60 pub(crate) safe_padding: Var<DipSideOffsets>,
61
62 pub(crate) scale_factor: Var<Factor>,
63
64 pub(crate) restore_state: Var<WindowState>,
65 pub(crate) restore_rect: Var<DipRect>,
66
67 pub(crate) enabled_buttons: Var<WindowButton>,
68
69 pub(crate) resizable: Var<bool>,
70 pub(crate) movable: Var<bool>,
71
72 pub(crate) always_on_top: Var<bool>,
73
74 pub(crate) visible: Var<bool>,
75 pub(crate) taskbar_visible: Var<bool>,
76
77 pub(crate) parent: Var<Option<WindowId>>,
78 pub(crate) nest_parent: Var<Option<WidgetId>>,
79 modal: Var<bool>,
80 pub(crate) children: Var<IdSet<WindowId>>,
81
82 pub(crate) color_scheme: Var<Option<ColorScheme>>,
83 pub(crate) actual_color_scheme: Var<ColorScheme>,
84 pub(crate) accent_color: Var<Option<LightDark>>,
85 pub(crate) actual_accent_color: Var<LightDark>,
86
87 pub(crate) focused: Var<bool>,
88
89 #[cfg(feature = "image")]
90 pub(crate) frame_capture_mode: Var<FrameCaptureMode>,
91 pub(crate) render_mode: Var<RenderMode>,
92
93 pub(crate) access_enabled: Var<AccessEnabled>,
94 pub(crate) system_shutdown_warn: Var<Txt>,
95
96 parallel: Var<Parallel>,
97}
98
99/// Variables that configure the opening or open window.
100///
101/// You can get the vars for any window using [`WINDOWS.vars`].
102///
103/// You can get the vars for the current context window using [`WINDOW.vars`].
104///
105/// [`WINDOWS.vars`]: crate::WINDOWS::vars
106/// [`WINDOW.vars`]: crate::WINDOW_Ext::vars
107#[derive(Clone)]
108pub struct WindowVars(pub(crate) Arc<WindowVarsData>);
109impl fmt::Debug for WindowVars {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 f.debug_tuple("WindowVars").finish_non_exhaustive()
112 }
113}
114impl WindowVars {
115 pub(crate) fn new(default_render_mode: RenderMode, primary_scale_factor: Factor, system_colors: ColorsConfig) -> Self {
116 let vars = Arc::new(WindowVarsData {
117 instance_state: var(WindowInstanceState::Building),
118 chrome: var(true),
119 icon: var(WindowIcon::Default),
120 #[cfg(feature = "image")]
121 actual_icon: var(None),
122 cursor: var_from(CursorIcon::Default),
123 #[cfg(feature = "image")]
124 actual_cursor_img: var(None),
125 title: var(zng_env::about().app.clone()),
126
127 state: var(WindowState::Normal),
128 focus_indicator: var(None),
129
130 position: var(Point::default()),
131 monitor: var(MonitorQuery::default()),
132 video_mode: var(VideoMode::default()),
133 size: var(Size::default()),
134
135 font_size: var(11.pt()),
136
137 actual_position: var(DipPoint::zero()),
138 global_position: var(PxPoint::zero()),
139 actual_monitor: var(None),
140 actual_size: var(DipSize::zero()),
141 actual_min_size: var(DipSize::zero()),
142 actual_max_size: var(DipSize::splat(Dip::MAX)),
143 safe_padding: var(DipSideOffsets::zero()),
144
145 scale_factor: var(primary_scale_factor),
146
147 restore_state: var(WindowState::Normal),
148 restore_rect: var(DipRect::new(
149 DipPoint::new(Dip::new(30), Dip::new(30)),
150 DipSize::new(Dip::new(800), Dip::new(600)),
151 )),
152
153 enabled_buttons: var(WindowButton::all()),
154
155 min_size: var(Size::new(192, 48)),
156 max_size: var(Size::new(100.pct(), 100.pct())),
157
158 auto_size: var(AutoSize::empty()),
159 auto_size_origin: var(Point::top_left()),
160
161 resizable: var(true),
162 movable: var(true),
163
164 always_on_top: var(false),
165
166 visible: var(true),
167 taskbar_visible: var(true),
168
169 parent: var(None),
170 nest_parent: var(None),
171 modal: var(false),
172 children: var(IdSet::default()),
173
174 color_scheme: var(None),
175 actual_color_scheme: var(system_colors.scheme),
176 accent_color: var(None),
177 actual_accent_color: var(system_colors.accent.into()),
178
179 focused: var(false),
180
181 #[cfg(feature = "image")]
182 frame_capture_mode: var(FrameCaptureMode::Sporadic),
183 render_mode: var(default_render_mode),
184
185 access_enabled: var(AccessEnabled::empty()),
186 system_shutdown_warn: var(Txt::from("")),
187
188 parallel: var(Parallel::default()),
189 });
190 Self(vars)
191 }
192
193 /// Require the window vars from the window state.
194 ///
195 /// # Panics
196 ///
197 /// Panics if called in a custom window context that did not setup the variables.
198 pub(crate) fn req() -> Self {
199 WINDOW.req_state(*WINDOW_VARS_ID)
200 }
201
202 /// Window instance state.
203 pub fn instance_state(&self) -> Var<WindowInstanceState> {
204 self.0.instance_state.read_only()
205 }
206
207 /// Defines if the window chrome is visible.
208 ///
209 /// The window chrome is the non-client area of the window, usually a border with resize handles and a title bar.
210 ///
211 /// The default value is `true`.
212 ///
213 /// Note that if the [`WINDOWS.system_chrome`] reports the windowing system prefers a custom chrome **and** does not
214 /// provide one the system chrome is not requested, even if this is `true`. Window widget implementers can use that variable to
215 /// detect when a fallback chrome must be provided.
216 ///
217 /// [`WINDOWS.system_chrome`]: crate::WINDOWS::system_chrome
218 pub fn chrome(&self) -> Var<bool> {
219 self.0.chrome.clone()
220 }
221
222 /// Window icon.
223 ///
224 /// See [`WindowIcon`] for details.
225 ///
226 /// The default value is [`WindowIcon::Default`].
227 ///
228 /// You can retrieve the custom icon image using [`actual_icon`].
229 ///
230 /// [`actual_icon`]: Self::actual_icon
231 pub fn icon(&self) -> Var<WindowIcon> {
232 self.0.icon.clone()
233 }
234
235 /// Window icon image.
236 ///
237 /// This is `None` if [`icon`] is [`WindowIcon::Default`], otherwise it is an [`ImageEntry`]
238 /// reference clone.
239 ///
240 /// [`icon`]: Self::icon
241 /// [`ImageEntry`]: zng_ext_image::ImageEntry
242 #[cfg(feature = "image")]
243 pub fn actual_icon(&self) -> Var<Option<ImageEntry>> {
244 self.0.actual_icon.read_only()
245 }
246
247 /// Window cursor icon and visibility.
248 ///
249 /// See [`CursorSource`] for details.
250 ///
251 /// The default is [`CursorIcon::Default`].
252 ///
253 /// [`CursorIcon`]: zng_view_api::window::CursorIcon
254 /// [`CursorIcon::Default`]: zng_view_api::window::CursorIcon::Default
255 pub fn cursor(&self) -> Var<CursorSource> {
256 self.0.cursor.clone()
257 }
258
259 /// Window custom cursor image.
260 ///
261 /// This is `None` if [`cursor`] is not set to a custom image, otherwise it is an [`ImageEntry`]
262 /// reference clone with computed hotspot [`PxPoint`].
263 ///
264 /// [`cursor`]: Self::cursor
265 /// [`ImageEntry`]: zng_ext_image::ImageEntry
266 /// [`PxPoint`]: zng_layout::unit::PxPoint
267 #[cfg(feature = "image")]
268 pub fn actual_cursor_img(&self) -> Var<Option<(ImageEntry, PxPoint)>> {
269 self.0.actual_cursor_img.read_only()
270 }
271
272 /// Window title text.
273 ///
274 /// The default value is `""`.
275 pub fn title(&self) -> Var<Txt> {
276 self.0.title.clone()
277 }
278
279 /// Window screen state.
280 ///
281 /// Minimized, maximized or fullscreen. See [`WindowState`] for details.
282 ///
283 /// The default value is [`WindowState::Normal`].
284 pub fn state(&self) -> Var<WindowState> {
285 self.0.state.clone()
286 }
287
288 /// Window monitor.
289 ///
290 /// The query selects the monitor to which the [`position`] and [`size`] is relative to.
291 ///
292 /// It evaluate once when the window opens and then once every time the variable updates. You can track
293 /// what the current monitor is by using [`actual_monitor`].
294 ///
295 /// # Behavior After Open
296 ///
297 /// If this variable is changed after the window has opened, and the new query produces a different
298 /// monitor from the [`actual_monitor`] and the window is visible; then the window is moved to
299 /// the new monitor:
300 ///
301 /// * **Maximized**: The window is maximized in the new monitor.
302 /// * **Fullscreen**: The window is fullscreen in the new monitor.
303 /// * **Normal**: The window is centered in the new monitor, keeping the same size, unless the
304 /// [`position`] and [`size`] where set in the same update, in that case these values are used.
305 /// * **Minimized/Hidden**: The window remains hidden, the restore position and size are defined like **Normal**.
306 ///
307 /// [`position`]: WindowVars::position
308 /// [`actual_monitor`]: WindowVars::actual_monitor
309 /// [`size`]: WindowVars::size
310 pub fn monitor(&self) -> Var<MonitorQuery> {
311 self.0.monitor.clone()
312 }
313
314 /// Video mode for exclusive fullscreen.
315 pub fn video_mode(&self) -> Var<VideoMode> {
316 self.0.video_mode.clone()
317 }
318
319 /// Current monitor hosting the window.
320 ///
321 /// This is `None` only if the window has not opened yet (before first render) or if
322 /// no monitors where found in the operating system or if the window is headless without renderer.
323 pub fn actual_monitor(&self) -> Var<Option<MonitorId>> {
324 self.0.actual_monitor.read_only()
325 }
326
327 /// Available video modes in the current monitor.
328 pub fn video_modes(&self) -> Var<Vec<VideoMode>> {
329 self.0.actual_monitor.flat_map(|&m| {
330 m.and_then(|m| super::MONITORS.monitor(m))
331 .unwrap_or_else(super::MonitorInfo::fallback)
332 .video_modes()
333 })
334 }
335
336 /// Current scale factor of the current monitor hosting the window.
337 pub fn scale_factor(&self) -> Var<Factor> {
338 self.0.scale_factor.read_only()
339 }
340
341 /// Window actual position on the [monitor].
342 ///
343 /// This is a read-only variable that tracks the computed position of the window, it updates every
344 /// time the window moves.
345 ///
346 /// The initial value is `(0, 0)`, it starts updating once the window opens. The point
347 /// is relative to the origin of the [monitor].
348 ///
349 /// [monitor]: Self::actual_monitor
350 pub fn actual_position(&self) -> Var<DipPoint> {
351 self.0.actual_position.read_only()
352 }
353
354 /// Window actual position on the virtual screen that encompasses all monitors.
355 ///
356 /// This is a read-only variable that tracks the computed position of the window, it updates every
357 /// time the window moves.
358 ///
359 /// The initial value is `(0, 0)`, it starts updating once the window opens.
360 pub fn global_position(&self) -> Var<PxPoint> {
361 self.0.global_position.read_only()
362 }
363
364 /// Window restore state.
365 ///
366 /// The restore state that the window must be set to be restored, if the [current state] is [`Maximized`], [`Fullscreen`] or [`Exclusive`]
367 /// the restore state is [`Normal`], if the [current state] is [`Minimized`] the restore state is the previous state.
368 ///
369 /// When the restore state is [`Normal`] the [`restore_rect`] defines the window position and size.
370 ///
371 /// [current state]: Self::state
372 /// [`Maximized`]: WindowState::Maximized
373 /// [`Fullscreen`]: WindowState::Fullscreen
374 /// [`Exclusive`]: WindowState::Exclusive
375 /// [`Normal`]: WindowState::Normal
376 /// [`Minimized`]: WindowState::Minimized
377 /// [`restore_rect`]: Self::restore_rect
378 pub fn restore_state(&self) -> Var<WindowState> {
379 self.0.restore_state.read_only()
380 }
381
382 /// Window restore position and size when restoring to [`Normal`].
383 ///
384 /// The restore rectangle is the window position and size when its state is [`Normal`], when the state is not [`Normal`]
385 /// this variable tracks the last normal position and size, it will be the window [`actual_position`] and [`actual_size`] again
386 /// when the state is set back to [`Normal`].
387 ///
388 /// This is a read-only variable, to programmatically set it assign the [`position`] and [`size`] variables. The initial
389 /// value is `(30, 30).at(800, 600)`, it starts updating when the window opens.
390 ///
391 /// Note that to restore the window you only need to set [`state`] to [`restore_state`], if the restore state is [`Normal`]
392 /// this position and size will be applied automatically.
393 ///
394 /// [`Normal`]: WindowState::Normal
395 /// [`actual_position`]: Self::actual_position
396 /// [`actual_size`]: Self::actual_size
397 /// [`position`]: Self::position
398 /// [`size`]: Self::size
399 /// [`monitor`]: Self::monitor
400 /// [`actual_monitor`]: Self::actual_monitor
401 /// [`state`]: Self::state
402 /// [`restore_state`]: Self::restore_state
403 pub fn restore_rect(&self) -> Var<DipRect> {
404 self.0.restore_rect.read_only()
405 }
406
407 /// Window top-left offset on the [`monitor`] when the window is [`Normal`].
408 ///
409 /// Relative values are computed in relation to the [`monitor`] size, updating every time the
410 /// position or monitor variable updates.
411 ///
412 /// When the user moves the window this value is considered stale, when it updates it overwrites the window position again,
413 /// note that the window is only moved if it is in the [`Normal`] state, otherwise only the [`restore_rect`] updates.
414 ///
415 /// When the window is moved by the user this variable does **not** update back, to track the current position of the window
416 /// use [`actual_position`], to track the restore position use [`restore_rect`].
417 ///
418 /// The [`Length::Default`] value causes the OS to select a value.
419 ///
420 /// [`restore_rect`]: WindowVars::restore_rect
421 /// [`actual_position`]: WindowVars::actual_position
422 /// [`monitor`]: WindowVars::monitor
423 /// [`Normal`]: WindowState::Normal
424 /// [`Length::Default`]: zng_layout::unit::Length::Default
425 pub fn position(&self) -> Var<Point> {
426 self.0.position.clone()
427 }
428
429 /// Window actual size on the screen.
430 ///
431 /// This is a read-only variable that tracks the computed size of the window, it updates every time
432 /// the window resizes.
433 ///
434 /// The initial value is `(0, 0)`, it starts updating when the window opens.
435 pub fn actual_size(&self) -> Var<DipSize> {
436 self.0.actual_size.read_only()
437 }
438
439 /// Window [`actual_size`], converted to pixels given the [`scale_factor`].
440 ///
441 /// [`actual_size`]: Self::actual_size
442 /// [`scale_factor`]: Self::scale_factor
443 pub fn actual_size_px(&self) -> Var<PxSize> {
444 merge_var!(self.0.actual_size.clone(), self.0.scale_factor.clone(), |size, factor| {
445 PxSize::new(size.width.to_px(*factor), size.height.to_px(*factor))
446 })
447 }
448
449 /// Window actual min size.
450 ///
451 /// This is a read-only variable that tracks the computed min size of the window.
452 pub fn actual_min_size(&self) -> Var<DipSize> {
453 self.0.actual_min_size.read_only()
454 }
455
456 /// Window actual max size.
457 ///
458 /// This is a read-only variable that tracks the computed min size of the window.
459 pub fn actual_max_size(&self) -> Var<DipSize> {
460 self.0.actual_max_size.read_only()
461 }
462
463 /// Padding that must be applied to the window content so that it stays clear of screen obstructions
464 /// such as a camera notch cutout.
465 ///
466 /// Note that the *unsafe* area must still be rendered as it may be partially visible, just don't place nay
467 /// interactive or important content outside of this padding.
468 pub fn safe_padding(&self) -> Var<DipSideOffsets> {
469 self.0.safe_padding.read_only()
470 }
471
472 /// Window width and height on the screen when the window is [`Normal`].
473 ///
474 /// Relative values are computed in relation to the [`monitor`] size, updating every time the
475 /// size or monitor variable updates.
476 ///
477 /// When the user resizes the window this value is considered stale, when it updates it overwrites the window size again,
478 /// note that the window is only resized if it is in the [`Normal`] state, otherwise only the [`restore_rect`] updates.
479 ///
480 /// When the window is resized this variable is **not** updated back, to track the current window size use [`actual_size`],
481 /// to track the restore size use [`restore_rect`].
482 ///
483 /// The default value is `(800, 600)`.
484 ///
485 /// [`actual_size`]: WindowVars::actual_size
486 /// [`monitor`]: WindowVars::monitor
487 /// [`restore_rect`]: WindowVars::restore_rect
488 /// [`Normal`]: WindowState::Normal
489 pub fn size(&self) -> Var<Size> {
490 self.0.size.clone()
491 }
492
493 /// Defines if and how the window size is controlled by the content layout.
494 ///
495 /// When enabled overwrites previously set window size, but is still coerced by [`min_size`]
496 /// and [`max_size`]. Auto-size is disabled if the user resizes the window or if [`size`]
497 /// is set.
498 ///
499 /// The default value is [`AutoSize::DISABLED`].
500 ///
501 /// [`size`]: Self::size
502 /// [`min_size`]: Self::min_size
503 /// [`max_size`]: Self::max_size
504 pub fn auto_size(&self) -> Var<AutoSize> {
505 self.0.auto_size.clone()
506 }
507
508 /// The point in the window content that does not move when the window is resized by [`auto_size`].
509 ///
510 /// When the window size increases it *grows* to the right-bottom, the top-left corner does not move because
511 /// the origin of windows it at the top-left and the position did not change, this variables overwrites this origin
512 /// for [`auto_size`] size changes, the window position is adjusted so that it is the center of the resize.
513 ///
514 /// Note this only applies to resizes, the initial auto-size when the window opens is positioned according to the [`StartPosition`] value.
515 ///
516 /// The default value is [`Point::top_left`].
517 ///
518 /// [`auto_size`]: Self::auto_size
519 /// [`monitor`]: WindowVars::monitor
520 /// [`StartPosition`]: crate::StartPosition
521 /// [`Point::top_left`]: zng_layout::unit::Point::top_left
522 pub fn auto_size_origin(&self) -> Var<Point> {
523 self.0.auto_size_origin.clone()
524 }
525
526 /// Minimal window width and height constraint on the [`size`].
527 ///
528 /// Relative values are computed in relation to the [`monitor`] size, updating every time the
529 /// size or monitor variable updates.
530 ///
531 /// Note that the OS can also define a minimum size that supersedes this variable.
532 ///
533 /// The default value is `(192, 48)`.
534 ///
535 /// [`monitor`]: WindowVars::monitor
536 /// [`size`]: Self::size
537 pub fn min_size(&self) -> Var<Size> {
538 self.0.min_size.clone()
539 }
540
541 /// Maximal window width and height constraint on the [`size`].
542 ///
543 /// Relative values are computed in relation to the [`monitor`] size, updating every time the
544 /// size or monitor variable updates.
545 ///
546 /// Note that the OS can also define a maximum size that supersedes this variable.
547 ///
548 /// The default value is `(100.pct(), 100.pct())`
549 ///
550 /// [`monitor`]: WindowVars::monitor
551 /// [`size`]: Self::size
552 pub fn max_size(&self) -> Var<Size> {
553 self.0.max_size.clone()
554 }
555
556 /// Root font size.
557 ///
558 /// This is the font size in all widget branches that do not override the font size. The [`rem`] unit is relative to this value.
559 ///
560 /// [`rem`]: LengthUnits::rem
561 pub fn font_size(&self) -> Var<Length> {
562 self.0.font_size.clone()
563 }
564
565 /// Defines if the user can resize the window using the window frame.
566 ///
567 /// Note that even if disabled the window can still be resized from other sources.
568 ///
569 /// The default value is `true`.
570 pub fn resizable(&self) -> Var<bool> {
571 self.0.resizable.clone()
572 }
573
574 /// Defines if the user can move the window using the window frame.
575 ///
576 /// Note that even if disabled the window can still be moved from other sources.
577 ///
578 /// The default value is `true`.
579 pub fn movable(&self) -> Var<bool> {
580 self.0.movable.clone()
581 }
582
583 /// Defines the enabled state of the window chrome buttons.
584 pub fn enabled_buttons(&self) -> Var<WindowButton> {
585 self.0.enabled_buttons.clone()
586 }
587
588 /// Defines if the window should always stay on top of other windows.
589 ///
590 /// Note this only applies to other windows that are not also "always-on-top".
591 ///
592 /// The default value is `false`.
593 pub fn always_on_top(&self) -> Var<bool> {
594 self.0.always_on_top.clone()
595 }
596
597 /// Defines if the window is visible on the screen and in the task-bar.
598 ///
599 /// This variable is observed only after the first frame render, before that the window
600 /// is always not visible.
601 ///
602 /// The default value is `true`.
603 pub fn visible(&self) -> Var<bool> {
604 self.0.visible.clone()
605 }
606
607 /// Defines if the window is visible in the task-bar.
608 ///
609 /// The default value is `true`.
610 pub fn taskbar_visible(&self) -> Var<bool> {
611 self.0.taskbar_visible.clone()
612 }
613
614 /// Defines the parent window.
615 ///
616 /// If a parent is set this behavior applies:
617 ///
618 /// * If the parent is minimized, this window is also minimized.
619 /// * If the parent window is maximized, this window is restored.
620 /// * This window is always on-top of the parent window.
621 /// * If the parent window is closed, this window is also closed.
622 /// * If [`modal`] is set, the parent window cannot be focused while this window is open.
623 /// * If a [`color_scheme`] is not set, the fallback is the parent's actual scheme.
624 /// * If an [`accent_color`] is not set, the fallback is the parent's actual accent.
625 ///
626 /// The default value is `None`.
627 ///
628 /// # Validation
629 ///
630 /// The parent window cannot have a parent, if it has, that parent ID is used instead.
631 /// The parent window must exist. This window (child) cannot have children, it also can't set itself as the parent.
632 ///
633 /// If any of these conditions are not met, an error is logged and the parent var is restored to the previous value.
634 ///
635 /// [`modal`]: Self::modal
636 /// [`color_scheme`]: Self::color_scheme
637 /// [`accent_color`]: Self::accent_color
638 pub fn parent(&self) -> Var<Option<WindowId>> {
639 self.0.parent.clone()
640 }
641
642 /// Gets the widget in [`parent`] that hosts the window, if it is nesting.
643 ///
644 /// Nesting windows are presented as an widget, similar to an "iframe".
645 ///
646 /// [`parent`]: Self::parent
647 pub fn nest_parent(&self) -> Var<Option<WidgetId>> {
648 self.0.nest_parent.read_only()
649 }
650
651 /// Defines the [`parent`](Self::parent) connection.
652 ///
653 /// Value is ignored if `parent` is not set. When this is `true` the parent window cannot be focused while this window is open.
654 ///
655 /// The default value is `false`.
656 pub fn modal(&self) -> Var<bool> {
657 self.0.modal.clone()
658 }
659
660 /// Window children.
661 ///
662 /// This is a set of other windows that have this window as a [`parent`].
663 ///
664 /// [`parent`]: Self::parent
665 pub fn children(&self) -> Var<IdSet<WindowId>> {
666 self.0.children.read_only()
667 }
668
669 /// Override the preferred color scheme.
670 ///
671 /// If set to `None` the system preference is used, see [`actual_color_scheme`].
672 ///
673 /// [`actual_color_scheme`]: Self::actual_color_scheme
674 pub fn color_scheme(&self) -> Var<Option<ColorScheme>> {
675 self.0.color_scheme.clone()
676 }
677
678 /// Actual color scheme to use.
679 ///
680 /// This is the system preference, or [`color_scheme`] if it is set.
681 ///
682 /// [`color_scheme`]: Self::color_scheme
683 pub fn actual_color_scheme(&self) -> Var<ColorScheme> {
684 self.0.actual_color_scheme.read_only()
685 }
686
687 /// Override the preferred accent color.
688 ///
689 /// If set to `None` the system preference is used, see [`actual_accent_color`].
690 ///
691 /// [`actual_accent_color`]: Self::actual_accent_color
692 pub fn accent_color(&self) -> Var<Option<LightDark>> {
693 self.0.accent_color.clone()
694 }
695
696 /// Actual accent color to use.
697 ///
698 /// This is the system preference, or [`color_scheme`] if it is set.
699 ///
700 /// The window widget also sets [`ACCENT_COLOR_VAR`] to this variable.
701 ///
702 /// [`color_scheme`]: Self::color_scheme
703 /// [`ACCENT_COLOR_VAR`]: zng_color::colors::ACCENT_COLOR_VAR
704 pub fn actual_accent_color(&self) -> Var<LightDark> {
705 self.0.actual_accent_color.read_only()
706 }
707
708 /// Read-only variable that tracks if the window is focused in the system window manager.
709 ///
710 /// Note that most of the time its preferable to use the `FOCUS` service as it also tracks the widget focus.
711 pub fn is_focused(&self) -> Var<bool> {
712 self.0.focused.read_only()
713 }
714
715 /// Defines the active user attention required indicator.
716 ///
717 /// This is usually a visual indication on the taskbar icon that prompts the user to focus on the window, it is automatically
718 /// changed to `None` once the window receives focus or you can set it to `None` to cancel the indicator.
719 ///
720 /// Prefer using the `FOCUS` service and advanced `FocusRequest` configs instead of setting this variable directly.
721 pub fn focus_indicator(&self) -> Var<Option<FocusIndicator>> {
722 self.0.focus_indicator.clone()
723 }
724
725 /// Defines if and how the frame pixels are captured for the next rendered frames.
726 ///
727 /// If set to [`Next`] the value will change to [`Sporadic`] after the next frame is rendered.
728 ///
729 /// Note that setting this to [`Next`] does not cause a frame request. Use [`WIDGET.render_update`] for that.
730 ///
731 /// [`Next`]: FrameCaptureMode::Next
732 /// [`Sporadic`]: FrameCaptureMode::Sporadic
733 /// [`WIDGET.render_update`]: zng_app::widget::WIDGET::render_update
734 #[cfg(feature = "image")]
735 pub fn frame_capture_mode(&self) -> Var<FrameCaptureMode> {
736 self.0.frame_capture_mode.clone()
737 }
738
739 /// Window actual render mode.
740 ///
741 /// The initial value is the [`default_render_mode`], it can update after the window is created, when the view-process
742 /// actually creates the backend window and after a view-process respawn.
743 ///
744 /// [`default_render_mode`]: crate::WINDOWS::default_render_mode
745 pub fn render_mode(&self) -> Var<RenderMode> {
746 self.0.render_mode.read_only()
747 }
748
749 /// If an accessibility service has requested info from this window.
750 ///
751 /// You can enable this in the app-process using [`enable_access`], the
752 /// view-process can also enable it on the first request for accessibility info by an external tool.
753 ///
754 /// This variable does not update to fully disabled after first enable, but the VIEW bit can disable and re-enable.
755 ///
756 /// [`enable_access`]: crate::WINDOW_Ext::enable_access
757 pub fn access_enabled(&self) -> Var<AccessEnabled> {
758 self.0.access_enabled.read_only()
759 }
760
761 /// Attempt to set a system wide shutdown warning associated with the window.
762 ///
763 /// Operating systems that support this show the text in a warning for the user, it must be a short text
764 /// that identifies the critical operation that cannot be cancelled.
765 ///
766 /// Set to an empty text to remove the warning.
767 ///
768 /// Note that this does not stop the window from closing or the app from exiting normally, you must
769 /// handle window close requests and show some feedback to the user, the view-process will send a window close
770 /// request when a system shutdown attempt is detected.
771 ///
772 /// Note that there is no guarantee that the view-process or operating system will actually set a block, there
773 /// is no error result because operating systems can silently ignore block requests at any moment, even after
774 /// an initial successful block.
775 ///
776 /// ## Current Limitations
777 ///
778 /// The current `zng::view_process` or `zng-view` only implements this feature on Windows and it will only work properly
779 /// under these conditions:
780 ///
781 /// * It must be running in `run_same_process` mode. Windows kills all other processes, so in a run with `init` the app-process
782 /// will be lost. Note that this also mean that the crash handler and worker processes are also killed.
783 /// * Must be built with `#![windows_subsystem = "windows"]` and must be running from the Windows Explorer (desktop).
784 pub fn system_shutdown_warn(&self) -> Var<Txt> {
785 self.0.system_shutdown_warn.clone()
786 }
787
788 /// Defines what node list methods can run in parallel in the window widgets.
789 ///
790 /// The window binds [`PARALLEL_VAR`] to this value at the root node ensuring all window nodes are
791 /// configured.
792 ///
793 /// See also [`WINDOWS.parallel`] to define parallelization between windows.
794 ///
795 /// [`PARALLEL_VAR`]: zng_app::widget::base::PARALLEL_VAR
796 /// [`WINDOWS.parallel`]: crate::WINDOWS::parallel
797 pub fn parallel(&self) -> Var<Parallel> {
798 self.0.parallel.clone()
799 }
800
801 #[cfg(feature = "image")]
802 pub(crate) fn take_frame_capture(&self) -> zng_view_api::window::FrameCapture {
803 use zng_view_api::window::FrameCapture;
804 match self.0.frame_capture_mode.get() {
805 FrameCaptureMode::Sporadic => FrameCapture::None,
806 FrameCaptureMode::Next => {
807 self.0.frame_capture_mode.set(FrameCaptureMode::Sporadic);
808 FrameCapture::Full
809 }
810 FrameCaptureMode::All => FrameCapture::Full,
811 FrameCaptureMode::NextMask(m) => {
812 self.0.frame_capture_mode.set(FrameCaptureMode::Sporadic);
813 FrameCapture::Mask(m)
814 }
815 FrameCaptureMode::AllMask(m) => FrameCapture::Mask(m),
816 }
817 }
818 #[cfg(not(feature = "image"))]
819 pub(crate) fn take_frame_capture(&self) -> zng_view_api::window::FrameCapture {
820 zng_view_api::window::FrameCapture::None
821 }
822
823 pub(crate) fn window_state_all(&self) -> WindowStateAll {
824 WindowStateAll::new(
825 self.0.state.get(),
826 self.0.global_position.get(),
827 self.0.restore_rect.get(),
828 self.0.restore_state.get(),
829 self.0.actual_min_size.get(),
830 self.0.actual_max_size.get(),
831 self.0.chrome.get(),
832 )
833 }
834
835 pub(crate) fn set_from_view<T: VarValue>(&self, var: impl FnOnce(&Self) -> &Var<T>, new_value: T) {
836 var(self).modify(move |a| {
837 if a.set(new_value) {
838 a.push_tag(SetFromViewTag);
839 }
840 });
841 }
842}
843impl PartialEq for WindowVars {
844 fn eq(&self, other: &Self) -> bool {
845 Arc::ptr_eq(&self.0, &other.0)
846 }
847}
848impl Eq for WindowVars {}
849
850static_id! {
851 pub(crate) static ref WINDOW_VARS_ID: StateId<WindowVars>;
852}
853
854/// Identifies a variable update set from the view-process.
855#[derive(Debug, Clone, Copy, PartialEq, Eq)]
856pub struct SetFromViewTag;
857
858/// Identifies a variable update set from layout, meaning it should not request window layout back.
859#[derive(Debug, Clone, Copy, PartialEq, Eq)]
860pub struct SetFromLayoutTag;