changed framing

This commit is contained in:
2025-02-04 23:39:18 +01:00
parent def18fed65
commit d30da4dfe1
6 changed files with 203 additions and 194 deletions
-13
View File
@@ -1,13 +0,0 @@
use crate::Element;
impl Element for &str {
fn render(&self, frame: &mut String) {
*frame += self;
}
}
impl Element for String {
fn render(&self, frame: &mut String) {
*frame += self;
}
}
+147
View File
@@ -0,0 +1,147 @@
use crossterm::event::Event;
use crate::style;
// Structs
#[derive(Debug, Clone)]
pub struct Area {
pub x: u16,
pub y: u16,
pub width: u16,
pub height: u16,
}
pub struct Frame {
pub area: Area,
pub event: Option<Event>,
}
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,
width,
height,
});
c(&mut frame)
}
pub fn draw_str(&self, s: &str, props: Props) {
let (written_width, written_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),
);
let (x, y) = (
props.x.get(width, self.area.width),
props.y.get(height, self.area.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,
);
}
}
}
#[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()
}
}
+32 -164
View File
@@ -1,153 +1,66 @@
pub mod elements; pub mod frame;
#[cfg(feature = "state")] #[cfg(feature = "state")]
pub mod state; pub mod state;
pub mod style; pub mod style;
pub mod utils; pub mod utils;
use crossterm::event::Event;
use frame::{Area, Frame};
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},
func, func,
state::State,
style::{Color, Style}, style::{Color, Style},
utils::init, utils::init,
Props, Result, Ui, Component, Result,
}; };
#[cfg(feature = "state")] #[cfg(feature = "state")]
pub use crate::state::use_state; pub use crate::state::{default_state, use_state, State};
pub use crossterm::event::read; pub use crossterm::event::{read, Event, KeyCode, KeyEvent, KeyEventKind};
}
pub trait Element {
fn render(&self, frame: &mut String);
fn event(&self, event: crossterm::event::Event) {
_ = event
}
} }
// Types // Types
pub type Handler<T> = std::sync::Arc<std::sync::Mutex<T>>; pub type Handler<T> = std::sync::Arc<std::sync::Mutex<T>>;
pub type Ui = Box<dyn Fn(&mut Frame)>; pub type Component = Box<dyn FnMut(&Frame)>;
// Structs // Structs
pub struct Frame(Vec<(Box<dyn Element>, Props)>);
#[derive(Debug, Clone)]
pub struct Props {
x: style::Position,
y: style::Position,
width: style::Dimension,
height: style::Dimension,
style: style::Style,
}
/// 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 {
mouse: bool, mouse: bool,
width: u16,
height: u16,
pub mouse_position: Option<(u16, u16)>,
}
impl Frame {
pub fn draw(&mut self, element: Box<dyn Element>, props: Props) {
self.0.push((element, props));
}
}
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,
style: style::Style::default(),
}
}
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,
style: style::Style::default(),
}
}
pub fn center_horizontal() -> Self {
Self {
x: style::Position::Center,
y: style::Position::Num(0),
width: style::Dimension::Auto,
height: style::Dimension::Auto,
style: style::Style::default(),
}
}
pub fn center_vertical() -> Self {
Self {
x: style::Position::Num(0),
y: style::Position::Center,
width: style::Dimension::Auto,
height: style::Dimension::Auto,
style: style::Style::default(),
}
}
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 style(&mut self, style: style::Style) -> Self {
self.style = style;
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()
}
} }
impl Console { impl Console {
pub fn render(&mut self, ui: &Ui) -> Result<()> { pub fn render(&mut self, ui: &mut Component) -> Result<()> {
(self.width, self.height) = crossterm::terminal::size()?; let (width, height) = crossterm::terminal::size()?;
utils::clear()?; utils::clear()?;
let mut frame = Frame(Vec::new()); let mut frame = Frame::new(Area {
width,
height,
x: 0,
y: 0,
});
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),
};
ui(&mut frame); ui(&mut frame);
for (e, _) in frame.0 {
self.draw(e.as_ref(), &Props::auto());
}
Ok(()) Ok(())
} }
@@ -159,51 +72,6 @@ impl Console {
utils::show_cursor()?; utils::show_cursor()?;
utils::clear() utils::clear()
} }
fn draw(&mut self, element: &dyn Element, props: &Props) {
let mut written = String::new();
element.render(&mut written);
let (written_width, written_height) = utils::str_size(&written);
let (width, height) = (
props.width.get_root(written_width, self.width),
props.height.get_root(written_height, self.height),
);
let (x, y) = (
props.x.get(width, self.width),
props.y.get(height, self.height),
);
let ansi = if props.style.background == style::Color::NoColor {
format!("\x1b[{}m", props.style.color.ansi_number(true))
} else {
format!(
"\x1b[{};{}m",
props.style.color.ansi_number(true),
props.style.background.ansi_number(false)
)
};
let (x, y) = (x - props.style.px, y - props.style.py);
let px = " ".repeat(props.style.px as usize);
written
.push_str(&format!("\n{}", " ".repeat(width as usize)).repeat(props.style.py as usize));
written.insert_str(
0,
&format!("{}\n", " ".repeat(width as usize)).repeat(props.style.py as usize),
);
for (i, line) in written.lines().enumerate() {
println!(
"\x1b[{};{}H{ansi}{px}{}{px}\x1b[0m",
y as usize + i + 1,
x + 1,
line,
);
}
}
} }
pub fn func<T>(t: T) -> Handler<T> { pub fn func<T>(t: T) -> Handler<T> {
+19 -10
View File
@@ -1,23 +1,32 @@
use osmium_engine::prelude::*; use osmium_engine::prelude::*;
fn main() -> Result<()> { fn main() -> Result<()> {
let mut console = init(true)?; let mut console = init(false)?;
let mut count = use_state(0); let mut ui = app(use_state(0));
let ui = app(count); console.render(&mut ui)?;
loop { read()?;
console.render(&ui)?;
read()?;
count += 1;
}
console.close() console.close()
} }
pub fn app(count: State<i32>) -> Ui { pub fn app(mut _count: State<i32>) -> Component {
let mut my_test = test();
Box::new(move |frame| { Box::new(move |frame| {
frame.draw(Box::new(format!("Events: {count}")), Props::center()); if let Some(_) = frame.event {
_count += 1;
}
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());
}) })
} }
+4
View File
@@ -21,6 +21,10 @@ pub fn use_state<T>(v: T) -> State<T> {
State(Box::into_raw(Box::new(v))) State(Box::into_raw(Box::new(v)))
} }
pub fn default_state<T: Default>() -> State<T> {
State(Box::into_raw(Box::new(T::default())))
}
impl<T: std::fmt::Display> std::fmt::Display for State<T> { impl<T: std::fmt::Display> std::fmt::Display for State<T> {
/// Formats the value of the state for display. /// Formats the value of the state for display.
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+1 -7
View File
@@ -107,11 +107,5 @@ pub fn init<'a>(mouse: bool) -> crate::Result<crate::Console> {
if mouse { if mouse {
crossterm::execute!(std::io::stdout(), crossterm::event::EnableMouseCapture)?; crossterm::execute!(std::io::stdout(), crossterm::event::EnableMouseCapture)?;
} }
let (width, height) = crossterm::terminal::size()?; Ok(crate::Console { mouse })
Ok(crate::Console {
width,
height,
mouse,
mouse_position: None,
})
} }