state management (optional)
This commit is contained in:
@@ -8,6 +8,10 @@ repository = "https://github.com/osui-rs/engine"
|
|||||||
homepage = "https://osui.netlify.app"
|
homepage = "https://osui.netlify.app"
|
||||||
documentation = "https://osui.netlify.app/docs/"
|
documentation = "https://osui.netlify.app/docs/"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["state"]
|
||||||
|
state = []
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "osmium_engine"
|
name = "osmium_engine"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|||||||
+16
-2
@@ -1,3 +1,5 @@
|
|||||||
|
use crossterm::event::{Event, MouseButton, MouseEvent, MouseEventKind};
|
||||||
|
|
||||||
use crate::Element;
|
use crate::Element;
|
||||||
|
|
||||||
impl Element for &str {
|
impl Element for &str {
|
||||||
@@ -21,7 +23,19 @@ impl Element for Button {
|
|||||||
fn render(&self) -> String {
|
fn render(&self) -> String {
|
||||||
self.text.to_owned()
|
self.text.to_owned()
|
||||||
}
|
}
|
||||||
fn event(&self, _: crossterm::event::Event) {
|
fn event(&self, event: crossterm::event::Event) {
|
||||||
(self.on_click.clone().lock().unwrap())()
|
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;
|
pub mod elements;
|
||||||
|
#[cfg(feature = "state")]
|
||||||
|
pub mod state;
|
||||||
pub mod style;
|
pub mod style;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
@@ -13,11 +15,14 @@ pub mod prelude {
|
|||||||
utils::init,
|
utils::init,
|
||||||
Props, Result, Ui,
|
Props, Result, Ui,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "state")]
|
||||||
|
pub use crate::state::use_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Traits
|
// Traits
|
||||||
pub trait Framing {
|
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 {
|
pub trait Element {
|
||||||
@@ -142,7 +147,7 @@ impl Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Framing for Console<'_> {
|
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 mut written = element.render();
|
||||||
|
|
||||||
let (written_width, written_height) = utils::str_size(&written);
|
let (written_width, written_height) = utils::str_size(&written);
|
||||||
|
|||||||
+5
-4
@@ -25,11 +25,12 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn app() -> Ui {
|
pub fn app() -> Ui {
|
||||||
Arc::new(|frame| {
|
let mut count = use_state(0);
|
||||||
|
Arc::new(move |frame| {
|
||||||
frame.draw(
|
frame.draw(
|
||||||
&Button {
|
&mut Button {
|
||||||
on_click: func(|| {}),
|
on_click: func(move || count += 1),
|
||||||
text: String::from("Testing"),
|
text: format!("Count: {count}"),
|
||||||
},
|
},
|
||||||
&Props::center().state(
|
&Props::center().state(
|
||||||
"hover",
|
"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