use crate::{
access::{AccessCmd, AccessNodeId},
api_extension::{ApiExtensionId, ApiExtensionPayload, ApiExtensions},
config::{AnimationsConfig, ColorScheme, FontAntiAliasing, KeyRepeatConfig, LocaleConfig, MultiClickConfig, TouchConfig},
dialog::{DialogId, FileDialogResponse, MsgDialogResponse},
image::{ImageId, ImageLoadedData, ImagePpi},
ipc::IpcBytes,
keyboard::{Key, KeyCode, KeyState},
mouse::{ButtonId, ButtonState, MouseButton, MouseScrollDelta},
touch::{TouchPhase, TouchUpdate},
window::{EventFrameRendered, FrameId, HeadlessOpenData, MonitorId, MonitorInfo, WindowChanged, WindowId, WindowOpenData},
};
use serde::{Deserialize, Serialize};
use std::{fmt, path::PathBuf};
use zng_txt::Txt;
use zng_unit::{DipPoint, PxRect, PxSize, Rgba};
macro_rules! declare_id {
($(
$(#[$docs:meta])+
pub struct $Id:ident(_);
)+) => {$(
$(#[$docs])+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub struct $Id(u32);
impl $Id {
pub const INVALID: Self = Self(0);
pub const fn first() -> Self {
Self(1)
}
#[must_use]
pub const fn next(self) -> Self {
let r = Self(self.0.wrapping_add(1));
if r.0 == Self::INVALID.0 {
Self::first()
} else {
r
}
}
#[must_use]
pub fn incr(&mut self) -> Self {
std::mem::replace(self, self.next())
}
pub const fn get(self) -> u32 {
self.0
}
pub const fn from_raw(id: u32) -> Self {
Self(id)
}
}
)+};
}
pub(crate) use declare_id;
declare_id! {
pub struct DeviceId(_);
pub struct ViewProcessGen(_);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct AxisId(pub u32);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Inited {
pub generation: ViewProcessGen,
pub is_respawn: bool,
pub available_monitors: Vec<(MonitorId, MonitorInfo)>,
pub multi_click_config: MultiClickConfig,
pub key_repeat_config: KeyRepeatConfig,
pub touch_config: TouchConfig,
pub font_aa: FontAntiAliasing,
pub animations_config: AnimationsConfig,
pub locale_config: LocaleConfig,
pub color_scheme: ColorScheme,
pub extensions: ApiExtensions,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Ime {
Preview(Txt, (usize, usize)),
Commit(Txt),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Event {
Inited(Inited),
Disconnected(ViewProcessGen),
WindowOpened(WindowId, WindowOpenData),
HeadlessOpened(WindowId, HeadlessOpenData),
WindowOrHeadlessOpenError {
id: WindowId,
error: Txt,
},
FrameRendered(EventFrameRendered),
WindowChanged(WindowChanged),
DroppedFile {
window: WindowId,
file: PathBuf,
},
HoveredFile {
window: WindowId,
file: PathBuf,
},
HoveredFileCancelled(WindowId),
FocusChanged {
prev: Option<WindowId>,
new: Option<WindowId>,
},
KeyboardInput {
window: WindowId,
device: DeviceId,
key_code: KeyCode,
state: KeyState,
key: Key,
key_modified: Key,
text: Txt,
},
Ime {
window: WindowId,
ime: Ime,
},
MouseMoved {
window: WindowId,
device: DeviceId,
coalesced_pos: Vec<DipPoint>,
position: DipPoint,
},
MouseEntered {
window: WindowId,
device: DeviceId,
},
MouseLeft {
window: WindowId,
device: DeviceId,
},
MouseWheel {
window: WindowId,
device: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
},
MouseInput {
window: WindowId,
device: DeviceId,
state: ButtonState,
button: MouseButton,
},
TouchpadPressure {
window: WindowId,
device: DeviceId,
pressure: f32,
stage: i64,
},
AxisMotion {
window: WindowId,
device: DeviceId,
axis: AxisId,
value: f64,
},
Touch {
window: WindowId,
device: DeviceId,
touches: Vec<TouchUpdate>,
},
ScaleFactorChanged {
monitor: MonitorId,
windows: Vec<WindowId>,
scale_factor: f32,
},
MonitorsChanged(Vec<(MonitorId, MonitorInfo)>),
ColorSchemeChanged(WindowId, ColorScheme),
WindowCloseRequested(WindowId),
WindowClosed(WindowId),
ImageMetadataLoaded {
image: ImageId,
size: PxSize,
ppi: Option<ImagePpi>,
is_mask: bool,
},
ImageLoaded(ImageLoadedData),
ImagePartiallyLoaded {
image: ImageId,
partial_size: PxSize,
ppi: Option<ImagePpi>,
is_opaque: bool,
is_mask: bool,
partial_pixels: IpcBytes,
},
ImageLoadError {
image: ImageId,
error: Txt,
},
ImageEncoded {
image: ImageId,
format: Txt,
data: IpcBytes,
},
ImageEncodeError {
image: ImageId,
format: Txt,
error: Txt,
},
FrameImageReady {
window: WindowId,
frame: FrameId,
image: ImageId,
selection: PxRect,
},
FontsChanged,
FontAaChanged(FontAntiAliasing),
MultiClickConfigChanged(MultiClickConfig),
AnimationsConfigChanged(AnimationsConfig),
KeyRepeatConfigChanged(KeyRepeatConfig),
TouchConfigChanged(TouchConfig),
LocaleChanged(LocaleConfig),
DeviceAdded(DeviceId),
DeviceRemoved(DeviceId),
DeviceMouseMotion {
device: DeviceId,
delta: euclid::Vector2D<f64, ()>,
},
DeviceMouseWheel {
device: DeviceId,
delta: MouseScrollDelta,
},
DeviceMotion {
device: DeviceId,
axis: AxisId,
value: f64,
},
DeviceButton {
device: DeviceId,
button: ButtonId,
state: ButtonState,
},
DeviceKey {
device: DeviceId,
key_code: KeyCode,
state: KeyState,
},
MsgDialogResponse(DialogId, MsgDialogResponse),
FileDialogResponse(DialogId, FileDialogResponse),
AccessInit {
window: WindowId,
},
AccessCommand {
window: WindowId,
target: AccessNodeId,
command: AccessCmd,
},
AccessDeinit {
window: WindowId,
},
LowMemory,
RecoveredFromComponentPanic {
component: Txt,
recover: Txt,
panic: Txt,
},
ExtensionEvent(ApiExtensionId, ApiExtensionPayload),
}
impl Event {
#[allow(clippy::result_large_err)]
pub fn coalesce(&mut self, other: Event) -> Result<(), Event> {
use Event::*;
match (self, other) {
(
MouseMoved {
window,
device,
coalesced_pos,
position,
},
MouseMoved {
window: n_window,
device: n_device,
coalesced_pos: n_coal_pos,
position: n_pos,
},
) if *window == n_window && *device == n_device => {
coalesced_pos.push(*position);
coalesced_pos.extend(n_coal_pos);
*position = n_pos;
}
(
DeviceMouseMotion { device, delta },
DeviceMouseMotion {
device: n_device,
delta: n_delta,
},
) if *device == n_device => {
*delta += n_delta;
}
(
MouseWheel {
window,
device,
delta: MouseScrollDelta::LineDelta(delta_x, delta_y),
phase,
},
MouseWheel {
window: n_window,
device: n_device,
delta: MouseScrollDelta::LineDelta(n_delta_x, n_delta_y),
phase: n_phase,
},
) if *window == n_window && *device == n_device && *phase == n_phase => {
*delta_x += n_delta_x;
*delta_y += n_delta_y;
}
(
MouseWheel {
window,
device,
delta: MouseScrollDelta::PixelDelta(delta_x, delta_y),
phase,
},
MouseWheel {
window: n_window,
device: n_device,
delta: MouseScrollDelta::PixelDelta(n_delta_x, n_delta_y),
phase: n_phase,
},
) if *window == n_window && *device == n_device && *phase == n_phase => {
*delta_x += n_delta_x;
*delta_y += n_delta_y;
}
(
DeviceMouseWheel {
device,
delta: MouseScrollDelta::LineDelta(delta_x, delta_y),
},
DeviceMouseWheel {
device: n_device,
delta: MouseScrollDelta::LineDelta(n_delta_x, n_delta_y),
},
) if *device == n_device => {
*delta_x += n_delta_x;
*delta_y += n_delta_y;
}
(
DeviceMouseWheel {
device,
delta: MouseScrollDelta::PixelDelta(delta_x, delta_y),
},
DeviceMouseWheel {
device: n_device,
delta: MouseScrollDelta::PixelDelta(n_delta_x, n_delta_y),
},
) if *device == n_device => {
*delta_x += n_delta_x;
*delta_y += n_delta_y;
}
(
Touch { window, device, touches },
Touch {
window: n_window,
device: n_device,
touches: mut n_touches,
},
) if *window == n_window && *device == n_device => {
touches.append(&mut n_touches);
}
(WindowChanged(change), WindowChanged(n_change))
if change.window == n_change.window && change.cause == n_change.cause && change.frame_wait_id.is_none() =>
{
if n_change.state.is_some() {
change.state = n_change.state;
}
if n_change.position.is_some() {
change.position = n_change.position;
}
if n_change.monitor.is_some() {
change.monitor = n_change.monitor;
}
if n_change.size.is_some() {
change.size = n_change.size;
}
change.frame_wait_id = n_change.frame_wait_id;
}
(FocusChanged { prev, new }, FocusChanged { prev: n_prev, new: n_new })
if prev.is_some() && new.is_none() && n_prev.is_none() && n_new.is_some() =>
{
*new = n_new;
}
(
Ime {
window,
ime: ime @ self::Ime::Preview(_, _),
},
Ime {
window: n_window,
ime: n_ime @ self::Ime::Commit(_),
},
) if *window == n_window => {
*ime = n_ime;
}
(
ScaleFactorChanged {
monitor,
windows,
scale_factor,
},
ScaleFactorChanged {
monitor: n_monitor,
windows: n_windows,
scale_factor: n_scale_factor,
},
) if *monitor == n_monitor => {
for w in n_windows {
if !windows.contains(&w) {
windows.push(w);
}
}
*scale_factor = n_scale_factor;
}
(FontsChanged, FontsChanged) => {}
(FontAaChanged(config), FontAaChanged(n_config)) => {
*config = n_config;
}
(MultiClickConfigChanged(config), MultiClickConfigChanged(n_config)) => {
*config = n_config;
}
(TouchConfigChanged(config), TouchConfigChanged(n_config)) => {
*config = n_config;
}
(AnimationsConfigChanged(config), AnimationsConfigChanged(n_config)) => {
*config = n_config;
}
(KeyRepeatConfigChanged(config), KeyRepeatConfigChanged(n_config)) => {
*config = n_config;
}
(LocaleChanged(config), LocaleChanged(n_config)) => {
*config = n_config;
}
(_, e) => return Err(e),
}
Ok(())
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
pub struct ViewProcessOffline;
impl fmt::Display for ViewProcessOffline {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "view-process disconnected or is initing, try again after the init event")
}
}
impl std::error::Error for ViewProcessOffline {}
pub(crate) type VpResult<T> = std::result::Result<T, ViewProcessOffline>;
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct GradientStop {
pub offset: f32,
pub color: Rgba,
}
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct BorderSide {
pub color: Rgba,
pub style: BorderStyle,
}
#[derive(Default, Clone, Copy, serde::Deserialize, Eq, Hash, PartialEq, serde::Serialize)]
#[repr(u8)]
pub enum TransformStyle {
#[default]
Flat = 0,
Preserve3D = 1,
}
impl fmt::Debug for TransformStyle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "TransformStyle::")?;
}
match self {
Self::Flat => write!(f, "Flat"),
Self::Preserve3D => write!(f, "Preserve3D"),
}
}
}
#[derive(Default, Debug, Clone, Copy, serde::Deserialize, Eq, Hash, PartialEq, serde::Serialize)]
pub struct ReferenceFrameId(pub u64, pub u64);
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum RepeatMode {
Stretch,
Repeat,
Round,
Space,
}
#[allow(missing_docs)]
#[repr(u8)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum MixBlendMode {
Normal = 0,
Multiply = 1,
Screen = 2,
Overlay = 3,
Darken = 4,
Lighten = 5,
ColorDodge = 6,
ColorBurn = 7,
HardLight = 8,
SoftLight = 9,
Difference = 10,
Exclusion = 11,
Hue = 12,
Saturation = 13,
Color = 14,
Luminosity = 15,
PlusLighter = 16,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum ImageRendering {
Auto = 0,
CrispEdges = 1,
Pixelated = 2,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum AlphaType {
Alpha = 0,
PremultipliedAlpha = 1,
}
#[allow(missing_docs)]
#[repr(u8)]
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
pub enum ExtendMode {
Clamp,
Repeat,
}
#[derive(Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum LineOrientation {
Vertical,
Horizontal,
}
impl fmt::Debug for LineOrientation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "LineOrientation::")?;
}
match self {
LineOrientation::Vertical => {
write!(f, "Vertical")
}
LineOrientation::Horizontal => {
write!(f, "Horizontal")
}
}
}
}
#[allow(missing_docs)]
#[repr(u8)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub enum LineStyle {
Solid,
Dotted,
Dashed,
Wavy(f32),
}
#[repr(u8)]
#[derive(Default, Debug, Clone, Copy, PartialEq, Hash, Eq, serde::Serialize, serde::Deserialize)]
pub enum BorderStyle {
#[default]
None = 0,
Solid = 1,
Double = 2,
Dotted = 3,
Dashed = 4,
Hidden = 5,
Groove = 6,
Ridge = 7,
Inset = 8,
Outset = 9,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn key_code_iter() {
let mut iter = KeyCode::all_identified();
let first = iter.next().unwrap();
assert_eq!(first, KeyCode::Backquote);
for k in iter {
assert_eq!(k.name(), &format!("{:?}", k));
}
}
}