sizing actually working correctly now
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(®ion),
|
||||
);
|
||||
} 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, _)) =
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user