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}