From a952b34a7295c7fa4844a025079a8c536da92471 Mon Sep 17 00:00:00 2001 From: shadow cat Date: Thu, 20 Nov 2025 00:18:30 -0500 Subject: [PATCH] mistakes were fixed and sins were committed --- src/core/image.rs | 8 +- src/core/mask.rs | 8 ++ src/core/position/align.rs | 11 +- src/core/position/offset.rs | 8 +- src/core/position/pad.rs | 25 ++-- src/core/position/sized.rs | 37 ++++-- src/core/position/span.rs | 109 ++++++++++++++---- src/core/position/stack.rs | 13 ++- src/core/ptr.rs | 14 ++- src/core/rect.rs | 8 ++ src/core/text/edit.rs | 13 ++- src/core/text/mod.rs | 8 +- src/layout/orientation.rs | 29 +++-- src/layout/painter.rs | 224 ++++++++++++++++++++++++++---------- src/layout/pos.rs | 8 +- src/layout/ui.rs | 13 +-- src/layout/vec2.rs | 11 +- src/layout/widget.rs | 13 ++- 18 files changed, 418 insertions(+), 142 deletions(-) diff --git a/src/core/image.rs b/src/core/image.rs index 2ce1590..a2b58b9 100644 --- a/src/core/image.rs +++ b/src/core/image.rs @@ -10,8 +10,12 @@ impl Widget for Image { painter.texture(&self.handle); } - fn desired_size(&mut self, _: &mut SizeCtx) -> Size { - Size::abs(self.handle.size()) + fn desired_width(&mut self, _: &mut SizeCtx) -> Len { + Len::abs(self.handle.size().x) + } + + fn desired_height(&mut self, _: &mut SizeCtx) -> Len { + Len::abs(self.handle.size().y) } } diff --git a/src/core/mask.rs b/src/core/mask.rs index bd977d7..3cdad7d 100644 --- a/src/core/mask.rs +++ b/src/core/mask.rs @@ -9,4 +9,12 @@ impl Widget for Masked { painter.set_mask(painter.region()); painter.widget(&self.inner); } + + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + ctx.width(&self.inner) + } + + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + ctx.height(&self.inner) + } } diff --git a/src/core/position/align.rs b/src/core/position/align.rs index 591023a..28ed7df 100644 --- a/src/core/position/align.rs +++ b/src/core/position/align.rs @@ -7,11 +7,16 @@ pub struct Aligned { impl Widget for Aligned { fn draw(&mut self, painter: &mut Painter) { - let region = UiRegion::from_ui_size_align(painter.region_size(&self.inner), self.align); + let region = + UiRegion::from_ui_size_align(painter.size(&self.inner).to_uivec2(), self.align); painter.widget_within(&self.inner, region); } - fn desired_size(&mut self, ctx: &mut SizeCtx) -> Size { - ctx.size(&self.inner) + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + ctx.width(&self.inner) + } + + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + ctx.height(&self.inner) } } diff --git a/src/core/position/offset.rs b/src/core/position/offset.rs index 9559939..32286f2 100644 --- a/src/core/position/offset.rs +++ b/src/core/position/offset.rs @@ -11,7 +11,11 @@ impl Widget for Offset { painter.widget_within(&self.inner, region); } - fn desired_size(&mut self, ctx: &mut SizeCtx) -> Size { - ctx.size(&self.inner) + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + ctx.width(&self.inner) + } + + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + ctx.height(&self.inner) } } diff --git a/src/core/position/pad.rs b/src/core/position/pad.rs index 1fdcdad..276c4df 100644 --- a/src/core/position/pad.rs +++ b/src/core/position/pad.rs @@ -10,14 +10,23 @@ impl Widget for Pad { painter.widget_within(&self.inner, self.padding.region()); } - fn desired_size(&mut self, ctx: &mut SizeCtx) -> Size { - let mut size = ctx.size(&self.inner); - if size.x.rest == 0.0 { - size.x.abs += self.padding.left + self.padding.right; - } - if size.y.rest == 0.0 { - size.y.abs += self.padding.top + self.padding.bottom; - } + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + let width = self.padding.left + self.padding.right; + let height = self.padding.top + self.padding.bottom; + ctx.outer.abs.x -= width; + ctx.outer.abs.y -= height; + let mut size = ctx.width(&self.inner); + size.abs += width; + size + } + + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + let width = self.padding.left + self.padding.right; + let height = self.padding.top + self.padding.bottom; + ctx.outer.abs.x -= width; + ctx.outer.abs.y -= height; + let mut size = ctx.height(&self.inner); + size.abs += height; size } } diff --git a/src/core/position/sized.rs b/src/core/position/sized.rs index 2558e39..706c8a5 100644 --- a/src/core/position/sized.rs +++ b/src/core/position/sized.rs @@ -6,22 +6,35 @@ pub struct Sized { pub y: Option, } +impl Sized { + fn apply_to_outer(&self, ctx: &mut SizeCtx) { + if let Some(x) = self.x { + let outer = ctx.outer.axis(Axis::X); + ctx.outer + .axis_mut(Axis::X) + .set(x.apply_rest().within_len(outer)); + } + if let Some(y) = self.y { + let outer = ctx.outer.axis(Axis::Y); + ctx.outer + .axis_mut(Axis::Y) + .set(y.apply_rest().within_len(outer)); + } + } +} + impl Widget for Sized { fn draw(&mut self, painter: &mut Painter) { painter.widget(&self.inner); } - fn desired_size(&mut self, ctx: &mut SizeCtx) -> Size { - let rel = ctx.size.rel; - if let Some(x) = self.x { - ctx.size.axis_mut(Axis::X).set(x.apply_rest(rel.x)); - } - if let Some(y) = self.y { - ctx.size.axis_mut(Axis::Y).set(y.apply_rest(rel.y)); - } - Size { - x: self.x.unwrap_or_else(|| ctx.size(&self.inner).x), - y: self.y.unwrap_or_else(|| ctx.size(&self.inner).y), - } + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + self.apply_to_outer(ctx); + self.x.unwrap_or_else(|| ctx.width(&self.inner)) + } + + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + self.apply_to_outer(ctx); + self.y.unwrap_or_else(|| ctx.height(&self.inner)) } } diff --git a/src/core/position/span.rs b/src/core/position/span.rs index d876467..535db00 100644 --- a/src/core/position/span.rs +++ b/src/core/position/span.rs @@ -16,7 +16,7 @@ impl Widget for Span { let mut child_region = UiRegion::full(); let mut axis = child_region.axis_mut(self.dir.axis); axis.top_left.set(start); - let len = painter.size(child).axis(self.dir.axis); + let len = painter.len_axis(child, self.dir.axis); if len.rest > 0.0 { let offset = UiScalar::new(total.rel, total.abs); let rel_end = UiScalar::from_anchor(len.rest / total.rest); @@ -33,25 +33,18 @@ impl Widget for Span { } } - fn desired_size(&mut self, ctx: &mut SizeCtx) -> Size { - let sums = self.len_sum(ctx); - let dir_len = if sums.rest == 0.0 && sums.rel == 0.0 { - sums - } else { - Len::default() - }; - let mut max_ortho = Len::ZERO; - for child in &self.children { - let len = ctx.size(child).axis(!self.dir.axis); - // TODO: rel shouldn't do this, but no easy way before actually calculating pixels - if len.rel > 0.0 || len.rest > 0.0 { - max_ortho.rest = 1.0; - max_ortho.abs = 0.0; - break; - } - max_ortho.abs = max_ortho.abs.max(len.abs); + 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), } - Size::from_axis(self.dir.axis, dir_len, max_ortho) } } @@ -71,11 +64,85 @@ impl Span { fn len_sum(&mut self, ctx: &mut SizeCtx) -> Len { let gap = self.gap * self.children.len().saturating_sub(1) as f32; - self.children.iter_mut().fold(Len::abs(gap), |mut s, id| { - s += ctx.size(id).axis(self.dir.axis); + 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 { + len + } else { + Len::default() + } + } + + fn desired_ortho(&mut self, ctx: &mut SizeCtx) -> Len { + // this is an awful hack to get text wrapping to work properly when in a downward span + if self.dir.axis == Axis::X { + // so....... this literally copies draw so that the lengths are correctly set in the + // context, which makes this slow and not cool + let total = self.len_sum(ctx); + let mut start = UiScalar::rel_min(); + let outer = ctx.outer.axis(self.dir.axis); + let mut ortho_len = Len::ZERO; + for child in &self.children { + let mut child_region = UiRegion::full(); + let mut axis = child_region.axis_mut(self.dir.axis); + axis.top_left.set(start); + let len = ctx.len_axis(child, self.dir.axis); + if len.rest > 0.0 { + let offset = UiScalar::new(total.rel, total.abs); + let rel_end = UiScalar::from_anchor(len.rest / total.rest); + start = rel_end.within(start, (UiScalar::rel_max() + start) - offset); + } + start.abs += len.abs; + start.rel += len.rel; + axis.bot_right.set(start); + // if self.dir.sign == Sign::Neg { + // child_region.flip(self.dir.axis); + // } + let scalar = child_region.size().axis(self.dir.axis); + ctx.outer + .axis_mut(self.dir.axis) + .set(scalar.within_len(outer)); + let ortho = ctx.len_axis(child, !self.dir.axis); + // TODO: rel shouldn't do this, but no easy way before actually calculating pixels + if ortho.rel > 0.0 || ortho.rest > 0.0 { + ortho_len.rest = 1.0; + ortho_len.abs = 0.0; + break; + } + ortho_len.abs = ortho_len.abs.max(ortho.abs); + start.abs += self.gap; + } + ortho_len + } else { + let mut ortho_len = Len::ZERO; + let ortho = !self.dir.axis; + for child in &self.children { + let len = ctx.len_axis(child, ortho); + // TODO: rel shouldn't do this, but no easy way before actually calculating pixels + if len.rel > 0.0 || len.rest > 0.0 { + ortho_len.rest = 1.0; + ortho_len.abs = 0.0; + break; + } + ortho_len.abs = ortho_len.abs.max(len.abs); + } + ortho_len + } + } } pub struct SpanBuilder, Tag> { diff --git a/src/core/position/stack.rs b/src/core/position/stack.rs index aefed57..60bfb74 100644 --- a/src/core/position/stack.rs +++ b/src/core/position/stack.rs @@ -24,10 +24,17 @@ impl Widget for Stack { } } - fn desired_size(&mut self, ctx: &mut SizeCtx) -> Size { + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { match self.size { - StackSize::Default => Size::default(), - StackSize::Child(i) => ctx.size(&self.children[i]), + StackSize::Default => Len::default(), + StackSize::Child(i) => ctx.width(&self.children[i]), + } + } + + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + match self.size { + StackSize::Default => Len::default(), + StackSize::Child(i) => ctx.height(&self.children[i]), } } } diff --git a/src/core/ptr.rs b/src/core/ptr.rs index ed806e5..a650c29 100644 --- a/src/core/ptr.rs +++ b/src/core/ptr.rs @@ -12,11 +12,19 @@ impl Widget for WidgetPtr { } } - fn desired_size(&mut self, ctx: &mut SizeCtx) -> Size { + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { if let Some(id) = &self.inner { - ctx.size(id) + ctx.width(id) } else { - Size::ZERO + Len::ZERO + } + } + + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + if let Some(id) = &self.inner { + ctx.height(id) + } else { + Len::ZERO } } } diff --git a/src/core/rect.rs b/src/core/rect.rs index be8989a..f72820e 100644 --- a/src/core/rect.rs +++ b/src/core/rect.rs @@ -36,6 +36,14 @@ impl Widget for Rect { inner_radius: self.inner_radius, }); } + + fn desired_width(&mut self, _: &mut SizeCtx) -> Len { + Len::rest(1) + } + + fn desired_height(&mut self, _: &mut SizeCtx) -> Len { + Len::rest(1) + } } pub fn rect(color: UiColor) -> Rect { diff --git a/src/core/text/edit.rs b/src/core/text/edit.rs index 6fda7af..74cbac4 100644 --- a/src/core/text/edit.rs +++ b/src/core/text/edit.rs @@ -51,8 +51,17 @@ impl Widget for TextEdit { } } - fn desired_size(&mut self, ctx: &mut SizeCtx) -> Size { - Size::abs(self.view.draw(ctx).size()) + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + Len::abs(self.view.draw(ctx).size().x) + } + + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + let res = Len::abs(self.view.draw(ctx).size().y); + if (res.abs - 639.0).abs() < 0.5 && ctx.label(ctx.id()) == "WHAT" { + let source = ctx.label(ctx.source()); + println!("{source} => {:?}: [{}] -> {}", ctx.id(), ctx.outer, res.abs); + } + res } } diff --git a/src/core/text/mod.rs b/src/core/text/mod.rs index c5e9c12..13711a8 100644 --- a/src/core/text/mod.rs +++ b/src/core/text/mod.rs @@ -91,8 +91,12 @@ impl Widget for Text { painter.texture_within(&tex.handle, region); } - fn desired_size(&mut self, ctx: &mut SizeCtx) -> Size { - Size::abs(self.update_buf(ctx).size()) + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + Len::abs(self.update_buf(ctx).size().x) + } + + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + Len::abs(self.update_buf(ctx).size().y) } } diff --git a/src/layout/orientation.rs b/src/layout/orientation.rs index d9f46f5..1c819d3 100644 --- a/src/layout/orientation.rs +++ b/src/layout/orientation.rs @@ -136,6 +136,7 @@ impl Size { x: Len::ZERO, y: Len::ZERO, }; + pub fn abs(v: Vec2) -> Self { Self { x: Len::abs(v.x), @@ -143,8 +144,22 @@ impl Size { } } - pub fn to_uivec2(self, rel_len: Vec2) -> UiVec2 { - UiVec2::from_scalars(self.x.apply_rest(rel_len.x), self.y.apply_rest(rel_len.y)) + pub fn rel(v: Vec2) -> Self { + Self { + x: Len::rel(v.x), + y: Len::rel(v.y), + } + } + + pub fn rest(v: Vec2) -> Self { + Self { + x: Len::rest(v.x), + y: Len::rest(v.y), + } + } + + pub fn to_uivec2(self) -> UiVec2 { + UiVec2::from_scalars(self.x.apply_rest(), self.y.apply_rest()) } pub fn from_axis(axis: Axis, aligned: Len, ortho: Len) -> Self { @@ -175,14 +190,10 @@ impl Len { rest: 0.0, }; - pub fn apply_rest(&self, rel_len: f32) -> UiScalar { + pub fn apply_rest(&self) -> UiScalar { UiScalar { - rel: if self.rest > 0.0 { - self.rel.max(1.0) - } else { - self.rel - } * rel_len, - abs: if self.rest > 0.0 { 0.0 } else { self.abs }, + rel: self.rel + if self.rest > 0.0 { 1.0 } else { 0.0 }, + abs: self.abs, } } diff --git a/src/layout/painter.rs b/src/layout/painter.rs index dbd9dd2..fc680fa 100644 --- a/src/layout/painter.rs +++ b/src/layout/painter.rs @@ -1,7 +1,7 @@ use crate::{ layout::{ - Layers, Modules, Size, TextAttrs, TextBuffer, TextData, TextTexture, TextureHandle, - Textures, UiRegion, UiVec2, Vec2, WidgetId, Widgets, + Axis, Layers, Len, Modules, Size, TextAttrs, TextBuffer, TextData, TextTexture, + TextureHandle, Textures, UiRegion, UiVec2, Vec2, WidgetId, Widgets, }, render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst}, util::{HashMap, HashSet, Id, TrackedArena}, @@ -14,7 +14,8 @@ pub struct Painter<'a, 'c> { textures: Vec, primitives: Vec, children: Vec, - sized_children: HashMap, + children_width: HashMap, + children_height: HashMap, /// whether this widget depends on region's final pixel size or not /// TODO: decide if point (pt) should be used here instead of px pub layer: usize, @@ -31,9 +32,17 @@ pub struct PainterCtx<'a> { pub screen_size: Vec2, pub modules: &'a mut Modules, pub px_dependent: &'a mut HashSet, + pub cache_width: HashMap, + pub cache_height: HashMap, draw_started: HashSet, } +#[derive(Default)] +pub struct ResizeRef { + x: Option<(Id, (UiVec2, Len))>, + y: Option<(Id, (UiVec2, Len))>, +} + pub struct WidgetInstance { pub id: Id, pub region: UiRegion, @@ -41,10 +50,9 @@ pub struct WidgetInstance { pub textures: Vec, pub primitives: Vec, pub children: Vec, - pub resize: Option, + pub resize: ResizeRef, pub mask: MaskIdx, pub layer: usize, - pub desired_size: Size, } #[derive(Default)] @@ -72,7 +80,9 @@ impl<'a> PainterCtx<'a> { modules: &mut data.modules, px_dependent: &mut data.px_dependent, masks: &mut data.masks, - draw_started: HashSet::default(), + cache_width: Default::default(), + cache_height: Default::default(), + draw_started: Default::default(), } } @@ -84,19 +94,51 @@ impl<'a> PainterCtx<'a> { return; }; - if let Some(rid) = active.resize { - let desired = SizeCtx { - checked: &mut Default::default(), + // TODO: this is stupid having 2 of these + if let Some((rid, (outer, old_desired))) = active.resize.x { + let new_desired = SizeCtx { + source: id, + cache_width: &mut self.cache_width, + cache_height: &mut self.cache_height, text: self.text, textures: self.textures, widgets: self.widgets, - size: UiVec2::FULL_SIZE, - screen_size: self.screen_size, + outer, + output_size: self.screen_size, + checked_width: &mut Default::default(), + checked_height: &mut Default::default(), px_dependent: &mut Default::default(), id, } - .size_inner(id, active.region.size()); - if active.desired_size != desired { + .width_inner(id); + if new_desired != old_desired { + self.redraw(rid); + if self.draw_started.contains(&id) { + return; + } + } + } + + let Some(active) = self.active.get(&id) else { + return; + }; + if let Some((rid, (outer, old_desired))) = active.resize.y { + let new_desired = SizeCtx { + source: id, + cache_width: &mut self.cache_width, + cache_height: &mut self.cache_height, + text: self.text, + textures: self.textures, + widgets: self.widgets, + outer, + output_size: self.screen_size, + checked_width: &mut Default::default(), + checked_height: &mut Default::default(), + px_dependent: &mut Default::default(), + id, + } + .height_inner(id); + if new_desired != old_desired { self.redraw(rid); if self.draw_started.contains(&id) { return; @@ -147,7 +189,7 @@ impl<'a> PainterCtx<'a> { ); } let mut old_children = old_children.unwrap_or_default(); - let mut resize = None; + let mut resize = ResizeRef::default(); if let Some(active) = self.active.get_mut(&id) { if active.parent != parent { panic!("Cannot draw the same widget twice (2)"); @@ -167,18 +209,6 @@ impl<'a> PainterCtx<'a> { self.draw_started.insert(id); - let desired_size = SizeCtx { - text: self.text, - textures: self.textures, - widgets: self.widgets, - checked: &mut Default::default(), - screen_size: self.screen_size, - px_dependent: &mut Default::default(), - id, - size: region.size(), - } - .size_raw(id); - let mut painter = Painter { region, mask, @@ -188,13 +218,15 @@ impl<'a> PainterCtx<'a> { primitives: Vec::new(), ctx: self, children: Vec::new(), - sized_children: Default::default(), + children_width: Default::default(), + children_height: Default::default(), }; // draw widgets painter.ctx.widgets.get_dyn_dynamic(id).draw(&mut painter); - let sized_children = painter.sized_children; + let children_width = painter.children_width; + let children_height = painter.children_height; // add to active let instance = WidgetInstance { @@ -206,14 +238,20 @@ impl<'a> PainterCtx<'a> { children: painter.children, resize, mask: painter.mask, - desired_size, layer, }; - for cid in sized_children.keys() { - if let Some(w) = self.active.get_mut(cid) - && w.resize.is_none() + for (cid, outer) in children_width { + if let Some(w) = self.active.get_mut(&cid) + && w.resize.x.is_none() { - w.resize = Some(id) + 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)) } } for c in &old_children { @@ -230,8 +268,6 @@ impl<'a> PainterCtx<'a> { fn mov(&mut self, id: Id, from: UiRegion, to: UiRegion) { let active = self.active.get_mut(&id).unwrap(); - // children will not be changed, so this technically should not be needed - // probably need unsafe for h in &active.primitives { let region = self.layers[h.layer].primitives.region_mut(h); *region = region.outside(&from).within(&to); @@ -240,6 +276,8 @@ impl<'a> PainterCtx<'a> { for m in self.modules.iter_mut() { m.on_move(active); } + // children will not be changed, so this technically should not be needed + // probably need unsafe let children = active.children.clone(); for child in children { self.mov(child, from, to); @@ -354,8 +392,11 @@ impl<'a, 'c> Painter<'a, 'c> { self.size_ctx().size(id) } - pub fn region_size(&mut self, id: &WidgetId) -> UiVec2 { - self.size_ctx().size(id).to_uivec2(self.region.size().rel) + pub fn len_axis(&mut self, id: &WidgetId, axis: Axis) -> Len { + match axis { + Axis::X => self.size_ctx().width(id), + Axis::Y => self.size_ctx().height(id), + } } pub fn size_ctx(&mut self) -> SizeCtx<'_> { @@ -363,11 +404,15 @@ impl<'a, 'c> Painter<'a, 'c> { text: self.ctx.text, textures: self.ctx.textures, widgets: self.ctx.widgets, - checked: &mut self.sized_children, - screen_size: self.ctx.screen_size, + output_size: self.ctx.screen_size, px_dependent: self.ctx.px_dependent, + checked_width: &mut self.children_width, + checked_height: &mut self.children_height, + cache_width: &mut self.ctx.cache_width, + cache_height: &mut self.ctx.cache_height, + source: self.id, id: self.id, - size: self.region.size(), + outer: self.region.size(), } } @@ -396,41 +441,98 @@ impl<'a, 'c> Painter<'a, 'c> { pub struct SizeCtx<'a> { pub text: &'a mut TextData, pub textures: &'a mut Textures, + source: Id, widgets: &'a Widgets, px_dependent: &'a mut HashSet, - checked: &'a mut HashMap, + cache_width: &'a mut HashMap, + cache_height: &'a mut HashMap, + checked_width: &'a mut HashMap, + checked_height: &'a mut HashMap, /// TODO: should this be pub? rn used for sized - pub size: UiVec2, - screen_size: Vec2, + pub outer: UiVec2, + output_size: Vec2, id: Id, } impl SizeCtx<'_> { - fn size_inner(&mut self, id: Id, size: UiVec2) -> Size { - let self_size = self.size; - self.size = size; - let size = self.widgets.get_dyn_dynamic(id).desired_size(self); - self.size = self_size; - self.checked.insert(id, size); - size + pub fn id(&self) -> &Id { + &self.id } + + pub fn source(&self) -> &Id { + &self.source + } + + fn width_inner(&mut self, id: Id) -> Len { + if let Some(&(outer, len)) = self.cache_width.get(&id) + && outer == self.outer + { + self.checked_width.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_width(self); + self.outer = self_outer; + self.id = self_id; + self.cache_width.insert(id, (self.outer, len)); + self.checked_width.insert(id, (self.outer, len)); + len + } + + // TODO: should be refactored to share code w width_inner + fn height_inner(&mut self, id: Id) -> 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: &WidgetId) -> Len { + self.width_inner(id.id) + } + + pub fn height(&mut self, id: &WidgetId) -> Len { + self.height_inner(id.id) + } + + pub fn len_axis(&mut self, id: &WidgetId, axis: Axis) -> Len { + match axis { + Axis::X => self.width(id), + Axis::Y => self.height(id), + } + } + pub fn size(&mut self, id: &WidgetId) -> Size { - // if let Some(&size) = self.checked.get(&id.id) { - // return size; - // } - self.size_inner(id.id, self.size) - } - fn size_raw(&mut self, id: Id) -> Size { - self.size_inner(id, self.size) + Size { + x: self.width(id), + y: self.height(id), + } } + pub fn px_size(&mut self) -> Vec2 { - self.px_dependent.insert(self.id); - self.size.to_abs(self.screen_size) + // WARNING IF UNCOMMENT: self.id is no longer valid + // self.px_dependent.insert(self.id); + self.outer.to_abs(self.output_size) } + pub fn draw_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> TextTexture { self.text.draw(buffer, attrs, self.textures) } - pub fn label(&self) -> &String { - self.widgets.label(&self.id) + + pub fn label(&self, id: &Id) -> &String { + self.widgets.label(id) } } diff --git a/src/layout/pos.rs b/src/layout/pos.rs index 6ad4aaf..53bf20b 100644 --- a/src/layout/pos.rs +++ b/src/layout/pos.rs @@ -6,7 +6,7 @@ use crate::{ }; #[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable, Default)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, bytemuck::Pod, bytemuck::Zeroable, Default)] pub struct UiVec2 { pub rel: Vec2, pub abs: Vec2, @@ -123,7 +123,7 @@ impl UiVec2 { impl Display for UiVec2 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "rel {} / abs {}", self.rel, self.abs) + write!(f, "rel{};abs{}", self.rel, self.abs) } } @@ -212,6 +212,10 @@ impl UiScalar { abs: offset, } } + + pub fn within_len(&self, len: UiScalar) -> Self { + self.within(UiScalar::ZERO, len) + } } #[repr(C)] diff --git a/src/layout/ui.rs b/src/layout/ui.rs index 86d4acb..b4ddd84 100644 --- a/src/layout/ui.rs +++ b/src/layout/ui.rs @@ -6,7 +6,7 @@ use crate::{ IdLike, PainterCtx, PainterData, PixelRegion, StaticWidgetId, TextureHandle, Vec2, Widget, WidgetId, WidgetLike, }, - util::Id, + util::{HashSet, Id}, }; use std::{ any::{Any, TypeId}, @@ -18,7 +18,7 @@ pub struct Ui { // TODO: make this at least pub(super) pub(crate) data: PainterData, root: Option, - updates: Vec, + updates: HashSet, recv: Receiver, pub(super) send: Sender, full_redraw: bool, @@ -142,7 +142,7 @@ impl Ui { // self.redraw_all(); // } let mut ctx = PainterCtx::new(&mut self.data); - for id in self.updates.drain(..) { + for id in self.updates.drain() { ctx.redraw(id); } self.free(); @@ -172,7 +172,7 @@ impl Ui { } pub fn text(&mut self, id: &impl IdLike) -> TextEditCtx<'_> { - self.updates.push(id.id()); + self.updates.insert(id.id()); TextEditCtx { text: self.data.widgets.get_mut(id).unwrap(), font_system: &mut self.data.text.font_system, @@ -196,7 +196,6 @@ impl Ui { " pixel region: {}", inst.region.to_px(self.data.output_size) ); - println!(" desired_size: {}", inst.desired_size); println!("}}"); } } @@ -212,7 +211,7 @@ impl Index<&WidgetId> for Ui { impl IndexMut<&WidgetId> for Ui { fn index_mut(&mut self, id: &WidgetId) -> &mut Self::Output { - self.updates.push(id.id); + self.updates.insert(id.id); self.get_mut(id).unwrap() } } @@ -227,7 +226,7 @@ impl Index> for Ui { impl IndexMut> for Ui { fn index_mut(&mut self, id: StaticWidgetId) -> &mut Self::Output { - self.updates.push(id.id); + self.updates.insert(id.id); self.data.widgets.get_mut(&id).unwrap() } } diff --git a/src/layout/vec2.rs b/src/layout/vec2.rs index 9f36f32..593095f 100644 --- a/src/layout/vec2.rs +++ b/src/layout/vec2.rs @@ -2,7 +2,7 @@ use crate::{ layout::UiNum, util::{DivOr, impl_op}, }; -use std::{marker::Destruct, ops::*}; +use std::{hash::Hash, marker::Destruct, ops::*}; #[repr(C)] #[derive(Clone, Copy, PartialEq, Default, bytemuck::Pod, bytemuck::Zeroable)] @@ -11,6 +11,15 @@ pub struct Vec2 { pub y: f32, } +impl Eq for Vec2 {} + +impl Hash for Vec2 { + fn hash(&self, state: &mut H) { + state.write_u32(self.x.to_bits()); + state.write_u32(self.y.to_bits()); + } +} + pub const fn vec2(x: impl const UiNum, y: impl const UiNum) -> Vec2 { Vec2::new(x.to_f32(), y.to_f32()) } diff --git a/src/layout/widget.rs b/src/layout/widget.rs index b88c206..204afa7 100644 --- a/src/layout/widget.rs +++ b/src/layout/widget.rs @@ -1,16 +1,21 @@ -use crate::layout::{Painter, Size, SizeCtx, StaticWidgetId, Ui, WidgetId, WidgetIdFn}; +use crate::layout::{Len, Painter, SizeCtx, StaticWidgetId, Ui, WidgetId, WidgetIdFn}; use std::{any::Any, marker::PhantomData}; pub trait Widget: Any { fn draw(&mut self, painter: &mut Painter); - fn desired_size(&mut self, _: &mut SizeCtx) -> Size { - Size::default() - } + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len; + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len; } 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 + } } pub struct WidgetTag;