state management (optional)
This commit is contained in:
+16
-2
@@ -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
@@ -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
@@ -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
@@ -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 }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user