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