1use super::{EventCx, EventState, PopupState};
9use crate::cast::Cast;
10use crate::event::{Event, FocusSource};
11use crate::runner::{AppData, Platform, RunnerT, WindowDataErased};
12use crate::theme::ThemeSize;
13#[cfg(all(wayland_platform, feature = "clipboard"))]
14use crate::util::warn_about_error;
15use crate::window::{PopupDescriptor, Window, WindowId, WindowWidget};
16use crate::{ActionRedraw, Id, Node, WindowActions};
17use winit::event::{ButtonSource, ElementState, PointerKind, PointerSource};
18use winit::window::ResizeDirection;
19
20impl EventState {
21 pub fn platform(&self) -> Platform {
23 self.platform
24 }
25
26 #[inline]
28 pub fn window_has_focus(&self) -> bool {
29 self.window_has_focus
30 }
31
32 #[must_use]
38 pub(super) fn close_popup(&mut self, index: usize) -> WindowId {
39 let state = self.popups.remove(index);
40 if state.is_sized {
41 self.popup_removed.push((state.desc.id, state.id));
42 }
43 self.mouse.tooltip_popup_close(&state.desc.parent);
44
45 if let Some(id) = state.old_nav_focus {
46 self.set_nav_focus(id, FocusSource::Synthetic);
47 }
48
49 state.id
50 }
51
52 pub(crate) fn confirm_popup_is_sized(&mut self, id: WindowId) {
53 for popup in &mut self.popups {
54 if popup.id == id {
55 popup.is_sized = true;
56 }
57 }
58 }
59
60 #[must_use]
62 pub(crate) fn flush_pending<'a>(
63 &'a mut self,
64 runner: &'a mut dyn RunnerT,
65 theme: &'a dyn ThemeSize,
66 window: &'a dyn WindowDataErased,
67 mut node: Node,
68 ) -> WindowActions {
69 if !self.pending_send_targets.is_empty() {
70 runner.set_send_targets(&mut self.pending_send_targets);
71 }
72
73 let resize = self.with(runner, theme, window, |cx| {
74 while let Some((id, wid)) = cx.popup_removed.pop() {
75 cx.send_event(node.re(), id, Event::PopupClosed(wid));
76 }
77
78 cx.mouse_handle_pending(node.re());
79 cx.touch_handle_pending(node.re());
80
81 if cx.nav.has_pending_changes() {
82 cx.handle_pending_nav_focus(node.re());
83 }
84
85 if cx.input.has_pending_changes() {
87 cx.flush_pending_input_focus(node.re());
88 }
89
90 cx.poll_futures();
92
93 let window_id = Id::ROOT.make_child(cx.window_id.get().cast());
94 while let Some((mut id, msg)) = cx.send_queue.pop_front() {
95 if !id.is_valid() {
96 id = match cx.runner.send_target_for(msg.type_id()) {
97 Some(target) => target,
98 None => {
99 log::warn!(target: "kas_core::erased", "no send target for: {msg:?}");
101 continue;
102 }
103 }
104 }
105
106 if window_id.is_ancestor_of(&id) {
107 cx.send_or_replay(node.re(), id, msg);
108 } else {
109 cx.runner.send_erased(id, msg);
110 }
111 }
112
113 if cx.action_moved.take().is_some() {
115 cx.action_redraw(ActionRedraw);
116 }
117 });
118
119 if let Some(icon) = self.mouse.update_pointer_icon() {
120 window.set_pointer_icon(icon);
121 }
122
123 WindowActions {
124 resize,
125 redraw: self.action_redraw.take(),
126 close: self.action_close.take(),
127 }
128 }
129
130 pub(crate) fn suspended(&mut self, runner: &mut dyn RunnerT) {
132 while !self.popups.is_empty() {
133 let id = self.close_popup(self.popups.len() - 1);
134 runner.close_window(id);
135 }
136 }
137}
138
139impl<'a> EventCx<'a> {
140 pub(super) fn close_non_ancestors_of(&mut self, id: Option<&Id>) {
142 for index in (0..self.popups.len()).rev() {
143 if let Some(id) = id
144 && self.popups[index].desc.id.is_ancestor_of(id)
145 {
146 continue;
147 }
148
149 let id = self.close_popup(index);
150 self.runner.close_window(id);
151 }
152 }
153
154 pub(super) fn handle_close(&mut self) {
155 let mut id = self.window_id;
156 if !self.popups.is_empty() {
157 let index = self.popups.len() - 1;
158 id = self.close_popup(index);
159 }
160 self.runner.close_window(id);
161 }
162
163 pub(crate) fn add_popup(&mut self, popup: PopupDescriptor, set_focus: bool) -> WindowId {
182 log::trace!(target: "kas_core::event", "add_popup: {popup:?}");
183
184 let parent_id = self.window.window_id();
185 let id = self.runner.add_popup(parent_id, popup.clone());
186 let mut old_nav_focus = None;
187 if set_focus {
188 old_nav_focus = self.nav_focus().cloned();
189 self.clear_nav_focus();
190 }
191 self.popups.push(PopupState {
192 id,
193 desc: popup,
194 old_nav_focus,
195 is_sized: false,
196 });
197 id
198 }
199
200 pub(crate) fn reposition_popup(&mut self, id: WindowId, desc: PopupDescriptor) {
205 self.runner.reposition_popup(id, desc.clone());
206 for popup in self.popups.iter_mut() {
207 if popup.id == id {
208 debug_assert_eq!(popup.desc.id, desc.id);
209 popup.desc = desc;
210 break;
211 }
212 }
213 }
214
215 #[inline]
229 pub fn add_dataless_window(&mut self, mut window: Window<()>, modal: bool) -> WindowId {
230 if modal {
231 window.set_modal_with_parent(self.window_id);
232 }
233 self.runner.add_dataless_window(window)
234 }
235
236 #[inline]
254 pub fn add_window<Data: AppData>(&mut self, mut window: Window<Data>, modal: bool) -> WindowId {
255 if modal {
256 window.set_modal_with_parent(self.window_id);
257 }
258 let data_type_id = std::any::TypeId::of::<Data>();
259 unsafe {
260 let window: Window<()> = std::mem::transmute(window);
261 self.runner.add_window(window, data_type_id)
262 }
263 }
264
265 pub fn close_window(&mut self, mut id: WindowId) {
270 for (index, p) in self.popups.iter().enumerate() {
271 if p.id == id {
272 id = self.close_popup(index);
273 break;
274 }
275 }
276
277 self.runner.close_window(id);
278 }
279
280 pub fn drag_window(&self) {
284 if let Some(ww) = self.window.winit_window()
285 && let Err(e) = ww.drag_window()
286 {
287 log::warn!("EventCx::drag_window: {e}");
288 }
289 }
290
291 pub fn drag_resize_window(&self, direction: ResizeDirection) {
295 if let Some(ww) = self.window.winit_window()
296 && let Err(e) = ww.drag_resize_window(direction)
297 {
298 log::warn!("EventCx::drag_resize_window: {e}");
299 }
300 }
301
302 pub fn get_clipboard(&mut self) -> Option<String> {
307 #[cfg(all(wayland_platform, feature = "clipboard"))]
308 if let Some(cb) = self.window.wayland_clipboard() {
309 return match cb.load() {
310 Ok(s) => Some(s),
311 Err(e) => {
312 warn_about_error("Failed to get clipboard contents", &e);
313 None
314 }
315 };
316 }
317
318 self.runner.get_clipboard()
319 }
320
321 pub fn set_clipboard(&mut self, content: String) {
323 #[cfg(all(wayland_platform, feature = "clipboard"))]
324 if let Some(cb) = self.window.wayland_clipboard() {
325 cb.store(content);
326 return;
327 }
328
329 self.runner.set_clipboard(content)
330 }
331
332 #[inline]
334 pub fn has_primary(&self) -> bool {
335 cfg_if::cfg_if! {
336 if #[cfg(unix)] {
337 true
338 } else {
339 false
340 }
341 }
342 }
343
344 pub fn get_primary(&mut self) -> Option<String> {
349 #[cfg(all(wayland_platform, feature = "clipboard"))]
350 if let Some(cb) = self.window.wayland_clipboard() {
351 return match cb.load_primary() {
352 Ok(s) => Some(s),
353 Err(e) => {
354 warn_about_error("Failed to get clipboard contents", &e);
355 None
356 }
357 };
358 }
359
360 self.runner.get_primary()
361 }
362
363 pub fn set_primary(&mut self, content: String) {
368 #[cfg(all(wayland_platform, feature = "clipboard"))]
369 if let Some(cb) = self.window.wayland_clipboard() {
370 cb.store_primary(content);
371 return;
372 }
373
374 self.runner.set_primary(content)
375 }
376
377 pub fn winit_window(&self) -> Option<&dyn winit::window::Window> {
381 self.window.winit_window()
382 }
383
384 pub(crate) fn handle_winit<A>(
390 &mut self,
391 win: &mut dyn WindowWidget<Data = A>,
392 data: &A,
393 event: winit::event::WindowEvent,
394 ) {
395 use winit::event::WindowEvent::*;
396
397 match event {
398 CloseRequested => self.close_own_window(),
399 Focused(state) => {
405 self.window_has_focus = state;
406 if state {
407 self.redraw();
409 } else {
410 while let Some(id) = self.popups.last().map(|state| state.id) {
412 self.close_window(id);
413 }
414 }
415 }
416 KeyboardInput {
417 event,
418 is_synthetic,
419 ..
420 } => self.keyboard_input(win.as_node(data), event, is_synthetic),
421 ModifiersChanged(modifiers) => self.modifiers_changed(modifiers.state()),
422 Ime(event) => self.ime_event(win.as_node(data), event),
423 PointerMoved {
424 position, source, ..
425 } => match source {
426 PointerSource::Mouse => self.handle_pointer_moved(win, data, position.into()),
427 PointerSource::Touch { finger_id, .. } => {
428 self.handle_touch_moved(win.as_node(data), finger_id, position.into())
429 }
430 _ => (),
431 },
432 PointerEntered { kind, .. } => {
433 if kind == PointerKind::Mouse {
434 self.handle_pointer_entered()
435 }
436 }
437 PointerLeft { kind, .. } => {
438 if kind == PointerKind::Mouse {
439 self.handle_pointer_left(win.as_node(data))
440 }
441 }
442 MouseWheel { delta, .. } => self.handle_mouse_wheel(win.as_node(data), delta),
443 PointerButton {
444 state,
445 position,
446 button,
447 ..
448 } => match button {
449 ButtonSource::Mouse(button) => {
450 self.handle_mouse_input(win.as_node(data), state, button)
451 }
452 ButtonSource::Touch { finger_id, .. } => match state {
453 ElementState::Pressed => {
454 self.handle_touch_start(win.as_node(data), finger_id, position.into())
455 }
456 ElementState::Released => {
457 self.handle_touch_end(win.as_node(data), finger_id, position.into())
458 }
459 },
460 _ => (),
461 },
462 _ => (),
463 }
464 }
465}