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