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