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