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