state management (optional)

This commit is contained in:
2025-01-27 00:31:13 +01:00
parent a168559b75
commit 65c8bb03d8
5 changed files with 151 additions and 9 deletions
+4
View File
@@ -8,6 +8,10 @@ repository = "https://github.com/osui-rs/engine"
homepage = "https://osui.netlify.app"
documentation = "https://osui.netlify.app/docs/"
[features]
default = ["state"]
state = []
[lib]
name = "osmium_engine"
path = "src/lib.rs"
+16 -2
View File
@@ -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()
}
}
+7 -2
View File
@@ -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);
+5 -4
View File
@@ -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",
+118
View File
@@ -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<T>(pub(crate) *mut T);
pub fn use_state<T>(v: T) -> State<T> {
State(Box::into_raw(Box::new(v)))
}
impl<T: std::fmt::Display> std::fmt::Display for State<T> {
/// 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<T: std::fmt::Debug> std::fmt::Debug for State<T> {
/// 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<T: std::ops::Add<Output = T> + Clone> std::ops::Add<T> for State<T> {
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<T: std::ops::Sub<Output = T> + Clone> std::ops::Sub<T> for State<T> {
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<T: std::ops::Div<Output = T> + Clone> std::ops::Div<T> for State<T> {
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<T: std::ops::AddAssign + Clone> std::ops::AddAssign<T> for State<T> {
/// Adds a value to the state in place.
fn add_assign(&mut self, rhs: T) {
unsafe { *self.0 += rhs }
}
}
impl<T: std::ops::SubAssign + Clone> std::ops::SubAssign<T> for State<T> {
/// Subtracts a value from the state in place.
fn sub_assign(&mut self, rhs: T) {
unsafe { *self.0 -= rhs }
}
}
impl<T> std::ops::Deref for State<T> {
type Target = T;
/// Dereferences the state to access the underlying value.
fn deref(&self) -> &Self::Target {
unsafe { &*self.0 }
}
}
impl<T> std::ops::DerefMut for State<T> {
/// Dereferences the state to access the underlying mutable value.
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.0 }
}
}
impl<T> State<T> {
/// Returns a copy of the current state.
pub fn copy_state(&self) -> Self {
Self(self.0)
}
}
impl<T: PartialEq<T>> PartialEq<T> for State<T> {
/// Compares the state with another value for equality.
fn eq(&self, other: &T) -> bool {
unsafe { &*self.0 == other }
}
}