Files
iris/core/src/ui/mod.rs
T

140 lines
4.1 KiB
Rust

use crate::{
Mask, MoveIdx, MoveOffset, PrimitiveRegistry, TextData, Textures, UiRegion, WeakWidget,
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<Mask, u32>,
}
/// 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<MoveOffset, u32>,
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, which is
/// the same walk the vertex shader does.
pub fn resolve(&self, idx: MoveIdx, local: UiRegion) -> UiRegion {
let mut region = local;
let mut at = idx;
for _ in 0..CHAIN_LIMIT {
if at == MoveIdx::NONE {
return region;
}
let entry = self.arena[at.idx()];
region = region.within(&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"
);
region
}
/// 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();
}
}