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 {
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);
}
fn get_size(&mut self, ctx: &mut SizeCtx) -> Vec2 {
fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 {
ctx.size(&self.inner)
}
}

View File

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

View File

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

View File

@@ -10,10 +10,10 @@ impl Widget for Padded {
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);
size.x += self.padding.left + self.padding.right;
size.y += self.padding.top + self.padding.bottom;
size.abs.x += self.padding.left + self.padding.right;
size.abs.y += self.padding.top + self.padding.bottom;
size
}
}
@@ -36,10 +36,10 @@ impl Padding {
}
pub fn region(&self) -> UiRegion {
let mut region = UiRegion::full();
region.top_left.offset.x += self.left;
region.top_left.offset.y += self.top;
region.bot_right.offset.x -= self.right;
region.bot_right.offset.y -= self.bottom;
region.top_left.abs.x += self.left;
region.top_left.abs.y += self.top;
region.bot_right.abs.x -= self.right;
region.bot_right.abs.y -= self.bottom;
region
}
}

View File

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

View File

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

View File

@@ -66,15 +66,15 @@ impl Widget for Text {
let dims = handle.size();
self.size = offset.size(&handle);
let mut region = self.region();
region.top_left.offset += offset.top_left;
region.bot_right.offset = region.top_left.offset + dims;
region.top_left.abs += offset.top_left;
region.bot_right.abs = region.top_left.abs + dims;
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);
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 {
fn draw(&mut self, painter: &mut Painter) {
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.buf.shape_until_scroll(font_system, false);
let (handle, tex_offset) = painter.render_text(&mut self.buf, &self.attrs);
let dims = handle.size();
self.size = tex_offset.size(&handle);
let region = self.region();
let mut tex_region = region;
tex_region.top_left.offset += tex_offset.top_left;
tex_region.bot_right.offset = tex_region.top_left.offset + dims;
tex_region.top_left.abs += tex_offset.top_left;
tex_region.bot_right.abs = tex_region.top_left.abs + dims;
painter.texture_within(&handle, tex_region);
if let Some(cursor) = &self.cursor
&& let Some(pos) = cursor_pos(cursor, &self.buf)
{
@@ -45,12 +46,15 @@ impl Widget for TextEdit {
.shifted(offset)
.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);
offset.size(&handle)
UiVec2::abs(offset.size(&handle))
}
}
@@ -105,6 +109,10 @@ impl TextEditBuilder {
self.attrs.line_height = height;
self
}
pub fn text_align(mut self, align: Align) -> Self {
self.align = align;
self
}
}
pub struct TextEditCtx<'a> {
@@ -113,6 +121,21 @@ pub struct 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) {
if let Some(cursor) = self.text.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 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 {
Self { r, g, b, a }
}

View File

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

View File

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

View File

@@ -101,6 +101,9 @@ pub trait UiCtx {
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) {
let active = std::mem::take(&mut ctx.ui().active.sensors);
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;
}
}
ctx.ui().sensor_map = map;
ctx.ui().active.sensors = active;
let ui = ctx.ui();
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 {

View File

@@ -66,6 +66,10 @@ impl<Ctx> Ui<Ctx> {
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> {
self.push(w)
}
@@ -281,6 +285,10 @@ impl Widgets {
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> {
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};
pub trait Widget: Any {
fn draw(&mut self, painter: &mut Painter);
fn get_size(&mut self, ctx: &mut SizeCtx) -> Vec2 {
ctx.size
fn desired_size(&mut self, _: &mut SizeCtx) -> UiVec2 {
UiVec2::max_size()
}
}

View File

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

View File

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

View File

@@ -54,3 +54,13 @@ impl CopyId {
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())
}
}