widget view

This commit is contained in:
2026-01-05 17:01:09 -05:00
parent 07de7c8722
commit d11107f965
7 changed files with 130 additions and 15 deletions

View File

@@ -90,8 +90,8 @@ impl<W: ?Sized> Drop for WidgetHandle<W> {
} }
} }
pub trait WidgetIdFn<State, W: ?Sized = dyn Widget>: FnOnce(&mut State) -> WidgetRef<W> {} pub trait WidgetIdFn<Rsc, W: ?Sized = dyn Widget>: FnOnce(&mut Rsc) -> WidgetRef<W> {}
impl<State, W: ?Sized, F: FnOnce(&mut State) -> WidgetRef<W>> WidgetIdFn<State, W> for F {} impl<Rsc, W: ?Sized, F: FnOnce(&mut Rsc) -> WidgetRef<W>> WidgetIdFn<Rsc, W> for F {}
pub trait IdLike { pub trait IdLike {
type Widget: ?Sized; type Widget: ?Sized;

View File

@@ -5,12 +5,14 @@ mod data;
mod handle; mod handle;
mod like; mod like;
mod tag; mod tag;
mod view;
mod widgets; mod widgets;
pub use data::*; pub use data::*;
pub use handle::*; pub use handle::*;
pub use like::*; pub use like::*;
pub use tag::*; pub use tag::*;
pub use view::*;
pub use widgets::*; pub use widgets::*;
pub trait Widget: Any { pub trait Widget: Any {

View File

@@ -5,16 +5,16 @@ use std::marker::Unsize;
pub struct WidgetTag; pub struct WidgetTag;
impl<Rsc: HasUi, W: Widget> WidgetLike<Rsc, WidgetTag> for W { impl<Rsc: HasUi, W: Widget> WidgetLike<Rsc, WidgetTag> for W {
type Widget = W; type Widget = W;
fn add(self, state: &mut Rsc) -> WidgetRef<W> { fn add(self, rsc: &mut Rsc) -> WidgetRef<W> {
state.ui_mut().widgets.add_weak(self) rsc.ui_mut().widgets.add_weak(self)
} }
} }
pub struct FnTag; pub struct FnTag;
impl<Rsc: HasUi, W: Widget, F: FnOnce(&mut Rsc) -> W> WidgetLike<Rsc, FnTag> for F { impl<Rsc: HasUi, W: Widget, F: FnOnce(&mut Rsc) -> W> WidgetLike<Rsc, FnTag> for F {
type Widget = W; type Widget = W;
fn add(self, state: &mut Rsc) -> WidgetRef<W> { fn add(self, rsc: &mut Rsc) -> WidgetRef<W> {
self(state).add(state) self(rsc).add(rsc)
} }
} }
@@ -26,26 +26,34 @@ pub struct FnTraitTag;
impl<Rsc: HasUi, T: WidgetFnTrait<Rsc>> WidgetLike<Rsc, FnTraitTag> for T { impl<Rsc: HasUi, T: WidgetFnTrait<Rsc>> WidgetLike<Rsc, FnTraitTag> for T {
type Widget = T::Widget; type Widget = T::Widget;
#[track_caller] #[track_caller]
fn add(self, state: &mut Rsc) -> WidgetRef<T::Widget> { fn add(self, rsc: &mut Rsc) -> WidgetRef<T::Widget> {
self.run(state).add(state) self.run(rsc).add(rsc)
} }
} }
pub struct IdTag; pub struct RefTag;
impl<Rsc: HasUi, W: ?Sized + Widget + Unsize<dyn Widget>> WidgetLike<Rsc, IdTag> for WidgetRef<W> { impl<Rsc: HasUi, W: ?Sized + Widget + Unsize<dyn Widget>> WidgetLike<Rsc, RefTag> for WidgetRef<W> {
type Widget = W; type Widget = W;
fn add(self, _: &mut Rsc) -> WidgetRef<W> { fn add(self, _: &mut Rsc) -> WidgetRef<W> {
self self
} }
} }
pub struct IdFnTag; pub struct RefFnTag;
impl<Rsc: HasUi, W: ?Sized + Widget + Unsize<dyn Widget>, F: FnOnce(&mut Rsc) -> WidgetRef<W>> impl<Rsc: HasUi, W: ?Sized + Widget + Unsize<dyn Widget>, F: FnOnce(&mut Rsc) -> WidgetRef<W>>
WidgetLike<Rsc, IdFnTag> for F WidgetLike<Rsc, RefFnTag> for F
{ {
type Widget = W; type Widget = W;
fn add(self, state: &mut Rsc) -> WidgetRef<W> { fn add(self, rsc: &mut Rsc) -> WidgetRef<W> {
self(state) self(rsc)
}
}
pub struct ViewTag;
impl<Rsc: HasUi, V: WidgetView> WidgetLike<Rsc, ViewTag> for V {
type Widget = V::Widget;
fn add(self, _: &mut Rsc) -> WidgetRef<Self::Widget> {
self.root()
} }
} }

16
core/src/widget/view.rs Normal file
View File

@@ -0,0 +1,16 @@
use std::marker::Unsize;
use crate::{Widget, WidgetRef};
pub trait WidgetView {
type Widget: Widget + ?Sized + Unsize<dyn Widget>;
fn root(&self) -> WidgetRef<Self::Widget>;
}
pub trait HasWidget {
type Widget: Widget + ?Sized + Unsize<dyn Widget>;
}
impl<W: Widget + Unsize<dyn Widget>> HasWidget for WidgetRef<W> {
type Widget = W;
}

View File

@@ -18,7 +18,7 @@ impl DefaultAppState for State {
ctx.task.update(move |_, rsc| { ctx.task.update(move |_, rsc| {
let rect = rect(rsc); let rect = rect(rsc);
if rect.color == Color::RED { if rect.color == Color::RED {
rect.color = Color::GREEN; rect.color = Color::BLUE;
} else { } else {
rect.color = Color::RED; rect.color = Color::RED;
} }

46
examples/view.rs Normal file
View File

@@ -0,0 +1,46 @@
use iris::prelude::*;
fn main() {
DefaultApp::<State>::run();
}
#[derive(DefaultUiState)]
struct State {
ui_state: DefaultUiState,
}
type Rsc = DefaultRsc<State>;
#[derive(Clone, Copy, WidgetView)]
struct Test {
#[root]
root: WidgetRef<Rect>,
}
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<Self>, _: Proxy<Self::Event>) -> Self {
let test = Test::new(rsc);
test.on(CursorSense::click(), move |_, rsc| {
test.toggle(rsc);
})
.set_root(rsc);
Self { ui_state }
}
}

View File

@@ -151,3 +151,46 @@ pub fn derive_default_ui_state(input: TokenStream) -> TokenStream {
}); });
output.into() 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()
}