diff --git a/core/src/orientation/axis.rs b/core/src/orientation/axis.rs index 5b58a7e..0f31958 100644 --- a/core/src/orientation/axis.rs +++ b/core/src/orientation/axis.rs @@ -68,3 +68,48 @@ impl Vec2 { } } } + +pub const trait AxisT { + fn get() -> Axis; +} + +pub struct XAxis; +impl const AxisT for XAxis { + fn get() -> Axis { + Axis::X + } +} + +pub struct YAxis; +impl const AxisT for YAxis { + fn get() -> Axis { + Axis::Y + } +} + +#[derive(Clone, Copy, Debug, Default)] +pub struct BothAxis { + pub x: T, + pub y: T, +} + +impl BothAxis { + pub const fn axis(&mut self) -> &mut T { + match A::get() { + Axis::X => &mut self.x, + Axis::Y => &mut self.y, + } + } + pub fn take_axis(self) -> T { + match A::get() { + Axis::X => self.x, + Axis::Y => self.y, + } + } + pub fn axis_dyn(&mut self, axis: Axis) -> &mut T { + match axis { + Axis::X => &mut self.x, + Axis::Y => &mut self.y, + } + } +} diff --git a/core/src/primitive/text.rs b/core/src/primitive/text.rs index 0e557a7..663af0f 100644 --- a/core/src/primitive/text.rs +++ b/core/src/primitive/text.rs @@ -58,7 +58,7 @@ pub type TextBuffer = Buffer; impl Default for TextAttrs { fn default() -> Self { - let size = 14.0; + let size = 16.0; Self { color: UiColor::WHITE, font_size: size, diff --git a/core/src/ui/active.rs b/core/src/ui/active.rs index 76c7438..b2c6ec9 100644 --- a/core/src/ui/active.rs +++ b/core/src/ui/active.rs @@ -1,4 +1,4 @@ -use crate::{LayerId, Len, MaskIdx, PrimitiveHandle, TextureHandle, UiRegion, UiVec2, WidgetId}; +use crate::{LayerId, MaskIdx, PrimitiveHandle, TextureHandle, UiRegion, WidgetId}; /// important non rendering data for retained drawing #[derive(Debug)] @@ -9,15 +9,6 @@ pub struct ActiveData { pub textures: Vec, pub primitives: Vec, pub children: Vec, - pub resize: ResizeRef, pub mask: MaskIdx, pub layer: LayerId, } - -/// stores information for children about the highest level parent that needed their size -/// so that they can redraw the parent if their size changes -#[derive(Clone, Copy, Debug, Default)] -pub struct ResizeRef { - pub x: Option<(WidgetId, (UiVec2, Len))>, - pub y: Option<(WidgetId, (UiVec2, Len))>, -} diff --git a/core/src/ui/cache.rs b/core/src/ui/cache.rs new file mode 100644 index 0000000..10565ee --- /dev/null +++ b/core/src/ui/cache.rs @@ -0,0 +1,18 @@ +use crate::{BothAxis, Len, UiVec2, WidgetId, util::HashMap}; + +#[derive(Default)] +pub struct Cache { + pub size: BothAxis>, +} + +impl Cache { + pub fn remove(&mut self, id: WidgetId) { + self.size.x.remove(&id); + self.size.y.remove(&id); + } + + pub fn clear(&mut self) { + self.size.x.clear(); + self.size.y.clear(); + } +} diff --git a/core/src/ui/draw_state.rs b/core/src/ui/draw_state.rs index 338584d..91c8cd2 100644 --- a/core/src/ui/draw_state.rs +++ b/core/src/ui/draw_state.rs @@ -1,16 +1,12 @@ use crate::{ - ActiveData, Len, Painter, SizeCtx, Ui, UiRegion, UiVec2, WidgetId, - render::MaskIdx, - ui::ResizeRef, - util::{HashMap, HashSet}, + ActiveData, Axis, Painter, SizeCtx, Ui, UiRegion, UiVec2, WidgetId, render::MaskIdx, + util::HashSet, }; use std::ops::{Deref, DerefMut}; /// state maintained between widgets during painting pub struct DrawState<'a, State> { pub(super) ui: &'a mut Ui, - cache_width: HashMap, - cache_height: HashMap, draw_started: HashSet, } @@ -18,8 +14,6 @@ impl<'a, State: 'static> DrawState<'a, State> { pub fn new(ui: &'a mut Ui) -> Self { Self { ui, - cache_width: Default::default(), - cache_height: Default::default(), draw_started: Default::default(), } } @@ -39,64 +33,23 @@ impl<'a, State: 'static> DrawState<'a, State> { if self.draw_started.contains(&id) { return; } - let Some(active) = self.ui.active.get(&id) else { + // 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.cache.size.axis_dyn(axis).get(&id) + && let Some(current) = self.active.get(&id) + && let Some(pid) = current.parent + { + self.cache.size.axis_dyn(axis).remove(&id); + let new = self.size_ctx(id, outer).len_axis(id, axis); + self.cache.size.axis_dyn(axis).insert(id, (outer, new)); + if new != old { + self.redraw(pid); + } + } + } + + if self.draw_started.contains(&id) { return; - }; - let mut resize = active.resize; - - // set resize back after redrawing - let finish = |s: &mut Self, resize| { - if let Some(active) = s.active.get_mut(&id) { - // might need to get_or_insert here instead of just assuming - active.resize = resize; - } - }; - - // check if a parent depends on the desired size of this, if so then redraw it first - // TODO: this is stupid having 2 of these, don't ask me what the consequences are - let mut ret = false; - if let Some((rid, (outer, old_desired))) = &mut resize.x { - let new_desired = self - .size_ctx( - id, - *outer, - id, - &mut Default::default(), - &mut Default::default(), - ) - .width_inner(id); - if new_desired != *old_desired { - // unsure if I need to walk down the tree here - self.redraw(*rid); - *old_desired = new_desired; - if self.draw_started.contains(&id) { - ret = true; - } - } - } - - if let Some((rid, (outer, old_desired))) = &mut resize.y { - // NOTE: might need hack in Span here (or also do it properly here) - let new_desired = self - .size_ctx( - id, - *outer, - id, - &mut Default::default(), - &mut Default::default(), - ) - .height_inner(id); - if new_desired != *old_desired { - self.redraw(*rid); - *old_desired = new_desired; - if self.draw_started.contains(&id) { - ret = true; - } - } - } - - if ret { - return finish(self, resize); } let Some(active) = self.remove(id, false) else { @@ -111,43 +64,36 @@ impl<'a, State: 'static> DrawState<'a, State> { active.mask, Some(active.children), ); - finish(self, resize); } pub(super) fn size_ctx<'b>( &'b mut self, source: WidgetId, outer: UiVec2, - id: WidgetId, - checked_width: &'b mut HashMap, - checked_height: &'b mut HashMap, ) -> SizeCtx<'b, State> { SizeCtx { source, - cache_width: &mut self.cache_width, - cache_height: &mut self.cache_height, + cache: &mut self.ui.cache, text: &mut self.ui.text, textures: &mut self.ui.textures, widgets: &self.ui.widgets, outer, output_size: self.ui.output_size, - checked_width, - checked_height, - id, + id: source, } } pub fn redraw_all(&mut self) { - // update event managers + // free all resources & cache for (id, active) in self.ui.active.drain() { let data = self.ui.widgets.data(id).unwrap(); self.ui.events.undraw(data, &active); } - // free before bc nothing should exist + self.ui.cache.clear(); self.free(); - self.layers.clear(); self.widgets.needs_redraw.clear(); + if let Some(id) = &self.ui.root { self.draw_inner(0, id.id(), UiRegion::FULL, None, MaskIdx::NONE, None); } @@ -175,7 +121,6 @@ impl<'a, State: 'static> DrawState<'a, State> { // ); // } let mut old_children = old_children.unwrap_or_default(); - let mut resize = ResizeRef::default(); if let Some(active) = self.ui.active.get_mut(&id) && !self.ui.widgets.needs_redraw.contains(&id) { @@ -194,7 +139,6 @@ impl<'a, State: 'static> DrawState<'a, State> { // if not, then maintain resize and track old children to remove unneeded let active = self.remove(id, false).unwrap(); old_children = active.children; - resize = active.resize; } // draw widget @@ -209,8 +153,6 @@ impl<'a, State: 'static> DrawState<'a, State> { textures: Vec::new(), primitives: Vec::new(), children: Vec::new(), - children_width: Default::default(), - children_height: Default::default(), }; let mut widget = painter.state.widgets.get_dyn_dynamic(id); @@ -224,8 +166,6 @@ impl<'a, State: 'static> DrawState<'a, State> { textures, primitives, children, - children_width, - children_height, layer, id, } = painter; @@ -238,27 +178,10 @@ impl<'a, State: 'static> DrawState<'a, State> { textures, primitives, children, - resize, mask, layer, }; - // set resize for children who's size this widget depends on - for (cid, outer) in children_width { - if let Some(w) = self.active.get_mut(&cid) - && w.resize.x.is_none() - { - w.resize.x = Some((id, outer)) - } - } - for (cid, outer) in children_height { - if let Some(w) = self.active.get_mut(&cid) - && w.resize.y.is_none() - { - w.resize.y = Some((id, outer)) - } - } - // remove old children that weren't kept for c in &old_children { if !active.children.contains(c) { @@ -308,6 +231,7 @@ impl<'a, State: 'static> DrawState<'a, State> { } fn remove_rec(&mut self, id: WidgetId) -> Option { + self.cache.remove(id); let inst = self.remove(id, true); if let Some(inst) = &inst { for c in &inst.children { diff --git a/core/src/ui/mod.rs b/core/src/ui/mod.rs index 4f1ecdb..6371b3a 100644 --- a/core/src/ui/mod.rs +++ b/core/src/ui/mod.rs @@ -11,12 +11,14 @@ use std::{ }; mod active; +mod cache; mod draw_state; mod painter; mod size; mod state; pub use active::*; +use cache::*; pub use painter::Painter; pub use size::*; @@ -32,6 +34,7 @@ pub struct Ui { pub text: TextData, output_size: Vec2, pub masks: TrackedArena, + pub cache: Cache, root: Option>, recv: Receiver, @@ -202,6 +205,7 @@ impl Default for Ui { masks: Default::default(), text: Default::default(), textures: Default::default(), + cache: Default::default(), output_size: Vec2::ZERO, root: Default::default(), full_redraw: false, diff --git a/core/src/ui/painter.rs b/core/src/ui/painter.rs index 3256b2d..730ff2c 100644 --- a/core/src/ui/painter.rs +++ b/core/src/ui/painter.rs @@ -1,9 +1,9 @@ use crate::{ Axis, Len, RenderedText, Size, SizeCtx, TextAttrs, TextBuffer, TextData, TextureHandle, - UiRegion, UiVec2, WidgetHandle, WidgetId, + UiRegion, Widget, WidgetHandle, WidgetId, render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst}, ui::draw_state::DrawState, - util::{HashMap, Vec2}, + util::Vec2, }; /// makes your surfaces look pretty @@ -15,8 +15,6 @@ pub struct Painter<'a, 'b, State> { pub(super) textures: Vec, pub(super) primitives: Vec, pub(super) children: Vec, - pub(super) children_width: HashMap, - pub(super) children_height: HashMap, pub layer: usize, pub(super) id: WidgetId, } @@ -97,11 +95,15 @@ impl<'a, 'c, State: 'static> Painter<'a, 'c, State> { self.region } - pub fn size(&mut self, id: &WidgetHandle) -> Size { + pub fn size>(&mut self, id: &WidgetHandle) -> Size { self.size_ctx().size(id) } - pub fn len_axis(&mut self, id: &WidgetHandle, axis: Axis) -> Len { + pub fn len_axis>( + &mut self, + id: &WidgetHandle, + axis: Axis, + ) -> Len { match axis { Axis::X => self.size_ctx().width(id), Axis::Y => self.size_ctx().height(id), @@ -137,12 +139,6 @@ impl<'a, 'c, State: 'static> Painter<'a, 'c, State> { } pub fn size_ctx(&mut self) -> SizeCtx<'_, State> { - self.state.size_ctx( - self.id, - self.region.size(), - self.id, - &mut self.children_width, - &mut self.children_height, - ) + self.state.size_ctx(self.id, self.region.size()) } } diff --git a/core/src/ui/size.rs b/core/src/ui/size.rs index 5042ed0..eecc1f9 100644 --- a/core/src/ui/size.rs +++ b/core/src/ui/size.rs @@ -1,7 +1,6 @@ use crate::{ - Axis, Len, RenderedText, Size, TextAttrs, TextBuffer, TextData, Textures, UiVec2, WidgetHandle, - WidgetId, Widgets, - util::{HashMap, Vec2}, + Axis, AxisT, IdLike, Len, RenderedText, Size, TextAttrs, TextBuffer, TextData, Textures, + UiVec2, WidgetAxisFns, WidgetId, Widgets, XAxis, YAxis, ui::cache::Cache, util::Vec2, }; pub struct SizeCtx<'a, State> { @@ -9,10 +8,7 @@ pub struct SizeCtx<'a, State> { pub textures: &'a mut Textures, pub(super) source: WidgetId, pub(super) widgets: &'a Widgets, - pub(super) cache_width: &'a mut HashMap, - pub(super) cache_height: &'a mut HashMap, - pub(super) checked_width: &'a mut HashMap, - pub(super) checked_height: &'a mut HashMap, + pub(super) cache: &'a mut Cache, /// TODO: should this be pub? rn used for sized pub outer: UiVec2, pub(super) output_size: Vec2, @@ -28,65 +24,44 @@ impl SizeCtx<'_, State> { &self.source } - pub(super) fn width_inner(&mut self, id: WidgetId) -> Len { - // first check cache - // TODO: is this needed? broken rn bc does not store children during upper size check, - // so if something actually using check_* hits cache it fails to add them - // if let Some(&(outer, len)) = self.cache_width.get(&id) - // && outer == self.outer - // { - // self.checked_width.insert(id, (self.outer, len)); - // return len; - // } - // store self vars that need to be maintained - let self_outer = self.outer; - let self_id = self.id; - // get size of input id - self.id = id; - let len = self.widgets.get_dyn_dynamic(id).desired_width(self); - // restore vars & update cache + checked - self.outer = self_outer; - self.id = self_id; - self.cache_width.insert(id, (self.outer, len)); - self.checked_width.insert(id, (self.outer, len)); + pub(super) fn len_inner(&mut self, id: WidgetId) -> Len { + if let Some((_, len)) = self.cache.size.axis::().get(&id) { + return *len; + } + let len = self + .widgets + .get_dyn_dynamic(id) + .desired_len::(&mut SizeCtx { + text: self.text, + textures: self.textures, + source: self.source, + widgets: self.widgets, + cache: self.cache, + outer: self.outer, + output_size: self.output_size, + id, + }); + self.cache.size.axis::().insert(id, (self.outer, len)); len } - // TODO: should be refactored to share code w width_inner - pub(super) fn height_inner(&mut self, id: WidgetId) -> Len { - // if let Some(&(outer, len)) = self.cache_height.get(&id) - // && outer == self.outer - // { - // self.checked_height.insert(id, (self.outer, len)); - // return len; - // } - let self_outer = self.outer; - let self_id = self.id; - self.id = id; - let len = self.widgets.get_dyn_dynamic(id).desired_height(self); - self.outer = self_outer; - self.id = self_id; - self.cache_height.insert(id, (self.outer, len)); - self.checked_height.insert(id, (self.outer, len)); - len + pub fn width(&mut self, id: impl IdLike) -> Len { + self.len_inner::(id.id()) } - pub fn width(&mut self, id: &WidgetHandle) -> Len { - self.width_inner(id.id()) + pub fn height(&mut self, id: impl IdLike) -> Len { + self.len_inner::(id.id()) } - pub fn height(&mut self, id: &WidgetHandle) -> Len { - self.height_inner(id.id()) - } - - pub fn len_axis(&mut self, id: &WidgetHandle, axis: Axis) -> Len { + pub fn len_axis(&mut self, id: impl IdLike, axis: Axis) -> Len { match axis { Axis::X => self.width(id), Axis::Y => self.height(id), } } - pub fn size(&mut self, id: &WidgetHandle) -> Size { + pub fn size(&mut self, id: impl IdLike) -> Size { + let id = id.id(); Size { x: self.width(id), y: self.height(id), diff --git a/core/src/widget/mod.rs b/core/src/widget/mod.rs index f9657d4..917a2c2 100644 --- a/core/src/widget/mod.rs +++ b/core/src/widget/mod.rs @@ -1,4 +1,4 @@ -use crate::{Len, Painter, SizeCtx, Ui}; +use crate::{Axis, AxisT, Len, Painter, SizeCtx, Ui}; use std::any::Any; mod data; @@ -19,6 +19,19 @@ pub trait Widget: Any { fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len; } +pub trait WidgetAxisFns { + fn desired_len(&mut self, ctx: &mut SizeCtx) -> Len; +} + +impl + ?Sized> WidgetAxisFns for W { + fn desired_len(&mut self, ctx: &mut SizeCtx) -> Len { + match A::get() { + Axis::X => self.desired_width(ctx), + Axis::Y => self.desired_height(ctx), + } + } +} + impl Widget for () { fn draw(&mut self, _: &mut Painter) {} fn desired_width(&mut self, _: &mut SizeCtx) -> Len {