diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..3b35e3e
Binary files /dev/null and b/.DS_Store differ
diff --git a/Cargo.toml b/Cargo.toml
index 890fa69..56edcc0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "osui"
-version = "0.0.5"
+version = "0.0.6"
edition = "2021"
license = "Apache-2.0"
description = "A rsx TUI library"
@@ -22,5 +22,6 @@ crossterm = "0.28.1"
regex = "1.11.1"
lazy_static = "1.4"
paste = "1.0.15"
-osui-element = "0.1.0"
dyn-clone = "1.0.17"
+flume = "=0.10.12"
+osui-element = "0.1.11"
diff --git a/README.md b/README.md
index efbfe8f..b62090d 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,9 @@
-
+
+osui
+
@@ -28,11 +30,8 @@
## Getting Started
-To use OSUI in your project, include it in your `Cargo.toml`:
-
-```toml
-[dependencies]
-osui = "0.2" # Replace with the latest version
+```bash
+cargo add osui
```
## Example Usage
diff --git a/build.rs b/build.rs
deleted file mode 100644
index 5806a52..0000000
--- a/build.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-fn main() {
- cc::Build::new()
- .file("src/app.c").compile("app");
-}
\ No newline at end of file
diff --git a/build.sh b/build.sh
deleted file mode 100755
index 3bc7006..0000000
--- a/build.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-rm -rf ./lib/
-mkdir lib
-gcc -c -o lib/app.o src/app.c
-ar rcs lib/libapp.a lib/app.o
\ No newline at end of file
diff --git a/github/OSUI.svg b/github/OSUI.svg
new file mode 100644
index 0000000..9339c50
--- /dev/null
+++ b/github/OSUI.svg
@@ -0,0 +1,90 @@
+
diff --git a/github/OSUI.png b/github/OSUIt.png
similarity index 100%
rename from github/OSUI.png
rename to github/OSUIt.png
diff --git a/github/OSUIw.svg b/github/OSUIw.svg
new file mode 100644
index 0000000..79a536b
--- /dev/null
+++ b/github/OSUIw.svg
@@ -0,0 +1,90 @@
+
diff --git a/src/app.c b/src/app.c
deleted file mode 100644
index 50a5475..0000000
--- a/src/app.c
+++ /dev/null
@@ -1,32 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-
-extern boolean_t render(void* element);
-extern void *event_loop(void* element);
-
-void sleep_ms(long milliseconds)
-{
- struct timespec ts;
- ts.tv_sec = milliseconds / 1000;
- ts.tv_nsec = (milliseconds % 1000) * 1000000;
- nanosleep(&ts, NULL);
-}
-
-boolean_t c_run(void* element)
-{
- pthread_t thread;
-
- if (pthread_create(&thread, NULL, event_loop, element) != 0)
- {
- perror("Failed to create thread");
- }
-
- while (render(element))
- {
- sleep_ms(20);
- }
- return 0;
-}
\ No newline at end of file
diff --git a/src/app.rs b/src/app.rs
deleted file mode 100644
index 8e22e3f..0000000
--- a/src/app.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-use crossterm::{
- event::Event,
- terminal::{disable_raw_mode, enable_raw_mode},
-};
-
-use crate::{
- utils::{clear, create_frame, flush, get_term_size, hide_cursor, render_to_frame, show_cursor},
- Element,
-};
-use std::{ffi::c_void, ptr::null};
-
-extern "C" {
- fn c_run(element: *mut c_void) -> bool;
-}
-
-#[no_mangle]
-extern "C" fn render(ptr: *mut c_void) -> bool {
- if ptr.is_null() {
- return false;
- }
- let element = unsafe { &mut *(ptr as *mut Element) };
- let (width, height) = get_term_size();
- let mut frame: Vec = create_frame(width, height);
- render_to_frame(true, width, &mut frame, element);
- clear();
- print!("{}", frame.join(""));
- flush();
- true
-}
-
-#[no_mangle]
-extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void {
- if ptr.is_null() {
- return null() as *const c_void;
- }
- let element = unsafe { &mut *(ptr as *mut Element) };
- let (width, height) = get_term_size();
- element.update_data(width, height);
- loop {
- if ptr.is_null() {
- break;
- }
- event(element, crossterm::event::read().unwrap())
- }
- null() as *const c_void
-}
-
-fn event(element: &mut Element, event: Event) {
- if let crossterm::event::Event::Resize(width, height) = event {
- element.update_data(width as usize, height as usize);
- }
- element.event(event);
-}
-
-pub fn run(element: &mut Element) -> bool {
- hide_cursor();
- enable_raw_mode().unwrap();
- clear();
- unsafe {
- let c = c_run(element as *mut Element as *mut c_void);
- if !c {
- show_cursor();
- disable_raw_mode().unwrap();
- clear();
- }
- return c;
- }
-}
diff --git a/src/css.rs b/src/css.rs
new file mode 100644
index 0000000..19eddcc
--- /dev/null
+++ b/src/css.rs
@@ -0,0 +1,261 @@
+#[macro_export]
+macro_rules! __css {
+ ($_style:expr,)=>{};
+ // percentage (n%)
+ (
+ $_style:expr, $name:ident: $value:literal% $(, $($other:tt)*)?
+ ) => {{
+ $_style.$name = Number::Pe($value);
+ $crate::__css!($_style, $($($other)*)?);
+ }};
+ (
+ $_style:expr, ($name:ident): $value:literal% $(, $($other:tt)*)?
+ ) => {{
+ $_style.other.insert(stringify!($name).to_string(), Box::new(
+ Number::Pe($value)
+ ));
+ $crate::__css!($_style, $($($other)*)?);
+ }};
+
+ // Number
+ (
+ $_style:expr, $name:ident: $value:literal $(, $($other:tt)*)?
+ ) => {{
+ $_style.$name = Number::Px($value);
+ $crate::__css!($_style, $($($other)*)?);
+ }};
+
+ // Vector
+ (
+ $_style:expr, $name:ident: [$($inner:tt),*] $(, $($other:tt)*)?
+ ) => {{
+ $_style.$name = vec![$($inner),*];
+ $crate::__css!($_style, $($($other)*)?);
+ }};
+
+ // Normal
+ (
+ $_style:expr, $name:ident: $value:expr $(, $($other:tt)*)?
+ ) => {{
+ $_style.$name = $value;
+ $crate::__css!($_style, $($($other)*)?);
+ }};
+
+ (
+ $_style:expr, ($name:ident): $value:expr $(, $($other:tt)*)?
+ ) => {{
+ $_style.other.insert(stringify!($name).to_string(), Box::new($value));
+ $crate::__css!($_style, $($($other)*)?);
+ }};
+}
+
+#[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)*);
+ }};
+}
+
+/// Creates a styling map `Css`
+///
+/// # Examples
+/// ```
+/// .btn: clicked {
+/// color: Blue
+/// }
+/// ```
+///
+/// # Returns
+/// `Css`
+#[macro_export]
+macro_rules! css {
+ (
+ $($inner:tt)*
+ ) => {{
+ let mut hm: $crate::ui::Css = std::collections::HashMap::new();
+ $crate::_css!(hm, $($inner)*);
+ hm
+ }};
+}
+
+#[macro_export]
+macro_rules! style {
+ (
+ $key:ident: $value:expr $(, $($rest:tt)*)?
+ ) => {{
+ let mut _style = $crate::ui::Style::default();
+ $crate::__css!(_style.0, $key: $value);
+ $crate::style!{ _style; $($($rest)*)? }
+ }};
+ (
+ $style:expr; $key:ident: $value:expr $(, $($rest:tt)*)?
+ ) => {{
+ let mut style_elem = $crate::ui::StyleElement::default();
+ $crate::__css!($style.0, $key: $value);
+ $crate::style!{ $style; $($($rest)*)? }
+ }};
+ (
+ ($key:ident): $value:expr $(, $($rest:tt)*)?
+ ) => {{
+ let mut _style = $crate::ui::Style::default();
+ $crate::__css!(_style.0, ($key): $value);
+ $crate::style!{ _style; $($($rest)*)? }
+ }};
+ (
+ $style:expr; ($key:ident): $value:expr $(, $($rest:tt)*)?
+ ) => {{
+ let mut style_elem = $crate::ui::StyleElement::default();
+ $crate::__css!($style.0, ($key): $value);
+ $crate::style!{ $style; $($($rest)*)? }
+ }};
+
+ (
+ $kind:ident {$($inner:tt)*} $(, $($rest:tt)*)?
+ ) => {{
+ 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);
+ $crate::style!{ _style; $($($rest)*)? }
+ }};
+ (
+ $style:expr; $kind:ident {$($inner:tt)*} $(, $($rest:tt)*)?
+ ) => {{
+ let mut style_elem = $crate::ui::StyleElement::default();
+ $crate::__css!(style_elem, $($inner)*);
+ $style.1.insert(stringify!($kind).to_string(), style_elem);
+ $crate::style!{ $style; $($($rest)*)? }
+ }};
+
+ (
+ $style:expr;
+ ) => {{
+ $style
+ }};
+
+ () => {{
+ $crate::ui::Style::default()
+ }};
+}
diff --git a/src/examples.rs b/src/examples.rs
new file mode 100644
index 0000000..24deab3
--- /dev/null
+++ b/src/examples.rs
@@ -0,0 +1,25 @@
+use crate::prelude::*;
+
+pub fn examples() -> Element {
+ rsx! {
+ styling: Some(styling()),
+
+ button { class: "btn", "Click me!" }
+
+ text { "Welcome!" }
+ }
+}
+
+pub fn styling() -> Css {
+ css! {
+ .btn {
+ x: Center,
+ y: Center,
+ color: Red,
+ }
+
+ .btn: clicked {
+ color: Blue,
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 92b23b6..a28f81c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,13 +10,12 @@
//! ```rust
//! use osui::prelude::*;
//!
-//! run(&mut rsx! {
+//! launch(rsx! {
//! text { "Hello, World!" }
//! });
//! ```
//!
//! ## Modules
-//! - `app` - The main function for rendering.
//! - `ui` - Contains all user interface components, enabling rich CLI experiences.
//! - `utils` - Utility functions for common TUI tasks such as clearing the screen.
@@ -26,24 +25,20 @@ use std::{
sync::{Arc, Mutex},
};
-use crossterm::event::Event;
-
-pub mod app;
+pub mod css;
+pub mod examples;
pub mod macros;
+pub mod rsx;
pub mod ui;
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::{self as osui, css, rsx, rsx_elem, ui::*, Handler};
- pub use crate::{Element, Value};
- // useful for Element making
- pub use crate::{run_handler, Children, ElementCore, ElementWidget};
+ pub use crate::ui::Number::*;
+ pub use crate::ui::Font::*;
+ pub use crate::{self as osui, css, ersx, launch, rsx, ui::*, Handler};
+ pub use crate::{call, Children, Element, ElementCore, ElementWidget};
+ pub use crate::{style, Document, RenderResult, RenderWriter};
pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};
pub fn sleep(ms: u64) {
std::thread::sleep(std::time::Duration::from_millis(ms));
@@ -51,90 +46,53 @@ pub mod prelude {
}
//////////////////////////////////////////////////////////////////////////////////////////////////
-/// Value Enum
+// Type aliases
//////////////////////////////////////////////////////////////////////////////////////////////////
-#[derive(Debug, Clone, Copy)]
-pub enum Value {
- Default(T),
- Custom(T),
-}
-
-impl Value {
- pub fn get_value(&self) -> T {
- match self {
- Value::Custom(s) => *s,
- Value::Default(s) => *s,
- }
- }
-
- pub fn try_set_value(&mut self, value: T) {
- if let Value::Default(_) = *self {
- *self = Value::Default(value);
- }
- }
-}
-
-impl Default for Value {
- fn default() -> Self {
- Self::Default(T::default())
- }
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////
-/// ElementCore Traits
-//////////////////////////////////////////////////////////////////////////////////////////////////
-
-pub trait ElementCore: Send + Sync {
- fn get_data(&self) -> (Value, 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);
-}
-
-pub trait ElementWidget: ElementCore + std::fmt::Debug {
- fn render(&self, focused: bool) -> String {
- _ = focused;
- String::new()
- }
-
- fn event(&mut self, event: Event) {
- _ = event
- }
-}
-
pub type Element = Box;
//////////////////////////////////////////////////////////////////////////////////////////////////
-/// Handler Struct
+/// Traits
//////////////////////////////////////////////////////////////////////////////////////////////////
-pub struct Handler(pub Arc>);
-
-impl Handler {
- pub fn new(handler_fn: F) -> Handler
- where
- F: FnMut(&mut T, Event) + 'static + Send + Sync,
- {
- Handler(Arc::new(Mutex::new(handler_fn)))
- }
+pub trait ElementCore {
+ fn get_element_by_id(&mut self, id: &str) -> Option<&mut Element>;
+ fn set_styling(&mut self, styling: &HashMap);
+ fn get_id(&self) -> String;
}
-impl Default for Handler {
- fn default() -> Self {
- Handler(Arc::new(Mutex::new(|_: &mut T, _: Event| {})))
- }
-}
-
-impl std::fmt::Debug for Handler {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.write_str("Handler()")
+pub trait ElementWidget: ElementCore + std::fmt::Debug + Send + Sync {
+ fn render(&self, focused: bool) -> Option;
+ fn event(&mut self, event: crossterm::event::Event, document: &Document) {
+ _ = (event, document)
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////
-/// Children Enum
+/// Structs
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+pub struct Handler(
+ pub Arc>,
+);
+
+pub struct Document {
+ element: *mut Element,
+ running: *mut Option,
+}
+
+/// ```
+/// RenderResult(output, (x, y))
+/// ```
+pub struct RenderResult(String, ui::StyleElement);
+pub struct RenderWriter {
+ w: String,
+ style: ui::Style,
+ focused: bool,
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+/// Enums
//////////////////////////////////////////////////////////////////////////////////////////////////
#[derive(Debug)]
@@ -144,6 +102,57 @@ pub enum Children {
Children(Vec, usize),
}
+//////////////////////////////////////////////////////////////////////////////////////////////////
+/// Implementations
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+impl Document {
+ pub fn exit(&self) {
+ if !self.running.is_null() {
+ unsafe {
+ *self.running = Some(false);
+ }
+ }
+ }
+ pub fn rebuild(&self) {
+ if !self.running.is_null() {
+ unsafe {
+ *self.running = Some(true);
+ }
+ }
+ }
+ pub fn get_element_by_id(&self, id: &str) -> Option<&mut Box> {
+ if let Some(e) = self.get_element_by_id_raw(id) {
+ Some(unsafe { &mut *(e as *mut Element as *mut Box) })
+ } else {
+ None
+ }
+ }
+ pub fn get_element_by_id_raw(&self, id: &str) -> Option<&mut Element> {
+ if !self.element.is_null() {
+ unsafe {
+ if (*self.element).get_id() == id {
+ Some(&mut *self.element)
+ } else {
+ (*self.element).get_element_by_id(id)
+ }
+ }
+ } else {
+ None
+ }
+ }
+ pub fn render(&self) {
+ if !self.running.is_null() {
+ let (width, height) = utils::get_term_size();
+ let mut frame = utils::Frame::new(width, height);
+ frame.render(true, unsafe { &*self.element });
+ utils::clear();
+ print!("{}", frame.output_nnl());
+ utils::flush();
+ }
+ }
+}
+
impl Default for Children {
fn default() -> Self {
Self::None
@@ -163,4 +172,109 @@ impl Children {
_ => String::new(),
}
}
+ pub fn get_text_mut(&mut self) -> Option<&mut String> {
+ match self {
+ Children::Text(text) => Some(text),
+ _ => None,
+ }
+ }
+ pub fn set_text(&mut self, text: &str) {
+ match self {
+ Children::Text(t) => {
+ *t = text.to_string();
+ }
+ _ => {}
+ }
+ }
+ pub fn set_text_force(&mut self, text: &str) {
+ match self {
+ Children::Text(t) => {
+ *t = text.to_string();
+ }
+ _ => {
+ *self = Children::Text(text.to_string());
+ }
+ }
+ }
+ pub fn add_child(&mut self, element: Element) {
+ if let Children::Children(children, _) = self {
+ children.push(element);
+ }
+ }
+ pub fn get_child(&mut self) -> Option<&mut Element> {
+ if let Children::Children(children, child) = self {
+ children.get_mut(*child)
+ } else {
+ None
+ }
+ }
+}
+
+impl Handler {
+ pub fn new(handler_fn: F) -> Handler
+ where
+ F: FnMut(&mut T, crossterm::event::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 Default for Handler {
+ fn default() -> Self {
+ Handler(Arc::new(Mutex::new(
+ |_: &mut T, _: crossterm::event::Event, _: &Document| {},
+ )))
+ }
+}
+
+impl std::fmt::Debug for Handler {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str("Handler()")
+ }
+}
+
+pub fn run(element: &mut Element) -> bool {
+ let document = Document {
+ element: element,
+ running: &mut None,
+ };
+ let (width, height) = crate::utils::get_term_size();
+ element.event(
+ crossterm::event::Event::Resize(width as u16, height as u16),
+ &document,
+ );
+ element.event(crossterm::event::Event::FocusGained, &document);
+
+ utils::hide_cursor();
+ crossterm::terminal::enable_raw_mode().unwrap();
+ utils::clear();
+
+ while unsafe { *document.running } == None {
+ document.render();
+ element.event(crossterm::event::read().unwrap(), &document);
+ }
+
+ utils::show_cursor();
+ crossterm::terminal::disable_raw_mode().unwrap();
+ utils::clear();
+
+ unsafe { (*document.running).unwrap() }
}
diff --git a/src/macros.rs b/src/macros.rs
index 88c3966..b5db83a 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -1,171 +1,5 @@
-/// **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.
-///
-/// ## Example
-/// ```rust
-/// parse_rsx_param!(elem, title: "Hello", for (i in 0..5) { }, text: "World");
-/// ```
-///
-/// ## Syntax
-///
-/// **Set property or field of an element:**
-/// ```rust
-/// parse_rsx_param!($elem, $($k:ident).+: $v:expr);
-/// ```
-///
-/// **Process multiple parameters:**
-/// ```rust
-/// parse_rsx_param!($elem, $($k:ident).+: $v:expr, $($rest:tt)*);
-/// ```
-///
-/// **Handle path expression:**
-/// ```rust
-/// parse_rsx_param!($elem, $p:path);
-/// ```
-///
-/// **Add nested children:**
-/// ```rust
-/// parse_rsx_param!($elem, for ($($for:tt)*) { $($inner:tt)* } $($rest:tt)*);
-/// ```
-///
-/// **Add child element directly:**
-/// ```rust
-/// parse_rsx_param!($elem, {$ielem:expr} $($rest:tt)*);
-/// ```
-///
-/// **Set element text:**
-/// ```rust
-/// parse_rsx_param!($elem, $text:expr);
-/// ```
-///
-/// **Handle empty case:**
-/// ```rust
-/// parse_rsx_param!($elem, );
-/// ```
#[macro_export]
-macro_rules! parse_rsx_param {
- ($elem:expr, $($k:ident).+: $v:expr) => {
- $elem.$($k).+ = $v;
- };
-
- ($elem:expr, $($k:ident).+: $v:expr, $($rest:tt)*) => {
- $elem.$($k).+ = $v;
- osui::parse_rsx_param!($elem, $($rest)*);
- };
-
- ($elem:expr, $p:path) => {
- $p;
- };
- ($elem:expr, $($k:ident).+, $($rest:tt)*) => {
- $elem.$($k).+;
- osui::parse_rsx_param!($elem, $($rest)*);
- };
- ($elem:expr, $($k:ident).+., $($rest:tt)*) => {
- $elem.$($k).
- +.;
- osui::parse_rsx_param!($elem, $($rest)*);
- };
-
- ($elem:expr, $($k:ident).+.: $v:expr) => {
- $elem.$($k).+. = $v;
- };
- ($elem:expr, $($k:ident).+.: $v:expr, $($rest:tt)*) => {
- $elem.$($k).+. = $v;
- 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, {$ielem: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 {
- children.push({$ielem});
- }
- osui::parse_rsx_param!($elem, $($rest)*);
- };
-
- ($elem:expr, $pelem:path { $($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 {
- children.push(osui::rsx_elem!($pelem { $($inner)* }));
- }
- osui::parse_rsx_param!($elem, $($rest)*)
- };
-
- ($elem:expr, $text:expr) => {
- if $elem.children.is_none() {$elem.children = $crate::Children::Text(format!($text))}
- };
- ($elem:expr, $text:expr, $($inner:tt)*) => {
- if $elem.children.is_none() {$elem.children = $crate::Children::Text(format!($text, $($inner)*))}
- };
-
- ($elem:expr, ) => {};
-}
-
-/// **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.
-///
-/// ## Example
-/// ```rust
-/// let element = rsx_elem!(ui::Button { text: "Click me" });
-/// ```
-///
-/// ## Syntax
-/// ```rust
-/// rsx_elem!($elem:path { $($inner:tt)* })
-/// ```
-#[macro_export]
-macro_rules! rsx_elem {
- ($elem:path { $($inner:tt)* }) => {{
- #[allow(unused_mut)]
- let mut elem = $elem();
- $crate::parse_rsx_param!(elem, $($inner)*);
- elem as $crate::Element
- }};
-}
-
-/// **rsx!** - A shorthand macro to create a `div` element and apply nested content to it.
-///
-/// This macro is a simple wrapper for `rsx_elem!`, making it easy to create a `div` element and add children to it.
-///
-/// ## Example
-/// ```rust
-/// rsx! {
-/// text { "Hello, World" }
-/// div { button {"Click me!"} }
-/// }
-/// ```
-///
-/// ## Syntax
-/// ```rust
-/// rsx!($($inner:tt)*)
-/// ```
-#[macro_export]
-macro_rules! rsx {
- ($($inner:tt)*) => {{
- $crate::rsx_elem!( $crate::ui::div { $($inner)* } )
- }};
-}
-
-#[macro_export]
-macro_rules! val {
- ($a:expr) => {
- $crate::Value::new($a)
- };
-}
-
-#[macro_export]
-macro_rules! run_handler {
+macro_rules! call {
($self:ident.$handler:ident ($($inner:tt)*)) => {{
let a = std::sync::Arc::clone(&$self.$handler.0);
let mut o = a.lock().unwrap();
@@ -177,162 +11,8 @@ macro_rules! run_handler {
}
#[macro_export]
-macro_rules! __css {
- ($style:expr,)=>{};
- (
- $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
- }};
+macro_rules! launch {
+ ($expr:expr) => {
+ while $crate::run(&mut $expr) {}
+ };
}
diff --git a/src/main.rs b/src/main.rs
index 3e7931e..6a2f33b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,19 +1,6 @@
+use osui::examples;
use osui::prelude::*;
fn main() {
- while osui::run(&mut app()) {}
-}
-
-fn app() -> Element {
- rsx! { styling: Some(styles()),
- button { class: "btn", "This is a button!" }
- }
-}
-
-fn styles() -> Css {
- css! {
- .btn::clicked {
- color: Blue
- }
- }
+ launch!(examples::examples());
}
diff --git a/src/rsx.rs b/src/rsx.rs
new file mode 100644
index 0000000..8015222
--- /dev/null
+++ b/src/rsx.rs
@@ -0,0 +1,95 @@
+#[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, $($k:ident).+: $v:expr) => {
+ $elem.$($k).+ = $v;
+ };
+
+ ($elem:expr, $($k:ident).+: $v:expr, $($rest:tt)*) => {
+ $elem.$($k).+ = $v;
+ osui::parse_rsx_param!($elem, $($rest)*);
+ };
+ ($elem:expr, $p:path) => {
+ $p;
+ };
+ ($elem:expr, $($k:ident).+, $($rest:tt)*) => {
+ $elem.$($k).+;
+ osui::parse_rsx_param!($elem, $($rest)*);
+ };
+ ($elem:expr, $($k:ident).+., $($rest:tt)*) => {
+ $elem.$($k).
+ +.;
+ osui::parse_rsx_param!($elem, $($rest)*);
+ };
+
+ ($elem:expr, $($k:ident).+.: $v:expr) => {
+ $elem.$($k).+. = $v;
+ };
+ ($elem:expr, $($k:ident).+.: $v:expr, $($rest:tt)*) => {
+ $elem.$($k).+. = $v;
+ osui::parse_rsx_param!($elem, $($rest)*);
+ };
+
+ ($elem:expr, $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 {
+ children.push($code);
+ }
+ osui::parse_rsx_param!($elem, $($rest)*);
+ };
+
+ ($elem:expr, $elem_path:path { $($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 {
+ children.push(osui::ersx!($elem_path { $($inner)* }));
+ }
+ osui::parse_rsx_param!($elem, $($rest)*)
+ };
+
+ ($elem:expr, $text:expr) => {
+ if $elem.children.is_none() {$elem.children = $crate::Children::Text(format!($text))}
+ };
+ ($elem:expr, $text:expr, $($inner:tt)*) => {
+ if $elem.children.is_none() {$elem.children = $crate::Children::Text(format!($text, $($inner)*))}
+ };
+
+ ($elem:expr, ) => {};
+}
+
+#[macro_export]
+macro_rules! ersx {
+ ($elem:path { $($inner:tt)* }) => {{
+ #[allow(unused_mut)]
+ let mut elem = $elem();
+ $crate::parse_rsx_param!(elem, $($inner)*);
+ elem as $crate::Element
+ }};
+}
+
+/// Makes a div and puts elements into it
+///
+/// # Example
+/// ```
+/// rsx! {
+/// button { class: "btn", "Click me!" }
+/// }
+/// ```
+///
+/// # Returns
+/// A `osui::Element` - Which is a `Box`
+#[macro_export]
+macro_rules! rsx {
+ ($($inner:tt)*) => {
+ $crate::ersx!{ $crate::ui::div { $($inner)* } }
+ };
+
+}
diff --git a/src/ui/mod.rs b/src/ui/mod.rs
index 981fdf8..f4813d2 100644
--- a/src/ui/mod.rs
+++ b/src/ui/mod.rs
@@ -3,60 +3,37 @@
/// # Modules
/// - `styles`: A module for defining UI styles.
pub mod styles;
+
pub use styles::*;
use crate::prelude::*;
use osui_element::{elem_fn, element};
-/// The `Text` element represents a piece of static text in the terminal UI.
-///
-/// It is used to render plain text within a UI component. The `Text` element does not interact
-/// with events and only outputs the text when rendered.
-///
-/// # Example
-/// ```rust
-/// let text = Text::default();
-/// let rendered_text = text.render(crate::State::Normal);
-/// println!("{}", rendered_text);
-/// ```
-///
-/// ## Methods
-/// - `render(state: crate::State) -> String`: Renders the text to a string, depending on the state.
-/// If the element contains text as a child, it returns that text. Otherwise, it returns an empty string.
#[element]
#[elem_fn]
#[derive(Default, Debug)]
-pub struct Text {}
-
-impl ElementWidget for Text<'_> {
- fn render(&self, _: bool) -> String {
- if let Children::Text(text) = &self.children {
- text.clone()
- } else {
- String::new()
- }
- }
+pub struct Text {
+ pub on_event: Handler>,
}
-/// The `Div` element represents a container for other UI elements. It can hold child components
-/// and render them in a specific layout. The `Div` element also handles keyboard navigation through
-/// its children.
-///
-/// The `Div` element supports rendering a list of children and allows interactive navigation using
-/// arrow keys (Up, Down, Left, Right). It renders the selected child element differently based on the state.
-///
-/// # Example
-/// ```rust
-/// let div = Div::default();
-/// let rendered_div = div.render(crate::State::Normal);
-/// println!("{}", rendered_div);
-/// ```
-///
-/// ## Methods
-/// - `render(state: crate::State) -> String`: Renders the `Div` and its child elements to a string.
-/// The child elements are rendered according to the current selection (`child` index).
-/// - `event(event: Event, state: &crate::StateChanger)`: Handles keyboard events for
-/// navigation through child elements. Arrow keys (Up, Down, Left, Right) change the currently selected child.
+impl ElementWidget for Text<'_> {
+ fn render(&self, focused: bool) -> Option {
+ let mut writer = RenderWriter::new(focused, self.style.clone());
+
+ writer.write(&{
+ if let Children::Text(text) = &self.children {
+ text.clone()
+ } else {
+ String::new()
+ }
+ });
+
+ Some(writer.result())
+ }
+ fn event(&mut self, event: Event, document: &Document) {
+ call!(self.on_event(event, document));
+ }
+}
#[element]
#[elem_fn]
@@ -66,69 +43,28 @@ pub struct Div {
}
impl ElementWidget for Div<'_> {
- fn render(&self, focused: bool) -> String {
- let mut frame = crate::utils::create_frame(self.width.get_value(), self.height.get_value());
+ fn render(&self, focused: bool) -> Option {
+ 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 {
for (i, elem) in children.iter().enumerate() {
- crate::utils::render_to_frame(
- focused && i == *child,
- self.width.get_value(),
- &mut frame,
- elem,
- );
+ frame.render(focused && i == *child, elem);
}
}
+ writer.write(&frame.output());
- frame.join("\n")
+ Some(writer.result())
}
- fn event(&mut self, event: Event) {
- if let Some(styling) = self.styling.clone() {
- self.set_styling(&styling);
+ fn event(&mut self, event: Event, document: &Document) {
+ if event == Event::FocusGained {
+ if let Some(styling) = self.styling.clone() {
+ self.set_styling(&styling);
+ }
}
- match event {
- Event::Key(key) => {
- if let Children::Children(children, child) = &mut self.children {
- *child = match key.code {
- KeyCode::Up => crate::utils::closest_component(
- children,
- *child,
- crate::utils::Direction::Up,
- ),
-
- KeyCode::Down => crate::utils::closest_component(
- children,
- *child,
- crate::utils::Direction::Down,
- ),
-
- KeyCode::Left => crate::utils::closest_component(
- children,
- *child,
- crate::utils::Direction::Left,
- ),
-
- KeyCode::Right => crate::utils::closest_component(
- children,
- *child,
- crate::utils::Direction::Right,
- ),
-
- _ => {
- if let Some(c) = children.get_mut(*child) {
- c.event(event);
- }
- *child
- }
- }
- }
- }
- _ => {
- if let Some(child) = self.get_child() {
- child.event(event);
- }
- }
+ if let Some(child) = self.children.get_child() {
+ child.event(event, document);
}
}
}
@@ -138,32 +74,51 @@ impl ElementWidget for Div<'_> {
#[derive(Default, Debug)]
pub struct Button {
pub on_click: Handler