zsui 0.3.0-alpha.2

Rust-first feature-gated native UI framework for Win32, AppKit, and Linux.
Documentation
use crate::{
    command_protocol::UiCommand,
    geometry::{Point, Size},
};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ComponentPhase {
    New,
    Mounted,
    Active,
    Suspended,
    Unmounted,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LifecycleEvent {
    Mount,
    Resume,
    Suspend,
    Unmount,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LifecycleState {
    phase: ComponentPhase,
}

impl LifecycleState {
    pub const fn new() -> Self {
        Self {
            phase: ComponentPhase::New,
        }
    }

    pub const fn phase(self) -> ComponentPhase {
        self.phase
    }

    pub fn apply(&mut self, event: LifecycleEvent) -> bool {
        let next = match (self.phase, event) {
            (ComponentPhase::New, LifecycleEvent::Mount) => ComponentPhase::Mounted,
            (ComponentPhase::Mounted | ComponentPhase::Suspended, LifecycleEvent::Resume) => {
                ComponentPhase::Active
            }
            (ComponentPhase::Active, LifecycleEvent::Suspend) => ComponentPhase::Suspended,
            (
                ComponentPhase::Mounted | ComponentPhase::Active | ComponentPhase::Suspended,
                LifecycleEvent::Unmount,
            ) => ComponentPhase::Unmounted,
            _ => return false,
        };
        self.phase = next;
        true
    }
}

impl Default for LifecycleState {
    fn default() -> Self {
        Self::new()
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MouseButton {
    Left,
    Right,
    Middle,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyState {
    Down,
    Up,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum UiEvent<AppEvent = crate::AppEvent> {
    Application(AppEvent),
    Lifecycle(LifecycleEvent),
    PointerMove {
        position: Point,
    },
    PointerHover {
        position: Point,
    },
    PointerLeave,
    PointerCancel,
    PointerButton {
        position: Point,
        button: MouseButton,
        pressed: bool,
        click_count: u8,
    },
    MouseWheel {
        delta: i32,
    },
    Key {
        code: u32,
        state: KeyState,
        system: bool,
    },
    TextInput(String),
    Command(UiCommand),
    ControlCommand {
        control_id: u32,
        notification: u16,
    },
    ControlSelectionChanged {
        control_id: u32,
        index: usize,
    },
    GlobalHotkey {
        id: i32,
    },
    ClipboardChanged,
    Timer {
        id: u64,
    },
    WindowSize {
        size: Size,
        minimized: bool,
    },
    AppActivationChanged {
        active: bool,
    },
    SystemMetricsChanged,
    WindowMoved,
    WindowMoveCompleted,
    CloseRequested,
    ThemeChanged,
    DpiChanged {
        dpi: u32,
    },
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn lifecycle_rejects_out_of_order_transitions() {
        let mut lifecycle = LifecycleState::new();
        assert!(!lifecycle.apply(LifecycleEvent::Resume));
        assert!(lifecycle.apply(LifecycleEvent::Mount));
        assert!(lifecycle.apply(LifecycleEvent::Resume));
        assert_eq!(lifecycle.phase(), ComponentPhase::Active);
        assert!(lifecycle.apply(LifecycleEvent::Suspend));
        assert!(lifecycle.apply(LifecycleEvent::Unmount));
        assert!(!lifecycle.apply(LifecycleEvent::Resume));
    }
}