diff --git a/src/element/input.rs b/src/element/input.rs deleted file mode 100644 index f6ccfa9..0000000 --- a/src/element/input.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::sync::Arc; - -use crossterm::event::KeyEvent; - -use crate::{ - event, event_handler, events::EventManager, extensions::keypress::KeyPressEventHandler, Element, -}; - -pub struct Input(String, usize); - -impl Input { - pub fn new() -> Input { - Input(String::new(), 0) - } -} - -impl Element for Input { - fn init(&mut self, events: &Arc) { - event_handler!(Self, self, events, on_keypress); - } - - fn render(&mut self, scope: &mut crate::render_scope::RenderScope) { - let mut s = self.0.clone(); - - if self.1 < self.0.len() { - s.insert_str(self.1 + 1, "\x1b[0m"); - s.insert_str(self.1, "\x1b[42m"); - } else { - s += "\x1b[42m \x1b[0m"; - } - - scope.draw_text(&s); - } - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl KeyPressEventHandler for Input { - fn on_keypress(&mut self, events: &Arc, event: &KeyEvent) { - match event.code { - crossterm::event::KeyCode::Char(c) => { - self.0.insert(self.1, c); - self.1 += 1; - events.dispatch(InputUpdateEvent(self.0.clone())); - } - crossterm::event::KeyCode::Backspace => { - if self.1 > 0 { - self.0.remove(self.1 - 1); - self.1 -= 1; - events.dispatch(InputUpdateEvent(self.0.clone())); - } - } - crossterm::event::KeyCode::Left => { - if self.1 > 0 { - self.1 -= 1; - events.dispatch(InputUpdateEvent(self.0.clone())); - } - } - crossterm::event::KeyCode::Right => { - if self.1 < self.0.len() { - self.1 += 1; - events.dispatch(InputUpdateEvent(self.0.clone())); - } - } - _ => { - events.dispatch(InputKeyPress(self.0.clone(), *event)); - } - } - } -} - -event!(InputUpdateEvent(pub String)); -event!(InputKeyPress(pub String, pub KeyEvent)); diff --git a/src/element/mod.rs b/src/element/mod.rs deleted file mode 100644 index 73ba4d7..0000000 --- a/src/element/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod rect; -pub mod text; -pub mod input; diff --git a/src/element/rect.rs b/src/element/rect.rs deleted file mode 100644 index 545c947..0000000 --- a/src/element/rect.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::Element; - -pub struct Rect(pub u32); -impl Element for Rect { - fn render(&mut self, scope: &mut crate::render_scope::RenderScope) { - let (w, h) = scope.get_size(); - - if w > 0 && h > 0 { - scope.draw_rect(w, h, self.0); - } - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} diff --git a/src/element/text.rs b/src/elements/mod.rs similarity index 100% rename from src/element/text.rs rename to src/elements/mod.rs index da27b3f..83f3c82 100644 --- a/src/element/text.rs +++ b/src/elements/mod.rs @@ -5,11 +5,11 @@ impl Element for String { scope.draw_text(self); } - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - fn as_any(&self) -> &dyn std::any::Any { self } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } } diff --git a/src/events.rs b/src/events.rs deleted file mode 100644 index 4eddc7f..0000000 --- a/src/events.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::{ - any::{Any, TypeId}, - collections::HashMap, - sync::{Arc, Mutex}, -}; - -use crate::{event, state::StateManager}; - -event!(Close); - -pub trait Event { - fn as_any(&self) -> &dyn Any; -} - -pub struct EventManager { - handlers: Mutex, &dyn Event)>>>>>, - states: Mutex>>, -} - -impl EventManager { - pub fn new() -> Arc { - Arc::new(EventManager { - handlers: Mutex::new(HashMap::new()), - states: Mutex::new(None), - }) - } - - pub fn on, &E) + 'static>( - self: &Arc, - mut f: F, - ) { - let states = self.states.lock().unwrap().clone(); - self.handlers - .lock() - .unwrap() - .entry(TypeId::of::()) - .or_insert_with(Vec::new) - .push(Arc::new(Mutex::new( - move |e: &Arc, evt: &dyn Event| { - if let Some(concrete) = evt.as_any().downcast_ref::() { - f(&e, concrete); - if let Some(states) = &states { - states.clone().flush(); - } - } - }, - ))); - } - - pub fn dispatch(self: &Arc, evt: E) { - let handlers = { self.handlers.lock().unwrap().get(&evt.type_id()).cloned() }; - - if let Some(handlers) = handlers { - for h in handlers { - let mut ha = h.lock().unwrap(); - (ha)(self, &evt); - } - } - } - - pub fn close(self: &Arc) { - self.dispatch(Close); - std::process::exit(0); - } - - pub fn set_state_manager(self: &Arc, states: Arc) { - *self.states.lock().unwrap() = Some(states); - } -} diff --git a/src/extensions/keypress.rs b/src/extensions/keypress.rs deleted file mode 100644 index 1613f54..0000000 --- a/src/extensions/keypress.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::sync::Arc; - -use crossterm::event::KeyEvent; - -use crate::{ - events::{Close, Event, EventManager}, - extensions::Extension, - Screen, -}; - -impl Event for KeyEvent { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -pub trait KeyPressEventHandler { - fn on_keypress(&mut self, events: &Arc, event: &KeyEvent); -} - -pub struct KeyPressExtension; - -impl Extension for KeyPressExtension { - fn init(&mut self, _: &mut Screen, events: &Arc) { - crossterm::terminal::enable_raw_mode().unwrap(); - crate::utils::clear().unwrap(); - crate::utils::hide_cursor().unwrap(); - - events.on(|_, _: &Close| { - crossterm::terminal::disable_raw_mode().unwrap(); - crate::utils::show_cursor().unwrap(); - crate::utils::clear().unwrap(); - }); - } - - fn tick_start(&mut self, _: &mut Screen, _: &Arc) {} - - fn tick_end(&mut self, _: &mut Screen, events: &Arc) { - if crossterm::event::poll(std::time::Duration::from_millis(13)).unwrap() { - match crossterm::event::read().unwrap() { - crossterm::event::Event::Key(e) => { - events.dispatch(e); - } - _ => {} - } - } - } -} diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs deleted file mode 100644 index 9e09980..0000000 --- a/src/extensions/mod.rs +++ /dev/null @@ -1,43 +0,0 @@ -pub mod keypress; -pub mod tick_rate; - -use std::sync::Arc; - -use crate::{events::EventManager, Screen}; - -pub trait Extension { - fn init(&mut self, screen: &mut Screen, events: &Arc); - fn tick_start(&mut self, screen: &mut Screen, events: &Arc); - fn tick_end(&mut self, screen: &mut Screen, events: &Arc); -} - -pub struct ExtensionManager(Vec>); - -impl Extension for ExtensionManager { - fn init(&mut self, screen: &mut Screen, events: &Arc) { - for ext in &mut self.0 { - ext.init(screen, events); - } - } - - fn tick_start(&mut self, screen: &mut Screen, events: &Arc) { - for ext in &mut self.0 { - ext.tick_start(screen, events); - } - } - - fn tick_end(&mut self, screen: &mut Screen, events: &Arc) { - for ext in &mut self.0 { - ext.tick_end(screen, events); - } - } -} - -impl ExtensionManager { - pub fn new() -> ExtensionManager { - ExtensionManager(Vec::new()) - } - pub fn add(&mut self, extension: E) { - self.0.push(Box::new(extension)); - } -} diff --git a/src/extensions/tick_rate.rs b/src/extensions/tick_rate.rs deleted file mode 100644 index 7f624bc..0000000 --- a/src/extensions/tick_rate.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::sync::Arc; - -use crate::{events::EventManager, extensions::Extension}; - -pub struct TickRate(pub u8); - -impl Extension for TickRate { - fn init(&mut self, _: &mut crate::Screen, _: &Arc) {} - - fn tick_end(&mut self, _: &mut crate::Screen, _: &Arc) { - std::thread::sleep(std::time::Duration::from_millis(1000 / self.0 as u64)); - } - - fn tick_start(&mut self, _: &mut crate::Screen, _: &Arc) {} -} diff --git a/src/lib.rs b/src/lib.rs index a67f12d..ef8459b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,113 +1,63 @@ -use std::{ - any::{Any, TypeId}, - collections::HashMap, - sync::Arc, -}; +use std::sync::Arc; use crate::{ - events::EventManager, - extensions::{Extension, ExtensionManager}, render_scope::RenderScope, style::Transform, + widget::{Element, Widget}, }; -pub mod element; -pub mod events; -pub mod extensions; +pub mod elements; pub mod macros; pub mod render_scope; -pub mod state; pub mod style; pub mod utils; - -pub trait Element { - #[allow(unused)] - fn render(&mut self, scope: &mut RenderScope) {} - #[allow(unused)] - fn init(&mut self, events: &Arc) {} - fn as_any(&self) -> &dyn Any; - fn as_any_mut(&mut self) -> &mut dyn Any; -} - -pub trait Component { - fn as_any(&self) -> &dyn Any; - fn as_any_mut(&mut self) -> &mut dyn Any; -} - -pub struct Widget(pub Box, HashMap>); - -impl Widget { - pub fn component(&mut self, c: C) -> &mut Self { - self.1.entry(c.type_id()).or_insert_with(|| Box::new(c)); - self - } - - pub fn set_component(&mut self, c: C) -> &mut Self { - self.1.insert(c.type_id(), Box::new(c)); - self - } - - pub fn get(&self) -> Option<&C> { - self.1 - .get(&TypeId::of::()) - .and_then(|c| c.as_any().downcast_ref::()) - } - - pub fn get_mut(&mut self) -> Option<&mut C> { - self.1 - .get_mut(&TypeId::of::()) - .and_then(|c| c.as_any_mut().downcast_mut::()) - } -} +pub mod widget; pub struct Screen { - pub elements: Vec, - pub events: Arc, + pub elements: Vec>, } impl Screen { pub fn new() -> Screen { Screen { elements: Vec::new(), - events: EventManager::new(), } } - pub fn draw(&mut self, element: E) -> &mut Widget { - self.elements - .push(Widget(Box::new(element), HashMap::new())); - self.elements.last_mut().unwrap() + pub fn draw(&mut self, element: E) -> &Arc { + self.elements.push(Arc::new(Widget::new(Box::new(element)))); + self.elements.last().unwrap() } - pub fn run(&mut self, extensions: &mut ExtensionManager) -> std::io::Result<()> { - let mut scope = RenderScope::new(); - + pub fn run(&mut self) -> std::io::Result<()> { for elem in &mut self.elements { elem.component(Transform::new()); - elem.0.init(&self.events); } - extensions.init(self, &self.events.clone()); + utils::hide_cursor()?; - loop { - extensions.tick_start(self, &self.events.clone()); + self.render()?; - utils::clear().unwrap(); - for elem in &mut self.elements { - scope.clear(); - if let Some(t) = elem.get() { - scope.set_transform(t); - } + loop {} + } - elem.0.render(&mut scope); + pub fn render(&mut self) -> std::io::Result<()> { + let mut scope = RenderScope::new(); - if let Some(t) = elem.get() { - scope.set_transform(t); - } - scope.draw(); + utils::clear()?; + for elem in &mut self.elements { + scope.clear(); + if let Some(t) = elem.get() { + scope.set_transform(&t); } - extensions.tick_end(self, &self.events.clone()); + elem.0.lock().unwrap().render(&mut scope); + + if let Some(t) = elem.get() { + scope.set_transform(&t); + } + scope.draw(); } + Ok(()) } } diff --git a/src/macros.rs b/src/macros.rs index a8d7d19..f415f18 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -42,7 +42,7 @@ macro_rules! component { #[derive(Debug, Clone)] pub struct $name; - impl $crate::Component for $name { + impl $crate::widget::Component for $name { fn as_any(&self) -> &dyn std::any::Any { self } @@ -59,7 +59,7 @@ macro_rules! component { $($inner)* } - impl $crate::Component for $name { + impl $crate::widget::Component for $name { fn as_any(&self) -> &dyn std::any::Any { self } @@ -74,7 +74,7 @@ macro_rules! component { #[derive(Debug, Clone)] pub struct $name ($($inner)*); - impl $crate::Component for $name { + impl $crate::widget::Component for $name { fn as_any(&self) -> &dyn std::any::Any { self } diff --git a/src/main.rs b/src/main.rs index 003086f..075cf8d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,52 +1,11 @@ -use std::sync::Arc; - -use crossterm::event::KeyEvent; -use osui::{ - element::rect::Rect, - events::EventManager, - extensions::{keypress::KeyPressExtension, ExtensionManager}, - state::StateManager, - style::Transform, - Screen, -}; +use osui::{style::Transform, Screen}; fn main() { - init(&StateManager::new(init)); -} - -fn init(states: &Arc) { let mut screen = Screen::new(); - screen.events.set_state_manager(states.clone()); - - let mut extensions = ExtensionManager::new(); - extensions.add(KeyPressExtension); - - app(states, &mut screen); - - screen.run(&mut extensions).unwrap(); -} - -fn app(states: &Arc, screen: &mut Screen) { - screen.events.set_state_manager(states.clone()); - - let mut count = states.use_state(0); screen - .draw(Rect(0xffffff)) - .component(Transform::center().dimensions(30, 3)); - - screen - .draw(format!("Count: {count}")) + .draw(format!("Hello, World!")) .component(Transform::center()); - screen.events.on( - move |events: &Arc, event: &KeyEvent| match event.code { - crossterm::event::KeyCode::Enter => { - count += 1; - } - _ => { - events.close(); - } - }, - ); + screen.run().unwrap(); } diff --git a/src/state.rs b/src/state.rs deleted file mode 100644 index d3807fa..0000000 --- a/src/state.rs +++ /dev/null @@ -1,182 +0,0 @@ -use std::{ - any::Any, - collections::HashMap, - fmt::{Debug, Display}, - marker::PhantomData, - ops::{Add, AddAssign, Div, Sub, SubAssign}, - sync::{Arc, Mutex}, -}; - -/// ---------- public API ---------- - -pub struct StateManager { - recall: Box)>, - store: Mutex>>, - cursor: Mutex, - dirty: Mutex, -} - -#[derive(Clone)] -pub struct State { - manager: Arc, - id: usize, - _pd: PhantomData, -} - -impl StateManager { - pub fn new) + 'static>(recall: F) -> Arc { - Arc::new(Self { - recall: Box::new(recall), - store: Mutex::new(HashMap::new()), - cursor: Mutex::new(0), - dirty: Mutex::new(false), - }) - } - - 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, - } - } - - pub fn flush(self: &Arc) { - let mut d = self.dirty.lock().unwrap(); - if *d { - *d = false; - drop(d); - - *self.cursor.lock().unwrap() = 0; - (self.recall)(self) - } - } -} - -impl State { - pub fn dirty(&self) { - *self.manager.dirty.lock().unwrap() = true; - } - - pub fn get(&self) -> S - where - S: Clone, - { - self.manager.store.lock().unwrap()[&self.id] - .downcast_ref::>() - .unwrap() - .lock() - .unwrap() - .clone() - } -} - -impl Display for State { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.manager.store.lock().unwrap()[&self.id] - .downcast_ref::>() - .unwrap() - .lock() - .unwrap() - .fmt(f) - } -} - -impl Debug for State { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.manager.store.lock().unwrap()[&self.id] - .downcast_ref::>() - .unwrap() - .lock() - .unwrap() - .fmt(f) - } -} - -impl + Clone + 'static> Add for State { - type Output = >::Output; - - fn add(self, rhs: Sv) -> Self::Output { - self.manager.store.lock().unwrap()[&self.id] - .downcast_ref::>() - .unwrap() - .lock() - .unwrap() - .clone() - .add(rhs) - } -} - -impl + Clone + 'static> Sub for State { - type Output = >::Output; - - fn sub(self, rhs: Sv) -> Self::Output { - self.manager.store.lock().unwrap()[&self.id] - .downcast_ref::>() - .unwrap() - .lock() - .unwrap() - .clone() - .sub(rhs) - } -} - -impl + Clone + 'static> Div for State { - type Output = >::Output; - - fn div(self, rhs: Sv) -> Self::Output { - self.manager.store.lock().unwrap()[&self.id] - .downcast_ref::>() - .unwrap() - .lock() - .unwrap() - .clone() - .div(rhs) - } -} - -impl + 'static> AddAssign for State { - fn add_assign(&mut self, rhs: Sv) { - self.manager.store.lock().unwrap()[&self.id] - .downcast_ref::>() - .unwrap() - .lock() - .unwrap() - .add_assign(rhs); - self.dirty(); - } -} - -impl + 'static> SubAssign for State { - fn sub_assign(&mut self, rhs: Sv) { - self.manager.store.lock().unwrap()[&self.id] - .downcast_ref::>() - .unwrap() - .lock() - .unwrap() - .sub_assign(rhs); - self.dirty(); - } -} - -impl + 'static> PartialEq for State { - fn eq(&self, other: &S) -> bool { - self.manager.store.lock().unwrap()[&self.id] - .downcast_ref::>() - .unwrap() - .lock() - .unwrap() - .eq(other) - } -} diff --git a/src/widget.rs b/src/widget.rs new file mode 100644 index 0000000..bbc09fc --- /dev/null +++ b/src/widget.rs @@ -0,0 +1,53 @@ +use std::{ + any::{Any, TypeId}, + collections::HashMap, + sync::{Arc, Mutex}, +}; + +use crate::render_scope::RenderScope; + +pub trait Element { + #[allow(unused)] + fn render(&mut self, scope: &mut RenderScope) {} + fn as_any(&self) -> &dyn Any; + fn as_any_mut(&mut self) -> &mut dyn Any; +} + +pub trait Component { + fn as_any(&self) -> &dyn Any; + fn as_any_mut(&mut self) -> &mut dyn Any; +} + +pub struct Widget( + pub Mutex>, + Mutex>>, +); + +impl Widget { + pub fn new(e: Box) -> Self { + Self(Mutex::new(e), Mutex::new(HashMap::new())) + } + + pub fn component(self: &Arc, c: C) -> &Arc { + self.1 + .lock() + .unwrap() + .entry(c.type_id()) + .or_insert_with(|| Box::new(c)); + self + } + + pub fn set_component(self: &Arc, c: C) -> &Arc { + self.1.lock().unwrap().insert(c.type_id(), Box::new(c)); + self + } + + pub fn get(&self) -> Option { + self.1 + .lock() + .unwrap() + .get(&TypeId::of::()) + .and_then(|c| c.as_any().downcast_ref::()) + .map(|c| c.clone()) + } +}