1use super::{EventCx, EventState, PopupState};
9use crate::cast::Cast;
10use crate::event::{Event, FocusSource};
11use crate::runner::{AppData, Platform, RunnerT, WindowDataErased};
12#[cfg(all(wayland_platform, feature = "clipboard"))]
13use crate::util::warn_about_error;
14use crate::window::{PopupDescriptor, Window, WindowId, WindowWidget};
15use crate::{Action, Id, Node, Tile};
16use winit::window::ResizeDirection;
17
18impl EventState {
19 pub fn platform(&self) -> Platform {
21 self.platform
22 }
23
24 #[inline]
26 pub fn window_has_focus(&self) -> bool {
27 self.window_has_focus
28 }
29
30 #[must_use]
36 pub(super) fn close_popup(&mut self, index: usize) -> WindowId {
37 let state = self.popups.remove(index);
38 if state.is_sized {
39 self.popup_removed.push((state.desc.id, state.id));
40 }
41 self.mouse.tooltip_popup_close(&state.desc.parent);
42
43 if let Some(id) = state.old_nav_focus {
44 self.set_nav_focus(id, FocusSource::Synthetic);
45 }
46
47 state.id
48 }
49
50 pub(crate) fn confirm_popup_is_sized(&mut self, id: WindowId) {
51 for popup in &mut self.popups {
52 if popup.id == id {
53 popup.is_sized = true;
54 }
55 }
56 }
57
58 pub(crate) fn flush_pending<'a>(
60 &'a mut self,
61 runner: &'a mut dyn RunnerT,
62 window: &'a dyn WindowDataErased,
63 mut node: Node,
64 ) -> Action {
65 if !self.pending_send_targets.is_empty() {
66 runner.set_send_targets(&mut self.pending_send_targets);
67 }
68
69 self.with(runner, window, |cx| {
70 while let Some((id, wid)) = cx.popup_removed.pop() {
71 cx.send_event(node.re(), id, Event::PopupClosed(wid));
72 }
73
74 cx.mouse_handle_pending(node.re());
75 cx.touch_handle_pending(node.re());
76
77 if let Some(id) = cx.pending_update.take() {
78 node.find_node(&id, |node| cx.update(node));
79 }
80
81 if cx.pending_nav_focus.is_some() {
82 cx.handle_pending_nav_focus(node.re());
83 }
84
85 if let Some(pending) = cx.pending_sel_focus.take() {
87 cx.set_sel_focus(cx.window, node.re(), pending);
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.contains(Action::REGION_MOVED) {
115 cx.action.remove(Action::REGION_MOVED);
116 cx.action.insert(Action::REDRAW);
117 }
118 });
119
120 if let Some(icon) = self.mouse.update_cursor_icon() {
121 window.set_cursor_icon(icon);
122 }
123
124 std::mem::take(&mut self.action)
125 }
126
127 pub(crate) fn suspended(&mut self, runner: &mut dyn RunnerT) {
129 while !self.popups.is_empty() {
130 let id = self.close_popup(self.popups.len() - 1);
131 runner.close_window(id);
132 }
133 }
134}
135
136impl<'a> EventCx<'a> {
137 pub(super) fn close_non_ancestors_of(&mut self, id: Option<&Id>) {
139 for index in (0..self.popups.len()).rev() {
140 if let Some(id) = id
141 && self.popups[index].desc.id.is_ancestor_of(id)
142 {
143 continue;
144 }
145
146 let id = self.close_popup(index);
147 self.runner.close_window(id);
148 }
149 }
150
151 pub(super) fn handle_close(&mut self) {
152 let mut id = self.window_id;
153 if !self.popups.is_empty() {
154 let index = self.popups.len() - 1;
155 id = self.close_popup(index);
156 }
157 self.runner.close_window(id);
158 }
159
160 pub(crate) fn add_popup(&mut self, popup: PopupDescriptor, set_focus: bool) -> WindowId {
179 log::trace!(target: "kas_core::event", "add_popup: {popup:?}");
180
181 let parent_id = self.window.window_id();
182 let id = self.runner.add_popup(parent_id, popup.clone());
183 let mut old_nav_focus = None;
184 if set_focus {
185 old_nav_focus = self.nav_focus.clone();
186 self.clear_nav_focus();
187 }
188 self.popups.push(PopupState {
189 id,
190 desc: popup,
191 old_nav_focus,
192 is_sized: false,
193 });
194 id
195 }
196
197 pub(crate) fn reposition_popup(&mut self, id: WindowId, desc: PopupDescriptor) {
202 self.runner.reposition_popup(id, desc.clone());
203 for popup in self.popups.iter_mut() {
204 if popup.id == id {
205 debug_assert_eq!(popup.desc.id, desc.id);
206 popup.desc = desc;
207 break;
208 }
209 }
210 }
211
212 #[inline]
226 pub fn add_dataless_window(&mut self, mut window: Window<()>, modal: bool) -> WindowId {
227 if modal {
228 window.set_modal_with_parent(self.window_id);
229 }
230 self.runner.add_dataless_window(window)
231 }
232
233 #[inline]
246 pub fn add_window<Data: AppData>(&mut self, window: Window<Data>) -> WindowId {
247 let data_type_id = std::any::TypeId::of::<Data>();
248 unsafe {
249 let window: Window<()> = std::mem::transmute(window);
250 self.runner.add_window(window, data_type_id)
251 }
252 }
253
254 pub fn close_window(&mut self, mut id: WindowId) {
259 for (index, p) in self.popups.iter().enumerate() {
260 if p.id == id {
261 id = self.close_popup(index);
262 break;
263 }
264 }
265
266 self.runner.close_window(id);
267 }
268
269 pub fn drag_window(&self) {
273 if let Some(ww) = self.window.winit_window()
274 && let Err(e) = ww.drag_window()
275 {
276 log::warn!("EventCx::drag_window: {e}");
277 }
278 }
279
280 pub fn drag_resize_window(&self, direction: ResizeDirection) {
284 if let Some(ww) = self.window.winit_window()
285 && let Err(e) = ww.drag_resize_window(direction)
286 {
287 log::warn!("EventCx::drag_resize_window: {e}");
288 }
289 }
290
291 pub fn get_clipboard(&mut self) -> Option<String> {
296 #[cfg(all(wayland_platform, feature = "clipboard"))]
297 if let Some(cb) = self.window.wayland_clipboard() {
298 return match cb.load() {
299 Ok(s) => Some(s),
300 Err(e) => {
301 warn_about_error("Failed to get clipboard contents", &e);
302 None
303 }
304 };
305 }
306
307 self.runner.get_clipboard()
308 }
309
310 pub fn set_clipboard(&mut self, content: String) {
312 #[cfg(all(wayland_platform, feature = "clipboard"))]
313 if let Some(cb) = self.window.wayland_clipboard() {
314 cb.store(content);
315 return;
316 }
317
318 self.runner.set_clipboard(content)
319 }
320
321 #[inline]
323 pub fn has_primary(&self) -> bool {
324 cfg_if::cfg_if! {
325 if #[cfg(unix)] {
326 true
327 } else {
328 false
329 }
330 }
331 }
332
333 pub fn get_primary(&mut self) -> Option<String> {
338 #[cfg(all(wayland_platform, feature = "clipboard"))]
339 if let Some(cb) = self.window.wayland_clipboard() {
340 return match cb.load_primary() {
341 Ok(s) => Some(s),
342 Err(e) => {
343 warn_about_error("Failed to get clipboard contents", &e);
344 None
345 }
346 };
347 }
348
349 self.runner.get_primary()
350 }
351
352 pub fn set_primary(&mut self, content: String) {
357 #[cfg(all(wayland_platform, feature = "clipboard"))]
358 if let Some(cb) = self.window.wayland_clipboard() {
359 cb.store_primary(content);
360 return;
361 }
362
363 self.runner.set_primary(content)
364 }
365
366 pub fn winit_window(&self) -> Option<&winit::window::Window> {
370 self.window.winit_window()
371 }
372
373 pub(crate) fn handle_winit<A>(
379 &mut self,
380 win: &mut dyn WindowWidget<Data = A>,
381 data: &A,
382 event: winit::event::WindowEvent,
383 ) {
384 use winit::event::WindowEvent::*;
385
386 match event {
387 CloseRequested => self.action(win.id(), Action::CLOSE),
388 Focused(state) => {
394 self.window_has_focus = state;
395 if state {
396 self.redraw(win.id());
398 } else {
399 while let Some(id) = self.popups.last().map(|state| state.id) {
401 self.close_window(id);
402 }
403 }
404 }
405 KeyboardInput {
406 event,
407 is_synthetic,
408 ..
409 } => self.keyboard_input(win.as_node(data), event, is_synthetic),
410 ModifiersChanged(modifiers) => self.modifiers_changed(modifiers.state()),
411 Ime(event) => self.ime_event(win.as_node(data), event),
412 CursorMoved { position, .. } => self.handle_cursor_moved(win, data, position.into()),
413 CursorEntered { .. } => self.handle_cursor_entered(),
414 CursorLeft { .. } => self.handle_cursor_left(win.as_node(data)),
415 MouseWheel { delta, .. } => self.handle_mouse_wheel(win.as_node(data), delta),
416 MouseInput { state, button, .. } => {
417 self.handle_mouse_input(win.as_node(data), state, button)
418 }
419 Touch(touch) => self.handle_touch_event(win.as_node(data), touch),
422 _ => (),
423 }
424 }
425}