diff --git a/Cargo.toml b/Cargo.toml index e2ad459..b8ebace 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,8 @@ documentation = "https://osui.netlify.app/docs/" [features] rsx = [] +frontend = ["rsx"] +portable = [] [lib] name = "osui" diff --git a/src/console.rs b/src/console.rs index 624966e..a4cdc26 100644 --- a/src/console.rs +++ b/src/console.rs @@ -26,20 +26,20 @@ pub fn end() -> crate::Result<()> { } impl Console { - pub fn draw(&self, f: Element) -> crate::Result<()> { - crate::utils::clear()?; - f(&self.0) + pub fn draw(&mut self, ui: Element, event: Option) -> crate::Result<()> { + self.0.clear()?; + ui(&mut self.0, event) } pub fn run(&mut self, ui: Element) -> crate::Result<()> { + self.draw(ui.clone(), None)?; loop { - self.draw(ui.clone())?; let event = read()?; if let Event::Resize(w, h) = event { - self.0.area.width = w; - self.0.area.height = h; + self.0.width = w; + self.0.height = h; } - + self.draw(ui.clone(), Some(event))?; } } } diff --git a/src/lib.rs b/src/lib.rs index d9b4f71..d049319 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ pub mod prelude { pub use std::io::Result; -pub type Element = std::sync::Arc crate::Result<()>>; +pub type Element = std::sync::Arc) -> crate::Result<()>>; pub trait Widget { fn render(&self) -> String; @@ -21,25 +21,59 @@ pub trait Widget { } } +#[derive(Debug, Clone, Copy)] +pub enum Pos { + Auto, + Center, + Num(u16), +} + +impl Pos { + pub fn get(self, auto: u16, width: u16, frame: u16) -> u16 { + match self { + Self::Auto => auto, + Self::Center => (frame - width) / 2, + Self::Num(n) => n, + } + } +} + +#[derive(Debug, Clone, Copy)] +pub enum Size { + Auto, + Num(u16), +} + +impl Size { + fn get_(self, written: u16) -> u16 { + match self { + Self::Auto => written, + Self::Num(n) => n, + } + } + + pub fn get(self, written: u16, _frame: u16) -> u16 { + self.get_(written) + } +} + #[derive(Debug, Clone, Copy)] pub struct Area { - width: u16, - height: u16, - x: u16, - y: u16, - center_x: bool, - center_y: bool, - width_auto: bool, - height_auto: bool, + pub width: Size, + pub height: Size, + pub x: Pos, + pub y: Pos, } #[derive(Debug, Default, Clone, Copy)] pub struct Frame { - pub area: Area, + pub width: u16, + pub height: u16, + last_elem: (u16, u16), } impl Frame { - pub fn draw(&self, w: &W, mut props: Area) -> Result<()> + pub fn draw(&mut self, w: &W, props: Area) -> Result<()> where W: Widget, { @@ -47,49 +81,47 @@ impl Frame { let (ww, wh) = utils::str_size(&written); - if props.width_auto { - props.width = ww; - } - if props.height_auto { - props.height = wh; - } + let (width, height) = ( + props.width.get(ww, self.width), + props.height.get(wh, self.height), + ); - if props.width > self.area.width { - props.width -= props.width - self.area.width; - } - - if props.width > self.area.width { - props.width -= props.width - self.area.width; - } - - if props.center_x { - props.x = (self.area.width - ww) / 2; - } - if props.center_y { - props.y = (self.area.height - wh) / 2; - } + let (x, y) = ( + props.x.get(self.last_elem.0, width, self.width), + props.y.get(self.last_elem.1, height, self.height), + ); for (i, line) in written.lines().enumerate() { - if i as u16 > props.height { + if i as u16 > height { break; } println!( "\x1b[{};{}H{}", - props.y + (i as u16) + 1, - props.x + 1, - line.chars().take(props.width as usize).collect::() + y + (i as u16) + 1, + x + 1, + line.chars().take(width as usize).collect::() ); } + self.last_elem.0 = x + width; + self.last_elem.1 = y + height; + Ok(()) } + pub fn clear(&mut self) -> Result<()> { + self.last_elem.0 = 0; + self.last_elem.1 = 0; + utils::clear() + } + pub fn new((width, height): (u16, u16)) -> Self { - let mut f = Self::default(); - f.area.width(width); - f.area.height(height); - f + Self { + width, + height, + last_elem: (0, 0), + } } } @@ -98,59 +130,72 @@ impl Area { Self::default() } + #[cfg(feature = "portable")] pub fn center_x() -> Self { let mut s = Self::default(); - s.center_x = true; + s.x = Pos::Center; s } + #[cfg(feature = "portable")] pub fn center_y() -> Self { let mut s = Self::default(); - s.center_y = true; + s.y = Pos::Center; s } + #[cfg(feature = "portable")] pub fn center() -> Self { let mut s = Self::default(); - s.center_x = true; - s.center_y = true; + s.x = Pos::Center; + s.y = Pos::Center; s } - pub fn width(&mut self, w: u16) -> Self { + #[cfg(feature = "portable")] + pub fn set_width(&mut self, w: Size) -> Self { self.width = w; - self.width_auto = false; *self } - pub fn height(&mut self, h: u16) -> Self { + #[cfg(feature = "portable")] + pub fn set_height(&mut self, h: Size) -> Self { self.height = h; - self.height_auto = false; *self } - pub fn x(&mut self, x: u16) -> Self { + #[cfg(feature = "portable")] + pub fn set_x(&mut self, x: Pos) -> Self { self.x = x; *self } - pub fn y(&mut self, y: u16) -> Self { + #[cfg(feature = "portable")] + pub fn set_y(&mut self, y: Pos) -> Self { self.y = y; *self } + + #[cfg(feature = "portable")] + pub fn x_auto(&mut self) -> Self { + self.x = Pos::Auto; + *self + } + + #[cfg(feature = "portable")] + pub fn y_zero(&mut self) -> Self { + self.y = Pos::Num(0); + *self + } } impl Default for Area { fn default() -> Self { Self { - width: 0, - height: 0, - x: 0, - y: 0, - center_x: false, - center_y: false, - width_auto: true, - height_auto: true, + width: Size::Auto, + height: Size::Auto, + x: Pos::Num(0), + y: Pos::Auto, } } } diff --git a/src/main.rs b/src/main.rs index 3713ea6..341a8e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,20 +3,16 @@ use osui::prelude::*; fn main() -> osui::Result<()> { let mut con = console::init()?; - let (ui, _count) = app(); - con.run(ui).unwrap(); + con.run(app())?; console::end() } -pub fn app() -> (Element, state::State) { - let count = use_state(0); - let count1 = count.copy_state(); +pub fn app() -> Element { + rsx! { + "X" (x-Center) + "Y" (y-Center) - ( - rsx! { - "{count}" - }, - count1, - ) + "Hello, World" (x-Center, y-Center) + } } diff --git a/src/rsx.rs b/src/rsx.rs index fc10634..db13d95 100644 --- a/src/rsx.rs +++ b/src/rsx.rs @@ -1,13 +1,85 @@ #[macro_export] macro_rules! rsx { - ($($e:literal)*) => { + ($($inner:tt)*) => { std::sync::Arc::new( - move |frame: &$crate::Frame| -> $crate::Result<()> { - $( - frame.draw(&format!($e), Area::center())?; - )* + move |frame: &mut $crate::Frame, event: Option<$crate::console::Event>| -> $crate::Result<()> { + $crate::rsx_inner!(frame, event; $($inner)*); Ok(()) } ) }; } + +#[macro_export] +macro_rules! rsx_inner { + ($frame:expr, $event:expr; + $elem:literal ($($inner:tt)*) + $($rest:tt)*) => { + let mut area = $crate::Area::new(); + $crate::tw_area!(area, $($inner)*); + $frame.draw(&format!($elem), area)?; + $crate::rsx_inner!($frame, $event; $($rest)*); + }; + + ($frame:expr, $event:expr; + $elem:literal + $($rest:tt)*) => { + $frame.draw(&format!($elem), Area::new())?; + $crate::rsx_inner!($frame, $event; $($rest)*); + }; + + ($frame:expr, $event:expr;) => {}; +} + +#[macro_export] +macro_rules! tw_area { + ($a:expr, $n:ident) => { + $a.$n; + }; + + // X + ($a:expr, x-$v:ident $(, $($rest:tt)*)?) => { + $a.x = $crate::Pos::$v; + $crate::tw_area!($a, $($($rest)*)?); + }; + + ($a:expr, x-$v:literal $(, $($rest:tt)*)?) => { + $a.x = $crate::Pos::Num($v); + $crate::tw_area!($a, $($($rest)*)?); + }; + + // Y + ($a:expr, y-$v:ident $(, $($rest:tt)*)?) => { + $a.y = $crate::Pos::$v; + $crate::tw_area!($a, $($($rest)*)?); + }; + + ($a:expr, y-$v:literal $(, $($rest:tt)*)?) => { + $a.y = $crate::Pos::Num($v); + $crate::tw_area!($a, $($($rest)*)?); + }; + + // Width + ($a:expr, width-$v:ident $(, $($rest:tt)*)?) => { + $a.width = $crate::Size::$v; + $crate::tw_area!($a, $($($rest)*)?); + }; + + ($a:expr, width-$v:literal $(, $($rest:tt)*)?) => { + $a.width = $crate::Size::Num($v); + $crate::tw_area!($a, $($($rest)*)?); + }; + + // Height + ($a:expr, height-$v:ident $(, $($rest:tt)*)?) => { + $a.height = $crate::Size::$v; + $crate::tw_area!($a, $($($rest)*)?); + }; + + ($a:expr, height-$v:literal $(, $($rest:tt)*)?) => { + $a.height = $crate::Size::Num($v); + $crate::tw_area!($a, $($($rest)*)?); + }; + + ($a:expr,) => {}; +} diff --git a/src/state.rs b/src/state.rs index af6c13f..0e51328 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,3 +1,4 @@ +#[derive(Clone, Copy)] pub struct State(pub(crate) *mut T); impl std::fmt::Display for State {