diff --git a/src/css.rs b/src/css.rs index 31fc113..0feef17 100644 --- a/src/css.rs +++ b/src/css.rs @@ -3,181 +3,40 @@ macro_rules! __css { ($_style:expr,)=>{}; // percentage (n%) ( - $_style:expr, $name:ident: $value:literal% $(, $($other:tt)*)? + $_style:expr, $name:ident: $value:literal% $(, $($rest:tt)*)? ) => {{ $_style.$name = Number::Pe($value); - $crate::__css!($_style, $($($other)*)?); - }}; - ( - $_style:expr, $name:literal: $value:literal% $(, $($other:tt)*)? - ) => {{ - $_style.other.insert($name.to_string(), Box::new( - Number::Pe($value) - )); - $crate::__css!($_style, $($($other)*)?); + $crate::__css!($_style, $($($rest)*)?); }}; // Number ( - $_style:expr, $name:ident: $value:literal px $(, $($other:tt)*)? + $_style:expr, $name:ident: $value:literal px $(, $($rest:tt)*)? ) => {{ $_style.$name = Number::Px($value); - $crate::__css!($_style, $($($other)*)?); + $crate::__css!($_style, $($($rest)*)?); }}; // Vector ( - $_style:expr, $name:ident: [$($inner:tt),*] $(, $($other:tt)*)? + $_style:expr, $name:ident: [$($inner:tt),*] $(, $($rest:tt)*)? ) => {{ $_style.$name = vec![$($inner),*]; - $crate::__css!($_style, $($($other)*)?); + $crate::__css!($_style, $($($rest)*)?); }}; // Normal ( - $_style:expr, $name:ident: $value:expr $(, $($other:tt)*)? + $_style:expr, $name:ident: $value:expr $(, $($rest:tt)*)? ) => {{ $_style.$name = $value; - $crate::__css!($_style, $($($other)*)?); - }}; - - ( - $_style:expr, $name:literal: $value:expr $(, $($other:tt)*)? - ) => {{ - $_style.other.insert($name.to_string(), Box::new($value)); - $crate::__css!($_style, $($($other)*)?); + $crate::__css!($_style, $($($rest)*)?); }}; } #[macro_export] macro_rules! _css { ($_style:expr,) => {}; - ( - $hm:expr, .$name:ident { $($inner:tt)* } $($rest:tt)* - ) => {{ - let name = $crate::ui::StyleName::Class(stringify!($name).to_string()); - if let Some(_style) = $hm.get_mut(&name) { - $crate::__css!(_style.0, $($inner)*); - } else { - let mut _style = $crate::ui::Style::default(); - - $crate::__css!(_style.0, $($inner)*); - - $hm.insert( - name, - _style, - ); - } - $crate::_css!($hm, $($rest)*); - }}; - ( - $hm:expr, .$name:ident:$kind:ident { $($inner:tt)* } $($rest:tt)* - ) => {{ - let name = $crate::ui::StyleName::Class(stringify!($name).to_string()); - if let Some(_style) = $hm.get_mut(&name) { - let mut style_elem = $crate::ui::StyleElement::default(); - $crate::__css!(style_elem, $($inner)*); - _style.1.insert(stringify!($kind).to_string(), style_elem); - } else { - let mut _style = $crate::ui::Style::default(); - - let mut style_elem = $crate::ui::StyleElement::default(); - $crate::__css!(style_elem, $($inner)*); - _style.1.insert(stringify!($kind).to_string(), style_elem); - - $hm.insert( - name, - _style, - ); - } - $crate::_css!($hm, $($rest)*); - }}; - - - ( - $hm:expr, #$name:ident { $($inner:tt)* } $($rest:tt)* - ) => {{ - let name = $crate::ui::StyleName::Id(stringify!($name).to_string()); - if let Some(_style) = $hm.get_mut(&name) { - $crate::__css!(_style.0, $($inner)*); - } else { - let mut _style = $crate::ui::Style::default(); - - $crate::__css!(_style.0, $($inner)*); - - $hm.insert( - name, - _style, - ); - } - $crate::_css!($hm, $($rest)*); - }}; - ( - $hm:expr, #$name:ident:$kind:ident { $($inner:tt)* } $($rest:tt)* - ) => {{ - let name = $crate::ui::StyleName::Id(stringify!($name).to_string()); - if let Some(_style) = $hm.get_mut(&name) { - let mut style_elem = $crate::ui::StyleElement::default(); - $crate::__css!(style_elem, $($inner)*); - _style.1.insert(stringify!($kind).to_string(), style_elem); - } else { - let mut _style = $crate::ui::Style::default(); - - let mut style_elem = $crate::ui::StyleElement::default(); - $crate::__css!(style_elem, $($inner)*); - _style.1.insert(stringify!($kind).to_string(), style_elem); - - $hm.insert( - name, - _style, - ); - } - $crate::_css!($hm, $($rest)*); - }}; - - - ( - $hm:expr, $name:ident { $($inner:tt)* } $($rest:tt)* - ) => {{ - let name = $crate::ui::StyleName::Component(stringify!($name).to_string()); - if let Some(_style) = $hm.get_mut(&name) { - $crate::__css!(_style.0, $($inner)*); - } else { - let mut _style = $crate::ui::Style::default(); - - $crate::__css!(_style.0, $($inner)*); - - $hm.insert( - name, - _style, - ); - } - $crate::_css!($hm, $($rest)*); - }}; - ( - $hm:expr, $name:ident:$kind:ident { $($inner:tt)* } $($rest:tt)* - ) => {{ - let name = $crate::ui::StyleName::Component(stringify!($name).to_string()); - if let Some(_style) = $hm.get_mut(&name) { - let mut style_elem = $crate::ui::StyleElement::default(); - $crate::__css!(style_elem, $($inner)*); - _style.1.insert(stringify!($kind).to_string(), style_elem); - } else { - let mut _style = $crate::ui::Style::default(); - - let mut style_elem = $crate::ui::StyleElement::default(); - $crate::__css!(style_elem, $($inner)*); - _style.1.insert(stringify!($kind).to_string(), style_elem); - - $hm.insert( - name, - _style, - ); - } - $crate::_css!($hm, $($rest)*); - }}; - - ( $hm:expr, $name:literal { $($inner:tt)* } $($rest:tt)* ) => {{ @@ -197,19 +56,19 @@ macro_rules! _css { $crate::_css!($hm, $($rest)*); }}; ( - $hm:expr, $name:literal:$kind:ident { $($inner:tt)* } $($rest:tt)* + $hm:expr, $name:literal: $kind:literal { $($inner:tt)* } $($rest:tt)* ) => {{ let name = $crate::ui::StyleName::Class(String::from($name)); if let Some(_style) = $hm.get_mut(&name) { let mut style_elem = $crate::ui::StyleElement::default(); $crate::__css!(style_elem, $($inner)*); - _style.1.insert(stringify!($kind).to_string(), style_elem); + _style.1.insert($kind.to_string(), style_elem); } else { let mut _style = $crate::ui::Style::default(); let mut style_elem = $crate::ui::StyleElement::default(); $crate::__css!(style_elem, $($inner)*); - _style.1.insert(stringify!($kind).to_string(), style_elem); + _style.1.insert($kind.to_string(), style_elem); $hm.insert( name, @@ -224,8 +83,11 @@ macro_rules! _css { /// /// # Examples /// ``` -/// .btn: clicked { -/// color: Blue +/// "btn" { +/// color: Red, +/// } +/// "btn": "clicked" { +/// color: Blue, /// } /// ``` /// @@ -245,6 +107,32 @@ macro_rules! css { #[macro_export] macro_rules! style { + ( + $key:ident: $value:literal% $(, $($rest:tt)*)? + ) => {{ + let mut _style = $crate::ui::Style::default(); + $crate::__css!(_style.0, $key: $crate::ui::Number::Pe($value)); + $crate::style!{ _style; $($($rest)*)? } + }}; + ( + $key:ident: $value:literal px $(, $($rest:tt)*)? + ) => {{ + let mut _style = $crate::ui::Style::default(); + $crate::__css!(_style.0, $key: $crate::ui::Number::Px($value)); + $crate::style!{ _style; $($($rest)*)? } + }}; + ( + $style:expr; $key:ident: $value:literal% $(, $($rest:tt)*)? + ) => {{ + $crate::__css!($style.0, $key: $crate::ui::Number::Pe($value)); + $crate::style!{ $style; $($($rest)*)? } + }}; + ( + $style:expr; $key:ident: $value:literal px $(, $($rest:tt)*)? + ) => {{ + $crate::__css!($style.0, $key: $crate::ui::Number::Px($value)); + $crate::style!{ $style; $($($rest)*)? } + }}; ( $key:ident: $value:expr $(, $($rest:tt)*)? ) => {{ diff --git a/src/examples.rs b/src/examples.rs index 61b60d7..927e173 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -24,33 +24,28 @@ pub fn todo_app() -> Element { styling: Some(styling()), class: "root", div { - class: "first", + class: "tab", text { class: "title", "not started" } } - div { - class: "second", - text { class: "title", "completed" } - } + // div { + // class: "tab", + // text { class: "title", "completed" } + // } } } pub fn styling() -> Css { css! { - "first" { - x: Auto, - y: 0 px, - width: 50%, - } - "second" { - x: Auto, - y: 0 px, - width: 50%, - } - "root" { - width: 100%, + "tab" { + // x: 0 px, + // y: Auto, + // height: 50%, + outline: true, } "title" { - x: Center + // x: Center, + // color: Red, + // width: 15 px, } } } diff --git a/src/lib.rs b/src/lib.rs index c6b6ecb..fd7c91e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,9 +82,10 @@ pub struct Document { running: *mut Option, } +#[derive(Debug)] pub struct Writer { text: String, - style: Style, + style: ui::StyleElement, focused: bool, size: (u16, u16), } @@ -145,7 +146,7 @@ impl Document { let mut frame = utils::Frame::new(width, height); frame.render(true, unsafe { &*self.element }); utils::clear(); - print!("{}", frame.output_nnl()); + print!("{}", frame.output_nnl().replace("\n", "")); utils::flush(); } } @@ -227,7 +228,7 @@ impl Handler { } impl Writer { - pub fn new(focused: bool, style: ui::Style, size: (u16, u16)) -> Writer { + pub fn new(focused: bool, style: ui::StyleElement, size: (u16, u16)) -> Writer { Writer { text: String::new(), style, @@ -237,16 +238,19 @@ impl Writer { } pub fn write(&mut self, s: &str) { - self.text += self.style.get(self.focused).write(s).as_str(); - } - - pub fn get_size(&self) -> (u16, u16) { - self.size + self.text += self.style.write(s).as_str(); } pub fn get_focused(&self) -> bool { self.focused } + + pub fn get_size(&self) -> (u16, u16) { + ( + self.style.width.as_size(self.size.0, self.style.outline), + self.style.height.as_size(self.size.1, self.style.outline), + ) + } } impl Default for Handler { diff --git a/src/main.rs b/src/main.rs index 6c3666c..b7a1c38 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,3 +3,12 @@ use osui::{examples::todo_app, prelude::*}; fn main() { launch!(todo_app()) } + +// use osui::utils::overlay; + +// fn main() { +// let layer0 = " ".repeat(crossterm::terminal::size().unwrap().0 as usize); +// let layer1 = "\0\x1b[32m\0ABC"; +// let overlayed = overlay(&layer0, &layer1, 0).replace('\0', ""); +// println!("{overlayed}END"); +// } diff --git a/src/ui/styles.rs b/src/ui/styles.rs index d71c35c..99d2a13 100644 --- a/src/ui/styles.rs +++ b/src/ui/styles.rs @@ -83,7 +83,6 @@ pub struct StyleElement { pub height: Number, pub visible: bool, pub outline: bool, - pub other: HashMap>, } #[derive(Debug, Clone)] @@ -129,7 +128,7 @@ impl StyleElement { .into_iter() .map(|p| { format!( - "{}{}{}{p}\x1b[0m", + "\0{}{}{}\0{p}\0\x1b[0m\0", self.color.ansi(), self.background.ansi_bg(), self.font.ansi() @@ -148,12 +147,11 @@ impl Default for StyleElement { font: Vec::new(), outline_color: Color::NoColor, x: Number::Default, - y: Number::Default, + y: Number::Px(0), width: Number::Default, height: Number::Default, visible: true, outline: false, - other: HashMap::new(), } } } @@ -305,13 +303,18 @@ impl StyleCore for Vec { } impl Number { - pub fn as_size(&self, text_size: u16, frame_size: u16) -> u16 { + pub fn as_size_raw(&self, frame_size: u16) -> u16 { match self { Number::Px(s) => *s, Number::Pe(pe) => (frame_size * pe) / 100, - Number::Default => frame_size, - crate::ui::Number::Auto => text_size, - _ => 0, + _ => frame_size, + } + } + pub fn as_size(&self, frame_size: u16, outline: bool) -> u16 { + if outline { + self.as_size_raw(frame_size) - 2 + } else { + self.as_size_raw(frame_size) } } pub fn as_position_y(&self, used: &Vec, content_size: u16, frame_size: u16) -> u16 { @@ -320,14 +323,14 @@ impl Number { crate::ui::Number::Pe(pe) => (frame_size * pe) / 100, crate::ui::Number::Center => (frame_size - content_size) / 2, crate::ui::Number::Auto | crate::ui::Number::Default => { - let mut x = 0; + let mut y = 0; for (i, n) in used.iter().enumerate() { if *n == 0 { - x = i as u16; + y = i as u16; break; } } - x + y } } } diff --git a/src/utils.rs b/src/utils.rs index 34e9775..81b14db 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,86 +1,36 @@ -/// The `utils` module contains helper functions and utilities that assist in rendering and -/// managing UI state. These functions are used by UI elements like `Div` and `Text` for common -/// tasks such as frame creation, event handling, and component selection. -/// -/// # Example -/// ```rust -/// use crate::ui::utils::{create_frame, render_to_frame}; -/// -/// let frame = create_frame(50, 10); -/// render_to_frame(crate::State::Normal, 50, &mut frame, &text_element); -/// ``` -use lazy_static::lazy_static; -use regex::Regex; -use std::{ - collections::HashMap, - io::{stdout, Write}, -}; +use std::io::{stdout, Write}; -lazy_static! { - pub static ref ANSI: Regex = Regex::new(r"(\x1b\[([0-9;]*)[a-zA-Z])+").unwrap(); -} +pub fn overlay(layer0: &str, layer1: &str, mut index: usize) -> String { + let layer0: Vec = layer0.chars().collect(); + let layer1: Vec = layer1.chars().collect(); + let mut layer1_index = 0; + let mut result = String::new(); + let mut is_ansi = false; + let mut ansi = String::new(); -/// Compress a string by a regex pattern -pub fn compress_string(input: &str, re: &Regex) -> (String, HashMap) { - let mut matches_map = HashMap::new(); - let mut res = String::new(); - let input_chars: Vec = input.chars().collect(); - - let mut i = 0; - while i < input_chars.len() { - let remaining_input: String = input_chars[i..].iter().collect(); - if let Some(loc) = re.find(&remaining_input) { - if loc.start() == 0 { - let matched_chars: String = input_chars[i..i + loc.end()].iter().collect(); - matches_map.insert(res.len(), matched_chars.clone()); - i += loc.end(); - } else { - res.push(input_chars[i]); - i += 1; + for (i, &c) in layer0.iter().enumerate() { + if c == '\0' { + if is_ansi { + result.push_str(&ansi); + ansi.clear(); } + is_ansi = !is_ansi; + index += 1; + } else if is_ansi { + ansi.push(c); + index += 1; + } else if i >= index && layer1_index < layer1.len() { + if layer1[layer1_index] == '\0' { + result.push(c); + } + result.push(layer1[layer1_index]); + layer1_index += 1; } else { - res.push(input_chars[i]); - i += 1; + result.push(c); } } - (res, matches_map) -} - -/// Merges a frame withe a line by x -fn merge_line(frame_: &str, (mut line_, lm): (String, HashMap), x: usize) -> String { - let (frame_, fm) = compress_string(frame_, &ANSI); - - if let Some(_) = lm.get(&line_.len()) { - line_.push('\n'); - } - - let mut res = String::new(); - let frame: Vec = frame_.chars().collect(); - let line: Vec = (line_).chars().collect(); - - let flen = frame.len(); - let llen = line.len(); - - for i in 0..flen { - if let Some(v) = fm.get(&i) { - res.push_str(v); - } - if i >= x && i - x < llen && line[i - x] != '\t' { - if let Some(v) = lm.get(&(i - x)) { - res.push_str(v); - } - if line[i - x] == '\n' { - res.push(frame[i]); - } else { - res.push(line[i - x]); - } - } else { - res.push(frame[i]); - } - } - - res + result } pub struct Frame { @@ -98,31 +48,49 @@ impl Frame { } } pub fn render(&mut self, focused: bool, element: &crate::Element) { + let style = element.get_style().get(focused); let mut writer = crate::Writer::new( focused, - element.get_style().clone(), + style.clone(), (self.width, self.frame.len() as u16), ); - let style = writer.style.get(focused); - element.render(&mut writer); if style.visible { let frame_height = self.frame.len() as u16; - let width = style.width.as_size(writer.text.len() as u16, self.width); - let height = style.height.as_size(0, frame_height); + let (width, height) = writer.get_size(); let y = style.y.as_position_y(&self.used, height, frame_height) as usize; - let x = style - .x - .as_position_x(self.used.get(y).unwrap(), width, self.width) - as usize; - - for (i, line) in writer.text.lines().enumerate() { - if frame_height > (y + i) as u16 { - let frame_line = self.frame.get_mut(y + i).unwrap(); - let (line_, lm) = compress_string(line, &ANSI); - let formatted = format_string(&line_, width as usize); - *frame_line = - merge_line(&frame_line, (formatted.clone(), lm.clone()), x as usize); - *self.used.get_mut(y + i).unwrap() += (x + formatted.len()) as u16; + if let Some(y_) = self.used.get(y) { + let x = style.x.as_position_x(y_, width, self.width) as usize; + if style.outline { + // { + // let frame_line = self.frame.get_mut(y).unwrap(); + // *frame_line = + // overlay(&frame_line, &"_".repeat(width as usize - 2), x as usize); + // // *self.used.get_mut(y - 1).unwrap() += (x as u16 + width + 1) as u16; + // } + // { + // if let Some(frame_line) = self.frame.get_mut(y + height as usize) { + // *frame_line = overlay( + // &frame_line, + // &"⎻".repeat(width as usize - 2), + // x as usize + 1, + // ); + // // *self.used.get_mut(y + height as usize).unwrap() += + // // (x as u16 + width + 1) as u16; + // } + // } + // y += 1; + } + element.render(&mut writer); + for (i, line) in writer.text.lines().enumerate() { + if frame_height > (y + i) as u16 { + let frame_line = self.frame.get_mut(y + i).unwrap(); + if style.outline { + *frame_line = overlay(frame_line, &format!("a{line}b"), x as usize); + } else { + *frame_line = overlay(frame_line, line, x as usize); + } + // *self.used.get_mut(y + i).unwrap() += (x + formatted.len()) as u16; + } } } } @@ -136,16 +104,25 @@ impl Frame { pub fn output_nnl(&self) -> String { self.frame.join("") } + + pub fn output_array(&self) -> &Vec { + &self.frame + } } -fn format_string(input: &str, width: usize) -> String { - let truncated = if input.len() > width { - &input[..width] - } else { - input - }; - format!("{: String { +// let truncated = if input.len() > width { +// let input_chars: Vec = input.chars().collect(); +// input_chars[..width].iter().collect::() +// } else { +// input.to_string() +// }; +// if outline { +// format!("a{: