diff --git a/src/frame.rs b/src/frame.rs index 6dfe945..010c0fd 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -1,147 +1,45 @@ use crossterm::event::Event; -use crate::style; - -// Structs -#[derive(Debug, Clone)] -pub struct Area { - pub x: u16, - pub y: u16, +pub struct Frame { pub width: u16, pub height: u16, -} - -pub struct Frame { - pub area: Area, pub event: Option, } impl Frame { - pub fn new(area: Area) -> Frame { - Frame { area, event: None } - } - - pub fn draw(&self, c: &mut crate::Component, props: Props) { - let (width, height) = ( - props.width.get(0, self.area.width), - props.height.get(0, self.area.height), - ); - - let (x, y) = ( - props.x.get(0, self.area.width), - props.y.get(0, self.area.height), - ); - - let mut frame = Frame::new(Area { - x, - y, + pub fn new((width, height): (u16, u16)) -> Frame { + Frame { width, height, - }); - - c(&mut frame) + event: None, + } } - pub fn draw_str(&self, s: &str, props: Props) { - let (written_width, written_height) = crate::utils::str_size(s); + pub fn draw_component(&self, component: &mut crate::Component) { + component(self) + } + + pub fn draw(&self, mut element: E, props: crate::Props) { + if let Some(event) = &self.event { + element.event(event.to_owned()); + } + + let s = element.render((props.width.get(self.width), props.height.get(self.height))); + + let (width, height) = crate::utils::str_size(&s); let (width, height) = ( - props.width.get_root(written_width, self.area.width), - props.height.get_root(written_height, self.area.height), + props.width.get_after(width, self.width), + props.height.get_after(height, self.height), ); let (x, y) = ( - props.x.get(width, self.area.width), - props.y.get(height, self.area.height), + props.x.get(width, self.width), + props.y.get(height, self.height), ); for (i, line) in s.lines().enumerate() { - println!( - "\x1b[{};{}H{}\x1b[0m", - (y + self.area.y + 1) as usize + i, - x + self.area.x + 1, - line, - ); + println!("\x1b[{};{}H{}\x1b[0m", (y + 1) as usize + i, x + 1, line,); } } } - -#[derive(Debug, Clone)] -pub struct Props { - x: style::Position, - y: style::Position, - width: style::Dimension, - height: style::Dimension, -} - -impl Props { - pub fn new(x: u16, y: u16) -> Self { - Self { - x: style::Position::Num(x), - y: style::Position::Num(y), - width: style::Dimension::Auto, - height: style::Dimension::Auto, - } - } - - pub fn auto() -> Self { - Self::new(0, 0) - } - - pub fn center() -> Self { - Self { - x: style::Position::Center, - y: style::Position::Center, - width: style::Dimension::Auto, - height: style::Dimension::Auto, - } - } - - pub fn center_horizontal() -> Self { - Self { - x: style::Position::Center, - y: style::Position::Num(0), - width: style::Dimension::Auto, - height: style::Dimension::Auto, - } - } - - pub fn center_vertical() -> Self { - Self { - x: style::Position::Num(0), - y: style::Position::Center, - width: style::Dimension::Auto, - height: style::Dimension::Auto, - } - } - - pub fn x_percent(&mut self, p: u16) -> Self { - self.x = style::Position::Percent(p); - self.clone() - } - - pub fn y_percent(&mut self, p: u16) -> Self { - self.y = style::Position::Percent(p); - self.clone() - } - - pub fn width(&mut self, w: u16) -> Self { - self.width = style::Dimension::Num(w); - self.clone() - } - - pub fn height(&mut self, h: u16) -> Self { - self.height = style::Dimension::Num(h); - self.clone() - } - - pub fn width_percent(&mut self, w: u16) -> Self { - self.width = style::Dimension::Percent(w); - self.clone() - } - - pub fn height_percent(&mut self, h: u16) -> Self { - self.height = style::Dimension::Percent(h); - self.clone() - } -} diff --git a/src/lib.rs b/src/lib.rs index 4379d6f..ca3cb10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,20 +1,20 @@ pub mod frame; +mod props; #[cfg(feature = "state")] pub mod state; -pub mod style; pub mod utils; use crossterm::event::Event; -use frame::{Area, Frame}; +use frame::Frame; +pub use props::*; pub use std::io::Result; pub mod prelude { pub use crate::{ - frame::{Frame, Props}, - func, - style::{Color, Style}, + frame::Frame, + props::{Color, Style}, utils::init, - Component, Result, + Component, Element, Props, Result, }; #[cfg(feature = "state")] @@ -23,11 +23,13 @@ pub mod prelude { pub use crossterm::event::{read, Event, KeyCode, KeyEvent, KeyEventKind}; } -// Types -pub type Handler = std::sync::Arc>; -pub type Component = Box; +pub trait Element { + fn render(&self, size: (u16, u16)) -> String; + fn event(&mut self, _event: crossterm::event::Event) {} + fn action(&mut self, _action: u16) {} +} -// Structs +pub type Component = Box; /// Represents the console state, containing a frame for rendering and a mouse capture flag. pub struct Console { @@ -36,30 +38,16 @@ pub struct Console { impl Console { pub fn render(&mut self, ui: &mut Component) -> Result<()> { - let (width, height) = crossterm::terminal::size()?; utils::clear()?; - let mut frame = Frame::new(Area { - width, - height, - x: 0, - y: 0, - }); + let mut frame = Frame::new(crossterm::terminal::size()?); ui(&mut frame); Ok(()) } pub fn event(&mut self, ui: &mut Component, event: Event) -> Result<()> { - let (width, height) = crossterm::terminal::size()?; utils::clear()?; - let mut frame = Frame { - area: Area { - width, - height, - x: 0, - y: 0, - }, - event: Some(event), - }; + let mut frame = Frame::new(crossterm::terminal::size()?); + frame.event = Some(event); ui(&mut frame); Ok(()) } @@ -74,6 +62,14 @@ impl Console { } } -pub fn func(t: T) -> Handler { - std::sync::Arc::new(std::sync::Mutex::new(t)) +impl Element for &str { + fn render(&self, _: (u16, u16)) -> String { + self.to_string() + } +} + +impl Element for String { + fn render(&self, _: (u16, u16)) -> String { + self.to_owned() + } } diff --git a/src/main.rs b/src/main.rs index 49da96b..74c2d4a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,32 +1,56 @@ -use osmium_engine::prelude::*; +use osmium_engine::{prelude::*, utils::clear}; fn main() -> Result<()> { - let mut console = init(false)?; + let mut console = init(true)?; - let mut ui = app(use_state(0)); + let mut ui = app(); console.render(&mut ui)?; - read()?; + loop { + console.event(&mut ui, read()?)?; + } console.close() } -pub fn app(mut _count: State) -> Component { - let mut my_test = test(); +pub fn app() -> Component { + let mut count = use_state(0); + Box::new(move |frame| { - if let Some(_) = frame.event { - _count += 1; + frame.draw( + Button { + text: format!("The current count: {count}"), + on_click: || count += 1, + }, + Props::center(), + ); + }) +} + +pub struct Button { + text: String, + on_click: F, +} + +impl Element for Button { + fn render(&self, _size: (u16, u16)) -> String { + self.text.to_owned() + } + + fn event(&mut self, event: crossterm::event::Event) { + match event { + Event::Key(KeyEvent { + code: KeyCode::Enter, + .. + }) => self.action(0), + _ => (), } + } - frame.draw(&mut my_test, Props::center_vertical()); - - my_test(frame); - }) -} - -fn test() -> Component { - Box::new(move |frame| { - frame.draw_str("Hello, World", Props::center()); - }) + fn action(&mut self, action: u16) { + if action == 0 { + (self.on_click)() + } + } } diff --git a/src/style.rs b/src/style.rs deleted file mode 100644 index 60434d2..0000000 --- a/src/style.rs +++ /dev/null @@ -1,138 +0,0 @@ -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Color { - NoColor, - Black, - Red, - Green, - Yellow, - Blue, - Magenta, - Cyan, - White, - BrightBlack, - BrightRed, - BrightGreen, - BrightYellow, - BrightBlue, - BrightMagenta, - BrightCyan, - BrightWhite, - Rgb(u8, u8, u8), // Custom RGB color -} - -impl Color { - /// Converts the enum to its ANSI escape code - pub fn ansi_number(&self, fg: bool) -> String { - let f = if fg { 30 } else { 40 }; - - if *self == Self::NoColor { - return String::new(); - } else if let Self::Rgb(r, g, b) = self { - return format!("{};2;{r};{g};{b}", f + 8); - } - - (f + match self { - Self::Rgb(_, _, _) => 0, - Self::NoColor => 0, - - Self::Black => 0, - Self::Red => 1, - Self::Green => 2, - Self::Yellow => 3, - Self::Blue => 4, - Self::Magenta => 5, - Self::Cyan => 6, - Self::White => 7, - Self::BrightBlack => 60, - Self::BrightRed => 61, - Self::BrightGreen => 62, - Self::BrightYellow => 63, - Self::BrightBlue => 64, - Self::BrightMagenta => 65, - Self::BrightCyan => 66, - Self::BrightWhite => 67, - }) - .to_string() - } - - pub fn to_ansi(&self, foreground: bool) -> String { - let f = if foreground { "3" } else { "4" }; - match self { - Self::NoColor => String::new(), - Self::Black => format!("\x1b[{f}0m"), - Self::Red => format!("\x1b[{f}1m"), - Self::Green => format!("\x1b[{f}2m"), - Self::Yellow => format!("\x1b[{f}3m"), - Self::Blue => format!("\x1b[{f}4m"), - Self::Magenta => format!("\x1b[{f}5m"), - Self::Cyan => format!("\x1b[{f}6m"), - Self::White => format!("\x1b[{f}7m"), - Self::BrightBlack => format!("\x1b[{f}90m"), - Self::BrightRed => format!("\x1b[{f}91m"), - Self::BrightGreen => format!("\x1b[{f}92m"), - Self::BrightYellow => format!("\x1b[{f}93m"), - Self::BrightBlue => format!("\x1b[{f}94m"), - Self::BrightMagenta => format!("\x1b[{f}95m"), - Self::BrightCyan => format!("\x1b[{f}96m"), - Self::BrightWhite => format!("\x1b[{f}97m"), - Self::Rgb(r, g, b) => { - let code = if foreground { "38" } else { "48" }; - format!("\x1b[{code};2;{r};{g};{b}m") - } - } - } -} - -impl Default for Color { - fn default() -> Self { - Self::NoColor - } -} - -#[derive(Debug, Default, Clone, Copy, PartialEq)] -pub struct Style { - pub color: Color, - pub background: Color, - pub px: u16, - pub py: u16, -} - -#[derive(Debug, Clone, Copy)] -pub enum Position { - Num(u16), - Percent(u16), - Center, -} -impl Position { - pub fn get(&self, written: u16, frame: u16) -> u16 { - match self { - Self::Center => (frame - written) / 2, - Self::Percent(percent) => (((*percent as f32) / 100.0) * (frame as f32)) as u16, - Self::Num(n) => *n, - } - } -} - -#[derive(Debug, Clone, Copy)] -pub enum Dimension { - Num(u16), - Percent(u16), - Auto, -} -impl Dimension { - pub fn get(&self, _written: u16, frame: u16) -> u16 { - match self { - Self::Auto => frame, - Self::Percent(percent) => (((*percent as f32) / 100.0) * (frame as f32)) as u16, - Self::Num(n) => *n, - } - } - - pub fn get_root(&self, written: u16, frame: u16) -> u16 { - match self { - Self::Auto => written, - Self::Percent(percent) => (((*percent as f32) / 100.0) * (frame as f32)) as u16, - Self::Num(n) => *n, - } - } -}