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