some nice dynamic stuff
This commit is contained in:
+16
-40
@@ -1,55 +1,31 @@
|
||||
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 {
|
||||
let count = State::new(0);
|
||||
|
||||
rsx! {
|
||||
styling: Some(styling()),
|
||||
class: "root",
|
||||
div {
|
||||
class: "tab",
|
||||
text { class: "title", "not started" }
|
||||
text { class: "todo", "TODO: Something" }
|
||||
}
|
||||
div {
|
||||
class: "tab",
|
||||
text { class: "title", "completed" }
|
||||
}
|
||||
|
||||
button { on_click: fn(_, _, _) @count {
|
||||
let mut count = count.use_state();
|
||||
*count += 1;
|
||||
}, class: "btn", "Count: {count}" }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn styling() -> Css {
|
||||
css! {
|
||||
"tab" {
|
||||
x: Auto,
|
||||
width: 50%,
|
||||
outline: true,
|
||||
}
|
||||
"title" {
|
||||
width: Auto,
|
||||
color: Red,
|
||||
}
|
||||
"todo" {
|
||||
x: 0 px,
|
||||
y: Auto,
|
||||
color: Green,
|
||||
x: Center,
|
||||
}
|
||||
|
||||
"container" {
|
||||
width: 50%,
|
||||
}
|
||||
|
||||
"btn": "clicked" {
|
||||
color: Red
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+66
-24
@@ -37,10 +37,10 @@ pub mod utils;
|
||||
pub mod prelude {
|
||||
pub use crate::ui::Color::*;
|
||||
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::{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 fn sleep(ms: u64) {
|
||||
std::thread::sleep(std::time::Duration::from_millis(ms));
|
||||
@@ -88,19 +88,29 @@ pub struct Writer {
|
||||
focused: bool,
|
||||
size: (u16, u16),
|
||||
absolute: (u16, u16),
|
||||
written: (u16, u16),
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Enums
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Children {
|
||||
None,
|
||||
Text(String),
|
||||
Text(Arc<dyn Fn() -> String>),
|
||||
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
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -152,12 +162,6 @@ impl Document {
|
||||
pub fn draw(&mut self, element: &mut 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 {
|
||||
@@ -175,31 +179,25 @@ impl Children {
|
||||
}
|
||||
pub fn get_text(&self) -> String {
|
||||
match self {
|
||||
Children::Text(text) => text.clone(),
|
||||
Children::Text(text) => text(),
|
||||
_ => 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) {
|
||||
pub fn set_text(&mut self, text: Arc<dyn Fn() -> String>) {
|
||||
match self {
|
||||
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 {
|
||||
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,
|
||||
size,
|
||||
absolute,
|
||||
written: (0, 1),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,6 +248,11 @@ impl Writer {
|
||||
self.absolute.0 + 1,
|
||||
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) {
|
||||
(
|
||||
self.style.width.as_size(self.size.0, self.style.outline),
|
||||
self.style.height.as_size(self.size.1, self.style.outline),
|
||||
self.style
|
||||
.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() }
|
||||
}
|
||||
|
||||
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() {
|
||||
launch!(todo_app())
|
||||
launch!(todo_app());
|
||||
}
|
||||
|
||||
+12
-21
@@ -12,17 +12,6 @@ macro_rules! check_expr {
|
||||
}
|
||||
#[macro_export]
|
||||
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)*) => {
|
||||
if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)}
|
||||
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.$($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)*)?);
|
||||
};
|
||||
|
||||
@@ -78,16 +77,8 @@ macro_rules! parse_rsx_param {
|
||||
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.children.set_text_force(&format!($text))
|
||||
$elem.children.set_text_force(std::sync::Arc::new(move || format!($text)))
|
||||
};
|
||||
|
||||
($elem:expr, $text:expr, $($inner:tt)*) => {
|
||||
|
||||
+2
-24
@@ -18,9 +18,7 @@ pub struct Text {
|
||||
|
||||
impl ElementWidget for Text<'_> {
|
||||
fn render(&self, writer: &mut crate::Writer) {
|
||||
if let Children::Text(text) = &self.children {
|
||||
writer.write(&text);
|
||||
}
|
||||
writer.write(&self.children.get_text());
|
||||
}
|
||||
fn event(&mut self, event: Event, document: &Document) {
|
||||
call!(self.on_event(event, document));
|
||||
@@ -77,7 +75,7 @@ impl ElementWidget for Button<'_> {
|
||||
self.style.set_state("clicked");
|
||||
call!(self.on_click(event, document));
|
||||
document.render();
|
||||
sleep(100);
|
||||
sleep(65);
|
||||
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,
|
||||
}
|
||||
}
|
||||
pub fn as_size(&self, frame_size: u16, outline: bool) -> u16 {
|
||||
if outline {
|
||||
pub fn as_size(&self, written: u16, frame_size: u16, outline: bool) -> u16 {
|
||||
if *self == Number::Auto || *self == Number::Default {
|
||||
written
|
||||
} else if outline {
|
||||
self.as_size_raw(frame_size) - 2
|
||||
} else {
|
||||
self.as_size_raw(frame_size)
|
||||
}
|
||||
}
|
||||
pub fn as_position_y(&self, used: &Vec<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 {
|
||||
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,
|
||||
|
||||
+26
-4
@@ -1,5 +1,7 @@
|
||||
use std::io::{stdout, Write};
|
||||
|
||||
use crossterm::terminal::disable_raw_mode;
|
||||
|
||||
pub struct Frame {
|
||||
used: Vec<u16>,
|
||||
width: u16,
|
||||
@@ -21,9 +23,21 @@ impl Frame {
|
||||
pub fn render(&mut self, focused: bool, element: &crate::Element) {
|
||||
let style = element.get_style().get(focused);
|
||||
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) {
|
||||
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(
|
||||
focused,
|
||||
style.clone(),
|
||||
@@ -31,9 +45,17 @@ impl Frame {
|
||||
(self.width, self.height),
|
||||
);
|
||||
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 {
|
||||
*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