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}