style update
This commit is contained in:
+2
-1
@@ -19,4 +19,5 @@ crossterm = "0.28.1"
|
|||||||
regex = "*"
|
regex = "*"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
paste = "1.0.15"
|
paste = "1.0.15"
|
||||||
osui-element = { path = "./osui-element" }
|
osui-element = { path = "./osui-element" }
|
||||||
|
dyn-clone = "1.0.17"
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ pub fn element(_args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
.parse2(quote! { pub children: Children })
|
.parse2(quote! { pub children: Children })
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
|
fields.named.push(
|
||||||
|
syn::Field::parse_named
|
||||||
|
.parse2(quote! { pub style: Style })
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
fields.named.push(
|
fields.named.push(
|
||||||
syn::Field::parse_named
|
syn::Field::parse_named
|
||||||
.parse2(quote! { pub id: &'a str })
|
.parse2(quote! { pub id: &'a str })
|
||||||
@@ -92,6 +97,20 @@ pub fn element(_args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn set_styling(&mut self, styling: &std::collections::HashMap<StyleName, Style>) {
|
||||||
|
if let Some(style) = styling.get(&StyleName::Class(self.class.to_string())) {
|
||||||
|
self.style = style.clone();
|
||||||
|
} else if let Some(style) = styling.get(&StyleName::Id(self.id.to_string())) {
|
||||||
|
self.style = style.clone();
|
||||||
|
} else if let Some(style) = styling.get(&StyleName::Component(stringify!(#struct_name).to_string())) {
|
||||||
|
self.style = style.clone();
|
||||||
|
}
|
||||||
|
if let Children::Children(children, _) = &mut self.children {
|
||||||
|
for child in children {
|
||||||
|
child.set_styling(styling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+37
-5
@@ -20,6 +20,12 @@
|
|||||||
//! - `ui` - Contains all user interface components, enabling rich CLI experiences.
|
//! - `ui` - Contains all user interface components, enabling rich CLI experiences.
|
||||||
//! - `utils` - Utility functions for common TUI tasks such as clearing the screen.
|
//! - `utils` - Utility functions for common TUI tasks such as clearing the screen.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fmt::Debug,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
use crossterm::event::Event;
|
use crossterm::event::Event;
|
||||||
|
|
||||||
pub mod app;
|
pub mod app;
|
||||||
@@ -31,8 +37,19 @@ pub mod utils;
|
|||||||
/// of UI elements. It re-exports commonly used traits, structs, and utility functions to make
|
/// of UI elements. It re-exports commonly used traits, structs, and utility functions to make
|
||||||
/// the UI framework easier to use.
|
/// the UI framework easier to use.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::{app::run, rsx, rsx_elem, ui::*, StateChanger};
|
pub use crate::ui::Color::*;
|
||||||
pub use crate::{Children, Component, Element, ElementCore, State, Value};
|
pub use crate::{app::run, css, rsx, rsx_elem, ui::*, Handler};
|
||||||
|
pub use crate::{Children, Element, ElementCore, State, Value};
|
||||||
|
pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};
|
||||||
|
pub fn sleep(ms: u64) {
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod prelude_extra{
|
||||||
|
pub use crate::ui::Color::*;
|
||||||
|
pub use crate::{app::run, css, rsx, rsx_elem, ui::*, Handler, StateChanger};
|
||||||
|
pub use crate::{Children, Component, Element, ElementCore, State, Value, run_handler};
|
||||||
pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};
|
pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};
|
||||||
pub fn sleep(ms: u64) {
|
pub fn sleep(ms: u64) {
|
||||||
std::thread::sleep(std::time::Duration::from_millis(ms));
|
std::thread::sleep(std::time::Duration::from_millis(ms));
|
||||||
@@ -79,6 +96,7 @@ pub trait ElementCore: Send + Sync {
|
|||||||
fn update_data(&mut self, width: usize, height: usize);
|
fn update_data(&mut self, width: usize, height: usize);
|
||||||
fn get_element_by_id(&mut self, id: &str) -> Option<&mut Element>;
|
fn get_element_by_id(&mut self, id: &str) -> Option<&mut Element>;
|
||||||
fn get_child(&mut self) -> Option<&mut Element>;
|
fn get_child(&mut self) -> Option<&mut Element>;
|
||||||
|
fn set_styling(&mut self, styling: &HashMap<crate::ui::StyleName, crate::ui::Style>);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Component: ElementCore + std::fmt::Debug {
|
pub trait Component: ElementCore + std::fmt::Debug {
|
||||||
@@ -98,14 +116,28 @@ pub type Element = Box<dyn Component>;
|
|||||||
/// Handler Struct
|
/// Handler Struct
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
pub struct Handler<T>(pub Box<dyn FnMut(&T, Event, &StateChanger) + Send>);
|
pub struct Handler<T>(pub Arc<Mutex<dyn FnMut(&mut T, Event, &StateChanger) + Send + Sync>>);
|
||||||
|
|
||||||
impl<T> Handler<T> {
|
impl<T> Handler<T> {
|
||||||
pub fn new<F>(handler_fn: F) -> Handler<T>
|
pub fn new<F>(handler_fn: F) -> Handler<T>
|
||||||
where
|
where
|
||||||
F: FnMut(&T, Event, &StateChanger) + 'static + Send,
|
F: FnMut(&mut T, Event, &StateChanger) + 'static + Send + Sync,
|
||||||
{
|
{
|
||||||
Handler(Box::new(handler_fn))
|
Handler(Arc::new(Mutex::new(handler_fn)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for Handler<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Handler(Arc::new(Mutex::new(
|
||||||
|
|_: &mut T, _: Event, _: &StateChanger| {},
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> std::fmt::Debug for Handler<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str("Handler()")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+173
-47
@@ -1,14 +1,14 @@
|
|||||||
/// **parse_rsx_param!** - A macro for parsing and setting parameters on elements, allowing for dynamic handling of element properties and children.
|
/// **parse_rsx_param!** - A macro for parsing and setting parameters on elements, allowing for dynamic handling of element properties and children.
|
||||||
///
|
///
|
||||||
/// This macro enables flexible configuration of UI elements. It handles setting fields, adding children, invoking paths, and processing complex structures.
|
/// This macro enables flexible configuration of UI elements. It handles setting fields, adding children, invoking paths, and processing complex structures.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// parse_rsx_param!(elem, title: "Hello", for (i in 0..5) { <child> }, text: "World");
|
/// parse_rsx_param!(elem, title: "Hello", for (i in 0..5) { <child> }, text: "World");
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ## Syntax
|
/// ## Syntax
|
||||||
///
|
///
|
||||||
/// **Set property or field of an element:**
|
/// **Set property or field of an element:**
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// parse_rsx_param!($elem, $($k:ident).+: $v:expr);
|
/// parse_rsx_param!($elem, $($k:ident).+: $v:expr);
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// parse_rsx_param!($elem, {$ielem:expr} $($rest:tt)*);
|
/// parse_rsx_param!($elem, {$ielem:expr} $($rest:tt)*);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// **Set element text:**
|
/// **Set element text:**
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// parse_rsx_param!($elem, $text:expr);
|
/// parse_rsx_param!($elem, $text:expr);
|
||||||
@@ -112,14 +112,14 @@ macro_rules! parse_rsx_param {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// **rsx_elem!** - A macro to instantiate a UI element and apply parameters to it.
|
/// **rsx_elem!** - A macro to instantiate a UI element and apply parameters to it.
|
||||||
///
|
///
|
||||||
/// This macro simplifies creating an element and applying a set of properties or children to it, enabling a declarative style of UI building.
|
/// This macro simplifies creating an element and applying a set of properties or children to it, enabling a declarative style of UI building.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// let element = rsx_elem!(ui::Button { text: "Click me" });
|
/// let element = rsx_elem!(ui::Button { text: "Click me" });
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ## Syntax
|
/// ## Syntax
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// rsx_elem!($elem:path { $($inner:tt)* })
|
/// rsx_elem!($elem:path { $($inner:tt)* })
|
||||||
@@ -143,9 +143,9 @@ macro_rules! rsx_elem {
|
|||||||
/// rsx! {
|
/// rsx! {
|
||||||
/// text { "Hello, World" }
|
/// text { "Hello, World" }
|
||||||
/// div { button {"Click me!"} }
|
/// div { button {"Click me!"} }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ## Syntax
|
/// ## Syntax
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// rsx!($($inner:tt)*)
|
/// rsx!($($inner:tt)*)
|
||||||
@@ -165,48 +165,174 @@ macro_rules! val {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! write {
|
macro_rules! run_handler {
|
||||||
(($self:ident, $state:expr), $expr:expr) => {{
|
($self:ident.$handler:ident ($($inner:tt)*)) => {{
|
||||||
if $state == 0 {
|
let a = std::sync::Arc::clone(&$self.$handler.0);
|
||||||
return format!(
|
let mut o = a.lock().unwrap();
|
||||||
"{}{}{}{}\x1b[0m",
|
o($self, $($inner)*);
|
||||||
$self.style.color.ansi(),
|
|
||||||
$self.style.background.ansi_bg(),
|
|
||||||
$self.style.font.ansi(),
|
|
||||||
$expr
|
|
||||||
);
|
|
||||||
}
|
|
||||||
format!(
|
|
||||||
"{}{}{}{}\x1b[0m",
|
|
||||||
$self.style.hover_color.ansi(),
|
|
||||||
$self.style.hover_background.ansi_bg(),
|
|
||||||
$self.style.hover_font.ansi(),
|
|
||||||
$expr
|
|
||||||
)
|
|
||||||
}};
|
}};
|
||||||
|
($self:ident.$handler:ident) => {{
|
||||||
($self:ident, $kind:ident, $expr:expr) => {{
|
&$self.$handler;
|
||||||
use paste::paste;
|
|
||||||
format!(
|
|
||||||
"{}{}{}{}\x1b[0m",
|
|
||||||
paste! {
|
|
||||||
$self.style.[<$kind _color>].ansi()
|
|
||||||
},
|
|
||||||
paste! {
|
|
||||||
$self.style.[<$kind _background>].ansi_bg()
|
|
||||||
},
|
|
||||||
paste! {
|
|
||||||
$self.style.[<$kind _font>].ansi()
|
|
||||||
},
|
|
||||||
$expr
|
|
||||||
)
|
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! run_handler {
|
macro_rules! __css {
|
||||||
($self:ident.$handler:ident ($renderer:expr, $event:expr)) => {
|
($style:expr,)=>{};
|
||||||
let mut o = $self.$handler.0.lock().unwrap();
|
(
|
||||||
o($self, $renderer, $event);
|
$style:expr, ($name:ident): $value:expr
|
||||||
};
|
) => {{
|
||||||
|
$style.other.insert(stringify!($name).to_string(), Box::new($value));
|
||||||
|
}};
|
||||||
|
(
|
||||||
|
$style:expr, ($name:ident): $value:expr, $($other:tt)*
|
||||||
|
) => {{
|
||||||
|
$style.other.insert(stringify!($name).to_string(), Box::new($value));
|
||||||
|
$crate::__css!($($other)*);
|
||||||
|
}};
|
||||||
|
(
|
||||||
|
$style:expr, $name:ident: $value:expr
|
||||||
|
) => {{
|
||||||
|
$style.$name = $value;
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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)*);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! css {
|
||||||
|
(
|
||||||
|
$($inner:tt)*
|
||||||
|
) => {{
|
||||||
|
let mut hm: $crate::ui::Css = std::collections::HashMap::new();
|
||||||
|
$crate::_css!(hm, $($inner)*);
|
||||||
|
hm
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-2
@@ -5,7 +5,15 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn app() -> Element {
|
fn app() -> Element {
|
||||||
rsx! {
|
rsx! { styling: Some(styles()),
|
||||||
button { "This is a button!" }
|
button { class: "btn", "This is a button!" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn styles() -> Css {
|
||||||
|
css! {
|
||||||
|
.btn::click {
|
||||||
|
color: Blue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-13
@@ -1,18 +1,11 @@
|
|||||||
/// The `ui` module defines the basic building blocks for creating a terminal-based UI framework.
|
/// The `ui` module defines the basic building blocks for creating a terminal-based UI framework.
|
||||||
/// This includes elements like `Text` and `Div`, which can be combined to create complex layouts
|
|
||||||
/// and interactive UIs in the terminal. It also provides utility functions for rendering, event
|
|
||||||
/// handling, and managing child components.
|
|
||||||
///
|
///
|
||||||
/// Key Components:
|
/// # Modules
|
||||||
/// - `Text`: A simple text element that renders a string to the terminal.
|
|
||||||
/// - `Div`: A container element that holds and renders child components, allowing navigation
|
|
||||||
/// through them with keyboard events (Up, Down, Left, Right).
|
|
||||||
/// - `styles`: A module for defining UI styles.
|
/// - `styles`: A module for defining UI styles.
|
||||||
/// - `utils`: Utility functions for rendering and managing state.
|
|
||||||
pub mod styles;
|
pub mod styles;
|
||||||
pub use styles::*;
|
pub use styles::*;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude_extra::*;
|
||||||
use osui_element::{elem_fn, element};
|
use osui_element::{elem_fn, element};
|
||||||
|
|
||||||
/// The `Text` element represents a piece of static text in the terminal UI.
|
/// The `Text` element represents a piece of static text in the terminal UI.
|
||||||
@@ -68,7 +61,9 @@ impl Component for Text<'_> {
|
|||||||
#[element]
|
#[element]
|
||||||
#[elem_fn]
|
#[elem_fn]
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Div {}
|
pub struct Div {
|
||||||
|
pub styling: Option<std::collections::HashMap<StyleName, Style>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Component for Div<'_> {
|
impl Component for Div<'_> {
|
||||||
fn render(&self, state: State) -> String {
|
fn render(&self, state: State) -> String {
|
||||||
@@ -93,6 +88,9 @@ impl Component for Div<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, event: Event, state: &crate::StateChanger) {
|
fn event(&mut self, event: Event, state: &crate::StateChanger) {
|
||||||
|
if let Some(styling) = self.styling.clone() {
|
||||||
|
self.set_styling(&styling);
|
||||||
|
}
|
||||||
match event {
|
match event {
|
||||||
Event::Key(key) => {
|
Event::Key(key) => {
|
||||||
if let Children::Children(children, child) = &mut self.children {
|
if let Children::Children(children, child) = &mut self.children {
|
||||||
@@ -142,13 +140,16 @@ impl Component for Div<'_> {
|
|||||||
#[element]
|
#[element]
|
||||||
#[elem_fn]
|
#[elem_fn]
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Button {}
|
pub struct Button {
|
||||||
|
pub on_click: Handler<Button<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Component for Button<'_> {
|
impl Component for Button<'_> {
|
||||||
fn render(&self, state: State) -> String {
|
fn render(&self, state: State) -> String {
|
||||||
match state {
|
match state {
|
||||||
State::Custom(_) => format!("\x1b[32m{}\x1b[0m", self.children.get_text()),
|
State::Custom(_) => self.style.write("click", &self.children.get_text()),
|
||||||
_ => self.children.get_text(),
|
State::Hover => self.style.write("hover", &self.children.get_text()),
|
||||||
|
_ => self.style.write("", &self.children.get_text()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +158,7 @@ impl Component for Button<'_> {
|
|||||||
Event::Key(key) => {
|
Event::Key(key) => {
|
||||||
if key.code == KeyCode::Enter {
|
if key.code == KeyCode::Enter {
|
||||||
let prev_state = state.get_state();
|
let prev_state = state.get_state();
|
||||||
|
run_handler!(self.on_click(event, state));
|
||||||
state.set_state(State::Custom(0));
|
state.set_state(State::Custom(0));
|
||||||
sleep(100);
|
sleep(100);
|
||||||
state.set_state(prev_state);
|
state.set_state(prev_state);
|
||||||
|
|||||||
+86
-53
@@ -1,18 +1,19 @@
|
|||||||
//! The `styles` module defines various styling options for UI elements. This includes colors,
|
//! The `styles` module defines various styling options for UI elements. This includes colors,
|
||||||
//! fonts, padding, margins, and other visual properties that can be applied to UI components
|
//! fonts, padding, margins, and other visual properties that can be applied to UI components
|
||||||
//! to change their appearance.
|
//! to change their appearance.
|
||||||
//!
|
|
||||||
//! # Example
|
|
||||||
//! ```rust
|
|
||||||
//! use crate::ui::styles::{Style, Color};
|
|
||||||
//!
|
|
||||||
//! let style = Style {
|
|
||||||
//! color: Color::Blue,
|
|
||||||
//! padding: 5,
|
|
||||||
//! };
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
use std::{collections::HashMap, fmt::Debug};
|
||||||
|
|
||||||
|
use dyn_clone::{clone_trait_object, DynClone};
|
||||||
|
|
||||||
|
pub trait StyleCore: Debug + Send + Sync + DynClone {
|
||||||
|
fn ansi(&self) -> String;
|
||||||
|
fn ansi_bg(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
clone_trait_object!(StyleCore);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum Font {
|
pub enum Font {
|
||||||
None,
|
None,
|
||||||
Bold,
|
Bold,
|
||||||
@@ -20,11 +21,11 @@ pub enum Font {
|
|||||||
Italic,
|
Italic,
|
||||||
Reverse,
|
Reverse,
|
||||||
Strike,
|
Strike,
|
||||||
Mul(Vec<Font>),
|
Mul(Vec<Font>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Font {
|
impl StyleCore for Font {
|
||||||
pub fn ansi(&self) -> String {
|
fn ansi(&self) -> String {
|
||||||
String::from(match self {
|
String::from(match self {
|
||||||
Font::None => "",
|
Font::None => "",
|
||||||
Font::Bold => "\x1b[1m",
|
Font::Bold => "\x1b[1m",
|
||||||
@@ -41,24 +42,15 @@ impl Font {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
fn ansi_bg(&self) -> String {
|
||||||
|
self.ansi()
|
||||||
|
|
||||||
pub fn prioritize<'a>(&'a self, secondary: &'a Font) -> &Font {
|
|
||||||
if secondary == &Font::None {
|
|
||||||
self
|
|
||||||
} else {
|
|
||||||
secondary
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
None,
|
NoColor,
|
||||||
|
Rgb(u8, u8, u8),
|
||||||
Rgb(u8, u8, u8),
|
|
||||||
Hex(String),
|
Hex(String),
|
||||||
Black,
|
Black,
|
||||||
Red,
|
Red,
|
||||||
@@ -70,11 +62,10 @@ pub enum Color {
|
|||||||
White,
|
White,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Color {
|
impl StyleCore for Color {
|
||||||
|
fn ansi(&self) -> String {
|
||||||
pub fn ansi(&self) -> String {
|
|
||||||
String::from(match self {
|
String::from(match self {
|
||||||
Color::None => "",
|
Color::NoColor => "",
|
||||||
Color::Rgb(r, g, b) => {
|
Color::Rgb(r, g, b) => {
|
||||||
return format!("\x1b[38;2;{r};{g};{b}m");
|
return format!("\x1b[38;2;{r};{g};{b}m");
|
||||||
}
|
}
|
||||||
@@ -93,10 +84,9 @@ impl Color {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ansi_bg(&self) -> String {
|
||||||
pub fn ansi_bg(&self) -> String {
|
|
||||||
String::from(match self {
|
String::from(match self {
|
||||||
Color::None => "",
|
Color::NoColor => "",
|
||||||
Color::Rgb(r, g, b) => {
|
Color::Rgb(r, g, b) => {
|
||||||
return format!("\x1b[48;2;{r};{g};{b}m");
|
return format!("\x1b[48;2;{r};{g};{b}m");
|
||||||
}
|
}
|
||||||
@@ -114,21 +104,11 @@ impl Color {
|
|||||||
Color::White => "\x1b[47m",
|
Color::White => "\x1b[47m",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn prioritize<'a>(&'a self, secondary: &'a Color) -> &Color {
|
|
||||||
if secondary == &Color::None {
|
|
||||||
self
|
|
||||||
} else {
|
|
||||||
secondary
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Color {
|
impl Default for Color {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Color::None
|
Color::NoColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,24 +121,77 @@ impl Default for Font {
|
|||||||
fn hex_to_rgb(hex: &str) -> (u8, u8, u8) {
|
fn hex_to_rgb(hex: &str) -> (u8, u8, u8) {
|
||||||
let hex = hex.trim_start_matches('#');
|
let hex = hex.trim_start_matches('#');
|
||||||
|
|
||||||
|
|
||||||
let hex = if hex.len() == 3 {
|
let hex = if hex.len() == 3 {
|
||||||
format!(
|
format!(
|
||||||
"{}{}{}{}{}{}",
|
"{}{}{}{}{}{}",
|
||||||
&hex[0..1], &hex[0..1],
|
&hex[0..1],
|
||||||
&hex[1..2], &hex[1..2],
|
&hex[0..1],
|
||||||
&hex[2..3], &hex[2..3]
|
&hex[1..2],
|
||||||
|
&hex[1..2],
|
||||||
|
&hex[2..3],
|
||||||
|
&hex[2..3]
|
||||||
)
|
)
|
||||||
} else if hex.len() == 6 {
|
} else if hex.len() == 6 {
|
||||||
hex.to_string()
|
hex.to_string()
|
||||||
} else {
|
} else {
|
||||||
return (255, 255, 255);
|
return (255, 255, 255);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(255);
|
let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(255);
|
||||||
let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(255);
|
let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(255);
|
||||||
let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(255);
|
let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(255);
|
||||||
|
|
||||||
(r, g, b)
|
(r, g, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
|
||||||
|
pub enum StyleName {
|
||||||
|
Class(String),
|
||||||
|
Id(String),
|
||||||
|
Component(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct StyleElement {
|
||||||
|
pub color: Color,
|
||||||
|
pub other: HashMap<String, Box<dyn StyleCore>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for StyleElement {
|
||||||
|
fn default() -> Self {
|
||||||
|
StyleElement {
|
||||||
|
color: Color::NoColor,
|
||||||
|
other: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Style(pub StyleElement, pub HashMap<String, StyleElement>);
|
||||||
|
|
||||||
|
impl Default for Style {
|
||||||
|
fn default() -> Self {
|
||||||
|
Style(StyleElement::default(), HashMap::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Style {
|
||||||
|
pub fn write(&self, state: &str, s: &str) -> String {
|
||||||
|
if state == "" {
|
||||||
|
return self.0.write(s);
|
||||||
|
}
|
||||||
|
if let Some(style_element) = self.1.get(state) {
|
||||||
|
style_element.write(s)
|
||||||
|
} else {
|
||||||
|
s.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StyleElement {
|
||||||
|
pub fn write(&self, s: &str) -> String {
|
||||||
|
format!("{}{s}\x1b[0m", self.color.ansi())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Css = HashMap<StyleName, Style>;
|
||||||
|
|||||||
Reference in New Issue
Block a user