From 627ab8033898ca42c249f10c4de3f3ff258fe20b Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 27 Jun 2025 15:51:22 -0500 Subject: [PATCH] state management --- src/main.rs | 17 +++++++---- src/state.rs | 84 ++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 80 insertions(+), 21 deletions(-) diff --git a/src/main.rs b/src/main.rs index 2e5f703..21af7e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,20 +18,24 @@ fn main() { } fn app(states: Arc) { + states.begin(); let mut screen = Screen::new(); let mut extensions = ExtensionManager::new(); let mut events = EventManager::new(); extensions.add(KeyPressExtension); extensions.add(TickRate(255)); - let my_state = states.use_state(20); + let my_state = states.use_state(String::new()); screen .draw(Rect(0xffffff)) .component(Transform::center().dimensions(30, 3)); screen - .draw(format!("Type quit to quit {}", my_state.get())) + .draw(format!( + "Type exit to exit\nMy-State: {}", + my_state.get().trim() + )) .component(Transform::new().bottom().margin(0, -1)); screen @@ -41,10 +45,11 @@ fn app(states: Arc) { events.on( move |events, event: Box| match event.1.code { crossterm::event::KeyCode::Enter => { - my_state.set(my_state.get() + 10); - // if event.0 == "quit" { - // events.dispatch(Close); - // } + my_state.set(my_state.get() + &format!("{}\n", event.0)); + if event.0 == "exit" { + events.dispatch(Close); + } + my_state.update(); } _ => {} }, diff --git a/src/state.rs b/src/state.rs index 497647b..21ad6b0 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,28 +1,82 @@ -use std::sync::{Arc, Mutex}; +use std::{ + any::Any, + collections::HashMap, + marker::PhantomData, + sync::{Arc, Mutex}, +}; -pub struct StateManager(fn(Arc)); +/// ---------- public API ---------- -pub struct State(Arc, Arc>); +pub struct StateManager { + recall: fn(Arc), + store: Mutex>>, + cursor: Mutex, +} + +pub struct State { + manager: Arc, + id: usize, + _pd: PhantomData, +} impl StateManager { - pub fn new(recall: fn(Arc)) -> Arc { - Arc::new(StateManager(recall)) + pub fn new(recall: fn(Arc)) -> Arc { + Arc::new(Self { + recall, + store: Mutex::new(HashMap::new()), + cursor: Mutex::new(0), + }) } - pub fn use_state(self: Arc, state: S) -> State { - State(self.clone(), Arc::new(Mutex::new(state))) + /// Call this at the very top of every “render” before doing any `use_state` calls + pub fn begin(&self) { + *self.cursor.lock().unwrap() = 0; + } + + pub fn use_state(self: &Arc, initial: S) -> State { + // `slot` == position of this call in the render + let mut cur = self.cursor.lock().unwrap(); + let slot = *cur; + *cur += 1; + + // Insert the initial value only the first time we see this slot + let mut store = self.store.lock().unwrap(); + store + .entry(slot) + .or_insert_with(|| Box::new(Mutex::new(initial))); + + State { + manager: Arc::clone(self), + id: slot, + _pd: PhantomData, + } } } -impl State { - pub fn set(&self, to: S) { - *self.1.lock().unwrap() = to; - (self.0 .0)(self.0.clone()) +impl State { + pub fn get(&self) -> S { + let store = self.manager.store.lock().unwrap(); + let x = store[&self.id] + .downcast_ref::>() + .unwrap() + .lock() + .unwrap() + .clone(); + x } -} -impl State { - pub fn get(&self) -> i32 { - *self.1.lock().unwrap() + pub fn set(&self, val: S) { + { + let store = self.manager.store.lock().unwrap(); + *store[&self.id] + .downcast_ref::>() + .unwrap() + .lock() + .unwrap() = val; + } + } + + pub fn update(&self) { + (self.manager.recall)(Arc::clone(&self.manager)); } }