From bae17235c608fa6cb9ea57ca0a832be4d5245fcb Mon Sep 17 00:00:00 2001 From: Shadow Cat Date: Fri, 19 Dec 2025 21:54:48 -0500 Subject: [PATCH] better global state structure? --- Cargo.lock | 1 + core/src/event/ctx.rs | 39 ++++++++++++++--- core/src/event/manager.rs | 56 ++++++++---------------- core/src/event/mod.rs | 11 +++-- core/src/event/rsc.rs | 64 +++++++++++++++++---------- core/src/ui/mod.rs | 22 ++++++++-- core/src/widget/handle.rs | 10 ++++- core/src/widget/like.rs | 2 +- core/src/widget/tag.rs | 12 +++--- examples/minimal.rs | 18 ++++---- macro/Cargo.toml | 1 + macro/src/lib.rs | 88 ++++++++++++++++++++++++++++++++++++- src/bin/test/main.rs | 78 ++++++++++++++++----------------- src/default/attr.rs | 29 ++++++++----- src/default/input.rs | 2 +- src/default/mod.rs | 91 ++++++++++++--------------------------- src/default/sense.rs | 10 ++--- src/event.rs | 4 +- src/typed.rs | 6 +-- src/widget/image.rs | 2 +- src/widget/text/build.rs | 6 +-- src/widget/text/edit.rs | 5 ++- src/widget/trait_fns.rs | 8 ++-- 23 files changed, 335 insertions(+), 230 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 12b55a5..c77f763 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1068,6 +1068,7 @@ dependencies = [ name = "iris-macro" version = "0.1.0" dependencies = [ + "proc-macro2", "quote", "syn", ] diff --git a/core/src/event/ctx.rs b/core/src/event/ctx.rs index afc689e..1b8969e 100644 --- a/core/src/event/ctx.rs +++ b/core/src/event/ctx.rs @@ -1,7 +1,6 @@ +use crate::{HasEvents, HasState, HasUi, Ui, Widget, WidgetRef}; use std::ops::{Deref, DerefMut}; -use crate::{HasUi, Ui, Widget, WidgetRef}; - pub struct EventCtx<'a, State, Data> { pub state: &'a mut State, pub data: &'a mut Data, @@ -14,21 +13,47 @@ pub struct EventIdCtx<'a, State, Data, W: ?Sized> { } impl Deref for EventIdCtx<'_, State, Data, W> { - type Target = Ui; + type Target = State; fn deref(&self) -> &Self::Target { - self.state.ui_ref() + self.state } } impl DerefMut for EventIdCtx<'_, State, Data, W> { fn deref_mut(&mut self) -> &mut Self::Target { - self.state.ui() + self.state } } -impl<'a, State: HasUi + 'static, Data, W: Widget> EventIdCtx<'a, State, Data, W> { +impl<'a, State: HasUi, Data, W: Widget> EventIdCtx<'a, State, Data, W> { pub fn widget(&mut self) -> &mut W { - &mut self.state.ui()[self.widget] + &mut self.state.get_mut()[self.widget] + } +} + +impl HasUi for EventIdCtx<'_, State, Data, W> { + fn get(&self) -> &Ui { + self.state.ui() + } + + fn get_mut(&mut self) -> &mut Ui { + self.state.ui_mut() + } +} + +impl HasState for EventIdCtx<'_, State, Data, W> { + type State = State; +} + +impl, Data, W: Widget> HasEvents + for EventIdCtx<'_, State, Data, W> +{ + fn get(&self) -> &super::EventManager { + self.state.events() + } + + fn get_mut(&mut self) -> &mut super::EventManager { + self.state.events_mut() } } diff --git a/core/src/event/manager.rs b/core/src/event/manager.rs index 01bc629..14a05c2 100644 --- a/core/src/event/manager.rs +++ b/core/src/event/manager.rs @@ -1,6 +1,6 @@ use crate::{ - ActiveData, Event, EventCtx, EventFn, EventLike, HasRsc, HasUi, IdLike, LayerId, WidgetData, - WidgetId, + ActiveData, Event, EventCtx, EventFn, EventIdCtx, EventLike, IdLike, LayerId, Widget, + WidgetData, WidgetEventFn, WidgetId, WidgetRef, util::{HashMap, TypeMap}, }; use std::{any::TypeId, rc::Rc}; @@ -22,11 +22,11 @@ impl EventManager { self.types.type_or_default() } - pub fn register( + pub fn register( &mut self, - id: I, + id: WidgetRef, event: E, - f: impl for<'a> EventFn::Data<'a>>, + f: impl for<'a> WidgetEventFn::Data<'a>, W>, ) { self.get_type::().register(id, event, f); } @@ -106,17 +106,23 @@ impl Default for TypeEventManager { } impl TypeEventManager { - fn register( + fn register( &mut self, - id: impl IdLike, + widget: WidgetRef, event: impl EventLike, - f: impl for<'a> EventFn>, + f: impl for<'a> WidgetEventFn, W>, ) { let event = event.into_event(); - self.map - .entry(id.id()) - .or_default() - .push((event, Rc::new(f))); + self.map.entry(widget.id()).or_default().push(( + event, + Rc::new(move |ctx| { + f(&mut EventIdCtx { + widget, + state: ctx.state, + data: ctx.data, + }); + }), + )); } pub fn run_fn<'a>( @@ -127,7 +133,7 @@ impl TypeEventManager { move |ctx| { for (e, f) in fs { if e.should_run(ctx.data) { - f(EventCtx { + f(&mut EventCtx { state: ctx.state, data: ctx.data, }) @@ -136,27 +142,3 @@ impl TypeEventManager { } } } - -pub trait HasEvents: Sized { - type State: HasRsc; - - fn events(&mut self) -> &mut EventManager; - - fn register_event( - &mut self, - id: I, - event: E, - f: impl for<'a> EventFn::Data<'a>>, - ) where - Self: HasUi, - { - let id = id.id(); - self.events().register(id, event, f); - self.ui() - .widgets - .data_mut(id) - .unwrap() - .event_mgrs - .insert(EventManager::::type_key::()); - } -} diff --git a/core/src/event/mod.rs b/core/src/event/mod.rs index 3c82473..4223fab 100644 --- a/core/src/event/mod.rs +++ b/core/src/event/mod.rs @@ -28,11 +28,14 @@ impl EventLike for E { } } -pub trait EventFn: Fn(EventCtx) + 'static {} -impl) + 'static, Data> EventFn for F {} +pub trait EventFn: Fn(&mut EventCtx) + 'static {} +impl) + 'static, Data> EventFn for F {} -pub trait WidgetEventFn: Fn(EventIdCtx) + 'static {} -impl) + 'static, Data, W: ?Sized> +pub trait WidgetEventFn: + Fn(&mut EventIdCtx) + 'static +{ +} +impl) + 'static, Data, W: ?Sized> WidgetEventFn for F { } diff --git a/core/src/event/rsc.rs b/core/src/event/rsc.rs index 773349c..26004a7 100644 --- a/core/src/event/rsc.rs +++ b/core/src/event/rsc.rs @@ -1,29 +1,47 @@ -use crate::{Event, EventCtx, EventLike, EventManager, HasEvents, HasUi, IdLike}; +use crate::{ + Event, EventCtx, EventLike, EventManager, HasUi, IdLike, Widget, WidgetEventFn, WidgetRef, +}; -pub trait HasRsc: Sized + 'static { - type Rsc: HasEvents + HasUi; - fn rsc(&self) -> &Self::Rsc; - fn rsc_mut(&mut self) -> &mut Self::Rsc; +pub trait HasState { + type State: HasUi; +} - fn run_event(&mut self, id: impl IdLike, data: &mut ::Data<'_>) - where - Self: 'static, +pub trait HasEvents: Sized + HasState + HasUi { + fn get(&self) -> &EventManager; + fn get_mut(&mut self) -> &mut EventManager; + fn events(&self) -> &EventManager { + HasEvents::get(self) + } + fn events_mut(&mut self) -> &mut EventManager { + HasEvents::get_mut(self) + } + + fn register_event( + &mut self, + id: WidgetRef, + event: E, + f: impl for<'a> WidgetEventFn::Data<'a>, W>, + ) where + Self::State: 'static, { - let f = self.rsc_mut().events().get_type::().run_fn(id); + self.events_mut().register(id, event, f); + self.ui_mut() + .widgets + .data_mut(id) + .unwrap() + .event_mgrs + .insert(EventManager::::type_key::()); + } +} + +pub trait RunEvents: HasEvents + HasState + 'static { + fn run_event( + &mut self, + id: impl IdLike, + data: &mut ::Data<'_>, + ) { + let f = self.events_mut().get_type::().run_fn(id); f(EventCtx { state: self, data }) } - - fn events(&mut self) -> &mut EventManager { - self.rsc_mut().events() - } -} - -impl HasUi for R { - fn ui_ref(&self) -> &crate::Ui { - self.rsc().ui_ref() - } - - fn ui(&mut self) -> &mut crate::Ui { - self.rsc_mut().ui() - } } +impl + 'static> RunEvents for T {} diff --git a/core/src/ui/mod.rs b/core/src/ui/mod.rs index 055ead7..1d119df 100644 --- a/core/src/ui/mod.rs +++ b/core/src/ui/mod.rs @@ -42,9 +42,25 @@ pub struct Ui { resized: bool, } -pub trait HasUi: Sized + 'static { - fn ui_ref(&self) -> &Ui; - fn ui(&mut self) -> &mut Ui; +pub trait HasUi: Sized { + fn get(&self) -> &Ui; + fn get_mut(&mut self) -> &mut Ui; + fn ui(&self) -> &Ui { + self.get() + } + fn ui_mut(&mut self) -> &mut Ui { + self.get_mut() + } +} + +impl HasUi for Ui { + fn get(&self) -> &Ui { + self + } + + fn get_mut(&mut self) -> &mut Ui { + self + } } impl Ui { diff --git a/core/src/widget/handle.rs b/core/src/widget/handle.rs index 001a67e..e23fded 100644 --- a/core/src/widget/handle.rs +++ b/core/src/widget/handle.rs @@ -1,7 +1,7 @@ use std::{marker::Unsize, mem::MaybeUninit, ops::CoerceUnsized, sync::mpsc::Sender}; use crate::{ - Widget, + HasUi, Widget, util::{RefCounter, SlotId}, }; @@ -143,3 +143,11 @@ impl std::fmt::Debug for WidgetHandle { self.id.fmt(f) } } + +impl<'a, W: Widget + 'a, State: HasUi> FnOnce<(&'a mut State,)> for WidgetRef { + type Output = &'a mut W; + + extern "rust-call" fn call_once(self, args: (&'a mut State,)) -> Self::Output { + &mut args.0.ui_mut()[self] + } +} diff --git a/core/src/widget/like.rs b/core/src/widget/like.rs index c32ffb7..7841149 100644 --- a/core/src/widget/like.rs +++ b/core/src/widget/like.rs @@ -19,7 +19,7 @@ pub trait WidgetLike: Sized { } fn set_root(self, state: &mut State) { - state.ui().root = Some(self.add(state)); + state.get_mut().root = Some(self.add(state)); } fn handles(self, state: &mut State) -> WidgetHandles { diff --git a/core/src/widget/tag.rs b/core/src/widget/tag.rs index aba11fa..fa30cc4 100644 --- a/core/src/widget/tag.rs +++ b/core/src/widget/tag.rs @@ -7,15 +7,15 @@ use super::*; pub struct ArrTag; pub struct WidgetTag; -impl WidgetLike for W { +impl WidgetLike for W { type Widget = W; fn add(self, state: &mut State) -> WidgetHandle { - state.ui().add_widget(self) + state.get_mut().add_widget(self) } } pub struct FnTag; -impl W> WidgetLike for F { +impl W> WidgetLike for F { type Widget = W; fn add(self, state: &mut State) -> WidgetHandle { self(state).add(state) @@ -23,8 +23,8 @@ impl W> WidgetLike + 'static> WidgetLike - for WidgetHandle +impl + 'static> + WidgetLike for WidgetHandle { type Widget = W; fn add(self, _: &mut State) -> WidgetHandle { @@ -34,7 +34,7 @@ impl + 'static> WidgetLike pub struct IdFnTag; impl< - State: HasUi, + State: HasUi + 'static, W: ?Sized + Widget + Unsize + 'static, F: FnOnce(&mut State) -> WidgetHandle, > WidgetLike for F diff --git a/examples/minimal.rs b/examples/minimal.rs index a91e58b..5f1ef78 100644 --- a/examples/minimal.rs +++ b/examples/minimal.rs @@ -1,22 +1,20 @@ use iris::prelude::*; -use winit::event_loop::ActiveEventLoop; fn main() { App::::run(); } -#[derive(DefaultUiState)] -struct State { - ui: Ui, - ui_state: UiState, -} +#[default_ui_state] +struct State {} impl DefaultAppState for State { - fn new(event_loop: &ActiveEventLoop, _proxy: Proxy) -> Self { + fn new(ui_state: DefaultUiState, _proxy: Proxy) -> Self { let mut ui = Ui::new(); - let window = event_loop.create_window(Default::default()).unwrap(); - let ui_state = UiState::new(window); rect(Color::RED).set_root(&mut ui); - Self { ui, ui_state } + Self { + ui, + ui_state, + events: EventManager::default(), + } } } diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 88997fa..42d8dab 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -4,6 +4,7 @@ version.workspace = true edition.workspace = true [dependencies] +proc-macro2 = "1.0.103" quote = "1.0.42" syn = { version = "2.0.111", features = ["full"] } diff --git a/macro/src/lib.rs b/macro/src/lib.rs index ee00759..43ab00b 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -2,10 +2,11 @@ extern crate proc_macro; use proc_macro::TokenStream; use quote::quote; use syn::{ - Attribute, Block, Error, GenericParam, Generics, Ident, ItemTrait, Signature, Token, - Visibility, + Attribute, Block, Error, Fields, FieldsNamed, GenericParam, Generics, Ident, ItemStruct, + ItemTrait, Meta, Signature, Token, Visibility, parse::{Parse, ParseStream, Result}, parse_macro_input, parse_quote, + spanned::Spanned, }; struct Input { @@ -94,3 +95,86 @@ pub fn widget_trait(input: TokenStream) -> TokenStream { } .into() } + +#[proc_macro_derive(UiState, attributes(rsc))] +pub fn derive_ui_state(input: TokenStream) -> TokenStream { + let mut output = proc_macro2::TokenStream::new(); + + let state: ItemStruct = parse_macro_input!(input); + let sname = state.ident; + let rscname = Ident::new(&(sname.to_string() + "Rsc"), sname.span()); + let mut rsc_fields = Vec::new(); + + for field in state.fields { + let Some(attr) = field.attrs.iter().find(|a| a.path().is_ident("rsc")) else { + continue; + }; + let Meta::List(list) = &attr.meta else { + output.extend(Error::new(attr.span(), "invalid attr syntax").into_compile_error()); + continue; + }; + let tname: Ident = match list.parse_args::() { + Ok(ident) => ident, + Err(err) => { + output.extend(err.to_compile_error()); + continue; + } + }; + let fty = &field.ty; + let fname = &field.ident.unwrap(); + rsc_fields.extend(quote! {#fname: #fty,}); + output.extend(quote! { + impl #tname for #sname { + fn get(&self) -> &#fty { + &self.#fname + } + fn get_mut(&mut self) -> &mut #fty { + &mut self.#fname + } + } + impl #tname for #rscname { + fn get(&self) -> &#fty { + &self.#fname + } + fn get_mut(&mut self) -> &mut #fty { + &mut self.#fname + } + } + }); + } + let vis = state.vis; + output.extend(quote! { + #vis struct #rscname { + #(#rsc_fields)* + } + + impl HasState for #sname { + type State = #sname; + } + + impl HasState for #rscname { + type State = #sname; + } + }); + output.into() +} + +#[proc_macro_attribute] +pub fn default_ui_state(_attr: TokenStream, input: TokenStream) -> TokenStream { + let mut state: ItemStruct = parse_macro_input!(input); + let Fields::Named(fields) = &mut state.fields else { + panic!("must be on named fields struct"); + }; + let name = &state.ident; + state.attrs.push(parse_quote! {#[derive(UiState)]}); + let new: FieldsNamed = parse_quote! {{ + #[rsc(HasUi)] + pub ui: Ui, + #[rsc(HasDefaultUiState)] + pub ui_state: DefaultUiState, + #[rsc(HasEvents)] + pub events: iris::prelude::EventManager<#name>, + }}; + fields.named.extend(new.named); + quote! {#state}.into() +} diff --git a/src/bin/test/main.rs b/src/bin/test/main.rs index 47b8c23..c6b3848 100644 --- a/src/bin/test/main.rs +++ b/src/bin/test/main.rs @@ -1,36 +1,26 @@ use cosmic_text::Family; use std::{cell::RefCell, rc::Rc}; -use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::WindowAttributes}; +use winit::event::WindowEvent; -iris::state_prelude!(DefaultUiRsc); +iris::state_prelude!(ClientRsc); fn main() { App::::run(); } +#[default_ui_state] pub struct Client { - rsc: DefaultUiRsc, info: WidgetRef, } -impl HasRsc for Client { - type Rsc = DefaultUiRsc; - - fn rsc(&self) -> &Self::Rsc { - &self.rsc - } - - fn rsc_mut(&mut self) -> &mut Self::Rsc { - &mut self.rsc - } -} - impl DefaultAppState for Client { - fn new(event_loop: &ActiveEventLoop, _proxy: Proxy) -> Self { - let window = event_loop - .create_window(WindowAttributes::default()) - .unwrap(); - let mut rsc = DefaultUiRsc::new(window); + fn new(ui_state: DefaultUiState, _proxy: Proxy) -> Self { + let mut rsc = ClientRsc { + ui: Ui::new(), + ui_state, + events: EventManager::default(), + }; + let info; let rrect = rect(Color::WHITE).radius(20); let pad_test = ( @@ -71,19 +61,19 @@ impl DefaultAppState for Client { let add_button = rect(Color::LIME) .radius(30) - .on(CursorSense::click(), move |mut ctx| { + .on(CursorSense::click(), move |ctx| { let child = image(include_bytes!("assets/sungals.png")) .center() - .add(&mut ctx.state.rsc); - ctx[span_add.r].children.push(child); + .add(ctx.state); + (span_add.r)(ctx).children.push(child); }) .sized((150, 150)) .align(Align::BOT_RIGHT); let del_button = rect(Color::RED) .radius(30) - .on(CursorSense::click(), move |mut ctx| { - ctx[span_add.r].children.pop(); + .on(CursorSense::click(), move |ctx| { + (span_add.r)(ctx).children.pop(); }) .sized((150, 150)) .align(Align::BOT_LEFT); @@ -120,8 +110,9 @@ impl DefaultAppState for Client { .text_align(Align::LEFT) .size(30) .attr::(()) - .on(Submit, move |mut ctx| { - let content = ctx.widget.edit(ctx.state.ui()).take(); + .on(Submit, move |ctx| { + let w = ctx.widget; + let content = w.edit(ctx).take(); let text = wtext(content) .editable(EditMode::MultiLine) .size(30) @@ -130,8 +121,8 @@ impl DefaultAppState for Client { .attr::(()); let msg_box = text .background(rect(Color::WHITE.darker(0.5))) - .add(&mut ctx.state.rsc); - ctx[texts.r].children.push(msg_box); + .add(ctx.state); + (texts.r)(ctx).children.push(msg_box); }) .handles(&mut rsc); let text_edit_scroll = ( @@ -171,21 +162,21 @@ impl DefaultAppState for Client { } let vals = vals.clone(); let rect = rect(color) - .on(CursorSense::click(), move |mut ctx| { + .on(CursorSense::click(), move |ctx| { let (prev, vec) = &mut *vals.borrow_mut(); if let Some(h) = vec[i].take() { - vec[*prev] = ctx[main.r].replace(h); + vec[*prev] = (main.r)(ctx).replace(h); *prev = i; } ctx.widget().color = color.darker(0.3); }) .on( CursorSense::HoverStart | CursorSense::unclick(), - move |mut ctx| { + move |ctx| { ctx.widget().color = color.brighter(0.2); }, ) - .on(CursorSense::HoverEnd, move |mut ctx| { + .on(CursorSense::HoverEnd, move |ctx| { ctx.widget().color = color; }); (rect, wtext(label).size(30).text_align(Align::CENTER)).stack() @@ -211,21 +202,26 @@ impl DefaultAppState for Client { .stack() .set_root(&mut rsc); - Self { rsc, info: info.r } + Self { + ui: rsc.ui, + ui_state: rsc.ui_state, + events: rsc.events, + info: info.r, + } } fn window_event(&mut self, _: WindowEvent) { let new = format!( "widgets: {}\nactive:{}\nviews: {}", - self.ui().num_widgets(), - self.ui().active_widgets(), - self.ui_state().renderer.ui.view_count() + self.ui.num_widgets(), + self.ui.active_widgets(), + self.ui_state.renderer.ui.view_count() ); - if new != *self.rsc.ui[self.info].content { - *self.rsc.ui[self.info].content = new; + if new != *self.ui[self.info].content { + *self.ui[self.info].content = new; } - if self.ui().needs_redraw() { - self.ui_state().window.request_redraw(); + if self.ui.needs_redraw() { + self.ui_state.window.request_redraw(); } } } diff --git a/src/default/attr.rs b/src/default/attr.rs index 666ca31..47f08da 100644 --- a/src/default/attr.rs +++ b/src/default/attr.rs @@ -4,12 +4,15 @@ use winit::dpi::{LogicalPosition, LogicalSize}; pub struct Selector; -impl WidgetAttr for Selector { +impl WidgetAttr for Selector +where + State::State: HasDefaultUiState, +{ type Input = WidgetRef; fn run(state: &mut State, container: WidgetRef, id: Self::Input) { state.register_event(container, CursorSense::click_or_drag(), move |ctx| { - let region = ctx.state.ui().window_region(&id).unwrap(); + let region = ctx.ui().window_region(&id).unwrap(); let id_pos = region.top_left; let container_pos = ctx.state.ui().window_region(&container).unwrap().top_left; let pos = ctx.data.pos + container_pos - id_pos; @@ -21,7 +24,10 @@ impl WidgetAttr fo pub struct Selectable; -impl WidgetAttr for Selectable { +impl WidgetAttr for Selectable +where + State::State: HasDefaultUiState, +{ type Input = (); fn run(state: &mut State, id: WidgetRef, _: Self::Input) { @@ -38,22 +44,23 @@ impl WidgetAttr for Selectable { } fn select( - state: &mut impl HasUiState, + state: &mut impl HasDefaultUiState, id: WidgetRef, pos: Vec2, size: Vec2, dragging: bool, ) { + let (ui, state) = HasDefaultUiState::ui_with_state(state); let now = Instant::now(); - let recent = (now - state.ui_state().last_click) < Duration::from_millis(300); - state.ui_state().last_click = now; - id.edit(state.ui()).select(pos, size, dragging, recent); - if let Some(region) = state.ui().window_region(&id) { - state.ui_state().window.set_ime_allowed(true); - state.ui_state().window.set_ime_cursor_area( + let recent = (now - state.last_click) < Duration::from_millis(300); + state.last_click = now; + id.edit(ui).select(pos, size, dragging, recent); + if let Some(region) = ui.window_region(&id) { + state.window.set_ime_allowed(true); + state.window.set_ime_cursor_area( LogicalPosition::::from(region.top_left.tuple()), LogicalSize::::from(region.size().tuple()), ); } - state.ui_state().focus = Some(id); + state.focus = Some(id); } diff --git a/src/default/input.rs b/src/default/input.rs index 4aa96b7..07b6bbf 100644 --- a/src/default/input.rs +++ b/src/default/input.rs @@ -66,7 +66,7 @@ impl Input { } } -impl UiState { +impl DefaultUiState { pub fn window_size(&self) -> Vec2 { let size = self.renderer.window().inner_size(); (size.width, size.height).into() diff --git a/src/default/mod.rs b/src/default/mod.rs index 09b881f..a69536e 100644 --- a/src/default/mod.rs +++ b/src/default/mod.rs @@ -5,7 +5,7 @@ use std::{marker::Sized, sync::Arc, time::Instant}; use winit::{ event::{Ime, WindowEvent}, event_loop::{ActiveEventLoop, EventLoopProxy}, - window::Window, + window::{Window, WindowAttributes}, }; mod app; @@ -24,47 +24,7 @@ pub use sense::*; pub type Proxy = EventLoopProxy; -pub struct DefaultUiRsc { - pub ui: Ui, - pub ui_state: UiState, - pub events: EventManager, -} - -impl DefaultUiRsc { - pub fn new(window: impl Into>) -> Self { - Self { - ui: Ui::new(), - ui_state: UiState::new(window), - events: EventManager::default(), - } - } -} - -impl HasUi for DefaultUiRsc { - fn ui_ref(&self) -> &Ui { - &self.ui - } - - fn ui(&mut self) -> &mut Ui { - &mut self.ui - } -} - -impl> HasEvents for DefaultUiRsc { - type State = State; - - fn events(&mut self) -> &mut EventManager { - &mut self.events - } -} - -impl HasUiState for DefaultUiRsc { - fn ui_state(&mut self) -> &mut UiState { - &mut self.ui_state - } -} - -pub struct UiState { +pub struct DefaultUiState { pub renderer: UiRenderer, pub input: Input, pub focus: Option>, @@ -74,7 +34,7 @@ pub struct UiState { pub last_click: Instant, } -impl UiState { +impl DefaultUiState { pub fn new(window: impl Into>) -> Self { let window = window.into(); Self { @@ -89,39 +49,44 @@ impl UiState { } } -pub trait HasUiState: Sized + 'static + HasUi { - fn ui_state(&mut self) -> &mut UiState; - fn ui_with_ui_state(&mut self) -> (&mut Ui, &mut UiState) { +pub trait HasDefaultUiState: Sized + 'static + HasUi { + fn get(&self) -> &DefaultUiState; + fn get_mut(&mut self) -> &mut DefaultUiState; + fn ui_state(&self) -> &DefaultUiState { + HasDefaultUiState::get(self) + } + fn ui_state_mut(&mut self) -> &mut DefaultUiState { + HasDefaultUiState::get_mut(self) + } + fn ui_with_state(&mut self) -> (&mut Ui, &mut DefaultUiState) { // as long as you're not doing anything actually unhinged this should always work safely - (unsafe { forget_mut(self.ui()) }, self.ui_state()) + ( + unsafe { forget_mut(self.ui_mut()) }, + HasDefaultUiState::get_mut(self), + ) } } -impl HasUiState for R -where - R::Rsc: HasUiState, -{ - fn ui_state(&mut self) -> &mut UiState { - self.rsc_mut().ui_state() - } -} - -pub trait DefaultAppState: Sized + 'static + HasRsc + HasUi + HasUiState { +pub trait DefaultAppState: RunEvents + HasDefaultUiState { type Event: 'static = (); - fn new(event_loop: &ActiveEventLoop, proxy: Proxy) -> Self; + fn new(ui_state: DefaultUiState, proxy: Proxy) -> Self; #[allow(unused_variables)] fn event(&mut self, event: Self::Event) {} #[allow(unused_variables)] fn exit(&mut self) {} #[allow(unused_variables)] fn window_event(&mut self, event: WindowEvent) {} + fn window_attributes() -> WindowAttributes { + Default::default() + } } impl AppState for State { type Event = State::Event; fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy) -> Self { - Self::new(event_loop, proxy) + let window = event_loop.create_window(Self::window_attributes()).unwrap(); + Self::new(DefaultUiState::new(window), proxy) } fn event(&mut self, event: Self::Event, _: &ActiveEventLoop) { @@ -129,8 +94,8 @@ impl AppState for State { } fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop) { - let events = unsafe { forget_mut(self.events()) }; - let ui_state = self.ui_state(); + let events = unsafe { forget_mut(self.events_mut()) }; + let ui_state = HasDefaultUiState::get_mut(self); let input_changed = ui_state.input.event(&event); let cursor_state = ui_state.cursor_state().clone(); let old = ui_state.focus; @@ -141,7 +106,7 @@ impl AppState for State { let window_size = ui_state.window_size(); self.run_sensors(&cursor_state, window_size); } - let (mut ui, mut ui_state) = self.ui_with_ui_state(); + let (mut ui, mut ui_state) = self.ui_with_state(); if old != ui_state.focus && let Some(old) = old { @@ -208,7 +173,7 @@ impl AppState for State { _ => (), } self.window_event(event); - (ui, ui_state) = self.ui_with_ui_state(); + (ui, ui_state) = self.ui_with_state(); if ui.needs_redraw() { ui_state.renderer.window().request_redraw(); } diff --git a/src/default/sense.rs b/src/default/sense.rs index fc36c50..ff45e26 100644 --- a/src/default/sense.rs +++ b/src/default/sense.rs @@ -142,10 +142,10 @@ pub trait SensorUi { fn run_sensors(&mut self, cursor: &CursorState, window_size: Vec2); } -impl SensorUi for State { +impl SensorUi for State { fn run_sensors(&mut self, cursor: &CursorState, window_size: Vec2) { - let layers = std::mem::take(&mut self.ui().layers); - let mut active = std::mem::take(&mut self.events().get_type::().active); + let layers = std::mem::take(&mut self.ui_mut().layers); + let mut active = std::mem::take(&mut self.events_mut().get_type::().active); for layer in layers.indices().rev() { let mut sensed = false; for (id, sensor) in active.get_mut(&layer).into_iter().flatten() { @@ -174,8 +174,8 @@ impl SensorUi for State { break; } } - self.events().get_type::().active = active; - self.ui().layers = layers; + self.events_mut().get_type::().active = active; + self.ui_mut().layers = layers; } } diff --git a/src/event.rs b/src/event.rs index ef60a8a..876a145 100644 --- a/src/event.rs +++ b/src/event.rs @@ -3,7 +3,7 @@ use crate::prelude::*; pub mod eventable { use super::*; widget_trait! { - pub trait Eventable; + pub trait Eventable; fn on( self, event: E, @@ -12,7 +12,7 @@ pub mod eventable { move |state| { let id = self.handles(state); state.register_event(id.r, event.into_event(), move |ctx| { - f(EventIdCtx { + f(&mut EventIdCtx { widget: id.r, state: ctx.state, data: ctx.data, diff --git a/src/typed.rs b/src/typed.rs index 21039ca..f3792aa 100644 --- a/src/typed.rs +++ b/src/typed.rs @@ -10,7 +10,7 @@ macro_rules! event_state { self, event: E, f: impl for<'a> WidgetEventFn< - <$state as HasEvents>::State, + <$state as HasState>::State, ::Data<'a>, WL::Widget >, @@ -21,7 +21,7 @@ macro_rules! event_state { self, event: E, f: impl for<'a> WidgetEventFn< - <$state as HasEvents>::State, + <$state as HasState>::State, ::Data<'a>, WL::Widget >, @@ -30,7 +30,7 @@ macro_rules! event_state { } } } - $vis type EventManager = $crate::prelude::EventManager<$state>; + $vis type EventManager = $crate::prelude::EventManager<<$state as HasState>::State>; $vis use local_event_trait::*; }; } diff --git a/src/widget/image.rs b/src/widget/image.rs index ebe8722..0c3e9ce 100644 --- a/src/widget/image.rs +++ b/src/widget/image.rs @@ -22,7 +22,7 @@ impl Widget for Image { pub fn image(image: impl LoadableImage) -> impl WidgetFn { let image = image.get_image().expect("Failed to load image"); move |state| Image { - handle: state.ui().add_texture(image), + handle: state.get_mut().add_texture(image), } } diff --git a/src/widget/text/build.rs b/src/widget/text/build.rs index cedfab6..1f97193 100644 --- a/src/widget/text/build.rs +++ b/src/widget/text/build.rs @@ -51,7 +51,7 @@ impl> TextBuilder { } } -impl TextBuilder { +impl TextBuilder { pub fn hint, Tag>( self, hint: W, @@ -87,7 +87,7 @@ impl TextBuilderOutput for TextOutput { builder.attrs.line_height, )); let hint = builder.hint.get(state); - let font_system = &mut state.ui().text.font_system; + let font_system = &mut state.get_mut().text.font_system; buf.set_text(font_system, &builder.content, &Attrs::new(), SHAPING, None); let mut text = Text { content: builder.content.into(), @@ -118,7 +118,7 @@ impl TextBuilderOutput for TextEditOutput { TextView::new(buf, builder.attrs, builder.hint.get(state)), builder.output.mode, ); - let font_system = &mut state.ui().text.font_system; + let font_system = &mut state.get_mut().text.font_system; text.buf .set_text(font_system, &builder.content, &Attrs::new(), SHAPING, None); builder.attrs.apply(font_system, &mut text.buf, None); diff --git a/src/widget/text/edit.rs b/src/widget/text/edit.rs index a09ed43..d5cc1bb 100644 --- a/src/widget/text/edit.rs +++ b/src/widget/text/edit.rs @@ -617,11 +617,12 @@ impl DerefMut for TextEdit { } pub trait TextEditable { - fn edit<'a>(&self, ui: &'a mut Ui) -> TextEditCtx<'a>; + fn edit<'a>(&self, ui: &'a mut impl HasUi) -> TextEditCtx<'a>; } impl> TextEditable for I { - fn edit<'a>(&self, ui: &'a mut Ui) -> TextEditCtx<'a> { + fn edit<'a>(&self, ui: &'a mut impl HasUi) -> TextEditCtx<'a> { + let ui = ui.ui_mut(); TextEditCtx { text: ui.widgets.get_mut(self).unwrap(), font_system: &mut ui.text.font_system, diff --git a/src/widget/trait_fns.rs b/src/widget/trait_fns.rs index 10e4660..f87bcce 100644 --- a/src/widget/trait_fns.rs +++ b/src/widget/trait_fns.rs @@ -3,7 +3,7 @@ use crate::prelude::*; // these methods should "not require any context" (require unit) because they're in core widget_trait! { - pub trait CoreWidget; + pub trait CoreWidget; fn pad(self, padding: impl Into) -> impl WidgetFn { |state| Pad { @@ -26,7 +26,7 @@ widget_trait! { fn label(self, label: impl Into) -> impl WidgetIdFn { |state| { let id = self.add(state); - state.ui().set_label(&id, label.into()); + state.get_mut().set_label(&id, label.into()); id } } @@ -87,7 +87,7 @@ widget_trait! { use eventable::*; move |state| { Scroll::new(self.add(state), Axis::Y) - .on(CursorSense::Scroll, |mut ctx| { + .on(CursorSense::Scroll, |ctx: &mut EventIdCtx<'_, State::State, CursorData<'_>, Scroll>| { let delta = ctx.data.scroll_delta.y * 50.0; ctx.widget().scroll(delta); }) @@ -128,7 +128,7 @@ widget_trait! { fn set_ptr(self, ptr: WidgetRef, state: &mut State) { let id = self.add(state); - state.ui()[ptr].inner = Some(id); + state.get_mut()[ptr].inner = Some(id); } }