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 winit::keyboard::Key::Character(character) => {
674 Some(vizia_input::Key::Character(character.to_string()))
675 }
676 winit::keyboard::Key::Unidentified(_) => {
677 Some(vizia_input::Key::Named(vizia_input::NamedKey::Unidentified))
678 }
679 winit::keyboard::Key::Dead(_) => {
680 Some(vizia_input::Key::Named(vizia_input::NamedKey::Dead))
681 }
682 };
683
684 if event.state == ElementState::Pressed {
685 match &event.logical_key {
686 winit::keyboard::Key::Character(character) => {
687 if let Some(character) = character.as_str().chars().next() {
688 self.cx.emit_window_event(
689 window.entity,
690 WindowEvent::CharInput(character),
691 );
692 }
693 }
694 winit::keyboard::Key::Named(winit::keyboard::NamedKey::Space) => {
696 self.cx.emit_window_event(window.entity, WindowEvent::CharInput(' '));
697 }
698 _ => {}
699 }
700 }
701
702 let event = match event.state {
703 winit::event::ElementState::Pressed => WindowEvent::KeyDown(code, key),
704 winit::event::ElementState::Released => WindowEvent::KeyUp(code, key),
705 };
706
707 self.cx.emit_window_event(window.entity, event);
708 }
709 winit::event::WindowEvent::ModifiersChanged(modifiers) => {
710 self.cx.modifiers().set(Modifiers::SHIFT, modifiers.state().shift_key());
711
712 self.cx.modifiers().set(Modifiers::ALT, modifiers.state().alt_key());
713
714 self.cx.modifiers().set(Modifiers::CTRL, modifiers.state().control_key());
715
716 self.cx.modifiers().set(Modifiers::SUPER, modifiers.state().super_key());
717 }
718 winit::event::WindowEvent::Ime(ime) => match ime {
719 winit::event::Ime::Enabled => {
720 self.cx.0.set_ime_state(ImeState::StartComposition);
721 self.cx.emit_window_event(window.entity, WindowEvent::ImeActivate(true));
722 }
723 winit::event::Ime::Preedit(text, cursor) => {
724 self.cx.0.set_ime_state(ImeState::Composing {
725 preedit: Some(text.clone()),
726 cursor_pos: cursor,
727 });
728 self.cx.emit_window_event(window.entity, WindowEvent::ImePreedit(text, cursor));
729 }
730 winit::event::Ime::Commit(text) => {
731 self.cx.0.set_ime_state(ImeState::EndComposition);
732 self.cx.emit_window_event(window.entity, WindowEvent::ImeCommit(text));
733 }
734 winit::event::Ime::Disabled => {
735 self.cx.0.set_ime_state(ImeState::Inactive);
736 self.cx.emit_window_event(window.entity, WindowEvent::ImeActivate(false));
737 }
738 },
739 winit::event::WindowEvent::CursorMoved { device_id: _, position } => {
740 self.cx.emit_window_event(
741 window.entity,
742 WindowEvent::MouseMove(position.x as f32, position.y as f32),
743 );
744 }
745 winit::event::WindowEvent::CursorEntered { device_id: _ } => {
746 self.cx.emit_window_event(window.entity, WindowEvent::MouseEnter);
747 }
748 winit::event::WindowEvent::CursorLeft { device_id: _ } => {
749 self.cx.emit_window_event(window.entity, WindowEvent::MouseLeave);
750 }
751 winit::event::WindowEvent::MouseWheel { device_id: _, delta, phase: _ } => {
752 let out_event = match delta {
753 winit::event::MouseScrollDelta::LineDelta(x, y) => {
754 WindowEvent::MouseScroll(x, y)
755 }
756 winit::event::MouseScrollDelta::PixelDelta(pos) => {
757 WindowEvent::MouseScroll(
758 pos.x as f32 / 20.0,
759 pos.y as f32 / 20.0, )
761 }
762 };
763
764 self.cx.emit_window_event(window.entity, out_event);
765 }
766 winit::event::WindowEvent::MouseInput { device_id: _, state, button } => {
767 let button = match button {
768 winit::event::MouseButton::Left => MouseButton::Left,
769 winit::event::MouseButton::Right => MouseButton::Right,
770 winit::event::MouseButton::Middle => MouseButton::Middle,
771 winit::event::MouseButton::Other(val) => MouseButton::Other(val),
772 winit::event::MouseButton::Back => MouseButton::Back,
773 winit::event::MouseButton::Forward => MouseButton::Forward,
774 };
775
776 let event = match state {
777 winit::event::ElementState::Pressed => WindowEvent::MouseDown(button),
778 winit::event::ElementState::Released => WindowEvent::MouseUp(button),
779 };
780
781 self.cx.emit_window_event(window.entity, event);
782 }
783
784 winit::event::WindowEvent::ScaleFactorChanged {
785 scale_factor,
786 inner_size_writer: _,
787 } => {
788 self.cx.set_scale_factor(scale_factor);
789 self.cx.needs_refresh(window.entity);
790 }
791 winit::event::WindowEvent::ThemeChanged(theme) => {
792 let theme = match theme {
793 winit::window::Theme::Light => ThemeMode::LightMode,
794 winit::window::Theme::Dark => ThemeMode::DarkMode,
795 };
796 self.cx.emit_window_event(window.entity, WindowEvent::ThemeChanged(theme));
797 }
798 winit::event::WindowEvent::Occluded(_) => {}
799 winit::event::WindowEvent::RedrawRequested => {
800 for window in self.windows.values_mut() {
801 window.make_current();
802 if self.cx.draw(window.entity, &mut window.surface, &mut window.dirty_surface) {
804 window.swap_buffers();
805 }
806
807 #[cfg(target_os = "windows")]
809 if window.is_initially_cloaked {
810 window.is_initially_cloaked = false;
811 set_cloak(window.window(), false);
812 }
813 }
814 }
815
816 _ => {}
817 }
818 }
819
820 fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
821 if self.windows.is_empty() {
822 event_loop.exit();
823 return;
824 }
825
826 event_loop.set_control_flow(self.control_flow);
827
828 Runtime::drain_pending_work();
829
830 self.event_manager.flush_events(self.cx.context(), |_| {});
831
832 self.cx.process_style_updates();
833
834 if self.cx.process_animations() {
835 for window in self.windows.values() {
836 window.window().request_redraw();
837 }
838 }
839
840 self.cx.process_visual_updates();
841
842 #[cfg(feature = "accesskit")]
843 {
844 self.cx.process_tree_updates();
845
846 if self.adapter_initialized {
847 for update in self.cx.0.tree_updates.iter_mut() {
848 self.accesskit_adapter
849 .as_mut()
850 .unwrap()
851 .update_if_active(|| update.take().unwrap());
852 }
853 }
854
855 self.cx.0.tree_updates.clear();
856 }
857
858 if let Some(idle_callback) = &self.on_idle {
859 self.cx.set_current(Entity::root());
860 (idle_callback)(self.cx.context());
861 }
862
863 if self.cx.has_queued_events() {
864 self.event_loop_proxy
865 .send_event(UserEvent::Event(ProxyEvent::new(())))
866 .expect("Failed to send event");
867 }
868
869 if self.cx.0.windows.iter().any(|(_, window_state)| !window_state.redraw_list.is_empty()) {
870 for window in self.windows.values() {
871 window.window().request_redraw();
872 }
873 }
874
875 if self.control_flow != ControlFlow::Poll {
876 if let Some(timer_time) = self.cx.get_next_timer_time() {
877 event_loop.set_control_flow(ControlFlow::WaitUntil(timer_time));
878 } else {
879 event_loop.set_control_flow(ControlFlow::Wait);
880 }
881 }
882
883 let window_entities = self
884 .cx
885 .0
886 .windows
887 .iter()
888 .filter_map(|(entity, state)| state.should_close.then_some(*entity))
889 .collect::<Vec<_>>();
890
891 for window_entity in window_entities {
892 self.cx.0.remove(window_entity);
893 }
894
895 self.windows.retain(|_, win| self.cx.0.windows.contains_key(&win.entity));
897 self.window_ids.retain(|e, _| self.cx.0.windows.contains_key(e));
898
899 if self.windows.len() != self.cx.0.windows.len() {
900 for (window_entity, window_state) in self.cx.0.windows.clone().iter() {
901 if !self.window_ids.contains_key(window_entity) {
902 let owner = window_state.owner.and_then(|entity| {
903 self.window_ids
904 .get(&entity)
905 .and_then(|id| self.windows.get(id).map(|ws| ws.window.clone()))
906 });
907
908 let window = self
909 .create_window(
910 event_loop,
911 *window_entity,
912 &window_state.window_description,
913 owner,
914 )
915 .expect("Failed to create window");
916
917 self.cx.add_main_window(
918 *window_entity,
919 &window_state.window_description,
920 window.scale_factor() as f32,
921 );
922
923 window.set_visible(window_state.window_description.visible);
924
925 self.cx.0.with_current(*window_entity, |cx| {
926 if let Some(content) = &window_state.content {
927 (content)(cx)
928 }
929 });
930
931 self.cx.mutate_window(*window_entity, |cx, win: &mut Window| {
932 win.window = Some(window.clone());
933 if let Some(callback) = &win.on_create {
934 (callback)(&mut EventContext::new_with_current(
935 cx.context(),
936 *window_entity,
937 ));
938 }
939 });
940 }
941 }
942 }
943
944 if self.windows.is_empty() {
945 event_loop.exit();
946 }
947 }
948
949 fn new_events(&mut self, _event_loop: &ActiveEventLoop, _cause: winit::event::StartCause) {
950 self.cx.process_timers();
951 self.cx.emit_scheduled_events();
952 }
953
954 fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
955 Runtime::deinit_on_ui_thread();
956 }
957}
958
959impl WindowModifiers for Application {
960 fn title<T: ToStringLocalized>(mut self, title: impl Res<T> + Clone + 'static) -> Self {
961 self.window_description.title = title.get_value(&self.cx.0).to_string_local(&self.cx.0);
962
963 let getter_for_locale = title.clone();
964
965 title.set_or_bind(&mut self.cx.0, move |cx, val| {
966 let title_str = val.get_value(cx).to_string_local(cx);
967
968 cx.emit(WindowEvent::SetTitle(title_str));
969 });
970
971 let locale = self.cx.0.environment().locale;
972 locale.set_or_bind(&mut self.cx.0, move |cx, _| {
973 let title = getter_for_locale.get_value(cx).to_string_local(cx);
974 cx.emit(WindowEvent::SetTitle(title));
975 });
976
977 self
978 }
979
980 fn inner_size<S: Into<WindowSize>>(mut self, size: impl Res<S>) -> Self {
981 self.window_description.inner_size = size.get_value(&self.cx.0).into();
982
983 size.set_or_bind(&mut self.cx.0, |cx, size| {
984 cx.emit(WindowEvent::SetSize(size.get_value(cx).into()));
985 });
986
987 self
988 }
989
990 fn min_inner_size<S: Into<WindowSize>>(mut self, size: impl Res<Option<S>>) -> Self {
991 self.window_description.min_inner_size = size.get_value(&self.cx.0).map(|s| s.into());
992
993 size.set_or_bind(&mut self.cx.0, |cx, size| {
994 cx.emit(WindowEvent::SetMinSize(size.get_value(cx).map(|s| s.into())));
995 });
996
997 self
998 }
999
1000 fn max_inner_size<S: Into<WindowSize>>(mut self, size: impl Res<Option<S>>) -> Self {
1001 self.window_description.max_inner_size = size.get_value(&self.cx.0).map(|s| s.into());
1002
1003 size.set_or_bind(&mut self.cx.0, |cx, size| {
1004 cx.emit(WindowEvent::SetMaxSize(size.get_value(cx).map(|s| s.into())));
1005 });
1006 self
1007 }
1008
1009 fn position<P: Into<WindowPosition>>(mut self, position: impl Res<P>) -> Self {
1010 self.window_description.position = Some(position.get_value(&self.cx.0).into());
1011
1012 position.set_or_bind(&mut self.cx.0, |cx, size| {
1013 cx.emit(WindowEvent::SetPosition(size.get_value(cx).into()));
1014 });
1015
1016 self
1017 }
1018
1019 fn offset<P: Into<WindowPosition>>(mut self, offset: impl Res<P>) -> Self {
1020 self.window_description.offset = Some(offset.get_value(&self.cx.0).into());
1021
1022 self
1023 }
1024
1025 fn anchor<P: Into<Anchor>>(mut self, anchor: impl Res<P>) -> Self {
1026 self.window_description.anchor = Some(anchor.get_value(&self.cx.0).into());
1027
1028 self
1029 }
1030
1031 fn anchor_target<P: Into<AnchorTarget>>(mut self, anchor_target: impl Res<P>) -> Self {
1032 self.window_description.anchor_target = Some(anchor_target.get_value(&self.cx.0).into());
1033
1034 self
1035 }
1036
1037 fn parent_anchor<P: Into<Anchor>>(mut self, parent_anchor: impl Res<P>) -> Self {
1038 self.window_description.parent_anchor = Some(parent_anchor.get_value(&self.cx.0).into());
1039
1040 self
1041 }
1042
1043 fn resizable(mut self, flag: impl Res<bool>) -> Self {
1044 self.window_description.resizable = flag.get_value(&self.cx.0);
1045
1046 flag.set_or_bind(&mut self.cx.0, |cx, flag| {
1047 cx.emit(WindowEvent::SetResizable(flag.get_value(cx)));
1048 });
1049
1050 self
1051 }
1052
1053 fn minimized(mut self, flag: impl Res<bool>) -> Self {
1054 self.window_description.minimized = flag.get_value(&self.cx.0);
1055
1056 flag.set_or_bind(&mut self.cx.0, |cx, flag| {
1057 cx.emit(WindowEvent::SetMinimized(flag.get_value(cx)));
1058 });
1059 self
1060 }
1061
1062 fn maximized(mut self, flag: impl Res<bool>) -> Self {
1063 self.window_description.maximized = flag.get_value(&self.cx.0);
1064
1065 flag.set_or_bind(&mut self.cx.0, |cx, flag| {
1066 cx.emit(WindowEvent::SetMaximized(flag.get_value(cx)));
1067 });
1068
1069 self
1070 }
1071
1072 fn visible(mut self, flag: impl Res<bool>) -> Self {
1073 self.window_description.visible = flag.get_value(&self.cx.0);
1074
1075 flag.set_or_bind(&mut self.cx.0, |cx, flag| {
1076 cx.emit(WindowEvent::SetVisible(flag.get_value(cx)));
1077 });
1078
1079 self
1080 }
1081
1082 fn transparent(mut self, flag: bool) -> Self {
1083 self.window_description.transparent = flag;
1084
1085 self
1086 }
1087
1088 fn decorations(mut self, flag: bool) -> Self {
1089 self.window_description.decorations = flag;
1090
1091 self
1092 }
1093
1094 fn always_on_top(mut self, flag: bool) -> Self {
1095 self.window_description.always_on_top = flag;
1096 self
1097 }
1098
1099 fn vsync(mut self, flag: bool) -> Self {
1100 self.window_description.vsync = flag;
1101
1102 self
1103 }
1104
1105 fn icon(mut self, width: u32, height: u32, image: Vec<u8>) -> Self {
1106 self.window_description.icon = Some(image);
1107 self.window_description.icon_width = width;
1108 self.window_description.icon_height = height;
1109
1110 self
1111 }
1112
1113 fn on_close(self, _callback: impl Fn(&mut EventContext)) -> Self {
1114 self
1115 }
1116
1117 fn on_create(self, _callback: impl Fn(&mut EventContext)) -> Self {
1118 self
1119 }
1120
1121 fn enabled_window_buttons(mut self, window_buttons: WindowButtons) -> Self {
1122 self.window_description.enabled_window_buttons = window_buttons;
1123
1124 self
1125 }
1126}
1127
1128fn apply_window_description(description: &WindowDescription) -> WindowAttributes {
1129 let mut window_attributes = winit::window::Window::default_attributes();
1130
1131 window_attributes = window_attributes.with_title(&description.title).with_inner_size(
1132 LogicalSize::new(description.inner_size.width, description.inner_size.height),
1133 );
1134
1135 if let Some(min_inner_size) = description.min_inner_size {
1136 window_attributes = window_attributes
1137 .with_min_inner_size(LogicalSize::new(min_inner_size.width, min_inner_size.height));
1138 }
1139
1140 if let Some(max_inner_size) = description.max_inner_size {
1141 window_attributes = window_attributes
1142 .with_max_inner_size(LogicalSize::new(max_inner_size.width, max_inner_size.height));
1143 }
1144
1145 if let Some(position) = description.position {
1146 window_attributes =
1147 window_attributes.with_position(LogicalPosition::new(position.x, position.y));
1148 }
1149
1150 window_attributes
1151 .with_resizable(description.resizable)
1152 .with_maximized(description.maximized)
1153 .with_visible(false)
1155 .with_window_level(if description.always_on_top {
1156 WindowLevel::AlwaysOnTop
1157 } else {
1158 WindowLevel::Normal
1159 })
1160 .with_transparent(description.transparent)
1161 .with_decorations(description.decorations)
1162 .with_window_icon(description.icon.as_ref().map(|icon| {
1163 winit::window::Icon::from_rgba(
1164 icon.clone(),
1165 description.icon_width,
1166 description.icon_height,
1167 )
1168 .unwrap()
1169 }))
1170 .with_enabled_buttons(
1171 winit::window::WindowButtons::from_bits(description.enabled_window_buttons.bits())
1172 .unwrap(),
1173 )
1174}
1175
1176#[allow(unused_variables)]
1177pub fn load_default_cursors(event_loop: &ActiveEventLoop) -> HashMap<CursorIcon, CustomCursor> {
1178 #[allow(unused_mut)]
1179 let mut custom_cursors = HashMap::new();
1180
1181 #[cfg(target_os = "windows")]
1182 {
1183 let mut load_cursor = |cursor, bytes, x, y| {
1184 custom_cursors.insert(
1185 cursor,
1186 event_loop.create_custom_cursor(
1187 CustomCursor::from_rgba(bytes, 32, 32, x, y)
1188 .expect("Failed to create custom cursor"),
1189 ),
1190 );
1191 };
1192
1193 load_cursor(
1194 CursorIcon::Alias, include_bytes!("../resources/cursors/windows/aliasb"),
1196 0,
1197 0,
1198 );
1199 load_cursor(
1200 CursorIcon::Cell, include_bytes!("../resources/cursors/windows/cell"),
1202 7,
1203 7,
1204 );
1205 load_cursor(
1206 CursorIcon::ColResize,
1207 include_bytes!("../resources/cursors/windows/col_resize"),
1208 10,
1209 8,
1210 );
1211 load_cursor(
1212 CursorIcon::Copy, include_bytes!("../resources/cursors/windows/copy"),
1214 0,
1215 0,
1216 );
1217 load_cursor(
1218 CursorIcon::Grab, include_bytes!("../resources/cursors/windows/grab"),
1220 6,
1221 0,
1222 );
1223 load_cursor(
1224 CursorIcon::Grabbing, include_bytes!("../resources/cursors/windows/grabbing"),
1226 6,
1227 0,
1228 );
1229 load_cursor(
1230 CursorIcon::RowResize, include_bytes!("../resources/cursors/windows/row_resize"),
1232 9,
1233 10,
1234 );
1235 load_cursor(
1236 CursorIcon::VerticalText, include_bytes!("../resources/cursors/windows/vertical_text"),
1238 9,
1239 3,
1240 );
1241 load_cursor(
1242 CursorIcon::ZoomIn, include_bytes!("../resources/cursors/windows/zoom_in"),
1244 6,
1245 6,
1246 );
1247 load_cursor(
1248 CursorIcon::ZoomOut, include_bytes!("../resources/cursors/windows/zoom_out"),
1250 6,
1251 6,
1252 );
1253 }
1254
1255 custom_cursors
1256}