diff --git a/src/bin/test/main.rs b/src/bin/test/main.rs index 413f335..b3c9b1c 100644 --- a/src/bin/test/main.rs +++ b/src/bin/test/main.rs @@ -121,8 +121,10 @@ impl Client { .span(Dir::DOWN) .add_static(&mut ui); - let texts = Span::empty(Dir::DOWN).gap(10).add_static(&mut ui); - let msg_area = (Rect::new(Color::SKY), texts.scroll().masked()).stack(); + let texts = Span::empty(Dir::DOWN) + .gap(10) + .add_static(&mut ui); + let msg_area = texts.scroll().masked().background(rect(Color::SKY)); let add_text = text("add") .editable() .text_align(Align::LEFT) @@ -142,9 +144,8 @@ impl Client { client.ui.text(id).select(ctx.cursor, ctx.size); client.focus = Some(id.clone()); }); - let msg_box = (rect(Color::WHITE.darker(0.5)), text) - .stack() - .size(StackSize::Child(1)) + let msg_box = text + .background(rect(Color::WHITE.darker(0.5))) .add(&mut client.ui); client.ui[texts].children.push(msg_box.any()); }) diff --git a/src/core/position/mod.rs b/src/core/position/mod.rs index 5241c90..5349a66 100644 --- a/src/core/position/mod.rs +++ b/src/core/position/mod.rs @@ -9,6 +9,7 @@ mod stack; pub use align::*; pub use offset::*; pub use pad::*; +pub use scroll::*; pub use sized::*; pub use span::*; pub use stack::*; diff --git a/src/core/position/scroll.rs b/src/core/position/scroll.rs index 7400b9a..c61ac1a 100644 --- a/src/core/position/scroll.rs +++ b/src/core/position/scroll.rs @@ -3,9 +3,10 @@ use crate::prelude::*; pub struct Scroll { inner: WidgetId, axis: Axis, - snap_end: bool, amt: f32, - draw_len: f32, + snap_end: bool, + container_len: f32, + content_len: f32, } impl Widget for Scroll { @@ -17,10 +18,14 @@ impl Widget for Scroll { .apply_rest() .within_len(container_len) .to_abs(output_len); - let container_len = container_len.to_abs(output_len); - self.draw_len = content_len; - // let region = UiRegion::FULL.offset(self.amt); - // painter.widget_within(&self.inner, region); + self.container_len = container_len.to_abs(output_len); + self.content_len = content_len; + if self.snap_end { + self.amt = self.content_len - self.container_len; + } + self.update_amt(); + let region = UiRegion::FULL.offset(Vec2::from_axis(self.axis, -self.amt, 0.0)); + painter.widget_within(&self.inner, region); } fn desired_width(&mut self, _: &mut SizeCtx) -> Len { @@ -33,7 +38,26 @@ impl Widget for Scroll { } impl Scroll { + pub fn new(inner: WidgetId, axis: Axis) -> Self { + Self { + inner, + axis, + amt: 0.0, + snap_end: true, + container_len: 0.0, + content_len: 0.0, + } + } + + pub fn update_amt(&mut self) { + self.amt = self.amt.max(0.0); + let len = (self.content_len - self.container_len).max(0.0); + self.amt = self.amt.min(len); + self.snap_end = self.amt == len; + } + pub fn scroll(&mut self, amt: f32) { - self.amt += amt; + self.amt -= amt; + self.update_amt(); } } diff --git a/src/core/trait_fns.rs b/src/core/trait_fns.rs index 517f4d4..fd61ec4 100644 --- a/src/core/trait_fns.rs +++ b/src/core/trait_fns.rs @@ -10,7 +10,7 @@ pub trait CoreWidget { fn width(self, len: impl Into) -> impl WidgetFn; fn height(self, len: impl Into) -> impl WidgetFn; fn offset(self, amt: impl Into) -> impl WidgetFn; - fn scroll(self) -> impl WidgetIdFn; + fn scroll(self) -> impl WidgetIdFn; fn masked(self) -> impl WidgetFn; fn background(self, w: impl WidgetLike) -> impl WidgetFn; fn z_offset(self, offset: usize) -> impl WidgetFn; @@ -77,11 +77,14 @@ impl, Tag> CoreWidget for W { } } - fn scroll(self) -> impl WidgetIdFn { - self.offset(UiVec2::ZERO) - .edit_on(CursorSense::Scroll, |w, data| { - w.amt += UiVec2::abs(data.scroll_delta * 50.0); - }) + fn scroll(self) -> impl WidgetIdFn { + move |ui| { + Scroll::new(self.add(ui).any(), Axis::Y) + .edit_on(CursorSense::Scroll, |w, data| { + w.scroll(data.scroll_delta.y * 50.0); + }) + .add(ui) + } } fn masked(self) -> impl WidgetFn { diff --git a/src/layout/painter.rs b/src/layout/painter.rs index 9ad568a..970fc0f 100644 --- a/src/layout/painter.rs +++ b/src/layout/painter.rs @@ -24,7 +24,7 @@ pub struct Painter<'a, 'c> { } /// context for a painter; lets you draw and redraw widgets -pub struct PainterCtx<'a> { +struct PainterCtx<'a> { pub widgets: &'a Widgets, pub active: &'a mut HashMap, pub layers: &'a mut Layers, @@ -35,6 +35,7 @@ pub struct PainterCtx<'a> { pub modules: &'a mut Modules, pub cache_width: HashMap, pub cache_height: HashMap, + pub needs_redraw: HashSet, draw_started: HashSet, } @@ -75,26 +76,11 @@ pub struct PainterData { } impl<'a> PainterCtx<'a> { - pub fn new(data: &'a mut PainterData) -> Self { - Self { - widgets: &data.widgets, - active: &mut data.active, - layers: &mut data.layers, - textures: &mut data.textures, - text: &mut data.text, - output_size: data.output_size, - modules: &mut data.modules, - masks: &mut data.masks, - cache_width: Default::default(), - cache_height: Default::default(), - draw_started: Default::default(), - } - } - /// redraws a widget that's currently active (drawn) /// can be called on something already drawn or removed, /// will just return if so pub fn redraw(&mut self, id: Id) { + self.needs_redraw.remove(&id); if self.draw_started.contains(&id) { return; } @@ -113,6 +99,7 @@ impl<'a> PainterCtx<'a> { // 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 = SizeCtx { source: id, @@ -129,15 +116,17 @@ impl<'a> PainterCtx<'a> { } .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) { - return finish(self, resize); + 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 = SizeCtx { source: id, cache_width: &mut self.cache_width, @@ -156,11 +145,15 @@ impl<'a> PainterCtx<'a> { self.redraw(*rid); *old_desired = new_desired; if self.draw_started.contains(&id) { - return finish(self, resize); + ret = true; } } } + if ret { + return finish(self, resize); + } + let Some(active) = self.remove(id) else { return; }; @@ -176,12 +169,6 @@ impl<'a> PainterCtx<'a> { finish(self, resize); } - pub fn draw(&mut self, id: Id) { - self.draw_started.clear(); - self.layers.clear(); - self.draw_inner(0, id, UiRegion::FULL, None, MaskIdx::NONE, None); - } - fn draw_inner( &mut self, layer: usize, @@ -205,7 +192,9 @@ impl<'a> PainterCtx<'a> { } let mut old_children = old_children.unwrap_or_default(); let mut resize = ResizeRef::default(); - if let Some(active) = self.active.get_mut(&id) { + if let Some(active) = self.active.get_mut(&id) + && !self.needs_redraw.contains(&id) + { // check to see if we can skip drawing first if active.parent != parent { panic!("Cannot draw the same widget twice (2)"); @@ -335,6 +324,39 @@ impl<'a> PainterCtx<'a> { } } +impl PainterData { + fn ctx(&mut self, needs_redraw: HashSet) -> PainterCtx<'_> { + PainterCtx { + widgets: &self.widgets, + active: &mut self.active, + layers: &mut self.layers, + textures: &mut self.textures, + text: &mut self.text, + output_size: self.output_size, + modules: &mut self.modules, + masks: &mut self.masks, + cache_width: Default::default(), + cache_height: Default::default(), + draw_started: Default::default(), + needs_redraw, + } + } + + pub fn draw(&mut self, id: Id) { + let mut ctx = self.ctx(Default::default()); + ctx.draw_started.clear(); + ctx.layers.clear(); + ctx.draw_inner(0, id, UiRegion::FULL, None, MaskIdx::NONE, None); + } + + pub fn redraw(&mut self, ids: HashSet) { + let mut ctx = self.ctx(ids); + while let Some(&id) = ctx.needs_redraw.iter().next() { + ctx.redraw(id); + } + } +} + impl<'a, 'c> Painter<'a, 'c> { fn primitive_at(&mut self, primitive: P, region: UiRegion) { let h = self.ctx.layers.write( @@ -458,6 +480,10 @@ impl<'a, 'c> Painter<'a, 'c> { pub fn label(&self) -> &str { &self.ctx.widgets.data(&self.id).unwrap().label } + + pub fn id(&self) -> &Id { + &self.id + } } pub struct SizeCtx<'a> { @@ -486,12 +512,14 @@ impl SizeCtx<'_> { fn width_inner(&mut self, id: Id) -> Len { // first check cache - if let Some(&(outer, len)) = self.cache_width.get(&id) - && outer == self.outer - { - self.checked_width.insert(id, (self.outer, len)); - return len; - } + // 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; @@ -508,12 +536,13 @@ impl SizeCtx<'_> { // 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; - } + // if let Some(&(outer, len)) = self.cache_height.get(&id) + // && outer == self.outer + // { + // prntln!("FAIL {id:?}"); + // self.checked_height.insert(id, (self.outer, len)); + // return len; + // } let self_outer = self.outer; let self_id = self.id; self.id = id; diff --git a/src/layout/ui.rs b/src/layout/ui.rs index 4ece198..1fe05ce 100644 --- a/src/layout/ui.rs +++ b/src/layout/ui.rs @@ -3,7 +3,7 @@ use image::DynamicImage; use crate::{ core::{TextEdit, TextEditCtx}, layout::{ - IdLike, PainterCtx, PainterData, PixelRegion, StaticWidgetId, TextureHandle, Vec2, Widget, + IdLike, PainterData, PixelRegion, StaticWidgetId, TextureHandle, Vec2, Widget, WidgetId, WidgetInstance, WidgetLike, }, util::{HashSet, Id}, @@ -109,9 +109,8 @@ impl Ui { } // free before bc nothing should exist self.free(); - let mut ctx = PainterCtx::new(&mut self.data); if let Some(root) = &self.root { - ctx.draw(root.id); + self.data.draw(root.id); } } @@ -138,13 +137,7 @@ impl Ui { } fn redraw_updates(&mut self) { - // if self.updates.drain(..).next().is_some() { - // self.redraw_all(); - // } - let mut ctx = PainterCtx::new(&mut self.data); - for id in self.updates.drain() { - ctx.redraw(id); - } + self.data.redraw(std::mem::take(&mut self.updates)); self.free(); } diff --git a/src/layout/widgets.rs b/src/layout/widgets.rs index 0d01277..d29af12 100644 --- a/src/layout/widgets.rs +++ b/src/layout/widgets.rs @@ -56,7 +56,11 @@ impl Widgets { } pub fn insert(&mut self, id: Id, widget: W) { - self.insert_any(id, Box::new(widget), std::any::type_name::().to_string()); + let mut label = std::any::type_name::().to_string(); + if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) { + label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1; + } + self.insert_any(id, Box::new(widget), label); } pub fn data(&self, id: &Id) -> Option<&WidgetData> {