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