better state management

This commit is contained in:
2025-06-28 01:57:36 +02:00
parent 627ab80338
commit 551a062021
3 changed files with 47 additions and 40 deletions
+11 -2
View File
@@ -4,7 +4,7 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use crate::event; use crate::{event, state::StateManager};
event!(Close); event!(Close);
@@ -21,15 +21,16 @@ impl std::fmt::Debug for EventHandlerFn {
} }
} }
#[derive(Debug)]
pub struct EventManager { pub struct EventManager {
handlers: HashMap<TypeId, Vec<EventHandlerFn>>, handlers: HashMap<TypeId, Vec<EventHandlerFn>>,
states: Option<Arc<StateManager>>,
} }
impl EventManager { impl EventManager {
pub fn new() -> EventManager { pub fn new() -> EventManager {
EventManager { EventManager {
handlers: HashMap::new(), handlers: HashMap::new(),
states: None,
} }
} }
@@ -37,10 +38,14 @@ impl EventManager {
&mut self, &mut self,
mut f: F, mut f: F,
) { ) {
let states = self.states.clone();
let wrapper = Arc::new(Mutex::new( let wrapper = Arc::new(Mutex::new(
move |e: &mut EventManager, evt: Box<dyn Event>| { move |e: &mut EventManager, evt: Box<dyn Event>| {
if let Ok(concrete) = evt.as_any().downcast::<E>() { if let Ok(concrete) = evt.as_any().downcast::<E>() {
f(e, concrete); f(e, concrete);
if let Some(states) = &states {
states.clone().flush();
}
} }
}, },
)) as Arc<Mutex<dyn FnMut(&mut EventManager, Box<dyn Event>)>>; )) as Arc<Mutex<dyn FnMut(&mut EventManager, Box<dyn Event>)>>;
@@ -68,4 +73,8 @@ impl EventManager {
std::process::exit(0); std::process::exit(0);
} }
} }
pub fn set_state_manager(&mut self, states: Arc<StateManager>) {
self.states = Some(states);
}
} }
+18 -29
View File
@@ -1,10 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use crossterm::event::KeyEvent;
use osui::{ use osui::{
element::{ element::rect::Rect,
input::{Input, InputKeyPress},
rect::Rect,
},
events::{Close, EventManager}, events::{Close, EventManager},
extensions::{keypress::KeyPressExtension, tick_rate::TickRate, ExtensionManager}, extensions::{keypress::KeyPressExtension, tick_rate::TickRate, ExtensionManager},
state::StateManager, state::StateManager,
@@ -18,42 +16,33 @@ fn main() {
} }
fn app(states: Arc<StateManager>) { fn app(states: Arc<StateManager>) {
states.begin();
let mut screen = Screen::new(); let mut screen = Screen::new();
let mut extensions = ExtensionManager::new(); let mut extensions = ExtensionManager::new();
let mut events = EventManager::new(); let mut events = EventManager::new();
extensions.add(KeyPressExtension);
extensions.add(TickRate(255));
let my_state = states.use_state(String::new()); extensions.add(TickRate(50));
extensions.add(KeyPressExtension);
events.set_state_manager(states.clone());
let count = states.use_state(0);
screen screen
.draw(Rect(0xffffff)) .draw(Rect(0xffffff))
.component(Transform::center().dimensions(30, 3)); .component(Transform::center().dimensions(30, 3));
screen screen
.draw(format!( .draw(format!("Count: {}", count.get()))
"Type exit to exit\nMy-State: {}", .component(Transform::center());
my_state.get().trim()
))
.component(Transform::new().bottom().margin(0, -1));
screen events.on(move |events, event: Box<KeyEvent>| match event.code {
.draw(Input::new()) crossterm::event::KeyCode::Enter => {
.component(Transform::new().bottom()); count.set(count.get() + 1);
}
events.on( _ => {
move |events, event: Box<InputKeyPress>| match event.1.code { events.dispatch(Close);
crossterm::event::KeyCode::Enter => { }
my_state.set(my_state.get() + &format!("{}\n", event.0)); });
if event.0 == "exit" {
events.dispatch(Close);
}
my_state.update();
}
_ => {}
},
);
screen.run(&mut events, &mut extensions).unwrap(); screen.run(&mut events, &mut extensions).unwrap();
} }
+18 -9
View File
@@ -11,6 +11,7 @@ pub struct StateManager {
recall: fn(Arc<StateManager>), recall: fn(Arc<StateManager>),
store: Mutex<HashMap<usize, Box<dyn Any + Send + Sync>>>, store: Mutex<HashMap<usize, Box<dyn Any + Send + Sync>>>,
cursor: Mutex<usize>, cursor: Mutex<usize>,
dirty: Mutex<bool>,
} }
pub struct State<S> { pub struct State<S> {
@@ -25,14 +26,10 @@ impl StateManager {
recall, recall,
store: Mutex::new(HashMap::new()), store: Mutex::new(HashMap::new()),
cursor: Mutex::new(0), cursor: Mutex::new(0),
dirty: Mutex::new(false),
}) })
} }
/// 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<S: 'static + Send + Sync>(self: &Arc<Self>, initial: S) -> State<S> { pub fn use_state<S: 'static + Send + Sync>(self: &Arc<Self>, initial: S) -> State<S> {
// `slot` == position of this call in the render // `slot` == position of this call in the render
let mut cur = self.cursor.lock().unwrap(); let mut cur = self.cursor.lock().unwrap();
@@ -51,9 +48,24 @@ impl StateManager {
_pd: PhantomData, _pd: PhantomData,
} }
} }
pub fn flush(self: Arc<Self>) {
let mut d = self.dirty.lock().unwrap();
if *d {
*d = false;
drop(d);
*self.cursor.lock().unwrap() = 0;
(self.recall)(self)
}
}
} }
impl<S: 'static + Clone + Send + Sync> State<S> { impl<S: 'static + Clone + Send + Sync> State<S> {
pub fn dirty(&self) {
*self.manager.dirty.lock().unwrap() = true;
}
pub fn get(&self) -> S { pub fn get(&self) -> S {
let store = self.manager.store.lock().unwrap(); let store = self.manager.store.lock().unwrap();
let x = store[&self.id] let x = store[&self.id]
@@ -74,9 +86,6 @@ impl<S: 'static + Clone + Send + Sync> State<S> {
.lock() .lock()
.unwrap() = val; .unwrap() = val;
} }
} self.dirty();
pub fn update(&self) {
(self.manager.recall)(Arc::clone(&self.manager));
} }
} }