some refractoring
This commit is contained in:
@@ -4,24 +4,18 @@
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
|
||||
extern boolean_t render_loop(void* args);
|
||||
extern void *event_loop(void* args);
|
||||
extern void *cmd_loop(void* args);
|
||||
|
||||
boolean_t c_run(void* args)
|
||||
{
|
||||
pthread_t event_thread;
|
||||
pthread_t cmd_thread;
|
||||
|
||||
if (pthread_create(&event_thread, NULL, event_loop, args) != 0)
|
||||
{
|
||||
perror("Failed to create thread");
|
||||
}
|
||||
|
||||
if (pthread_create(&cmd_thread, NULL, cmd_loop, args) != 0)
|
||||
{
|
||||
perror("Failed to create thread");
|
||||
}
|
||||
|
||||
return render_loop(args);
|
||||
cmd_loop(args);
|
||||
return FALSE;
|
||||
}
|
||||
+3
-17
@@ -7,7 +7,7 @@ use crate::prelude::*;
|
||||
use crate::utils::{clear, flush, get_term_size, hide_cursor, show_cursor, Frame};
|
||||
use std::{ffi::c_void, ptr::null, sync::mpsc};
|
||||
|
||||
pub struct LArgs<'a> {
|
||||
struct LArgs<'a> {
|
||||
pub element: &'a mut Element,
|
||||
|
||||
// for sending commands
|
||||
@@ -55,18 +55,6 @@ pub fn run(element: &mut Element) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn render_loop(ptr: *mut c_void) -> bool {
|
||||
if ptr.is_null() {
|
||||
return false;
|
||||
}
|
||||
let args = unsafe { &mut *(ptr as *mut LArgs) };
|
||||
while args.running {
|
||||
sleep(1000);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void {
|
||||
if ptr.is_null() {
|
||||
@@ -87,10 +75,7 @@ extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void {
|
||||
);
|
||||
document.render();
|
||||
|
||||
while args.running {
|
||||
if ptr.is_null() {
|
||||
break;
|
||||
}
|
||||
while args.running || !ptr.is_null() {
|
||||
event(args.element, crossterm::event::read().unwrap(), &document);
|
||||
document.render();
|
||||
}
|
||||
@@ -102,6 +87,7 @@ extern "C" fn cmd_loop(ptr: *mut c_void) -> *const c_void {
|
||||
if ptr.is_null() {
|
||||
return null() as *const c_void;
|
||||
}
|
||||
|
||||
let args = unsafe { &mut *(ptr as *mut LArgs) };
|
||||
|
||||
loop {
|
||||
|
||||
+8
-6
@@ -1,21 +1,23 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn event_logger() -> Element {
|
||||
pub fn examples() -> Element {
|
||||
rsx! {
|
||||
styling: Some(styling()),
|
||||
button { class: "txt", "Hello, World!" }
|
||||
if (2 == 2) {
|
||||
rsx_elem! { button { class: "btn", "Hello, World!" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn styling() -> Css {
|
||||
css! {
|
||||
.txt {
|
||||
x: Center,
|
||||
y: Center,
|
||||
.btn {
|
||||
x: 30%,
|
||||
y: Auto,
|
||||
color: Red,
|
||||
}
|
||||
|
||||
.txt: clicked {
|
||||
.btn: clicked {
|
||||
color: Blue,
|
||||
}
|
||||
}
|
||||
|
||||
+149
-125
@@ -38,16 +38,12 @@ pub mod utils;
|
||||
|
||||
pub use app::run;
|
||||
|
||||
/// The `prelude` module provides commonly used imports and macros to simplify the development
|
||||
/// of UI elements. It re-exports commonly used traits, structs, and utility functions to make
|
||||
/// the UI framework easier to use.
|
||||
pub mod prelude {
|
||||
pub use crate::ui::Color::*;
|
||||
pub use crate::ui::Number::*;
|
||||
pub use crate::{self as osui, css, rsx, rsx_elem, ui::*, Handler};
|
||||
pub use crate::{style, Command, Document, Element, Value};
|
||||
// useful for Element making
|
||||
pub use crate::{call, Children, ElementCore, ElementWidget};
|
||||
pub use crate::{style, Command, Document, Element, Value};
|
||||
pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};
|
||||
pub fn sleep(ms: u64) {
|
||||
std::thread::sleep(std::time::Duration::from_millis(ms));
|
||||
@@ -55,7 +51,54 @@ pub mod prelude {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Value Enum
|
||||
// Type aliases
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub type Element = Box<dyn ElementWidget>;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Traits
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub trait ElementCore: Send + Sync {
|
||||
fn get_data(&self) -> (Value<usize>, usize, String);
|
||||
fn update_data(&mut self, width: usize, height: usize);
|
||||
fn get_element_by_id(&mut self, id: &str) -> 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 ElementWidget: ElementCore + std::fmt::Debug {
|
||||
fn render(&self, focused: bool) -> RenderResult;
|
||||
|
||||
fn event(&mut self, event: Event, document: &Document) {
|
||||
_ = (event, document)
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Structs
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct Handler<T>(pub Arc<Mutex<dyn FnMut(&mut T, Event, &Document) + Send + Sync>>);
|
||||
|
||||
pub struct Document {
|
||||
cmd_sender: std::sync::mpsc::Sender<Command>,
|
||||
cmd_recv: *const std::ffi::c_void,
|
||||
}
|
||||
|
||||
/// ```
|
||||
/// RenderResult(output, (x, y))
|
||||
/// ```
|
||||
pub struct RenderResult(String, ui::StyleElement);
|
||||
pub struct RenderWriter {
|
||||
w: String,
|
||||
style: ui::Style,
|
||||
focused: bool,
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Enums
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@@ -64,6 +107,48 @@ pub enum Value<T: Copy + PartialEq> {
|
||||
Custom(T),
|
||||
}
|
||||
|
||||
/// Child enum of an `Element`
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// // Check if it's text
|
||||
/// if let Children::Text(text) {
|
||||
/// // do something
|
||||
/// }
|
||||
/// // Check if it's inner elements
|
||||
/// if let Children::Children(text) {
|
||||
/// // do something
|
||||
/// }
|
||||
/// // recommended: use get_text
|
||||
/// children.get_text() // returns a empty string if it's not text
|
||||
/// ```
|
||||
///
|
||||
/// # Useful for
|
||||
/// - `Element::event`
|
||||
/// - `Element::render`
|
||||
/// - `Handler`
|
||||
#[derive(Debug)]
|
||||
pub enum Children {
|
||||
None,
|
||||
Text(String),
|
||||
Children(Vec<Element>, usize),
|
||||
}
|
||||
|
||||
pub enum Command {
|
||||
Exit,
|
||||
Render,
|
||||
GetElementById(String),
|
||||
}
|
||||
|
||||
pub enum CommandResult {
|
||||
Element(*mut Element),
|
||||
None,
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Implementations
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl<T: Copy + PartialEq> Value<T> {
|
||||
pub fn get_value(&self) -> T {
|
||||
match self {
|
||||
@@ -85,89 +170,33 @@ impl<T: Copy + PartialEq + Default> Default for Value<T> {
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// ElementCore Traits
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// ```
|
||||
/// RenderResult(output, (x, y), (width, height))
|
||||
/// ```
|
||||
pub struct RenderResult(String, (ui::Number, ui::Number));
|
||||
|
||||
pub trait ElementCore: Send + Sync {
|
||||
fn get_data(&self) -> (Value<usize>, usize, String);
|
||||
fn update_data(&mut self, width: usize, height: usize);
|
||||
fn get_element_by_id(&mut self, id: &str) -> Option<&mut Element>;
|
||||
fn get_child(&mut self) -> Option<&mut Element>;
|
||||
fn set_styling(&mut self, styling: &HashMap<crate::ui::StyleName, crate::ui::Style>);
|
||||
impl Document {
|
||||
pub fn exit(&self) {
|
||||
self.cmd_sender.send(Command::Exit).unwrap();
|
||||
}
|
||||
|
||||
pub trait ElementWidget: ElementCore + std::fmt::Debug {
|
||||
fn render(&self, focused: bool) -> RenderResult;
|
||||
|
||||
fn event(&mut self, event: Event, document: &Document) {
|
||||
_ = (event, document)
|
||||
pub fn get_element_by_id<T>(&self, id: &str) -> Option<&mut Box<T>> {
|
||||
if let Some(e) = self.get_element_by_id_raw(id) {
|
||||
Some(convert(unsafe { &mut *e }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub type Element = Box<dyn ElementWidget>;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Handler Struct
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct Handler<T>(pub Arc<Mutex<dyn FnMut(&mut T, Event, &Document) + Send + Sync>>);
|
||||
|
||||
impl<T> Handler<T> {
|
||||
pub fn new<F>(handler_fn: F) -> Handler<T>
|
||||
where
|
||||
F: FnMut(&mut T, Event, &Document) + 'static + Send + Sync,
|
||||
{
|
||||
Handler(Arc::new(Mutex::new(handler_fn)))
|
||||
pub fn get_element_by_id_raw(&self, id: &str) -> Option<*mut Element> {
|
||||
self.cmd_sender
|
||||
.send(Command::GetElementById(id.to_string()))
|
||||
.unwrap();
|
||||
let rx = unsafe { &*(self.cmd_recv as *const std::sync::mpsc::Receiver<CommandResult>) };
|
||||
if let Ok(CommandResult::Element(e)) = rx.recv() {
|
||||
Some(e)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Handler<T> {
|
||||
fn default() -> Self {
|
||||
Handler(Arc::new(Mutex::new(|_: &mut T, _: Event, _: &Document| {})))
|
||||
pub fn render(&self) {
|
||||
self.cmd_sender.send(Command::Render).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::fmt::Debug for Handler<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("Handler()")
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Children Enum
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Child enum of an `Element`
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// // Check if it's text
|
||||
/// if let Children::Text(text) {
|
||||
/// // do something
|
||||
/// }
|
||||
/// // Check if it's inner elements
|
||||
/// if let Children::Children(text) {
|
||||
/// // do something
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Useful for
|
||||
/// - `Element::event`
|
||||
/// - `Element::render`
|
||||
/// - `Handler`
|
||||
#[derive(Debug)]
|
||||
pub enum Children {
|
||||
None,
|
||||
Text(String),
|
||||
Children(Vec<Element>, usize),
|
||||
}
|
||||
|
||||
impl Default for Children {
|
||||
fn default() -> Self {
|
||||
Self::None
|
||||
@@ -218,50 +247,45 @@ impl Children {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a `Element` into the struct
|
||||
impl<T> Handler<T> {
|
||||
pub fn new<F>(handler_fn: F) -> Handler<T>
|
||||
where
|
||||
F: FnMut(&mut T, Event, &Document) + 'static + Send + Sync,
|
||||
{
|
||||
Handler(Arc::new(Mutex::new(handler_fn)))
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderWriter {
|
||||
pub fn new(focused: bool, style: ui::Style) -> RenderWriter {
|
||||
RenderWriter {
|
||||
w: String::new(),
|
||||
style,
|
||||
focused,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&mut self, s: &str) {
|
||||
self.w += self.style.get(self.focused).write(s).as_str();
|
||||
}
|
||||
|
||||
pub fn result(&self) -> RenderResult {
|
||||
RenderResult(self.w.clone(), self.style.get(self.focused).clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Handler<T> {
|
||||
fn default() -> Self {
|
||||
Handler(Arc::new(Mutex::new(|_: &mut T, _: Event, _: &Document| {})))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::fmt::Debug for Handler<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("Handler()")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert<T>(widget: &mut Box<dyn ElementWidget>) -> &mut Box<T> {
|
||||
unsafe { &mut *(widget as *mut _ as *mut Box<T>) }
|
||||
}
|
||||
|
||||
pub enum Command {
|
||||
Exit,
|
||||
Render,
|
||||
GetElementById(String),
|
||||
}
|
||||
|
||||
pub enum CommandResult {
|
||||
Element(*mut Element),
|
||||
None,
|
||||
}
|
||||
|
||||
pub struct Document {
|
||||
cmd_sender: std::sync::mpsc::Sender<Command>,
|
||||
cmd_recv: *const std::ffi::c_void,
|
||||
}
|
||||
|
||||
impl Document {
|
||||
pub fn exit(&self) {
|
||||
self.cmd_sender.send(Command::Exit).unwrap();
|
||||
}
|
||||
pub fn get_element_by_id<T>(&self, id: &str) -> Option<&mut Box<T>> {
|
||||
if let Some(e) = self.get_element_by_id_raw(id) {
|
||||
Some(convert(unsafe { &mut *e }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn get_element_by_id_raw(&self, id: &str) -> Option<*mut Element> {
|
||||
self.cmd_sender
|
||||
.send(Command::GetElementById(id.to_string()))
|
||||
.unwrap();
|
||||
let rx = unsafe { &*(self.cmd_recv as *const std::sync::mpsc::Receiver<CommandResult>) };
|
||||
if let Ok(CommandResult::Element(e)) = rx.recv() {
|
||||
Some(e)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn render(&self) {
|
||||
self.cmd_sender.send(Command::Render).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
use osui::prelude::*;
|
||||
use osui::examples;
|
||||
use osui::prelude::*;
|
||||
|
||||
fn main() {
|
||||
while osui::run(&mut examples::event_logger()) {}
|
||||
while osui::run(&mut examples::examples()) {}
|
||||
}
|
||||
|
||||
+17
-10
@@ -1,5 +1,22 @@
|
||||
#[macro_export]
|
||||
macro_rules! parse_rsx_param {
|
||||
($elem:expr, for ($($for:tt)*) $code:block $($rest:tt)*) => {
|
||||
if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)}
|
||||
if let $crate::Children::Children(children, _) = &mut $elem.children {
|
||||
for $($for)* {
|
||||
children.push($code)
|
||||
}
|
||||
}
|
||||
osui::parse_rsx_param!($elem, $($rest)*);
|
||||
};
|
||||
($elem:expr, if ($($if:tt)*) $code:block $($rest:tt)*) => {
|
||||
if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)}
|
||||
if let $crate::Children::Children(children, _) = &mut $elem.children {
|
||||
if $($if)* {children.push($code)}
|
||||
}
|
||||
osui::parse_rsx_param!($elem, $($rest)*);
|
||||
};
|
||||
|
||||
($elem:expr, $($k:ident).+: $v:expr) => {
|
||||
$elem.$($k).+ = $v;
|
||||
};
|
||||
@@ -29,16 +46,6 @@ macro_rules! parse_rsx_param {
|
||||
osui::parse_rsx_param!($elem, $($rest)*);
|
||||
};
|
||||
|
||||
($elem:expr, for ($($for:tt)*) { $($inner:tt)* } $($rest:tt)*) => {
|
||||
if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)}
|
||||
if let $crate::Children::Children(children, _) = &mut $elem.children {
|
||||
for $($for)* {
|
||||
children.push({$($inner)*})
|
||||
}
|
||||
}
|
||||
osui::parse_rsx_param!($elem, $($rest)*);
|
||||
};
|
||||
|
||||
($elem:expr, {$inner_elem:expr} $($rest:tt)*) => {
|
||||
if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)}
|
||||
if let $crate::Children::Children(children, _) = &mut $elem.children {
|
||||
|
||||
+16
-25
@@ -5,7 +5,7 @@
|
||||
pub mod styles;
|
||||
pub use styles::*;
|
||||
|
||||
use crate::{prelude::*, RenderResult};
|
||||
use crate::{prelude::*, RenderResult, RenderWriter};
|
||||
use osui_element::{elem_fn, element};
|
||||
|
||||
/// The `Text` element represents a piece of static text in the terminal UI.
|
||||
@@ -31,17 +31,18 @@ pub struct Text {
|
||||
}
|
||||
|
||||
impl ElementWidget for Text<'_> {
|
||||
fn render(&self, _: bool) -> RenderResult {
|
||||
self.style.result(
|
||||
"",
|
||||
self.style.write("", &{
|
||||
fn render(&self, focused: bool) -> RenderResult {
|
||||
let mut writer = RenderWriter::new(focused, self.style.clone());
|
||||
|
||||
writer.write(&{
|
||||
if let Children::Text(text) = &self.children {
|
||||
text.clone()
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}),
|
||||
)
|
||||
});
|
||||
|
||||
writer.result()
|
||||
}
|
||||
fn event(&mut self, event: Event, document: &Document) {
|
||||
call!(self.on_event(event, document));
|
||||
@@ -77,6 +78,7 @@ pub struct Div {
|
||||
|
||||
impl ElementWidget for Div<'_> {
|
||||
fn render(&self, focused: bool) -> RenderResult {
|
||||
let mut writer = RenderWriter::new(focused, self.style.clone());
|
||||
let mut frame = crate::utils::Frame::new(crate::utils::get_term_size().0, 12);
|
||||
|
||||
if let Children::Children(children, child) = &self.children {
|
||||
@@ -84,8 +86,9 @@ impl ElementWidget for Div<'_> {
|
||||
frame.render(focused && i == *child, elem);
|
||||
}
|
||||
}
|
||||
writer.write(&frame.output());
|
||||
|
||||
self.style.result("", frame.output())
|
||||
writer.result()
|
||||
}
|
||||
|
||||
fn event(&mut self, event: Event, document: &Document) {
|
||||
@@ -145,25 +148,13 @@ impl ElementWidget for Div<'_> {
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Button {
|
||||
pub on_click: Handler<Button<'a>>,
|
||||
pub state: &'a str,
|
||||
}
|
||||
|
||||
impl ElementWidget for Button<'_> {
|
||||
fn render(&self, focused: bool) -> RenderResult {
|
||||
if self.state != "" {
|
||||
self.style.result(
|
||||
self.state,
|
||||
self.style.write(self.state, &self.children.get_text()),
|
||||
)
|
||||
} else if focused {
|
||||
self.style.result(
|
||||
"hover",
|
||||
self.style.write("hover", &self.children.get_text()),
|
||||
)
|
||||
} else {
|
||||
self.style
|
||||
.result("", self.style.write("", &self.children.get_text()))
|
||||
}
|
||||
let mut writer = RenderWriter::new(focused, self.style.clone());
|
||||
writer.write(&self.children.get_text());
|
||||
writer.result()
|
||||
}
|
||||
|
||||
fn event(&mut self, event: Event, document: &Document) {
|
||||
@@ -171,11 +162,11 @@ impl ElementWidget for Button<'_> {
|
||||
Event::Key(key) => {
|
||||
if key.code == KeyCode::Enter {
|
||||
document.render();
|
||||
self.state = "clicked";
|
||||
self.style.set_state("clicked");
|
||||
call!(self.on_click(event, document));
|
||||
document.render();
|
||||
sleep(100);
|
||||
self.state = "";
|
||||
self.style.set_state("");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
||||
+155
-132
@@ -6,16 +6,27 @@ use std::{collections::HashMap, fmt::Debug};
|
||||
|
||||
use dyn_clone::{clone_trait_object, DynClone};
|
||||
|
||||
use crate::RenderResult;
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Type aliases
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub type Css = HashMap<StyleName, Style>;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Traits
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub trait StyleCore: Debug + Send + Sync + DynClone {
|
||||
fn ansi(&self) -> String;
|
||||
fn ansi_bg(&self) -> String;
|
||||
fn is_null(&self) -> bool;
|
||||
}
|
||||
|
||||
clone_trait_object!(StyleCore);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Enums
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Font {
|
||||
None,
|
||||
@@ -27,32 +38,6 @@ pub enum Font {
|
||||
Mul(Vec<Font>),
|
||||
}
|
||||
|
||||
impl StyleCore for Font {
|
||||
fn ansi(&self) -> String {
|
||||
String::from(match self {
|
||||
Font::None => "",
|
||||
Font::Bold => "\x1b[1m",
|
||||
Font::Underline => "\x1b[4m",
|
||||
Font::Italic => "\x1b[3m",
|
||||
Font::Reverse => "\x1b[7m",
|
||||
Font::Strike => "\x1b[9m",
|
||||
Font::Mul(v) => {
|
||||
let mut s = String::new();
|
||||
for n in v {
|
||||
s += n.ansi().as_str();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
})
|
||||
}
|
||||
fn ansi_bg(&self) -> String {
|
||||
self.ansi()
|
||||
}
|
||||
fn is_null(&self) -> bool {
|
||||
*self == Self::None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Color {
|
||||
NoColor,
|
||||
@@ -68,6 +53,128 @@ pub enum Color {
|
||||
White,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Number {
|
||||
Px(usize),
|
||||
Pe(usize),
|
||||
Center,
|
||||
Auto,
|
||||
Default,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
|
||||
pub enum StyleName {
|
||||
Class(String),
|
||||
Id(String),
|
||||
Component(String),
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Structs
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StyleElement {
|
||||
pub color: Color,
|
||||
pub background: Color,
|
||||
pub font: Font,
|
||||
pub x: Number,
|
||||
pub y: Number,
|
||||
pub width: Number,
|
||||
pub height: Number,
|
||||
pub other: HashMap<String, Box<dyn StyleCore>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Style(
|
||||
pub StyleElement,
|
||||
pub HashMap<String, StyleElement>,
|
||||
pub String,
|
||||
);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Implementations
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl Default for Style {
|
||||
fn default() -> Self {
|
||||
Style(StyleElement::default(), HashMap::new(), String::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl Style {
|
||||
pub fn get(&self, hover: bool) -> StyleElement {
|
||||
if let Some(style_element) = self.1.get(&self.2) {
|
||||
self.0.clone().prioritize(style_element)
|
||||
} else if hover {
|
||||
if let Some(style_element) = self.1.get("hover") {
|
||||
self.0.clone().prioritize(style_element)
|
||||
} else {
|
||||
self.0.clone()
|
||||
}
|
||||
} else {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_state(&mut self, state: &str) {
|
||||
self.2 = state.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
impl StyleElement {
|
||||
pub fn write(&self, s: &str) -> String {
|
||||
format!(
|
||||
"{}{}{}{s}\x1b[0m",
|
||||
self.color.ansi(),
|
||||
self.background.ansi_bg(),
|
||||
self.font.ansi()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for StyleElement {
|
||||
fn default() -> Self {
|
||||
StyleElement {
|
||||
color: Color::NoColor,
|
||||
background: Color::NoColor,
|
||||
font: Font::None,
|
||||
x: Number::Default,
|
||||
y: Number::Default,
|
||||
width: Number::Default,
|
||||
height: Number::Default,
|
||||
other: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StyleElement {
|
||||
pub fn prioritize(mut self, other: &Self) -> StyleElement {
|
||||
if !other.color.is_null() {
|
||||
self.color = other.color.clone();
|
||||
}
|
||||
if !other.background.is_null() {
|
||||
self.background = other.background.clone();
|
||||
}
|
||||
if !other.font.is_null() {
|
||||
self.font = other.font.clone();
|
||||
}
|
||||
if other.x != Number::Default {
|
||||
self.x = other.x.clone();
|
||||
}
|
||||
if other.y != Number::Default {
|
||||
self.y = other.y.clone();
|
||||
}
|
||||
if other.width != Number::Default {
|
||||
self.width = other.width.clone();
|
||||
}
|
||||
if other.height != Number::Default {
|
||||
self.height = other.height.clone();
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl StyleCore for Color {
|
||||
fn ansi(&self) -> String {
|
||||
String::from(match self {
|
||||
@@ -153,112 +260,28 @@ fn hex_to_rgb(hex: &str) -> (u8, u8, u8) {
|
||||
(r, g, b)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Number {
|
||||
Px(usize),
|
||||
Pe(usize),
|
||||
Center,
|
||||
Auto,
|
||||
Default,
|
||||
impl StyleCore for Font {
|
||||
fn ansi(&self) -> String {
|
||||
String::from(match self {
|
||||
Font::None => "",
|
||||
Font::Bold => "\x1b[1m",
|
||||
Font::Underline => "\x1b[4m",
|
||||
Font::Italic => "\x1b[3m",
|
||||
Font::Reverse => "\x1b[7m",
|
||||
Font::Strike => "\x1b[9m",
|
||||
Font::Mul(v) => {
|
||||
let mut s = String::new();
|
||||
for n in v {
|
||||
s += n.ansi().as_str();
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
|
||||
pub enum StyleName {
|
||||
Class(String),
|
||||
Id(String),
|
||||
Component(String),
|
||||
return s;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StyleElement {
|
||||
pub color: Color,
|
||||
pub background: Color,
|
||||
pub font: Font,
|
||||
pub x: Number,
|
||||
pub y: Number,
|
||||
pub width: Number,
|
||||
pub height: Number,
|
||||
pub other: HashMap<String, Box<dyn StyleCore>>,
|
||||
})
|
||||
}
|
||||
|
||||
impl Default for StyleElement {
|
||||
fn default() -> Self {
|
||||
StyleElement {
|
||||
color: Color::NoColor,
|
||||
background: Color::NoColor,
|
||||
font: Font::None,
|
||||
x: Number::Default,
|
||||
y: Number::Default,
|
||||
width: Number::Default,
|
||||
height: Number::Default,
|
||||
other: HashMap::new(),
|
||||
fn ansi_bg(&self) -> String {
|
||||
self.ansi()
|
||||
}
|
||||
fn is_null(&self) -> bool {
|
||||
*self == Self::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StyleElement {
|
||||
pub fn prioritize(mut self, other: &Self) -> StyleElement {
|
||||
if !other.color.is_null() {
|
||||
self.color = other.color.clone();
|
||||
}
|
||||
if !other.background.is_null() {
|
||||
self.background = other.background.clone();
|
||||
}
|
||||
if !other.font.is_null() {
|
||||
self.font = other.font.clone();
|
||||
}
|
||||
if other.x != Number::Default {
|
||||
self.x = other.x.clone();
|
||||
}
|
||||
if other.y != Number::Default {
|
||||
self.y = other.y.clone();
|
||||
}
|
||||
if other.width != Number::Default {
|
||||
self.width = other.width.clone();
|
||||
}
|
||||
if other.height != Number::Default {
|
||||
self.height = other.height.clone();
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[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 let Some(style_element) = self.1.get(state) {
|
||||
style_element.write(s)
|
||||
} else {
|
||||
return self.0.write(s);
|
||||
}
|
||||
}
|
||||
pub fn result(&self, state: &str, s: String) -> RenderResult {
|
||||
if let Some(style_element) = self.1.get(state) {
|
||||
let style_element = self.0.clone().prioritize(&style_element.clone());
|
||||
RenderResult(s, (style_element.x.clone(), style_element.y.clone()))
|
||||
} else {
|
||||
RenderResult(s, (self.0.x.clone(), self.0.y.clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StyleElement {
|
||||
pub fn write(&self, s: &str) -> String {
|
||||
format!(
|
||||
"{}{}{}{s}\x1b[0m",
|
||||
self.color.ansi(),
|
||||
self.background.ansi_bg(),
|
||||
self.font.ansi()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Css = HashMap<StyleName, Style>;
|
||||
|
||||
+2
-2
@@ -103,7 +103,7 @@ impl Frame {
|
||||
let output = element.render(focused);
|
||||
let x;
|
||||
let mut y = 0;
|
||||
match output.1 .1 {
|
||||
match output.1.y {
|
||||
crate::ui::Number::Px(_y) => {
|
||||
y = _y;
|
||||
}
|
||||
@@ -128,7 +128,7 @@ impl Frame {
|
||||
}
|
||||
}
|
||||
}
|
||||
match output.1 .0 {
|
||||
match output.1.x {
|
||||
crate::ui::Number::Px(_x) => {
|
||||
x = _x;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user