diff --git a/src/core/text.rs b/src/core/text.rs index 4f089ff..83fbccf 100644 --- a/src/core/text.rs +++ b/src/core/text.rs @@ -1,8 +1,8 @@ use crate::prelude::*; pub struct Text { - content: String, - attrs: TextAttrs, + pub content: String, + pub attrs: TextAttrs, } impl Text { diff --git a/src/core/trait_fns.rs b/src/core/trait_fns.rs index 49be97c..9b0a851 100644 --- a/src/core/trait_fns.rs +++ b/src/core/trait_fns.rs @@ -1,10 +1,13 @@ use super::*; -use crate::layout::{Dir, UiPos, UiRegion, Vec2, WidgetArrLike, WidgetFnRet, WidgetLike}; +use crate::layout::{ + Dir, UiPos, UiRegion, Vec2, WidgetArrLike, WidgetFnRet, WidgetIdFnRet, WidgetLike, +}; -pub trait CoreWidget { +pub trait CoreWidget { fn pad(self, padding: impl Into) -> WidgetFnRet!(Regioned, Ctx); fn center(self, size: impl Into) -> WidgetFnRet!(Regioned, Ctx); fn region(self, region: UiRegion) -> WidgetFnRet!(Regioned, Ctx); + fn label(self, label: impl Into) -> WidgetIdFnRet!(W, Ctx); } impl, Ctx: 'static, Tag> CoreWidget for W { @@ -28,6 +31,14 @@ impl, Ctx: 'static, Tag> CoreWidget inner: self.add(ui).erase_type(), } } + + fn label(self, label: impl Into) -> WidgetIdFnRet!(W::Widget, Ctx) { + |ui| { + let id = self.add(ui); + ui.set_label(&id, label.into()); + id + } + } } pub trait CoreWidgetArr { diff --git a/src/layout/id.rs b/src/layout/id.rs index 0a167dd..0d6006e 100644 --- a/src/layout/id.rs +++ b/src/layout/id.rs @@ -79,6 +79,7 @@ impl Drop for WidgetId { } pub struct IdTag; +pub struct IdFnTag; // pub trait WidgetIdFn = FnOnce(&mut Ui) -> WidgetId; macro_rules! WidgetIdFnRet { @@ -126,14 +127,14 @@ impl) -> W, W: Widget, Ctx> Idable for F } } -impl WidgetLike for WidgetId { +impl WidgetLike for WidgetId { type Widget = W; fn add(self, _: &mut Ui) -> WidgetId { self } } -impl) -> WidgetId, Ctx> WidgetLike for F { +impl) -> WidgetId, Ctx> WidgetLike for F { type Widget = W; fn add(self, ui: &mut Ui) -> WidgetId { self(ui) diff --git a/src/layout/painter.rs b/src/layout/painter.rs index 4e643e8..e6791fa 100644 --- a/src/layout/painter.rs +++ b/src/layout/painter.rs @@ -6,7 +6,7 @@ use crate::{ WidgetInstance, Widgets, }, render::{Primitive, PrimitiveHandle, Primitives}, - util::Id, + util::{HashMap, Id}, }; struct State { @@ -25,6 +25,7 @@ pub struct Painter<'a, Ctx: 'static> { pub(super) primitives: &'a mut Primitives, textures: &'a mut Textures, text: &'a mut TextData, + labels: &'a HashMap, screen_size: Vec2, /// state of what's currently being drawn state: State, @@ -41,6 +42,7 @@ impl<'a, Ctx> Painter<'a, Ctx> { text: &'a mut TextData, textures: &'a mut Textures, screen_size: Vec2, + labels: &'a HashMap, ) -> Self { Self { widgets: nodes, @@ -51,6 +53,7 @@ impl<'a, Ctx> Painter<'a, Ctx> { text, textures, screen_size, + labels, state: State { region: UiRegion::full(), children: Vec::new(), @@ -123,7 +126,7 @@ impl<'a, Ctx> Painter<'a, Ctx> { id, WidgetInstance { region, - primitives: (start_i..end_i).into(), + span: start_i..end_i, children: child_state.children, parent: child_state.parent, }, @@ -168,21 +171,51 @@ impl<'a, Ctx> Painter<'a, Ctx> { } let instance = self.free(id); self.state.id = instance.parent; - self.primitives.prepare(instance.primitives.into()); + self.primitives.prepare(instance.span.clone()); self.draw_raw_at(id, instance.region); - let delta = self.primitives.apply(instance.primitives.into()); - if let Some(parent) = self.state.id.take() { - self.shift_end(parent, delta); + let start = instance.span.start; + let delta = self.primitives.apply(instance.span); + if delta != 0 + && let Some(parent) = self.state.id.take() + { + self.shift_parent(parent, start, delta); } } - fn shift_end(&mut self, id: Id, delta: isize) { - let instance = self.active.widgets.get_mut(&id).unwrap(); - let end = &mut instance.primitives.end; + fn shift_parent(&mut self, parent: Id, start: usize, delta: isize) { + let instance = self.active.widgets.get_mut(&parent).unwrap(); + let end = &mut instance.span.end; *end = end.strict_add_signed(delta); - if let Some(parent) = &instance.parent { - let parent = parent.duplicate(); - self.shift_end(parent, delta); + // ids are supposed to be unique so theoretically no cloning is needed + // leaving for now for testing + let parent = instance.parent.as_ref().map(|p| p.duplicate()); + for child in instance + .children + .iter() + .map(|id| id.duplicate()) + .collect::>() + { + self.shift_child(&child, start, delta); + } + if let Some(parent) = parent { + self.shift_parent(parent, start, delta); + } + } + + fn shift_child(&mut self, child: &Id, start: usize, delta: isize) { + let instance = self.active.widgets.get_mut(child).unwrap(); + if instance.span.start <= start { + return; + } + instance.span.start = instance.span.start.strict_add_signed(delta); + instance.span.end = instance.span.end.strict_add_signed(delta); + for child in instance + .children + .iter() + .map(|id| id.duplicate()) + .collect::>() + { + self.shift_child(&child, start, delta); } } diff --git a/src/layout/ui.rs b/src/layout/ui.rs index 09513ca..e124581 100644 --- a/src/layout/ui.rs +++ b/src/layout/ui.rs @@ -10,14 +10,14 @@ use crate::{ }; use std::{ any::{Any, TypeId}, - ops::{Index, IndexMut}, - range::Range, + ops::{Index, IndexMut, Range}, sync::mpsc::{Receiver, Sender, channel}, }; pub struct Ui { base: Option, widgets: Widgets, + labels: HashMap, updates: Vec, recv: Receiver, send: Sender, @@ -46,6 +46,11 @@ impl Ui { w.add(self) } + /// useful for debugging + pub fn set_label(&mut self, id: &WidgetId, label: String) { + self.labels.insert(id.id.duplicate(), label); + } + pub fn add_widget>(&mut self, w: W) -> WidgetId { self.push(w) } @@ -56,8 +61,8 @@ impl Ui { id } - pub fn set>(&mut self, i: &WidgetId, w: W) { - self.widgets.insert(i.id.duplicate(), w); + pub fn set>(&mut self, id: &WidgetId, w: W) { + self.widgets.insert(id.id.duplicate(), w); } pub fn set_base(&mut self, w: impl WidgetLike) { @@ -108,6 +113,7 @@ impl Ui { &mut self.text, &mut self.textures, self.size, + &self.labels, ); if let Some(base) = &self.base { painter.draw(base); @@ -131,6 +137,7 @@ impl Ui { where Ctx: 'static, { + self.free(); let mut painter = Painter::new( &self.widgets, &mut self.primitives, @@ -140,6 +147,7 @@ impl Ui { &mut self.text, &mut self.textures, self.size, + &self.labels, ); for id in self.updates.drain(..) { painter.redraw(&id.id); @@ -149,6 +157,7 @@ impl Ui { /// free any resources that don't have references anymore fn free(&mut self) { for id in self.recv.try_iter() { + self.labels.remove(&id); self.widgets.delete(id); } self.textures.free(); @@ -244,6 +253,7 @@ impl Default for Ui { Self { base: Default::default(), widgets: Widgets::new(), + labels: Default::default(), updates: Default::default(), primitives: Default::default(), textures: Textures::new(), @@ -260,7 +270,7 @@ impl Default for Ui { pub struct WidgetInstance { pub region: UiRegion, - pub primitives: Range, + pub span: Range, pub children: Vec, pub parent: Option, } diff --git a/src/lib.rs b/src/lib.rs index 3c089ee..1935c7b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ #![feature(const_trait_impl)] #![feature(const_from)] #![feature(map_try_insert)] -#![feature(new_range_api)] pub mod core; pub mod layout; diff --git a/src/render/texture.rs b/src/render/texture.rs index b78c19c..2bc3468 100644 --- a/src/render/texture.rs +++ b/src/render/texture.rs @@ -23,6 +23,9 @@ impl GpuTextures { TextureUpdate::Free(i) => self.free(i), } } + // if changed { + // println!("{}", self.views.len()); + // } changed } fn set(&mut self, i: u32, image: &DynamicImage) { diff --git a/src/testing/app.rs b/src/testing/app.rs index 6466a26..b766298 100644 --- a/src/testing/app.rs +++ b/src/testing/app.rs @@ -26,8 +26,8 @@ impl ApplicationHandler for App { let window = event_loop .create_window(Window::default_attributes()) .unwrap(); - let (ui, ids) = Client::create_ui(); - let client = Client::new(window.into(), ids); + let (ui, cui) = Client::create_ui(); + let client = Client::new(window.into(), cui); self.client = Some((client, ui)); } } diff --git a/src/testing/mod.rs b/src/testing/mod.rs index 97d79cb..3bae7f5 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -18,15 +18,16 @@ pub fn main() { pub struct Client { renderer: Renderer, input: Input, - ui: UiIds, + ui: ClientUi, } -pub struct UiIds { - span_add: WidgetId, +pub struct ClientUi { + info: WidgetId, + old_num: usize, } impl Client { - pub fn create_ui() -> (Ui, UiIds) { + pub fn create_ui() -> (Ui, ClientUi) { let mut ui = Ui::new(); let span_add = ui.id(); let rect = Rect { @@ -109,10 +110,15 @@ impl Client { ) .span(Dir::RIGHT, [1, 1, 1]), ); - let test_button = Rect::new(Color::PURPLE) + let s = span_add.clone(); + let add_button = Rect::new(Color::LIME) .radius(30) .on(Sense::PressStart, move |ctx| { - println!("{}", ctx.ui.num_widgets()); + let child = ctx + .ui + .add(image(include_bytes!("assets/sungals.png"))) + .erase_type(); + ctx.ui[&s].children.push((child, ratio(1))); }) .region( UiPos::corner(Corner::BotRight) @@ -131,22 +137,35 @@ impl Client { .expand((150, 150)) .shifted((75, -75)), ); + + let info = ui.add(text("")); + let info_sect = info.clone().region( + UiPos::corner(Corner::TopRight) + .expand((150, 150)) + .shifted((-75, 0)), + ); ui.set_base( ( - tabs, - (pad_test.pad(10).id(&main), test_button, del_button).stack(), + tabs.label("tabs"), + ( + pad_test.pad(10).id(&main), + add_button.label("add button"), + del_button.label("del button"), + info_sect.label("info sect"), + ) + .stack().label("main stack"), ) - .span(Dir::DOWN, [fixed(40), ratio(1)]), + .span(Dir::DOWN, [fixed(40), ratio(1)]).label("root"), ); - (ui, UiIds { span_add }) + (ui, ClientUi { info, old_num: 0 }) } - pub fn new(window: Arc, ui: UiIds) -> Self { + pub fn new(window: Arc, ui: ClientUi) -> Self { let renderer = Renderer::new(window); Self { renderer, - ui, input: Input::default(), + ui, } } @@ -166,17 +185,13 @@ impl Client { ui.resize((size.width, size.height)); self.renderer.resize(&size) } - WindowEvent::KeyboardInput { event, .. } => { - if event.state.is_pressed() { - let child = ui - .add(image(include_bytes!("assets/sungals.png"))) - .erase_type(); - ui[&self.ui.span_add].children.push((child, ratio(1))); - self.renderer.window().request_redraw(); - } - } _ => (), } + let num = ui.num_widgets(); + if num != self.ui.old_num { + ui[&self.ui.info].content = format!("widgets: {}", num); + self.ui.old_num = num; + } if ui.needs_redraw() { self.renderer.window().request_redraw(); }