freya_winit/
window.rs

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