Skip to main content

vizia_winit/
application.rs

1#[cfg(target_os = "windows")]
2use crate::window::set_cloak;
3use crate::{
4    convert::{winit_key_code_to_code, winit_key_to_key},
5    window::{WinState, Window},
6    window_modifiers::WindowModifiers,
7};
8#[cfg(feature = "accesskit")]
9use accesskit_winit::Adapter;
10#[cfg(all(
11    feature = "clipboard",
12    feature = "wayland",
13    any(
14        target_os = "linux",
15        target_os = "dragonfly",
16        target_os = "freebsd",
17        target_os = "netbsd",
18        target_os = "openbsd"
19    )
20))]
21use copypasta::wayland_clipboard::create_clipboards_from_external;
22use hashbrown::HashMap;
23use log::warn;
24use std::{error::Error, fmt::Display, sync::Arc};
25use vizia_input::ImeState;
26
27// #[cfg(feature = "accesskit")]
28// use accesskit::{Action, NodeBuilder, NodeId, TreeUpdate};
29// #[cfg(feature = "accesskit")]
30// use accesskit_winit;
31// use std::cell::RefCell;
32use vizia_core::context::EventProxy;
33use vizia_core::prelude::*;
34use vizia_core::{backend::*, events::EventManager};
35use vizia_reactive::Runtime;
36use winit::{
37    application::ApplicationHandler,
38    dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize},
39    error::EventLoopError,
40    event::ElementState,
41    event_loop::{ActiveEventLoop, ControlFlow, EventLoop, EventLoopProxy},
42    keyboard::{NativeKeyCode, PhysicalKey},
43    window::{CursorIcon, CustomCursor, WindowAttributes, WindowId, WindowLevel},
44};
45
46#[cfg(all(
47    feature = "clipboard",
48    feature = "wayland",
49    any(
50        target_os = "linux",
51        target_os = "dragonfly",
52        target_os = "freebsd",
53        target_os = "netbsd",
54        target_os = "openbsd"
55    )
56))]
57use winit::raw_window_handle::{HasDisplayHandle, RawDisplayHandle};
58
59// #[cfg(all(
60//     feature = "clipboard",
61//     feature = "wayland",
62//     any(
63//         target_os = "linux",
64//         target_os = "dragonfly",
65//         target_os = "freebsd",
66//         target_os = "netbsd",
67//         target_os = "openbsd"
68//     )
69// ))]
70// use raw_window_handle::{HasRawDisplayHandle, RawDisplayHandle};
71use vizia_window::{Anchor, AnchorTarget, WindowPosition};
72
73#[derive(Debug)]
74pub enum UserEvent {
75    Event(Event),
76    #[cfg(feature = "accesskit")]
77    AccessKitEvent(accesskit_winit::Event),
78}
79
80#[cfg(feature = "accesskit")]
81impl From<accesskit_winit::Event> for UserEvent {
82    fn from(action_request_event: accesskit_winit::Event) -> Self {
83        UserEvent::AccessKitEvent(action_request_event)
84    }
85}
86
87impl From<vizia_core::events::Event> for UserEvent {
88    fn from(event: vizia_core::events::Event) -> Self {
89        UserEvent::Event(event)
90    }
91}
92
93type IdleCallback = Option<Box<dyn Fn(&mut Context)>>;
94
95#[derive(Debug)]
96pub enum ApplicationError {
97    EventLoopError(EventLoopError),
98    LogError,
99}
100
101impl Display for ApplicationError {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        match self {
104            ApplicationError::EventLoopError(ele) => write!(f, "{}", ele),
105            ApplicationError::LogError => write!(f, "log error"),
106        }
107    }
108}
109
110impl std::error::Error for ApplicationError {}
111
112///Creating a new application creates a root `Window` and a `Context`. Views declared within the closure passed to `Application::new()` are added to the context and rendered into the root window.
113///
114/// # Example
115/// ```no_run
116/// # use vizia_core::prelude::*;
117/// # use vizia_winit::application::Application;
118/// Application::new(|cx|{
119///    // Content goes here
120/// })
121/// .run();
122///```
123/// Calling `run()` on the `Application` causes the program to enter the event loop and for the main window to display.
124pub struct Application {
125    cx: BackendContext,
126    event_manager: EventManager,
127    pub(crate) event_loop: Option<EventLoop<UserEvent>>,
128    on_idle: IdleCallback,
129    window_description: WindowDescription,
130    control_flow: ControlFlow,
131    event_loop_proxy: EventLoopProxy<UserEvent>,
132    windows: HashMap<WindowId, WinState>,
133    window_ids: HashMap<Entity, WindowId>,
134    #[cfg(feature = "accesskit")]
135    accesskit_adapter: Option<accesskit_winit::Adapter>,
136    #[cfg(feature = "accesskit")]
137    adapter_initialized: bool,
138}
139
140pub struct WinitEventProxy(EventLoopProxy<UserEvent>);
141
142impl EventProxy for WinitEventProxy {
143    fn send(&self, event: Event) -> Result<(), ()> {
144        self.0.send_event(UserEvent::Event(event)).map_err(|_| ())
145    }
146
147    fn make_clone(&self) -> Box<dyn EventProxy> {
148        Box::new(WinitEventProxy(self.0.clone()))
149    }
150}
151
152impl Application {
153    pub fn new<F>(content: F) -> Self
154    where
155        F: 'static + FnOnce(&mut Context),
156    {
157        let context = Context::new();
158
159        let event_loop =
160            EventLoop::<UserEvent>::with_user_event().build().expect("Failed to create event loop");
161
162        let mut cx = BackendContext::new(context);
163
164        // Mark the current thread as the UI thread so that rayon workers
165        // correctly enqueue effects via SYNC_RUNTIME instead of the
166        // thread-local RUNTIME (which nobody drains).
167        Runtime::init_on_ui_thread();
168
169        let proxy = event_loop.create_proxy();
170        cx.set_event_proxy(Box::new(WinitEventProxy(proxy.clone())));
171
172        // Ensure we wake the event loop when a SyncSignal is mutated off the UI thread.
173        let waker_proxy = proxy.clone();
174        Runtime::set_sync_effect_waker(move || {
175            let _ = waker_proxy.send_event(UserEvent::Event(Event::new(())));
176        });
177
178        cx.renegotiate_language();
179        cx.0.add_built_in_translations();
180        (content)(cx.context());
181
182        Self {
183            cx,
184            event_manager: EventManager::new(),
185            event_loop: Some(event_loop),
186            on_idle: None,
187            window_description: WindowDescription::new(),
188            control_flow: ControlFlow::Wait,
189            event_loop_proxy: proxy,
190            windows: HashMap::new(),
191            window_ids: HashMap::new(),
192            #[cfg(feature = "accesskit")]
193            accesskit_adapter: None,
194            #[cfg(feature = "accesskit")]
195            adapter_initialized: false,
196        }
197    }
198
199    fn create_window(
200        &mut self,
201        event_loop: &ActiveEventLoop,
202        window_entity: Entity,
203        window_description: &WindowDescription,
204        #[allow(unused_variables)] owner: Option<Arc<winit::window::Window>>,
205    ) -> Result<Arc<winit::window::Window>, Box<dyn Error>> {
206        #[allow(unused_mut)]
207        let mut window_attributes = apply_window_description(window_description);
208
209        let window_state = WinState::new(event_loop, window_entity, window_attributes, owner)?;
210        let window = window_state.window.clone();
211
212        if let Some(position) = window_description.position {
213            window.set_outer_position(LogicalPosition::new(position.x, position.y));
214        } else {
215            let (anchor, mut parent_anchor) =
216                match (window_description.anchor, window_description.parent_anchor) {
217                    (Some(a), None) => (Some(a), Some(a)),
218                    (None, Some(b)) => (Some(b.opposite()), Some(b)),
219                    t => t,
220                };
221
222            if let Some(anchor) = anchor {
223                let (y, x) = match anchor {
224                    Anchor::TopLeft => (0.0, 0.0),
225                    Anchor::TopCenter => (0.0, 0.5),
226                    Anchor::TopRight => (0.0, 1.0),
227                    Anchor::Left => (0.5, 0.0),
228                    Anchor::Center => (0.5, 0.5),
229                    Anchor::Right => (0.5, 1.0),
230                    Anchor::BottomLeft => (1.0, 0.0),
231                    Anchor::BottomCenter => (1.0, 0.5),
232                    Anchor::BottomRight => (1.0, 1.0),
233                };
234
235                let window_size = window.inner_size();
236
237                let anchor_target = window_description.anchor_target.unwrap_or_default();
238                let parent = match anchor_target {
239                    AnchorTarget::Monitor => window
240                        .current_monitor()
241                        .map(|monitor| (PhysicalPosition::default(), monitor.size())),
242                    AnchorTarget::Window => self
243                        .cx
244                        .0
245                        .tree
246                        .get_parent_window(window_entity)
247                        .and_then(|parent_window| self.window_ids.get(&parent_window))
248                        .and_then(|id| self.windows.get(id))
249                        .and_then(|WinState { window, .. }| {
250                            let position = window
251                                .outer_position()
252                                .inspect_err(|e| warn!("can't get window position: {e:?}"));
253                            Some((position.ok()?, window.inner_size()))
254                        }),
255                    AnchorTarget::Mouse => self
256                        .cx
257                        .0
258                        .tree
259                        .get_parent_window(window_entity)
260                        .and_then(|parent_window| self.window_ids.get(&parent_window))
261                        .and_then(|id| self.windows.get(id))
262                        .and_then(|WinState { window, .. }| {
263                            window
264                                .outer_position()
265                                .inspect_err(|e| warn!("can't get window position: {e:?}"))
266                                .ok()
267                        })
268                        .map(|pos| {
269                            (
270                                PhysicalPosition::new(
271                                    pos.x + self.cx.0.mouse.cursor_x as i32,
272                                    pos.y + self.cx.0.mouse.cursor_y as i32,
273                                ),
274                                PhysicalSize::new(0, 0),
275                            )
276                        }),
277                };
278
279                if let Some((parent_position, parent_size)) = parent {
280                    if anchor_target != AnchorTarget::Window {
281                        parent_anchor = Some(anchor);
282                    }
283
284                    let (py, px) = match parent_anchor.unwrap_or_default() {
285                        Anchor::TopLeft => (0.0, 0.0),
286                        Anchor::TopCenter => (0.0, 0.5),
287                        Anchor::TopRight => (0.0, 1.0),
288                        Anchor::Left => (0.5, 0.0),
289                        Anchor::Center => (0.5, 0.5),
290                        Anchor::Right => (0.5, 1.0),
291                        Anchor::BottomLeft => (1.0, 0.0),
292                        Anchor::BottomCenter => (1.0, 0.5),
293                        Anchor::BottomRight => (1.0, 1.0),
294                    };
295
296                    let x = (((parent_size.width as f32 * px) as i32
297                        - (window_size.width as f32 * x) as i32)
298                        as f32) as i32;
299                    let y = (((parent_size.height as f32 * py) as i32
300                        - (window_size.height as f32 * y) as i32)
301                        as f32) as i32;
302
303                    let offset = window_description.offset.unwrap_or_default();
304                    let offset: PhysicalPosition<i32> = PhysicalPosition::from_logical(
305                        LogicalPosition::new(offset.x, offset.y),
306                        window.scale_factor(),
307                    );
308
309                    window.set_outer_position(PhysicalPosition::new(
310                        parent_position.x + x + offset.x,
311                        parent_position.y + y + offset.y,
312                    ));
313                }
314            }
315        }
316
317        let window_id = window_state.window.id();
318        self.windows.insert(window_id, window_state);
319        self.window_ids.insert(window_entity, window_id);
320        Ok(window)
321    }
322
323    #[cfg(all(
324        feature = "clipboard",
325        feature = "wayland",
326        any(
327            target_os = "linux",
328            target_os = "dragonfly",
329            target_os = "freebsd",
330            target_os = "netbsd",
331            target_os = "openbsd"
332        )
333    ))]
334    fn init_wayland_clipboard(&mut self, window: &winit::window::Window) {
335        let Ok(display_handle) = window.display_handle() else {
336            return;
337        };
338
339        if let RawDisplayHandle::Wayland(handle) = display_handle.as_raw() {
340            // SAFETY: The display handle comes from a live winit window and remains valid for
341            // at least as long as the window/application lifetime where the provider is used.
342            let (_, clipboard) =
343                unsafe { create_clipboards_from_external(handle.display.as_ptr()) };
344            self.cx.set_clipboard_provider(Box::new(clipboard));
345        }
346    }
347
348    /// Sets the default built-in theming to be ignored.
349    pub fn ignore_default_theme(mut self) -> Self {
350        self.cx.context().ignore_default_theme = true;
351        self
352    }
353
354    pub fn should_poll(mut self) -> Self {
355        self.control_flow = ControlFlow::Poll;
356
357        self
358    }
359
360    /// Takes a closure which will be called at the end of every loop of the application.
361    ///
362    /// The callback provides a place to run 'idle' processing and happens at the end of each loop but before drawing.
363    /// If the callback pushes events into the queue in state then the event loop will re-run. Care must be taken not to
364    /// push events into the queue every time the callback runs unless this is intended.
365    ///
366    /// # Example
367    ///
368    /// ```no_run
369    /// # use vizia_core::prelude::*;
370    /// # use vizia_winit::application::Application;
371    /// #
372    /// Application::new(|cx| {
373    ///     // Build application here
374    /// })
375    /// .on_idle(|cx| {
376    ///     // Code here runs at the end of every event loop after OS and vizia events have been handled
377    /// })
378    /// .run();
379    /// ```
380    pub fn on_idle<F: 'static + Fn(&mut Context)>(mut self, callback: F) -> Self {
381        self.on_idle = Some(Box::new(callback));
382
383        self
384    }
385
386    /// Returns a `ContextProxy` which can be used to send events from another thread.
387    pub fn get_proxy(&self) -> ContextProxy {
388        self.cx.0.get_proxy()
389    }
390
391    pub fn run(mut self) -> Result<(), ApplicationError> {
392        self.event_loop.take().unwrap().run_app(&mut self).map_err(ApplicationError::EventLoopError)
393    }
394}
395
396impl ApplicationHandler<UserEvent> for Application {
397    fn user_event(&mut self, _event_loop: &ActiveEventLoop, user_event: UserEvent) {
398        match user_event {
399            UserEvent::Event(event) => {
400                self.cx.send_event(event);
401            }
402
403            #[cfg(feature = "accesskit")]
404            UserEvent::AccessKitEvent(access_event) => {
405                match access_event.window_event {
406                    accesskit_winit::WindowEvent::InitialTreeRequested => {
407                        let tree_update = self.cx.init_accessibility_tree();
408                        if let Some(adapter) = &mut self.accesskit_adapter {
409                            adapter.update_if_active(|| {
410                                self.adapter_initialized = true;
411                                tree_update
412                            });
413                        }
414                    }
415                    accesskit_winit::WindowEvent::ActionRequested(action_request) => {
416                        let node_id = action_request.target_node;
417
418                        if action_request.action != Action::ScrollIntoView {
419                            let entity = Entity::new(node_id.0, 0);
420
421                            // Handle focus action from screen reader
422                            if action_request.action == Action::Focus {
423                                self.cx.0.with_current(entity, |cx| {
424                                    cx.focus();
425                                });
426                            }
427
428                            self.cx.send_event(
429                                Event::new(WindowEvent::ActionRequest(action_request))
430                                    .direct(entity),
431                            );
432                        }
433                    }
434                    accesskit_winit::WindowEvent::AccessibilityDeactivated => todo!(),
435                }
436            }
437        }
438    }
439
440    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
441        if self.windows.is_empty() {
442            // Create the main window
443            let main_window: Arc<winit::window::Window> = self
444                .create_window(event_loop, Entity::root(), &self.window_description.clone(), None)
445                .expect("failed to create initial window");
446
447            #[cfg(all(
448                feature = "clipboard",
449                feature = "wayland",
450                any(
451                    target_os = "linux",
452                    target_os = "dragonfly",
453                    target_os = "freebsd",
454                    target_os = "netbsd",
455                    target_os = "openbsd"
456                )
457            ))]
458            self.init_wayland_clipboard(&main_window);
459
460            let custom_cursors = Arc::new(load_default_cursors(event_loop));
461            self.cx.add_main_window(
462                Entity::root(),
463                &self.window_description,
464                main_window.scale_factor() as f32,
465            );
466            self.cx.add_window(Window {
467                window: Some(main_window.clone()),
468                on_close: None,
469                on_create: None,
470                should_close: false,
471                custom_cursors: custom_cursors.clone(),
472            });
473
474            self.cx.0.windows.insert(
475                Entity::root(),
476                WindowState {
477                    window_description: self.window_description.clone(),
478                    ..Default::default()
479                },
480            );
481
482            #[cfg(feature = "accesskit")]
483            {
484                self.accesskit_adapter = Some(Adapter::with_event_loop_proxy(
485                    event_loop,
486                    &main_window,
487                    self.event_loop_proxy.clone(),
488                ));
489            }
490
491            main_window.set_visible(self.window_description.visible);
492
493            // set current system theme if available
494            if let Some(theme) = main_window.theme() {
495                let theme = match theme {
496                    winit::window::Theme::Light => ThemeMode::LightMode,
497                    winit::window::Theme::Dark => ThemeMode::DarkMode,
498                };
499                self.cx.emit_origin(WindowEvent::ThemeChanged(theme));
500            }
501
502            self.cx.0.add_built_in_styles();
503
504            // Create any subwindows
505            for (window_entity, window_state) in self.cx.0.windows.clone().into_iter() {
506                if window_entity == Entity::root() {
507                    continue;
508                }
509                let owner = window_state.owner.and_then(|entity| {
510                    self.window_ids
511                        .get(&entity)
512                        .and_then(|id| self.windows.get(id).map(|ws| ws.window.clone()))
513                });
514
515                let window = self
516                    .create_window(
517                        event_loop,
518                        window_entity,
519                        &window_state.window_description,
520                        owner,
521                    )
522                    .expect("Failed to create window");
523
524                self.cx.add_main_window(
525                    window_entity,
526                    &window_state.window_description,
527                    window.scale_factor() as f32,
528                );
529
530                window.set_visible(window_state.window_description.visible);
531
532                self.cx.0.with_current(window_entity, |cx| {
533                    if let Some(content) = &window_state.content {
534                        (content)(cx)
535                    }
536                });
537                self.cx.mutate_window(window_entity, |cx, win: &mut Window| {
538                    win.window = Some(window.clone());
539                    win.custom_cursors = custom_cursors.clone();
540                    if let Some(callback) = &win.on_create {
541                        (callback)(&mut EventContext::new_with_current(
542                            cx.context(),
543                            window_entity,
544                        ));
545                    }
546                });
547                self.cx.needs_refresh(window_entity);
548            }
549        }
550    }
551
552    fn window_event(
553        &mut self,
554        _event_loop: &ActiveEventLoop,
555        window_id: WindowId,
556        event: winit::event::WindowEvent,
557    ) {
558        let window = match self.windows.get_mut(&window_id) {
559            Some(window) => window,
560            None => return,
561        };
562
563        match event {
564            winit::event::WindowEvent::Resized(size) => {
565                window.resize(size);
566                self.cx.set_window_size(window.entity, size.width as f32, size.height as f32);
567                self.cx.needs_refresh(window.entity);
568                window.window().request_redraw();
569
570                #[cfg(target_os = "windows")]
571                {
572                    self.event_manager.flush_events(self.cx.context(), |_| {});
573
574                    self.cx.process_style_updates();
575
576                    if self.cx.process_animations() {
577                        window.window().request_redraw();
578                    }
579
580                    self.cx.process_visual_updates();
581
582                    // #[cfg(feature = "accesskit")]
583
584                    // self.cx.process_tree_updates(|tree_updates| {
585                    //     for update in tree_updates.iter_mut() {
586                    //         self.accesskit_adapter
587                    //             .unwrap()
588                    //             .update_if_active(|| update.take().unwrap());
589                    //     }
590                    // });
591
592                    // for update in self.cx.0.tree_updates.iter_mut() {
593                    //     self.accesskit_adapter
594                    //         .as_mut()
595                    //         .unwrap()
596                    //         .update_if_active(|| update.take().unwrap());
597                    // }
598
599                    // self.cx.0.tree_updates.clear();
600
601                    window.window().request_redraw();
602                }
603            }
604
605            winit::event::WindowEvent::Moved(position) => {
606                let window_entity = window.entity;
607                self.cx.emit_window_event(
608                    window_entity,
609                    WindowEvent::WindowMoved(WindowPosition { x: position.x, y: position.y }),
610                );
611
612                #[cfg(target_os = "windows")]
613                {
614                    self.event_manager.flush_events(self.cx.context(), |_| {});
615
616                    self.cx.process_style_updates();
617
618                    if self.cx.process_animations() {
619                        window.window().request_redraw();
620                    }
621
622                    self.cx.process_visual_updates();
623
624                    // #[cfg(feature = "accesskit")]
625
626                    // self.cx.process_tree_updates(|tree_updates| {
627                    //     for update in tree_updates.iter_mut() {
628                    //         self.accesskit_adapter
629                    //             .unwrap()
630                    //             .update_if_active(|| update.take().unwrap());
631                    //     }
632                    // });
633
634                    // for update in self.cx.0.tree_updates.iter_mut() {
635                    //     self.accesskit_adapter
636                    //         .as_mut()
637                    //         .unwrap()
638                    //         .update_if_active(|| update.take().unwrap());
639                    // }
640
641                    // self.cx.0.tree_updates.clear();
642                }
643            }
644
645            winit::event::WindowEvent::CloseRequested | winit::event::WindowEvent::Destroyed => {
646                let window_entity = window.entity;
647                self.cx.emit_window_event(window_entity, WindowEvent::WindowClose);
648            }
649            winit::event::WindowEvent::DroppedFile(path) => {
650                self.cx.emit_window_event(window.entity, WindowEvent::Drop(DropData::File(path)));
651            }
652
653            winit::event::WindowEvent::HoveredFile(_) => {}
654            winit::event::WindowEvent::HoveredFileCancelled => {}
655            winit::event::WindowEvent::Focused(is_focused) => {
656                self.cx.emit_window_event(window.entity, WindowEvent::WindowFocused(is_focused));
657
658                self.cx.0.window_has_focus = is_focused;
659                // #[cfg(feature = "accesskit")]
660                // accesskit.update_if_active(|| TreeUpdate {
661                //     nodes: vec![],
662                //     tree: None,
663                //     focus: is_focused.then_some(self.cx.focused().accesskit_id()).unwrap_or(NodeId(0)),
664                // });
665            }
666            winit::event::WindowEvent::KeyboardInput { device_id: _, event, is_synthetic: _ } => {
667                let code = match event.physical_key {
668                    PhysicalKey::Code(code) => winit_key_code_to_code(code),
669                    PhysicalKey::Unidentified(native) => match native {
670                        NativeKeyCode::Windows(_scancode) => return,
671                        _ => return,
672                    },
673                };
674
675                let key = match event.logical_key {
676                    winit::keyboard::Key::Named(named_key) => winit_key_to_key(named_key),
677                    _ => None,
678                };
679
680                if event.state == ElementState::Pressed {
681                    match &event.logical_key {
682                        winit::keyboard::Key::Character(character) => {
683                            if let Some(character) = character.as_str().chars().next() {
684                                self.cx.emit_window_event(
685                                    window.entity,
686                                    WindowEvent::CharInput(character),
687                                );
688                            }
689                        }
690                        // Some platforms report space as a named key instead of character text.
691                        winit::keyboard::Key::Named(winit::keyboard::NamedKey::Space) => {
692                            self.cx.emit_window_event(window.entity, WindowEvent::CharInput(' '));
693                        }
694                        _ => {}
695                    }
696                }
697
698                let event = match event.state {
699                    winit::event::ElementState::Pressed => WindowEvent::KeyDown(code, key),
700                    winit::event::ElementState::Released => WindowEvent::KeyUp(code, key),
701                };
702
703                self.cx.emit_window_event(window.entity, event);
704            }
705            winit::event::WindowEvent::ModifiersChanged(modifiers) => {
706                self.cx.modifiers().set(Modifiers::SHIFT, modifiers.state().shift_key());
707
708                self.cx.modifiers().set(Modifiers::ALT, modifiers.state().alt_key());
709
710                self.cx.modifiers().set(Modifiers::CTRL, modifiers.state().control_key());
711
712                self.cx.modifiers().set(Modifiers::SUPER, modifiers.state().super_key());
713            }
714            winit::event::WindowEvent::Ime(ime) => match ime {
715                winit::event::Ime::Enabled => {
716                    self.cx.0.set_ime_state(ImeState::StartComposition);
717                    self.cx.emit_window_event(window.entity, WindowEvent::ImeActivate(true));
718                }
719                winit::event::Ime::Preedit(text, cursor) => {
720                    self.cx.0.set_ime_state(ImeState::Composing {
721                        preedit: Some(text.clone()),
722                        cursor_pos: cursor,
723                    });
724                    self.cx.emit_window_event(window.entity, WindowEvent::ImePreedit(text, cursor));
725                }
726                winit::event::Ime::Commit(text) => {
727                    self.cx.0.set_ime_state(ImeState::EndComposition);
728                    self.cx.emit_window_event(window.entity, WindowEvent::ImeCommit(text));
729                }
730                winit::event::Ime::Disabled => {
731                    self.cx.0.set_ime_state(ImeState::Inactive);
732                    self.cx.emit_window_event(window.entity, WindowEvent::ImeActivate(false));
733                }
734            },
735            winit::event::WindowEvent::CursorMoved { device_id: _, position } => {
736                self.cx.emit_window_event(
737                    window.entity,
738                    WindowEvent::MouseMove(position.x as f32, position.y as f32),
739                );
740            }
741            winit::event::WindowEvent::CursorEntered { device_id: _ } => {
742                self.cx.emit_window_event(window.entity, WindowEvent::MouseEnter);
743            }
744            winit::event::WindowEvent::CursorLeft { device_id: _ } => {
745                self.cx.emit_window_event(window.entity, WindowEvent::MouseLeave);
746            }
747            winit::event::WindowEvent::MouseWheel { device_id: _, delta, phase: _ } => {
748                let out_event = match delta {
749                    winit::event::MouseScrollDelta::LineDelta(x, y) => {
750                        WindowEvent::MouseScroll(x, y)
751                    }
752                    winit::event::MouseScrollDelta::PixelDelta(pos) => {
753                        WindowEvent::MouseScroll(
754                            pos.x as f32 / 20.0,
755                            pos.y as f32 / 20.0, // this number calibrated for wayland
756                        )
757                    }
758                };
759
760                self.cx.emit_window_event(window.entity, out_event);
761            }
762            winit::event::WindowEvent::MouseInput { device_id: _, state, button } => {
763                let button = match button {
764                    winit::event::MouseButton::Left => MouseButton::Left,
765                    winit::event::MouseButton::Right => MouseButton::Right,
766                    winit::event::MouseButton::Middle => MouseButton::Middle,
767                    winit::event::MouseButton::Other(val) => MouseButton::Other(val),
768                    winit::event::MouseButton::Back => MouseButton::Back,
769                    winit::event::MouseButton::Forward => MouseButton::Forward,
770                };
771
772                let event = match state {
773                    winit::event::ElementState::Pressed => WindowEvent::MouseDown(button),
774                    winit::event::ElementState::Released => WindowEvent::MouseUp(button),
775                };
776
777                self.cx.emit_window_event(window.entity, event);
778            }
779
780            winit::event::WindowEvent::ScaleFactorChanged {
781                scale_factor,
782                inner_size_writer: _,
783            } => {
784                self.cx.set_scale_factor(scale_factor);
785                self.cx.needs_refresh(window.entity);
786            }
787            winit::event::WindowEvent::ThemeChanged(theme) => {
788                let theme = match theme {
789                    winit::window::Theme::Light => ThemeMode::LightMode,
790                    winit::window::Theme::Dark => ThemeMode::DarkMode,
791                };
792                self.cx.emit_window_event(window.entity, WindowEvent::ThemeChanged(theme));
793            }
794            winit::event::WindowEvent::Occluded(_) => {}
795            winit::event::WindowEvent::RedrawRequested => {
796                for window in self.windows.values_mut() {
797                    window.make_current();
798                    //self.cx.needs_refresh(window.entity);
799                    if self.cx.draw(window.entity, &mut window.surface, &mut window.dirty_surface) {
800                        window.swap_buffers();
801                    }
802
803                    // Un-cloak
804                    #[cfg(target_os = "windows")]
805                    if window.is_initially_cloaked {
806                        window.is_initially_cloaked = false;
807                        set_cloak(window.window(), false);
808                    }
809                }
810            }
811
812            _ => {}
813        }
814    }
815
816    fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
817        if self.windows.is_empty() {
818            event_loop.exit();
819            return;
820        }
821
822        event_loop.set_control_flow(self.control_flow);
823
824        Runtime::drain_pending_work();
825
826        self.event_manager.flush_events(self.cx.context(), |_| {});
827
828        self.cx.process_style_updates();
829
830        if self.cx.process_animations() {
831            for window in self.windows.values() {
832                window.window().request_redraw();
833            }
834        }
835
836        self.cx.process_visual_updates();
837
838        #[cfg(feature = "accesskit")]
839        {
840            self.cx.process_tree_updates();
841
842            if self.adapter_initialized {
843                for update in self.cx.0.tree_updates.iter_mut() {
844                    self.accesskit_adapter
845                        .as_mut()
846                        .unwrap()
847                        .update_if_active(|| update.take().unwrap());
848                }
849            }
850
851            self.cx.0.tree_updates.clear();
852        }
853
854        if let Some(idle_callback) = &self.on_idle {
855            self.cx.set_current(Entity::root());
856            (idle_callback)(self.cx.context());
857        }
858
859        if self.cx.has_queued_events() {
860            self.event_loop_proxy
861                .send_event(UserEvent::Event(Event::new(())))
862                .expect("Failed to send event");
863        }
864
865        if self.cx.0.windows.iter().any(|(_, window_state)| !window_state.redraw_list.is_empty()) {
866            for window in self.windows.values() {
867                window.window().request_redraw();
868            }
869        }
870
871        if self.control_flow != ControlFlow::Poll {
872            if let Some(timer_time) = self.cx.get_next_timer_time() {
873                event_loop.set_control_flow(ControlFlow::WaitUntil(timer_time));
874            } else {
875                event_loop.set_control_flow(ControlFlow::Wait);
876            }
877        }
878
879        let window_entities = self
880            .cx
881            .0
882            .windows
883            .iter()
884            .filter_map(|(entity, state)| state.should_close.then_some(*entity))
885            .collect::<Vec<_>>();
886
887        for window_entity in window_entities {
888            self.cx.0.remove(window_entity);
889        }
890
891        // Sync window state with context
892        self.windows.retain(|_, win| self.cx.0.windows.contains_key(&win.entity));
893        self.window_ids.retain(|e, _| self.cx.0.windows.contains_key(e));
894
895        if self.windows.len() != self.cx.0.windows.len() {
896            for (window_entity, window_state) in self.cx.0.windows.clone().iter() {
897                if !self.window_ids.contains_key(window_entity) {
898                    let owner = window_state.owner.and_then(|entity| {
899                        self.window_ids
900                            .get(&entity)
901                            .and_then(|id| self.windows.get(id).map(|ws| ws.window.clone()))
902                    });
903
904                    let window = self
905                        .create_window(
906                            event_loop,
907                            *window_entity,
908                            &window_state.window_description,
909                            owner,
910                        )
911                        .expect("Failed to create window");
912
913                    self.cx.add_main_window(
914                        *window_entity,
915                        &window_state.window_description,
916                        window.scale_factor() as f32,
917                    );
918
919                    window.set_visible(window_state.window_description.visible);
920
921                    self.cx.0.with_current(*window_entity, |cx| {
922                        if let Some(content) = &window_state.content {
923                            (content)(cx)
924                        }
925                    });
926
927                    self.cx.mutate_window(*window_entity, |cx, win: &mut Window| {
928                        win.window = Some(window.clone());
929                        if let Some(callback) = &win.on_create {
930                            (callback)(&mut EventContext::new_with_current(
931                                cx.context(),
932                                *window_entity,
933                            ));
934                        }
935                    });
936                }
937            }
938        }
939
940        if self.windows.is_empty() {
941            event_loop.exit();
942        }
943    }
944
945    fn new_events(&mut self, _event_loop: &ActiveEventLoop, _cause: winit::event::StartCause) {
946        self.cx.process_timers();
947        self.cx.emit_scheduled_events();
948    }
949
950    fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
951        Runtime::deinit_on_ui_thread();
952    }
953}
954
955impl WindowModifiers for Application {
956    fn title<T: ToStringLocalized>(mut self, title: impl Res<T> + Clone + 'static) -> Self {
957        self.window_description.title = title.get_value(&self.cx.0).to_string_local(&self.cx.0);
958
959        let getter_for_locale = title.clone();
960
961        title.set_or_bind(&mut self.cx.0, move |cx, val| {
962            let title_str = val.get_value(cx).to_string_local(cx);
963
964            cx.emit(WindowEvent::SetTitle(title_str));
965        });
966
967        let locale = self.cx.0.environment().locale;
968        locale.set_or_bind(&mut self.cx.0, move |cx, _| {
969            let title = getter_for_locale.get_value(cx).to_string_local(cx);
970            cx.emit(WindowEvent::SetTitle(title));
971        });
972
973        self
974    }
975
976    fn inner_size<S: Into<WindowSize>>(mut self, size: impl Res<S>) -> Self {
977        self.window_description.inner_size = size.get_value(&self.cx.0).into();
978
979        size.set_or_bind(&mut self.cx.0, |cx, size| {
980            cx.emit(WindowEvent::SetSize(size.get_value(cx).into()));
981        });
982
983        self
984    }
985
986    fn min_inner_size<S: Into<WindowSize>>(mut self, size: impl Res<Option<S>>) -> Self {
987        self.window_description.min_inner_size = size.get_value(&self.cx.0).map(|s| s.into());
988
989        size.set_or_bind(&mut self.cx.0, |cx, size| {
990            cx.emit(WindowEvent::SetMinSize(size.get_value(cx).map(|s| s.into())));
991        });
992
993        self
994    }
995
996    fn max_inner_size<S: Into<WindowSize>>(mut self, size: impl Res<Option<S>>) -> Self {
997        self.window_description.max_inner_size = size.get_value(&self.cx.0).map(|s| s.into());
998
999        size.set_or_bind(&mut self.cx.0, |cx, size| {
1000            cx.emit(WindowEvent::SetMaxSize(size.get_value(cx).map(|s| s.into())));
1001        });
1002        self
1003    }
1004
1005    fn position<P: Into<WindowPosition>>(mut self, position: impl Res<P>) -> Self {
1006        self.window_description.position = Some(position.get_value(&self.cx.0).into());
1007
1008        position.set_or_bind(&mut self.cx.0, |cx, size| {
1009            cx.emit(WindowEvent::SetPosition(size.get_value(cx).into()));
1010        });
1011
1012        self
1013    }
1014
1015    fn offset<P: Into<WindowPosition>>(mut self, offset: impl Res<P>) -> Self {
1016        self.window_description.offset = Some(offset.get_value(&self.cx.0).into());
1017
1018        self
1019    }
1020
1021    fn anchor<P: Into<Anchor>>(mut self, anchor: impl Res<P>) -> Self {
1022        self.window_description.anchor = Some(anchor.get_value(&self.cx.0).into());
1023
1024        self
1025    }
1026
1027    fn anchor_target<P: Into<AnchorTarget>>(mut self, anchor_target: impl Res<P>) -> Self {
1028        self.window_description.anchor_target = Some(anchor_target.get_value(&self.cx.0).into());
1029
1030        self
1031    }
1032
1033    fn parent_anchor<P: Into<Anchor>>(mut self, parent_anchor: impl Res<P>) -> Self {
1034        self.window_description.parent_anchor = Some(parent_anchor.get_value(&self.cx.0).into());
1035
1036        self
1037    }
1038
1039    fn resizable(mut self, flag: impl Res<bool>) -> Self {
1040        self.window_description.resizable = flag.get_value(&self.cx.0);
1041
1042        flag.set_or_bind(&mut self.cx.0, |cx, flag| {
1043            cx.emit(WindowEvent::SetResizable(flag.get_value(cx)));
1044        });
1045
1046        self
1047    }
1048
1049    fn minimized(mut self, flag: impl Res<bool>) -> Self {
1050        self.window_description.minimized = flag.get_value(&self.cx.0);
1051
1052        flag.set_or_bind(&mut self.cx.0, |cx, flag| {
1053            cx.emit(WindowEvent::SetMinimized(flag.get_value(cx)));
1054        });
1055        self
1056    }
1057
1058    fn maximized(mut self, flag: impl Res<bool>) -> Self {
1059        self.window_description.maximized = flag.get_value(&self.cx.0);
1060
1061        flag.set_or_bind(&mut self.cx.0, |cx, flag| {
1062            cx.emit(WindowEvent::SetMaximized(flag.get_value(cx)));
1063        });
1064
1065        self
1066    }
1067
1068    fn visible(mut self, flag: impl Res<bool>) -> Self {
1069        self.window_description.visible = flag.get_value(&self.cx.0);
1070
1071        flag.set_or_bind(&mut self.cx.0, |cx, flag| {
1072            cx.emit(WindowEvent::SetVisible(flag.get_value(cx)));
1073        });
1074
1075        self
1076    }
1077
1078    fn transparent(mut self, flag: bool) -> Self {
1079        self.window_description.transparent = flag;
1080
1081        self
1082    }
1083
1084    fn decorations(mut self, flag: bool) -> Self {
1085        self.window_description.decorations = flag;
1086
1087        self
1088    }
1089
1090    fn always_on_top(mut self, flag: bool) -> Self {
1091        self.window_description.always_on_top = flag;
1092        self
1093    }
1094
1095    fn vsync(mut self, flag: bool) -> Self {
1096        self.window_description.vsync = flag;
1097
1098        self
1099    }
1100
1101    fn icon(mut self, width: u32, height: u32, image: Vec<u8>) -> Self {
1102        self.window_description.icon = Some(image);
1103        self.window_description.icon_width = width;
1104        self.window_description.icon_height = height;
1105
1106        self
1107    }
1108
1109    fn on_close(self, _callback: impl Fn(&mut EventContext)) -> Self {
1110        self
1111    }
1112
1113    fn on_create(self, _callback: impl Fn(&mut EventContext)) -> Self {
1114        self
1115    }
1116
1117    fn enabled_window_buttons(mut self, window_buttons: WindowButtons) -> Self {
1118        self.window_description.enabled_window_buttons = window_buttons;
1119
1120        self
1121    }
1122}
1123
1124fn apply_window_description(description: &WindowDescription) -> WindowAttributes {
1125    let mut window_attributes = winit::window::Window::default_attributes();
1126
1127    window_attributes = window_attributes.with_title(&description.title).with_inner_size(
1128        LogicalSize::new(description.inner_size.width, description.inner_size.height),
1129    );
1130
1131    if let Some(min_inner_size) = description.min_inner_size {
1132        window_attributes = window_attributes
1133            .with_min_inner_size(LogicalSize::new(min_inner_size.width, min_inner_size.height));
1134    }
1135
1136    if let Some(max_inner_size) = description.max_inner_size {
1137        window_attributes = window_attributes
1138            .with_max_inner_size(LogicalSize::new(max_inner_size.width, max_inner_size.height));
1139    }
1140
1141    if let Some(position) = description.position {
1142        window_attributes =
1143            window_attributes.with_position(LogicalPosition::new(position.x, position.y));
1144    }
1145
1146    window_attributes
1147        .with_resizable(description.resizable)
1148        .with_maximized(description.maximized)
1149        // Accesskit requires that the window start invisible until accesskit is initialized.
1150        .with_visible(false)
1151        .with_window_level(if description.always_on_top {
1152            WindowLevel::AlwaysOnTop
1153        } else {
1154            WindowLevel::Normal
1155        })
1156        .with_transparent(description.transparent)
1157        .with_decorations(description.decorations)
1158        .with_window_icon(description.icon.as_ref().map(|icon| {
1159            winit::window::Icon::from_rgba(
1160                icon.clone(),
1161                description.icon_width,
1162                description.icon_height,
1163            )
1164            .unwrap()
1165        }))
1166        .with_enabled_buttons(
1167            winit::window::WindowButtons::from_bits(description.enabled_window_buttons.bits())
1168                .unwrap(),
1169        )
1170}
1171
1172#[allow(unused_variables)]
1173pub fn load_default_cursors(event_loop: &ActiveEventLoop) -> HashMap<CursorIcon, CustomCursor> {
1174    #[allow(unused_mut)]
1175    let mut custom_cursors = HashMap::new();
1176
1177    #[cfg(target_os = "windows")]
1178    {
1179        let mut load_cursor = |cursor, bytes, x, y| {
1180            custom_cursors.insert(
1181                cursor,
1182                event_loop.create_custom_cursor(
1183                    CustomCursor::from_rgba(bytes, 32, 32, x, y)
1184                        .expect("Failed to create custom cursor"),
1185                ),
1186            );
1187        };
1188
1189        load_cursor(
1190            CursorIcon::Alias, //
1191            include_bytes!("../resources/cursors/windows/aliasb"),
1192            0,
1193            0,
1194        );
1195        load_cursor(
1196            CursorIcon::Cell, //
1197            include_bytes!("../resources/cursors/windows/cell"),
1198            7,
1199            7,
1200        );
1201        load_cursor(
1202            CursorIcon::ColResize,
1203            include_bytes!("../resources/cursors/windows/col_resize"),
1204            10,
1205            8,
1206        );
1207        load_cursor(
1208            CursorIcon::Copy, //
1209            include_bytes!("../resources/cursors/windows/copy"),
1210            0,
1211            0,
1212        );
1213        load_cursor(
1214            CursorIcon::Grab, //
1215            include_bytes!("../resources/cursors/windows/grab"),
1216            6,
1217            0,
1218        );
1219        load_cursor(
1220            CursorIcon::Grabbing, //
1221            include_bytes!("../resources/cursors/windows/grabbing"),
1222            6,
1223            0,
1224        );
1225        load_cursor(
1226            CursorIcon::RowResize, //
1227            include_bytes!("../resources/cursors/windows/row_resize"),
1228            9,
1229            10,
1230        );
1231        load_cursor(
1232            CursorIcon::VerticalText, //
1233            include_bytes!("../resources/cursors/windows/vertical_text"),
1234            9,
1235            3,
1236        );
1237        load_cursor(
1238            CursorIcon::ZoomIn, //
1239            include_bytes!("../resources/cursors/windows/zoom_in"),
1240            6,
1241            6,
1242        );
1243        load_cursor(
1244            CursorIcon::ZoomOut, //
1245            include_bytes!("../resources/cursors/windows/zoom_out"),
1246            6,
1247            6,
1248        );
1249    }
1250
1251    custom_cursors
1252}