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