stack & padding fix sorta, preparing for scroll areas

This commit is contained in:
2025-09-25 19:59:18 -04:00
parent 273a92d1f7
commit 6d829dbe81
10 changed files with 151 additions and 45 deletions

0
src/core/empty.rs Normal file
View File

View File

@@ -12,8 +12,20 @@ impl Widget for Padded {
fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 { fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 {
let mut size = ctx.size(&self.inner); let mut size = ctx.size(&self.inner);
// TODO: this is currently a hack
// the correct solution is that the position is not linear
// becauase if you have 0.5 rel + 100 abs, it's linear
// until you fill up parent rel (in abs units) and then 1.0 rel (piecewise linear)
// so I guess yet another detection system is needed
// to determine when abs translated to rel + rel > 1.0
// hopefully find a way to get working on gpu so no cpu changing needed
// eg by sending size to calculate above and then doing... something?
if size.rel.x != 1.0 {
size.abs.x += self.padding.left + self.padding.right; size.abs.x += self.padding.left + self.padding.right;
}
if size.rel.y != 1.0 {
size.abs.y += self.padding.top + self.padding.bottom; size.abs.y += self.padding.top + self.padding.bottom;
}
size size
} }
} }

View File

@@ -47,7 +47,7 @@ impl Widget for Span {
} else { } else {
UiScalar::new(total.rel, total.abs) UiScalar::new(total.rel, total.abs)
}; };
let mut max_ortho = UiScalar::default(); let mut max_ortho = UiScalar::ZERO;
for (child, _) in &self.children { for (child, _) in &self.children {
let size = ctx.size(child); let size = ctx.size(child);
max_ortho = max_ortho.max(size.axis(!self.dir.axis)); max_ortho = max_ortho.max(size.axis(!self.dir.axis));

View File

@@ -1,19 +1,82 @@
use std::marker::PhantomData;
use crate::prelude::*; use crate::prelude::*;
pub struct Stack { pub struct Stack {
pub children: Vec<WidgetId>, pub children: Vec<WidgetId>,
pub size: StackSize,
pub offset: usize,
} }
impl Widget for Stack { impl Widget for Stack {
fn draw(&mut self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
let base = painter.layer; for _ in 0..self.offset {
for child in &self.children {
if painter.layer == base {
painter.layer = painter.child_layer();
} else {
painter.layer = painter.next_layer(); painter.layer = painter.next_layer();
} }
let base = painter.layer;
for child in &self.children {
painter.layer = if painter.layer == base {
painter.child_layer()
} else {
painter.next_layer()
};
painter.widget(child); painter.widget(child);
} }
} }
fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 {
match self.size {
StackSize::Full => UiVec2::MAX_SIZE,
StackSize::Child(i) => ctx.size(&self.children[i]),
}
}
}
#[derive(Default, Debug)]
pub enum StackSize {
#[default]
Full,
Child(usize),
}
pub struct StackBuilder<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> {
pub children: Wa,
pub size: StackSize,
pub offset: usize,
_pd: PhantomData<Tag>,
}
impl<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> FnOnce<(&mut Ui,)>
for StackBuilder<LEN, Wa, Tag>
{
type Output = Stack;
extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output {
Stack {
children: self.children.ui(args.0).arr.to_vec(),
offset: self.offset,
size: self.size,
}
}
}
impl<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> StackBuilder<LEN, Wa, Tag> {
pub fn new(children: Wa) -> Self {
Self {
children,
size: StackSize::default(),
offset: 0,
_pd: PhantomData,
}
}
pub fn stack_size(mut self, size: StackSize) -> Self {
self.size = size;
self
}
pub fn offset_layer(mut self, offset: usize) -> Self {
self.offset = offset;
self
}
} }

View File

@@ -44,12 +44,12 @@ impl<W: WidgetLike<Tag>, Tag> CoreWidget<W::Widget, Tag> for W {
} }
} }
pub trait CoreWidgetArr<const LEN: usize, Tag> { pub trait CoreWidgetArr<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> {
fn span(self, dir: Dir, lengths: impl IntoSpanLens<LEN>) -> impl WidgetFn<Span>; fn span(self, dir: Dir, lengths: impl IntoSpanLens<LEN>) -> impl WidgetFn<Span>;
fn stack(self) -> impl WidgetFn<Stack>; fn stack(self) -> StackBuilder<LEN, Wa, Tag>;
} }
impl<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> CoreWidgetArr<LEN, Tag> for Wa { impl<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> CoreWidgetArr<LEN, Wa, Tag> for Wa {
fn span(self, dir: Dir, lengths: impl IntoSpanLens<LEN>) -> impl WidgetFn<Span> { fn span(self, dir: Dir, lengths: impl IntoSpanLens<LEN>) -> impl WidgetFn<Span> {
let lengths = lengths.into_lens(); let lengths = lengths.into_lens();
move |ui| Span { move |ui| Span {
@@ -57,10 +57,8 @@ impl<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> CoreWidgetArr<LEN, Tag>
dir, dir,
} }
} }
fn stack(self) -> impl WidgetFn<Stack> { fn stack(self) -> StackBuilder<LEN, Wa, Tag> {
move |ui| Stack { StackBuilder::new(self)
children: self.ui(ui).arr.to_vec(),
}
} }
} }

View File

@@ -27,6 +27,8 @@ enum Ptr {
/// is that realistically desired? /// is that realistically desired?
pub struct Layers { pub struct Layers {
vec: Vec<LayerNode>, vec: Vec<LayerNode>,
/// index of last layer at top level (start at first = 0)
last: usize,
} }
/// TODO: this can be replaced with Primitives itself atm /// TODO: this can be replaced with Primitives itself atm
@@ -45,6 +47,7 @@ impl Layers {
pub fn new() -> Layers { pub fn new() -> Layers {
Self { Self {
vec: vec![LayerNode::head()], vec: vec![LayerNode::head()],
last: 0,
} }
} }
@@ -73,7 +76,7 @@ impl Layers {
match self.vec[i_new].next { match self.vec[i_new].next {
Ptr::Next(i) => self.vec[i].prev = Ptr::Next(i_new), Ptr::Next(i) => self.vec[i].prev = Ptr::Next(i_new),
Ptr::Parent(i) => self.vec[i].child.as_mut().unwrap().tail = i_new, Ptr::Parent(i) => self.vec[i].child.as_mut().unwrap().tail = i_new,
Ptr::None => (), Ptr::None => self.last = i_new,
} }
i_new i_new
} }
@@ -95,11 +98,15 @@ impl Layers {
} }
pub fn iter_mut(&mut self) -> LayerIteratorMut<'_> { pub fn iter_mut(&mut self) -> LayerIteratorMut<'_> {
LayerIteratorMut::new(&mut self.vec) LayerIteratorMut::new(&mut self.vec, self.last)
}
pub fn iter(&self) -> impl Iterator<Item = (usize, &Layer)> {
self.indices().map(|i| (i, &self.vec[i].data))
} }
pub fn indices(&self) -> LayerIndexIterator<'_> { pub fn indices(&self) -> LayerIndexIterator<'_> {
LayerIndexIterator::new(&self.vec) LayerIndexIterator::new(&self.vec, self.last)
} }
pub fn write<P: Primitive>( pub fn write<P: Primitive>(
@@ -179,9 +186,9 @@ impl<'a> DoubleEndedIterator for LayerIteratorMut<'a> {
} }
impl<'a> LayerIteratorMut<'a> { impl<'a> LayerIteratorMut<'a> {
fn new(vec: &'a mut Vec<LayerNode>) -> Self { fn new(vec: &'a mut Vec<LayerNode>, last: usize) -> Self {
Self { Self {
inner: LayerIndexIterator::new(vec), inner: LayerIndexIterator::new(vec, last),
} }
} }
} }
@@ -203,7 +210,10 @@ impl<'a> Iterator for LayerIndexIterator<'a> {
} else if let Ptr::Next(i) = node.next { } else if let Ptr::Next(i) = node.next {
Some(i) Some(i)
} else if let Ptr::Parent(i) = node.next { } else if let Ptr::Parent(i) = node.next {
let node = &self.vec[i]; let mut node = &self.vec[i];
while let Ptr::Parent(i) = node.next {
node = &self.vec[i];
}
if let Ptr::Next(i) = node.next { if let Ptr::Next(i) = node.next {
Some(i) Some(i)
} else { } else {
@@ -243,8 +253,8 @@ impl<'a> DoubleEndedIterator for LayerIndexIterator<'a> {
} }
impl<'a> LayerIndexIterator<'a> { impl<'a> LayerIndexIterator<'a> {
fn new(vec: &'a Vec<LayerNode>) -> Self { fn new(vec: &'a Vec<LayerNode>, last: usize) -> Self {
let mut last = 0; let mut last = last;
while let Some(c) = vec[last].child { while let Some(c) = vec[last].child {
last = c.tail; last = c.tail;
} }

View File

@@ -69,11 +69,21 @@ impl<'a> PainterCtx<'a> {
}; };
if let Some((rid, size)) = &active.resize { if let Some((rid, size)) = &active.resize {
let checked = &mut HashMap::default();
let mut ctx = SizeCtx {
checked,
text: self.text,
textures: self.textures,
widgets: self.widgets,
};
let desired = ctx.size_inner(id);
if *size != desired {
self.redraw(&rid.duplicate()); self.redraw(&rid.duplicate());
if self.drawing.contains(id) { if self.drawing.contains(id) {
return; return;
} }
} }
}
let Some(active) = self.remove(id) else { let Some(active) = self.remove(id) else {
return; return;
@@ -298,14 +308,17 @@ pub struct SizeCtx<'a> {
} }
impl SizeCtx<'_> { impl SizeCtx<'_> {
fn size_inner(&mut self, id: &Id) -> UiVec2 {
let size = self.widgets.get_dyn_dynamic(id).desired_size(self);
self.checked.insert(id.duplicate(), size);
size
}
pub fn size<W>(&mut self, id: &WidgetId<W>) -> UiVec2 { pub fn size<W>(&mut self, id: &WidgetId<W>) -> UiVec2 {
// TODO: determine if this is useful // TODO: determine if this is useful
// if let Some(size) = self.checked.get(&id.id) { // if let Some(size) = self.checked.get(&id.id) {
// return Some(*size); // return Some(*size);
// } // }
let size = self.widgets.get_dyn_dynamic(&id.id).desired_size(self); self.size_inner(&id.id)
self.checked.insert(id.id.duplicate(), size);
size
} }
pub fn draw_text( pub fn draw_text(
&mut self, &mut self,

View File

@@ -100,9 +100,7 @@ impl UiVec2 {
self.rel * size + self.abs self.rel * size + self.abs
} }
pub const fn max_size() -> Self { pub const MAX_SIZE: Self = Self::rel(Vec2::ONE);
Self::rel(Vec2::ONE)
}
pub const fn from_axis(axis: Axis, aligned: UiScalar, ortho: UiScalar) -> Self { pub const fn from_axis(axis: Axis, aligned: UiScalar, ortho: UiScalar) -> Self {
Self { Self {
@@ -134,6 +132,8 @@ impl_op!(UiScalar Add add; rel abs);
impl_op!(UiScalar Sub sub; rel abs); impl_op!(UiScalar Sub sub; rel abs);
impl UiScalar { impl UiScalar {
pub const ZERO: Self = Self { rel: 0.0, abs: 0.0 };
pub fn new(rel: f32, abs: f32) -> Self { pub fn new(rel: f32, abs: f32) -> Self {
Self { rel, abs } Self { rel, abs }
} }

View File

@@ -5,7 +5,7 @@ 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 desired_size(&mut self, _: &mut SizeCtx) -> UiVec2 { fn desired_size(&mut self, _: &mut SizeCtx) -> UiVec2 {
UiVec2::max_size() UiVec2::MAX_SIZE
} }
} }
@@ -76,7 +76,7 @@ impl<const LEN: usize, Ws> WidgetArr<LEN, Ws> {
} }
pub struct ArrTag; pub struct ArrTag;
pub trait WidgetArrLike<const LEN: usize, Tags> { pub trait WidgetArrLike<const LEN: usize, Tag> {
type Ws; type Ws;
fn ui(self, ui: &mut Ui) -> WidgetArr<LEN, Self::Ws>; fn ui(self, ui: &mut Ui) -> WidgetArr<LEN, Self::Ws>;
} }

View File

@@ -138,17 +138,22 @@ impl Client {
let content = client.ui.text(id).take(); let content = client.ui.text(id).take();
let text = text_edit(content) let text = text_edit(content)
.font_size(30) .font_size(30)
.text_align(Align::Left)
.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.focus = Some(id.clone()); client.focus = Some(id.clone());
}) });
.pad(10) let msg_box = (rect(Color::WHITE.darker(0.5)), text)
.stack()
.stack_size(StackSize::Child(1))
.add(&mut client.ui); .add(&mut client.ui);
client.ui[texts].children.push((text.any(), sized())); client.ui[texts].children.push((msg_box.any(), sized()));
}) })
.add(&mut ui); .add(&mut ui);
let text_edit_scroll = ( let text_edit_scroll = (
(Rect::new(Color::SKY), texts).stack(), (Rect::new(Color::SKY), texts).stack(),
(
Rect::new(Color::WHITE.darker(0.9)),
( (
add_text.clone(), add_text.clone(),
Rect::new(Color::GREEN) Rect::new(Color::GREEN)
@@ -158,7 +163,12 @@ impl Client {
.size(40), .size(40),
) )
.span(Dir::RIGHT, [ratio(1), sized()]) .span(Dir::RIGHT, [ratio(1), sized()])
.pad(30), .pad(10),
)
.stack()
.stack_size(StackSize::Child(1))
.offset_layer(1)
.align(Align::Bot),
) )
.span(Dir::DOWN, [ratio(1), sized()]) .span(Dir::DOWN, [ratio(1), sized()])
.add_static(&mut ui); .add_static(&mut ui);
@@ -192,7 +202,7 @@ 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::Right);
( (
(tabs, main).span(Dir::DOWN, [fixed(40), ratio(1)]), (tabs, main).span(Dir::DOWN, [fixed(40), ratio(1)]),