redesign
This commit is contained in:
@@ -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<EventManager>) {
|
|
||||||
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<EventManager>, 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));
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
pub mod rect;
|
|
||||||
pub mod text;
|
|
||||||
pub mod input;
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,11 +5,11 @@ impl Element for String {
|
|||||||
scope.draw_text(self);
|
scope.draw_text(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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<HashMap<TypeId, Vec<Arc<Mutex<dyn FnMut(&Arc<EventManager>, &dyn Event)>>>>>,
|
|
||||||
states: Mutex<Option<Arc<StateManager>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventManager {
|
|
||||||
pub fn new() -> Arc<EventManager> {
|
|
||||||
Arc::new(EventManager {
|
|
||||||
handlers: Mutex::new(HashMap::new()),
|
|
||||||
states: Mutex::new(None),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on<E: Event + 'static, F: FnMut(&Arc<EventManager>, &E) + 'static>(
|
|
||||||
self: &Arc<Self>,
|
|
||||||
mut f: F,
|
|
||||||
) {
|
|
||||||
let states = self.states.lock().unwrap().clone();
|
|
||||||
self.handlers
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.entry(TypeId::of::<E>())
|
|
||||||
.or_insert_with(Vec::new)
|
|
||||||
.push(Arc::new(Mutex::new(
|
|
||||||
move |e: &Arc<EventManager>, evt: &dyn Event| {
|
|
||||||
if let Some(concrete) = evt.as_any().downcast_ref::<E>() {
|
|
||||||
f(&e, concrete);
|
|
||||||
if let Some(states) = &states {
|
|
||||||
states.clone().flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dispatch<E: Event + 'static + Clone>(self: &Arc<Self>, 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>) {
|
|
||||||
self.dispatch(Close);
|
|
||||||
std::process::exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_state_manager(self: &Arc<Self>, states: Arc<StateManager>) {
|
|
||||||
*self.states.lock().unwrap() = Some(states);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<EventManager>, event: &KeyEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct KeyPressExtension;
|
|
||||||
|
|
||||||
impl Extension for KeyPressExtension {
|
|
||||||
fn init(&mut self, _: &mut Screen, events: &Arc<EventManager>) {
|
|
||||||
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<EventManager>) {}
|
|
||||||
|
|
||||||
fn tick_end(&mut self, _: &mut Screen, events: &Arc<EventManager>) {
|
|
||||||
if crossterm::event::poll(std::time::Duration::from_millis(13)).unwrap() {
|
|
||||||
match crossterm::event::read().unwrap() {
|
|
||||||
crossterm::event::Event::Key(e) => {
|
|
||||||
events.dispatch(e);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<EventManager>);
|
|
||||||
fn tick_start(&mut self, screen: &mut Screen, events: &Arc<EventManager>);
|
|
||||||
fn tick_end(&mut self, screen: &mut Screen, events: &Arc<EventManager>);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ExtensionManager(Vec<Box<dyn Extension>>);
|
|
||||||
|
|
||||||
impl Extension for ExtensionManager {
|
|
||||||
fn init(&mut self, screen: &mut Screen, events: &Arc<EventManager>) {
|
|
||||||
for ext in &mut self.0 {
|
|
||||||
ext.init(screen, events);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tick_start(&mut self, screen: &mut Screen, events: &Arc<EventManager>) {
|
|
||||||
for ext in &mut self.0 {
|
|
||||||
ext.tick_start(screen, events);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tick_end(&mut self, screen: &mut Screen, events: &Arc<EventManager>) {
|
|
||||||
for ext in &mut self.0 {
|
|
||||||
ext.tick_end(screen, events);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExtensionManager {
|
|
||||||
pub fn new() -> ExtensionManager {
|
|
||||||
ExtensionManager(Vec::new())
|
|
||||||
}
|
|
||||||
pub fn add<E: Extension + 'static>(&mut self, extension: E) {
|
|
||||||
self.0.push(Box::new(extension));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<EventManager>) {}
|
|
||||||
|
|
||||||
fn tick_end(&mut self, _: &mut crate::Screen, _: &Arc<EventManager>) {
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(1000 / self.0 as u64));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tick_start(&mut self, _: &mut crate::Screen, _: &Arc<EventManager>) {}
|
|
||||||
}
|
|
||||||
+22
-72
@@ -1,113 +1,63 @@
|
|||||||
use std::{
|
use std::sync::Arc;
|
||||||
any::{Any, TypeId},
|
|
||||||
collections::HashMap,
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
events::EventManager,
|
|
||||||
extensions::{Extension, ExtensionManager},
|
|
||||||
render_scope::RenderScope,
|
render_scope::RenderScope,
|
||||||
style::Transform,
|
style::Transform,
|
||||||
|
widget::{Element, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod element;
|
pub mod elements;
|
||||||
pub mod events;
|
|
||||||
pub mod extensions;
|
|
||||||
pub mod macros;
|
pub mod macros;
|
||||||
pub mod render_scope;
|
pub mod render_scope;
|
||||||
pub mod state;
|
|
||||||
pub mod style;
|
pub mod style;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
pub mod widget;
|
||||||
pub trait Element {
|
|
||||||
#[allow(unused)]
|
|
||||||
fn render(&mut self, scope: &mut RenderScope) {}
|
|
||||||
#[allow(unused)]
|
|
||||||
fn init(&mut self, events: &Arc<EventManager>) {}
|
|
||||||
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<dyn Element>, HashMap<TypeId, Box<dyn Component>>);
|
|
||||||
|
|
||||||
impl Widget {
|
|
||||||
pub fn component<C: Component + 'static>(&mut self, c: C) -> &mut Self {
|
|
||||||
self.1.entry(c.type_id()).or_insert_with(|| Box::new(c));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_component<C: Component + 'static>(&mut self, c: C) -> &mut Self {
|
|
||||||
self.1.insert(c.type_id(), Box::new(c));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get<C: Component + 'static>(&self) -> Option<&C> {
|
|
||||||
self.1
|
|
||||||
.get(&TypeId::of::<C>())
|
|
||||||
.and_then(|c| c.as_any().downcast_ref::<C>())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut<C: Component + 'static>(&mut self) -> Option<&mut C> {
|
|
||||||
self.1
|
|
||||||
.get_mut(&TypeId::of::<C>())
|
|
||||||
.and_then(|c| c.as_any_mut().downcast_mut::<C>())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Screen {
|
pub struct Screen {
|
||||||
pub elements: Vec<Widget>,
|
pub elements: Vec<Arc<Widget>>,
|
||||||
pub events: Arc<EventManager>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Screen {
|
impl Screen {
|
||||||
pub fn new() -> Screen {
|
pub fn new() -> Screen {
|
||||||
Screen {
|
Screen {
|
||||||
elements: Vec::new(),
|
elements: Vec::new(),
|
||||||
events: EventManager::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw<E: Element + 'static>(&mut self, element: E) -> &mut Widget {
|
pub fn draw<E: Element + 'static>(&mut self, element: E) -> &Arc<Widget> {
|
||||||
self.elements
|
self.elements.push(Arc::new(Widget::new(Box::new(element))));
|
||||||
.push(Widget(Box::new(element), HashMap::new()));
|
self.elements.last().unwrap()
|
||||||
self.elements.last_mut().unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, extensions: &mut ExtensionManager) -> std::io::Result<()> {
|
pub fn run(&mut self) -> std::io::Result<()> {
|
||||||
let mut scope = RenderScope::new();
|
|
||||||
|
|
||||||
for elem in &mut self.elements {
|
for elem in &mut self.elements {
|
||||||
elem.component(Transform::new());
|
elem.component(Transform::new());
|
||||||
elem.0.init(&self.events);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extensions.init(self, &self.events.clone());
|
utils::hide_cursor()?;
|
||||||
|
|
||||||
loop {
|
self.render()?;
|
||||||
extensions.tick_start(self, &self.events.clone());
|
|
||||||
|
|
||||||
utils::clear().unwrap();
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&mut self) -> std::io::Result<()> {
|
||||||
|
let mut scope = RenderScope::new();
|
||||||
|
|
||||||
|
utils::clear()?;
|
||||||
for elem in &mut self.elements {
|
for elem in &mut self.elements {
|
||||||
scope.clear();
|
scope.clear();
|
||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
|
|
||||||
elem.0.render(&mut scope);
|
elem.0.lock().unwrap().render(&mut scope);
|
||||||
|
|
||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
scope.draw();
|
scope.draw();
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
extensions.tick_end(self, &self.events.clone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -42,7 +42,7 @@ macro_rules! component {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct $name;
|
pub struct $name;
|
||||||
|
|
||||||
impl $crate::Component for $name {
|
impl $crate::widget::Component for $name {
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ macro_rules! component {
|
|||||||
$($inner)*
|
$($inner)*
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $crate::Component for $name {
|
impl $crate::widget::Component for $name {
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ macro_rules! component {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct $name ($($inner)*);
|
pub struct $name ($($inner)*);
|
||||||
|
|
||||||
impl $crate::Component for $name {
|
impl $crate::widget::Component for $name {
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-44
@@ -1,52 +1,11 @@
|
|||||||
use std::sync::Arc;
|
use osui::{style::Transform, Screen};
|
||||||
|
|
||||||
use crossterm::event::KeyEvent;
|
|
||||||
use osui::{
|
|
||||||
element::rect::Rect,
|
|
||||||
events::EventManager,
|
|
||||||
extensions::{keypress::KeyPressExtension, ExtensionManager},
|
|
||||||
state::StateManager,
|
|
||||||
style::Transform,
|
|
||||||
Screen,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
init(&StateManager::new(init));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(states: &Arc<StateManager>) {
|
|
||||||
let mut screen = Screen::new();
|
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<StateManager>, screen: &mut Screen) {
|
|
||||||
screen.events.set_state_manager(states.clone());
|
|
||||||
|
|
||||||
let mut count = states.use_state(0);
|
|
||||||
|
|
||||||
screen
|
screen
|
||||||
.draw(Rect(0xffffff))
|
.draw(format!("Hello, World!"))
|
||||||
.component(Transform::center().dimensions(30, 3));
|
|
||||||
|
|
||||||
screen
|
|
||||||
.draw(format!("Count: {count}"))
|
|
||||||
.component(Transform::center());
|
.component(Transform::center());
|
||||||
|
|
||||||
screen.events.on(
|
screen.run().unwrap();
|
||||||
move |events: &Arc<EventManager>, event: &KeyEvent| match event.code {
|
|
||||||
crossterm::event::KeyCode::Enter => {
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
events.close();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
-182
@@ -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<dyn Fn(&Arc<Self>)>,
|
|
||||||
store: Mutex<HashMap<usize, Box<dyn Any + Send + Sync>>>,
|
|
||||||
cursor: Mutex<usize>,
|
|
||||||
dirty: Mutex<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct State<S> {
|
|
||||||
manager: Arc<StateManager>,
|
|
||||||
id: usize,
|
|
||||||
_pd: PhantomData<S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StateManager {
|
|
||||||
pub fn new<F: Fn(&Arc<Self>) + 'static>(recall: F) -> Arc<Self> {
|
|
||||||
Arc::new(Self {
|
|
||||||
recall: Box::new(recall),
|
|
||||||
store: Mutex::new(HashMap::new()),
|
|
||||||
cursor: Mutex::new(0),
|
|
||||||
dirty: Mutex::new(false),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn use_state<S: 'static + Send + Sync>(self: &Arc<Self>, initial: S) -> State<S> {
|
|
||||||
// `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<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> State<S> {
|
|
||||||
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::<Mutex<S>>()
|
|
||||||
.unwrap()
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: Display + 'static> Display for State<S> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.manager.store.lock().unwrap()[&self.id]
|
|
||||||
.downcast_ref::<Mutex<S>>()
|
|
||||||
.unwrap()
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: Debug + 'static> Debug for State<S> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.manager.store.lock().unwrap()[&self.id]
|
|
||||||
.downcast_ref::<Mutex<S>>()
|
|
||||||
.unwrap()
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Sv, S: Add<Sv> + Clone + 'static> Add<Sv> for State<S> {
|
|
||||||
type Output = <S as Add<Sv>>::Output;
|
|
||||||
|
|
||||||
fn add(self, rhs: Sv) -> Self::Output {
|
|
||||||
self.manager.store.lock().unwrap()[&self.id]
|
|
||||||
.downcast_ref::<Mutex<S>>()
|
|
||||||
.unwrap()
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
.add(rhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Sv, S: Sub<Sv> + Clone + 'static> Sub<Sv> for State<S> {
|
|
||||||
type Output = <S as Sub<Sv>>::Output;
|
|
||||||
|
|
||||||
fn sub(self, rhs: Sv) -> Self::Output {
|
|
||||||
self.manager.store.lock().unwrap()[&self.id]
|
|
||||||
.downcast_ref::<Mutex<S>>()
|
|
||||||
.unwrap()
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
.sub(rhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Sv, S: Div<Sv> + Clone + 'static> Div<Sv> for State<S> {
|
|
||||||
type Output = <S as Div<Sv>>::Output;
|
|
||||||
|
|
||||||
fn div(self, rhs: Sv) -> Self::Output {
|
|
||||||
self.manager.store.lock().unwrap()[&self.id]
|
|
||||||
.downcast_ref::<Mutex<S>>()
|
|
||||||
.unwrap()
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
.div(rhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Sv, S: AddAssign<Sv> + 'static> AddAssign<Sv> for State<S> {
|
|
||||||
fn add_assign(&mut self, rhs: Sv) {
|
|
||||||
self.manager.store.lock().unwrap()[&self.id]
|
|
||||||
.downcast_ref::<Mutex<S>>()
|
|
||||||
.unwrap()
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.add_assign(rhs);
|
|
||||||
self.dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Sv, S: SubAssign<Sv> + 'static> SubAssign<Sv> for State<S> {
|
|
||||||
fn sub_assign(&mut self, rhs: Sv) {
|
|
||||||
self.manager.store.lock().unwrap()[&self.id]
|
|
||||||
.downcast_ref::<Mutex<S>>()
|
|
||||||
.unwrap()
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.sub_assign(rhs);
|
|
||||||
self.dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: PartialEq<S> + 'static> PartialEq<S> for State<S> {
|
|
||||||
fn eq(&self, other: &S) -> bool {
|
|
||||||
self.manager.store.lock().unwrap()[&self.id]
|
|
||||||
.downcast_ref::<Mutex<S>>()
|
|
||||||
.unwrap()
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.eq(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<Box<dyn Element>>,
|
||||||
|
Mutex<HashMap<TypeId, Box<dyn Component>>>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Widget {
|
||||||
|
pub fn new(e: Box<dyn Element>) -> Self {
|
||||||
|
Self(Mutex::new(e), Mutex::new(HashMap::new()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn component<C: Component + 'static>(self: &Arc<Self>, c: C) -> &Arc<Self> {
|
||||||
|
self.1
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.entry(c.type_id())
|
||||||
|
.or_insert_with(|| Box::new(c));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_component<C: Component + 'static>(self: &Arc<Self>, c: C) -> &Arc<Self> {
|
||||||
|
self.1.lock().unwrap().insert(c.type_id(), Box::new(c));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get<C: Component + 'static + Clone>(&self) -> Option<C> {
|
||||||
|
self.1
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.get(&TypeId::of::<C>())
|
||||||
|
.and_then(|c| c.as_any().downcast_ref::<C>())
|
||||||
|
.map(|c| c.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user