widget state

This commit is contained in:
2026-01-19 20:39:58 -05:00
parent 06dd015092
commit 7bafb04a34
6 changed files with 137 additions and 206 deletions

View File

@@ -1,196 +0,0 @@
use crate::{
ActiveData, Axis, EventsLike, Painter, SizeCtx, StrongWidget, UiRegion, UiRenderState, UiVec2,
WidgetId, Widgets,
render::MaskIdx,
util::{HashSet, forget_ref},
};
/// state maintained between widgets during painting
pub struct Drawer<'a> {
pub(super) widgets: &'a mut Widgets,
pub(super) data: &'a mut PainterData,
pub(super) events: &'a mut dyn EventsLike,
pub(super) render: &'a mut UiRenderState,
root: Option<&'a StrongWidget>,
draw_started: HashSet<WidgetId>,
}
impl<'a> Drawer<'a> {
/// redraws a widget that's currently active (drawn)
pub fn redraw(&mut self, id: WidgetId) {
self.widgets.needs_redraw.remove(&id);
self.draw_started.remove(&id);
// check if parent depends on the desired size of this, if so then redraw it first
for axis in [Axis::X, Axis::Y] {
if let Some(&(outer, old)) = self.render.cache.size.axis_dyn(axis).get(&id)
&& let Some(current) = self.render.active.get(&id)
&& let Some(pid) = current.parent
{
self.render.cache.size.axis_dyn(axis).remove(&id);
let new = self.size_ctx(id, outer).len_axis(id, axis);
self.render
.cache
.size
.axis_dyn(axis)
.insert(id, (outer, new));
if new != old {
self.redraw(pid);
}
}
}
if self.draw_started.contains(&id) {
return;
}
let Some(active) = self.remove(id, false) else {
return;
};
self.draw_inner(
active.layer,
id,
active.region,
active.parent,
active.mask,
Some(active.children),
);
}
pub(super) fn size_ctx<'b>(&'b mut self, source: WidgetId, outer: UiVec2) -> SizeCtx<'b> {
SizeCtx {
source,
cache: &mut self.render.cache,
text: &mut self.data.text,
textures: &mut self.data.textures,
widgets: &self.widgets,
outer,
output_size: self.render.output_size,
id: source,
}
}
pub(super) fn draw_inner(
&mut self,
layer: usize,
id: WidgetId,
region: UiRegion,
parent: Option<WidgetId>,
mask: MaskIdx,
old_children: Option<Vec<WidgetId>>,
) {
let mut old_children = old_children.unwrap_or_default();
if let Some(active) = self.render.active.get_mut(&id)
&& !self.widgets.needs_redraw.contains(&id)
{
// check to see if we can skip drawing first
if active.region == region {
return;
} else if active.region.size() == region.size() {
// TODO: epsilon?
let from = active.region;
self.mov(id, from, region);
return;
}
// if not, then maintain resize and track old children to remove unneeded
let active = self.remove(id, false).unwrap();
old_children = active.children;
}
// draw widget
self.draw_started.insert(id);
let mut painter = Painter {
drawer: self,
region,
mask,
layer,
id,
textures: Vec::new(),
primitives: Vec::new(),
children: Vec::new(),
};
let mut widget = painter.drawer.widgets.get_dyn_dynamic(id);
widget.draw(&mut painter);
drop(widget);
let Painter {
drawer: _,
region,
mask,
textures,
primitives,
children,
layer,
id,
} = painter;
// add to active
let active = ActiveData {
id,
region,
parent,
textures,
primitives,
children,
mask,
layer,
};
// remove old children that weren't kept
for c in &old_children {
if !active.children.contains(c) {
self.remove_rec(*c);
}
}
// update modules
self.events.draw(&active);
self.render.active.insert(id, active);
}
fn mov(&mut self, id: WidgetId, from: UiRegion, to: UiRegion) {
let active = self.render.active.get_mut(&id).unwrap();
for h in &active.primitives {
let region = self.render.layers[h.layer].region_mut(h);
*region = region.outside(&from).within(&to);
}
active.region = active.region.outside(&from).within(&to);
// SAFETY: children cannot be recursive
let children = unsafe { forget_ref(&active.children) };
for child in children {
self.mov(*child, from, to);
}
}
/// NOTE: instance textures are cleared and self.textures freed
fn remove(&mut self, id: WidgetId, undraw: bool) -> Option<ActiveData> {
let mut active = self.render.active.remove(&id);
if let Some(active) = &mut active {
for h in &active.primitives {
let mask = self.render.layers.free(h);
if mask != MaskIdx::NONE {
self.data.masks.remove(mask);
}
}
active.textures.clear();
self.data.textures.free();
if undraw {
self.events.undraw(active);
}
}
active
}
fn remove_rec(&mut self, id: WidgetId) -> Option<ActiveData> {
self.render.cache.remove(id);
let inst = self.remove(id, true);
if let Some(inst) = &inst {
for c in &inst.children {
self.remove_rec(*c);
}
}
inst
}
}

View File

@@ -2,11 +2,9 @@ use crate::{Mask, TextData, Textures, WeakWidget, WidgetId, Widgets, util::Track
mod active; mod active;
mod cache; mod cache;
// mod draw_state;
mod painter; mod painter;
mod render_state; mod render_state;
mod size; mod size;
mod state;
pub use active::*; pub use active::*;
pub use painter::Painter; pub use painter::Painter;

View File

@@ -1,3 +0,0 @@
// pub struct DynState {
//
// }

View File

@@ -15,19 +15,22 @@ type Rsc = DefaultRsc<State>;
struct Test { struct Test {
#[root] #[root]
root: WeakWidget<Rect>, root: WeakWidget<Rect>,
cur: WeakState<bool>,
} }
impl Test { impl Test {
pub fn new(rsc: &mut Rsc) -> Self { pub fn new(rsc: &mut Rsc) -> Self {
let root = rect(Color::RED).add(rsc); let root = rect(Color::RED).add(rsc);
Self { root } let cur = rsc.create_state(root, false);
Self { root, cur }
} }
pub fn toggle(&self, rsc: &mut Rsc) { pub fn toggle(&self, rsc: &mut Rsc) {
let rect = (self.root)(rsc); let cur = &mut rsc[self.cur];
if rect.color == Color::RED { *cur = !*cur;
rect.color = Color::BLUE; if *cur {
rsc[self.root].color = Color::BLUE;
} else { } else {
rect.color = Color::RED; rsc[self.root].color = Color::RED;
} }
} }
} }

View File

@@ -17,6 +17,7 @@ mod event;
mod input; mod input;
mod render; mod render;
mod sense; mod sense;
mod state;
mod task; mod task;
pub use app::*; pub use app::*;
@@ -25,6 +26,7 @@ pub use event::*;
pub use input::*; pub use input::*;
pub use render::*; pub use render::*;
pub use sense::*; pub use sense::*;
pub use state::*;
pub use task::*; pub use task::*;
pub type Proxy<Event> = EventLoopProxy<Event>; pub type Proxy<Event> = EventLoopProxy<Event>;
@@ -98,6 +100,7 @@ pub struct DefaultRsc<State: 'static> {
pub ui: UiData, pub ui: UiData,
pub events: EventManager<Self>, pub events: EventManager<Self>,
pub tasks: Tasks<Self>, pub tasks: Tasks<Self>,
pub state: WidgetState,
_state: PhantomData<State>, _state: PhantomData<State>,
} }
@@ -109,11 +112,16 @@ impl<State> DefaultRsc<State> {
ui: Default::default(), ui: Default::default(),
events: Default::default(), events: Default::default(),
tasks, tasks,
state: Default::default(),
_state: Default::default(), _state: Default::default(),
}, },
recv, recv,
) )
} }
pub fn create_state<T: 'static>(&mut self, id: impl IdLike, data: T) -> WeakState<T> {
self.state.add(id.id(), data)
}
} }
impl<State> UiRsc for DefaultRsc<State> { impl<State> UiRsc for DefaultRsc<State> {
@@ -158,6 +166,16 @@ impl<State: 'static> HasTasks for DefaultRsc<State> {
} }
} }
impl<State: 'static> HasWidgetState for DefaultRsc<State> {
fn widget_state(&self) -> &WidgetState {
&self.state
}
fn widget_state_mut(&mut self) -> &mut WidgetState {
&mut self.state
}
}
pub struct DefaultApp<State: DefaultAppState> { pub struct DefaultApp<State: DefaultAppState> {
rsc: DefaultRsc<State>, rsc: DefaultRsc<State>,
render: UiRenderState, render: UiRenderState,
@@ -289,3 +307,47 @@ impl<State: DefaultAppState> AppState for DefaultApp<State> {
self.state.exit(&mut self.rsc, &mut self.render); self.state.exit(&mut self.rsc, &mut self.render);
} }
} }
pub trait RscIdx<Rsc> {
type Output;
fn get(self, rsc: &Rsc) -> &Self::Output;
fn get_mut(self, rsc: &mut Rsc) -> &mut Self::Output;
}
impl<State: 'static, I: RscIdx<DefaultRsc<State>>> std::ops::Index<I> for DefaultRsc<State> {
type Output = I::Output;
fn index(&self, index: I) -> &Self::Output {
index.get(self)
}
}
impl<State: 'static, I: RscIdx<DefaultRsc<State>>> std::ops::IndexMut<I> for DefaultRsc<State> {
fn index_mut(&mut self, index: I) -> &mut Self::Output {
index.get_mut(self)
}
}
impl<W: Widget, Rsc: UiRsc> RscIdx<Rsc> for WeakWidget<W> {
type Output = W;
fn get(self, rsc: &Rsc) -> &Self::Output {
&rsc.ui().widgets[self]
}
fn get_mut(self, rsc: &mut Rsc) -> &mut Self::Output {
&mut rsc.ui_mut().widgets[self]
}
}
impl<T: 'static, Rsc: HasWidgetState> RscIdx<Rsc> for WeakState<T> {
type Output = T;
fn get(self, rsc: &Rsc) -> &Self::Output {
rsc.widget_state().get(self)
}
fn get_mut(self, rsc: &mut Rsc) -> &mut Self::Output {
rsc.widget_state_mut().get_mut(self)
}
}

67
src/default/state.rs Normal file
View File

@@ -0,0 +1,67 @@
use iris_core::{WidgetId, util::HashMap};
use std::{
any::{Any, TypeId},
marker::PhantomData,
};
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
struct Key {
id: WidgetId,
ty: TypeId,
i: usize,
}
#[derive(Default)]
pub struct WidgetState {
counts: HashMap<(WidgetId, TypeId), usize>,
map: HashMap<Key, Box<dyn Any>>,
}
impl WidgetState {
pub fn new() -> Self {
Self::default()
}
pub fn add<T: 'static>(&mut self, id: WidgetId, data: T) -> WeakState<T> {
let ty = TypeId::of::<T>();
let count = self.counts.entry((id, ty)).or_default();
let key = Key { ty, i: *count, id };
self.map.insert(key, Box::new(data));
*count += 1;
WeakState {
key,
_pd: PhantomData,
}
}
pub fn remove<T>(&mut self, state: WeakState<T>) {
self.map.remove(&state.key);
}
pub fn get<T: 'static>(&self, state: WeakState<T>) -> &T {
self.map.get(&state.key).unwrap().downcast_ref().unwrap()
}
pub fn get_mut<T: 'static>(&mut self, state: WeakState<T>) -> &mut T {
self.map
.get_mut(&state.key)
.unwrap()
.downcast_mut()
.unwrap()
}
}
#[derive(Clone, Copy)]
pub struct WeakState<T> {
key: Key,
_pd: PhantomData<T>,
}
pub trait HasWidgetState {
fn widget_state(&self) -> &WidgetState;
fn widget_state_mut(&mut self) -> &mut WidgetState;
}
impl<'a, T: 'static> FnOnce<(&'a mut WidgetState,)> for WeakState<T> {
type Output = &'a mut T;
extern "rust-call" fn call_once(self, (state,): (&'a mut WidgetState,)) -> Self::Output {
state.get_mut(self)
}
}