diff --git a/core/src/widget/handle.rs b/core/src/widget/handle.rs index 3de2b73..98e6951 100644 --- a/core/src/widget/handle.rs +++ b/core/src/widget/handle.rs @@ -90,8 +90,8 @@ impl Drop for WidgetHandle { } } -pub trait WidgetIdFn: FnOnce(&mut State) -> WidgetRef {} -impl WidgetRef> WidgetIdFn for F {} +pub trait WidgetIdFn: FnOnce(&mut Rsc) -> WidgetRef {} +impl WidgetRef> WidgetIdFn for F {} pub trait IdLike { type Widget: ?Sized; diff --git a/core/src/widget/mod.rs b/core/src/widget/mod.rs index 89348ad..b47cf67 100644 --- a/core/src/widget/mod.rs +++ b/core/src/widget/mod.rs @@ -5,12 +5,14 @@ mod data; mod handle; mod like; mod tag; +mod view; mod widgets; pub use data::*; pub use handle::*; pub use like::*; pub use tag::*; +pub use view::*; pub use widgets::*; pub trait Widget: Any { diff --git a/core/src/widget/tag.rs b/core/src/widget/tag.rs index 27432fa..e7c9454 100644 --- a/core/src/widget/tag.rs +++ b/core/src/widget/tag.rs @@ -5,16 +5,16 @@ use std::marker::Unsize; pub struct WidgetTag; impl WidgetLike for W { type Widget = W; - fn add(self, state: &mut Rsc) -> WidgetRef { - state.ui_mut().widgets.add_weak(self) + fn add(self, rsc: &mut Rsc) -> WidgetRef { + rsc.ui_mut().widgets.add_weak(self) } } pub struct FnTag; impl W> WidgetLike for F { type Widget = W; - fn add(self, state: &mut Rsc) -> WidgetRef { - self(state).add(state) + fn add(self, rsc: &mut Rsc) -> WidgetRef { + self(rsc).add(rsc) } } @@ -26,26 +26,34 @@ pub struct FnTraitTag; impl> WidgetLike for T { type Widget = T::Widget; #[track_caller] - fn add(self, state: &mut Rsc) -> WidgetRef { - self.run(state).add(state) + fn add(self, rsc: &mut Rsc) -> WidgetRef { + self.run(rsc).add(rsc) } } -pub struct IdTag; -impl> WidgetLike for WidgetRef { +pub struct RefTag; +impl> WidgetLike for WidgetRef { type Widget = W; fn add(self, _: &mut Rsc) -> WidgetRef { self } } -pub struct IdFnTag; +pub struct RefFnTag; impl, F: FnOnce(&mut Rsc) -> WidgetRef> - WidgetLike for F + WidgetLike for F { type Widget = W; - fn add(self, state: &mut Rsc) -> WidgetRef { - self(state) + fn add(self, rsc: &mut Rsc) -> WidgetRef { + self(rsc) + } +} + +pub struct ViewTag; +impl WidgetLike for V { + type Widget = V::Widget; + fn add(self, _: &mut Rsc) -> WidgetRef { + self.root() } } diff --git a/core/src/widget/view.rs b/core/src/widget/view.rs new file mode 100644 index 0000000..7f56faf --- /dev/null +++ b/core/src/widget/view.rs @@ -0,0 +1,16 @@ +use std::marker::Unsize; + +use crate::{Widget, WidgetRef}; + +pub trait WidgetView { + type Widget: Widget + ?Sized + Unsize; + fn root(&self) -> WidgetRef; +} + +pub trait HasWidget { + type Widget: Widget + ?Sized + Unsize; +} + +impl> HasWidget for WidgetRef { + type Widget = W; +} diff --git a/examples/task.rs b/examples/task.rs index 4503ead..2dafebb 100644 --- a/examples/task.rs +++ b/examples/task.rs @@ -18,7 +18,7 @@ impl DefaultAppState for State { ctx.task.update(move |_, rsc| { let rect = rect(rsc); if rect.color == Color::RED { - rect.color = Color::GREEN; + rect.color = Color::BLUE; } else { rect.color = Color::RED; } diff --git a/examples/view.rs b/examples/view.rs new file mode 100644 index 0000000..5c8bcc3 --- /dev/null +++ b/examples/view.rs @@ -0,0 +1,46 @@ +use iris::prelude::*; + +fn main() { + DefaultApp::::run(); +} + +#[derive(DefaultUiState)] +struct State { + ui_state: DefaultUiState, +} + +type Rsc = DefaultRsc; + +#[derive(Clone, Copy, WidgetView)] +struct Test { + #[root] + root: WidgetRef, +} + +impl Test { + pub fn new(rsc: &mut Rsc) -> Self { + let root = rect(Color::RED).add(rsc); + Self { root } + } + pub fn toggle(&self, rsc: &mut Rsc) { + let rect = (self.root)(rsc); + if rect.color == Color::RED { + rect.color = Color::BLUE; + } else { + rect.color = Color::RED; + } + } +} + +impl DefaultAppState for State { + fn new(ui_state: DefaultUiState, rsc: &mut DefaultRsc, _: Proxy) -> Self { + let test = Test::new(rsc); + + test.on(CursorSense::click(), move |_, rsc| { + test.toggle(rsc); + }) + .set_root(rsc); + + Self { ui_state } + } +} diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 4e859b1..98b13cf 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -151,3 +151,46 @@ pub fn derive_default_ui_state(input: TokenStream) -> TokenStream { }); output.into() } + +#[proc_macro_derive(WidgetView, attributes(root))] +pub fn derive_widget_view(input: TokenStream) -> TokenStream { + let mut output = proc_macro2::TokenStream::new(); + + let state: ItemStruct = parse_macro_input!(input); + + let mut found_attr = false; + let mut state_field = None; + for field in &state.fields { + let Some(attr) = field.attrs.iter().find(|a| a.path().is_ident("root")) else { + continue; + }; + if found_attr { + output.extend( + Error::new(attr.span(), "cannot have more than one root widget") + .into_compile_error(), + ); + continue; + } + found_attr = true; + state_field = Some(field); + } + let Some(field) = state_field else { + output.extend( + Error::new(state.ident.span(), "no root widget field found (#[root])") + .into_compile_error(), + ); + return output.into(); + }; + let sname = &state.ident; + let fname = field.ident.as_ref().unwrap(); + let fty = &field.ty; + output.extend(quote! { + impl iris::core::WidgetView for #sname { + type Widget = <#fty as iris::core::HasWidget>::Widget; + fn root(&self) -> #fty { + self.#fname + } + } + }); + output.into() +}