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