freya_winit/extensions.rs
1use freya_core::{
2 elements::rect::Rect,
3 prelude::{
4 Event,
5 EventHandlersExt,
6 EventsCombos,
7 Platform,
8 PointerEventData,
9 PressEventType,
10 UserEvent,
11 },
12 user_event::SingleThreadErasedEvent,
13};
14use winit::window::{
15 Window,
16 WindowId,
17};
18
19use crate::{
20 config::WindowConfig,
21 renderer::{
22 NativeWindowErasedEventAction,
23 WithWindowCallback,
24 },
25};
26
27/// Extension trait that adds winit-specific window management capabilities to [`Platform`].
28pub trait WinitPlatformExt {
29 /// Dynamically launch a new window at runtime with the given configuration.
30 ///
31 /// This is meant to create windows on the fly after the application has started,
32 /// as opposed to the initial windows registered via [`crate::config::LaunchConfig`].
33 ///
34 /// Returns the [`WindowId`] of the newly created window once it has been created.
35 ///
36 /// # Example
37 ///
38 /// ```rust,no_run
39 /// use freya::prelude::*;
40 ///
41 /// async fn open_new_window() {
42 /// let window_id = Platform::get()
43 /// .launch_window(WindowConfig::new(my_app).with_title("New Window"))
44 /// .await;
45 /// }
46 /// # fn my_app() -> impl IntoElement { rect() }
47 /// ```
48 fn launch_window(&self, window_config: WindowConfig) -> impl Future<Output = WindowId>;
49
50 /// Close an existing window by its [`WindowId`].
51 ///
52 /// # Example
53 ///
54 /// ```rust,no_run
55 /// use freya::{
56 /// prelude::*,
57 /// winit::window::WindowId,
58 /// };
59 ///
60 /// fn close_window(window_id: WindowId) {
61 /// Platform::get().close_window(window_id);
62 /// }
63 /// ```
64 fn close_window(&self, window_id: WindowId);
65
66 /// Focus a window by its [`WindowId`].
67 ///
68 /// If `window_id` is `None`, the current window will be focused.
69 ///
70 /// # Example
71 ///
72 /// ```rust,no_run
73 /// use freya::{
74 /// prelude::*,
75 /// winit::window::WindowId,
76 /// };
77 ///
78 /// fn focus_specific_window(window_id: WindowId) {
79 /// Platform::get().focus_window(Some(window_id));
80 /// }
81 ///
82 /// fn focus_current_window() {
83 /// Platform::get().focus_window(None);
84 /// }
85 /// ```
86 fn focus_window(&self, window_id: Option<WindowId>);
87
88 /// Execute a callback with mutable access to a [`Window`].
89 ///
90 /// If `window_id` is `None`, the callback will be executed on the current window.
91 /// This allows direct manipulation of the underlying winit [`Window`] for advanced use cases.
92 ///
93 /// To create new windows dynamically, see [`WinitPlatformExt::launch_window()`].
94 ///
95 /// # Example
96 ///
97 /// ```rust,no_run
98 /// use freya::{
99 /// prelude::*,
100 /// winit::window::WindowId,
101 /// };
102 ///
103 /// fn set_window_title(window_id: Option<WindowId>, title: &'static str) {
104 /// Platform::get().with_window(window_id, move |window| {
105 /// window.set_title(title);
106 /// });
107 /// }
108 ///
109 /// fn minimize_current_window() {
110 /// Platform::get().with_window(None, |window| {
111 /// window.set_minimized(true);
112 /// });
113 /// }
114 /// ```
115 fn with_window(
116 &self,
117 window_id: Option<WindowId>,
118 callback: impl FnOnce(&mut Window) + 'static,
119 );
120}
121
122pub trait WindowDragExt {
123 fn window_drag(self) -> Self;
124}
125
126impl WindowDragExt for Rect {
127 fn window_drag(self) -> Self {
128 self.on_pointer_down(move |e: Event<PointerEventData>| {
129 match EventsCombos::pressed(e.global_location()) {
130 PressEventType::Single => {
131 Platform::get().with_window(None, |window| {
132 let _ = window.drag_window();
133 });
134 }
135 PressEventType::Double => {
136 Platform::get().with_window(None, |window| {
137 if window.is_maximized() {
138 window.set_maximized(false);
139 } else {
140 window.set_maximized(true);
141 }
142 });
143 }
144 _ => {}
145 }
146 })
147 }
148}
149
150impl WinitPlatformExt for Platform {
151 async fn launch_window(&self, window_config: WindowConfig) -> WindowId {
152 let (tx, rx) = futures_channel::oneshot::channel();
153 self.send(UserEvent::Erased(SingleThreadErasedEvent(Box::new(
154 NativeWindowErasedEventAction::LaunchWindow {
155 window_config,
156 ack: tx,
157 },
158 ))));
159 rx.await.expect("Failed to create Window")
160 }
161
162 fn close_window(&self, window_id: WindowId) {
163 self.send(UserEvent::Erased(SingleThreadErasedEvent(Box::new(
164 NativeWindowErasedEventAction::CloseWindow(window_id),
165 ))));
166 }
167
168 fn focus_window(&self, window_id: Option<WindowId>) {
169 self.send(UserEvent::Erased(SingleThreadErasedEvent(Box::new(
170 NativeWindowErasedEventAction::WithWindow {
171 window_id,
172 callback: WithWindowCallback(Box::new(|window| window.focus_window())),
173 },
174 ))));
175 }
176
177 fn with_window(
178 &self,
179 window_id: Option<WindowId>,
180 callback: impl FnOnce(&mut Window) + 'static,
181 ) {
182 self.send(UserEvent::Erased(SingleThreadErasedEvent(Box::new(
183 NativeWindowErasedEventAction::WithWindow {
184 window_id,
185 callback: WithWindowCallback(Box::new(callback)),
186 },
187 ))));
188 }
189}