freya_winit/
window.rs

1use std::{
2    borrow::Cow,
3    path::PathBuf,
4    rc::Rc,
5    sync::Arc,
6    task::Waker,
7};
8
9use accesskit_winit::Adapter;
10use freya_clipboard::copypasta::{
11    ClipboardContext,
12    ClipboardProvider,
13};
14use freya_components::{
15    cache::AssetCacher,
16    integration::integration,
17};
18use freya_core::{
19    integration::*,
20    prelude::Color,
21};
22use freya_engine::prelude::{
23    FontCollection,
24    FontMgr,
25};
26use futures_util::task::{
27    ArcWake,
28    waker,
29};
30use ragnarok::NodesState;
31use raw_window_handle::HasDisplayHandle;
32#[cfg(target_os = "linux")]
33use raw_window_handle::RawDisplayHandle;
34use torin::prelude::{
35    CursorPoint,
36    Size2D,
37};
38use winit::{
39    dpi::LogicalSize,
40    event::ElementState,
41    event_loop::{
42        ActiveEventLoop,
43        EventLoopProxy,
44    },
45    keyboard::ModifiersState,
46    window::{
47        Theme,
48        Window,
49        WindowId,
50    },
51};
52
53use crate::{
54    accessibility::AccessibilityTask,
55    config::WindowConfig,
56    drivers::GraphicsDriver,
57    plugins::{
58        PluginEvent,
59        PluginHandle,
60        PluginsManager,
61    },
62    renderer::{
63        NativeEvent,
64        NativeWindowEvent,
65        NativeWindowEventAction,
66    },
67};
68
69pub struct AppWindow {
70    pub(crate) runner: Runner,
71    pub(crate) tree: Tree,
72    pub(crate) driver: GraphicsDriver,
73    pub(crate) window: Window,
74    pub(crate) nodes_state: NodesState<NodeId>,
75
76    pub(crate) position: CursorPoint,
77    pub(crate) mouse_state: ElementState,
78    pub(crate) modifiers_state: ModifiersState,
79
80    pub(crate) events_receiver: futures_channel::mpsc::UnboundedReceiver<EventsChunk>,
81    pub(crate) events_sender: futures_channel::mpsc::UnboundedSender<EventsChunk>,
82
83    pub(crate) accessibility: AccessibilityTree,
84    pub(crate) accessibility_adapter: accesskit_winit::Adapter,
85    pub(crate) accessibility_tasks_for_next_render: AccessibilityTask,
86
87    pub(crate) process_layout_on_next_render: bool,
88
89    pub(crate) waker: Waker,
90
91    pub(crate) ticker_sender: RenderingTickerSender,
92
93    pub(crate) platform: Platform,
94
95    pub(crate) animation_clock: AnimationClock,
96
97    pub(crate) background: Color,
98
99    pub(crate) dropped_file_paths: Vec<PathBuf>,
100}
101
102impl AppWindow {
103    #[allow(clippy::too_many_arguments)]
104    pub fn new(
105        mut window_config: WindowConfig,
106        active_event_loop: &ActiveEventLoop,
107        event_loop_proxy: &EventLoopProxy<NativeEvent>,
108        plugins: &mut PluginsManager,
109        font_collection: &FontCollection,
110        font_manager: &FontMgr,
111        fallback_fonts: &[Cow<'static, str>],
112        screen_reader: ScreenReader,
113    ) -> Self {
114        let mut window_attributes = Window::default_attributes()
115            .with_resizable(window_config.resizable)
116            .with_window_icon(window_config.icon.take())
117            .with_visible(false)
118            .with_title(window_config.title)
119            .with_decorations(window_config.decorations)
120            .with_transparent(window_config.transparent)
121            .with_inner_size(LogicalSize::<f64>::from(window_config.size));
122
123        if let Some(min_size) = window_config.min_size {
124            window_attributes =
125                window_attributes.with_min_inner_size(LogicalSize::<f64>::from(min_size));
126        }
127        if let Some(max_size) = window_config.max_size {
128            window_attributes =
129                window_attributes.with_max_inner_size(LogicalSize::<f64>::from(max_size));
130        }
131        if let Some(window_attributes_hook) = window_config.window_attributes_hook.take() {
132            window_attributes = window_attributes_hook(window_attributes);
133        }
134        let (driver, mut window) =
135            GraphicsDriver::new(active_event_loop, window_attributes, &window_config);
136
137        if let Some(window_handle_hook) = window_config.window_handle_hook.take() {
138            window_handle_hook(&mut window);
139        }
140
141        let (events_sender, events_receiver) = futures_channel::mpsc::unbounded();
142
143        let mut runner = Runner::new(move || integration(window_config.app.clone()).into_element());
144
145        runner.provide_root_context(|| screen_reader);
146
147        let (mut ticker_sender, ticker) = RenderingTicker::new();
148        ticker_sender.set_overflow(true);
149        runner.provide_root_context(|| ticker);
150
151        let animation_clock = AnimationClock::new();
152        runner.provide_root_context(|| animation_clock.clone());
153
154        runner.provide_root_context(AssetCacher::create);
155        let mut tree = Tree::default();
156
157        let window_size = window.inner_size();
158        let platform = runner.provide_root_context({
159            let event_loop_proxy = event_loop_proxy.clone();
160            let window_id = window.id();
161            let theme = match window.theme() {
162                Some(Theme::Dark) => PreferredTheme::Dark,
163                _ => PreferredTheme::Light,
164            };
165            move || Platform {
166                focused_accessibility_id: State::create(ACCESSIBILITY_ROOT_ID),
167                focused_accessibility_node: State::create(accesskit::Node::new(
168                    accesskit::Role::Window,
169                )),
170                root_size: State::create(Size2D::new(
171                    window_size.width as f32,
172                    window_size.height as f32,
173                )),
174                navigation_mode: State::create(NavigationMode::NotKeyboard),
175                preferred_theme: State::create(theme),
176                sender: Rc::new(move |user_event| {
177                    event_loop_proxy
178                        .send_event(NativeEvent::Window(NativeWindowEvent {
179                            window_id,
180                            action: NativeWindowEventAction::User(user_event),
181                        }))
182                        .unwrap();
183                }),
184            }
185        });
186
187        let clipboard = {
188            if let Ok(handle) = window.display_handle() {
189                #[allow(clippy::match_single_binding)]
190                match handle.as_raw() {
191                    #[cfg(target_os = "linux")]
192                    RawDisplayHandle::Wayland(handle) => {
193                        let (_primary, clipboard) = unsafe {
194                            use freya_clipboard::copypasta::wayland_clipboard;
195
196                            wayland_clipboard::create_clipboards_from_external(
197                                handle.display.as_ptr(),
198                            )
199                        };
200                        let clipboard: Box<dyn ClipboardProvider> = Box::new(clipboard);
201                        Some(clipboard)
202                    }
203                    _ => ClipboardContext::new().ok().map(|c| {
204                        let clipboard: Box<dyn ClipboardProvider> = Box::new(c);
205                        clipboard
206                    }),
207                }
208            } else {
209                None
210            }
211        };
212
213        runner.provide_root_context(|| State::create(clipboard));
214
215        runner.provide_root_context(|| tree.accessibility_generator.clone());
216
217        runner.provide_root_context(|| tree.accessibility_generator.clone());
218
219        let mutations = runner.sync_and_update();
220        tree.apply_mutations(mutations);
221        tree.measure_layout(
222            (
223                window.inner_size().width as f32,
224                window.inner_size().height as f32,
225            )
226                .into(),
227            font_collection,
228            font_manager,
229            &events_sender,
230            window.scale_factor(),
231            fallback_fonts,
232        );
233
234        let nodes_state = NodesState::default();
235
236        let accessibility_adapter =
237            Adapter::with_event_loop_proxy(active_event_loop, &window, event_loop_proxy.clone());
238
239        window.set_visible(true);
240
241        struct TreeHandle(EventLoopProxy<NativeEvent>, WindowId);
242
243        impl ArcWake for TreeHandle {
244            fn wake_by_ref(arc_self: &Arc<Self>) {
245                _ = arc_self
246                    .0
247                    .send_event(NativeEvent::Window(NativeWindowEvent {
248                        window_id: arc_self.1,
249                        action: NativeWindowEventAction::PollRunner,
250                    }));
251            }
252        }
253
254        let waker = waker(Arc::new(TreeHandle(event_loop_proxy.clone(), window.id())));
255
256        plugins.send(
257            PluginEvent::WindowCreated {
258                window: &window,
259                font_collection,
260                tree: &tree,
261                animation_clock: &animation_clock,
262            },
263            PluginHandle::new(event_loop_proxy),
264        );
265
266        AppWindow {
267            runner,
268            tree,
269            driver,
270            window,
271            nodes_state,
272
273            mouse_state: ElementState::Released,
274            position: CursorPoint::default(),
275            modifiers_state: ModifiersState::default(),
276
277            events_receiver,
278            events_sender,
279
280            accessibility: AccessibilityTree::default(),
281            accessibility_adapter,
282            accessibility_tasks_for_next_render: AccessibilityTask::ProcessUpdate { mode: None },
283
284            process_layout_on_next_render: true,
285
286            waker,
287
288            ticker_sender,
289
290            platform,
291
292            animation_clock,
293
294            background: window_config.background,
295
296            dropped_file_paths: Vec::new(),
297        }
298    }
299
300    pub fn window(&self) -> &Window {
301        &self.window
302    }
303
304    pub fn window_mut(&mut self) -> &mut Window {
305        &mut self.window
306    }
307}