From 65c8bb03d8b0eb44b256daca84774eca5157aad2 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Mon, 27 Jan 2025 00:31:13 +0100 Subject: [PATCH] state management (optional) --- Cargo.toml | 6 ++- src/elements.rs | 18 +++++++- src/lib.rs | 9 +++- src/main.rs | 9 ++-- src/state.rs | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 151 insertions(+), 9 deletions(-) create mode 100644 src/state.rs diff --git a/Cargo.toml b/Cargo.toml index a8685f3..2cf3389 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,11 @@ license = "Apache-2.0" description = "A TUI library for advanced uis" repository = "https://github.com/osui-rs/engine" homepage = "https://osui.netlify.app" -documentation = "https://osui.netlify.app/docs/" +documentation = "https://osui.netlify.app/docs/" + +[features] +default = ["state"] +state = [] [lib] name = "osmium_engine" diff --git a/src/elements.rs b/src/elements.rs index c31827c..fc3f3ba 100644 --- a/src/elements.rs +++ b/src/elements.rs @@ -1,3 +1,5 @@ +use crossterm::event::{Event, MouseButton, MouseEvent, MouseEventKind}; + use crate::Element; impl Element for &str { @@ -21,7 +23,19 @@ impl Element for Button { fn render(&self) -> String { self.text.to_owned() } - fn event(&self, _: crossterm::event::Event) { - (self.on_click.clone().lock().unwrap())() + fn event(&self, event: crossterm::event::Event) { + match event { + Event::Mouse(MouseEvent { + kind: MouseEventKind::Down(MouseButton::Left), + .. + }) => (self.on_click.clone().lock().unwrap())(), + _ => {} + } + } +} + +impl Element for crate::Ui { + fn render(&self) -> String { + String::new() } } diff --git a/src/lib.rs b/src/lib.rs index 6a476ee..50272ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ pub mod elements; +#[cfg(feature = "state")] +pub mod state; pub mod style; pub mod utils; @@ -13,11 +15,14 @@ pub mod prelude { utils::init, Props, Result, Ui, }; + + #[cfg(feature = "state")] + pub use crate::state::use_state; } // Traits pub trait Framing { - fn draw(&mut self, element: &dyn Element, props: &Props); + fn draw(&mut self, element: &mut dyn Element, props: &Props); } pub trait Element { @@ -142,7 +147,7 @@ impl Props { } impl Framing for Console<'_> { - fn draw(&mut self, element: &dyn Element, props: &Props) { + fn draw(&mut self, element: &mut dyn Element, props: &Props) { let mut written = element.render(); let (written_width, written_height) = utils::str_size(&written); diff --git a/src/main.rs b/src/main.rs index eb06f17..2530f25 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,11 +25,12 @@ fn main() -> Result<()> { } pub fn app() -> Ui { - Arc::new(|frame| { + let mut count = use_state(0); + Arc::new(move |frame| { frame.draw( - &Button { - on_click: func(|| {}), - text: String::from("Testing"), + &mut Button { + on_click: func(move || count += 1), + text: format!("Count: {count}"), }, &Props::center().state( "hover", diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..428446b --- /dev/null +++ b/src/state.rs @@ -0,0 +1,118 @@ +//! The `state` module provides the `State` type for managing mutable state across UI components +//! and operations. It wraps a raw pointer to enable shared mutability and integrates with common +//! traits for ease of use. + +/// A wrapper around a raw pointer to a mutable value, allowing shared state management. +/// +/// # Safety +/// This type uses unsafe code to dereference raw pointers. Proper care must be taken +/// to ensure the validity and lifetime of the referenced value. +/// +/// # Examples +/// ``` +/// let value = 42; +/// let state = State(Box::into_raw(Box::new(value))); +/// assert_eq!(*state, 42); +/// ``` +#[derive(Clone, Copy)] +pub struct State(pub(crate) *mut T); + +pub fn use_state(v: T) -> State { + State(Box::into_raw(Box::new(v))) +} + +impl std::fmt::Display for State { + /// Formats the value of the state for display. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", unsafe { &*self.0 }) + } +} + +impl std::fmt::Debug for State { + /// Formats the state for debugging purposes. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "State({:?})", unsafe { &*self.0 }) + } +} + +//// State Operations //// + +impl + Clone> std::ops::Add for State { + type Output = T; + + /// Adds a value to the state, returning the result. + fn add(self, rhs: T) -> Self::Output { + unsafe { + let lhs = &mut *self.0; + lhs.clone() + rhs + } + } +} + +impl + Clone> std::ops::Sub for State { + type Output = T; + + /// Subtracts a value from the state, returning the result. + fn sub(self, rhs: T) -> Self::Output { + unsafe { + let lhs = &mut *self.0; + lhs.clone() - rhs + } + } +} + +impl + Clone> std::ops::Div for State { + type Output = T; + + /// Divides the state by a value, returning the result. + fn div(self, rhs: T) -> Self::Output { + unsafe { + let lhs = &mut *self.0; + lhs.clone() / rhs + } + } +} + +impl std::ops::AddAssign for State { + /// Adds a value to the state in place. + fn add_assign(&mut self, rhs: T) { + unsafe { *self.0 += rhs } + } +} + +impl std::ops::SubAssign for State { + /// Subtracts a value from the state in place. + fn sub_assign(&mut self, rhs: T) { + unsafe { *self.0 -= rhs } + } +} + +impl std::ops::Deref for State { + type Target = T; + + /// Dereferences the state to access the underlying value. + fn deref(&self) -> &Self::Target { + unsafe { &*self.0 } + } +} + +impl std::ops::DerefMut for State { + /// Dereferences the state to access the underlying mutable value. + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.0 } + } +} + +impl State { + /// Returns a copy of the current state. + pub fn copy_state(&self) -> Self { + Self(self.0) + } +} + +impl> PartialEq for State { + /// Compares the state with another value for equality. + fn eq(&self, other: &T) -> bool { + unsafe { &*self.0 == other } + } +}