Dynamic Widget system

This commit is contained in:
2025-07-28 15:02:22 +02:00
parent 00f7493741
commit 02bdca151b
7 changed files with 82 additions and 224 deletions
-93
View File
@@ -1,93 +0,0 @@
use std::sync::Arc;
use crate::widget::Element;
pub trait DependencyHandler: std::fmt::Debug + Send + Sync {
fn add(&self);
fn check(&self) -> bool;
}
pub struct Dependency<T> {
handlers: Vec<std::sync::Arc<dyn DependencyHandler>>,
element: T,
element_reset: Box<dyn FnMut() -> T + Send + Sync>,
}
impl<T> Dependency<T> {
pub fn new<F: FnMut() -> T + Send + Sync + 'static>(mut f: F) -> Self {
Self {
handlers: Vec::new(),
element: f(),
element_reset: Box::new(f),
}
}
pub fn from(
mut f: Box<dyn FnMut() -> T + Send + Sync>,
handlers: Vec<std::sync::Arc<dyn DependencyHandler>>,
) -> Self {
for h in &handlers {
h.add();
}
Self {
handlers,
element: f(),
element_reset: f,
}
}
pub fn auto_reset(&mut self) {
for i in &self.handlers {
if i.check() {
self.element = (self.element_reset)();
}
}
}
pub fn add<D: DependencyHandler + 'static>(mut self, dep: D) -> Self {
dep.add();
self.handlers.push(Arc::new(dep));
self
}
}
impl Element for Dependency<Box<dyn Element>> {
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
self.element.render(scope);
self.auto_reset();
}
fn after_render(&mut self, scope: &crate::render_scope::RenderScope) {
self.element.after_render(scope);
self.auto_reset();
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
impl<T: Element + 'static> Element for Dependency<T> {
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
self.element.render(scope);
self.auto_reset();
}
fn after_render(&mut self, scope: &crate::render_scope::RenderScope) {
self.element.after_render(scope);
self.auto_reset();
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
-1
View File
@@ -1,6 +1,5 @@
pub mod div; pub mod div;
pub mod grid; pub mod grid;
pub mod state;
use crate::Element; use crate::Element;
-94
View File
@@ -1,94 +0,0 @@
use std::{
fmt::{Debug, Display, Formatter, Result as FmtResult},
ops::{Deref, DerefMut},
sync::{Arc, Mutex, MutexGuard},
};
use crate::dependency::DependencyHandler;
#[derive(Debug, Clone)]
pub struct State<T> {
inner: Arc<Mutex<Inner<T>>>,
}
#[derive(Debug)]
pub struct Inner<T> {
value: T,
dependencies: usize,
changed: usize,
}
pub fn use_state<T>(v: T) -> State<T> {
State {
inner: Arc::new(Mutex::new(Inner {
value: v,
dependencies: 0,
changed: 0,
})),
}
}
impl<T: Clone> State<T> {
/// Gets the cloned value, recommended for preventing deadlocks
pub fn get_dl(&self) -> T {
self.inner.lock().unwrap().value.clone()
}
}
impl<T> State<T> {
/// Gets a lock on the state for read/write access.
pub fn get(&self) -> MutexGuard<'_, Inner<T>> {
self.inner.lock().unwrap()
}
/// Sets the value and marks it as changed.
pub fn set(&self, v: T) {
let mut inner = self.inner.lock().unwrap();
inner.value = v;
inner.changed = inner.dependencies;
}
/// Marks the state as updated.
pub fn update(&self) {
let mut inner = self.inner.lock().unwrap();
inner.changed = inner.dependencies;
}
}
impl<T: Debug + Send + Sync> DependencyHandler for State<T> {
fn check(&self) -> bool {
let mut inner = self.inner.lock().unwrap();
let i = inner.changed > 0;
if i {
inner.changed -= 1;
}
i
}
fn add(&self) {
let mut inner = self.inner.lock().unwrap();
inner.dependencies += 1;
}
}
impl<T: Display> Display for State<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let inner = self.inner.lock().unwrap();
write!(f, "{}", inner.value)
}
}
impl<T> Deref for Inner<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T> DerefMut for Inner<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.changed = self.dependencies;
&mut self.value
}
}
+3 -8
View File
@@ -1,14 +1,10 @@
use std::sync::Arc; use std::sync::Arc;
use crate::{ use crate::{state::DependencyHandler, widget::WidgetLoad, Screen};
dependency::{Dependency, DependencyHandler},
widget::{Element, Widget},
Screen,
};
pub struct Rsx( pub struct Rsx(
pub Vec<( pub Vec<(
Box<dyn FnMut() -> Box<dyn Element> + Send + Sync>, Box<dyn FnMut() -> WidgetLoad + Send + Sync>,
Vec<std::sync::Arc<dyn DependencyHandler>>, Vec<std::sync::Arc<dyn DependencyHandler>>,
)>, )>,
); );
@@ -16,8 +12,7 @@ pub struct Rsx(
impl Rsx { impl Rsx {
pub fn draw(self, screen: &Arc<Screen>) { pub fn draw(self, screen: &Arc<Screen>) {
for i in self.0 { for i in self.0 {
let d = Dependency::from(i.0, i.1); screen.draw_box(i.0);
screen.draw_widget(Widget::new(Box::new(d)));
} }
} }
} }
+13 -10
View File
@@ -3,16 +3,15 @@ use std::sync::{Arc, Mutex};
use crate::{ use crate::{
extensions::{Extension, Handler}, extensions::{Extension, Handler},
render_scope::RenderScope, render_scope::RenderScope,
style::Transform, widget::{Element, Widget, WidgetLoad},
widget::{Element, Widget},
}; };
pub mod dependency;
pub mod elements; pub mod elements;
pub mod extensions; pub mod extensions;
pub mod frontend; pub mod frontend;
pub mod macros; pub mod macros;
pub mod render_scope; pub mod render_scope;
pub mod state;
pub mod style; pub mod style;
pub mod utils; pub mod utils;
pub mod widget; pub mod widget;
@@ -41,14 +40,20 @@ impl Screen {
}) })
} }
pub fn draw<E: Element + 'static>(self: &Arc<Self>, element: E) -> Arc<Widget> { pub fn draw<F: FnMut() -> WidgetLoad + 'static + Send + Sync>(
self: &Arc<Self>,
element: F,
) -> Arc<Widget> {
let w = Arc::new(Widget::new(Box::new(element))); let w = Arc::new(Widget::new(Box::new(element)));
self.widgets.lock().unwrap().push(w.clone()); self.widgets.lock().unwrap().push(w.clone());
w w
} }
pub fn draw_widget(self: &Arc<Self>, w: Widget) -> Arc<Widget> { pub fn draw_box(
let w = Arc::new(w); self: &Arc<Self>,
element: Box<dyn FnMut() -> WidgetLoad + Send + Sync>,
) -> Arc<Widget> {
let w = Arc::new(Widget::new(Box::new(element)));
self.widgets.lock().unwrap().push(w.clone()); self.widgets.lock().unwrap().push(w.clone());
w w
} }
@@ -61,10 +66,6 @@ impl Screen {
} }
pub fn run(self: &Arc<Self>) -> std::io::Result<()> { pub fn run(self: &Arc<Self>) -> std::io::Result<()> {
for elem in self.widgets.lock().unwrap().iter() {
elem.component(Transform::new());
}
for ext in self.extensions.lock().unwrap().iter() { for ext in self.extensions.lock().unwrap().iter() {
ext.lock().unwrap().init(self.clone()); ext.lock().unwrap().init(self.clone());
} }
@@ -104,6 +105,8 @@ impl Screen {
scope.draw(); scope.draw();
elem.0.lock().unwrap().after_render(&scope); elem.0.lock().unwrap().after_render(&scope);
elem.auto_refresh();
} }
} }
Ok(()) Ok(())
+10 -15
View File
@@ -1,16 +1,7 @@
use std::sync::Arc; use osui::{state::use_state, widget::WidgetLoad, Screen};
use osui::{elements::state::use_state, frontend::Rsx, rsx, Screen};
fn main() -> std::io::Result<()> { fn main() -> std::io::Result<()> {
let screen = Screen::new(); let screen = Screen::new();
app().draw(&screen);
screen.run()
}
fn app() -> Rsx {
let count = use_state(0); let count = use_state(0);
std::thread::spawn({ std::thread::spawn({
@@ -18,12 +9,16 @@ fn app() -> Rsx {
move || loop { move || loop {
std::thread::sleep(std::time::Duration::from_millis(50)); std::thread::sleep(std::time::Duration::from_millis(50));
**count.get() += 1; **count.get() += 1;
count.update();
} }
}); });
rsx! { screen
%count .draw({
@Transform::center(); let count = count.clone();
"{count}" move || WidgetLoad::new(format!("Hello, World {count}"))
} })
.dependency(count);
screen.run()
} }
+56 -3
View File
@@ -4,7 +4,7 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use crate::render_scope::RenderScope; use crate::{render_scope::RenderScope, state::DependencyHandler};
pub type BoxedElement = Box<dyn Element + Send + Sync>; pub type BoxedElement = Box<dyn Element + Send + Sync>;
pub type BoxedComponent = Box<dyn Component + Send + Sync>; pub type BoxedComponent = Box<dyn Component + Send + Sync>;
@@ -26,11 +26,39 @@ pub trait Component: Send + Sync {
pub struct Widget( pub struct Widget(
pub Mutex<BoxedElement>, pub Mutex<BoxedElement>,
Mutex<HashMap<TypeId, BoxedComponent>>, Mutex<HashMap<TypeId, BoxedComponent>>,
Mutex<Box<dyn FnMut() -> WidgetLoad + Send + Sync>>,
Mutex<Vec<Box<dyn DependencyHandler>>>,
); );
impl Widget { impl Widget {
pub fn new(e: Box<dyn Element>) -> Self { pub fn new<F: FnMut() -> WidgetLoad + 'static + Send + Sync>(mut e: F) -> Self {
Self(Mutex::new(e), Mutex::new(HashMap::new())) let wl = e();
Self(
Mutex::new(wl.0),
Mutex::new(wl.1),
Mutex::new(Box::new(e)),
Mutex::new(Vec::new()),
)
}
pub fn refresh(self: &Arc<Self>) {
let w = (self.2.lock().unwrap())();
*self.0.lock().unwrap() = w.0;
*self.1.lock().unwrap() = w.1;
}
pub fn auto_refresh(self: &Arc<Self>) {
for d in self.3.lock().unwrap().iter() {
if d.check() {
self.refresh();
}
}
}
pub fn dependency<D: DependencyHandler + 'static>(self: &Arc<Self>, d: D) -> &Arc<Self> {
d.add();
self.3.lock().unwrap().push(Box::new(d));
self
} }
pub fn component<C: Component + 'static>(self: &Arc<Self>, c: C) -> &Arc<Self> { pub fn component<C: Component + 'static>(self: &Arc<Self>, c: C) -> &Arc<Self> {
@@ -56,3 +84,28 @@ impl Widget {
.map(|c| c.clone()) .map(|c| c.clone())
} }
} }
pub struct WidgetLoad(BoxedElement, HashMap<TypeId, BoxedComponent>);
impl WidgetLoad {
pub fn new<E: Element + 'static>(e: E) -> Self {
Self(Box::new(e), HashMap::new())
}
pub fn component<C: Component + 'static>(mut self, c: C) -> Self {
self.1.entry(c.type_id()).or_insert_with(|| Box::new(c));
self
}
pub fn set_component<C: Component + 'static>(mut self, c: C) -> Self {
self.1.insert(c.type_id(), Box::new(c));
self
}
pub fn get<C: Component + 'static + Clone>(&self) -> Option<C> {
self.1
.get(&TypeId::of::<C>())
.and_then(|c| c.as_any().downcast_ref::<C>())
.map(|c| c.clone())
}
}