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