1#![warn(missing_docs)]
5use crate::EventResult;
11use crate::drag_resize_window::{handle_cursor_move_for_resize, handle_resize};
12use crate::winitwindowadapter::WindowVisibility;
13use crate::{SharedBackendData, SlintEvent};
14use corelib::graphics::euclid;
15use corelib::input::{KeyEvent, KeyEventType, MouseEvent};
16use corelib::items::{ColorScheme, PointerEventButton};
17use corelib::lengths::LogicalPoint;
18use corelib::platform::PlatformError;
19use corelib::window::*;
20use i_slint_core as corelib;
21
22#[allow(unused_imports)]
23use std::cell::{RefCell, RefMut};
24use std::rc::Rc;
25use winit::event::WindowEvent;
26use winit::event_loop::ActiveEventLoop;
27use winit::event_loop::ControlFlow;
28use winit::window::ResizeDirection;
29
30pub enum CustomEvent {
33 #[cfg(target_arch = "wasm32")]
36 WakeEventLoopWorkaround,
37 UserEvent(Box<dyn FnOnce() + Send>),
39 Exit,
40 #[cfg(enable_accesskit)]
41 Accesskit(accesskit_winit::Event),
42 #[cfg(muda)]
43 Muda(muda::MenuEvent),
44}
45
46impl std::fmt::Debug for CustomEvent {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 match self {
49 #[cfg(target_arch = "wasm32")]
50 Self::WakeEventLoopWorkaround => write!(f, "WakeEventLoopWorkaround"),
51 Self::UserEvent(_) => write!(f, "UserEvent"),
52 Self::Exit => write!(f, "Exit"),
53 #[cfg(enable_accesskit)]
54 Self::Accesskit(a) => write!(f, "AccessKit({a:?})"),
55 #[cfg(muda)]
56 Self::Muda(e) => write!(f, "Muda({e:?})"),
57 }
58 }
59}
60
61pub struct EventLoopState {
62 shared_backend_data: Rc<SharedBackendData>,
63 cursor_pos: LogicalPoint,
65 pressed: bool,
66 current_touch_id: Option<u64>,
67
68 loop_error: Option<PlatformError>,
69 current_resize_direction: Option<ResizeDirection>,
70
71 pumping_events_instantly: bool,
73
74 custom_application_handler: Option<Box<dyn crate::CustomApplicationHandler>>,
75}
76
77impl EventLoopState {
78 pub fn new(
79 shared_backend_data: Rc<SharedBackendData>,
80 custom_application_handler: Option<Box<dyn crate::CustomApplicationHandler>>,
81 ) -> Self {
82 Self {
83 shared_backend_data,
84 cursor_pos: Default::default(),
85 pressed: Default::default(),
86 current_touch_id: Default::default(),
87 loop_error: Default::default(),
88 current_resize_direction: Default::default(),
89 pumping_events_instantly: Default::default(),
90 custom_application_handler,
91 }
92 }
93
94 fn suspend_all_hidden_windows(&self) {
97 let windows_to_suspend = self
98 .shared_backend_data
99 .active_windows
100 .borrow()
101 .values()
102 .filter_map(|w| w.upgrade())
103 .filter(|w| matches!(w.visibility(), WindowVisibility::Hidden))
104 .collect::<Vec<_>>();
105 for window in windows_to_suspend.into_iter() {
106 let _ = window.suspend();
107 }
108 }
109}
110
111impl winit::application::ApplicationHandler<SlintEvent> for EventLoopState {
112 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
113 if matches!(
114 self.custom_application_handler
115 .as_mut()
116 .map_or(EventResult::Propagate, |handler| { handler.resumed(event_loop) }),
117 EventResult::PreventDefault
118 ) {
119 return;
120 }
121 if let Err(err) = self.shared_backend_data.create_inactive_windows(event_loop) {
122 self.loop_error = Some(err);
123 event_loop.exit();
124 }
125 }
126
127 fn window_event(
128 &mut self,
129 event_loop: &ActiveEventLoop,
130 window_id: winit::window::WindowId,
131 event: WindowEvent,
132 ) {
133 let Some(window) = self.shared_backend_data.window_by_id(window_id) else {
134 if let Some(handler) = self.custom_application_handler.as_mut() {
135 handler.window_event(event_loop, window_id, None, None, &event);
136 }
137 return;
138 };
139
140 if let Some(winit_window) = window.winit_window() {
141 if matches!(
142 self.custom_application_handler.as_mut().map_or(
143 EventResult::Propagate,
144 |handler| handler.window_event(
145 event_loop,
146 window_id,
147 Some(&*winit_window),
148 Some(window.window()),
149 &event
150 )
151 ),
152 EventResult::PreventDefault
153 ) {
154 return;
155 }
156
157 if let Some(mut window_event_filter) = window.window_event_filter.take() {
158 let event_result = window_event_filter(window.window(), &event);
159 window.window_event_filter.set(Some(window_event_filter));
160
161 match event_result {
162 EventResult::PreventDefault => return,
163 EventResult::Propagate => (),
164 }
165 }
166
167 #[cfg(enable_accesskit)]
168 window
169 .accesskit_adapter()
170 .expect("internal error: accesskit adapter must exist when window exists")
171 .borrow_mut()
172 .process_event(&winit_window, &event);
173 } else {
174 return;
175 }
176
177 let runtime_window = WindowInner::from_pub(window.window());
178 match event {
179 WindowEvent::RedrawRequested => {
180 self.loop_error = window.draw().err();
181 }
182 WindowEvent::Resized(size) => {
183 self.loop_error = window.resize_event(size).err();
184
185 window.window_state_event();
191
192 #[cfg(target_os = "windows")]
195 {
196 if size.width == 0 || size.height == 0 {
197 window.renderer.occluded(true);
198 }
199 }
200 }
201 WindowEvent::CloseRequested => {
202 self.loop_error = window
203 .window()
204 .try_dispatch_event(corelib::platform::WindowEvent::CloseRequested)
205 .err();
206 }
207 WindowEvent::Focused(have_focus) => {
208 let have_focus = if cfg!(target_os = "macos") {
210 window.winit_window().map_or(have_focus, |w| w.has_focus())
211 } else {
212 have_focus
213 };
214 self.loop_error = window.activation_changed(have_focus).err();
215 }
216
217 WindowEvent::KeyboardInput { event, is_synthetic, .. } => {
218 let key_code = event.logical_key;
219 let swap_cmd_ctrl = i_slint_core::is_apple_platform();
221
222 let key_code = if swap_cmd_ctrl {
223 #[cfg_attr(slint_nightly_test, allow(non_exhaustive_omitted_patterns))]
224 match key_code {
225 winit::keyboard::Key::Named(winit::keyboard::NamedKey::Control) => {
226 winit::keyboard::Key::Named(winit::keyboard::NamedKey::Super)
227 }
228 winit::keyboard::Key::Named(winit::keyboard::NamedKey::Super) => {
229 winit::keyboard::Key::Named(winit::keyboard::NamedKey::Control)
230 }
231 code => code,
232 }
233 } else {
234 key_code
235 };
236
237 macro_rules! winit_key_to_char {
238 ($($char:literal # $name:ident # $($_qt:ident)|* # $($winit:ident $(($pos:ident))?)|* # $($_xkb:ident)|*;)*) => {
239 match &key_code {
240 $($(winit::keyboard::Key::Named(winit::keyboard::NamedKey::$winit) $(if event.location == winit::keyboard::KeyLocation::$pos)? => $char.into(),)*)*
241 winit::keyboard::Key::Character(str) => str.as_str().into(),
242 _ => {
243 if let Some(text) = &event.text {
244 text.as_str().into()
245 } else {
246 return;
247 }
248 }
249 }
250 }
251 }
252 #[cfg_attr(slint_nightly_test, allow(non_exhaustive_omitted_patterns))]
253 let text = i_slint_common::for_each_special_keys!(winit_key_to_char);
254
255 self.loop_error = window
256 .window()
257 .try_dispatch_event(match event.state {
258 winit::event::ElementState::Pressed if event.repeat => {
259 corelib::platform::WindowEvent::KeyPressRepeated { text }
260 }
261 winit::event::ElementState::Pressed => {
262 if is_synthetic {
263 use winit::keyboard::{Key::Named, NamedKey as N};
266 if !matches!(
267 key_code,
268 Named(N::Control | N::Shift | N::Super | N::Alt | N::AltGraph),
269 ) {
270 return;
271 }
272 }
273 corelib::platform::WindowEvent::KeyPressed { text }
274 }
275 winit::event::ElementState::Released => {
276 corelib::platform::WindowEvent::KeyReleased { text }
277 }
278 })
279 .err();
280 }
281 WindowEvent::Ime(winit::event::Ime::Preedit(string, preedit_selection)) => {
282 let event = KeyEvent {
283 event_type: KeyEventType::UpdateComposition,
284 preedit_text: string.into(),
285 preedit_selection: preedit_selection.map(|e| e.0 as i32..e.1 as i32),
286 ..Default::default()
287 };
288 runtime_window.process_key_input(event);
289 }
290 WindowEvent::Ime(winit::event::Ime::Commit(string)) => {
291 let event = KeyEvent {
292 event_type: KeyEventType::CommitComposition,
293 text: string.into(),
294 ..Default::default()
295 };
296 runtime_window.process_key_input(event);
297 }
298 WindowEvent::CursorMoved { position, .. } => {
299 self.current_resize_direction = handle_cursor_move_for_resize(
300 &window.winit_window().unwrap(),
301 position,
302 self.current_resize_direction,
303 runtime_window
304 .window_item()
305 .map_or(0_f64, |w| w.as_pin_ref().resize_border_width().get().into()),
306 );
307 let position = position.to_logical(runtime_window.scale_factor() as f64);
308 self.cursor_pos = euclid::point2(position.x, position.y);
309 runtime_window.process_mouse_input(MouseEvent::Moved {
310 position: self.cursor_pos,
311 is_touch: false,
312 });
313 }
314 WindowEvent::CursorLeft { .. } => {
315 if cfg!(target_arch = "wasm32") || !self.pressed {
317 self.pressed = false;
318 runtime_window.process_mouse_input(MouseEvent::Exit);
319 }
320 }
321 WindowEvent::MouseWheel { delta, .. } => {
322 let (delta_x, delta_y) = match delta {
323 winit::event::MouseScrollDelta::LineDelta(lx, ly) => (lx * 60., ly * 60.),
324 winit::event::MouseScrollDelta::PixelDelta(d) => {
325 let d = d.to_logical(runtime_window.scale_factor() as f64);
326 (d.x, d.y)
327 }
328 };
329 runtime_window.process_mouse_input(MouseEvent::Wheel {
330 position: self.cursor_pos,
331 delta_x,
332 delta_y,
333 });
334 }
335 WindowEvent::MouseInput { state, button, .. } => {
336 let button = match button {
337 winit::event::MouseButton::Left => PointerEventButton::Left,
338 winit::event::MouseButton::Right => PointerEventButton::Right,
339 winit::event::MouseButton::Middle => PointerEventButton::Middle,
340 winit::event::MouseButton::Back => PointerEventButton::Back,
341 winit::event::MouseButton::Forward => PointerEventButton::Forward,
342 winit::event::MouseButton::Other(_) => PointerEventButton::Other,
343 };
344 let ev = match state {
345 winit::event::ElementState::Pressed => {
346 if button == PointerEventButton::Left
347 && self.current_resize_direction.is_some()
348 {
349 handle_resize(
350 &window.winit_window().unwrap(),
351 self.current_resize_direction,
352 );
353 return;
354 }
355
356 self.pressed = true;
357 MouseEvent::Pressed {
358 position: self.cursor_pos,
359 button,
360 click_count: 0,
361 is_touch: false,
362 }
363 }
364 winit::event::ElementState::Released => {
365 self.pressed = false;
366 MouseEvent::Released {
367 position: self.cursor_pos,
368 button,
369 click_count: 0,
370 is_touch: false,
371 }
372 }
373 };
374 runtime_window.process_mouse_input(ev);
375 }
376 WindowEvent::Touch(touch) => {
377 if Some(touch.id) == self.current_touch_id || self.current_touch_id.is_none() {
378 let location = touch.location.to_logical(runtime_window.scale_factor() as f64);
379 let position = euclid::point2(location.x, location.y);
380 let ev = match touch.phase {
381 winit::event::TouchPhase::Started => {
382 self.pressed = true;
383 if self.current_touch_id.is_none() {
384 self.current_touch_id = Some(touch.id);
385 }
386 MouseEvent::Pressed {
387 position,
388 button: PointerEventButton::Left,
389 click_count: 0,
390 is_touch: true,
391 }
392 }
393 winit::event::TouchPhase::Ended | winit::event::TouchPhase::Cancelled => {
394 self.pressed = false;
395 self.current_touch_id = None;
396 MouseEvent::Released {
397 position,
398 button: PointerEventButton::Left,
399 click_count: 0,
400 is_touch: true,
401 }
402 }
403 winit::event::TouchPhase::Moved => {
404 MouseEvent::Moved { position, is_touch: true }
405 }
406 };
407 runtime_window.process_mouse_input(ev);
408 }
409 }
410 WindowEvent::ScaleFactorChanged { scale_factor, inner_size_writer: _ } => {
411 if std::env::var("SLINT_SCALE_FACTOR").is_err() {
412 self.loop_error = window
413 .window()
414 .try_dispatch_event(corelib::platform::WindowEvent::ScaleFactorChanged {
415 scale_factor: scale_factor as f32,
416 })
417 .err();
418 }
421 }
422 WindowEvent::ThemeChanged(theme) => window.set_color_scheme(match theme {
423 winit::window::Theme::Dark => ColorScheme::Dark,
424 winit::window::Theme::Light => ColorScheme::Light,
425 }),
426 WindowEvent::Occluded(x) => {
427 window.renderer.occluded(x);
428
429 window.window_state_event();
431 }
432 _ => {}
433 }
434
435 if self.loop_error.is_some() {
436 event_loop.exit();
437 }
438 }
439
440 fn user_event(&mut self, event_loop: &ActiveEventLoop, event: SlintEvent) {
441 match event.0 {
442 CustomEvent::UserEvent(user_callback) => user_callback(),
443 CustomEvent::Exit => {
444 self.suspend_all_hidden_windows();
445 event_loop.exit()
446 }
447 #[cfg(enable_accesskit)]
448 CustomEvent::Accesskit(accesskit_winit::Event { window_id, window_event }) => {
449 if let Some(window) = self.shared_backend_data.window_by_id(window_id) {
450 let deferred_action = window
451 .accesskit_adapter()
452 .expect("internal error: accesskit adapter must exist when window exists")
453 .borrow_mut()
454 .process_accesskit_event(window_event);
455 if let Some(deferred_action) = deferred_action {
457 deferred_action.invoke(window.window());
458 }
459 }
460 }
461 #[cfg(target_arch = "wasm32")]
462 CustomEvent::WakeEventLoopWorkaround => {
463 event_loop.set_control_flow(ControlFlow::Poll);
464 }
465 #[cfg(muda)]
466 CustomEvent::Muda(event) => {
467 if let Some((window, eid, muda_type)) =
468 event.id().0.split_once('|').and_then(|(w, e)| {
469 let (e, muda_type) = e.split_once('|')?;
470 Some((
471 self.shared_backend_data.window_by_id(
472 winit::window::WindowId::from(w.parse::<u64>().ok()?),
473 )?,
474 e.parse::<usize>().ok()?,
475 muda_type.parse::<crate::muda::MudaType>().ok()?,
476 ))
477 })
478 {
479 window.muda_event(eid, muda_type);
480 };
481 }
482 }
483 }
484
485 fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: winit::event::StartCause) {
486 if matches!(
487 self.custom_application_handler.as_mut().map_or(EventResult::Propagate, |handler| {
488 handler.new_events(event_loop, cause)
489 }),
490 EventResult::PreventDefault
491 ) {
492 return;
493 }
494
495 event_loop.set_control_flow(ControlFlow::Wait);
496
497 corelib::platform::update_timers_and_animations();
498 }
499
500 fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
501 if matches!(
502 self.custom_application_handler
503 .as_mut()
504 .map_or(EventResult::Propagate, |handler| { handler.about_to_wait(event_loop) }),
505 EventResult::PreventDefault
506 ) {
507 return;
508 }
509
510 if let Err(err) = self.shared_backend_data.create_inactive_windows(event_loop) {
511 self.loop_error = Some(err);
512 }
513
514 if !event_loop.exiting() {
515 for w in self
516 .shared_backend_data
517 .active_windows
518 .borrow()
519 .iter()
520 .filter_map(|(_, w)| w.upgrade())
521 {
522 if w.window().has_active_animations() {
523 w.request_redraw();
524 }
525 }
526 }
527
528 if event_loop.control_flow() == ControlFlow::Wait {
529 if let Some(next_timer) = corelib::platform::duration_until_next_timer_update() {
530 event_loop.set_control_flow(ControlFlow::wait_duration(next_timer));
531 }
532 }
533
534 if self.pumping_events_instantly {
535 event_loop.set_control_flow(ControlFlow::Poll);
536 }
537 }
538
539 fn device_event(
540 &mut self,
541 event_loop: &ActiveEventLoop,
542 device_id: winit::event::DeviceId,
543 event: winit::event::DeviceEvent,
544 ) {
545 if let Some(handler) = self.custom_application_handler.as_mut() {
546 handler.device_event(event_loop, device_id, event);
547 }
548 }
549
550 fn suspended(&mut self, event_loop: &ActiveEventLoop) {
551 if let Some(handler) = self.custom_application_handler.as_mut() {
552 handler.suspended(event_loop);
553 }
554 }
555
556 fn exiting(&mut self, event_loop: &ActiveEventLoop) {
557 if let Some(handler) = self.custom_application_handler.as_mut() {
558 handler.exiting(event_loop);
559 }
560 }
561
562 fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
563 if let Some(handler) = self.custom_application_handler.as_mut() {
564 handler.memory_warning(event_loop);
565 }
566 }
567}
568
569impl EventLoopState {
570 #[allow(unused_mut)] pub fn run(mut self) -> Result<Self, corelib::platform::PlatformError> {
574 let not_running_loop_instance = self
575 .shared_backend_data
576 .not_running_event_loop
577 .take()
578 .ok_or_else(|| PlatformError::from("Nested event loops are not supported"))?;
579 let mut winit_loop = not_running_loop_instance;
580
581 cfg_if::cfg_if! {
582 if #[cfg(any(target_arch = "wasm32", ios_and_friends))] {
583 winit_loop
584 .run_app(&mut self)
585 .map_err(|e| format!("Error running winit event loop: {e}"))?;
586 Ok(Self::new(self.shared_backend_data.clone(), None))
588 } else {
589 use winit::platform::run_on_demand::EventLoopExtRunOnDemand as _;
590 winit_loop
591 .run_app_on_demand(&mut self)
592 .map_err(|e| format!("Error running winit event loop: {e}"))?;
593
594 self.shared_backend_data.not_running_event_loop.replace(Some(winit_loop));
597
598 if let Some(error) = self.loop_error {
599 return Err(error);
600 }
601 Ok(self)
602 }
603 }
604 }
605
606 #[cfg(all(not(target_arch = "wasm32"), not(ios_and_friends)))]
609 pub fn pump_events(
610 mut self,
611 timeout: Option<std::time::Duration>,
612 ) -> Result<(Self, winit::platform::pump_events::PumpStatus), corelib::platform::PlatformError>
613 {
614 use winit::platform::pump_events::EventLoopExtPumpEvents;
615
616 let not_running_loop_instance = self
617 .shared_backend_data
618 .not_running_event_loop
619 .take()
620 .ok_or_else(|| PlatformError::from("Nested event loops are not supported"))?;
621 let mut winit_loop = not_running_loop_instance;
622
623 self.pumping_events_instantly = timeout.is_some_and(|duration| duration.is_zero());
624
625 let result = winit_loop.pump_app_events(timeout, &mut self);
626
627 self.pumping_events_instantly = false;
628
629 self.shared_backend_data.not_running_event_loop.replace(Some(winit_loop));
632
633 if let Some(error) = self.loop_error {
634 return Err(error);
635 }
636 Ok((self, result))
637 }
638
639 #[cfg(target_arch = "wasm32")]
640 pub fn spawn(self) -> Result<(), corelib::platform::PlatformError> {
641 use winit::platform::web::EventLoopExtWebSys;
642 let not_running_loop_instance = self
643 .shared_backend_data
644 .not_running_event_loop
645 .take()
646 .ok_or_else(|| PlatformError::from("Nested event loops are not supported"))?;
647
648 not_running_loop_instance.spawn_app(self);
649
650 Ok(())
651 }
652}