some refractoring
This commit is contained in:
@@ -4,24 +4,18 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
extern boolean_t render_loop(void* args);
|
|
||||||
extern void *event_loop(void* args);
|
extern void *event_loop(void* args);
|
||||||
extern void *cmd_loop(void* args);
|
extern void *cmd_loop(void* args);
|
||||||
|
|
||||||
boolean_t c_run(void* args)
|
boolean_t c_run(void* args)
|
||||||
{
|
{
|
||||||
pthread_t event_thread;
|
pthread_t event_thread;
|
||||||
pthread_t cmd_thread;
|
|
||||||
|
|
||||||
if (pthread_create(&event_thread, NULL, event_loop, args) != 0)
|
if (pthread_create(&event_thread, NULL, event_loop, args) != 0)
|
||||||
{
|
{
|
||||||
perror("Failed to create thread");
|
perror("Failed to create thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pthread_create(&cmd_thread, NULL, cmd_loop, args) != 0)
|
cmd_loop(args);
|
||||||
{
|
return FALSE;
|
||||||
perror("Failed to create thread");
|
|
||||||
}
|
|
||||||
|
|
||||||
return render_loop(args);
|
|
||||||
}
|
}
|
||||||
+3
-17
@@ -7,7 +7,7 @@ use crate::prelude::*;
|
|||||||
use crate::utils::{clear, flush, get_term_size, hide_cursor, show_cursor, Frame};
|
use crate::utils::{clear, flush, get_term_size, hide_cursor, show_cursor, Frame};
|
||||||
use std::{ffi::c_void, ptr::null, sync::mpsc};
|
use std::{ffi::c_void, ptr::null, sync::mpsc};
|
||||||
|
|
||||||
pub struct LArgs<'a> {
|
struct LArgs<'a> {
|
||||||
pub element: &'a mut Element,
|
pub element: &'a mut Element,
|
||||||
|
|
||||||
// for sending commands
|
// 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]
|
#[no_mangle]
|
||||||
extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void {
|
extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void {
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
@@ -87,10 +75,7 @@ extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void {
|
|||||||
);
|
);
|
||||||
document.render();
|
document.render();
|
||||||
|
|
||||||
while args.running {
|
while args.running || !ptr.is_null() {
|
||||||
if ptr.is_null() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
event(args.element, crossterm::event::read().unwrap(), &document);
|
event(args.element, crossterm::event::read().unwrap(), &document);
|
||||||
document.render();
|
document.render();
|
||||||
}
|
}
|
||||||
@@ -102,6 +87,7 @@ extern "C" fn cmd_loop(ptr: *mut c_void) -> *const c_void {
|
|||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return null() as *const c_void;
|
return null() as *const c_void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let args = unsafe { &mut *(ptr as *mut LArgs) };
|
let args = unsafe { &mut *(ptr as *mut LArgs) };
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|||||||
+8
-6
@@ -1,21 +1,23 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub fn event_logger() -> Element {
|
pub fn examples() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
styling: Some(styling()),
|
styling: Some(styling()),
|
||||||
button { class: "txt", "Hello, World!" }
|
if (2 == 2) {
|
||||||
|
rsx_elem! { button { class: "btn", "Hello, World!" } }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn styling() -> Css {
|
pub fn styling() -> Css {
|
||||||
css! {
|
css! {
|
||||||
.txt {
|
.btn {
|
||||||
x: Center,
|
x: 30%,
|
||||||
y: Center,
|
y: Auto,
|
||||||
color: Red,
|
color: Red,
|
||||||
}
|
}
|
||||||
|
|
||||||
.txt: clicked {
|
.btn: clicked {
|
||||||
color: Blue,
|
color: Blue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+149
-125
@@ -38,16 +38,12 @@ pub mod utils;
|
|||||||
|
|
||||||
pub use app::run;
|
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 mod prelude {
|
||||||
pub use crate::ui::Color::*;
|
pub use crate::ui::Color::*;
|
||||||
pub use crate::ui::Number::*;
|
pub use crate::ui::Number::*;
|
||||||
pub use crate::{self as osui, css, rsx, rsx_elem, ui::*, Handler};
|
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::{call, Children, ElementCore, ElementWidget};
|
||||||
|
pub use crate::{style, Command, Document, Element, Value};
|
||||||
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));
|
||||||
@@ -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)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@@ -64,6 +107,48 @@ pub enum Value<T: Copy + PartialEq> {
|
|||||||
Custom(T),
|
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> {
|
impl<T: Copy + PartialEq> Value<T> {
|
||||||
pub fn get_value(&self) -> T {
|
pub fn get_value(&self) -> T {
|
||||||
match self {
|
match self {
|
||||||
@@ -85,89 +170,33 @@ impl<T: Copy + PartialEq + Default> Default for Value<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
impl Document {
|
||||||
/// ElementCore Traits
|
pub fn exit(&self) {
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
self.cmd_sender.send(Command::Exit).unwrap();
|
||||||
|
|
||||||
/// ```
|
|
||||||
/// 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>);
|
|
||||||
}
|
}
|
||||||
|
pub fn get_element_by_id<T>(&self, id: &str) -> Option<&mut Box<T>> {
|
||||||
pub trait ElementWidget: ElementCore + std::fmt::Debug {
|
if let Some(e) = self.get_element_by_id_raw(id) {
|
||||||
fn render(&self, focused: bool) -> RenderResult;
|
Some(convert(unsafe { &mut *e }))
|
||||||
|
} else {
|
||||||
fn event(&mut self, event: Event, document: &Document) {
|
None
|
||||||
_ = (event, document)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn get_element_by_id_raw(&self, id: &str) -> Option<*mut Element> {
|
||||||
pub type Element = Box<dyn ElementWidget>;
|
self.cmd_sender
|
||||||
|
.send(Command::GetElementById(id.to_string()))
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
.unwrap();
|
||||||
/// Handler Struct
|
let rx = unsafe { &*(self.cmd_recv as *const std::sync::mpsc::Receiver<CommandResult>) };
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
if let Ok(CommandResult::Element(e)) = rx.recv() {
|
||||||
|
Some(e)
|
||||||
pub struct Handler<T>(pub Arc<Mutex<dyn FnMut(&mut T, Event, &Document) + Send + Sync>>);
|
} else {
|
||||||
|
None
|
||||||
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 render(&self) {
|
||||||
impl<T> Default for Handler<T> {
|
self.cmd_sender.send(Command::Render).unwrap();
|
||||||
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()")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// 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 {
|
impl Default for Children {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::None
|
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> {
|
pub fn convert<T>(widget: &mut Box<dyn ElementWidget>) -> &mut Box<T> {
|
||||||
unsafe { &mut *(widget as *mut _ as *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::examples;
|
||||||
|
use osui::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
while osui::run(&mut examples::event_logger()) {}
|
while osui::run(&mut examples::examples()) {}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-10
@@ -1,5 +1,22 @@
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! parse_rsx_param {
|
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:expr, $($k:ident).+: $v:expr) => {
|
||||||
$elem.$($k).+ = $v;
|
$elem.$($k).+ = $v;
|
||||||
};
|
};
|
||||||
@@ -29,16 +46,6 @@ macro_rules! parse_rsx_param {
|
|||||||
osui::parse_rsx_param!($elem, $($rest)*);
|
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)*) => {
|
($elem:expr, {$inner_elem:expr} $($rest:tt)*) => {
|
||||||
if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)}
|
if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)}
|
||||||
if let $crate::Children::Children(children, _) = &mut $elem.children {
|
if let $crate::Children::Children(children, _) = &mut $elem.children {
|
||||||
|
|||||||
+16
-25
@@ -5,7 +5,7 @@
|
|||||||
pub mod styles;
|
pub mod styles;
|
||||||
pub use styles::*;
|
pub use styles::*;
|
||||||
|
|
||||||
use crate::{prelude::*, RenderResult};
|
use crate::{prelude::*, RenderResult, RenderWriter};
|
||||||
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.
|
||||||
@@ -31,17 +31,18 @@ pub struct Text {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ElementWidget for Text<'_> {
|
impl ElementWidget for Text<'_> {
|
||||||
fn render(&self, _: bool) -> RenderResult {
|
fn render(&self, focused: bool) -> RenderResult {
|
||||||
self.style.result(
|
let mut writer = RenderWriter::new(focused, self.style.clone());
|
||||||
"",
|
|
||||||
self.style.write("", &{
|
writer.write(&{
|
||||||
if let Children::Text(text) = &self.children {
|
if let Children::Text(text) = &self.children {
|
||||||
text.clone()
|
text.clone()
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
}
|
}
|
||||||
}),
|
});
|
||||||
)
|
|
||||||
|
writer.result()
|
||||||
}
|
}
|
||||||
fn event(&mut self, event: Event, document: &Document) {
|
fn event(&mut self, event: Event, document: &Document) {
|
||||||
call!(self.on_event(event, document));
|
call!(self.on_event(event, document));
|
||||||
@@ -77,6 +78,7 @@ pub struct Div {
|
|||||||
|
|
||||||
impl ElementWidget for Div<'_> {
|
impl ElementWidget for Div<'_> {
|
||||||
fn render(&self, focused: bool) -> RenderResult {
|
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);
|
let mut frame = crate::utils::Frame::new(crate::utils::get_term_size().0, 12);
|
||||||
|
|
||||||
if let Children::Children(children, child) = &self.children {
|
if let Children::Children(children, child) = &self.children {
|
||||||
@@ -84,8 +86,9 @@ impl ElementWidget for Div<'_> {
|
|||||||
frame.render(focused && i == *child, elem);
|
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) {
|
fn event(&mut self, event: Event, document: &Document) {
|
||||||
@@ -145,25 +148,13 @@ impl ElementWidget for Div<'_> {
|
|||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Button {
|
pub struct Button {
|
||||||
pub on_click: Handler<Button<'a>>,
|
pub on_click: Handler<Button<'a>>,
|
||||||
pub state: &'a str,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementWidget for Button<'_> {
|
impl ElementWidget for Button<'_> {
|
||||||
fn render(&self, focused: bool) -> RenderResult {
|
fn render(&self, focused: bool) -> RenderResult {
|
||||||
if self.state != "" {
|
let mut writer = RenderWriter::new(focused, self.style.clone());
|
||||||
self.style.result(
|
writer.write(&self.children.get_text());
|
||||||
self.state,
|
writer.result()
|
||||||
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()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, event: Event, document: &Document) {
|
fn event(&mut self, event: Event, document: &Document) {
|
||||||
@@ -171,11 +162,11 @@ impl ElementWidget for Button<'_> {
|
|||||||
Event::Key(key) => {
|
Event::Key(key) => {
|
||||||
if key.code == KeyCode::Enter {
|
if key.code == KeyCode::Enter {
|
||||||
document.render();
|
document.render();
|
||||||
self.state = "clicked";
|
self.style.set_state("clicked");
|
||||||
call!(self.on_click(event, document));
|
call!(self.on_click(event, document));
|
||||||
document.render();
|
document.render();
|
||||||
sleep(100);
|
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 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 {
|
pub trait StyleCore: Debug + Send + Sync + DynClone {
|
||||||
fn ansi(&self) -> String;
|
fn ansi(&self) -> String;
|
||||||
fn ansi_bg(&self) -> String;
|
fn ansi_bg(&self) -> String;
|
||||||
fn is_null(&self) -> bool;
|
fn is_null(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
clone_trait_object!(StyleCore);
|
clone_trait_object!(StyleCore);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Enums
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum Font {
|
pub enum Font {
|
||||||
None,
|
None,
|
||||||
@@ -27,32 +38,6 @@ pub enum Font {
|
|||||||
Mul(Vec<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)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
NoColor,
|
NoColor,
|
||||||
@@ -68,6 +53,128 @@ pub enum Color {
|
|||||||
White,
|
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 {
|
impl StyleCore for Color {
|
||||||
fn ansi(&self) -> String {
|
fn ansi(&self) -> String {
|
||||||
String::from(match self {
|
String::from(match self {
|
||||||
@@ -153,112 +260,28 @@ fn hex_to_rgb(hex: &str) -> (u8, u8, u8) {
|
|||||||
(r, g, b)
|
(r, g, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
impl StyleCore for Font {
|
||||||
pub enum Number {
|
fn ansi(&self) -> String {
|
||||||
Px(usize),
|
String::from(match self {
|
||||||
Pe(usize),
|
Font::None => "",
|
||||||
Center,
|
Font::Bold => "\x1b[1m",
|
||||||
Auto,
|
Font::Underline => "\x1b[4m",
|
||||||
Default,
|
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;
|
||||||
#[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 background: Color,
|
|
||||||
pub font: Font,
|
|
||||||
pub x: Number,
|
|
||||||
pub y: Number,
|
|
||||||
pub width: Number,
|
|
||||||
pub height: Number,
|
|
||||||
pub other: HashMap<String, Box<dyn StyleCore>>,
|
|
||||||
}
|
}
|
||||||
|
fn ansi_bg(&self) -> String {
|
||||||
impl Default for StyleElement {
|
self.ansi()
|
||||||
fn default() -> Self {
|
}
|
||||||
StyleElement {
|
fn is_null(&self) -> bool {
|
||||||
color: Color::NoColor,
|
*self == Self::None
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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 output = element.render(focused);
|
||||||
let x;
|
let x;
|
||||||
let mut y = 0;
|
let mut y = 0;
|
||||||
match output.1 .1 {
|
match output.1.y {
|
||||||
crate::ui::Number::Px(_y) => {
|
crate::ui::Number::Px(_y) => {
|
||||||
y = _y;
|
y = _y;
|
||||||
}
|
}
|
||||||
@@ -128,7 +128,7 @@ impl Frame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match output.1 .0 {
|
match output.1.x {
|
||||||
crate::ui::Number::Px(_x) => {
|
crate::ui::Number::Px(_x) => {
|
||||||
x = _x;
|
x = _x;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user