diff --git a/core/src/orientation/align.rs b/core/src/orientation/align.rs index 4fe79d2..3008ae5 100644 --- a/core/src/orientation/align.rs +++ b/core/src/orientation/align.rs @@ -24,10 +24,23 @@ impl Align { pub const TOP: CardinalAlign = CardinalAlign::TOP; pub const V_CENTER: CardinalAlign = CardinalAlign::V_CENTER; pub const BOT: CardinalAlign = CardinalAlign::BOT; + pub const NONE: Align = Align { x: None, y: None }; pub fn tuple(&self) -> (Option, Option) { (self.x, self.y) } + + /// naming is a bit inconsistent w option, + /// normally would return Self, but can't see + /// that being needed atm and would wanna do + /// a trait if so, so they can both be named .or + /// (because Self impls From) + pub fn or(&self, other: RegionAlign) -> RegionAlign { + RegionAlign { + x: self.x.unwrap_or(other.x), + y: self.y.unwrap_or(other.x), + } + } } #[derive(Clone, Copy, PartialEq, Eq)] @@ -106,6 +119,7 @@ impl UiVec2 { } } + /// aligns this as a size within a region pub fn align(&self, align: RegionAlign) -> UiRegion { UiRegion { x: self.x.align(align.x), diff --git a/core/src/orientation/pos.rs b/core/src/orientation/pos.rs index 204e048..e66b2d6 100644 --- a/core/src/orientation/pos.rs +++ b/core/src/orientation/pos.rs @@ -140,12 +140,22 @@ where } #[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, Default, bytemuck::Zeroable)] +#[derive(Debug, Copy, Clone, bytemuck::Pod, Default, bytemuck::Zeroable)] pub struct UiScalar { pub rel: f32, pub abs: f32, } +// TODO: unknown exactly what these should be +const REL_EPSILON: f32 = 0.00001; +const ABS_EPSILON: f32 = 0.1; + +impl PartialEq for UiScalar { + fn eq(&self, other: &Self) -> bool { + (self.rel - other.rel).abs() < REL_EPSILON && (self.abs - other.abs).abs() < ABS_EPSILON + } +} + impl Eq for UiScalar {} impl Hash for UiScalar { fn hash(&self, state: &mut H) { diff --git a/core/src/ui/cache.rs b/core/src/ui/cache.rs index 10565ee..df4961a 100644 --- a/core/src/ui/cache.rs +++ b/core/src/ui/cache.rs @@ -1,8 +1,9 @@ -use crate::{BothAxis, Len, UiVec2, WidgetId, util::HashMap}; +use crate::{BothAxis, Len, UiRegion, UiVec2, WidgetId, util::HashMap}; #[derive(Default)] pub struct Cache { pub size: BothAxis>, + pub moves: HashMap, } impl Cache { diff --git a/core/src/ui/draw_state.rs b/core/src/ui/draw_state.rs index 555acb7..2daa257 100644 --- a/core/src/ui/draw_state.rs +++ b/core/src/ui/draw_state.rs @@ -1,5 +1,5 @@ use crate::{ - ActiveData, Axis, EventsLike, Painter, SizeCtx, Ui, UiRegion, UiVec2, WidgetId, + ActiveData, Align, Axis, EventsLike, Painter, SizeCtx, Ui, UiRegion, UiVec2, WidgetId, render::MaskIdx, util::{HashSet, forget_ref}, }; @@ -25,6 +25,7 @@ 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); } @@ -89,8 +90,17 @@ impl<'a> DrawState<'a> { self.widgets.needs_redraw.clear(); if let Some(id) = &self.ui.root { - self.draw_inner(0, id.id(), UiRegion::FULL, None, MaskIdx::NONE, None); + self.draw_inner( + 0, + id.id(), + UiRegion::FULL, + None, + MaskIdx::NONE, + None, + Align::NONE, + ); } + self.apply_moves(); } pub(super) fn draw_inner( @@ -101,19 +111,18 @@ impl<'a> DrawState<'a> { 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) { - // check to see if we can skip drawing first + // check to see if we can skip drawing first, and just need to move if active.region == region { - return; + return region; } else if active.region.size() == region.size() { - // TODO: epsilon? - let from = active.region; - self.mov(id, from, region); - return; + self.ui.cache.moves.insert(id, region); + return region; } // if not, then maintain resize and track old children to remove unneeded let active = self.remove(id, false).unwrap(); @@ -126,6 +135,7 @@ impl<'a> DrawState<'a> { let mut painter = Painter { state: self, region, + region_used: region, mask, layer, id, @@ -134,11 +144,13 @@ impl<'a> DrawState<'a> { children: Vec::new(), }; - let mut widget = painter.state.widgets.get_dyn_dynamic(id); + let mut widget = painter.state.widgets.get_dyn(id); widget.draw(&mut painter); - drop(widget); + let walign = painter.state.widgets.data(id).unwrap().align; + let align = align.or(walign.into()); let Painter { + region_used, state: _, region, mask, @@ -149,6 +161,14 @@ 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, @@ -171,6 +191,17 @@ impl<'a> DrawState<'a> { // update modules self.events.draw(&active); self.active.insert(id, active); + + 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; + self.mov(id, from, to); + } + self.ui.cache.moves = moves; } fn mov(&mut self, id: WidgetId, from: UiRegion, to: UiRegion) { diff --git a/core/src/ui/painter.rs b/core/src/ui/painter.rs index dc707b2..9304995 100644 --- a/core/src/ui/painter.rs +++ b/core/src/ui/painter.rs @@ -10,6 +10,7 @@ use crate::{ pub struct Painter<'a, 'b> { pub(super) state: &'a mut DrawState<'b>, + pub region_used: UiRegion, pub(super) region: UiRegion, pub(super) mask: MaskIdx, pub(super) textures: Vec, diff --git a/core/src/ui/size.rs b/core/src/ui/size.rs index 6f82492..7d9c2d9 100644 --- a/core/src/ui/size.rs +++ b/core/src/ui/size.rs @@ -30,7 +30,7 @@ impl SizeCtx<'_> { } let len = self .widgets - .get_dyn_dynamic(id) + .get_dyn(id) .desired_len::(&mut SizeCtx { text: self.text, textures: self.textures, diff --git a/core/src/widget/data.rs b/core/src/widget/data.rs index 8d693e7..594d59b 100644 --- a/core/src/widget/data.rs +++ b/core/src/widget/data.rs @@ -1,8 +1,11 @@ -use crate::Widget; +use crate::{RegionAlign, Widget}; pub struct WidgetData { pub widget: Box, pub label: String, + /// alignment used if this does not fill up container + /// and there is no enforced alignment from parent + pub align: RegionAlign, /// dynamic borrow checking pub borrowed: bool, } @@ -15,6 +18,7 @@ impl WidgetData { } Self { widget: Box::new(widget), + align: RegionAlign::CENTER, label, borrowed: false, } diff --git a/core/src/widget/widgets.rs b/core/src/widget/widgets.rs index 751ddbf..da6266a 100644 --- a/core/src/widget/widgets.rs +++ b/core/src/widget/widgets.rs @@ -26,18 +26,9 @@ impl Widgets { !self.needs_redraw.is_empty() } - pub fn get_dyn(&self, id: WidgetId) -> Option<&dyn Widget> { - Some(self.vec.get(id)?.widget.as_ref()) - } - - pub fn get_dyn_mut(&mut self, id: WidgetId) -> Option<&mut dyn Widget> { - self.needs_redraw.insert(id); - Some(self.vec.get_mut(id)?.widget.as_mut()) - } - /// get_dyn but dynamic borrow checking of widgets /// lets you do recursive (tree) operations, like the painter does - pub(crate) fn get_dyn_dynamic<'a>(&self, id: WidgetId) -> WidgetWrapper<'a> { + pub(crate) fn get_dyn<'a>(&self, id: WidgetId) -> WidgetWrapper<'a> { // SAFETY: must guarantee no other mutable references to this widget exist // done through the borrow variable let data = unsafe { forget_mut(to_mut(self.vec.get(id).unwrap())) }; @@ -51,14 +42,26 @@ impl Widgets { where I::Widget: Sized + Widget, { - self.get_dyn(id.id())?.as_any().downcast_ref() + self.vec + .get(id.id())? + .widget + .as_ref() + .as_any() + .downcast_ref() } pub fn get_mut(&mut self, id: &I) -> Option<&mut I::Widget> where I::Widget: Sized + Widget, { - self.get_dyn_mut(id.id())?.as_any_mut().downcast_mut() + let id = id.id(); + self.needs_redraw.insert(id); + self.vec + .get_mut(id)? + .widget + .as_mut() + .as_any_mut() + .downcast_mut() } pub fn add_strong(&mut self, widget: W) -> WidgetHandle { @@ -77,7 +80,9 @@ impl Widgets { if !self.waiting.remove(&rf.id()) { let label = self.label(rf); let id = rf.id(); - panic!("widget '{label}' ({id:?}) was already added\ncannot add a widget twice; consider creating two") + panic!( + "widget '{label}' ({id:?}) was already added\ncannot add a widget twice; consider creating two" + ) } WidgetHandle::new(rf.id(), self.send.clone()) }