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