use crate::tree::{El, Rect};
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct UiTarget {
pub key: String,
pub node_id: String,
pub rect: Rect,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PointerButton {
Primary,
Secondary,
Middle,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum UiKey {
Enter,
Escape,
Tab,
Space,
ArrowUp,
ArrowDown,
ArrowLeft,
ArrowRight,
Backspace,
Delete,
Home,
End,
PageUp,
PageDown,
Character(String),
Other(String),
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct KeyModifiers {
pub shift: bool,
pub ctrl: bool,
pub alt: bool,
pub logo: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct KeyPress {
pub key: UiKey,
pub modifiers: KeyModifiers,
pub repeat: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct KeyChord {
pub key: UiKey,
pub modifiers: KeyModifiers,
}
impl KeyChord {
pub fn vim(c: char) -> Self {
Self {
key: UiKey::Character(c.to_string()),
modifiers: KeyModifiers::default(),
}
}
pub fn ctrl(c: char) -> Self {
Self {
key: UiKey::Character(c.to_string()),
modifiers: KeyModifiers {
ctrl: true,
..Default::default()
},
}
}
pub fn ctrl_shift(c: char) -> Self {
Self {
key: UiKey::Character(c.to_string()),
modifiers: KeyModifiers {
ctrl: true,
shift: true,
..Default::default()
},
}
}
pub fn named(key: UiKey) -> Self {
Self {
key,
modifiers: KeyModifiers::default(),
}
}
pub fn with_modifiers(mut self, modifiers: KeyModifiers) -> Self {
self.modifiers = modifiers;
self
}
pub fn matches(&self, key: &UiKey, modifiers: KeyModifiers) -> bool {
key_eq(&self.key, key) && self.modifiers == modifiers
}
}
fn key_eq(a: &UiKey, b: &UiKey) -> bool {
match (a, b) {
(UiKey::Character(x), UiKey::Character(y)) => x.eq_ignore_ascii_case(y),
_ => a == b,
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct UiEvent {
pub key: Option<String>,
pub target: Option<UiTarget>,
pub pointer: Option<(f32, f32)>,
pub key_press: Option<KeyPress>,
pub text: Option<String>,
pub selection: Option<crate::selection::Selection>,
pub modifiers: KeyModifiers,
pub click_count: u8,
pub path: Option<std::path::PathBuf>,
pub kind: UiEventKind,
}
impl UiEvent {
pub fn synthetic_click(key: impl Into<String>) -> Self {
Self {
kind: UiEventKind::Click,
key: Some(key.into()),
target: None,
pointer: None,
key_press: None,
text: None,
selection: None,
modifiers: KeyModifiers::default(),
click_count: 1,
path: None,
}
}
pub fn route(&self) -> Option<&str> {
self.key.as_deref()
}
pub fn target_key(&self) -> Option<&str> {
self.target.as_ref().map(|t| t.key.as_str())
}
pub fn is_route(&self, key: &str) -> bool {
self.route() == Some(key)
}
pub fn is_click_or_activate(&self, key: &str) -> bool {
matches!(self.kind, UiEventKind::Click | UiEventKind::Activate) && self.is_route(key)
}
pub fn is_hotkey(&self, action: &str) -> bool {
self.kind == UiEventKind::Hotkey && self.is_route(action)
}
pub fn pointer_pos(&self) -> Option<(f32, f32)> {
self.pointer
}
pub fn pointer_x(&self) -> Option<f32> {
self.pointer.map(|(x, _)| x)
}
pub fn pointer_y(&self) -> Option<f32> {
self.pointer.map(|(_, y)| y)
}
pub fn target_rect(&self) -> Option<Rect> {
self.target.as_ref().map(|t| t.rect)
}
pub fn text(&self) -> Option<&str> {
self.text.as_deref()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum UiEventKind {
Click,
SecondaryClick,
MiddleClick,
Activate,
Escape,
Hotkey,
KeyDown,
TextInput,
Drag,
PointerUp,
PointerDown,
SelectionChanged,
PointerEnter,
PointerLeave,
FileHovered,
FileHoverCancelled,
FileDropped,
}
#[derive(Copy, Clone, Debug)]
pub struct BuildCx<'a> {
theme: &'a crate::Theme,
ui_state: Option<&'a crate::state::UiState>,
}
impl<'a> BuildCx<'a> {
pub fn new(theme: &'a crate::Theme) -> Self {
Self {
theme,
ui_state: None,
}
}
pub fn with_ui_state(mut self, ui_state: &'a crate::state::UiState) -> Self {
self.ui_state = Some(ui_state);
self
}
pub fn theme(&self) -> &crate::Theme {
self.theme
}
pub fn palette(&self) -> &crate::Palette {
self.theme.palette()
}
pub fn hovered_key(&self) -> Option<&str> {
self.ui_state?.hovered_key()
}
pub fn is_hovering_within(&self, key: &str) -> bool {
self.ui_state
.is_some_and(|state| state.is_hovering_within(key))
}
}
pub trait App {
fn before_build(&mut self) {}
fn build(&self, cx: &BuildCx) -> El;
fn on_event(&mut self, _event: UiEvent) {}
fn selection(&self) -> crate::selection::Selection {
crate::selection::Selection::default()
}
fn hotkeys(&self) -> Vec<(KeyChord, String)> {
Vec::new()
}
fn drain_toasts(&mut self) -> Vec<crate::toast::ToastSpec> {
Vec::new()
}
fn shaders(&self) -> Vec<AppShader> {
Vec::new()
}
fn theme(&self) -> crate::Theme {
crate::Theme::default()
}
}
#[derive(Clone, Copy, Debug)]
pub struct AppShader {
pub name: &'static str,
pub wgsl: &'static str,
pub samples_backdrop: bool,
pub samples_time: bool,
}