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}