better structure

This commit is contained in:
2025-02-06 16:47:06 +01:00
parent d30da4dfe1
commit e53c75097d
4 changed files with 89 additions and 309 deletions
+22 -124
View File
@@ -1,147 +1,45 @@
use crossterm::event::Event; use crossterm::event::Event;
use crate::style; pub struct Frame {
// Structs
#[derive(Debug, Clone)]
pub struct Area {
pub x: u16,
pub y: u16,
pub width: u16, pub width: u16,
pub height: u16, pub height: u16,
}
pub struct Frame {
pub area: Area,
pub event: Option<Event>, pub event: Option<Event>,
} }
impl Frame { impl Frame {
pub fn new(area: Area) -> Frame { pub fn new((width, height): (u16, u16)) -> Frame {
Frame { area, event: None } Frame {
}
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,
width, width,
height, height,
}); event: None,
}
c(&mut frame)
} }
pub fn draw_str(&self, s: &str, props: Props) { pub fn draw_component(&self, component: &mut crate::Component) {
let (written_width, written_height) = crate::utils::str_size(s); component(self)
}
pub fn draw<E: crate::Element>(&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) = ( let (width, height) = (
props.width.get_root(written_width, self.area.width), props.width.get_after(width, self.width),
props.height.get_root(written_height, self.area.height), props.height.get_after(height, self.height),
); );
let (x, y) = ( let (x, y) = (
props.x.get(width, self.area.width), props.x.get(width, self.width),
props.y.get(height, self.area.height), props.y.get(height, self.height),
); );
for (i, line) in s.lines().enumerate() { for (i, line) in s.lines().enumerate() {
println!( println!("\x1b[{};{}H{}\x1b[0m", (y + 1) as usize + i, x + 1, line,);
"\x1b[{};{}H{}\x1b[0m",
(y + self.area.y + 1) as usize + i,
x + self.area.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()
}
}
+25 -29
View File
@@ -1,20 +1,20 @@
pub mod frame; pub mod frame;
mod props;
#[cfg(feature = "state")] #[cfg(feature = "state")]
pub mod state; pub mod state;
pub mod style;
pub mod utils; pub mod utils;
use crossterm::event::Event; use crossterm::event::Event;
use frame::{Area, Frame}; use frame::Frame;
pub use props::*;
pub use std::io::Result; pub use std::io::Result;
pub mod prelude { pub mod prelude {
pub use crate::{ pub use crate::{
frame::{Frame, Props}, frame::Frame,
func, props::{Color, Style},
style::{Color, Style},
utils::init, utils::init,
Component, Result, Component, Element, Props, Result,
}; };
#[cfg(feature = "state")] #[cfg(feature = "state")]
@@ -23,11 +23,13 @@ pub mod prelude {
pub use crossterm::event::{read, Event, KeyCode, KeyEvent, KeyEventKind}; pub use crossterm::event::{read, Event, KeyCode, KeyEvent, KeyEventKind};
} }
// Types pub trait Element {
pub type Handler<T> = std::sync::Arc<std::sync::Mutex<T>>; fn render(&self, size: (u16, u16)) -> String;
pub type Component = Box<dyn FnMut(&Frame)>; fn event(&mut self, _event: crossterm::event::Event) {}
fn action(&mut self, _action: u16) {}
}
// Structs pub type Component = Box<dyn FnMut(&Frame)>;
/// Represents the console state, containing a frame for rendering and a mouse capture flag. /// Represents the console state, containing a frame for rendering and a mouse capture flag.
pub struct Console { pub struct Console {
@@ -36,30 +38,16 @@ pub struct Console {
impl Console { impl Console {
pub fn render(&mut self, ui: &mut Component) -> Result<()> { pub fn render(&mut self, ui: &mut Component) -> Result<()> {
let (width, height) = crossterm::terminal::size()?;
utils::clear()?; utils::clear()?;
let mut frame = Frame::new(Area { let mut frame = Frame::new(crossterm::terminal::size()?);
width,
height,
x: 0,
y: 0,
});
ui(&mut frame); ui(&mut frame);
Ok(()) Ok(())
} }
pub fn event(&mut self, ui: &mut Component, event: Event) -> Result<()> { pub fn event(&mut self, ui: &mut Component, event: Event) -> Result<()> {
let (width, height) = crossterm::terminal::size()?;
utils::clear()?; utils::clear()?;
let mut frame = Frame { let mut frame = Frame::new(crossterm::terminal::size()?);
area: Area { frame.event = Some(event);
width,
height,
x: 0,
y: 0,
},
event: Some(event),
};
ui(&mut frame); ui(&mut frame);
Ok(()) Ok(())
} }
@@ -74,6 +62,14 @@ impl Console {
} }
} }
pub fn func<T>(t: T) -> Handler<T> { impl Element for &str {
std::sync::Arc::new(std::sync::Mutex::new(t)) fn render(&self, _: (u16, u16)) -> String {
self.to_string()
}
}
impl Element for String {
fn render(&self, _: (u16, u16)) -> String {
self.to_owned()
}
} }
+42 -18
View File
@@ -1,32 +1,56 @@
use osmium_engine::prelude::*; use osmium_engine::{prelude::*, utils::clear};
fn main() -> Result<()> { 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)?; console.render(&mut ui)?;
read()?; loop {
console.event(&mut ui, read()?)?;
}
console.close() console.close()
} }
pub fn app(mut _count: State<i32>) -> Component { pub fn app() -> Component {
let mut my_test = test(); let mut count = use_state(0);
Box::new(move |frame| { Box::new(move |frame| {
if let Some(_) = frame.event { frame.draw(
_count += 1; Button {
text: format!("The current count: {count}"),
on_click: || count += 1,
},
Props::center(),
);
})
}
pub struct Button<F: FnMut()> {
text: String,
on_click: F,
}
impl<F: FnMut()> Element for Button<F> {
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()); fn action(&mut self, action: u16) {
if action == 0 {
my_test(frame); (self.on_click)()
}) }
} }
fn test() -> Component {
Box::new(move |frame| {
frame.draw_str("Hello, World", Props::center());
})
} }
-138
View File
@@ -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,
}
}
}