some nice dynamic stuff
This commit is contained in:
+16
-40
@@ -1,55 +1,31 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub fn examples() -> Element {
|
|
||||||
rsx! {
|
|
||||||
styling: Some(styling()),
|
|
||||||
|
|
||||||
button { class: "btn", on_click: fn(btn: &mut Button, _, document) {
|
|
||||||
let count = document.use_state::<u32>("count");
|
|
||||||
*count += 1;
|
|
||||||
btn.children.set_text(&count.to_string());
|
|
||||||
if *count == 10 {
|
|
||||||
document.exit();
|
|
||||||
}
|
|
||||||
}, "Click me!" }
|
|
||||||
|
|
||||||
text { "Welcome!" }
|
|
||||||
|
|
||||||
@count: i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn todo_app() -> Element {
|
pub fn todo_app() -> Element {
|
||||||
|
let count = State::new(0);
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
styling: Some(styling()),
|
styling: Some(styling()),
|
||||||
class: "root",
|
|
||||||
div {
|
button { on_click: fn(_, _, _) @count {
|
||||||
class: "tab",
|
let mut count = count.use_state();
|
||||||
text { class: "title", "not started" }
|
*count += 1;
|
||||||
text { class: "todo", "TODO: Something" }
|
}, class: "btn", "Count: {count}" }
|
||||||
}
|
|
||||||
div {
|
|
||||||
class: "tab",
|
|
||||||
text { class: "title", "completed" }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn styling() -> Css {
|
pub fn styling() -> Css {
|
||||||
css! {
|
css! {
|
||||||
"tab" {
|
|
||||||
x: Auto,
|
|
||||||
width: 50%,
|
|
||||||
outline: true,
|
|
||||||
}
|
|
||||||
"title" {
|
"title" {
|
||||||
width: Auto,
|
|
||||||
color: Red,
|
|
||||||
}
|
|
||||||
"todo" {
|
|
||||||
x: 0 px,
|
|
||||||
y: Auto,
|
|
||||||
color: Green,
|
color: Green,
|
||||||
|
x: Center,
|
||||||
|
}
|
||||||
|
|
||||||
|
"container" {
|
||||||
|
width: 50%,
|
||||||
|
}
|
||||||
|
|
||||||
|
"btn": "clicked" {
|
||||||
|
color: Red
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+66
-24
@@ -37,10 +37,10 @@ pub mod utils;
|
|||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::ui::Color::*;
|
pub use crate::ui::Color::*;
|
||||||
pub use crate::ui::Font::*;
|
pub use crate::ui::Font::*;
|
||||||
pub use crate::ui::Number::*;
|
pub use crate::ui::Number::{Auto, Center};
|
||||||
pub use crate::{self as osui, css, ersx, launch, rsx, ui::*, Handler};
|
pub use crate::{self as osui, css, ersx, launch, rsx, ui::*, Handler};
|
||||||
pub use crate::{call, Children, Element, ElementCore, ElementWidget};
|
pub use crate::{call, Children, Element, ElementCore, ElementWidget};
|
||||||
pub use crate::{style, Document};
|
pub use crate::{style, Document, State};
|
||||||
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));
|
||||||
@@ -88,19 +88,29 @@ pub struct Writer {
|
|||||||
focused: bool,
|
focused: bool,
|
||||||
size: (u16, u16),
|
size: (u16, u16),
|
||||||
absolute: (u16, u16),
|
absolute: (u16, u16),
|
||||||
|
written: (u16, u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
/// Enums
|
/// Enums
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Children {
|
pub enum Children {
|
||||||
None,
|
None,
|
||||||
Text(String),
|
Text(Arc<dyn Fn() -> String>),
|
||||||
Children(Vec<Element>, usize),
|
Children(Vec<Element>, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Children {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(&match self {
|
||||||
|
Self::None => String::new(),
|
||||||
|
Self::Children(a, b) => format!("({b}, {a:?})"),
|
||||||
|
Self::Text(a) => format!("{:?}", a()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
/// Implementations
|
/// Implementations
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -152,12 +162,6 @@ impl Document {
|
|||||||
pub fn draw(&mut self, element: &mut Element) {
|
pub fn draw(&mut self, element: &mut Element) {
|
||||||
self.element = element;
|
self.element = element;
|
||||||
}
|
}
|
||||||
pub fn use_state<T>(&self, name: &str) -> &mut T {
|
|
||||||
&mut self
|
|
||||||
.get_element_by_id::<crate::ui::DataHolder<T>>(name)
|
|
||||||
.unwrap()
|
|
||||||
.data
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Children {
|
impl Default for Children {
|
||||||
@@ -175,31 +179,25 @@ impl Children {
|
|||||||
}
|
}
|
||||||
pub fn get_text(&self) -> String {
|
pub fn get_text(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Children::Text(text) => text.clone(),
|
Children::Text(text) => text(),
|
||||||
_ => String::new(),
|
_ => String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_text_mut(&mut self) -> Option<&mut String> {
|
pub fn set_text(&mut self, text: Arc<dyn Fn() -> String>) {
|
||||||
match self {
|
|
||||||
Children::Text(text) => Some(text),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn set_text(&mut self, text: &str) {
|
|
||||||
match self {
|
match self {
|
||||||
Children::Text(t) => {
|
Children::Text(t) => {
|
||||||
*t = text.to_string();
|
*t = text;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn set_text_force(&mut self, text: &str) {
|
pub fn set_text_force(&mut self, text: Arc<dyn Fn() -> String>) {
|
||||||
match self {
|
match self {
|
||||||
Children::Text(t) => {
|
Children::Text(t) => {
|
||||||
*t = text.to_string();
|
*t = text;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
*self = Children::Text(text.to_string());
|
*self = Children::Text(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,6 +236,7 @@ impl Writer {
|
|||||||
focused,
|
focused,
|
||||||
size,
|
size,
|
||||||
absolute,
|
absolute,
|
||||||
|
written: (0, 1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,6 +248,11 @@ impl Writer {
|
|||||||
self.absolute.0 + 1,
|
self.absolute.0 + 1,
|
||||||
self.style.write(line)
|
self.style.write(line)
|
||||||
);
|
);
|
||||||
|
let line_len = line.len() as u16;
|
||||||
|
if line_len > self.written.0 {
|
||||||
|
self.written.0 = line_len;
|
||||||
|
}
|
||||||
|
self.written.1 += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,8 +262,23 @@ impl Writer {
|
|||||||
|
|
||||||
pub fn get_size(&self) -> (u16, u16) {
|
pub fn get_size(&self) -> (u16, u16) {
|
||||||
(
|
(
|
||||||
self.style.width.as_size(self.size.0, self.style.outline),
|
self.style
|
||||||
self.style.height.as_size(self.size.1, self.style.outline),
|
.width
|
||||||
|
.as_size(self.size.0, self.size.0, self.style.outline),
|
||||||
|
self.style
|
||||||
|
.height
|
||||||
|
.as_size(self.size.1, self.size.1, self.style.outline),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_size_root(&self) -> (u16, u16) {
|
||||||
|
(
|
||||||
|
self.style
|
||||||
|
.width
|
||||||
|
.as_size(self.written.0, self.size.0, self.style.outline),
|
||||||
|
self.style
|
||||||
|
.height
|
||||||
|
.as_size(self.written.1, self.size.1, self.style.outline),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,3 +325,26 @@ pub fn run(element: &mut Element) -> bool {
|
|||||||
|
|
||||||
unsafe { (*document.running).unwrap() }
|
unsafe { (*document.running).unwrap() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct State<T>(Arc<Mutex<T>>);
|
||||||
|
impl<T> State<T> {
|
||||||
|
pub fn new<'a>(v: T) -> State<T> {
|
||||||
|
State(Arc::new(Mutex::new(v)))
|
||||||
|
}
|
||||||
|
pub fn use_state<'a>(&'a self) -> std::sync::MutexGuard<'a, T> {
|
||||||
|
self.0.lock().unwrap()
|
||||||
|
}
|
||||||
|
pub fn copy_state<'a>(&'a self) -> State<T> {
|
||||||
|
State(Arc::clone(&self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: std::fmt::Display> std::fmt::Display for State<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
// Acquire the lock, handle potential poisoning
|
||||||
|
match self.0.lock() {
|
||||||
|
Ok(guard) => write!(f, "{}", *guard),
|
||||||
|
Err(poisoned) => write!(f, "<Poisoned: {}>", *poisoned.into_inner()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
use osui::{examples::todo_app, prelude::*};
|
use osui::{examples::todo_app, launch};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
launch!(todo_app())
|
launch!(todo_app());
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-21
@@ -12,17 +12,6 @@ macro_rules! check_expr {
|
|||||||
}
|
}
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! parse_rsx_param {
|
macro_rules! parse_rsx_param {
|
||||||
// Data fields
|
|
||||||
($elem:expr, @$name:ident: $type:ty; $($rest:tt)*) => {
|
|
||||||
if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)}
|
|
||||||
if let $crate::Children::Children(children, _) = &mut $elem.children {
|
|
||||||
let mut $name = $crate::ui::data_holder::<$type>();
|
|
||||||
$name.id = stringify!($name);
|
|
||||||
children.push($name);
|
|
||||||
}
|
|
||||||
osui::parse_rsx_param!($elem, $($rest)*)
|
|
||||||
};
|
|
||||||
|
|
||||||
($elem:expr, for ($($for:tt)*) $code:block $($rest:tt)*) => {
|
($elem:expr, for ($($for:tt)*) $code:block $($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 {
|
||||||
@@ -34,7 +23,17 @@ macro_rules! parse_rsx_param {
|
|||||||
};
|
};
|
||||||
|
|
||||||
($elem:expr, $($k:ident).+: fn($($params:tt)*) $code:block $(, $($rest:tt)*)?) => {
|
($elem:expr, $($k:ident).+: fn($($params:tt)*) $code:block $(, $($rest:tt)*)?) => {
|
||||||
$elem.$($k).+ = $crate::Handler::new(|$($params)*| $code);
|
$elem.$($k).+ = $crate::Handler::new(move |$($params)*| $code);
|
||||||
|
osui::parse_rsx_param!($elem, $($($rest)*)?);
|
||||||
|
};
|
||||||
|
|
||||||
|
($elem:expr, $($k:ident).+: fn($($params:tt)*) @$($v:ident),* $code:block $(, $($rest:tt)*)?) => {
|
||||||
|
$elem.$($k).+ = $crate::Handler::new({
|
||||||
|
$(
|
||||||
|
let $v = $v.copy_state();
|
||||||
|
)*
|
||||||
|
move |$($params)*| $code
|
||||||
|
});
|
||||||
osui::parse_rsx_param!($elem, $($($rest)*)?);
|
osui::parse_rsx_param!($elem, $($($rest)*)?);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -78,16 +77,8 @@ macro_rules! parse_rsx_param {
|
|||||||
osui::parse_rsx_param!($elem, $($rest)*)
|
osui::parse_rsx_param!($elem, $($rest)*)
|
||||||
};
|
};
|
||||||
|
|
||||||
($elem:expr, $elem_path:path as $t:ty { $($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 as $t { $($inner)* }));
|
|
||||||
}
|
|
||||||
osui::parse_rsx_param!($elem, $($rest)*)
|
|
||||||
};
|
|
||||||
|
|
||||||
($elem:expr, $text:expr) => {
|
($elem:expr, $text:expr) => {
|
||||||
$elem.children.set_text_force(&format!($text))
|
$elem.children.set_text_force(std::sync::Arc::new(move || format!($text)))
|
||||||
};
|
};
|
||||||
|
|
||||||
($elem:expr, $text:expr, $($inner:tt)*) => {
|
($elem:expr, $text:expr, $($inner:tt)*) => {
|
||||||
|
|||||||
+2
-24
@@ -18,9 +18,7 @@ pub struct Text {
|
|||||||
|
|
||||||
impl ElementWidget for Text<'_> {
|
impl ElementWidget for Text<'_> {
|
||||||
fn render(&self, writer: &mut crate::Writer) {
|
fn render(&self, writer: &mut crate::Writer) {
|
||||||
if let Children::Text(text) = &self.children {
|
writer.write(&self.children.get_text());
|
||||||
writer.write(&text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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,7 +75,7 @@ impl ElementWidget for Button<'_> {
|
|||||||
self.style.set_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(65);
|
||||||
self.style.set_state("");
|
self.style.set_state("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,23 +83,3 @@ impl ElementWidget for Button<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[element]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct DataHolder<'a, T> {
|
|
||||||
pub data: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn data_holder<'a, T: std::default::Default>() -> Box<DataHolder<'a, T>> {
|
|
||||||
Box::new(DataHolder {
|
|
||||||
style: Style::default(),
|
|
||||||
data: T::default(),
|
|
||||||
children: Children::None,
|
|
||||||
class: "",
|
|
||||||
id: "",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: std::fmt::Debug + Send + Sync> ElementWidget for DataHolder<'a, T> {
|
|
||||||
fn render(&self, _writer: &mut crate::Writer) {}
|
|
||||||
}
|
|
||||||
|
|||||||
+5
-20
@@ -310,31 +310,16 @@ impl Number {
|
|||||||
_ => frame_size,
|
_ => frame_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn as_size(&self, frame_size: u16, outline: bool) -> u16 {
|
pub fn as_size(&self, written: u16, frame_size: u16, outline: bool) -> u16 {
|
||||||
if outline {
|
if *self == Number::Auto || *self == Number::Default {
|
||||||
|
written
|
||||||
|
} else if outline {
|
||||||
self.as_size_raw(frame_size) - 2
|
self.as_size_raw(frame_size) - 2
|
||||||
} else {
|
} else {
|
||||||
self.as_size_raw(frame_size)
|
self.as_size_raw(frame_size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn as_position_y(&self, used: &Vec<u16>, frame_size: u16) -> u16 {
|
pub fn as_position(&self, used: &u16, frame_size: u16) -> u16 {
|
||||||
match self {
|
|
||||||
crate::ui::Number::Px(px) => *px,
|
|
||||||
crate::ui::Number::Pe(pe) => (frame_size * pe) / 100,
|
|
||||||
crate::ui::Number::Center => (frame_size) / 2,
|
|
||||||
crate::ui::Number::Auto | crate::ui::Number::Default => {
|
|
||||||
let mut y = 0;
|
|
||||||
for (i, n) in used.iter().enumerate() {
|
|
||||||
if *n == 0 {
|
|
||||||
y = i as u16;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn as_position_x(&self, used: &u16, frame_size: u16) -> u16 {
|
|
||||||
match self {
|
match self {
|
||||||
crate::ui::Number::Px(px) => *px,
|
crate::ui::Number::Px(px) => *px,
|
||||||
crate::ui::Number::Pe(pe) => (frame_size * pe) / 100,
|
crate::ui::Number::Pe(pe) => (frame_size * pe) / 100,
|
||||||
|
|||||||
+26
-4
@@ -1,5 +1,7 @@
|
|||||||
use std::io::{stdout, Write};
|
use std::io::{stdout, Write};
|
||||||
|
|
||||||
|
use crossterm::terminal::disable_raw_mode;
|
||||||
|
|
||||||
pub struct Frame {
|
pub struct Frame {
|
||||||
used: Vec<u16>,
|
used: Vec<u16>,
|
||||||
width: u16,
|
width: u16,
|
||||||
@@ -21,9 +23,21 @@ impl Frame {
|
|||||||
pub fn render(&mut self, focused: bool, element: &crate::Element) {
|
pub fn render(&mut self, focused: bool, element: &crate::Element) {
|
||||||
let style = element.get_style().get(focused);
|
let style = element.get_style().get(focused);
|
||||||
if style.visible {
|
if style.visible {
|
||||||
let y = style.y.as_position_y(&self.used, self.height);
|
let y = style.y.as_position(
|
||||||
|
&{
|
||||||
|
let mut y_ = 0;
|
||||||
|
for n in self.used.iter().rev() {
|
||||||
|
if *n == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
y_ += 1;
|
||||||
|
}
|
||||||
|
y_
|
||||||
|
},
|
||||||
|
self.height,
|
||||||
|
);
|
||||||
if let Some(y_) = self.used.get(y as usize) {
|
if let Some(y_) = self.used.get(y as usize) {
|
||||||
let x = style.x.as_position_x(y_, self.width);
|
let x = style.x.as_position(y_, self.width);
|
||||||
let mut writer = crate::Writer::new(
|
let mut writer = crate::Writer::new(
|
||||||
focused,
|
focused,
|
||||||
style.clone(),
|
style.clone(),
|
||||||
@@ -31,9 +45,17 @@ impl Frame {
|
|||||||
(self.width, self.height),
|
(self.width, self.height),
|
||||||
);
|
);
|
||||||
element.render(&mut writer);
|
element.render(&mut writer);
|
||||||
let (width, height) = writer.get_size();
|
let (width, height) = writer.get_size_root();
|
||||||
|
if style.outline {
|
||||||
|
clear();
|
||||||
|
show_cursor();
|
||||||
|
disable_raw_mode().unwrap();
|
||||||
|
panic!("the 'outline' option isn't available right now. please set 'outline' to 'false'")
|
||||||
|
}
|
||||||
for i in y..y + height {
|
for i in y..y + height {
|
||||||
*self.used.get_mut(i as usize).unwrap() += x + width;
|
if let Some(o) = self.used.get_mut(i as usize) {
|
||||||
|
*o += x + width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user