diff --git a/README.md b/README.md
index 157a9d4..4b28b99 100644
--- a/README.md
+++ b/README.md
@@ -21,14 +21,15 @@
```rust
-use osui::{style::Transform, Screen};
+use osui::prelude::*;
fn main() -> std::io::Result<()> {
let screen = Screen::new();
- screen
- .draw(format!("Hello, World"))
- .component(Transform::center());
+ rsx! {
+ "Hello, World"
+ }
+ .draw(&screen);
screen.run()
}
diff --git a/src/elements/div.rs b/src/elements/div.rs
index f559784..8806ad7 100644
--- a/src/elements/div.rs
+++ b/src/elements/div.rs
@@ -29,7 +29,7 @@ impl Element for Div {
if let Some(t) = elem.get() {
scope.set_transform(&t);
}
- elem.0.lock().unwrap().render(scope);
+ elem.get_elem().render(scope);
if let Some(t) = elem.get() {
scope.set_transform(&t);
}
@@ -40,7 +40,7 @@ impl Element for Div {
t.y += transform.y + transform.py;
scope.draw();
- elem.0.lock().unwrap().after_render(scope);
+ elem.get_elem().after_render(scope);
}
scope.set_parent_size(w, h);
self.size = (transform.width, transform.height);
diff --git a/src/elements/flex.rs b/src/elements/flex.rs
index 516007e..ab143dd 100644
--- a/src/elements/flex.rs
+++ b/src/elements/flex.rs
@@ -38,7 +38,7 @@ impl Element for FlexRow {
if let Some(t) = elem.get() {
scope.set_transform(&t);
}
- elem.0.lock().unwrap().render(scope);
+ elem.get_elem().render(scope);
if let Some(t) = elem.get() {
scope.set_transform(&t);
}
@@ -53,7 +53,7 @@ impl Element for FlexRow {
scope.draw();
- elem.0.lock().unwrap().after_render(scope);
+ elem.get_elem().after_render(scope);
}
scope.set_parent_size(w, h);
self.size = (transform.width, transform.height);
@@ -104,7 +104,7 @@ impl Element for FlexCol {
if let Some(t) = elem.get() {
scope.set_transform(&t);
}
- elem.0.lock().unwrap().render(scope);
+ elem.get_elem().render(scope);
if let Some(t) = elem.get() {
scope.set_transform(&t);
}
@@ -119,7 +119,7 @@ impl Element for FlexCol {
scope.draw();
- elem.0.lock().unwrap().after_render(scope);
+ elem.get_elem().after_render(scope);
}
scope.set_parent_size(w, h);
self.size = (transform.width, transform.height);
diff --git a/src/frontend.rs b/src/frontend.rs
index d9fac25..58d4eff 100644
--- a/src/frontend.rs
+++ b/src/frontend.rs
@@ -2,12 +2,14 @@ use std::sync::Arc;
use crate::{
state::DependencyHandler,
- widget::{Widget, WidgetLoad},
+ widget::{StaticWidget, Widget, WidgetLoad},
Screen,
};
pub enum RsxElement {
- Element(
+ Element(Arc, Rsx),
+
+ DynElement(
Box WidgetLoad + Send + Sync>,
Vec>,
Rsx,
@@ -24,13 +26,13 @@ impl Rsx {
pub fn draw_parent(self, screen: &Arc, parent: Option>) {
for rsx_elem in self.0 {
match rsx_elem {
- RsxElement::Element(f, dep, child) => {
+ RsxElement::DynElement(f, dep, child) => {
let w = if let Some(parent) = &parent {
- let w = screen.draw_box(f);
- parent.0.lock().unwrap().draw_child(&w);
+ let w = screen.draw_box_dyn(f);
+ parent.get_elem().draw_child(&w);
w
} else {
- screen.draw_box(f)
+ screen.draw_box_dyn(f)
};
for d in dep {
@@ -38,6 +40,17 @@ impl Rsx {
}
child.draw_parent(screen, Some(w.clone()));
}
+
+ RsxElement::Element(ws, child) => {
+ let w = Arc::new(Widget::Static(ws));
+ screen.draw_widget(w.clone());
+
+ if let Some(parent) = &parent {
+ parent.get_elem().draw_child(&w);
+ }
+
+ child.draw_parent(screen, Some(w.clone()));
+ }
}
}
}
@@ -48,8 +61,15 @@ impl Rsx {
dependencies: Vec>,
children: Rsx,
) {
- self.0
- .push(RsxElement::Element(Box::new(load), dependencies, children));
+ self.0.push(RsxElement::DynElement(
+ Box::new(load),
+ dependencies,
+ children,
+ ));
+ }
+
+ pub fn create_element_static(&mut self, element: Arc, children: Rsx) {
+ self.0.push(RsxElement::Element(element, children));
}
pub fn expand(&mut self, other: &mut Rsx) {
diff --git a/src/lib.rs b/src/lib.rs
index 7530411..4786975 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex};
use crate::{
extensions::{Extension, Handler},
render_scope::RenderScope,
- widget::{Element, Widget, WidgetLoad},
+ widget::{BoxedElement, DynWidget, Element, StaticWidget, Widget, WidgetLoad},
};
pub mod elements;
@@ -53,20 +53,38 @@ impl Screen {
})
}
- pub fn draw WidgetLoad + 'static + Send + Sync>(
- self: &Arc,
- element: F,
- ) -> Arc {
- let w = Arc::new(Widget::new(Box::new(element)));
+ pub fn draw(self: &Arc, element: E) -> Arc {
+ let w = Arc::new(Widget::Static(Arc::new(StaticWidget::new(Box::new(
+ element,
+ )))));
self.widgets.lock().unwrap().push(w.clone());
w
}
- pub fn draw_box(
+ pub fn draw_box(self: &Arc, element: BoxedElement) -> Arc {
+ let w = Arc::new(Widget::Static(Arc::new(StaticWidget::new(element))));
+ self.widgets.lock().unwrap().push(w.clone());
+ w
+ }
+
+ pub fn draw_widget(self: &Arc, widget: Arc) {
+ self.widgets.lock().unwrap().push(widget);
+ }
+
+ pub fn draw_dyn WidgetLoad + 'static + Send + Sync>(
+ self: &Arc,
+ element: F,
+ ) -> Arc {
+ let w = Arc::new(Widget::Dynamic(Arc::new(DynWidget::new(element))));
+ self.widgets.lock().unwrap().push(w.clone());
+ w
+ }
+
+ pub fn draw_box_dyn(
self: &Arc,
element: Box WidgetLoad + Send + Sync>,
) -> Arc {
- let w = Arc::new(Widget::new(Box::new(element)));
+ let w = Arc::new(Widget::Dynamic(Arc::new(DynWidget::new(element))));
self.widgets.lock().unwrap().push(w.clone());
w
}
@@ -122,14 +140,14 @@ impl Screen {
ext.lock().unwrap().render_widget(&mut scope, elem);
}
- elem.0.lock().unwrap().render(&mut scope);
+ elem.get_elem().render(&mut scope);
if let Some(t) = elem.get() {
scope.set_transform(&t);
}
scope.draw();
- elem.0.lock().unwrap().after_render(&mut scope);
+ elem.get_elem().after_render(&mut scope);
}
elem.auto_refresh();
diff --git a/src/macros.rs b/src/macros.rs
index 12f045d..dbdc403 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -145,6 +145,12 @@ macro_rules! rsx {
#[macro_export]
macro_rules! rsx_inner {
+ // static
+ ($r:expr, $(@$comp:expr;)* static $s:literal $($rest:tt)*) => {
+ $r.create_element_static(std::sync::Arc::new($crate::widget::StaticWidget::new(Box::new(format!($s)))) $(.component($comp))* .clone(), $crate::frontend::Rsx(Vec::new()));
+ $crate::rsx_inner! { $r, $($rest)* };
+ };
+
($r:expr, $(%$dep:ident)* $(@$comp:expr;)* $s:literal $($rest:tt)*) => {
$r.create_element({ $(let $dep = $dep.clone();)* move || {
$crate::widget::WidgetLoad::new(format!($s)) $(.component($comp))*
@@ -152,6 +158,18 @@ macro_rules! rsx_inner {
$crate::rsx_inner! { $r, $($rest)* };
};
+ // static
+ ($r:expr, $(%$dep:ident)* $(@$comp:expr;)* static $name:path { $($inner:tt)* } ($($e:expr),*) $($rest:tt)*) => {
+ $r.create_element_static(std::sync::Arc::new($crate::widget::StaticWidget::new(Box::new(<$name>::new($($e),*)))) $(.component($comp))* .clone(), $crate::frontend::Rsx(Vec::new()));
+ $crate::rsx_inner! { $r, $($rest)* };
+ };
+
+ // static
+ ($r:expr, $(@$comp:expr;)* static $name:path { $($inner:tt)* } $($rest:tt)*) => {
+ $r.create_element_static(std::sync::Arc::new($crate::widget::StaticWidget::new(Box::new(<$name>::new()))) $(.component($comp))* .clone(), $crate::frontend::Rsx(Vec::new()));
+ $crate::rsx_inner! { $r, $($rest)* };
+ };
+
($r:expr, $(%$dep:ident)* $(@$comp:expr;)* $name:path { $($inner:tt)* } ($($e:expr),*) $($rest:tt)*) => {
$r.create_element({ $(let $dep = $dep.clone();)* move || {
$crate::widget::WidgetLoad::new(<$name>::new($($e),*)) $(.component($comp))*
diff --git a/src/main.rs b/src/main.rs
index 7dce5e6..36a576b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,33 +2,21 @@ use osui::prelude::*;
fn main() -> std::io::Result<()> {
let screen = Screen::new();
+ screen.extension(VelocityExtension);
- app().draw(&screen);
+ let count = use_state(0);
+
+ rsx! {
+ @Velocity(20, 0);
+ @Transform::new();
+ static "Count: {count}"
+ }
+ .draw(&screen);
+
+ std::thread::spawn(move || loop {
+ **count.get() += 1;
+ std::thread::sleep(std::time::Duration::from_millis(100));
+ });
screen.run()
}
-
-fn app() -> Rsx {
- rsx! {
- @Transform::new().padding(3, 3);
- @Style { background: Background::RoundedOutline(0xff0000), foreground: None };
- Div {
- @Transform::new().padding(1, 1);
- @Style { background: Background::RoundedOutline(0x00ff00), foreground: None };
- FlexCol {
- @Transform::new().padding(1, 1);
- "Hello, World!"
- @Transform::new().padding(1, 1);
- "Hello, World!"
- @Transform::new().padding(1, 1);
- "Hello, World!"
- @Transform::new().padding(1, 1);
- "Hello, World!"
- @Transform::new().padding(1, 1);
- "Hello, World!"
- @Transform::new().padding(1, 1);
- "Hello, World!"
- } (2)
- }
- }
-}
diff --git a/src/widget.rs b/src/widget.rs
index 5c87cdf..33dd49e 100644
--- a/src/widget.rs
+++ b/src/widget.rs
@@ -1,7 +1,7 @@
use std::{
any::{Any, TypeId},
collections::HashMap,
- sync::{Arc, Mutex},
+ sync::{Arc, Mutex, MutexGuard},
};
use crate::{render_scope::RenderScope, state::DependencyHandler};
@@ -25,15 +25,190 @@ pub trait Component: Send + Sync {
fn as_any_mut(&mut self) -> &mut dyn Any;
}
-pub struct Widget(
- pub Mutex,
+pub struct WidgetLoad(BoxedElement, HashMap);
+
+impl WidgetLoad {
+ pub fn new(e: E) -> Self {
+ Self(Box::new(e), HashMap::new())
+ }
+
+ pub fn component(mut self, c: C) -> Self {
+ self.1.entry(c.type_id()).or_insert_with(|| Box::new(c));
+ self
+ }
+
+ pub fn set_component(mut self, c: C) -> Self {
+ self.1.insert(c.type_id(), Box::new(c));
+ self
+ }
+
+ pub fn get(&self) -> Option {
+ self.1
+ .get(&TypeId::of::())
+ .and_then(|c| c.as_any().downcast_ref::())
+ .map(|c| c.clone())
+ }
+}
+
+pub enum Widget {
+ Static(Arc),
+ Dynamic(Arc),
+}
+
+// === Trait Implementation ===
+impl Widget {
+ pub fn get_elem(&self) -> MutexGuard {
+ match self {
+ Widget::Static(w) => w.get_elem(),
+ Widget::Dynamic(w) => w.get_elem(),
+ }
+ }
+
+ pub fn after_render(&self) {
+ match self {
+ Widget::Static(w) => w.after_render(),
+ Widget::Dynamic(w) => w.after_render(),
+ }
+ }
+
+ pub fn component(self: &Arc, c: C) -> &Arc {
+ match &**self {
+ Widget::Static(w) => {
+ w.component(c);
+ }
+ Widget::Dynamic(w) => {
+ w.component(c);
+ }
+ }
+ self
+ }
+
+ pub fn set_component(self: &Arc, c: C) -> &Arc {
+ match &**self {
+ Widget::Static(w) => {
+ w.set_component(c);
+ }
+ Widget::Dynamic(w) => {
+ w.set_component(c);
+ }
+ }
+ self
+ }
+
+ pub fn get(&self) -> Option {
+ match self {
+ Widget::Static(w) => w.get(),
+ Widget::Dynamic(w) => w.get(),
+ }
+ }
+
+ pub fn inject WidgetLoad + 'static + Send + Sync>(
+ self: &Arc,
+ f: F,
+ ) {
+ match &**self {
+ Widget::Dynamic(w) => {
+ w.inject(f);
+ }
+ Widget::Static(_) => {}
+ }
+ }
+
+ pub fn refresh(self: &Arc) {
+ match &**self {
+ Widget::Dynamic(w) => {
+ w.refresh();
+ }
+ Widget::Static(_) => {}
+ }
+ }
+
+ pub fn auto_refresh(self: &Arc) {
+ match &**self {
+ Widget::Dynamic(w) => {
+ w.auto_refresh();
+ }
+ Widget::Static(_) => {}
+ }
+ }
+
+ pub fn dependency(self: &Arc, d: D) -> &Arc {
+ match &**self {
+ Widget::Dynamic(w) => {
+ w.dependency(d);
+ }
+ Widget::Static(_) => {}
+ }
+ self
+ }
+
+ pub fn dependency_box(self: &Arc, d: Box) -> &Arc {
+ match &**self {
+ Widget::Dynamic(w) => {
+ w.dependency_box(d);
+ }
+ Widget::Static(_) => {}
+ }
+ self
+ }
+}
+
+// === Static Widget ===
+pub struct StaticWidget(Mutex, Mutex>);
+
+impl StaticWidget {
+ fn after_render(&self) {}
+ fn get_elem(&self) -> MutexGuard {
+ self.0.lock().unwrap()
+ }
+}
+
+impl StaticWidget {
+ pub fn new(e: BoxedElement) -> Self {
+ Self(Mutex::new(e), Mutex::new(HashMap::new()))
+ }
+
+ pub fn component(self: &Arc, c: C) -> &Arc {
+ self.1
+ .lock()
+ .unwrap()
+ .entry(c.type_id())
+ .or_insert_with(|| Box::new(c));
+ self
+ }
+
+ pub fn set_component(self: &Arc, c: C) -> &Arc {
+ self.1.lock().unwrap().insert(c.type_id(), Box::new(c));
+ self
+ }
+
+ pub fn get(&self) -> Option {
+ self.1
+ .lock()
+ .unwrap()
+ .get(&TypeId::of::())
+ .and_then(|c| c.as_any().downcast_ref::())
+ .map(Clone::clone)
+ }
+}
+
+// === Dynamic Widget ===
+pub struct DynWidget(
+ Mutex,
Mutex>,
Mutex WidgetLoad + Send + Sync>>,
Mutex>>,
Mutex