zng/window.rs
1#![cfg(feature = "window")]
2
3//! Window service, widget, events, commands and other types.
4//!
5//! The [`Window!`](struct@Window) widget instantiates a window root, the windows service uses the window root as the
6//! root widget of new window.
7//!
8//! The example below declares a window that toggles if it can close.
9//!
10//! ```
11//! # fn main() {}
12//! use zng::prelude::*;
13//!
14//! fn app() {
15//! APP.defaults().run_window(async { window() });
16//! }
17//!
18//! fn window() -> window::WindowRoot {
19//! let allow_close = var(true);
20//! Window! {
21//! on_close_requested = hn!(allow_close, |args: &window::WindowCloseRequestedArgs| {
22//! if !allow_close.get() {
23//! args.propagation().stop();
24//! }
25//! });
26//!
27//! title = "Can I Close?";
28//! child_align = layout::Align::CENTER;
29//! child = Toggle! {
30//! child = Text!(allow_close.map(|a| formatx!("allow close = {a:?}")));
31//! checked = allow_close;
32//! }
33//! }
34//! }
35//! ```
36//!
37//! The [`WINDOWS`] service can be used to open, manage and close windows. The example below
38//! opens a parent and child window.
39//!
40//! ```
41//! use zng::prelude::*;
42//!
43//! fn app() {
44//! APP.defaults().run(async {
45//! let r = WINDOWS.open(async { main_window() });
46//! println!("opened {}", r.wait_rsp().await);
47//! });
48//! }
49//!
50//! fn main_window() -> window::WindowRoot {
51//! Window! {
52//! title = "Main Window";
53//! child_align = layout::Align::CENTER;
54//! child = {
55//! let enabled = var(true);
56//! Button! {
57//! child = Text!("Open/Close Child");
58//! on_click = async_hn!(enabled, |_| {
59//! enabled.set(false);
60//!
61//! if WINDOWS.is_open("child-id") {
62//! if let Ok(r) = WINDOWS.close("child-id") {
63//! r.wait_done().await;
64//! }
65//! } else {
66//! let parent = WINDOW.id();
67//! WINDOWS.open_id(
68//! "child-id",
69//! async move { child_window(parent) }
70//! )
71//! .wait_done()
72//! .await;
73//! }
74//!
75//! enabled.set(true);
76//! });
77//! widget::enabled;
78//! }
79//! }
80//! }
81//! }
82//!
83//! fn child_window(parent: WindowId) -> window::WindowRoot {
84//! Window! {
85//! parent;
86//! title = "Child Window";
87//! size = (200, 100);
88//! child = Button! {
89//! child = Text!("Close");
90//! on_click = hn!(|_| {
91//! let _ = WINDOW.close();
92//! });
93//! };
94//! }
95//! }
96//! # fn main() { }
97//! ```
98//!
99//! # Full API
100//!
101//! See [`zng_ext_window`], [`zng_app::window`] and [`zng_wgt_window`] for the full window API.
102
103pub use zng_app::window::{MonitorId, WINDOW, WindowId, WindowMode};
104
105pub use zng_ext_window::{
106 AppRunWindowExt, AutoSize, CloseWindowResult, FRAME_IMAGE_READY_EVENT, FocusIndicator, FrameCaptureMode, FrameImageReadyArgs,
107 HeadlessAppWindowExt, HeadlessMonitor, IME_EVENT, ImeArgs, MONITORS, MONITORS_CHANGED_EVENT, MonitorInfo, MonitorQuery,
108 MonitorsChangedArgs, ParallelWin, RenderMode, StartPosition, VideoMode, WINDOW_CHANGED_EVENT, WINDOW_CLOSE_EVENT,
109 WINDOW_CLOSE_REQUESTED_EVENT, WINDOW_Ext, WINDOW_LOAD_EVENT, WINDOW_OPEN_EVENT, WINDOWS, WidgetInfoBuilderImeArea, WidgetInfoImeArea,
110 WindowButton, WindowChangedArgs, WindowCloseArgs, WindowCloseRequestedArgs, WindowIcon, WindowLoadingHandle, WindowOpenArgs,
111 WindowRoot, WindowRootExtenderArgs, WindowState, WindowStateAllowed, WindowVars,
112};
113
114/// Window commands.
115pub mod cmd {
116 pub use zng_ext_window::cmd::*;
117
118 #[cfg(feature = "inspector")]
119 pub use zng_wgt_inspector::INSPECT_CMD;
120}
121
122pub use zng_wgt_window::{BlockWindowLoad, Window};
123
124pub use zng_wgt_window::events::{
125 on_frame_image_ready, on_ime, on_pre_frame_image_ready, on_pre_ime, on_pre_window_changed, on_pre_window_close_requested,
126 on_pre_window_exited_fullscreen, on_pre_window_fullscreen, on_pre_window_load, on_pre_window_maximized, on_pre_window_minimized,
127 on_pre_window_moved, on_pre_window_open, on_pre_window_resized, on_pre_window_restored, on_pre_window_state_changed,
128 on_pre_window_unmaximized, on_pre_window_unminimized, on_window_changed, on_window_close_requested, on_window_exited_fullscreen,
129 on_window_fullscreen, on_window_load, on_window_maximized, on_window_minimized, on_window_moved, on_window_open, on_window_resized,
130 on_window_restored, on_window_state_changed, on_window_unmaximized, on_window_unminimized,
131};
132
133/// Debug inspection helpers.
134///
135/// The properties in this module can be set on a window or widget to visualize layout and render internals.
136///
137/// Note that you can also use the [`cmd::INSPECT_CMD`] command to open the Inspector that shows the widget tree and properties.
138///
139/// # Full API
140///
141/// See [`zng_wgt_inspector::debug`] for the full API.
142///
143/// [`cmd::INSPECT_CMD`]: crate::window::cmd::INSPECT_CMD
144#[cfg(feature = "inspector")]
145pub mod inspector {
146 pub use zng_wgt_inspector::debug::{InspectMode, show_bounds, show_center_points, show_directional_query, show_hit_test, show_rows};
147}
148
149/// Default handler registered in mobile platforms.
150///
151/// This is registered on app init for platforms that only support one window, it intercepts headed window open requests after the
152/// first and opens them as a nested modal layer on the main window.
153///
154/// See [`WINDOWS::register_open_nested_handler`] for more details.
155pub fn default_mobile_nested_open_handler(args: &mut zng_ext_window::OpenNestedHandlerArgs) {
156 use crate::prelude::*;
157
158 if !matches!(args.ctx().mode(), WindowMode::Headed) {
159 return;
160 }
161
162 let open: Vec<_> = WINDOWS
163 .widget_trees()
164 .into_iter()
165 .filter(|w| WINDOWS.mode(w.window_id()) == Ok(window::WindowMode::Headed) && WINDOWS.nest_parent(w.window_id()).is_none())
166 .take(2)
167 .collect();
168
169 if open.len() == 1 {
170 let id = args.ctx().id();
171 let vars = args.vars();
172 #[cfg(feature = "image")]
173 let icon = vars.icon();
174 let title = vars.title();
175 let node = task::parking_lot::Mutex::new(Some(args.nest()));
176
177 let host_win_id = open[0].window_id();
178 let host_wgt_id = WidgetId::new_unique();
179 layer::LAYERS_INSERT_CMD.scoped(host_win_id).notify_param((
180 layer::LayerIndex::TOP_MOST,
181 wgt_fn!(|_: ()| {
182 let frame = Container! {
183 layout::margin = 10;
184 layout::align = Align::CENTER;
185 widget::modal = true;
186 #[cfg(feature = "color_filter")]
187 color::filter::drop_shadow = {
188 offset: 4,
189 blur_radius: 6,
190 color: colors::BLACK.with_alpha(50.pct()),
191 };
192 widget::background_color = light_dark(rgb(0.95, 0.95, 0.95), rgb(0.05, 0.05, 0.05));
193 widget::corner_radius = 4;
194 layout::padding = 5;
195 child_top = {
196 node: Container! {
197 #[cfg(feature = "image")]
198 child_start = Image! {
199 layout::size = 24;
200 source = icon.map(|i| match i {
201 WindowIcon::Image(s) => s.clone(),
202 WindowIcon::Default => ImageSource::flood(layout::PxSize::zero(), rgba(0, 0, 0, 0), None),
203 });
204 }, 4;
205 child = Text! {
206 txt = title.clone();
207 txt_align = Align::CENTER;
208 font_weight = FontWeight::BOLD;
209 };
210 #[cfg(feature = "button")]
211 child_end = Button! {
212 style_fn = zng::button::LightStyle!();
213 child = ICONS.get_or("close", || Text!("x"));
214 on_click = hn!(|args: &gesture::ClickArgs| {
215 args.propagation().stop();
216 let _ = WINDOWS.close(id);
217 });
218 }, 4;
219 },
220 spacing: 5,
221 };
222 child = node.lock().take().into_widget();
223 };
224 Container! {
225 id = host_wgt_id;
226 child = frame;
227 widget::background_color = colors::BLACK.with_alpha(20.pct());
228 layout::padding = WINDOWS.vars(host_win_id).unwrap().safe_padding().map_into();
229 }
230 }),
231 ));
232
233 window::WINDOW_CLOSE_EVENT
234 .on_pre_event(app_hn!(|args: &window::WindowCloseArgs, ev: &dyn zng::handler::AppWeakHandle| {
235 if args.windows.contains(&id) {
236 ev.unsubscribe();
237 layer::LAYERS_REMOVE_CMD.scoped(host_win_id).notify_param(host_wgt_id);
238 }
239 }))
240 .perm();
241 }
242}