lgui-core 0.2.0

Platform-neutral runtime and UI primitives for LGUI
Documentation
use std::{future::Future, ops::Range, sync::Arc};

use super::{
    ActionId, KeyboardEvent, PointerData, UiAction, UiAsyncContext, UiEventContext, UiId,
    WheelDelta,
};

pub type UiEventHandler = Arc<dyn Fn(&mut UiEventContext) + Send + Sync>;
pub type UiInputEventHandler = Arc<dyn Fn(&mut UiEventContext, &UiEventPayload) + Send + Sync>;
pub type UiActionHandler = Arc<dyn Fn(&mut UiEventContext, &UiAction) + Send + Sync>;
pub type UiValueEventHandler<A> = Arc<dyn Fn(&mut UiEventContext, A) + Send + Sync>;

pub fn async_handler<F, Fut>(handler: F) -> UiEventHandler
where
    F: Fn(UiAsyncContext) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = ()> + Send + 'static,
{
    Arc::new(move |context| {
        let future = handler(context.async_context());
        let _ = context.spawn(future);
    })
}

pub fn async_handler_with<A, F, Fut>(handler: F) -> UiValueEventHandler<A>
where
    A: Send + 'static,
    F: Fn(UiAsyncContext, A) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = ()> + Send + 'static,
{
    Arc::new(move |context, argument| {
        let future = handler(context.async_context(), argument);
        let _ = context.spawn(future);
    })
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum UiEventKind {
    Click,
    PointerDown,
    PointerMove,
    PointerUp,
    Wheel,
    KeyDown,
    KeyUp,
    Input,
    CompositionStart,
    CompositionUpdate,
    CompositionEnd,
    Focus,
    Blur,
    Change,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum UiEventPayload {
    Click,
    PointerDown {
        pointer: PointerData,
    },
    PointerMove {
        pointer: PointerData,
    },
    PointerUp {
        pointer: PointerData,
    },
    Wheel {
        delta: WheelDelta,
    },
    Keyboard {
        event: KeyboardEvent,
    },
    Input {
        text: String,
    },
    CompositionStart,
    CompositionUpdate {
        text: String,
        cursor: Option<Range<usize>>,
    },
    CompositionEnd,
    Focus,
    Blur,
    Change {
        value: Option<String>,
    },
}

impl UiEventPayload {
    pub const fn kind(&self) -> UiEventKind {
        match self {
            Self::Click => UiEventKind::Click,
            Self::PointerDown { .. } => UiEventKind::PointerDown,
            Self::PointerMove { .. } => UiEventKind::PointerMove,
            Self::PointerUp { .. } => UiEventKind::PointerUp,
            Self::Wheel { .. } => UiEventKind::Wheel,
            Self::Keyboard {
                event:
                    KeyboardEvent {
                        state: super::KeyState::Down,
                        ..
                    },
            } => UiEventKind::KeyDown,
            Self::Keyboard { .. } => UiEventKind::KeyUp,
            Self::Input { .. } => UiEventKind::Input,
            Self::CompositionStart => UiEventKind::CompositionStart,
            Self::CompositionUpdate { .. } => UiEventKind::CompositionUpdate,
            Self::CompositionEnd => UiEventKind::CompositionEnd,
            Self::Focus => UiEventKind::Focus,
            Self::Blur => UiEventKind::Blur,
            Self::Change { .. } => UiEventKind::Change,
        }
    }
}

#[derive(Clone)]
pub struct UiInputEventBinding {
    pub kind: UiEventKind,
    pub capture: bool,
    pub handler: UiInputEventHandler,
}

#[derive(Clone)]
pub struct UiHandlerEvent {
    pub target: UiId,
    pub payload: UiEventPayload,
    pub capture_handlers: Vec<UiInputEventHandler>,
    pub bubble_handlers: Vec<UiInputEventHandler>,
}

impl std::fmt::Debug for UiHandlerEvent {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        formatter
            .debug_struct("UiHandlerEvent")
            .field("target", &self.target)
            .field("payload", &self.payload)
            .field("capture_handlers", &self.capture_handlers.len())
            .field("bubble_handlers", &self.bubble_handlers.len())
            .finish()
    }
}

#[derive(Clone)]
pub struct UiActionEvent {
    pub target: UiId,
    pub action: UiAction,
    pub handler: UiActionHandler,
}

impl UiActionEvent {
    pub fn dispatch(&self, context: &mut UiEventContext) {
        (self.handler)(context, &self.action);
    }
}

impl std::fmt::Debug for UiActionEvent {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        formatter
            .debug_struct("UiActionEvent")
            .field("target", &self.target)
            .field("action", &self.action)
            .finish_non_exhaustive()
    }
}

pub struct UiActionBinding {
    pub id: ActionId,
    pub handler: UiActionHandler,
}

impl Clone for UiActionBinding {
    fn clone(&self) -> Self {
        Self {
            id: self.id.clone(),
            handler: Arc::clone(&self.handler),
        }
    }
}

pub trait IntoUiHandler {
    fn into_handler(self) -> UiEventHandler;
}

impl<F> IntoUiHandler for F
where
    F: Fn(&mut UiEventContext) + Send + Sync + 'static,
{
    fn into_handler(self) -> UiEventHandler {
        Arc::new(self)
    }
}

impl IntoUiHandler for UiEventHandler {
    fn into_handler(self) -> UiEventHandler {
        self
    }
}

#[cfg(test)]
#[path = "handler_test.rs"]
mod tests;