Skip to main content

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}