added rebuild and Arc insted of Box

This commit is contained in:
2024-11-12 15:59:27 +01:00
parent 994b7d2580
commit e1da609eb1
8 changed files with 154 additions and 78 deletions
+15 -3
View File
@@ -29,17 +29,21 @@ macro_rules! render {
}; };
} }
pub fn run(elem: Box<dyn Element>) { pub fn run(element: Arc<dyn Element>) -> bool {
hide_cursor(); hide_cursor();
crossterm::terminal::enable_raw_mode().unwrap(); crossterm::terminal::enable_raw_mode().unwrap();
clear(); clear();
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
let element = Arc::new(elem);
let (exit_tx, exit_) = mpsc::channel::<()>();
thread::spawn({ thread::spawn({
let element_thread = Arc::clone(&element); let element_thread = Arc::clone(&element);
move || loop { move || loop {
if exit_.try_recv().is_ok() {
break;
}
let event = crossterm::event::read().unwrap(); let event = crossterm::event::read().unwrap();
let e = Arc::clone(&element_thread); let e = Arc::clone(&element_thread);
let command_handler = crate::CommandHandler(tx.clone()); let command_handler = crate::CommandHandler(tx.clone());
@@ -57,11 +61,19 @@ pub fn run(elem: Box<dyn Element>) {
crate::Command::SetState(state) => { crate::Command::SetState(state) => {
current_state = state; current_state = state;
} }
crate::Command::Exit => break, crate::Command::Exit => {
exit_tx.send(()).unwrap();
break;
}
crate::Command::Rebuild => {
exit_tx.send(()).unwrap();
return true;
}
} }
} else { } else {
render!(&element, current_state.clone()); render!(&element, current_state.clone());
} }
thread::sleep(std::time::Duration::from_millis(20)); thread::sleep(std::time::Duration::from_millis(20));
} }
false
} }
+16 -8
View File
@@ -22,7 +22,7 @@
pub mod macros; pub mod macros;
pub mod ui; pub mod ui;
pub mod utils; pub mod utils;
use std::sync::mpsc; use std::sync::{mpsc, Arc, Mutex};
pub use utils::*; pub use utils::*;
pub mod app; pub mod app;
@@ -81,9 +81,9 @@ impl<T: Copy + PartialEq> Value<T> {
pub trait Element: Send + Sync { pub trait Element: Send + Sync {
fn get_data(&self) -> (Value<usize>, usize, String); fn get_data(&self) -> (Value<usize>, usize, String);
fn update_data(&mut self, width: usize, height: usize); fn update_data(&self, width: usize, height: usize);
fn get_element_by_id(&mut self, id: &str) -> Option<&mut Box<dyn Element>>; fn get_element_by_id(&self, id: &str) -> Option<std::sync::Arc<dyn Element>>;
fn render(&self, state: State) -> String { fn render(&self, state: State) -> String {
_ = state; _ = state;
@@ -95,16 +95,14 @@ pub trait Element: Send + Sync {
} }
} }
pub struct Handler<T>( pub struct Handler<T>(pub Box<dyn FnMut(&T, &CommandHandler, crossterm::event::Event) + Send>);
pub std::sync::Mutex<Box<dyn FnMut(&T, &CommandHandler, crossterm::event::Event) + Send>>,
);
impl<T> Handler<T> { impl<T> Handler<T> {
pub fn new<F>(handler_fn: F) -> Handler<T> pub fn new<F>(handler_fn: F) -> Handler<T>
where where
F: FnMut(&T, &CommandHandler, crossterm::event::Event) + 'static + Send, F: FnMut(&T, &CommandHandler, crossterm::event::Event) + 'static + Send,
{ {
Handler(std::sync::Mutex::new(Box::new(handler_fn))) Handler(Box::new(handler_fn))
} }
} }
@@ -118,12 +116,21 @@ impl CommandHandler {
pub fn exit(&self) { pub fn exit(&self) {
self.0.send(Command::Exit).unwrap(); self.0.send(Command::Exit).unwrap();
} }
pub fn get_element_by_id(&self) {
self.0.send(Command::Exit).unwrap();
}
pub fn rebuild(&self) -> bool {
self.0.send(Command::Rebuild).is_ok()
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Command { pub enum Command {
SetState(State), SetState(State),
Exit, Exit,
Rebuild,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -135,5 +142,6 @@ pub enum State {
} }
pub fn run_test() { pub fn run_test() {
app::run(test::app()); let i = Arc::new(Mutex::new(0));
while app::run(test::app(Arc::clone(&i))) {}
} }
+66 -53
View File
@@ -85,34 +85,35 @@ macro_rules! element {
) => { ) => {
$(#[$meta])* $(#[$meta])*
pub struct $name<'a> { pub struct $name<'a> {
pub x: $crate::Value<usize>, pub x: std::sync::Mutex<$crate::Value<usize>>,
pub y: usize, pub y: std::sync::Mutex<usize>,
pub width: $crate::Value<usize>, pub width: std::sync::Mutex<$crate::Value<usize>>,
pub height: $crate::Value<usize>, pub height: std::sync::Mutex<$crate::Value<usize>>,
pub children: Vec<Box<dyn $crate::Element>>, pub children: std::sync::Mutex<Vec<std::sync::Arc<dyn $crate::Element>>>,
pub child: usize, pub child: std::sync::Mutex<usize>,
pub text: String, pub text: std::sync::Mutex<String>,
pub id: &'a str, pub id: std::sync::Mutex<&'a str>,
$( $inner )* $( $inner )*
} }
impl $crate::Element for $name<'_> { impl $crate::Element for $name<'_> {
fn get_data(&self) -> ($crate::Value<usize>, usize, String) { fn get_data(&self) -> ($crate::Value<usize>, usize, String) {
(self.x.clone(), self.y.clone(), self.id.to_string()) (self.x.lock().unwrap().clone(), self.y.lock().unwrap().clone(), self.id.lock().unwrap().to_string())
} }
fn update_data(&mut self, width: usize, height: usize) { fn update_data(&self, width: usize, height: usize) {
self.width.try_set_value(width); self.width.lock().unwrap().try_set_value(width);
self.height.try_set_value(height); self.height.lock().unwrap().try_set_value(height);
for child in &mut self.children { let children = self.children.lock().unwrap();
for child in children.iter() {
child.update_data(width, height); child.update_data(width, height);
} }
} }
fn get_element_by_id(&mut self, id: &str) -> Option<&mut Box<dyn $crate::Element>> { fn get_element_by_id(&self, id: &str) -> Option<std::sync::Arc<(dyn $crate::Element + 'static)>> {
for elem in &mut self.children { for elem in &mut self.children.lock().unwrap().iter() {
if elem.get_data().2 == id { if elem.get_data().2 == id {
return Some(elem); return Some(std::sync::Arc::clone(elem));
} }
} }
None None
@@ -124,7 +125,7 @@ macro_rules! element {
impl<'a> $name<'_> { impl<'a> $name<'_> {
/// Creates a new instance of the element with default values. /// Creates a new instance of the element with default values.
pub fn new() -> $name<'a> { pub fn new() -> $name<'a> {
$name { $crate::def_!($name {
children: Vec::new(), children: Vec::new(),
x: $crate::Value::Default(0), x: $crate::Value::Default(0),
y: 0, y: 0,
@@ -134,20 +135,32 @@ macro_rules! element {
text: String::new(), text: String::new(),
id: "", id: "",
$( $defaults )* $( $defaults )*
} })
} }
/// Retrieves a mutable reference to the current child element, if any. pub fn get_child(&self) -> Option<std::sync::Arc<dyn $crate::Element>> {
/// if let Ok(elements) = self.children.lock() {
/// # Returns if let Some(e) = elements.get(*self.child.lock().unwrap()) {
/// An `Option` containing a mutable reference to the child `Element` or `None`. return Some(std::sync::Arc::clone(e));
pub fn get_child(&mut self) -> Option<&mut Box<dyn $crate::Element>> { }
self.children.get_mut(self.child) }
None
} }
} }
}; };
} }
#[macro_export]
macro_rules! def_ {
($name:ident { $($k:ident : $v:expr),* $(,)? }) => {
$name {
$(
$k : std::sync::Mutex::new($v)
),*
}
};
}
/// Macro for creating a command response from a list of commands. /// Macro for creating a command response from a list of commands.
/// ///
/// # Example /// # Example
@@ -175,63 +188,63 @@ macro_rules! command {
#[macro_export] #[macro_export]
macro_rules! parse_rsx_param { macro_rules! parse_rsx_param {
// Properties // Properties
($elem:expr, $($k:ident).+: $v:expr) => { ($elem:expr, $a:ident $($k:ident).+: $v:expr) => {
$elem.$($k).+ = $v; $elem.$a.lock().unwrap().$($k).+ = $v;
}; };
($elem:expr, $($k:ident).+: $v:expr, $($rest:tt)*) => { ($elem:expr, $a:ident $(.$($k:ident).+)?: $v:expr, $($rest:tt)*) => {
$elem.$($k).+ = $v; *$elem.$a.lock().unwrap()$($($k).+)? = $v;
$crate::parse_rsx_param!($elem, $($rest)*); $crate::parse_rsx_param!($elem, $($rest)*);
}; };
// for completion purposes // for completion purposes
($elem:expr, $p:path) => { // ($elem:expr, $p:path) => {
$p; // $p;
}; // };
($elem:expr, $($k:ident).+, $($rest:tt)*) => { // ($elem:expr, $a:ident . $($k:ident).+, $($rest:tt)*) => {
$elem.$($k).+; // $elem.$a.lock().unwrap().$($k).+;
$crate::parse_rsx_param!($elem, $($rest)*); // $crate::parse_rsx_param!($elem, $($rest)*);
}; // };
($elem:expr, $($k:ident).+., $($rest:tt)*) => { // ($elem:expr, $a:ident . $($k:ident).+., $($rest:tt)*) => {
$elem.$($k). // $elem.$a.lock().unwrap().$($k).
+.; // +.;
$crate::parse_rsx_param!($elem, $($rest)*); // $crate::parse_rsx_param!($elem, $($rest)*);
}; // };
($elem:expr, $($k:ident).+.: $v:expr) => { // ($elem:expr, $a:ident . $($k:ident).+.: $v:expr) => {
$elem.$($k).+. = $v; // $elem.$a.lock().unwrap().$($k).+. = $v;
}; // };
($elem:expr, $($k:ident).+.: $v:expr, $($rest:tt)*) => { // ($elem:expr, $a:ident . $($k:ident).*: $v:expr, $($rest:tt)*) => {
$elem.$($k).+. = $v; // $elem.$a.lock().unwrap().$($k).+. = $v;
$crate::parse_rsx_param!($elem, $($rest)*); // $crate::parse_rsx_param!($elem, $($rest)*);
}; // };
// For loop // For loop
($elem:expr, for ($($for:tt)*) { $($inner:tt)* } $($rest:tt)*) => { ($elem:expr, for ($($for:tt)*) { $($inner:tt)* } $($rest:tt)*) => {
for $($for)* { for $($for)* {
$elem.children.push({$($inner)*}) $elem.children.lock().unwrap().push({$($inner)*})
} }
$crate::parse_rsx_param!($elem, $($rest)*); $crate::parse_rsx_param!($elem, $($rest)*);
}; };
// Expression // Expression
($elem:expr, {$ielem:expr} $($rest:tt)*) => { ($elem:expr, {$ielem:expr} $($rest:tt)*) => {
$elem.children.push($ielem); $elem.children.lock().unwrap().push($ielem);
$crate::parse_rsx_param!($elem, $($rest)*); $crate::parse_rsx_param!($elem, $($rest)*);
}; };
// Element // Element
($elem:expr, $pelem:path { $($inner:tt)* } $($rest:tt)*) => { ($elem:expr, $pelem:path { $($inner:tt)* } $($rest:tt)*) => {
$elem.children.push($crate::rsx_elem!($pelem { $($inner)* })); $elem.children.lock().unwrap().push($crate::rsx_elem!($pelem { $($inner)* }));
$crate::parse_rsx_param!($elem, $($rest)*) $crate::parse_rsx_param!($elem, $($rest)*)
}; };
// Text // Text
($elem:expr, $text:expr) => { ($elem:expr, $text:expr) => {
$elem.text = format!($text); *$elem.text.lock().unwrap() = format!($text);
}; };
($elem:expr, $text:expr, $($inner:tt)*) => { ($elem:expr, $text:expr, $($inner:tt)*) => {
$elem.text = format!($text, $($inner)*); *$elem.text.lock().unwrap() = format!($text, $($inner)*);
}; };
// Empty // Empty
@@ -268,7 +281,7 @@ macro_rules! rsx_elem {
#[allow(unused_mut)] #[allow(unused_mut)]
let mut elem = $elem(); let mut elem = $elem();
$crate::parse_rsx_param!(elem, $($inner)*); $crate::parse_rsx_param!(elem, $($inner)*);
elem as Box<dyn $crate::Element> elem as std::sync::Arc<dyn $crate::Element>
}}; }};
} }
-1
View File
@@ -1,5 +1,4 @@
use osui::run_test; use osui::run_test;
fn main() { fn main() {
run_test(); run_test();
} }
+14 -3
View File
@@ -1,5 +1,16 @@
use crate::{rsx_elem, Element, ui::*}; use std::sync::{Arc, Mutex};
pub(crate) fn app() -> Box<dyn Element> { use crate::{rsx_elem, ui::*, Element, Handler};
rsx_elem! { text { "Hello, World!" } }
pub(crate) fn app(i: Arc<Mutex<i32>>) -> std::sync::Arc<dyn Element> {
let i1 = Arc::clone(&i);
rsx_elem! { text { on_click: Handler::new(move |t: &Text, ch, _| {
*i1.lock().unwrap() += 1;
ch.rebuild();
// {
// let mut i = i1.lock().unwrap();
// *i += 1;
// *t.text.lock().unwrap() = i.to_string();
// }
}), "{}", i.lock().unwrap() } }
} }
+33 -4
View File
@@ -1,9 +1,38 @@
use crate::{element, State}; use std::sync::Mutex;
use crate::{element, Handler, State};
element! { element! {
Text {} Text {
defaults {} pub on_click: Mutex<Handler<Text<'a>>>
}
defaults {
on_click: Handler::new(|_, _, _| {})
}
fn render(&self, _: State) -> String { fn render(&self, _: State) -> String {
self.text.clone() self.text.lock().unwrap().clone()
}
fn event(&self, ch: &crate::CommandHandler, event: crossterm::event::Event) {
(*self.on_click.lock().unwrap().0)(self, ch, event);
// *self.text.lock().unwrap() = "Done!".to_string();
}
}
element! {
Div {}
defaults {}
fn render(&self, s: State) -> String {
if let Some(child) = self.get_child() {
child.render(s)
} else {
format!("None")
}
}
fn event(&self, ch: &crate::CommandHandler, event: crossterm::event::Event) {
if let Some(e) = self.get_element_by_id("test") {
e.event(ch, event);
}
} }
} }
+6 -2
View File
@@ -7,6 +7,10 @@ pub use styles::*;
pub mod elements; pub mod elements;
pub use elements::*; pub use elements::*;
pub fn text<'a>() -> Box<Text<'a>> { pub fn text<'a>() -> std::sync::Arc<Text<'a>> {
Box::new(Text::new()) std::sync::Arc::new(Text::new())
}
pub fn div<'a>() -> std::sync::Arc<Div<'a>> {
std::sync::Arc::new(Div::new())
} }
+2 -2
View File
@@ -78,7 +78,7 @@ pub fn render_to_frame(
state: crate::State, state: crate::State,
width: usize, width: usize,
frame: &mut Vec<String>, frame: &mut Vec<String>,
element: &Box<dyn Element>, element: &std::sync::Arc<dyn Element>,
) { ) {
let data = element.get_data(); let data = element.get_data();
for (i, line) in element.render(state).split('\n').enumerate() { for (i, line) in element.render(state).split('\n').enumerate() {
@@ -130,7 +130,7 @@ pub enum Direction {
} }
pub fn closest_component( pub fn closest_component(
components: &[Box<dyn Element>], components: &[std::sync::Arc<dyn Element>],
current_index: usize, current_index: usize,
direction: Direction, direction: Direction,
) -> usize { ) -> usize {