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