sizing actually working correctly now

This commit is contained in:
2025-09-16 17:31:54 -04:00
parent b48acccb8d
commit f9097807a2
17 changed files with 333 additions and 194 deletions

View File

@@ -7,11 +7,11 @@ pub struct Aligned {
impl Widget for Aligned { impl Widget for Aligned {
fn draw(&mut self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
let region = UiRegion::from_size_align(painter.size(&self.inner), self.align); let region = UiRegion::from_ui_size_align(painter.size(&self.inner), self.align);
painter.widget_within(&self.inner, region); painter.widget_within(&self.inner, region);
} }
fn get_size(&mut self, ctx: &mut SizeCtx) -> Vec2 { fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 {
ctx.size(&self.inner) ctx.size(&self.inner)
} }
} }

View File

@@ -10,8 +10,8 @@ impl Widget for Image {
painter.texture(&self.handle); painter.texture(&self.handle);
} }
fn get_size(&mut self, _: &mut SizeCtx) -> Vec2 { fn desired_size(&mut self, _: &mut SizeCtx) -> UiVec2 {
self.handle.size() UiVec2::abs(self.handle.size())
} }
} }

View File

@@ -1,6 +1,6 @@
mod align; mod align;
mod frame;
mod image; mod image;
mod pad;
mod rect; mod rect;
mod sense; mod sense;
mod sized; mod sized;
@@ -11,8 +11,8 @@ mod text_edit;
mod trait_fns; mod trait_fns;
pub use align::*; pub use align::*;
pub use frame::*;
pub use image::*; pub use image::*;
pub use pad::*;
pub use rect::*; pub use rect::*;
pub use sense::*; pub use sense::*;
pub use sized::*; pub use sized::*;

View File

@@ -10,10 +10,10 @@ impl Widget for Padded {
painter.widget_within(&self.inner, self.padding.region()); painter.widget_within(&self.inner, self.padding.region());
} }
fn get_size(&mut self, ctx: &mut SizeCtx) -> Vec2 { fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 {
let mut size = ctx.size(&self.inner); let mut size = ctx.size(&self.inner);
size.x += self.padding.left + self.padding.right; size.abs.x += self.padding.left + self.padding.right;
size.y += self.padding.top + self.padding.bottom; size.abs.y += self.padding.top + self.padding.bottom;
size size
} }
} }
@@ -36,10 +36,10 @@ impl Padding {
} }
pub fn region(&self) -> UiRegion { pub fn region(&self) -> UiRegion {
let mut region = UiRegion::full(); let mut region = UiRegion::full();
region.top_left.offset.x += self.left; region.top_left.abs.x += self.left;
region.top_left.offset.y += self.top; region.top_left.abs.y += self.top;
region.bot_right.offset.x -= self.right; region.bot_right.abs.x -= self.right;
region.bot_right.offset.y -= self.bottom; region.bot_right.abs.y -= self.bottom;
region region
} }
} }

View File

@@ -10,7 +10,7 @@ impl Widget for Sized {
painter.widget(&self.inner); painter.widget(&self.inner);
} }
fn get_size(&mut self, _: &mut SizeCtx) -> Vec2 { fn desired_size(&mut self, _: &mut SizeCtx) -> UiVec2 {
self.size UiVec2::abs(self.size)
} }
} }

View File

@@ -8,34 +8,30 @@ pub struct Span {
impl Widget for Span { impl Widget for Span {
fn draw(&mut self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
let total = self.setup(&mut painter.size_ctx()); let total = self.setup(&mut painter.size_ctx());
let mut start = UIScalar::min(); let mut start = UiScalar::rel_min();
for (child, length) in &self.children { for (child, length) in &self.children {
let mut child_region = UiRegion::full(); let mut child_region = UiRegion::full();
let mut axis = child_region.axis_mut(self.dir.axis); let mut axis = child_region.axis_mut(self.dir.axis);
axis.top_left.set(start); axis.top_left.set(start);
match *length { match *length {
SpanLen::Fixed(offset) => { SpanLen::Fixed(offset) => {
start.offset += offset; start.abs += offset;
*axis.bot_right.offset = start.offset;
*axis.bot_right.anchor = *axis.top_left.anchor;
} }
SpanLen::Ratio(ratio) => { SpanLen::Ratio(ratio) => {
let offset = UIScalar::new(total.relative, total.fixed); let offset = UiScalar::new(total.rel, total.abs);
let rel_end = UIScalar::from_anchor(ratio / total.ratio); let rel_end = UiScalar::from_anchor(ratio / total.ratio);
start = rel_end.within(start, (UIScalar::max() + start) - offset); start = rel_end.within(start, (UiScalar::rel_max() + start) - offset);
axis.bot_right.set(start);
} }
SpanLen::Relative(rel) => { SpanLen::Relative(rel) => {
start.anchor += rel; start.rel += rel;
axis.bot_right.set(start);
} }
SpanLen::Sized(size) => { SpanLen::Sized(size) => {
let offset = size.axis(self.dir.axis); let size_axis = size.axis(self.dir.axis);
start.offset += offset; start.abs += size_axis.abs;
*axis.bot_right.offset = start.offset; start.rel += size_axis.rel;
*axis.bot_right.anchor = *axis.top_left.anchor;
} }
} }
axis.bot_right.set(start);
if self.dir.sign == Sign::Neg { if self.dir.sign == Sign::Neg {
child_region.flip(); child_region.flip();
} }
@@ -43,24 +39,28 @@ impl Widget for Span {
} }
} }
fn get_size(&mut self, ctx: &mut SizeCtx) -> Vec2 { fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 {
let total = self.setup(ctx); let total = self.setup(ctx);
let axis = self.dir.axis; let axis = self.dir.axis;
let dir_len = if total.ratio != 0.0 { let dir_len = if total.ratio != 0.0 {
ctx.size.axis(axis) UiScalar::rel_max()
} else { } else {
total.fixed + total.relative * ctx.size.axis(axis) UiScalar::new(total.rel, total.abs)
}; };
Vec2::from_axis(axis, dir_len, total.max_sized) let mut max_ortho = UiScalar::default();
for (child, _) in &self.children {
let size = ctx.size(child);
max_ortho = max_ortho.max(size.axis(!self.dir.axis));
}
UiVec2::from_axis(axis, dir_len, max_ortho)
} }
} }
#[derive(Default)] #[derive(Default)]
pub struct SpanLenSums { pub struct SpanLenSums {
pub fixed: f32, pub abs: f32,
pub ratio: f32, pub ratio: f32,
pub relative: f32, pub rel: f32,
pub max_sized: f32,
} }
impl Span { impl Span {
@@ -76,15 +76,15 @@ impl Span {
.iter_mut() .iter_mut()
.fold(SpanLenSums::default(), |mut s, (id, l)| { .fold(SpanLenSums::default(), |mut s, (id, l)| {
match l { match l {
SpanLen::Fixed(v) => s.fixed += *v, SpanLen::Fixed(v) => s.abs += *v,
SpanLen::Ratio(v) => s.ratio += *v, SpanLen::Ratio(v) => s.ratio += *v,
SpanLen::Relative(v) => s.relative += *v, SpanLen::Relative(v) => s.rel += *v,
SpanLen::Sized(v) => { SpanLen::Sized(v) => {
let size = ctx.size(id); let size = ctx.size(id);
let len = size.axis(self.dir.axis); let len = size.axis(self.dir.axis);
s.max_sized = s.max_sized.max(size.axis(!self.dir.axis));
*v = size; *v = size;
s.fixed += len; s.abs += len.abs;
s.rel += len.rel;
} }
} }
s s
@@ -105,7 +105,7 @@ pub enum SpanLen {
/// size determined by the child widget itself /// size determined by the child widget itself
/// the value is not used externally, I just don't wanna make a duplicate enum /// the value is not used externally, I just don't wanna make a duplicate enum
/// there are util functions instead so /// there are util functions instead so
Sized(Vec2), Sized(UiVec2),
} }
pub fn fixed<N: UiNum>(x: N) -> SpanLen { pub fn fixed<N: UiNum>(x: N) -> SpanLen {
@@ -121,7 +121,7 @@ pub fn relative<N: UiNum>(x: N) -> SpanLen {
} }
pub fn sized() -> SpanLen { pub fn sized() -> SpanLen {
SpanLen::Sized(Vec2::ZERO) SpanLen::Sized(UiVec2::default())
} }
impl<N: UiNum> From<N> for SpanLen { impl<N: UiNum> From<N> for SpanLen {

View File

@@ -66,15 +66,15 @@ impl Widget for Text {
let dims = handle.size(); let dims = handle.size();
self.size = offset.size(&handle); self.size = offset.size(&handle);
let mut region = self.region(); let mut region = self.region();
region.top_left.offset += offset.top_left; region.top_left.abs += offset.top_left;
region.bot_right.offset = region.top_left.offset + dims; region.bot_right.abs = region.top_left.abs + dims;
painter.texture_within(&handle, region); painter.texture_within(&handle, region);
} }
fn get_size(&mut self, ctx: &mut SizeCtx) -> Vec2 { fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 {
self.update_buf(&mut ctx.text.font_system); self.update_buf(&mut ctx.text.font_system);
let (handle, offset) = ctx.draw_text(&mut self.buf, &self.attrs); let (handle, offset) = ctx.draw_text(&mut self.buf, &self.attrs);
offset.size(&handle) UiVec2::abs(offset.size(&handle))
} }
} }

View File

@@ -24,16 +24,17 @@ impl TextEdit {
impl Widget for TextEdit { impl Widget for TextEdit {
fn draw(&mut self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
let font_system = &mut painter.text_data().font_system; let font_system = &mut painter.text_data().font_system;
self.buf.shape_until_scroll(font_system, false);
self.attrs.apply(font_system, &mut self.buf); self.attrs.apply(font_system, &mut self.buf);
self.buf.shape_until_scroll(font_system, false);
let (handle, tex_offset) = painter.render_text(&mut self.buf, &self.attrs); let (handle, tex_offset) = painter.render_text(&mut self.buf, &self.attrs);
let dims = handle.size(); let dims = handle.size();
self.size = tex_offset.size(&handle); self.size = tex_offset.size(&handle);
let region = self.region(); let region = self.region();
let mut tex_region = region; let mut tex_region = region;
tex_region.top_left.offset += tex_offset.top_left; tex_region.top_left.abs += tex_offset.top_left;
tex_region.bot_right.offset = tex_region.top_left.offset + dims; tex_region.bot_right.abs = tex_region.top_left.abs + dims;
painter.texture_within(&handle, tex_region); painter.texture_within(&handle, tex_region);
if let Some(cursor) = &self.cursor if let Some(cursor) = &self.cursor
&& let Some(pos) = cursor_pos(cursor, &self.buf) && let Some(pos) = cursor_pos(cursor, &self.buf)
{ {
@@ -45,12 +46,15 @@ impl Widget for TextEdit {
.shifted(offset) .shifted(offset)
.within(&region), .within(&region),
); );
} else {
// keep number of primitives constant so shifting isn't needed
painter.primitive(RectPrimitive::color(Color::NONE));
} }
} }
fn get_size(&mut self, ctx: &mut SizeCtx) -> Vec2 { fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 {
let (handle, offset) = ctx.draw_text(&mut self.buf, &self.attrs); let (handle, offset) = ctx.draw_text(&mut self.buf, &self.attrs);
offset.size(&handle) UiVec2::abs(offset.size(&handle))
} }
} }
@@ -105,6 +109,10 @@ impl TextEditBuilder {
self.attrs.line_height = height; self.attrs.line_height = height;
self self
} }
pub fn text_align(mut self, align: Align) -> Self {
self.align = align;
self
}
} }
pub struct TextEditCtx<'a> { pub struct TextEditCtx<'a> {
@@ -113,6 +121,21 @@ pub struct TextEditCtx<'a> {
} }
impl<'a> TextEditCtx<'a> { impl<'a> TextEditCtx<'a> {
pub fn take(&mut self) -> String {
let text = self
.text
.buf
.lines
.drain(..)
.map(|l| l.into_text())
.collect::<Vec<_>>()
.join("\n");
self.text
.buf
.set_text(self.font_system, "", &Attrs::new(), Shaping::Advanced);
text
}
pub fn motion(&mut self, motion: Motion) { pub fn motion(&mut self, motion: Motion) {
if let Some(cursor) = self.text.cursor if let Some(cursor) = self.text.cursor
&& let Some((cursor, _)) = && let Some((cursor, _)) =

View File

@@ -25,6 +25,8 @@ impl<T: ColorNum> Color<T> {
pub const PURPLE: Self = Self::rgb(T::MID, T::MIN, T::MAX); pub const PURPLE: Self = Self::rgb(T::MID, T::MIN, T::MAX);
pub const MAGENTA: Self = Self::rgb(T::MAX, T::MIN, T::MAX); pub const MAGENTA: Self = Self::rgb(T::MAX, T::MIN, T::MAX);
pub const NONE: Self = Self::new(T::MIN, T::MIN, T::MIN, T::MIN);
pub const fn new(r: T, g: T, b: T, a: T) -> Self { pub const fn new(r: T, g: T, b: T, a: T) -> Self {
Self { r, g, b, a } Self { r, g, b, a }
} }

View File

@@ -3,10 +3,10 @@ use std::ops::Range;
use crate::{ use crate::{
layout::{ layout::{
Active, TextAttrs, TextBuffer, TextData, TextOffset, TextureHandle, Textures, UiRegion, Active, TextAttrs, TextBuffer, TextData, TextOffset, TextureHandle, Textures, UiRegion,
Vec2, WidgetId, Widgets, UiVec2, Vec2, WidgetId, Widgets,
}, },
render::{Primitive, PrimitiveHandle, Primitives}, render::{Primitive, PrimitiveHandle, Primitives},
util::{HashSet, Id}, util::{HashMap, HashSet, Id},
}; };
pub struct Painter<'a, 'c> { pub struct Painter<'a, 'c> {
@@ -15,7 +15,7 @@ pub struct Painter<'a, 'c> {
textures: Vec<TextureHandle>, textures: Vec<TextureHandle>,
primitives: Vec<PrimitiveHandle>, primitives: Vec<PrimitiveHandle>,
children: Vec<Id>, children: Vec<Id>,
sized_children: HashSet<Id>, sized_children: HashMap<Id, UiVec2>,
id: Id, id: Id,
} }
@@ -36,7 +36,7 @@ pub struct WidgetInstance {
pub textures: Vec<TextureHandle>, pub textures: Vec<TextureHandle>,
pub primitives: Vec<PrimitiveHandle>, pub primitives: Vec<PrimitiveHandle>,
pub children: Vec<Id>, pub children: Vec<Id>,
pub resize: Option<Id>, pub resize: Option<(Id, UiVec2)>,
pub span: Range<usize>, pub span: Range<usize>,
} }
@@ -66,7 +66,7 @@ impl<'a> PainterCtx<'a> {
return; return;
}; };
if let Some(rid) = &active.resize { if let Some((rid, size)) = &active.resize {
self.redraw(&rid.duplicate()); self.redraw(&rid.duplicate());
if self.drawing.contains(id) { if self.drawing.contains(id) {
return; return;
@@ -76,15 +76,20 @@ impl<'a> PainterCtx<'a> {
let Some(active) = self.remove(id) else { let Some(active) = self.remove(id) else {
return; return;
}; };
let parent = active.parent();
drop(active.textures);
self.textures.free();
self.primitives.set_pos(active.span.start); self.primitives.set_pos(active.span.start);
self.draw_inner(id, active.region, active.parent(), Some(active.children)); self.draw_inner(id, active.region, parent, Some(active.children));
self.active.widgets.get_mut(id).unwrap().resize = active.resize; self.active.widgets.get_mut(id).unwrap().resize = active.resize;
let delta = self.primitives.apply(active.span.clone()); let delta = self.primitives.apply(active.span.clone());
if delta != 0 if delta != 0
&& let Some(parent) = active.parent && let Some(parent) = active.parent
{ {
self.shift_parent(parent, active.span.start, delta); self.shift_parent(id, parent, active.span.start, delta);
} }
} }
@@ -113,12 +118,12 @@ impl<'a> PainterCtx<'a> {
} }
let mut old_children = old_children.unwrap_or_default(); let mut old_children = old_children.unwrap_or_default();
let mut resize = None; let mut resize = None;
if let Some(active) = self.active.widgets.get(id) { if let Some(active) = self.active.widgets.get_mut(id) {
if active.parent != parent { if active.parent != parent {
panic!("Cannot draw the same widget twice (2)"); panic!("Cannot draw the same widget twice (2)");
} }
if active.region == region { if active.region == region {
self.primitives.skip(&active.span); self.primitives.skip(&mut active.span);
return; return;
} }
// TODO: // TODO:
@@ -126,6 +131,8 @@ impl<'a> PainterCtx<'a> {
let active = self.remove(id).unwrap(); let active = self.remove(id).unwrap();
old_children = active.children; old_children = active.children;
resize = active.resize; resize = active.resize;
drop(active.textures);
self.textures.free();
} }
let mut painter = Painter { let mut painter = Painter {
@@ -135,7 +142,7 @@ impl<'a> PainterCtx<'a> {
primitives: Vec::new(), primitives: Vec::new(),
ctx: self, ctx: self,
children: Vec::new(), children: Vec::new(),
sized_children: HashSet::new(), sized_children: Default::default(),
}; };
// draw widgets // draw widgets
@@ -156,9 +163,9 @@ impl<'a> PainterCtx<'a> {
children: painter.children, children: painter.children,
resize, resize,
}; };
for cid in sized_children { for (cid, size) in sized_children {
if let Some(w) = self.active.widgets.get_mut(&cid) { if let Some(w) = self.active.widgets.get_mut(&cid) {
w.resize = Some(id.duplicate()) w.resize = Some((id.duplicate(), size))
} }
} }
for c in &old_children { for c in &old_children {
@@ -193,28 +200,31 @@ impl<'a> PainterCtx<'a> {
/// shifts the primitive spans for all widgets above this one in the tree /// shifts the primitive spans for all widgets above this one in the tree
/// also goes into children of them and modifies those that come after this one /// also goes into children of them and modifies those that come after this one
/// should be done after applying primitives to ensure active spans are correct /// should be done after applying primitives to ensure active spans are correct
fn shift_parent(&mut self, parent: Id, start: usize, delta: isize) { fn shift_parent(&mut self, original: &Id, parent: Id, start: usize, delta: isize) {
let instance = self.active.widgets.get_mut(&parent).unwrap(); let instance = self.active.widgets.get_mut(&parent).unwrap();
let end = &mut instance.span.end; let end = &mut instance.span.end;
*end = end.strict_add_signed(delta); *end = end.strict_add_signed(delta);
let parent = instance.parent(); let parent_parent = instance.parent();
for child in instance for child in instance
.children .children
.iter() .iter()
// skip original
.skip_while(|id| *id != original)
.skip(1)
.map(|id| id.duplicate()) .map(|id| id.duplicate())
.collect::<Vec<_>>() .collect::<Vec<_>>()
{ {
self.shift_child(&child, start, delta); self.shift_child(&child, start, delta);
} }
if let Some(parent) = parent { if let Some(parent_parent) = parent_parent {
self.shift_parent(parent, start, delta); self.shift_parent(&parent, parent_parent, start, delta);
} }
} }
fn shift_child(&mut self, child: &Id, start: usize, delta: isize) { fn shift_child(&mut self, child: &Id, start: usize, delta: isize) {
let instance = self.active.widgets.get_mut(child).unwrap(); let instance = self.active.widgets.get_mut(child).unwrap();
// = also prevents the original id from getting shifted // = also prevents the original id from getting shifted
if instance.span.start <= start { if instance.span.start < start {
return; return;
} }
instance.span.start = instance.span.start.strict_add_signed(delta); instance.span.start = instance.span.start.strict_add_signed(delta);
@@ -232,11 +242,11 @@ impl<'a> PainterCtx<'a> {
impl<'a, 'c> Painter<'a, 'c> { impl<'a, 'c> Painter<'a, 'c> {
fn primitive_at<P: Primitive>(&mut self, primitive: P, region: UiRegion) { fn primitive_at<P: Primitive>(&mut self, primitive: P, region: UiRegion) {
self.primitives.push( let h = self
self.ctx .ctx
.primitives .primitives
.write(self.id.duplicate(), primitive, region), .write(self.id.duplicate(), primitive, region);
); self.primitives.push(h);
} }
/// Writes a primitive to be rendered /// Writes a primitive to be rendered
@@ -293,17 +303,12 @@ impl<'a, 'c> Painter<'a, 'c> {
self.region self.region
} }
pub fn size<W>(&mut self, id: &WidgetId<W>) -> Vec2 { pub fn size<W>(&mut self, id: &WidgetId<W>) -> UiVec2 {
self.sized_children.insert(id.id.duplicate()); self.size_ctx().size(id)
self.ctx
.widgets
.get_dyn_dynamic(&id.id)
.get_size(&mut self.size_ctx())
} }
pub fn size_ctx(&mut self) -> SizeCtx<'_> { pub fn size_ctx(&mut self) -> SizeCtx<'_> {
SizeCtx { SizeCtx {
size: self.region.in_size(self.ctx.screen_size),
text: self.ctx.text, text: self.ctx.text,
textures: self.ctx.textures, textures: self.ctx.textures,
widgets: self.ctx.widgets, widgets: self.ctx.widgets,
@@ -317,17 +322,21 @@ impl<'a, 'c> Painter<'a, 'c> {
} }
pub struct SizeCtx<'a> { pub struct SizeCtx<'a> {
pub size: Vec2,
pub text: &'a mut TextData, pub text: &'a mut TextData,
pub textures: &'a mut Textures, pub textures: &'a mut Textures,
widgets: &'a Widgets, widgets: &'a Widgets,
checked: &'a mut HashSet<Id>, checked: &'a mut HashMap<Id, UiVec2>,
} }
impl SizeCtx<'_> { impl SizeCtx<'_> {
pub fn size<W>(&mut self, id: &WidgetId<W>) -> Vec2 { pub fn size<W>(&mut self, id: &WidgetId<W>) -> UiVec2 {
self.checked.insert(id.id.duplicate()); // TODO: determine if this is useful
self.widgets.get_dyn_dynamic(&id.id).get_size(self) // if let Some(size) = self.checked.get(&id.id) {
// return Some(*size);
// }
let size = self.widgets.get_dyn_dynamic(&id.id).desired_size(self);
self.checked.insert(id.id.duplicate(), size);
size
} }
pub fn draw_text( pub fn draw_text(
&mut self, &mut self,

View File

@@ -5,12 +5,12 @@ use crate::{
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable, Default)] #[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable, Default)]
pub struct UiPos { pub struct UiVec2 {
pub anchor: Vec2, pub rel: Vec2,
pub offset: Vec2, pub abs: Vec2,
} }
impl UiPos { impl UiVec2 {
/// expands this position into a sized region centered at self /// expands this position into a sized region centered at self
pub fn expand(&self, size: impl Into<Vec2>) -> UiRegion { pub fn expand(&self, size: impl Into<Vec2>) -> UiRegion {
let size = size.into(); let size = size.into();
@@ -21,21 +21,29 @@ impl UiPos {
} }
pub const fn anchor(anchor: Vec2) -> Self { pub const fn anchor(anchor: Vec2) -> Self {
Self { Self::rel(anchor)
anchor,
offset: Vec2::ZERO,
}
} }
pub const fn offset(offset: Vec2) -> Self { pub const fn offset(offset: Vec2) -> Self {
Self::abs(offset)
}
pub const fn abs(abs: Vec2) -> Self {
Self { Self {
anchor: Vec2::ZERO, rel: Vec2::ZERO,
offset, abs,
}
}
pub const fn rel(rel: Vec2) -> Self {
Self {
rel,
abs: Vec2::ZERO,
} }
} }
pub const fn shift(&mut self, offset: impl const Into<Vec2>) { pub const fn shift(&mut self, offset: impl const Into<Vec2>) {
self.offset += offset.into(); self.abs += offset.into();
} }
pub const fn shifted(mut self, offset: Vec2) -> Self { pub const fn shifted(mut self, offset: Vec2) -> Self {
@@ -43,33 +51,44 @@ impl UiPos {
self self
} }
pub const fn within(&self, region: &UiRegion) -> UiPos { pub const fn within(&self, region: &UiRegion) -> UiVec2 {
let anchor = self let anchor = self.rel.lerp(region.top_left.rel, region.bot_right.rel);
.anchor let offset = self.abs + self.rel.lerp(region.top_left.abs, region.bot_right.abs);
.lerp(region.top_left.anchor, region.bot_right.anchor); UiVec2 {
let offset = self.offset rel: anchor,
+ self abs: offset,
.anchor }
.lerp(region.top_left.offset, region.bot_right.offset);
UiPos { anchor, offset }
} }
pub fn axis_mut(&mut self, axis: Axis) -> UIScalarView<'_> { pub fn axis_mut(&mut self, axis: Axis) -> UiScalarView<'_> {
match axis { match axis {
Axis::X => UIScalarView { Axis::X => UiScalarView {
anchor: &mut self.anchor.x, anchor: &mut self.rel.x,
offset: &mut self.offset.x, offset: &mut self.abs.x,
}, },
Axis::Y => UIScalarView { Axis::Y => UiScalarView {
anchor: &mut self.anchor.y, anchor: &mut self.rel.y,
offset: &mut self.offset.y, offset: &mut self.abs.y,
},
}
}
pub fn axis(&self, axis: Axis) -> UiScalar {
match axis {
Axis::X => UiScalar {
rel: self.rel.x,
abs: self.abs.x,
},
Axis::Y => UiScalar {
rel: self.rel.y,
abs: self.abs.y,
}, },
} }
} }
pub fn flip(&mut self) { pub fn flip(&mut self) {
self.anchor = 1.0 - self.anchor; self.rel = 1.0 - self.rel;
self.offset = -self.offset; self.abs = -self.abs;
} }
pub fn flipped(mut self) -> Self { pub fn flipped(mut self) -> Self {
@@ -78,64 +97,92 @@ impl UiPos {
} }
pub fn to_size(&self, size: Vec2) -> Vec2 { pub fn to_size(&self, size: Vec2) -> Vec2 {
self.anchor * size + self.offset self.rel * size + self.abs
}
pub const fn max_size() -> Self {
Self::rel(Vec2::ONE)
}
pub const fn from_axis(axis: Axis, aligned: UiScalar, ortho: UiScalar) -> Self {
Self {
rel: Vec2::from_axis(axis, aligned.rel, ortho.rel),
abs: Vec2::from_axis(axis, aligned.abs, ortho.abs),
}
} }
} }
impl const From<Align> for UiPos { impl const From<Align> for UiVec2 {
fn from(align: Align) -> Self { fn from(align: Align) -> Self {
Self::anchor(align.anchor()) Self::anchor(align.anchor())
} }
} }
impl Align { impl Align {
pub fn pos(self) -> UiPos { pub fn pos(self) -> UiVec2 {
UiPos::from(self) UiVec2::from(self)
} }
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, Default)]
pub struct UIScalar { pub struct UiScalar {
pub anchor: f32, pub rel: f32,
pub offset: f32, pub abs: f32,
} }
impl_op!(UIScalar Add add; anchor offset); impl_op!(UiScalar Add add; rel abs);
impl_op!(UIScalar Sub sub; anchor offset); impl_op!(UiScalar Sub sub; rel abs);
impl UIScalar { impl UiScalar {
pub fn new(anchor: f32, offset: f32) -> Self { pub fn new(rel: f32, abs: f32) -> Self {
Self { anchor, offset } Self { rel, abs }
} }
pub fn min() -> Self { pub fn rel_min() -> Self {
Self::new(0.0, 0.0) Self::new(0.0, 0.0)
} }
pub fn max() -> Self { pub fn rel_max() -> Self {
Self::new(1.0, 0.0) Self::new(1.0, 0.0)
} }
pub fn max(&self, other: Self) -> Self {
Self {
rel: self.rel.max(other.rel),
abs: self.abs.max(other.abs),
}
}
pub fn min(&self, other: Self) -> Self {
Self {
rel: self.rel.min(other.rel),
abs: self.abs.min(other.abs),
}
}
pub fn from_anchor(anchor: f32) -> Self { pub fn from_anchor(anchor: f32) -> Self {
Self::new(anchor, 0.0) Self::new(anchor, 0.0)
} }
pub fn offset(mut self, amt: f32) -> Self { pub fn offset(mut self, amt: f32) -> Self {
self.offset += amt; self.abs += amt;
self self
} }
pub fn within(&self, start: UIScalar, end: UIScalar) -> Self { pub fn within(&self, start: UiScalar, end: UiScalar) -> Self {
let anchor = self.anchor.lerp(start.anchor, end.anchor); let anchor = self.rel.lerp(start.rel, end.rel);
let offset = self.offset + self.anchor.lerp(start.offset, end.offset); let offset = self.abs + self.rel.lerp(start.abs, end.abs);
Self { anchor, offset } Self {
rel: anchor,
abs: offset,
}
} }
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
pub struct UiRegion { pub struct UiRegion {
pub top_left: UiPos, pub top_left: UiVec2,
pub bot_right: UiPos, pub bot_right: UiVec2,
} }
impl UiRegion { impl UiRegion {
@@ -147,8 +194,8 @@ impl UiRegion {
} }
pub fn anchor(anchor: Vec2) -> Self { pub fn anchor(anchor: Vec2) -> Self {
Self { Self {
top_left: UiPos::anchor(anchor), top_left: UiVec2::anchor(anchor),
bot_right: UiPos::anchor(anchor), bot_right: UiVec2::anchor(anchor),
} }
} }
pub fn within(&self, parent: &Self) -> Self { pub fn within(&self, parent: &Self) -> Self {
@@ -183,12 +230,12 @@ impl UiRegion {
pub fn to_screen(&self, size: Vec2) -> ScreenRegion { pub fn to_screen(&self, size: Vec2) -> ScreenRegion {
ScreenRegion { ScreenRegion {
top_left: self.top_left.anchor * size + self.top_left.offset, top_left: self.top_left.rel * size + self.top_left.abs,
bot_right: self.bot_right.anchor * size + self.bot_right.offset, bot_right: self.bot_right.rel * size + self.bot_right.abs,
} }
} }
pub fn center(&self) -> UiPos { pub fn center(&self) -> UiVec2 {
Align::Center.pos().within(self) Align::Center.pos().within(self)
} }
@@ -201,10 +248,23 @@ impl UiRegion {
} }
pub fn from_size_align(size: Vec2, align: Align) -> Self { pub fn from_size_align(size: Vec2, align: Align) -> Self {
let mut top_left = UiPos::from(align); let mut top_left = UiVec2::from(align);
top_left.offset -= size * align.anchor(); top_left.abs -= size * align.anchor();
let mut bot_right = UiPos::from(align); let mut bot_right = UiVec2::from(align);
bot_right.offset += size * (Vec2::ONE - align.anchor()); bot_right.abs += size * (Vec2::ONE - align.anchor());
Self {
top_left,
bot_right,
}
}
pub fn from_ui_size_align(size: UiVec2, align: Align) -> Self {
let mut top_left = UiVec2::from(align);
top_left.abs -= size.abs * align.anchor();
top_left.rel -= size.rel * align.anchor();
let mut bot_right = UiVec2::from(align);
bot_right.abs += size.abs * (Vec2::ONE - align.anchor());
bot_right.rel += size.rel * (Vec2::ONE - align.anchor());
Self { Self {
top_left, top_left,
bot_right, bot_right,
@@ -227,24 +287,24 @@ impl ScreenRegion {
} }
pub struct UIRegionAxisView<'a> { pub struct UIRegionAxisView<'a> {
pub top_left: UIScalarView<'a>, pub top_left: UiScalarView<'a>,
pub bot_right: UIScalarView<'a>, pub bot_right: UiScalarView<'a>,
} }
pub struct UIScalarView<'a> { pub struct UiScalarView<'a> {
pub anchor: &'a mut f32, pub anchor: &'a mut f32,
pub offset: &'a mut f32, pub offset: &'a mut f32,
} }
impl UIScalarView<'_> { impl UiScalarView<'_> {
pub fn set(&mut self, scalar: UIScalar) { pub fn set(&mut self, scalar: UiScalar) {
*self.anchor = scalar.anchor; *self.anchor = scalar.rel;
*self.offset = scalar.offset; *self.offset = scalar.abs;
} }
pub fn get(self) -> UIScalar { pub fn get(self) -> UiScalar {
UIScalar { UiScalar {
anchor: *self.anchor, rel: *self.anchor,
offset: *self.offset, abs: *self.offset,
} }
} }
} }

View File

@@ -101,6 +101,9 @@ pub trait UiCtx {
Self: Sized; Self: Sized;
} }
/// TODO: this takes ui.sensor_map and then puts it back
/// it extends so you can add to it, but you can't actually index sensors with it
/// should something be done about this?
pub fn run_sensors<Ctx: UiCtx>(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) { pub fn run_sensors<Ctx: UiCtx>(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) {
let active = std::mem::take(&mut ctx.ui().active.sensors); let active = std::mem::take(&mut ctx.ui().active.sensors);
let mut map = std::mem::take(&mut ctx.ui().sensor_map); let mut map = std::mem::take(&mut ctx.ui().sensor_map);
@@ -128,8 +131,11 @@ pub fn run_sensors<Ctx: UiCtx>(ctx: &mut Ctx, cursor: &CursorState, window_size:
break; break;
} }
} }
ctx.ui().sensor_map = map; let ui = ctx.ui();
ctx.ui().active.sensors = active; std::mem::swap(&mut ui.sensor_map, &mut map);
ui.sensor_map.extend(map);
assert!(ui.active.sensors.is_empty());
ui.active.sensors = active;
} }
pub fn should_run(senses: &Senses, cursor: &CursorButtons, hover: ActivationState) -> bool { pub fn should_run(senses: &Senses, cursor: &CursorButtons, hover: ActivationState) -> bool {

View File

@@ -66,6 +66,10 @@ impl<Ctx> Ui<Ctx> {
self.widgets.data_mut(&id.id).unwrap().label = label; self.widgets.data_mut(&id.id).unwrap().label = label;
} }
pub fn label<W>(&self, id: &WidgetId<W>) -> &String {
&self.widgets.data(&id.id).unwrap().label
}
pub fn add_widget<W: Widget>(&mut self, w: W) -> WidgetId<W> { pub fn add_widget<W: Widget>(&mut self, w: W) -> WidgetId<W> {
self.push(w) self.push(w)
} }
@@ -281,6 +285,10 @@ impl Widgets {
self.map.get(id) self.map.get(id)
} }
pub fn label(&self, id: &Id) -> &String {
&self.data(id).unwrap().label
}
pub fn data_mut(&mut self, id: &Id) -> Option<&mut WidgetData> { pub fn data_mut(&mut self, id: &Id) -> Option<&mut WidgetData> {
self.map.get_mut(id) self.map.get_mut(id)
} }

View File

@@ -1,11 +1,11 @@
use crate::layout::{Painter, SizeCtx, StaticWidgetId, Ui, Vec2, WidgetId, WidgetIdFn}; use crate::layout::{Painter, SizeCtx, StaticWidgetId, Ui, UiVec2, WidgetId, WidgetIdFn};
use std::{any::Any, marker::PhantomData}; use std::{any::Any, marker::PhantomData};
pub trait Widget: Any { pub trait Widget: Any {
fn draw(&mut self, painter: &mut Painter); fn draw(&mut self, painter: &mut Painter);
fn get_size(&mut self, ctx: &mut SizeCtx) -> Vec2 { fn desired_size(&mut self, _: &mut SizeCtx) -> UiVec2 {
ctx.size UiVec2::max_size()
} }
} }

View File

@@ -128,7 +128,7 @@ impl Primitives {
self.run.len() != 0 self.run.len() != 0
} }
pub fn skip(&mut self, range: &Range<usize>) { pub fn skip(&mut self, range: &mut Range<usize>) {
if self.running() { if self.running() {
self.run.data.extend(&self.instances.data[range.clone()]); self.run.data.extend(&self.instances.data[range.clone()]);
self.run.assoc.extend( self.run.assoc.extend(
@@ -137,7 +137,9 @@ impl Primitives {
.map(|i| i.duplicate()), .map(|i| i.duplicate()),
); );
} }
range.start = self.pos;
self.pos += range.len(); self.pos += range.len();
range.end = self.pos;
} }
pub(crate) fn clear(&mut self) { pub(crate) fn clear(&mut self) {
@@ -184,6 +186,7 @@ impl Primitives {
} }
} }
#[derive(Debug)]
pub struct PrimitiveHandle { pub struct PrimitiveHandle {
idx: usize, idx: usize,
binding: u32, binding: u32,

View File

@@ -21,7 +21,7 @@ pub struct Client {
input: Input, input: Input,
ui: Ui<Client>, ui: Ui<Client>,
info: WidgetId<Text>, info: WidgetId<Text>,
selected: Option<WidgetId<TextEdit>>, focus: Option<WidgetId<TextEdit>>,
} }
impl Client { impl Client {
@@ -38,7 +38,7 @@ impl Client {
rrect.color(Color::ORANGE), rrect.color(Color::ORANGE),
rrect.color(Color::LIME).pad(10.0), rrect.color(Color::LIME).pad(10.0),
) )
.span(Dir::RIGHT, [1, 1]), .span(Dir::RIGHT, ratio(1)),
rrect.color(Color::YELLOW), rrect.color(Color::YELLOW),
) )
.span(Dir::RIGHT, [2, 2, 1]) .span(Dir::RIGHT, [2, 2, 1])
@@ -110,6 +110,7 @@ impl Client {
btext(":gamer mode").family(Family::Monospace), btext(":gamer mode").family(Family::Monospace),
rect(Color::CYAN).size(10).center(), rect(Color::CYAN).size(10).center(),
rect(Color::RED).size(100).center(), rect(Color::RED).size(100).center(),
rect(Color::PURPLE).size(50).align(Align::Top),
) )
.span(Dir::RIGHT, sized()) .span(Dir::RIGHT, sized())
.center(), .center(),
@@ -119,14 +120,34 @@ impl Client {
.add_static(&mut ui); .add_static(&mut ui);
let texts = Span::empty(Dir::DOWN).add(&mut ui); let texts = Span::empty(Dir::DOWN).add(&mut ui);
let text_edit_scroll = ( let add_text = text_edit("add")
texts, .text_align(Align::Left)
text_edit("add")
.font_size(30) .font_size(30)
.id_on(Sense::click(), |id, client: &mut Client, ctx| { .id_on(Sense::click(), |id, client: &mut Client, ctx| {
client.ui.text(id).select(ctx.cursor, ctx.size); client.ui.text(id).select(ctx.cursor, ctx.size);
client.selected = Some(id.clone()); client.focus = Some(id.clone());
}) })
.add(&mut ui);
let text_edit_scroll = (
(Rect::new(Color::SKY), texts.clone()).stack(),
(
add_text.clone(),
Rect::new(Color::GREEN)
.on(Sense::click(), move |client: &mut Client, _| {
let content = client.ui.text(&add_text).take();
let text = text_edit(content)
.font_size(30)
.id_on(Sense::click(), |id, client: &mut Client, ctx| {
client.ui.text(id).select(ctx.cursor, ctx.size);
client.focus = Some(id.clone());
})
.pad(10)
.add(&mut client.ui);
client.ui[&texts].children.push((text.any(), sized()));
})
.size(40),
)
.span(Dir::RIGHT, [ratio(1), sized()])
.pad(30), .pad(30),
) )
.span(Dir::DOWN, [ratio(1), sized()]) .span(Dir::DOWN, [ratio(1), sized()])
@@ -161,24 +182,15 @@ impl Client {
.span(Dir::RIGHT, ratio(1)); .span(Dir::RIGHT, ratio(1));
let info = text("").add(&mut ui); let info = text("").add(&mut ui);
let info_sect = info.clone().pad(10).align(Align::BotLeft); // let info_sect = info.clone().pad(10).align(Align::BotLeft);
ui.set_root( ui.set_root((tabs, main).span(Dir::DOWN, [fixed(40), ratio(1)]));
(
tabs.label("tabs"),
(main, info_sect.label("info sect"))
.stack()
.label("main stack"),
)
.span(Dir::DOWN, [fixed(40), ratio(1)])
.label("root"),
);
Self { Self {
renderer, renderer,
input: Input::default(), input: Input::default(),
ui, ui,
info, info,
selected: None, focus: None,
} }
} }
@@ -186,6 +198,12 @@ impl Client {
self.input.event(&event); self.input.event(&event);
let cursor_state = self.cursor_state().clone(); let cursor_state = self.cursor_state().clone();
let window_size = self.window_size(); let window_size = self.window_size();
if let Some(focus) = &self.focus
&& cursor_state.buttons.left.is_start()
{
self.ui.text(focus).deselect();
self.focus = None;
}
run_sensors(self, &cursor_state, window_size); run_sensors(self, &cursor_state, window_size);
match event { match event {
WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::CloseRequested => event_loop.exit(),
@@ -199,24 +217,24 @@ impl Client {
self.renderer.resize(&size) self.renderer.resize(&size)
} }
WindowEvent::KeyboardInput { event, .. } => { WindowEvent::KeyboardInput { event, .. } => {
if let Some(sel) = &self.selected if let Some(sel) = &self.focus
&& event.state.is_pressed() && event.state.is_pressed()
&& self.ui.text(sel).apply_event(&event).unfocus() && self.ui.text(sel).apply_event(&event).unfocus()
{ {
self.selected = None; self.focus = None;
} }
} }
_ => (), _ => (),
} }
let new = format!( // let new = format!(
"widgets: {}\nactive:{}\nviews: {}", // "widgets: {}\nactive:{}\nviews: {}",
self.ui.num_widgets(), // self.ui.num_widgets(),
self.ui.active_widgets(), // self.ui.active_widgets(),
self.renderer.ui.view_count() // self.renderer.ui.view_count()
); // );
if new != self.ui[&self.info].content { // if new != self.ui[&self.info].content {
self.ui[&self.info].content = new; // self.ui[&self.info].content = new;
} // }
if self.ui.needs_redraw() { if self.ui.needs_redraw() {
self.renderer.window().request_redraw(); self.renderer.window().request_redraw();
} }

View File

@@ -54,3 +54,13 @@ impl CopyId {
Id(self.0) Id(self.0)
} }
} }
pub trait IdUtil {
fn duplicate(&self) -> Self;
}
impl IdUtil for Option<Id> {
fn duplicate(&self) -> Self {
self.as_ref().map(|i| i.duplicate())
}
}