zng_view_api/types.rs
1//! General event types.
2
3use crate::{
4 access::{AccessCmd, AccessNodeId},
5 api_extension::{ApiExtensionId, ApiExtensionPayload, ApiExtensions},
6 config::{
7 AnimationsConfig, ChromeConfig, ColorsConfig, FontAntiAliasing, KeyRepeatConfig, LocaleConfig, MultiClickConfig, TouchConfig,
8 },
9 dialog::{DialogId, FileDialogResponse, MsgDialogResponse},
10 drag_drop::{DragDropData, DragDropEffect},
11 image::{ImageId, ImageLoadedData, ImagePpi},
12 ipc::{self, IpcBytes},
13 keyboard::{Key, KeyCode, KeyLocation, KeyState},
14 mouse::{ButtonId, ButtonState, MouseButton, MouseScrollDelta},
15 touch::{TouchPhase, TouchUpdate},
16 window::{EventFrameRendered, FrameId, HeadlessOpenData, MonitorId, MonitorInfo, WindowChanged, WindowId, WindowOpenData},
17};
18use serde::{Deserialize, Serialize};
19use std::fmt;
20use zng_txt::Txt;
21use zng_unit::{DipPoint, PxRect, PxSize, Rgba};
22
23macro_rules! declare_id {
24 ($(
25 $(#[$docs:meta])+
26 pub struct $Id:ident(_);
27 )+) => {$(
28 $(#[$docs])+
29 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
30 #[serde(transparent)]
31 pub struct $Id(u32);
32
33 impl $Id {
34 /// Dummy ID, zero.
35 pub const INVALID: Self = Self(0);
36
37 /// Create the first valid ID.
38 pub const fn first() -> Self {
39 Self(1)
40 }
41
42 /// Create the next ID.
43 ///
44 /// IDs wrap around to [`first`] when the entire `u32` space is used, it is never `INVALID`.
45 ///
46 /// [`first`]: Self::first
47 #[must_use]
48 pub const fn next(self) -> Self {
49 let r = Self(self.0.wrapping_add(1));
50 if r.0 == Self::INVALID.0 {
51 Self::first()
52 } else {
53 r
54 }
55 }
56
57 /// Returns self and replace self with [`next`].
58 ///
59 /// [`next`]: Self::next
60 #[must_use]
61 pub fn incr(&mut self) -> Self {
62 std::mem::replace(self, self.next())
63 }
64
65 /// Get the raw ID.
66 pub const fn get(self) -> u32 {
67 self.0
68 }
69
70 /// Create an ID using a custom value.
71 ///
72 /// Note that only the documented process must generate IDs, and that it must only
73 /// generate IDs using this function or the [`next`] function.
74 ///
75 /// If the `id` is zero it will still be [`INVALID`] and handled differently by the other process,
76 /// zero is never valid.
77 ///
78 /// [`next`]: Self::next
79 /// [`INVALID`]: Self::INVALID
80 pub const fn from_raw(id: u32) -> Self {
81 Self(id)
82 }
83 }
84 )+};
85}
86
87pub(crate) use declare_id;
88
89declare_id! {
90 /// Device ID in channel.
91 ///
92 /// In the View Process this is mapped to a system id.
93 ///
94 /// In the App Process this is mapped to an unique id, but does not survived View crashes.
95 ///
96 /// The View Process defines the ID.
97 pub struct DeviceId(_);
98
99 /// View-process generation, starts at one and changes every respawn, it is never zero.
100 ///
101 /// The View Process defines the ID.
102 pub struct ViewProcessGen(_);
103}
104
105/// Identifier for a specific analog axis on some device.
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
107#[serde(transparent)]
108pub struct AxisId(pub u32);
109
110/// Identifier for a drag drop operation.
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
112#[serde(transparent)]
113pub struct DragDropId(pub u32);
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
116/// View process is connected and ready.
117///
118/// The [`ViewProcessGen`] is the generation of the new view-process, it must be passed to
119/// [`Controller::handle_inited`].
120///
121/// [`Controller::handle_inited`]: crate::Controller::handle_inited
122#[non_exhaustive]
123pub struct Inited {
124 /// View-process generation, changes after respawns and is never zero.
125 pub generation: ViewProcessGen,
126 /// If the view-process is a respawn from a previous crashed process.
127 pub is_respawn: bool,
128
129 /// Available monitors.
130 pub available_monitors: Vec<(MonitorId, MonitorInfo)>,
131 /// System multi-click config.
132 pub multi_click_config: MultiClickConfig,
133 /// System keyboard pressed key repeat start delay config.
134 pub key_repeat_config: KeyRepeatConfig,
135 /// System touch config.
136 pub touch_config: TouchConfig,
137 /// System font anti-aliasing config.
138 pub font_aa: FontAntiAliasing,
139 /// System animations config.
140 pub animations_config: AnimationsConfig,
141 /// System locale config.
142 pub locale_config: LocaleConfig,
143 /// System preferred color scheme and colors.
144 pub colors_config: ColorsConfig,
145 /// Window chrome (decorations) preference.
146 pub chrome_config: ChromeConfig,
147 /// API extensions implemented by the view-process.
148 ///
149 /// The extension IDs will stay valid for the duration of the view-process.
150 pub extensions: ApiExtensions,
151}
152impl Inited {
153 /// New response.
154 #[allow(clippy::too_many_arguments)] // already grouping stuff.
155 pub fn new(
156 generation: ViewProcessGen,
157 is_respawn: bool,
158 available_monitors: Vec<(MonitorId, MonitorInfo)>,
159 multi_click_config: MultiClickConfig,
160 key_repeat_config: KeyRepeatConfig,
161 touch_config: TouchConfig,
162 font_aa: FontAntiAliasing,
163 animations_config: AnimationsConfig,
164 locale_config: LocaleConfig,
165 colors_config: ColorsConfig,
166 chrome_config: ChromeConfig,
167 extensions: ApiExtensions,
168 ) -> Self {
169 Self {
170 generation,
171 is_respawn,
172 available_monitors,
173 multi_click_config,
174 key_repeat_config,
175 touch_config,
176 font_aa,
177 animations_config,
178 locale_config,
179 colors_config,
180 chrome_config,
181 extensions,
182 }
183 }
184}
185
186/// IME preview or insert event.
187#[derive(Debug, Clone, Serialize, Deserialize)]
188pub enum Ime {
189 /// Preview an IME insert at the last non-preview caret/selection.
190 ///
191 /// The associated values are the preview string and caret/selection inside the preview string.
192 ///
193 /// The preview must visually replace the last non-preview selection or insert at the last non-preview
194 /// caret index. If the preview string is empty the preview must be cancelled.
195 Preview(Txt, (usize, usize)),
196
197 /// Apply an IME insert at the last non-preview caret/selection. The caret must be moved to
198 /// the end of the inserted sub-string.
199 Commit(Txt),
200}
201
202/// System and User events sent from the View Process.
203#[derive(Debug, Clone, Serialize, Deserialize)]
204#[non_exhaustive]
205pub enum Event {
206 /// View-process inited.
207 Inited(Inited),
208 /// View-process suspended.
209 Suspended,
210
211 /// The event channel disconnected, probably because the view-process crashed.
212 ///
213 /// The [`ViewProcessGen`] is the generation of the view-process that was lost, it must be passed to
214 /// [`Controller::handle_disconnect`].
215 ///
216 /// [`Controller::handle_disconnect`]: crate::Controller::handle_disconnect
217 Disconnected(ViewProcessGen),
218
219 /// Window, context and renderer have finished initializing and is ready to receive commands.
220 WindowOpened(WindowId, WindowOpenData),
221
222 /// Headless context and renderer have finished initializing and is ready to receive commands.
223 HeadlessOpened(WindowId, HeadlessOpenData),
224
225 /// Window open or headless context open request failed.
226 WindowOrHeadlessOpenError {
227 /// Id from the request.
228 id: WindowId,
229 /// Error message.
230 error: Txt,
231 },
232
233 /// A frame finished rendering.
234 ///
235 /// `EventsCleared` is not send after this event.
236 FrameRendered(EventFrameRendered),
237
238 /// Window moved, resized, or minimized/maximized etc.
239 ///
240 /// This event aggregates events moves, resizes and other state changes into a
241 /// single event to simplify tracking composite changes, for example, the window changes size and position
242 /// when maximized, this can be trivially observed with this event.
243 ///
244 /// The [`EventCause`] can be used to identify a state change initiated by the app.
245 ///
246 /// [`EventCause`]: crate::window::EventCause
247 WindowChanged(WindowChanged),
248
249 /// A drag&drop gesture started dragging over the window.
250 DragHovered {
251 /// Window that is hovered.
252 window: WindowId,
253 /// Data payload.
254 data: Vec<DragDropData>,
255 /// Allowed effects.
256 allowed: DragDropEffect,
257 },
258 /// A drag&drop gesture moved over the window.
259 DragMoved {
260 /// Window that is hovered.
261 window: WindowId,
262 /// Cursor positions in between the previous event and this one.
263 coalesced_pos: Vec<DipPoint>,
264 /// Cursor position, relative to the window top-left in device independent pixels.
265 position: DipPoint,
266 },
267 /// A drag&drop gesture finished over the window.
268 DragDropped {
269 /// Window that received the file drop.
270 window: WindowId,
271 /// Data payload.
272 data: Vec<DragDropData>,
273 /// Allowed effects.
274 allowed: DragDropEffect,
275 /// ID of this drop operation.
276 ///
277 /// Handlers must call `drag_dropped` with this ID and what effect was applied to the data.
278 drop_id: DragDropId,
279 },
280 /// A drag&drop gesture stopped hovering the window without dropping.
281 DragCancelled {
282 /// Window that was previous hovered.
283 window: WindowId,
284 },
285 /// A drag started by the app was dropped or canceled.
286 AppDragEnded {
287 /// Window that started the drag.
288 window: WindowId,
289 /// Drag ID.
290 drag: DragDropId,
291 /// Effect applied to the data by the drop target.
292 ///
293 /// Is a single flag if the data was dropped in a valid drop target, or is empty if was canceled.
294 applied: DragDropEffect,
295 },
296
297 /// App window(s) focus changed.
298 FocusChanged {
299 /// Window that lost focus.
300 prev: Option<WindowId>,
301 /// Window that got focus.
302 new: Option<WindowId>,
303 },
304 /// An event from the keyboard has been received.
305 ///
306 /// This event is only send if the window is focused, all pressed keys should be considered released
307 /// after [`FocusChanged`] to `None`. Modifier keys receive special treatment, after they are pressed,
308 /// the modifier key state is monitored directly so that the `Released` event is always send, unless the
309 /// focus changed to none.
310 ///
311 /// [`FocusChanged`]: Self::FocusChanged
312 KeyboardInput {
313 /// Window that received the key event.
314 window: WindowId,
315 /// Device that generated the key event.
316 device: DeviceId,
317 /// Physical key.
318 key_code: KeyCode,
319 /// If the key was pressed or released.
320 state: KeyState,
321 /// The location of the key on the keyboard.
322 key_location: KeyLocation,
323
324 /// Semantic key unmodified.
325 ///
326 /// Pressing `Shift+A` key will produce `Key::Char('a')` in QWERTY keyboards, the modifiers are not applied. Note that
327 /// the numpad keys do not represents the numbers unmodified
328 key: Key,
329 /// Semantic key modified by the current active modifiers.
330 ///
331 /// Pressing `Shift+A` key will produce `Key::Char('A')` in QWERTY keyboards, the modifiers are applied.
332 key_modified: Key,
333 /// Text typed.
334 ///
335 /// This is only set during [`KeyState::Pressed`] of a key that generates text.
336 ///
337 /// This is usually the `key_modified` char, but is also `'\r'` for `Key::Enter`. On Windows when a dead key was
338 /// pressed earlier but cannot be combined with the character from this key press, the produced text
339 /// will consist of two characters: the dead-key-character followed by the character resulting from this key press.
340 text: Txt,
341 },
342 /// IME composition event.
343 Ime {
344 /// Window that received the IME event.
345 window: WindowId,
346 /// IME event.
347 ime: Ime,
348 },
349
350 /// The mouse cursor has moved on the window.
351 ///
352 /// This event can be coalesced, i.e. multiple cursor moves packed into the same event.
353 MouseMoved {
354 /// Window that received the cursor move.
355 window: WindowId,
356 /// Device that generated the cursor move.
357 device: DeviceId,
358
359 /// Cursor positions in between the previous event and this one.
360 coalesced_pos: Vec<DipPoint>,
361
362 /// Cursor position, relative to the window top-left in device independent pixels.
363 position: DipPoint,
364 },
365
366 /// The mouse cursor has entered the window.
367 MouseEntered {
368 /// Window that now is hovered by the cursor.
369 window: WindowId,
370 /// Device that generated the cursor move event.
371 device: DeviceId,
372 },
373 /// The mouse cursor has left the window.
374 MouseLeft {
375 /// Window that is no longer hovered by the cursor.
376 window: WindowId,
377 /// Device that generated the cursor move event.
378 device: DeviceId,
379 },
380 /// A mouse wheel movement or touchpad scroll occurred.
381 MouseWheel {
382 /// Window that was hovered by the cursor when the mouse wheel was used.
383 window: WindowId,
384 /// Device that generated the mouse wheel event.
385 device: DeviceId,
386 /// Delta of change in the mouse scroll wheel state.
387 delta: MouseScrollDelta,
388 /// Touch state if the device that generated the event is a touchpad.
389 phase: TouchPhase,
390 },
391 /// An mouse button press has been received.
392 MouseInput {
393 /// Window that was hovered by the cursor when the mouse button was used.
394 window: WindowId,
395 /// Mouse device that generated the event.
396 device: DeviceId,
397 /// If the button was pressed or released.
398 state: ButtonState,
399 /// The mouse button.
400 button: MouseButton,
401 },
402 /// Touchpad pressure event.
403 TouchpadPressure {
404 /// Window that was hovered when the touchpad was touched.
405 window: WindowId,
406 /// Touchpad device.
407 device: DeviceId,
408 /// Pressure level between 0 and 1.
409 pressure: f32,
410 /// Click level.
411 stage: i64,
412 },
413 /// Motion on some analog axis. May report data redundant to other, more specific events.
414 AxisMotion {
415 /// Window that was focused when the motion was realized.
416 window: WindowId,
417 /// Analog device.
418 device: DeviceId,
419 /// Axis.
420 axis: AxisId,
421 /// Motion value.
422 value: f64,
423 },
424 /// Touch event has been received.
425 Touch {
426 /// Window that was touched.
427 window: WindowId,
428 /// Touch device.
429 device: DeviceId,
430
431 /// Coalesced touch updates, never empty.
432 touches: Vec<TouchUpdate>,
433 },
434 /// The monitor’s scale factor has changed.
435 ScaleFactorChanged {
436 /// Monitor that has changed.
437 monitor: MonitorId,
438 /// Windows affected by this change.
439 ///
440 /// Note that a window's scale factor can also change if it is moved to another monitor,
441 /// the [`Event::WindowChanged`] event notifies this using the [`WindowChanged::monitor`].
442 windows: Vec<WindowId>,
443 /// The new scale factor.
444 scale_factor: f32,
445 },
446
447 /// The available monitors have changed.
448 MonitorsChanged(Vec<(MonitorId, MonitorInfo)>),
449
450 /// The window has been requested to close.
451 WindowCloseRequested(WindowId),
452 /// The window has closed.
453 WindowClosed(WindowId),
454
455 /// An image resource already decoded size and PPI.
456 ImageMetadataLoaded {
457 /// The image that started loading.
458 image: ImageId,
459 /// The image pixel size.
460 size: PxSize,
461 /// The image pixels-per-inch metadata.
462 ppi: Option<ImagePpi>,
463 /// The image is a single channel R8.
464 is_mask: bool,
465 },
466 /// An image resource finished decoding.
467 ImageLoaded(ImageLoadedData),
468 /// An image resource, progressively decoded has decoded more bytes.
469 ImagePartiallyLoaded {
470 /// The image that has decoded more pixels.
471 image: ImageId,
472 /// The size of the decoded pixels, can be different then the image size if the
473 /// image is not *interlaced*.
474 partial_size: PxSize,
475 /// The image pixels-per-inch metadata.
476 ppi: Option<ImagePpi>,
477 /// If the decoded pixels so-far are all opaque (255 alpha).
478 is_opaque: bool,
479 /// If the decoded pixels so-far are a single channel.
480 is_mask: bool,
481 /// Updated BGRA8 pre-multiplied pixel buffer or R8 if `is_mask`. This includes all the pixels
482 /// decoded so-far.
483 partial_pixels: IpcBytes,
484 },
485 /// An image resource failed to decode, the image ID is not valid.
486 ImageLoadError {
487 /// The image that failed to decode.
488 image: ImageId,
489 /// The error message.
490 error: Txt,
491 },
492 /// An image finished encoding.
493 ImageEncoded {
494 /// The image that finished encoding.
495 image: ImageId,
496 /// The format of the encoded data.
497 format: Txt,
498 /// The encoded image data.
499 data: IpcBytes,
500 },
501 /// An image failed to encode.
502 ImageEncodeError {
503 /// The image that failed to encode.
504 image: ImageId,
505 /// The encoded format that was requested.
506 format: Txt,
507 /// The error message.
508 error: Txt,
509 },
510
511 /// An image generated from a rendered frame is ready.
512 FrameImageReady {
513 /// Window that had pixels copied.
514 window: WindowId,
515 /// The frame that was rendered when the pixels where copied.
516 frame: FrameId,
517 /// The frame image.
518 image: ImageId,
519 /// The pixel selection relative to the top-left.
520 selection: PxRect,
521 },
522
523 /* Config events */
524 /// System fonts have changed.
525 FontsChanged,
526 /// System text anti-aliasing configuration has changed.
527 FontAaChanged(FontAntiAliasing),
528 /// System double-click definition changed.
529 MultiClickConfigChanged(MultiClickConfig),
530 /// System animations config changed.
531 AnimationsConfigChanged(AnimationsConfig),
532 /// System definition of pressed key repeat event changed.
533 KeyRepeatConfigChanged(KeyRepeatConfig),
534 /// System touch config changed.
535 TouchConfigChanged(TouchConfig),
536 /// System locale changed.
537 LocaleChanged(LocaleConfig),
538 /// System color scheme or colors changed.
539 ColorsConfigChanged(ColorsConfig),
540 /// System window chrome (decorations) preference changed.
541 ChromeConfigChanged(ChromeConfig),
542
543 /* Raw device events */
544 /// Device added or installed.
545 DeviceAdded(DeviceId),
546 /// Device removed.
547 DeviceRemoved(DeviceId),
548 /// Mouse pointer motion.
549 ///
550 /// The values if the delta of movement (x, y), not position.
551 DeviceMouseMotion {
552 /// Device that generated the event.
553 device: DeviceId,
554 /// Delta of change in the cursor position.
555 delta: euclid::Vector2D<f64, ()>,
556 },
557 /// Mouse scroll wheel turn.
558 DeviceMouseWheel {
559 /// Mouse device that generated the event.
560 device: DeviceId,
561 /// Delta of change in the mouse scroll wheel state.
562 delta: MouseScrollDelta,
563 },
564 /// Motion on some analog axis.
565 ///
566 /// This includes the mouse device and any other that fits.
567 DeviceMotion {
568 /// Device that generated the event.
569 device: DeviceId,
570 /// Device dependent axis of the motion.
571 axis: AxisId,
572 /// Device dependent value.
573 value: f64,
574 },
575 /// Device button press or release.
576 DeviceButton {
577 /// Device that generated the event.
578 device: DeviceId,
579 /// Device dependent button that was used.
580 button: ButtonId,
581 /// If the button was pressed or released.
582 state: ButtonState,
583 },
584 /// Device key press or release.
585 DeviceKey {
586 /// Device that generated the key event.
587 device: DeviceId,
588 /// Physical key.
589 key_code: KeyCode,
590 /// If the key was pressed or released.
591 state: KeyState,
592 },
593 /// User responded to a native message dialog.
594 MsgDialogResponse(DialogId, MsgDialogResponse),
595 /// User responded to a native file dialog.
596 FileDialogResponse(DialogId, FileDialogResponse),
597
598 /// Accessibility info tree is now required for the window.
599 AccessInit {
600 /// Window that must now build access info.
601 window: WindowId,
602 },
603 /// Accessibility command.
604 AccessCommand {
605 /// Window that had pixels copied.
606 window: WindowId,
607 /// Target widget.
608 target: AccessNodeId,
609 /// Command.
610 command: AccessCmd,
611 },
612 /// Accessibility info tree is no longer needed for the window.
613 ///
614 /// Note that accessibility may be enabled again after this. It is not an error
615 /// to send access updates after this, but they will be ignored.
616 AccessDeinit {
617 /// Window that can release access info.
618 window: WindowId,
619 },
620
621 /// System low memory warning, some platforms may kill the app if it does not release memory.
622 LowMemory,
623
624 /// An internal component panicked, but the view-process managed to recover from it without
625 /// needing to respawn.
626 RecoveredFromComponentPanic {
627 /// Component identifier.
628 component: Txt,
629 /// How the view-process recovered from the panic.
630 recover: Txt,
631 /// The panic.
632 panic: Txt,
633 },
634
635 /// Represents a custom event send by the extension.
636 ExtensionEvent(ApiExtensionId, ApiExtensionPayload),
637}
638impl Event {
639 /// Change `self` to incorporate `other` or returns `other` if both events cannot be coalesced.
640 #[expect(clippy::result_large_err)]
641 pub fn coalesce(&mut self, other: Event) -> Result<(), Event> {
642 use Event::*;
643
644 match (self, other) {
645 (
646 MouseMoved {
647 window,
648 device,
649 coalesced_pos,
650 position,
651 },
652 MouseMoved {
653 window: n_window,
654 device: n_device,
655 coalesced_pos: n_coal_pos,
656 position: n_pos,
657 },
658 ) if *window == n_window && *device == n_device => {
659 coalesced_pos.push(*position);
660 coalesced_pos.extend(n_coal_pos);
661 *position = n_pos;
662 }
663 (
664 DragMoved {
665 window,
666 coalesced_pos,
667 position,
668 },
669 DragMoved {
670 window: n_window,
671 coalesced_pos: n_coal_pos,
672 position: n_pos,
673 },
674 ) if *window == n_window => {
675 coalesced_pos.push(*position);
676 coalesced_pos.extend(n_coal_pos);
677 *position = n_pos;
678 }
679 // raw mouse motion.
680 (
681 DeviceMouseMotion { device, delta },
682 DeviceMouseMotion {
683 device: n_device,
684 delta: n_delta,
685 },
686 ) if *device == n_device => {
687 *delta += n_delta;
688 }
689
690 // wheel scroll.
691 (
692 MouseWheel {
693 window,
694 device,
695 delta: MouseScrollDelta::LineDelta(delta_x, delta_y),
696 phase,
697 },
698 MouseWheel {
699 window: n_window,
700 device: n_device,
701 delta: MouseScrollDelta::LineDelta(n_delta_x, n_delta_y),
702 phase: n_phase,
703 },
704 ) if *window == n_window && *device == n_device && *phase == n_phase => {
705 *delta_x += n_delta_x;
706 *delta_y += n_delta_y;
707 }
708
709 // trackpad scroll-move.
710 (
711 MouseWheel {
712 window,
713 device,
714 delta: MouseScrollDelta::PixelDelta(delta_x, delta_y),
715 phase,
716 },
717 MouseWheel {
718 window: n_window,
719 device: n_device,
720 delta: MouseScrollDelta::PixelDelta(n_delta_x, n_delta_y),
721 phase: n_phase,
722 },
723 ) if *window == n_window && *device == n_device && *phase == n_phase => {
724 *delta_x += n_delta_x;
725 *delta_y += n_delta_y;
726 }
727
728 // raw wheel scroll.
729 (
730 DeviceMouseWheel {
731 device,
732 delta: MouseScrollDelta::LineDelta(delta_x, delta_y),
733 },
734 DeviceMouseWheel {
735 device: n_device,
736 delta: MouseScrollDelta::LineDelta(n_delta_x, n_delta_y),
737 },
738 ) if *device == n_device => {
739 *delta_x += n_delta_x;
740 *delta_y += n_delta_y;
741 }
742
743 // raw trackpad scroll-move.
744 (
745 DeviceMouseWheel {
746 device,
747 delta: MouseScrollDelta::PixelDelta(delta_x, delta_y),
748 },
749 DeviceMouseWheel {
750 device: n_device,
751 delta: MouseScrollDelta::PixelDelta(n_delta_x, n_delta_y),
752 },
753 ) if *device == n_device => {
754 *delta_x += n_delta_x;
755 *delta_y += n_delta_y;
756 }
757
758 // touch
759 (
760 Touch { window, device, touches },
761 Touch {
762 window: n_window,
763 device: n_device,
764 touches: mut n_touches,
765 },
766 ) if *window == n_window && *device == n_device => {
767 touches.append(&mut n_touches);
768 }
769
770 // window changed.
771 (WindowChanged(change), WindowChanged(n_change))
772 if change.window == n_change.window && change.cause == n_change.cause && change.frame_wait_id.is_none() =>
773 {
774 if n_change.state.is_some() {
775 change.state = n_change.state;
776 }
777
778 if n_change.position.is_some() {
779 change.position = n_change.position;
780 }
781
782 if n_change.monitor.is_some() {
783 change.monitor = n_change.monitor;
784 }
785
786 if n_change.size.is_some() {
787 change.size = n_change.size;
788 }
789
790 if n_change.safe_padding.is_some() {
791 change.safe_padding = n_change.safe_padding;
792 }
793
794 change.frame_wait_id = n_change.frame_wait_id;
795 }
796 // window focus changed.
797 (FocusChanged { prev, new }, FocusChanged { prev: n_prev, new: n_new })
798 if prev.is_some() && new.is_none() && n_prev.is_none() && n_new.is_some() =>
799 {
800 *new = n_new;
801 }
802 // IME commit replaces preview.
803 (
804 Ime {
805 window,
806 ime: ime @ self::Ime::Preview(_, _),
807 },
808 Ime {
809 window: n_window,
810 ime: n_ime @ self::Ime::Commit(_),
811 },
812 ) if *window == n_window => {
813 *ime = n_ime;
814 }
815 // scale factor.
816 (
817 ScaleFactorChanged {
818 monitor,
819 windows,
820 scale_factor,
821 },
822 ScaleFactorChanged {
823 monitor: n_monitor,
824 windows: n_windows,
825 scale_factor: n_scale_factor,
826 },
827 ) if *monitor == n_monitor => {
828 for w in n_windows {
829 if !windows.contains(&w) {
830 windows.push(w);
831 }
832 }
833 *scale_factor = n_scale_factor;
834 }
835 // fonts changed.
836 (FontsChanged, FontsChanged) => {}
837 // text aa.
838 (FontAaChanged(config), FontAaChanged(n_config)) => {
839 *config = n_config;
840 }
841 // double-click timeout.
842 (MultiClickConfigChanged(config), MultiClickConfigChanged(n_config)) => {
843 *config = n_config;
844 }
845 // touch config.
846 (TouchConfigChanged(config), TouchConfigChanged(n_config)) => {
847 *config = n_config;
848 }
849 // animation enabled and caret speed.
850 (AnimationsConfigChanged(config), AnimationsConfigChanged(n_config)) => {
851 *config = n_config;
852 }
853 // key repeat delay and speed.
854 (KeyRepeatConfigChanged(config), KeyRepeatConfigChanged(n_config)) => {
855 *config = n_config;
856 }
857 // locale
858 (LocaleChanged(config), LocaleChanged(n_config)) => {
859 *config = n_config;
860 }
861 // drag hovered
862 (
863 DragHovered {
864 window,
865 data,
866 allowed: effects,
867 },
868 DragHovered {
869 window: n_window,
870 data: mut n_data,
871 allowed: n_effects,
872 },
873 ) if *window == n_window && effects.contains(n_effects) => {
874 data.append(&mut n_data);
875 }
876 // drag dropped
877 (
878 DragDropped {
879 window,
880 data,
881 allowed,
882 drop_id,
883 },
884 DragDropped {
885 window: n_window,
886 data: mut n_data,
887 allowed: n_allowed,
888 drop_id: n_drop_id,
889 },
890 ) if *window == n_window && allowed.contains(n_allowed) && *drop_id == n_drop_id => {
891 data.append(&mut n_data);
892 }
893 // drag cancelled
894 (DragCancelled { window }, DragCancelled { window: n_window }) if *window == n_window => {}
895 (_, e) => return Err(e),
896 }
897 Ok(())
898 }
899}
900
901/// View Process IPC result.
902pub(crate) type VpResult<T> = std::result::Result<T, ipc::ViewChannelError>;
903
904/// Offset and color in a gradient.
905#[repr(C)]
906#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize)]
907pub struct GradientStop {
908 /// Offset in pixels.
909 pub offset: f32,
910 /// Color at the offset.
911 pub color: Rgba,
912}
913
914/// Border side line style and color.
915#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize)]
916pub struct BorderSide {
917 /// Line color.
918 pub color: Rgba,
919 /// Line Style.
920 pub style: BorderStyle,
921}
922
923/// Defines if a widget is part of the same 3D space as the parent.
924#[derive(Default, Clone, Copy, serde::Deserialize, Eq, Hash, PartialEq, serde::Serialize)]
925#[repr(u8)]
926pub enum TransformStyle {
927 /// Widget is not a part of the 3D space of the parent. If it has
928 /// 3D children they will be rendered into a flat plane that is placed in the 3D space of the parent.
929 #[default]
930 Flat = 0,
931 /// Widget is a part of the 3D space of the parent. If it has 3D children
932 /// they will be positioned relative to siblings in the same space.
933 ///
934 /// Note that some properties require a flat image to work on, in particular all pixel filter properties including opacity.
935 /// When such a property is set in a widget that is `Preserve3D` and has both a parent and one child also `Preserve3D` the
936 /// filters are ignored and a warning is logged. When the widget is `Preserve3D` and the parent is not the filters are applied
937 /// *outside* the 3D space, when the widget is `Preserve3D` with all `Flat` children the filters are applied *inside* the 3D space.
938 Preserve3D = 1,
939}
940impl fmt::Debug for TransformStyle {
941 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
942 if f.alternate() {
943 write!(f, "TransformStyle::")?;
944 }
945 match self {
946 Self::Flat => write!(f, "Flat"),
947 Self::Preserve3D => write!(f, "Preserve3D"),
948 }
949 }
950}
951
952/// Identifies a reference frame.
953///
954/// This ID is mostly defined by the app process. IDs that set the most significant
955/// bit of the second part (`id.1 & (1 << 63) != 0`) are reserved for the view process.
956#[derive(Default, Debug, Clone, Copy, serde::Deserialize, Eq, Hash, PartialEq, serde::Serialize)]
957pub struct ReferenceFrameId(pub u64, pub u64);
958impl ReferenceFrameId {
959 /// If ID does not set the bit that indicates it is generated by the view process.
960 pub fn is_app_generated(&self) -> bool {
961 self.1 & (1 << 63) == 0
962 }
963}
964
965/// Nine-patch border repeat mode.
966///
967/// Defines how the edges and middle region of a nine-patch border is filled.
968#[repr(u8)]
969#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize, Default)]
970pub enum RepeatMode {
971 /// The source image's edge regions are stretched to fill the gap between each border.
972 #[default]
973 Stretch,
974 /// The source image's edge regions are tiled (repeated) to fill the gap between each
975 /// border. Tiles may be clipped to achieve the proper fit.
976 Repeat,
977 /// The source image's edge regions are tiled (repeated) to fill the gap between each
978 /// border. Tiles may be stretched to achieve the proper fit.
979 Round,
980 /// The source image's edge regions are tiled (repeated) to fill the gap between each
981 /// border. Extra space will be distributed in between tiles to achieve the proper fit.
982 Space,
983}
984#[cfg(feature = "var")]
985zng_var::impl_from_and_into_var! {
986 /// Converts `true` to `Repeat` and `false` to the default `Stretch`.
987 fn from(value: bool) -> RepeatMode {
988 match value {
989 true => RepeatMode::Repeat,
990 false => RepeatMode::Stretch,
991 }
992 }
993}
994
995/// Color mix blend mode.
996#[allow(missing_docs)]
997#[repr(u8)]
998#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Default)]
999#[non_exhaustive]
1000pub enum MixBlendMode {
1001 #[default]
1002 Normal = 0,
1003 Multiply = 1,
1004 Screen = 2,
1005 Overlay = 3,
1006 Darken = 4,
1007 Lighten = 5,
1008 ColorDodge = 6,
1009 ColorBurn = 7,
1010 HardLight = 8,
1011 SoftLight = 9,
1012 Difference = 10,
1013 Exclusion = 11,
1014 Hue = 12,
1015 Saturation = 13,
1016 Color = 14,
1017 Luminosity = 15,
1018 PlusLighter = 16,
1019}
1020
1021/// Image scaling algorithm in the renderer.
1022///
1023/// If an image is not rendered at the same size as their source it must be up-scaled or
1024/// down-scaled. The algorithms used for this scaling can be selected using this `enum`.
1025///
1026/// Note that the algorithms used in the renderer value performance over quality and do a good
1027/// enough job for small or temporary changes in scale only, such as a small size correction or a scaling animation.
1028/// If and image is constantly rendered at a different scale you should considered scaling it on the CPU using a
1029/// slower but more complex algorithm or pre-scaling it before including in the app.
1030#[repr(u8)]
1031#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
1032#[non_exhaustive]
1033pub enum ImageRendering {
1034 /// Let the renderer select the algorithm, currently this is the same as [`CrispEdges`].
1035 ///
1036 /// [`CrispEdges`]: ImageRendering::CrispEdges
1037 Auto = 0,
1038 /// The image is scaled with an algorithm that preserves contrast and edges in the image,
1039 /// and which does not smooth colors or introduce blur to the image in the process.
1040 ///
1041 /// Currently the [Bilinear] interpolation algorithm is used.
1042 ///
1043 /// [Bilinear]: https://en.wikipedia.org/wiki/Bilinear_interpolation
1044 CrispEdges = 1,
1045 /// When scaling the image up, the image appears to be composed of large pixels.
1046 ///
1047 /// Currently the [Nearest-neighbor] interpolation algorithm is used.
1048 ///
1049 /// [Nearest-neighbor]: https://en.wikipedia.org/wiki/Nearest-neighbor_interpolation
1050 Pixelated = 2,
1051}
1052
1053/// Pixel color alpha type.
1054#[repr(u8)]
1055#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
1056pub enum AlphaType {
1057 /// Components are not pre-multiplied by alpha.
1058 Alpha = 0,
1059 /// Components are pre-multiplied by alpha.
1060 PremultipliedAlpha = 1,
1061}
1062
1063/// Gradient extend mode.
1064#[allow(missing_docs)]
1065#[repr(u8)]
1066#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
1067pub enum ExtendMode {
1068 Clamp,
1069 Repeat,
1070}
1071
1072/// Orientation of a straight line.
1073#[derive(Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1074pub enum LineOrientation {
1075 /// Top-to-bottom line.
1076 Vertical,
1077 /// Left-to-right line.
1078 Horizontal,
1079}
1080impl fmt::Debug for LineOrientation {
1081 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1082 if f.alternate() {
1083 write!(f, "LineOrientation::")?;
1084 }
1085 match self {
1086 LineOrientation::Vertical => {
1087 write!(f, "Vertical")
1088 }
1089 LineOrientation::Horizontal => {
1090 write!(f, "Horizontal")
1091 }
1092 }
1093 }
1094}
1095
1096/// Represents a line style.
1097#[allow(missing_docs)]
1098#[repr(u8)]
1099#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
1100#[non_exhaustive]
1101pub enum LineStyle {
1102 Solid,
1103 Dotted,
1104 Dashed,
1105
1106 /// A wavy line, like an error underline.
1107 ///
1108 /// The wave magnitude is defined by the overall line thickness, the associated value
1109 /// here defines the thickness of the wavy line.
1110 Wavy(f32),
1111}
1112
1113/// The line style for the sides of a widget's border.
1114#[repr(u8)]
1115#[derive(Default, Debug, Clone, Copy, PartialEq, Hash, Eq, serde::Serialize, serde::Deserialize)]
1116#[non_exhaustive]
1117pub enum BorderStyle {
1118 /// No border line.
1119 #[default]
1120 None = 0,
1121
1122 /// A single straight solid line.
1123 Solid = 1,
1124 /// Two straight solid lines that add up to the pixel size defined by the side width.
1125 Double = 2,
1126
1127 /// Displays a series of rounded dots.
1128 Dotted = 3,
1129 /// Displays a series of short square-ended dashes or line segments.
1130 Dashed = 4,
1131
1132 /// Fully transparent line.
1133 Hidden = 5,
1134
1135 /// Displays a border with a carved appearance.
1136 Groove = 6,
1137 /// Displays a border with an extruded appearance.
1138 Ridge = 7,
1139
1140 /// Displays a border that makes the widget appear embedded.
1141 Inset = 8,
1142 /// Displays a border that makes the widget appear embossed.
1143 Outset = 9,
1144}
1145
1146/// Result of a focus request.
1147#[repr(u8)]
1148#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
1149#[non_exhaustive]
1150pub enum FocusResult {
1151 /// Focus was requested, an [`Event::FocusChanged`] will be send if the operating system gives focus to the window.
1152 Requested,
1153 /// Window is already focused.
1154 AlreadyFocused,
1155}
1156
1157#[cfg(test)]
1158mod tests {
1159 use super::*;
1160
1161 #[test]
1162 fn key_code_iter() {
1163 let mut iter = KeyCode::all_identified();
1164 let first = iter.next().unwrap();
1165 assert_eq!(first, KeyCode::Backquote);
1166
1167 for k in iter {
1168 assert_eq!(k.name(), &format!("{:?}", k));
1169 }
1170 }
1171}