diff --git a/src/core/text.rs b/src/core/text.rs index a785014..04bcc2d 100644 --- a/src/core/text.rs +++ b/src/core/text.rs @@ -41,12 +41,10 @@ impl Widget for Text { let (handle, offset) = painter.render_text(&mut self.buf, &self.content, &self.attrs); let dims = handle.size(); let size = offset.size(&handle); - let mut region = painter.region().center().expand(size); + let mut region = Align::Center.pos().expand(size); region.top_left.offset += offset.top_left; region.bot_right.offset = region.top_left.offset + dims; - painter.draw_texture_at(&handle, region); - // TODO: when on_update is added to painter, - // reuse TextureHandle + painter.draw_texture_within(&handle, region); } fn get_size(&mut self, ctx: &mut SizeCtx) -> Vec2 { diff --git a/src/layout/painter.rs b/src/layout/painter.rs index 1f6e1d2..70d64d6 100644 --- a/src/layout/painter.rs +++ b/src/layout/painter.rs @@ -1,185 +1,164 @@ +use std::ops::Range; + use crate::{ layout::{ Active, SensorMap, SizeCtx, TextAttrs, TextBuffer, TextData, TextOffset, TextureHandle, - Textures, UiRegion, Vec2, WidgetId, WidgetInstance, Widgets, + Textures, UiRegion, Vec2, WidgetId, Widgets, }, render::{Primitive, PrimitiveHandle, Primitives}, - util::Id, + util::{HashSet, Id}, }; -struct State { +pub struct Painter<'a, 'c> { + ctx: &'a mut PainterCtx<'c>, region: UiRegion, - children: Vec, - parent: Option, textures: Vec, - // TODO: there's probably a better way but idc at this point - id: Option, + primitives: Vec, + children: Vec, + id: Id, } -pub struct Painter<'a> { - widgets: &'a Widgets, - sensors_map: &'a SensorMap, - pub(super) active: &'a mut Active, - pub(super) primitives: &'a mut Primitives, - textures: &'a mut Textures, - text: &'a mut TextData, - screen_size: Vec2, - /// state of what's currently being drawn - state: State, +pub struct PainterCtx<'a> { + pub widgets: &'a Widgets, + pub sensor_map: &'a SensorMap, + pub active: &'a mut Active, + pub primitives: &'a mut Primitives, + pub textures: &'a mut Textures, + pub text: &'a mut TextData, + pub screen_size: Vec2, + drawing: HashSet, } -impl<'a> Painter<'a> { - #[allow(clippy::too_many_arguments)] - pub(super) fn new( +pub struct WidgetInstance { + pub id: Id, + pub region: UiRegion, + pub parent: Option, + pub textures: Vec, + pub primitives: Vec, + pub children: Vec, + pub span: Range, +} + +impl<'a> PainterCtx<'a> { + pub fn new( widgets: &'a Widgets, primitives: &'a mut Primitives, - sensors_map: &'a SensorMap, + sensor_map: &'a SensorMap, active: &'a mut Active, - text: &'a mut TextData, textures: &'a mut Textures, + text: &'a mut TextData, screen_size: Vec2, ) -> Self { Self { widgets, + sensor_map, active, - sensors_map, primitives, - text, textures, + text, screen_size, - state: State { - region: UiRegion::full(), - children: Vec::new(), - parent: None, - id: None, - textures: Vec::new(), - }, + drawing: HashSet::new(), } } - /// Writes a primitive to be rendered - pub fn write_at(&mut self, data: P, region: UiRegion) -> PrimitiveHandle

{ - self.primitives.write(data, region) - } - - /// Writes a primitive to be rendered - pub fn write(&mut self, data: P) -> PrimitiveHandle

{ - self.write_at(data, self.state.region) - } - - /// Draws a widget within this widget's region. - pub fn draw(&mut self, id: &WidgetId) { - self.draw_at(id, self.state.region); - } - - /// Draws a widget somewhere within this one. - /// Useful for drawing child widgets in select areas. - pub fn draw_within(&mut self, id: &WidgetId, region: UiRegion) { - self.draw_at(id, region.within(&self.state.region)); - } - - /// Draws a widget in an arbitrary region. - pub fn draw_at(&mut self, id: &WidgetId, region: UiRegion) { - self.draw_raw_at(&id.id, region); - } - - fn draw_raw_at(&mut self, id: &Id, region: UiRegion) { - self.state.children.push(id.duplicate()); - - // &mut self is passed to avoid copying all of the "static" pointers in self - // so need to save non static data here - let child_state = State { - region, - children: Vec::new(), - id: Some(id.duplicate()), - parent: self.state.id.as_ref().map(|i| i.duplicate()), - textures: Vec::new(), + pub fn redraw(&mut self, id: &Id) { + self.drawing.clear(); + let Some(active) = self.remove(id) else { + return; + }; + self.primitives.set_pos(active.span.start); + self.draw_inner(id, active.region, active.parent(), Some(active.children)); + let delta = self.primitives.apply(active.span.clone()); + if delta != 0 + && let Some(parent) = active.parent + { + self.shift_parent(parent, active.span.start, delta); + } + } + + pub fn draw(&mut self, id: &Id) { + self.drawing.clear(); + self.primitives.clear(); + self.draw_inner(id, UiRegion::full(), None, None); + self.primitives.replace(); + } + + fn draw_inner( + &mut self, + id: &Id, + region: UiRegion, + parent: Option, + old_children: Option>, + ) { + if self.drawing.contains(id) { + panic!("Cannot draw the same widget twice"); + } + let mut old_children = old_children.unwrap_or_default(); + if let Some(active) = self.active.widgets.get(id) { + if active.parent != parent { + panic!("Cannot draw the same widget twice"); + } + if active.region == region { + self.primitives.skip(&active.span); + return; + } + // TODO: + // else if active.region.size() == region.size() { move } + let active = self.remove(id).unwrap(); + old_children = active.children; + } + + let mut painter = Painter { + region, + id: id.duplicate(), + textures: Vec::new(), + primitives: Vec::new(), + ctx: self, + children: Vec::new(), }; - // save state - let self_state = std::mem::replace(&mut self.state, child_state); // draw widgets - let start_i = self.primitives.cur_pos(); - self.widgets.get_dyn_dynamic(id).draw(self); - let end_i = self.primitives.cur_pos(); - - // restore state - let child_state = std::mem::replace(&mut self.state, self_state); + let start_i = painter.ctx.primitives.cur_pos(); + painter.ctx.widgets.get_dyn_dynamic(id).draw(&mut painter); + let end_i = painter.ctx.primitives.cur_pos(); // add to active - self.active.add( - id, - WidgetInstance { - region, - span: start_i..end_i, - children: child_state.children, - parent: child_state.parent, - textures: child_state.textures, - }, - self.sensors_map, - ); - } - - pub fn draw_texture(&mut self, handle: &TextureHandle) { - self.state.textures.push(handle.clone()); - self.write(handle.primitive()); - } - - pub fn draw_texture_at(&mut self, handle: &TextureHandle, region: UiRegion) { - let old = self.state.region; - self.state.region = region; - self.draw_texture(handle); - self.state.region = old; - } - - /// returns (handle, offset from top left) - pub fn render_text( - &mut self, - buffer: &mut TextBuffer, - content: &str, - attrs: &TextAttrs, - ) -> (TextureHandle, TextOffset) { - self.text.draw(buffer, content, attrs, self.textures) - } - - pub fn region(&self) -> UiRegion { - self.state.region - } - - pub fn region_size(&self) -> Vec2 { - self.state.region.in_size(self.screen_size) - } - - pub fn size(&mut self, id: &WidgetId) -> Vec2 { - self.widgets - .get_dyn_dynamic(&id.id) - .get_size(&mut self.size_ctx()) - } - - pub fn size_ctx(&mut self) -> SizeCtx<'_> { - SizeCtx { - size: self.region().in_size(self.screen_size), - text: self.text, - textures: self.textures, - widgets: self.widgets, + let instance = WidgetInstance { + id: id.duplicate(), + region, + parent, + textures: painter.textures, + span: start_i..end_i, + primitives: painter.primitives, + children: painter.children, + }; + for c in &old_children { + if !instance.children.contains(c) { + self.remove_rec(c); + } } + + self.active.add(id, instance, self.sensor_map); } - pub(crate) fn redraw(&mut self, id: &Id) { - if !self.active.widgets.contains_key(id) { - return; + fn remove(&mut self, id: &Id) -> Option { + let inst = self.active.remove(id); + if let Some(inst) = &inst { + for h in &inst.primitives { + self.primitives.free(h); + } } - let instance = self.free(id); - self.state.id = instance.parent; - self.primitives.prepare(instance.span.clone()); - self.draw_raw_at(id, instance.region); - 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); + inst + } + + fn remove_rec(&mut self, id: &Id) -> Option { + let inst = self.remove(id); + if let Some(inst) = &inst { + for c in &inst.children { + self.remove_rec(c); + } } + inst } /// shifts the primitive spans for all widgets above this one in the tree @@ -191,7 +170,7 @@ impl<'a> Painter<'a> { *end = end.strict_add_signed(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()); + let parent = instance.parent(); for child in instance .children .iter() @@ -221,12 +200,86 @@ impl<'a> Painter<'a> { self.shift_child(&child, start, delta); } } +} - fn free(&mut self, id: &Id) -> WidgetInstance { - let instance = self.active.remove(id).unwrap(); - for child in &instance.children { - self.free(child); +impl<'a, 'c> Painter<'a, 'c> { + fn write_at(&mut self, data: P, region: UiRegion) { + self.primitives + .push(self.ctx.primitives.write(self.id.duplicate(), data, region)); + } + + /// Writes a primitive to be rendered + pub fn write(&mut self, data: P) { + self.write_at(data, self.region) + } + + /// Draws a widget within this widget's region. + pub fn draw(&mut self, id: &WidgetId) { + self.draw_at(id, self.region); + } + + /// Draws a widget somewhere within this one. + /// Useful for drawing child widgets in select areas. + pub fn draw_within(&mut self, id: &WidgetId, region: UiRegion) { + self.draw_at(id, region.within(&self.region)); + } + + fn draw_at(&mut self, id: &WidgetId, region: UiRegion) { + self.children.push(id.id.duplicate()); + self.ctx + .draw_inner(&id.id, region, Some(self.id.duplicate()), None); + } + + pub fn draw_texture_within(&mut self, handle: &TextureHandle, region: UiRegion) { + self.textures.push(handle.clone()); + self.write_at(handle.primitive(), region.within(&self.region)); + } + + pub fn draw_texture(&mut self, handle: &TextureHandle) { + self.textures.push(handle.clone()); + self.write(handle.primitive()); + } + + pub fn draw_texture_at(&mut self, handle: &TextureHandle, region: UiRegion) { + self.textures.push(handle.clone()); + self.write_at(handle.primitive(), region); + } + + /// returns (handle, offset from top left) + pub fn render_text( + &mut self, + buffer: &mut TextBuffer, + content: &str, + attrs: &TextAttrs, + ) -> (TextureHandle, TextOffset) { + self.ctx + .text + .draw(buffer, content, attrs, self.ctx.textures) + } + + pub fn region_size(&self) -> Vec2 { + self.region.in_size(self.ctx.screen_size) + } + + pub fn size(&mut self, id: &WidgetId) -> Vec2 { + self.ctx + .widgets + .get_dyn_dynamic(&id.id) + .get_size(&mut self.size_ctx()) + } + + pub fn size_ctx(&mut self) -> SizeCtx<'_> { + SizeCtx { + size: self.region.in_size(self.ctx.screen_size), + text: self.ctx.text, + textures: self.ctx.textures, + widgets: self.ctx.widgets, } - instance + } +} + +impl WidgetInstance { + pub fn parent(&self) -> Option { + self.parent.as_ref().map(|p| p.duplicate()) } } diff --git a/src/layout/pos.rs b/src/layout/pos.rs index fa15270..35987b4 100644 --- a/src/layout/pos.rs +++ b/src/layout/pos.rs @@ -4,7 +4,7 @@ use crate::{ }; #[repr(C)] -#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, Default)] +#[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable, Default)] pub struct UiPos { pub anchor: Vec2, pub offset: Vec2, @@ -132,7 +132,7 @@ impl UIScalar { } #[repr(C)] -#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +#[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] pub struct UiRegion { pub top_left: UiPos, pub bot_right: UiPos, diff --git a/src/layout/ui.rs b/src/layout/ui.rs index 18f60f1..45db3a8 100644 --- a/src/layout/ui.rs +++ b/src/layout/ui.rs @@ -2,15 +2,15 @@ use image::DynamicImage; use crate::{ layout::{ - ActiveSensors, Painter, SensorMap, StaticWidgetId, TextData, TextureHandle, Textures, - UiRegion, Vec2, Widget, WidgetId, WidgetLike, + ActiveSensors, PainterCtx, SensorMap, StaticWidgetId, TextData, TextureHandle, Textures, + Vec2, Widget, WidgetId, WidgetInstance, WidgetLike, }, render::Primitives, util::{HashMap, Id, IdTracker}, }; use std::{ any::{Any, TypeId}, - ops::{Deref, DerefMut, Index, IndexMut, Range}, + ops::{Deref, DerefMut, Index, IndexMut}, sync::mpsc::{Receiver, Sender, channel}, }; @@ -118,22 +118,20 @@ impl Ui { pub fn redraw_all(&mut self) { self.active.clear(); - self.primitives.clear(); // free before bc nothing should exist self.free(); - let mut painter = Painter::new( + let mut ctx = PainterCtx::new( &self.widgets, &mut self.primitives, &self.sensor_map, &mut self.active, - &mut self.text, &mut self.textures, + &mut self.text, self.size, ); if let Some(base) = &self.base { - painter.draw(base); + ctx.draw(&base.id); } - self.primitives.replace(); } pub fn update(&mut self) { @@ -146,17 +144,17 @@ impl Ui { } fn redraw_updates(&mut self) { - let mut painter = Painter::new( + let mut ctx = PainterCtx::new( &self.widgets, &mut self.primitives, &self.sensor_map, &mut self.active, - &mut self.text, &mut self.textures, + &mut self.text, self.size, ); for id in self.updates.drain(..) { - painter.redraw(&id); + ctx.redraw(&id); } self.free(); } @@ -350,17 +348,10 @@ impl Default for Ui { } } -pub struct WidgetInstance { - pub region: UiRegion, - pub span: Range, - pub children: Vec, - pub parent: Option, - pub textures: Vec, -} pub type ActiveWidgets = HashMap; #[derive(Default)] -pub(super) struct Active { +pub struct Active { pub sensors: ActiveSensors, pub widgets: ActiveWidgets, } diff --git a/src/render/mod.rs b/src/render/mod.rs index 651cdd0..b67110a 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -51,8 +51,8 @@ impl UiRenderer { pub fn update(&mut self, device: &Device, queue: &Queue, ui: &mut Ui) { if ui.primitives.updated { self.instance - .update(device, queue, &ui.primitives.instances); - self.primitives.update(device, queue, &ui.primitives.data); + .update(device, queue, ui.primitives.instances()); + self.primitives.update(device, queue, ui.primitives.data()); self.primitive_group = Self::primitive_group(device, &self.primitive_layout, self.primitives.buffers()) } diff --git a/src/render/primitive.rs b/src/render/primitive.rs index e659632..225bbc0 100644 --- a/src/render/primitive.rs +++ b/src/render/primitive.rs @@ -1,19 +1,24 @@ -use std::{ - marker::PhantomData, - ops::{Deref, DerefMut, Range}, -}; +use std::ops::{Deref, DerefMut, Range}; use crate::{ layout::{Color, UiRegion}, render::{ArrBuf, data::PrimitiveInstance}, + util::Id, }; use bytemuck::Pod; use wgpu::*; +#[derive(Default)] +struct Instances { + data: Vec, + assoc: Vec, +} + pub struct Primitives { - pub(super) instances: Vec, - pub(super) run: Vec, - pub(super) data: PrimitiveData, + instances: Instances, + run: Instances, + pos: usize, + data: PrimitiveData, pub updated: bool, run_start: usize, } @@ -23,6 +28,7 @@ impl Default for Primitives { Self { instances: Default::default(), run: Default::default(), + pos: 0, data: Default::default(), updated: true, run_start: 0, @@ -98,15 +104,40 @@ macro_rules! primitives { } impl Primitives { - pub fn write(&mut self, data: P, region: UiRegion) -> PrimitiveHandle

{ + pub fn write(&mut self, id: Id, data: P, region: UiRegion) -> PrimitiveHandle { let vec = P::vec(&mut self.data); let i = vec.add(data); - self.run.push(PrimitiveInstance { + let inst = PrimitiveInstance { region, idx: i as u32, binding: P::BINDING, - }); - PrimitiveHandle::new(i) + }; + if self.running() { + self.run.push(id, inst); + } else if self.instances.assoc.get(self.pos).is_some_and(|i| *i == id) { + self.instances.data[self.pos] = inst; + } else { + self.run_start = self.pos; + self.run.push(id, inst); + } + self.pos += 1; + PrimitiveHandle::new::

(i) + } + + fn running(&self) -> bool { + self.run.len() != 0 + } + + pub fn skip(&mut self, range: &Range) { + if self.running() { + self.run.data.extend(&self.instances.data[range.clone()]); + self.run.assoc.extend( + self.instances.assoc[range.clone()] + .iter() + .map(|i| i.duplicate()), + ); + } + self.pos += range.len(); } pub(crate) fn clear(&mut self) { @@ -114,53 +145,55 @@ impl Primitives { self.instances.clear(); self.run.clear(); self.data.clear(); - } - - pub fn set(&mut self, handle: &PrimitiveHandle

, data: P) { - P::vec(&mut self.data)[handle.idx] = data; - } - - pub fn prepare(&mut self, span: Range) { - self.run_start = span.start; - for instance in &self.instances[span] { - self.data.free(instance.binding, instance.idx as usize); - } + self.pos = 0; } pub fn apply(&mut self, span: Range) -> isize { - let delta = self.run.len() as isize - span.len() as isize; - self.instances.splice(span, self.run.drain(..)); + let delta = self.pos as isize - span.end as isize; + if self.run.len() == 0 { + self.run_start = self.pos; + } + let span = self.run_start..span.end; + self.instances.splice(span, &mut self.run); delta } + pub fn set_pos(&mut self, p: usize) { + self.pos = p; + } + + pub fn free(&mut self, h: &PrimitiveHandle) { + self.data.free(h.binding, h.idx); + } + pub fn replace(&mut self) { std::mem::swap(&mut self.run, &mut self.instances); self.run.clear(); } pub fn cur_pos(&self) -> usize { - self.run_start + self.run.len() + self.pos + } + + pub fn data(&self) -> &PrimitiveData { + &self.data + } + + pub fn instances(&self) -> &Vec { + &self.instances.data } } -/// primitives but only exposes set for redrawing -pub struct PrimitiveEditor<'a>(&'a mut Primitives); -impl PrimitiveEditor<'_> { - pub fn set(&mut self, handle: &PrimitiveHandle

, data: P) { - self.0.set(handle, data); - } -} - -pub struct PrimitiveHandle { +pub struct PrimitiveHandle { idx: usize, - _pd: PhantomData

, + binding: u32, } -impl PrimitiveHandle

{ - fn new(idx: usize) -> Self { +impl PrimitiveHandle { + fn new(idx: usize) -> Self { Self { idx, - _pd: PhantomData, + binding: P::BINDING, } } } @@ -236,3 +269,21 @@ impl DerefMut for PrimitiveVec { &mut self.vec } } + +impl Instances { + pub fn clear(&mut self) { + self.data.clear(); + self.assoc.clear(); + } + pub fn len(&self) -> usize { + self.data.len() + } + pub fn push(&mut self, id: Id, inst: PrimitiveInstance) { + self.assoc.push(id); + self.data.push(inst); + } + pub fn splice(&mut self, span: Range, other: &mut Instances) { + self.data.splice(span.clone(), other.data.drain(..)); + self.assoc.splice(span, other.assoc.drain(..)); + } +}