use crate::{ Mask, MoveIdx, MoveOffset, PrimitiveRegistry, TextData, Textures, UiRegion, WeakWidget, WideRegion, WideSize, WidgetId, Widgets, util::{Arena, Id, TrackedArena}, }; /// How far the shader will walk a move chain. It bounds a malformed cycle /// rather than any real tree; `Moves::resolve` uses the same number so the /// two agree on what a deep tree resolves to. pub const CHAIN_LIMIT: u32 = 64; mod active; mod holds; mod painter; mod render_state; pub use active::*; pub use holds::*; pub use painter::{Painter, PrimitiveLike}; pub use render_state::*; #[derive(Default)] pub struct UiData { pub widgets: Widgets, /// Every primitive this ui can draw. pub primitives: PrimitiveRegistry, pub textures: Textures, pub text: TextData, pub masks: TrackedArena, } /// Where each widget's drawing sits relative to its parent's slot, so moving /// a subtree writes one entry rather than every descendant's primitives. #[derive(Default)] pub struct Moves { arena: Arena, pub changed: bool, } impl Moves { pub fn push(&mut self, parent: MoveIdx, region: UiRegion) -> MoveIdx { self.changed = true; MoveIdx::slot(self.arena.push(MoveOffset::new(parent, region)).idx()) } /// Re-points a slot at a different parent, for a widget drawn somewhere /// else in the tree than it was. pub fn set_parent(&mut self, idx: MoveIdx, parent: MoveIdx) { let entry = self.arena.get_mut(Id::preset(idx.idx() as u32)); if entry.parent != parent { entry.parent = parent; self.changed = true; } } pub fn remove(&mut self, idx: MoveIdx) { self.changed = true; self.arena.remove(Id::preset(idx.idx() as u32)); } /// Sets the box a slot's contents are placed within, itself given in the /// coordinates of its parent slot. pub fn set(&mut self, idx: MoveIdx, region: UiRegion) { let entry = self.arena.get_mut(Id::preset(idx.idx() as u32)); if entry.region != region { entry.region = region; self.changed = true; } } /// Composes a region held in `idx`'s coordinates down the chain, on a /// grid fine enough that the whole walk rounds once. Composing in `Len` /// rounds four multiplies a level, so the residue grew with the depth of /// the tree -- and it is the box this produces that layout takes a /// structural decision on. pub fn compose(&self, idx: MoveIdx, local: UiRegion) -> WideRegion { let mut region = WideRegion::of(local); self.walk(idx, |entry| region = region.within(entry)); region } /// How long a region held in `idx`'s coordinates is, composed down the /// chain on the same fine grid as [`Self::compose`] and for the same /// reason. Half the multiplies of composing the box, since a length does /// not need to know where the box sits -- and what reads a box in pixels /// nearly always wants only this. pub fn size_of(&self, idx: MoveIdx, local: UiRegion) -> WideSize { let mut size = WideSize::of(local); self.walk(idx, |entry| size = size.within(entry)); size } /// The same walk the vertex shader does, in the same `Len` the shader is /// handed. What layout decides on is [`Self::compose`]; this is for /// asking where the drawing will actually land, which the shader works /// out again in floats from these same entries. pub fn resolve(&self, idx: MoveIdx, local: UiRegion) -> UiRegion { let mut region = local; self.walk(idx, |entry| region = region.within(entry)); region } fn walk(&self, idx: MoveIdx, mut step: impl FnMut(&UiRegion)) { let mut at = idx; for _ in 0..CHAIN_LIMIT { if at == MoveIdx::NONE { return; } let entry = &self.arena[at.idx()]; step(&entry.region); at = entry.parent; } debug_assert!( at == MoveIdx::NONE, "a move chain longer than {CHAIN_LIMIT} resolves to the wrong place, \ and the shader stops at the same depth" ); } /// How many slots a region in `idx` is composed through, which is what /// the shader's walk costs per primitive. pub fn depth(&self, idx: MoveIdx) -> usize { let mut depth = 0; let mut at = idx; while at != MoveIdx::NONE && depth < CHAIN_LIMIT as usize { at = self.arena[at.idx()].parent; depth += 1; } depth } pub fn entries(&self) -> &[MoveOffset] { &self.arena } pub fn clear(&mut self) { self.changed = true; self.arena = Arena::default(); } } pub trait UiRsc { fn ui(&self) -> &UiData; fn ui_mut(&mut self) -> &mut UiData; #[allow(unused_variables)] fn on_add(&mut self, id: WeakWidget) {} #[allow(unused_variables)] fn on_remove(&mut self, id: WidgetId) {} #[allow(unused_variables)] fn on_draw(&mut self, active: &ActiveData) {} #[allow(unused_variables)] fn on_undraw(&mut self, active: &ActiveData) {} fn widgets(&self) -> &Widgets { &self.ui().widgets } fn widgets_mut(&mut self) -> &mut Widgets { &mut self.ui_mut().widgets } fn free(&mut self) { while let Some(id) = self.widgets_mut().free_next() { self.on_remove(id); } self.ui_mut().textures.free(); } }