diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 9bcc3d8..87849a2 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -1,34 +1,15 @@ -use osui::{state::use_state, Context, Event, Node}; +use std::sync::Arc; -pub struct ClickEvent; - -impl Event for ClickEvent { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -fn app(cx: &mut Context) -> Node { - // Hooks - let count = use_state(0); - cx.event({ - let count = count.clone(); - move |_: &ClickEvent| { - **count.get() += 1; - } - }); - - Box::new(move |renderer| { - renderer - .draw_text(&format!("Count: {count}"), 0, 0) - .unwrap(); - }) -} +use osui::prelude::*; fn main() { - let mut osui = osui::Osui::new(Box::new(app)); - loop { - osui.render().unwrap(); - std::thread::sleep(std::time::Duration::from_millis(10)); - } + let mut cx = Context::new(app); + cx.refresh(); + // println!("{:?}", cx.get_nodes()); +} + +fn app(cx: &mut Context) -> Vec { + let count = use_state(0); + + vec![Node::String(Arc::new(move || format!("{count}")))] } diff --git a/src/component.rs b/src/component.rs new file mode 100644 index 0000000..2a5d095 --- /dev/null +++ b/src/component.rs @@ -0,0 +1,27 @@ +use std::sync::Arc; + +use crate::Node; + +pub type Component = Arc Vec>; + +pub struct Context { + component: Component, + nodes: Vec, +} + +impl Context { + pub fn new Vec + 'static>(component: F) -> Self { + Self { + component: Arc::new(component), + nodes: Vec::new(), + } + } + + pub fn refresh(&mut self) { + self.nodes = (self.component.clone())(self); + } + + pub fn get_nodes(&self) -> Vec { + self.nodes.clone() + } +} diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 0efb8d8..0000000 --- a/src/error.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub type Result = std::result::Result; - -#[derive(Debug)] -pub enum OsuiError { - Io(std::io::Error), -} - -impl From for OsuiError { - fn from(e: std::io::Error) -> Self { - OsuiError::Io(e) - } -} diff --git a/src/lib.rs b/src/lib.rs index c1ecc8c..d283559 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,74 +1,16 @@ -use std::any::Any; +use std::sync::Arc; -use crate::renderer::{OsuiRenderer, Renderer}; - -pub mod error; -pub mod renderer; +pub mod component; pub mod state; -pub use error::Result; - -pub type Node = Box; -pub type Component = Box Node>; - -pub trait Event { - fn as_any(&self) -> &dyn Any; +pub mod prelude { + pub use crate::component::*; + pub use crate::state::*; + pub use crate::Node; } -pub struct Context { - event_handlers: Vec>, -} - -pub struct Osui { - node: Node, - renderer: Option>, -} - -impl Osui { - pub fn new(component: Component) -> Self { - let mut cx = Context { - event_handlers: Vec::new(), - }; - Osui { - node: component(&mut cx), - renderer: None, - } - } - pub fn with_renderer(component: Component, renderer: Box) -> Self { - let mut cx = Context { - event_handlers: Vec::new(), - }; - Osui { - node: component(&mut cx), - renderer: Some(renderer), - } - } - - pub fn render(&mut self) -> Result<()> { - renderer::utils::clear()?; - if let Some(renderer) = &mut self.renderer { - (self.node)(renderer.as_mut()); - } else { - (self.node)(&mut OsuiRenderer); - } - Ok(()) - } -} - -impl Context { - pub fn event(&mut self, handler: F) { - self.event_handlers.push(Box::new(move |e| { - if let Some(e) = e.as_any().downcast_ref() { - (handler)(e) - } - })); - } - - pub fn global_event(&mut self, handler: F) { - self.event_handlers.push(Box::new(move |e| { - if let Some(e) = e.as_any().downcast_ref() { - (handler)(e) - } - })); - } +#[derive(Clone)] +pub enum Node { + String(Arc String>), + Component(component::Component), } diff --git a/src/renderer.rs b/src/renderer.rs deleted file mode 100644 index 98cc09b..0000000 --- a/src/renderer.rs +++ /dev/null @@ -1,91 +0,0 @@ -pub(crate) mod utils { - //! The `utils` module provides utility functions for terminal manipulation - //! and string operations, designed to enhance terminal-based applications. - - use std::io::{self, Write}; - - /// Clears the terminal screen and moves the cursor to the top-left corner. - /// - /// # Returns - /// A `io::Result<()>` indicating whether the operation succeeded. - /// - /// # Example - /// ``` - /// utils::clear().unwrap(); - /// ``` - pub fn clear() -> io::Result<()> { - print!("\x1B[2J\x1B[H"); - std::io::stdout().flush() - } - - /// Hides the terminal cursor. - /// - /// # Returns - /// A `io::Result<()>` indicating whether the operation succeeded. - /// - /// # Example - /// ``` - /// utils::hide_cursor().unwrap(); - /// ``` - pub fn hide_cursor() -> io::Result<()> { - print!("\x1b[?25l"); - std::io::stdout().flush() - } - - /// Shows the terminal cursor. - /// - /// # Returns - /// A `io::Result<()>` indicating whether the operation succeeded. - /// - /// # Example - /// ``` - /// utils::show_cursor().unwrap(); - /// ``` - pub fn show_cursor() -> io::Result<()> { - print!("\x1B[?25h"); - std::io::stdout().flush() - } - - /// Flushes the terminal's stdout buffer. - /// - /// # Returns - /// A `io::Result<()>` indicating whether the operation succeeded. - /// - /// # Example - /// ``` - /// utils::flush().unwrap(); - /// ``` - pub fn flush() -> io::Result<()> { - std::io::stdout().flush() - } - - pub fn hex_ansi(hex: u32) -> String { - let r = ((hex >> 16) & 0xFF) as u8; - let g = ((hex >> 8) & 0xFF) as u8; - let b = (hex & 0xFF) as u8; - format!("\x1b[38;2;{r};{g};{b}m") - } - - pub fn hex_ansi_bg(hex: u32) -> String { - let r = ((hex >> 16) & 0xFF) as u8; - let g = ((hex >> 8) & 0xFF) as u8; - let b = (hex & 0xFF) as u8; - format!("\x1b[48;2;{r};{g};{b}m") - } -} - -pub trait Renderer { - fn draw_text(&mut self, text: &str, x: u32, y: u32) -> crate::Result<()>; -} - -pub struct OsuiRenderer; - -impl Renderer for OsuiRenderer { - fn draw_text(&mut self, text: &str, x: u32, y: u32) -> crate::Result<()> { - for line in text.lines() { - print!("\x1b[{};{}H{line}\x1b[0m", y + 1, x + 1); - } - utils::flush()?; - Ok(()) - } -} diff --git a/src/state.rs b/src/state.rs index aac8756..76621cf 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,100 +1,103 @@ use std::{ fmt::{Debug, Display, Formatter, Result as FmtResult}, - ops::{Deref, DerefMut}, + ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}, sync::{Arc, Mutex, MutexGuard}, }; -/// Trait for tracking dependencies and reactivity. -pub trait DependencyHandler: std::fmt::Debug + Send + Sync { - /// Called when a dependent is registered. - fn add(&self); - /// Returns `true` if the state has changed since the last check. - fn check(&self) -> bool; -} - #[derive(Debug, Clone)] pub struct State { - inner: Arc>>, -} - -#[derive(Debug)] -pub struct Inner { - value: T, - dependencies: usize, - changed: usize, + inner: Arc>, } pub fn use_state(v: T) -> State { State { - inner: Arc::new(Mutex::new(Inner { - value: v, - dependencies: 0, - changed: 0, - })), + inner: Arc::new(Mutex::new(v)), } } impl State { /// Gets the cloned value, recommended for preventing deadlocks pub fn get_dl(&self) -> T { - self.inner.lock().unwrap().value.clone() + self.inner.lock().unwrap().clone() } } impl State { /// Gets a lock on the state for read/write access. - pub fn get(&self) -> MutexGuard<'_, Inner> { + pub fn get(&self) -> MutexGuard<'_, T> { self.inner.lock().unwrap() } /// Sets the value and marks it as changed. pub fn set(&self, v: T) { - let mut inner = self.inner.lock().unwrap(); - inner.value = v; - inner.changed = inner.dependencies; - } - - /// Marks the state as updated. - pub fn update(&self) { - let mut inner = self.inner.lock().unwrap(); - inner.changed = inner.dependencies; - } -} - -impl DependencyHandler for State { - fn check(&self) -> bool { - let mut inner = self.inner.lock().unwrap(); - let i = inner.changed > 0; - if i { - inner.changed -= 1; - } - i - } - - fn add(&self) { - let mut inner = self.inner.lock().unwrap(); - inner.dependencies += 1; + *self.inner.lock().unwrap() = v; } } impl Display for State { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - let inner = self.inner.lock().unwrap(); - write!(f, "{}", inner.value) + write!(f, "{}", self.inner.lock().unwrap()) } } -impl Deref for Inner { - type Target = T; +impl + Clone> Add for State { + type Output = T; - fn deref(&self) -> &Self::Target { - &self.value + fn add(self, rhs: T) -> Self::Output { + let v = self.inner.lock().unwrap().clone(); + v.add(rhs) } } -impl DerefMut for Inner { - fn deref_mut(&mut self) -> &mut Self::Target { - self.changed = self.dependencies; - &mut self.value +impl + Clone> Sub for State { + type Output = T; + + fn sub(self, rhs: T) -> Self::Output { + let v = self.inner.lock().unwrap().clone(); + v.sub(rhs) } -} \ No newline at end of file +} + +impl + Clone> Mul for State { + type Output = T; + + fn mul(self, rhs: T) -> Self::Output { + let v = self.inner.lock().unwrap().clone(); + v.mul(rhs) + } +} + +impl + Clone> Div for State { + type Output = T; + + fn div(self, rhs: T) -> Self::Output { + let v = self.inner.lock().unwrap().clone(); + v.div(rhs) + } +} + +// Assign + +impl AddAssign for State { + fn add_assign(&mut self, rhs: T) { + self.get().add_assign(rhs); + } +} + +impl SubAssign for State { + fn sub_assign(&mut self, rhs: T) { + self.get().sub_assign(rhs); + } +} + +impl MulAssign for State { + fn mul_assign(&mut self, rhs: T) { + self.get().mul_assign(rhs); + } +} + +impl DivAssign for State { + fn div_assign(&mut self, rhs: T) { + self.get().div_assign(rhs); + } +}