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
27use 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
59use 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
112pub 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 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 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 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 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 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 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 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 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 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 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 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 }
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 }
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 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, )
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 if self.cx.draw(window.entity, &mut window.surface, &mut window.dirty_surface) {
800 window.swap_buffers();
801 }
802
803 #[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 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 .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, include_bytes!("../resources/cursors/windows/aliasb"),
1192 0,
1193 0,
1194 );
1195 load_cursor(
1196 CursorIcon::Cell, 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, include_bytes!("../resources/cursors/windows/copy"),
1210 0,
1211 0,
1212 );
1213 load_cursor(
1214 CursorIcon::Grab, include_bytes!("../resources/cursors/windows/grab"),
1216 6,
1217 0,
1218 );
1219 load_cursor(
1220 CursorIcon::Grabbing, include_bytes!("../resources/cursors/windows/grabbing"),
1222 6,
1223 0,
1224 );
1225 load_cursor(
1226 CursorIcon::RowResize, include_bytes!("../resources/cursors/windows/row_resize"),
1228 9,
1229 10,
1230 );
1231 load_cursor(
1232 CursorIcon::VerticalText, include_bytes!("../resources/cursors/windows/vertical_text"),
1234 9,
1235 3,
1236 );
1237 load_cursor(
1238 CursorIcon::ZoomIn, include_bytes!("../resources/cursors/windows/zoom_in"),
1240 6,
1241 6,
1242 );
1243 load_cursor(
1244 CursorIcon::ZoomOut, include_bytes!("../resources/cursors/windows/zoom_out"),
1246 6,
1247 6,
1248 );
1249 }
1250
1251 custom_cursors
1252}