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}