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}