move widgets on draw if region size is same

This commit is contained in:
2025-09-27 16:11:30 -04:00
parent 5f2dffc189
commit 95f049acb4
13 changed files with 204 additions and 176 deletions

View File

@@ -113,7 +113,7 @@ impl<Ctx: 'static> UiModule for SensorModule<Ctx> {
self.active self.active
.entry(inst.layer) .entry(inst.layer)
.or_default() .or_default()
.insert(inst.id.duplicate(), inst.region); .insert(inst.id, inst.region);
} }
} }
@@ -126,17 +126,21 @@ impl<Ctx: 'static> UiModule for SensorModule<Ctx> {
fn on_remove(&mut self, id: &Id) { fn on_remove(&mut self, id: &Id) {
self.map.remove(id); self.map.remove(id);
} }
fn on_move(&mut self, inst: &WidgetInstance) {
if let Some(map) = self.active.get_mut(&inst.layer)
&& let Some(region) = map.get_mut(&inst.id)
{
*region = inst.region;
}
}
} }
impl<Ctx> SensorModule<Ctx> { impl<Ctx> SensorModule<Ctx> {
pub fn merge(&mut self, other: Self) { pub fn merge(&mut self, other: Self) {
for (id, group) in other.map { for (id, group) in other.map {
for sensor in group.sensors { for sensor in group.sensors {
self.map self.map.entry(id).or_default().sensors.push(sensor);
.entry(id.duplicate())
.or_default()
.sensors
.push(sensor);
} }
} }
} }

View File

@@ -57,7 +57,7 @@ impl<W: WidgetLike<Tag>, Tag> Eventable<W::Widget, Tag> for W {
let id = self.add(ui); let id = self.add(ui);
ui.modules ui.modules
.get_mut::<E::Module<Ctx>>() .get_mut::<E::Module<Ctx>>()
.register(id.id.duplicate(), event, f); .register(id.id, event, f);
id id
} }
} }

View File

@@ -10,7 +10,7 @@ use std::{
use crate::{ use crate::{
layout::{FnTag, Ui, Widget, WidgetLike, WidgetTag}, layout::{FnTag, Ui, Widget, WidgetLike, WidgetTag},
util::{CopyId, Id, RefCounter}, util::{Id, RefCounter},
}; };
pub struct AnyWidget; pub struct AnyWidget;
@@ -44,7 +44,7 @@ pub struct WidgetId<W = AnyWidget> {
#[repr(C)] #[repr(C)]
pub struct StaticWidgetId<W = AnyWidget> { pub struct StaticWidgetId<W = AnyWidget> {
pub(super) ty: TypeId, pub(super) ty: TypeId,
pub(super) id: CopyId, pub(super) id: Id,
_pd: PhantomData<W>, _pd: PhantomData<W>,
} }
@@ -57,7 +57,7 @@ impl<W> std::fmt::Debug for WidgetId<W> {
impl<W> Clone for WidgetId<W> { impl<W> Clone for WidgetId<W> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
id: self.id.duplicate(), id: self.id,
ty: self.ty, ty: self.ty,
counter: self.counter.clone(), counter: self.counter.clone(),
send: self.send.clone(), send: self.send.clone(),
@@ -89,7 +89,7 @@ impl<W> WidgetId<W> {
} }
pub fn key(&self) -> Id { pub fn key(&self) -> Id {
self.id.duplicate() self.id
} }
pub(super) fn cast_type<W2>(self) -> WidgetId<W2> { pub(super) fn cast_type<W2>(self) -> WidgetId<W2> {
@@ -105,7 +105,7 @@ impl<W> WidgetId<W> {
self.is_static.store(true, Ordering::Release); self.is_static.store(true, Ordering::Release);
StaticWidgetId { StaticWidgetId {
ty: self.ty, ty: self.ty,
id: self.id.copyable(), id: self.id,
_pd: PhantomData, _pd: PhantomData,
} }
} }
@@ -116,7 +116,7 @@ impl WidgetId {
let send = self.send.clone(); let send = self.send.clone();
drop(std::mem::replace( drop(std::mem::replace(
self, self,
Self::new(other.id.id(), self.ty, send, true), Self::new(other.id, self.ty, send, true),
)); ));
} }
} }
@@ -124,7 +124,7 @@ impl WidgetId {
impl<W> Drop for WidgetId<W> { impl<W> Drop for WidgetId<W> {
fn drop(&mut self) { fn drop(&mut self) {
if self.counter.drop() && !self.is_static.load(Ordering::Acquire) { if self.counter.drop() && !self.is_static.load(Ordering::Acquire) {
let _ = self.send.send(self.id.duplicate()); let _ = self.send.send(self.id);
} }
} }
} }
@@ -194,7 +194,7 @@ impl<W: 'static, F: FnOnce(&mut Ui) -> WidgetId<W>> WidgetLike<IdFnTag> for F {
impl<W> StaticWidgetId<W> { impl<W> StaticWidgetId<W> {
pub fn to_id(&self, send: &Sender<Id>) -> WidgetId<W> { pub fn to_id(&self, send: &Sender<Id>) -> WidgetId<W> {
WidgetId::new(self.id.id(), self.ty, send.clone(), true) WidgetId::new(self.id, self.ty, send.clone(), true)
} }
pub fn any(self) -> StaticWidgetId<AnyWidget> { pub fn any(self) -> StaticWidgetId<AnyWidget> {
// SAFETY: self is repr(C) // SAFETY: self is repr(C)

View File

@@ -10,6 +10,7 @@ pub trait UiModule: Any {
fn on_draw(&mut self, inst: &WidgetInstance) {} fn on_draw(&mut self, inst: &WidgetInstance) {}
fn on_undraw(&mut self, inst: &WidgetInstance) {} fn on_undraw(&mut self, inst: &WidgetInstance) {}
fn on_remove(&mut self, id: &Id) {} fn on_remove(&mut self, id: &Id) {}
fn on_move(&mut self, inst: &WidgetInstance) {}
} }
#[derive(Default)] #[derive(Default)]

View File

@@ -4,7 +4,7 @@ use crate::{
Textures, UiRegion, UiVec2, Vec2, WidgetId, Widgets, Textures, UiRegion, UiVec2, Vec2, WidgetId, Widgets,
}, },
render::{Primitive, PrimitiveHandle}, render::{Primitive, PrimitiveHandle},
util::{HashMap, HashSet, Id, IdUtil}, util::{HashMap, HashSet, Id},
}; };
pub struct Painter<'a, 'c> { pub struct Painter<'a, 'c> {
@@ -62,13 +62,13 @@ impl<'a> PainterCtx<'a> {
} }
} }
pub fn redraw(&mut self, id: &Id) { pub fn redraw(&mut self, id: Id) {
self.drawing.clear(); self.drawing.clear();
let Some(active) = self.active.get(id) else { let Some(active) = self.active.get(&id) else {
return; return;
}; };
if let Some((rid, size)) = &active.resize { if let Some((rid, size)) = active.resize {
let checked = &mut HashMap::default(); let checked = &mut HashMap::default();
let mut ctx = SizeCtx { let mut ctx = SizeCtx {
checked, checked,
@@ -77,9 +77,9 @@ impl<'a> PainterCtx<'a> {
widgets: self.widgets, widgets: self.widgets,
}; };
let desired = ctx.size_inner(id); let desired = ctx.size_inner(id);
if *size != desired { if size != desired {
self.redraw(&rid.duplicate()); self.redraw(rid);
if self.drawing.contains(id) { if self.drawing.contains(&id) {
return; return;
} }
} }
@@ -93,13 +93,13 @@ impl<'a> PainterCtx<'a> {
active.layer, active.layer,
id, id,
active.region, active.region,
active.parent.duplicate(), active.parent,
Some(active.children), Some(active.children),
); );
self.active.get_mut(id).unwrap().resize = active.resize; self.active.get_mut(&id).unwrap().resize = active.resize;
} }
pub fn draw(&mut self, id: &Id) { pub fn draw(&mut self, id: Id) {
self.drawing.clear(); self.drawing.clear();
self.layers.clear(); self.layers.clear();
self.draw_inner(0, id, UiRegion::full(), None, None); self.draw_inner(0, id, UiRegion::full(), None, None);
@@ -108,7 +108,7 @@ impl<'a> PainterCtx<'a> {
fn draw_inner( fn draw_inner(
&mut self, &mut self,
layer: usize, layer: usize,
id: &Id, id: Id,
region: UiRegion, region: UiRegion,
parent: Option<Id>, parent: Option<Id>,
old_children: Option<Vec<Id>>, old_children: Option<Vec<Id>>,
@@ -119,31 +119,32 @@ impl<'a> PainterCtx<'a> {
// but this has a very weird issue where you can't move widgets unless u remove first // but this has a very weird issue where you can't move widgets unless u remove first
// so swapping is impossible rn I think? // so swapping is impossible rn I think?
// there's definitely better solutions like a counter (>1 = panic) but don't care rn // there's definitely better solutions like a counter (>1 = panic) but don't care rn
if self.drawing.contains(id) { if self.drawing.contains(&id) {
panic!("Cannot draw the same widget twice (1)"); panic!("Cannot draw the same widget twice (1)");
} }
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.get_mut(id) { if let Some(active) = self.active.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 {
return; return;
} else if active.region.size() == region.size() {
self.mov(id, region);
return;
} }
// TODO:
// else if active.region.size() == region.size() { move }
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;
} }
self.drawing.insert(id.duplicate()); self.drawing.insert(id);
let mut painter = Painter { let mut painter = Painter {
region, region,
layer, layer,
id: id.duplicate(), id,
textures: Vec::new(), textures: Vec::new(),
primitives: Vec::new(), primitives: Vec::new(),
ctx: self, ctx: self,
@@ -158,7 +159,7 @@ impl<'a> PainterCtx<'a> {
// add to active // add to active
let instance = WidgetInstance { let instance = WidgetInstance {
id: id.duplicate(), id,
region, region,
parent, parent,
textures: painter.textures, textures: painter.textures,
@@ -169,24 +170,43 @@ impl<'a> PainterCtx<'a> {
}; };
for (cid, size) in sized_children { for (cid, size) in sized_children {
if let Some(w) = self.active.get_mut(&cid) { if let Some(w) = self.active.get_mut(&cid) {
w.resize = Some((id.duplicate(), size)) w.resize = Some((id, size))
} }
} }
for c in &old_children { for c in &old_children {
if !instance.children.contains(c) { if !instance.children.contains(c) {
self.remove_rec(c); self.remove_rec(*c);
} }
} }
for m in self.modules.iter_mut() { for m in self.modules.iter_mut() {
m.on_draw(&instance); m.on_draw(&instance);
} }
self.active.insert(id.duplicate(), instance); self.active.insert(id, instance);
}
fn mov(&mut self, id: Id, to: UiRegion) {
let active = self.active.get_mut(&id).unwrap();
// children will not be changed, so this technically should not be needed
// probably need unsafe
let from = active.region;
for h in &active.primitives {
let region = self.layers[h.layer].primitives.region_mut(h);
*region = region.outside(&from).within(&to);
}
active.region = to;
for m in self.modules.iter_mut() {
m.on_move(active);
}
let children = active.children.clone();
for child in children {
self.mov(child, to);
}
} }
/// NOTE: instance textures are cleared and self.textures freed /// NOTE: instance textures are cleared and self.textures freed
fn remove(&mut self, id: &Id) -> Option<WidgetInstance> { fn remove(&mut self, id: Id) -> Option<WidgetInstance> {
let mut inst = self.active.remove(id); let mut inst = self.active.remove(&id);
if let Some(inst) = &mut inst { if let Some(inst) = &mut inst {
for h in &inst.primitives { for h in &inst.primitives {
self.layers.free(h); self.layers.free(h);
@@ -200,11 +220,11 @@ impl<'a> PainterCtx<'a> {
inst inst
} }
fn remove_rec(&mut self, id: &Id) -> Option<WidgetInstance> { fn remove_rec(&mut self, id: Id) -> Option<WidgetInstance> {
let inst = self.remove(id); let inst = self.remove(id);
if let Some(inst) = &inst { if let Some(inst) = &inst {
for c in &inst.children { for c in &inst.children {
self.remove_rec(c); self.remove_rec(*c);
} }
} }
inst inst
@@ -216,7 +236,7 @@ impl<'a, 'c> Painter<'a, 'c> {
let h = self let h = self
.ctx .ctx
.layers .layers
.write(self.layer, self.id.duplicate(), primitive, region); .write(self.layer, self.id, primitive, region);
self.primitives.push(h); self.primitives.push(h);
} }
@@ -241,9 +261,9 @@ impl<'a, 'c> Painter<'a, 'c> {
} }
fn widget_at<W>(&mut self, id: &WidgetId<W>, region: UiRegion) { fn widget_at<W>(&mut self, id: &WidgetId<W>, region: UiRegion) {
self.children.push(id.id.duplicate()); self.children.push(id.id);
self.ctx self.ctx
.draw_inner(self.layer, &id.id, region, Some(self.id.duplicate()), None); .draw_inner(self.layer, id.id, region, Some(self.id), None);
} }
pub fn texture_within(&mut self, handle: &TextureHandle, region: UiRegion) { pub fn texture_within(&mut self, handle: &TextureHandle, region: UiRegion) {
@@ -308,9 +328,9 @@ pub struct SizeCtx<'a> {
} }
impl SizeCtx<'_> { impl SizeCtx<'_> {
fn size_inner(&mut self, id: &Id) -> UiVec2 { fn size_inner(&mut self, id: Id) -> UiVec2 {
let size = self.widgets.get_dyn_dynamic(id).desired_size(self); let size = self.widgets.get_dyn_dynamic(id).desired_size(self);
self.checked.insert(id.duplicate(), size); self.checked.insert(id, size);
size size
} }
pub fn size<W>(&mut self, id: &WidgetId<W>) -> UiVec2 { pub fn size<W>(&mut self, id: &WidgetId<W>) -> UiVec2 {
@@ -318,7 +338,7 @@ impl SizeCtx<'_> {
// if let Some(size) = self.checked.get(&id.id) { // if let Some(size) = self.checked.get(&id.id) {
// return Some(*size); // return Some(*size);
// } // }
self.size_inner(&id.id) self.size_inner(id.id)
} }
pub fn draw_text( pub fn draw_text(
&mut self, &mut self,

View File

@@ -1,6 +1,6 @@
use crate::{ use crate::{
layout::{Align, Axis, Vec2}, layout::{Align, Axis, Vec2},
util::{F32Util, impl_op}, util::{LerpUtil, impl_op},
}; };
#[repr(C)] #[repr(C)]
@@ -52,23 +52,26 @@ impl UiVec2 {
} }
pub const fn within(&self, region: &UiRegion) -> UiVec2 { pub const fn within(&self, region: &UiRegion) -> UiVec2 {
let anchor = self.rel.lerp(region.top_left.rel, region.bot_right.rel); let rel = 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); let abs = self.abs + self.rel.lerp(region.top_left.abs, region.bot_right.abs);
UiVec2 { UiVec2 { rel, abs }
rel: anchor,
abs: offset,
} }
pub const fn outside(&self, region: &UiRegion) -> UiVec2 {
let rel = self.rel.lerp_inv(region.top_left.rel, region.bot_right.rel);
let abs = self.abs - rel.lerp(region.top_left.abs, region.bot_right.abs);
UiVec2 { rel, abs }
} }
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.rel.x, rel: &mut self.rel.x,
offset: &mut self.abs.x, abs: &mut self.abs.x,
}, },
Axis::Y => UiScalarView { Axis::Y => UiScalarView {
anchor: &mut self.rel.y, rel: &mut self.rel.y,
offset: &mut self.abs.y, abs: &mut self.abs.y,
}, },
} }
} }
@@ -110,6 +113,9 @@ impl UiVec2 {
} }
} }
impl_op!(UiVec2 Add add; rel abs);
impl_op!(UiVec2 Sub sub; rel abs);
impl const From<Align> for UiVec2 { impl const From<Align> for UiVec2 {
fn from(align: Align) -> Self { fn from(align: Align) -> Self {
Self::anchor(align.anchor()) Self::anchor(align.anchor())
@@ -204,6 +210,12 @@ impl UiRegion {
bot_right: self.bot_right.within(parent), bot_right: self.bot_right.within(parent),
} }
} }
pub fn outside(&self, parent: &Self) -> Self {
Self {
top_left: self.top_left.outside(parent),
bot_right: self.bot_right.outside(parent),
}
}
pub fn axis_mut(&mut self, axis: Axis) -> UIRegionAxisView<'_> { pub fn axis_mut(&mut self, axis: Axis) -> UIRegionAxisView<'_> {
UIRegionAxisView { UIRegionAxisView {
top_left: self.top_left.axis_mut(axis), top_left: self.top_left.axis_mut(axis),
@@ -243,6 +255,10 @@ impl UiRegion {
self.bot_right.to_size(size) - self.top_left.to_size(size) self.bot_right.to_size(size) - self.top_left.to_size(size)
} }
pub fn size(&self) -> UiVec2 {
self.bot_right - self.top_left
}
pub fn select_aligned(&self, size: Vec2, align: Align) -> Self { pub fn select_aligned(&self, size: Vec2, align: Align) -> Self {
Self::from_size_align(size, align).within(self) Self::from_size_align(size, align).within(self)
} }
@@ -292,19 +308,19 @@ pub struct UIRegionAxisView<'a> {
} }
pub struct UiScalarView<'a> { pub struct UiScalarView<'a> {
pub anchor: &'a mut f32, pub rel: &'a mut f32,
pub offset: &'a mut f32, pub abs: &'a mut f32,
} }
impl UiScalarView<'_> { impl UiScalarView<'_> {
pub fn set(&mut self, scalar: UiScalar) { pub fn set(&mut self, scalar: UiScalar) {
*self.anchor = scalar.rel; *self.rel = scalar.rel;
*self.offset = scalar.abs; *self.abs = scalar.abs;
} }
pub fn get(self) -> UiScalar { pub fn get(self) -> UiScalar {
UiScalar { UiScalar {
rel: *self.anchor, rel: *self.rel,
abs: *self.offset, abs: *self.abs,
} }
} }
} }

View File

@@ -59,12 +59,12 @@ impl Ui {
pub fn push<W: Widget>(&mut self, w: W) -> WidgetId<W> { pub fn push<W: Widget>(&mut self, w: W) -> WidgetId<W> {
let id = self.id(); let id = self.id();
self.widgets.insert(id.id.duplicate(), w); self.widgets.insert(id.id, w);
id id
} }
pub fn set<W: Widget>(&mut self, id: &WidgetId<W>, w: W) { pub fn set<W: Widget>(&mut self, id: &WidgetId<W>, w: W) {
self.widgets.insert(id.id.duplicate(), w); self.widgets.insert(id.id, w);
} }
pub fn set_root<Tag>(&mut self, w: impl WidgetLike<Tag>) { pub fn set_root<Tag>(&mut self, w: impl WidgetLike<Tag>) {
@@ -120,7 +120,7 @@ impl Ui {
self.size, self.size,
); );
if let Some(root) = &self.root { if let Some(root) = &self.root {
ctx.draw(&root.id); ctx.draw(root.id);
} }
} }
@@ -144,7 +144,7 @@ impl Ui {
self.size, self.size,
); );
for id in self.updates.drain(..) { for id in self.updates.drain(..) {
ctx.redraw(&id); ctx.redraw(id);
} }
self.free(); self.free();
} }
@@ -173,7 +173,7 @@ impl Ui {
} }
pub fn text(&mut self, id: &WidgetId<TextEdit>) -> TextEditCtx<'_> { pub fn text(&mut self, id: &WidgetId<TextEdit>) -> TextEditCtx<'_> {
self.updates.push(id.id.duplicate()); self.updates.push(id.id);
TextEditCtx { TextEditCtx {
text: self.widgets.get_mut(id).unwrap(), text: self.widgets.get_mut(id).unwrap(),
font_system: &mut self.text.font_system, font_system: &mut self.text.font_system,
@@ -191,7 +191,7 @@ impl<W: Widget> Index<&WidgetId<W>> for Ui {
impl<W: Widget> IndexMut<&WidgetId<W>> for Ui { impl<W: Widget> IndexMut<&WidgetId<W>> for Ui {
fn index_mut(&mut self, id: &WidgetId<W>) -> &mut Self::Output { fn index_mut(&mut self, id: &WidgetId<W>) -> &mut Self::Output {
self.updates.push(id.id.duplicate()); self.updates.push(id.id);
self.get_mut(id).unwrap() self.get_mut(id).unwrap()
} }
} }
@@ -206,7 +206,7 @@ impl<W: Widget> Index<StaticWidgetId<W>> for Ui {
impl<W: Widget> IndexMut<StaticWidgetId<W>> for Ui { impl<W: Widget> IndexMut<StaticWidgetId<W>> for Ui {
fn index_mut(&mut self, id: StaticWidgetId<W>) -> &mut Self::Output { fn index_mut(&mut self, id: StaticWidgetId<W>) -> &mut Self::Output {
self.updates.push(id.id.id()); self.updates.push(id.id);
self.widgets.get_static_mut(&id).unwrap() self.widgets.get_static_mut(&id).unwrap()
} }
} }

View File

@@ -1,6 +1,6 @@
use crate::{ use crate::{
layout::UiNum, layout::UiNum,
util::{F32Util, impl_op}, util::{DivOr, impl_op},
}; };
use std::ops::*; use std::ops::*;
@@ -23,15 +23,6 @@ impl Vec2 {
Self { x, y } Self { x, y }
} }
pub const fn lerp(self, from: impl const Into<Self>, to: impl const Into<Self>) -> Self {
let from = from.into();
let to = to.into();
Self {
x: self.x.lerp(from.x, to.x),
y: self.y.lerp(from.y, to.y),
}
}
pub const fn round(self) -> Self { pub const fn round(self) -> Self {
Self { Self {
x: self.x.round(), x: self.x.round(),
@@ -69,6 +60,15 @@ impl_op!(Vec2 Sub sub; x y);
impl_op!(Vec2 Mul mul; x y); impl_op!(Vec2 Mul mul; x y);
impl_op!(Vec2 Div div; x y); impl_op!(Vec2 Div div; x y);
impl const DivOr for Vec2 {
fn div_or(self, rhs: Self, other: Self) -> Self {
Self {
x: self.x.div_or(rhs.x, other.x),
y: self.y.div_or(rhs.y, other.y),
}
}
}
impl Neg for Vec2 { impl Neg for Vec2 {
type Output = Self; type Output = Self;

View File

@@ -24,22 +24,22 @@ impl Widgets {
} }
} }
pub fn get_dyn(&self, id: &Id) -> Option<&dyn Widget> { pub fn get_dyn(&self, id: Id) -> Option<&dyn Widget> {
Some(self.map.get(id)?.widget.as_ref()) Some(self.map.get(&id)?.widget.as_ref())
} }
pub fn get_dyn_mut(&mut self, id: &Id) -> Option<&mut dyn Widget> { pub fn get_dyn_mut(&mut self, id: Id) -> Option<&mut dyn Widget> {
Some(self.map.get_mut(id)?.widget.as_mut()) Some(self.map.get_mut(&id)?.widget.as_mut())
} }
/// get_dyn but dynamic borrow checking of widgets /// get_dyn but dynamic borrow checking of widgets
/// lets you do recursive (tree) operations, like the painter does /// lets you do recursive (tree) operations, like the painter does
pub fn get_dyn_dynamic(&self, id: &Id) -> WidgetWrapper<'_> { pub fn get_dyn_dynamic(&self, id: Id) -> WidgetWrapper<'_> {
// SAFETY: must guarantee no other mutable references to this widget exist // SAFETY: must guarantee no other mutable references to this widget exist
// done through the borrow variable // done through the borrow variable
#[allow(mutable_transmutes)] #[allow(mutable_transmutes)]
let data = unsafe { let data = unsafe {
std::mem::transmute::<&WidgetData, &mut WidgetData>(self.map.get(id).unwrap()) std::mem::transmute::<&WidgetData, &mut WidgetData>(self.map.get(&id).unwrap())
}; };
if data.borrowed { if data.borrowed {
panic!("tried to mutably borrow the same widget twice"); panic!("tried to mutably borrow the same widget twice");
@@ -48,19 +48,19 @@ impl Widgets {
} }
pub fn get_static<W: Widget>(&self, id: &StaticWidgetId<W>) -> Option<&W> { pub fn get_static<W: Widget>(&self, id: &StaticWidgetId<W>) -> Option<&W> {
self.get_dyn(&id.id.id())?.as_any().downcast_ref() self.get_dyn(id.id)?.as_any().downcast_ref()
} }
pub fn get_static_mut<W: Widget>(&mut self, id: &StaticWidgetId<W>) -> Option<&mut W> { pub fn get_static_mut<W: Widget>(&mut self, id: &StaticWidgetId<W>) -> Option<&mut W> {
self.get_dyn_mut(&id.id.id())?.as_any_mut().downcast_mut() self.get_dyn_mut(id.id)?.as_any_mut().downcast_mut()
} }
pub fn get<W: Widget>(&self, id: &WidgetId<W>) -> Option<&W> { pub fn get<W: Widget>(&self, id: &WidgetId<W>) -> Option<&W> {
self.get_dyn(&id.id)?.as_any().downcast_ref() self.get_dyn(id.id)?.as_any().downcast_ref()
} }
pub fn get_mut<W: Widget>(&mut self, id: &WidgetId<W>) -> Option<&mut W> { pub fn get_mut<W: Widget>(&mut self, id: &WidgetId<W>) -> Option<&mut W> {
self.get_dyn_mut(&id.id)?.as_any_mut().downcast_mut() self.get_dyn_mut(id.id)?.as_any_mut().downcast_mut()
} }
pub fn insert<W: Widget>(&mut self, id: Id, widget: W) { pub fn insert<W: Widget>(&mut self, id: Id, widget: W) {

View File

@@ -6,6 +6,7 @@
#![feature(trait_alias)] #![feature(trait_alias)]
#![feature(unboxed_closures)] #![feature(unboxed_closures)]
#![feature(fn_traits)] #![feature(fn_traits)]
#![feature(const_cmp)]
pub mod core; pub mod core;
pub mod layout; pub mod layout;

View File

@@ -132,7 +132,7 @@ impl Primitives {
if i == self.instances.len() { if i == self.instances.len() {
return None; return None;
} }
let id = self.assoc[i].duplicate(); let id = self.assoc[i];
let old = self.instances.len(); let old = self.instances.len();
Some(PrimitiveChange { id, old, new: i }) Some(PrimitiveChange { id, old, new: i })
}) })
@@ -150,6 +150,11 @@ impl Primitives {
pub fn instances(&self) -> &Vec<PrimitiveInstance> { pub fn instances(&self) -> &Vec<PrimitiveInstance> {
&self.instances &self.instances
} }
pub fn region_mut(&mut self, h: &PrimitiveHandle) -> &mut UiRegion {
self.updated = true;
&mut self.instances[h.inst_idx].region
}
} }
pub struct PrimitiveChange { pub struct PrimitiveChange {

View File

@@ -1,9 +1,4 @@
/// intentionally does not implement copy or clone #[derive(Eq, Hash, PartialEq, Debug, Clone, Copy)]
/// which should make it harder to misuse;
/// the idea is to generally try to guarantee all IDs
/// point to something valid, although duplicate
/// gets around this if needed
#[derive(Eq, Hash, PartialEq, Debug)]
pub struct Id(u64); pub struct Id(u64);
#[derive(Default)] #[derive(Default)]
@@ -28,39 +23,3 @@ impl IdTracker {
self.free.push(id); self.free.push(id);
} }
} }
impl Id {
/// this must be used carefully to make sure
/// all IDs are still valid references;
/// named weirdly to indicate this.
/// generally should not be used in "user" code
pub fn duplicate(&self) -> Self {
Self(self.0)
}
/// this must be used carefully to make sure
/// all IDs are still valid references.
/// generally should not be used in "user" code
pub fn copyable(&self) -> CopyId {
CopyId(self.0)
}
}
#[derive(Eq, Hash, PartialEq, Debug, Clone, Copy)]
pub struct CopyId(u64);
impl CopyId {
pub fn id(&self) -> Id {
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())
}
}

View File

@@ -1,19 +1,41 @@
use std::ops::*; use std::ops::*;
#[const_trait] #[const_trait]
pub trait F32Util { pub trait LerpUtil {
fn lerp<T: const Mul<f32, Output = T> + const Add<Output = T>>(self, from: T, to: T) -> T; fn lerp(self, from: Self, to: Self) -> Self;
fn lerp_inv(self, from: Self, to: Self) -> Self;
} }
impl const F32Util for f32 { #[const_trait]
fn lerp<T: const Mul<f32, Output = T> + const Add<Output = T>>(self, from: T, to: T) -> T { pub trait DivOr {
from * (1.0 - self) + to * self fn div_or(self, rhs: Self, other: Self) -> Self;
}
impl const DivOr for f32 {
fn div_or(self, rhs: Self, other: Self) -> Self {
let res = self / rhs;
if res.is_nan() { other } else { res }
}
}
impl<T: const Add<Output = T> + const Sub<Output = T> + const Mul<Output = T> + const DivOr + Copy> const
LerpUtil for T
{
/// linear interpolation
/// from * (1.0 - self) + to * self
fn lerp(self, from: Self, to: Self) -> Self {
from + (to - from) * self
}
/// inverse of lerp
fn lerp_inv(self, from: Self, to: Self) -> Self {
(self - from).div_or(to - from, from)
} }
} }
macro_rules! impl_op { macro_rules! impl_op {
($T:ident $op:ident $fn:ident $opa:ident $fna:ident; $($field:ident)*) => { ($T:ident $op:ident $fn:ident $opa:ident $fna:ident; $($field:ident)*) => {
mod ${concat(op_, $fn, _impl)} { #[allow(non_snake_case)]
mod ${concat($T, _op_, $fn, _impl)} {
use super::*; use super::*;
#[allow(unused_imports)] #[allow(unused_imports)]
use std::ops::*; use std::ops::*;