diff --git a/core/src/orientation/align.rs b/core/src/orientation/align.rs index 3008ae5..ea10dbf 100644 --- a/core/src/orientation/align.rs +++ b/core/src/orientation/align.rs @@ -2,7 +2,7 @@ use crate::vec2; use super::*; -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Align { pub x: Option, pub y: Option, @@ -43,7 +43,7 @@ impl Align { } } -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum AxisAlign { Neg, Center, diff --git a/core/src/ui/active.rs b/core/src/ui/active.rs index b2c6ec9..3c47159 100644 --- a/core/src/ui/active.rs +++ b/core/src/ui/active.rs @@ -1,14 +1,16 @@ -use crate::{LayerId, MaskIdx, PrimitiveHandle, TextureHandle, UiRegion, WidgetId}; +use crate::{Align, LayerId, MaskIdx, PrimitiveHandle, TextureHandle, UiRegion, WidgetId}; /// important non rendering data for retained drawing #[derive(Debug)] pub struct ActiveData { pub id: WidgetId, pub region: UiRegion, + pub region_used: UiRegion, pub parent: Option, pub textures: Vec, pub primitives: Vec, pub children: Vec, pub mask: MaskIdx, pub layer: LayerId, + pub align: Align, } diff --git a/core/src/ui/cache.rs b/core/src/ui/cache.rs deleted file mode 100644 index df4961a..0000000 --- a/core/src/ui/cache.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::{BothAxis, Len, UiRegion, UiVec2, WidgetId, util::HashMap}; - -#[derive(Default)] -pub struct Cache { - pub size: BothAxis>, - pub moves: HashMap, -} - -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 2daa257..6d4a637 100644 --- a/core/src/ui/draw_state.rs +++ b/core/src/ui/draw_state.rs @@ -1,5 +1,5 @@ use crate::{ - ActiveData, Align, Axis, EventsLike, Painter, SizeCtx, Ui, UiRegion, UiVec2, WidgetId, + ActiveData, Align, Axis, EventsLike, Painter, Ui, UiRegion, WidgetId, render::MaskIdx, util::{HashSet, forget_ref}, }; @@ -25,58 +25,27 @@ impl<'a> DrawState<'a> { while let Some(&id) = self.widgets.needs_redraw.iter().next() { self.redraw(id); } - self.apply_moves(); self.ui.free(self.events); } /// 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.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 Some(active) = self.remove(id, false) else { + let Some(active) = self.active.get(&id) else { return; }; - - self.draw_inner( - active.layer, + let ActiveData { id, - active.region, - active.parent, - active.mask, - Some(active.children), - ); - } + region, + parent, + mask, + layer, + align, + .. + } = *active; - pub(super) fn size_ctx<'b>(&'b mut self, source: WidgetId, outer: UiVec2) -> SizeCtx<'b> { - SizeCtx { - source, - 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, - id: source, - } + self.draw_inner(layer, id, region, parent, mask, align); } pub fn redraw_all(&mut self) { @@ -84,23 +53,13 @@ impl<'a> DrawState<'a> { for (_, active) in self.ui.active.drain() { self.events.undraw(&active); } - self.ui.cache.clear(); self.ui.free(self.events); 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, - Align::NONE, - ); + self.draw_inner(0, id.id(), UiRegion::FULL, None, MaskIdx::NONE, Align::NONE); } - self.apply_moves(); } pub(super) fn draw_inner( @@ -110,21 +69,19 @@ impl<'a> DrawState<'a> { region: UiRegion, parent: Option, mask: MaskIdx, - old_children: Option>, align: Align, - ) -> UiRegion { - let mut old_children = old_children.unwrap_or_default(); - if let Some(active) = self.ui.active.get_mut(&id) - && !self.ui.widgets.needs_redraw.contains(&id) - { + ) { + let mut old_children = Vec::new(); + if let Some(active) = self.ui.active.get_mut(&id) { // check to see if we can skip drawing first, and just need to move - if active.region == region { - return region; - } else if active.region.size() == region.size() { - self.ui.cache.moves.insert(id, region); - return region; + if !self.ui.widgets.needs_redraw.contains(&id) { + if active.region == region { + return; + } else if active.region.size() == region.size() { + self.mov(id, active.region, 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; } @@ -161,47 +118,37 @@ impl<'a> DrawState<'a> { id, } = painter; - // TODO: unsure if there's a better way, currently using epsilon - // in PartialEq impl for UiScalar - let target = region_used.size().align(align); - if region_used != target { - TODO; // THIS WILL NOT WORK to avoid removing, need to look at children? - self.ui.cache.moves.insert(id, target); - } - - // 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) { + if !children.contains(c) { self.remove_rec(*c); } } - // update modules - self.events.draw(&active); - self.active.insert(id, active); + // add to active + self.active.insert( + id, + ActiveData { + id, + region, + parent, + textures, + primitives, + children, + region_used, + mask, + layer, + }, + ); - region_used - } - - fn apply_moves(&mut self) { - let mut moves = std::mem::take(&mut self.ui.cache.moves); - for (id, to) in moves.drain() { - let from = self.active.get(&id).unwrap().region; + // TODO: unsure if there's a better way, currently using epsilon + // in PartialEq impl for UiScalar + let target = region_used.size().align(align); + if region_used != target { self.mov(id, from, to); } - self.ui.cache.moves = moves; + + self.events.draw(self.active.get(&id).unwrap()); } fn mov(&mut self, id: WidgetId, from: UiRegion, to: UiRegion) { @@ -238,7 +185,6 @@ impl<'a> DrawState<'a> { } 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 bb5ecca..d93acc3 100644 --- a/core/src/ui/mod.rs +++ b/core/src/ui/mod.rs @@ -11,16 +11,12 @@ 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::*; pub struct Ui { // TODO: edit visibilities @@ -33,7 +29,6 @@ pub struct Ui { pub text: TextData, output_size: Vec2, pub masks: TrackedArena, - pub cache: Cache, pub root: Option, old_root: Option, @@ -206,7 +201,6 @@ impl Default for Ui { masks: Default::default(), text: Default::default(), textures: Default::default(), - cache: Default::default(), output_size: Vec2::ZERO, root: None, old_root: None, diff --git a/core/src/ui/painter.rs b/core/src/ui/painter.rs index 9304995..172f22d 100644 --- a/core/src/ui/painter.rs +++ b/core/src/ui/painter.rs @@ -1,6 +1,5 @@ use crate::{ - Axis, Len, RenderedText, Size, SizeCtx, TextAttrs, TextBuffer, TextData, TextureHandle, - UiRegion, Widget, WidgetHandle, WidgetId, + RenderedText, TextAttrs, TextBuffer, TextData, TextureHandle, UiRegion, WidgetHandle, WidgetId, render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst}, ui::draw_state::DrawState, util::Vec2, @@ -57,6 +56,10 @@ impl<'a, 'c> Painter<'a, 'c> { self.widget_at(id, self.region); } + pub fn rest_hint(&self, id: &WidgetHandle) -> f32 { + self.state.widgets.data(id).unwrap().align + } + /// Draws a widget somewhere within this one. /// Useful for drawing child widgets in select areas. pub fn widget_within(&mut self, id: &WidgetHandle, region: UiRegion) { @@ -66,7 +69,7 @@ impl<'a, 'c> Painter<'a, 'c> { fn widget_at(&mut self, id: &WidgetHandle, region: UiRegion) { self.children.push(id.id()); self.state - .draw_inner(self.layer, id.id(), region, Some(self.id), self.mask, None); + .draw_inner(self.layer, id.id(), region, Some(self.id), self.mask); } pub fn texture_within(&mut self, handle: &TextureHandle, region: UiRegion) { @@ -79,11 +82,6 @@ impl<'a, 'c> Painter<'a, 'c> { self.primitive(handle.primitive()); } - pub fn texture_at(&mut self, handle: &TextureHandle, region: UiRegion) { - self.textures.push(handle.clone()); - self.primitive_at(handle.primitive(), region); - } - /// returns (handle, offset from top left) pub fn render_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> RenderedText { self.state @@ -96,17 +94,6 @@ impl<'a, 'c> Painter<'a, 'c> { self.region } - pub fn size(&mut self, id: &WidgetHandle) -> Size { - self.size_ctx().size(id) - } - - 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), - } - } - pub fn output_size(&self) -> Vec2 { self.state.output_size } @@ -134,8 +121,4 @@ impl<'a, 'c> Painter<'a, 'c> { pub fn id(&self) -> &WidgetId { &self.id } - - pub fn size_ctx(&mut self) -> SizeCtx<'_> { - self.state.size_ctx(self.id, self.region.size()) - } } diff --git a/core/src/ui/size.rs b/core/src/ui/size.rs deleted file mode 100644 index 7d9c2d9..0000000 --- a/core/src/ui/size.rs +++ /dev/null @@ -1,86 +0,0 @@ -use crate::{ - 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> { - pub text: &'a mut TextData, - pub textures: &'a mut Textures, - pub(super) source: WidgetId, - pub(super) widgets: &'a Widgets, - pub(super) cache: &'a mut Cache, - /// TODO: should this be pub? rn used for sized - pub outer: UiVec2, - pub(super) output_size: Vec2, - pub(super) id: WidgetId, -} - -impl SizeCtx<'_> { - pub fn id(&self) -> &WidgetId { - &self.id - } - - pub fn source(&self) -> &WidgetId { - &self.source - } - - 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(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 - } - - pub fn width(&mut self, id: impl IdLike) -> Len { - self.len_inner::(id.id()) - } - - pub fn height(&mut self, id: impl IdLike) -> Len { - self.len_inner::(id.id()) - } - - 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: impl IdLike) -> Size { - let id = id.id(); - Size { - x: self.width(id), - y: self.height(id), - } - } - - pub fn px_size(&mut self) -> Vec2 { - self.outer.to_abs(self.output_size) - } - - pub fn output_size(&mut self) -> Vec2 { - self.output_size - } - - pub fn draw_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> RenderedText { - self.text.draw(buffer, attrs, self.textures) - } - - pub fn label(&self, id: WidgetId) -> &String { - self.widgets.label(id) - } -} diff --git a/core/src/widget/data.rs b/core/src/widget/data.rs index 594d59b..a2d1a81 100644 --- a/core/src/widget/data.rs +++ b/core/src/widget/data.rs @@ -6,6 +6,7 @@ pub struct WidgetData { /// alignment used if this does not fill up container /// and there is no enforced alignment from parent pub align: RegionAlign, + pub rest_len: Option, /// dynamic borrow checking pub borrowed: bool, } @@ -20,6 +21,7 @@ impl WidgetData { widget: Box::new(widget), align: RegionAlign::CENTER, label, + rest_len: None, borrowed: false, } } diff --git a/core/src/widget/mod.rs b/core/src/widget/mod.rs index 89348ad..c5382cd 100644 --- a/core/src/widget/mod.rs +++ b/core/src/widget/mod.rs @@ -1,4 +1,4 @@ -use crate::{Axis, AxisT, Len, Painter, SizeCtx}; +use crate::Painter; use std::any::Any; mod data; @@ -15,31 +15,10 @@ pub use widgets::*; pub trait Widget: Any { fn draw(&mut self, painter: &mut Painter); - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len; - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len; -} - -pub trait WidgetAxisFns { - fn desired_len(&mut self, ctx: &mut SizeCtx) -> Len; -} - -impl 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 { - Len::ZERO - } - fn desired_height(&mut self, _: &mut SizeCtx) -> Len { - Len::ZERO - } } impl dyn Widget { diff --git a/src/widget/position/span.rs b/src/widget/position/span.rs index 7431b46..8e0e2ec 100644 --- a/src/widget/position/span.rs +++ b/src/widget/position/span.rs @@ -9,11 +9,12 @@ pub struct Span { impl Widget for Span { fn draw(&mut self, painter: &mut Painter) { - let total = self.len_sum(&mut painter.size_ctx()); + let total = self.len_sum(); let mut start = UiScalar::rel_min(); for child in &self.children { let mut span = UiSpan::FULL; span.start = start; + painter.widget(id); let len = painter.len_axis(child, self.dir.axis); if len.rest > 0.0 { let offset = UiScalar::new(total.rel, total.abs); @@ -32,20 +33,6 @@ impl Widget for Span { start.abs += self.gap; } } - - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { - match self.dir.axis { - Axis::X => self.desired_len(ctx), - Axis::Y => self.desired_ortho(ctx), - } - } - - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { - match self.dir.axis { - Axis::X => self.desired_ortho(ctx), - Axis::Y => self.desired_len(ctx), - } - } } impl Span { @@ -70,22 +57,6 @@ impl Span { self.children.pop() } - fn len_sum(&mut self, ctx: &mut SizeCtx) -> Len { - let gap = self.gap * self.children.len().saturating_sub(1) as f32; - self.children.iter().fold(Len::abs(gap), |mut s, id| { - // it's tempting to subtract the abs & rel from the ctx outer, - // but that would create inconsistent sizing if you put - // a rest first vs last & only speed up in one direction. - // I think this is only solvable by restricting how you can - // compute size, bc currently you need child to define parent's - // sectioning and you need parent's sectioning to define child. - // Fortunately, that doesn't matter in most cases - let len = ctx.len_axis(id, self.dir.axis); - s += len; - s - }) - } - fn desired_len(&mut self, ctx: &mut SizeCtx) -> Len { let len = self.len_sum(ctx); if len.rest == 0.0 && len.rel == 0.0 {