This commit is contained in:
2026-01-12 18:40:27 -05:00
parent a9c76e4326
commit 79813db3ba
35 changed files with 378 additions and 403 deletions

View File

@@ -1,15 +1,15 @@
use crate::{HasUi, WidgetIdFn, WidgetLike, WidgetRef}; use crate::{UiRsc, WidgetIdFn, WidgetLike, WeakWidget};
pub trait WidgetAttr<Rsc, W: ?Sized> { pub trait WidgetAttr<Rsc, W: ?Sized> {
type Input; type Input;
fn run(rsc: &mut Rsc, id: WidgetRef<W>, input: Self::Input); fn run(rsc: &mut Rsc, id: WeakWidget<W>, input: Self::Input);
} }
pub trait Attrable<Rsc, W: ?Sized, Tag> { pub trait Attrable<Rsc, W: ?Sized, Tag> {
fn attr<A: WidgetAttr<Rsc, W>>(self, input: A::Input) -> impl WidgetIdFn<Rsc, W>; fn attr<A: WidgetAttr<Rsc, W>>(self, input: A::Input) -> impl WidgetIdFn<Rsc, W>;
} }
impl<Rsc: HasUi, WL: WidgetLike<Rsc, Tag>, Tag> Attrable<Rsc, WL::Widget, Tag> for WL { impl<Rsc: UiRsc, WL: WidgetLike<Rsc, Tag>, Tag> Attrable<Rsc, WL::Widget, Tag> for WL {
fn attr<A: WidgetAttr<Rsc, WL::Widget>>( fn attr<A: WidgetAttr<Rsc, WL::Widget>>(
self, self,
input: A::Input, input: A::Input,

View File

@@ -1,4 +1,4 @@
use crate::{HasEvents, HasUi, Widget, WidgetRef}; use crate::{HasEvents, WeakWidget, Widget};
pub struct EventCtx<'a, Rsc: HasEvents, Data> { pub struct EventCtx<'a, Rsc: HasEvents, Data> {
pub state: &'a mut Rsc::State, pub state: &'a mut Rsc::State,
@@ -6,13 +6,13 @@ pub struct EventCtx<'a, Rsc: HasEvents, Data> {
} }
pub struct EventIdCtx<'a, Rsc: HasEvents, Data, W: ?Sized> { pub struct EventIdCtx<'a, Rsc: HasEvents, Data, W: ?Sized> {
pub widget: WidgetRef<W>, pub widget: WeakWidget<W>,
pub state: &'a mut Rsc::State, pub state: &'a mut Rsc::State,
pub data: Data, pub data: Data,
} }
impl<Rsc: HasEvents + HasUi, Data, W: Widget> EventIdCtx<'_, Rsc, Data, W> { impl<Rsc: HasEvents, Data, W: Widget> EventIdCtx<'_, Rsc, Data, W> {
pub fn widget<'a>(&self, rsc: &'a mut Rsc) -> &'a mut W { pub fn widget<'a>(&self, rsc: &'a mut Rsc) -> &'a mut W {
&mut rsc.ui_mut()[self.widget] &mut rsc.widgets_mut()[self.widget]
} }
} }

View File

@@ -1,6 +1,6 @@
use crate::{ use crate::{
ActiveData, Event, EventCtx, EventFn, EventIdCtx, EventLike, HasEvents, IdLike, LayerId, ActiveData, Event, EventCtx, EventFn, EventIdCtx, EventLike, HasEvents, IdLike, LayerId,
Widget, WidgetEventFn, WidgetId, WidgetRef, Widget, WidgetEventFn, WidgetId, WeakWidget,
util::{HashMap, HashSet, TypeMap}, util::{HashMap, HashSet, TypeMap},
}; };
use std::{any::TypeId, rc::Rc}; use std::{any::TypeId, rc::Rc};
@@ -26,7 +26,7 @@ impl<Rsc: HasEvents + 'static> EventManager<Rsc> {
pub fn register<W: Widget + ?Sized, E: EventLike>( pub fn register<W: Widget + ?Sized, E: EventLike>(
&mut self, &mut self,
id: WidgetRef<W>, id: WeakWidget<W>,
event: E, event: E,
f: impl WidgetEventFn<Rsc, <E::Event as Event>::Data, W>, f: impl WidgetEventFn<Rsc, <E::Event as Event>::Data, W>,
) { ) {
@@ -114,7 +114,7 @@ impl<Rsc: HasEvents, E: Event> Default for TypeEventManager<Rsc, E> {
impl<Rsc: HasEvents + 'static, E: Event> TypeEventManager<Rsc, E> { impl<Rsc: HasEvents + 'static, E: Event> TypeEventManager<Rsc, E> {
fn register<W: Widget + ?Sized>( fn register<W: Widget + ?Sized>(
&mut self, &mut self,
widget: WidgetRef<W>, widget: WeakWidget<W>,
event: impl EventLike<Event = E>, event: impl EventLike<Event = E>,
f: impl WidgetEventFn<Rsc, E::Data, W>, f: impl WidgetEventFn<Rsc, E::Data, W>,
) { ) {

View File

@@ -1,18 +1,18 @@
use crate::{ use crate::{
Event, EventCtx, EventLike, EventManager, HasUi, IdLike, Widget, WidgetEventFn, WidgetRef, Event, EventCtx, EventLike, EventManager, IdLike, UiRsc, Widget, WidgetEventFn, WeakWidget,
}; };
pub trait HasState: 'static { pub trait HasState: 'static {
type State; type State;
} }
pub trait HasEvents: Sized + HasUi + HasState { pub trait HasEvents: Sized + UiRsc + HasState {
fn events(&self) -> &EventManager<Self>; fn events(&self) -> &EventManager<Self>;
fn events_mut(&mut self) -> &mut EventManager<Self>; fn events_mut(&mut self) -> &mut EventManager<Self>;
fn register_event<W: Widget + ?Sized, E: EventLike>( fn register_event<W: Widget + ?Sized, E: EventLike>(
&mut self, &mut self,
id: WidgetRef<W>, id: WeakWidget<W>,
event: E, event: E,
f: impl WidgetEventFn<Self, <E::Event as Event>::Data, W>, f: impl WidgetEventFn<Self, <E::Event as Event>::Data, W>,
) { ) {

View File

@@ -7,3 +7,12 @@ pub use color::*;
pub use layer::*; pub use layer::*;
pub use text::*; pub use text::*;
pub use texture::*; pub use texture::*;
use crate::{Mask, util::TrackedArena};
#[derive(Default)]
pub struct PainterData {
pub textures: Textures,
pub text: TextData,
pub masks: TrackedArena<Mask, u32>,
}

View File

@@ -3,7 +3,7 @@ use cosmic_text::{
Attrs, AttrsList, Buffer, CacheKey, Color, Family, FontSystem, Metrics, Placement, SwashCache, Attrs, AttrsList, Buffer, CacheKey, Color, Family, FontSystem, Metrics, Placement, SwashCache,
SwashContent, SwashContent,
}; };
use image::{GenericImageView, RgbaImage}; use image::{DynamicImage, GenericImageView, RgbaImage};
use std::simd::{Simd, num::SimdUint}; use std::simd::{Simd, num::SimdUint};
/// TODO: properly wrap this /// TODO: properly wrap this
@@ -185,3 +185,7 @@ pub struct RenderedText {
pub top_left_offset: Vec2, pub top_left_offset: Vec2,
pub size: Vec2, pub size: Vec2,
} }
pub trait HasTextures {
fn add_texture(&mut self, image: DynamicImage) -> TextureHandle;
}

View File

@@ -1,7 +1,7 @@
use std::num::NonZero; use std::num::NonZero;
use crate::{ use crate::{
Ui, Textures, UiRenderState,
render::{data::PrimitiveInstance, texture::GpuTextures, util::ArrBuf}, render::{data::PrimitiveInstance, texture::GpuTextures, util::ArrBuf},
util::HashMap, util::HashMap,
}; };
@@ -59,7 +59,13 @@ impl UiRenderNode {
} }
} }
pub fn update(&mut self, device: &Device, queue: &Queue, ui: &mut Ui) { pub fn update(
&mut self,
device: &Device,
queue: &Queue,
ui: &mut UiRenderState,
textures: &mut Textures,
) {
self.active.clear(); self.active.clear();
for (i, primitives) in ui.layers.iter_mut() { for (i, primitives) in ui.layers.iter_mut() {
self.active.push(i); self.active.push(i);
@@ -101,7 +107,7 @@ impl UiRenderNode {
} }
} }
let mut changed = false; let mut changed = false;
changed |= self.textures.update(&mut ui.textures); changed |= self.textures.update(textures);
if ui.masks.changed { if ui.masks.changed {
ui.masks.changed = false; ui.masks.changed = false;
self.masks.update(device, queue, &ui.masks[..]); self.masks.update(device, queue, &ui.masks[..]);

View File

@@ -1,22 +1,34 @@
use crate::{ use crate::{
ActiveData, Axis, EventsLike, Painter, SizeCtx, Ui, UiRegion, UiVec2, WidgetId, ActiveData, Axis, EventsLike, Painter, PainterData, SizeCtx, StrongWidget, UiRegion,
UiRenderState, UiVec2, WidgetId, Widgets,
render::MaskIdx, render::MaskIdx,
util::{HashSet, forget_ref}, util::{HashSet, forget_ref},
}; };
use std::ops::{Deref, DerefMut};
/// state maintained between widgets during painting /// state maintained between widgets during painting
pub struct DrawState<'a> { pub struct Drawer<'a> {
pub(super) ui: &'a mut Ui, pub(super) widgets: &'a mut Widgets,
pub(super) data: &'a mut PainterData,
pub(super) events: &'a mut dyn EventsLike, pub(super) events: &'a mut dyn EventsLike,
pub(super) render: &'a mut UiRenderState,
root: Option<&'a StrongWidget>,
draw_started: HashSet<WidgetId>, draw_started: HashSet<WidgetId>,
} }
impl<'a> DrawState<'a> { impl<'a> Drawer<'a> {
pub fn new(ui: &'a mut Ui, events: &'a mut dyn EventsLike) -> Self { pub fn new(
widgets: &'a mut Widgets,
data: &'a mut PainterData,
render: &'a mut UiRenderState,
events: &'a mut dyn EventsLike,
root: Option<&'a StrongWidget>,
) -> Self {
Self { Self {
ui, widgets,
data,
events, events,
render,
root,
draw_started: Default::default(), draw_started: Default::default(),
} }
} }
@@ -34,13 +46,17 @@ impl<'a> DrawState<'a> {
self.draw_started.remove(&id); self.draw_started.remove(&id);
// check if parent depends on the desired size of this, if so then redraw it first // check if parent depends on the desired size of this, if so then redraw it first
for axis in [Axis::X, Axis::Y] { for axis in [Axis::X, Axis::Y] {
if let Some(&(outer, old)) = self.cache.size.axis_dyn(axis).get(&id) if let Some(&(outer, old)) = self.render.cache.size.axis_dyn(axis).get(&id)
&& let Some(current) = self.active.get(&id) && let Some(current) = self.render.active.get(&id)
&& let Some(pid) = current.parent && let Some(pid) = current.parent
{ {
self.cache.size.axis_dyn(axis).remove(&id); self.render.cache.size.axis_dyn(axis).remove(&id);
let new = self.size_ctx(id, outer).len_axis(id, axis); let new = self.size_ctx(id, outer).len_axis(id, axis);
self.cache.size.axis_dyn(axis).insert(id, (outer, new)); self.render
.cache
.size
.axis_dyn(axis)
.insert(id, (outer, new));
if new != old { if new != old {
self.redraw(pid); self.redraw(pid);
} }
@@ -68,27 +84,27 @@ impl<'a> DrawState<'a> {
pub(super) fn size_ctx<'b>(&'b mut self, source: WidgetId, outer: UiVec2) -> SizeCtx<'b> { pub(super) fn size_ctx<'b>(&'b mut self, source: WidgetId, outer: UiVec2) -> SizeCtx<'b> {
SizeCtx { SizeCtx {
source, source,
cache: &mut self.ui.cache, cache: &mut self.render.cache,
text: &mut self.ui.text, text: &mut self.data.text,
textures: &mut self.ui.textures, textures: &mut self.data.textures,
widgets: &self.ui.widgets, widgets: &self.widgets,
outer, outer,
output_size: self.ui.output_size, output_size: self.render.output_size,
id: source, id: source,
} }
} }
pub fn redraw_all(&mut self) { pub fn redraw_all(&mut self) {
// free all resources & cache // free all resources & cache
for (_, active) in self.ui.active.drain() { for (_, active) in self.render.active.drain() {
self.events.undraw(&active); self.events.undraw(&active);
} }
self.ui.cache.clear(); self.render.cache.clear();
self.ui.free(self.events); self.ui.free(self.events);
self.layers.clear(); self.render.layers.clear();
self.widgets.needs_redraw.clear(); self.widgets.needs_redraw.clear();
if let Some(id) = &self.ui.root { if let Some(id) = self.root {
self.draw_inner(0, id.id(), UiRegion::FULL, None, MaskIdx::NONE, None); self.draw_inner(0, id.id(), UiRegion::FULL, None, MaskIdx::NONE, None);
} }
} }
@@ -103,8 +119,8 @@ impl<'a> DrawState<'a> {
old_children: Option<Vec<WidgetId>>, old_children: Option<Vec<WidgetId>>,
) { ) {
let mut old_children = old_children.unwrap_or_default(); let mut old_children = old_children.unwrap_or_default();
if let Some(active) = self.ui.active.get_mut(&id) if let Some(active) = self.render.active.get_mut(&id)
&& !self.ui.widgets.needs_redraw.contains(&id) && !self.widgets.needs_redraw.contains(&id)
{ {
// check to see if we can skip drawing first // check to see if we can skip drawing first
if active.region == region { if active.region == region {
@@ -124,7 +140,7 @@ impl<'a> DrawState<'a> {
self.draw_started.insert(id); self.draw_started.insert(id);
let mut painter = Painter { let mut painter = Painter {
state: self, drawer: self,
region, region,
mask, mask,
layer, layer,
@@ -134,12 +150,12 @@ impl<'a> DrawState<'a> {
children: Vec::new(), children: Vec::new(),
}; };
let mut widget = painter.state.widgets.get_dyn_dynamic(id); let mut widget = painter.drawer.widgets.get_dyn_dynamic(id);
widget.draw(&mut painter); widget.draw(&mut painter);
drop(widget); drop(widget);
let Painter { let Painter {
state: _, drawer: _,
region, region,
mask, mask,
textures, textures,
@@ -170,13 +186,13 @@ impl<'a> DrawState<'a> {
// update modules // update modules
self.events.draw(&active); self.events.draw(&active);
self.active.insert(id, active); self.render.active.insert(id, active);
} }
fn mov(&mut self, id: WidgetId, from: UiRegion, to: UiRegion) { fn mov(&mut self, id: WidgetId, from: UiRegion, to: UiRegion) {
let active = self.ui.active.get_mut(&id).unwrap(); let active = self.render.active.get_mut(&id).unwrap();
for h in &active.primitives { for h in &active.primitives {
let region = self.ui.layers[h.layer].region_mut(h); let region = self.render.layers[h.layer].region_mut(h);
*region = region.outside(&from).within(&to); *region = region.outside(&from).within(&to);
} }
active.region = active.region.outside(&from).within(&to); active.region = active.region.outside(&from).within(&to);
@@ -189,16 +205,16 @@ impl<'a> DrawState<'a> {
/// NOTE: instance textures are cleared and self.textures freed /// NOTE: instance textures are cleared and self.textures freed
fn remove(&mut self, id: WidgetId, undraw: bool) -> Option<ActiveData> { fn remove(&mut self, id: WidgetId, undraw: bool) -> Option<ActiveData> {
let mut active = self.active.remove(&id); let mut active = self.render.active.remove(&id);
if let Some(active) = &mut active { if let Some(active) = &mut active {
for h in &active.primitives { for h in &active.primitives {
let mask = self.layers.free(h); let mask = self.render.layers.free(h);
if mask != MaskIdx::NONE { if mask != MaskIdx::NONE {
self.masks.remove(mask); self.data.masks.remove(mask);
} }
} }
active.textures.clear(); active.textures.clear();
self.textures.free(); self.data.textures.free();
if undraw { if undraw {
self.events.undraw(active); self.events.undraw(active);
} }
@@ -207,7 +223,7 @@ impl<'a> DrawState<'a> {
} }
fn remove_rec(&mut self, id: WidgetId) -> Option<ActiveData> { fn remove_rec(&mut self, id: WidgetId) -> Option<ActiveData> {
self.cache.remove(id); self.render.cache.remove(id);
let inst = self.remove(id, true); let inst = self.remove(id, true);
if let Some(inst) = &inst { if let Some(inst) = &inst {
for c in &inst.children { for c in &inst.children {
@@ -217,16 +233,3 @@ impl<'a> DrawState<'a> {
inst inst
} }
} }
impl Deref for DrawState<'_> {
type Target = Ui;
fn deref(&self) -> &Self::Target {
self.ui
}
}
impl DerefMut for DrawState<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.ui
}
}

View File

@@ -1,211 +1,22 @@
use crate::{ use crate::{WeakWidget, Widget, Widgets};
EventsLike, IdLike, Mask, PixelRegion, PrimitiveLayers, TextData, TextureHandle, Textures,
Widget, WidgetHandle, WidgetId, Widgets,
ui::draw_state::DrawState,
util::{HashMap, TrackedArena, Vec2},
};
use image::DynamicImage;
use std::{
ops::{Index, IndexMut},
sync::mpsc::{Receiver, channel},
};
mod active; mod active;
mod cache; mod cache;
mod draw_state; mod draw_state;
mod painter; mod painter;
mod render_state;
mod size; mod size;
mod state; mod state;
pub use active::*; pub use active::*;
use cache::*;
pub use painter::Painter; pub use painter::Painter;
pub use render_state::*;
pub use size::*; pub use size::*;
pub struct Ui { pub struct Ui {}
// TODO: edit visibilities
pub widgets: Widgets,
// retained painter state pub trait UiRsc: Sized {
pub active: HashMap<WidgetId, ActiveData>, fn add_widget<W: Widget>(&mut self, widget: W) -> WeakWidget<W>;
pub layers: PrimitiveLayers, fn widgets(&self) -> &Widgets;
pub textures: Textures, fn widgets_mut(&mut self) -> &mut Widgets;
pub text: TextData,
output_size: Vec2,
pub masks: TrackedArena<Mask, u32>,
pub cache: Cache,
pub root: Option<WidgetHandle>,
old_root: Option<WidgetId>,
recv: Receiver<WidgetId>,
resized: bool,
}
pub trait HasUi: Sized {
fn ui(&self) -> &Ui;
fn ui_mut(&mut self) -> &mut Ui;
}
impl HasUi for Ui {
fn ui(&self) -> &Ui {
self
}
fn ui_mut(&mut self) -> &mut Ui {
self
}
}
impl Ui {
/// useful for debugging
pub fn set_label(&mut self, id: impl IdLike, label: String) {
self.widgets.data_mut(id.id()).unwrap().label = label;
}
pub fn label(&self, id: impl IdLike) -> &String {
&self.widgets.data(id.id()).unwrap().label
}
pub fn new() -> Self {
Self::default()
}
pub fn get<I: IdLike>(&self, id: &I) -> Option<&I::Widget>
where
I::Widget: Sized + Widget,
{
self.widgets.get(id)
}
pub fn get_mut<I: IdLike>(&mut self, id: &I) -> Option<&mut I::Widget>
where
I::Widget: Sized + Widget,
{
self.widgets.get_mut(id)
}
pub fn add_texture(&mut self, image: DynamicImage) -> TextureHandle {
self.textures.add(image)
}
pub fn resize(&mut self, size: impl Into<Vec2>) {
self.output_size = size.into();
self.resized = true;
}
pub fn update(&mut self, events: &mut dyn EventsLike) {
if !self.widgets.waiting.is_empty() {
let len = self.widgets.waiting.len();
let all: Vec<_> = self
.widgets
.waiting
.iter()
.map(|&w| format!("'{}' ({w:?})", self.label(w)))
.collect();
panic!(
"{len} widget(s) were never upgraded\n\
this is likely a memory leak; consider upgrading to strong if you plan on using it later\n\
weak widgets: {all:#?}"
);
}
if self.root_changed() {
DrawState::new(self, events).redraw_all();
self.old_root = self.root.as_ref().map(|r| r.id());
} else if self.widgets.has_updates() {
DrawState::new(self, events).redraw_updates();
}
if self.resized {
self.resized = false;
DrawState::new(self, events).redraw_all();
}
}
/// free any resources that don't have references anymore
fn free(&mut self, events: &mut dyn EventsLike) {
for id in self.recv.try_iter() {
events.remove(id);
self.widgets.delete(id);
}
self.textures.free();
}
pub fn root_changed(&self) -> bool {
self.root.as_ref().map(|r| r.id()) != self.old_root
}
pub fn needs_redraw(&self) -> bool {
self.root_changed() || self.widgets.has_updates()
}
pub fn num_widgets(&self) -> usize {
self.widgets.len()
}
pub fn active_widgets(&self) -> usize {
self.active.len()
}
pub fn debug_layers(&self) {
for ((idx, depth), primitives) in self.layers.iter_depth() {
let indent = " ".repeat(depth * 2);
let len = primitives.instances().len();
print!("{indent}{idx}: {len} primitives");
if len >= 1 {
print!(" ({})", primitives.instances()[0].binding);
}
println!();
}
}
pub fn window_region(&self, id: &impl IdLike) -> Option<PixelRegion> {
let region = self.active.get(&id.id())?.region;
Some(region.to_px(self.output_size))
}
pub fn debug(&self, label: &str) -> impl Iterator<Item = &ActiveData> {
self.active.iter().filter_map(move |(&id, inst)| {
let l = self.widgets.label(id);
if l == label { Some(inst) } else { None }
})
}
}
impl<I: IdLike> Index<I> for Ui
where
I::Widget: Sized + Widget,
{
type Output = I::Widget;
fn index(&self, id: I) -> &Self::Output {
self.get(&id).unwrap()
}
}
impl<I: IdLike> IndexMut<I> for Ui
where
I::Widget: Sized + Widget,
{
fn index_mut(&mut self, id: I) -> &mut Self::Output {
self.get_mut(&id).unwrap()
}
}
impl Default for Ui {
fn default() -> Self {
let (send, recv) = channel();
Self {
widgets: Widgets::new(send),
active: Default::default(),
layers: Default::default(),
masks: Default::default(),
text: Default::default(),
textures: Default::default(),
cache: Default::default(),
output_size: Vec2::ZERO,
root: None,
old_root: None,
recv,
resized: false,
}
}
} }

View File

@@ -1,14 +1,14 @@
use crate::{ use crate::{
Axis, Len, RenderedText, Size, SizeCtx, TextAttrs, TextBuffer, TextData, TextureHandle, Axis, Len, RenderedText, Size, SizeCtx, StrongWidget, TextAttrs, TextBuffer, TextData,
UiRegion, Widget, WidgetHandle, WidgetId, TextureHandle, UiRegion, Widget, WidgetId,
render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst}, render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst},
ui::draw_state::DrawState, ui::draw_state::Drawer,
util::Vec2, util::Vec2,
}; };
/// makes your surfaces look pretty /// makes your surfaces look pretty
pub struct Painter<'a, 'b> { pub struct Painter<'a, 'b> {
pub(super) state: &'a mut DrawState<'b>, pub(super) drawer: &'a mut Drawer<'b>,
pub(super) region: UiRegion, pub(super) region: UiRegion,
pub(super) mask: MaskIdx, pub(super) mask: MaskIdx,
@@ -21,7 +21,7 @@ pub struct Painter<'a, 'b> {
impl<'a, 'c> Painter<'a, 'c> { impl<'a, 'c> Painter<'a, 'c> {
fn primitive_at<P: Primitive>(&mut self, primitive: P, region: UiRegion) { fn primitive_at<P: Primitive>(&mut self, primitive: P, region: UiRegion) {
let h = self.state.layers.write( let h = self.drawer.layers.write(
self.layer, self.layer,
PrimitiveInst { PrimitiveInst {
id: self.id, id: self.id,
@@ -32,7 +32,7 @@ impl<'a, 'c> Painter<'a, 'c> {
); );
if self.mask != MaskIdx::NONE { if self.mask != MaskIdx::NONE {
// TODO: I have no clue if this works at all :joy: // TODO: I have no clue if this works at all :joy:
self.state.masks.push_ref(self.mask); self.drawer.masks.push_ref(self.mask);
} }
self.primitives.push(h); self.primitives.push(h);
} }
@@ -48,23 +48,23 @@ impl<'a, 'c> Painter<'a, 'c> {
pub fn set_mask(&mut self, region: UiRegion) { pub fn set_mask(&mut self, region: UiRegion) {
assert!(self.mask == MaskIdx::NONE); assert!(self.mask == MaskIdx::NONE);
self.mask = self.state.masks.push(Mask { region }); self.mask = self.drawer.masks.push(Mask { region });
} }
/// Draws a widget within this widget's region. /// Draws a widget within this widget's region.
pub fn widget<W: ?Sized>(&mut self, id: &WidgetHandle<W>) { pub fn widget<W: ?Sized>(&mut self, id: &StrongWidget<W>) {
self.widget_at(id, self.region); self.widget_at(id, self.region);
} }
/// Draws a widget somewhere within this one. /// Draws a widget somewhere within this one.
/// Useful for drawing child widgets in select areas. /// Useful for drawing child widgets in select areas.
pub fn widget_within<W: ?Sized>(&mut self, id: &WidgetHandle<W>, region: UiRegion) { pub fn widget_within<W: ?Sized>(&mut self, id: &StrongWidget<W>, region: UiRegion) {
self.widget_at(id, region.within(&self.region)); self.widget_at(id, region.within(&self.region));
} }
fn widget_at<W: ?Sized>(&mut self, id: &WidgetHandle<W>, region: UiRegion) { fn widget_at<W: ?Sized>(&mut self, id: &StrongWidget<W>, region: UiRegion) {
self.children.push(id.id()); self.children.push(id.id());
self.state self.drawer
.draw_inner(self.layer, id.id(), region, Some(self.id), self.mask, None); .draw_inner(self.layer, id.id(), region, Some(self.id), self.mask, None);
} }
@@ -85,21 +85,21 @@ impl<'a, 'c> Painter<'a, 'c> {
/// returns (handle, offset from top left) /// returns (handle, offset from top left)
pub fn render_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> RenderedText { pub fn render_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> RenderedText {
self.state self.drawer
.ui .ui
.text .text
.draw(buffer, attrs, &mut self.state.ui.textures) .draw(buffer, attrs, &mut self.drawer.ui.textures)
} }
pub fn region(&self) -> UiRegion { pub fn region(&self) -> UiRegion {
self.region self.region
} }
pub fn size<W: ?Sized + Widget>(&mut self, id: &WidgetHandle<W>) -> Size { pub fn size<W: ?Sized + Widget>(&mut self, id: &StrongWidget<W>) -> Size {
self.size_ctx().size(id) self.size_ctx().size(id)
} }
pub fn len_axis<W: ?Sized + Widget>(&mut self, id: &WidgetHandle<W>, axis: Axis) -> Len { pub fn len_axis<W: ?Sized + Widget>(&mut self, id: &StrongWidget<W>, axis: Axis) -> Len {
match axis { match axis {
Axis::X => self.size_ctx().width(id), Axis::X => self.size_ctx().width(id),
Axis::Y => self.size_ctx().height(id), Axis::Y => self.size_ctx().height(id),
@@ -107,27 +107,27 @@ impl<'a, 'c> Painter<'a, 'c> {
} }
pub fn output_size(&self) -> Vec2 { pub fn output_size(&self) -> Vec2 {
self.state.output_size self.drawer.output_size
} }
pub fn px_size(&mut self) -> Vec2 { pub fn px_size(&mut self) -> Vec2 {
self.region.size().to_abs(self.state.output_size) self.region.size().to_abs(self.drawer.output_size)
} }
pub fn text_data(&mut self) -> &mut TextData { pub fn text_data(&mut self) -> &mut TextData {
&mut self.state.text &mut self.drawer.text
} }
pub fn child_layer(&mut self) { pub fn child_layer(&mut self) {
self.layer = self.state.layers.child(self.layer); self.layer = self.drawer.layers.child(self.layer);
} }
pub fn next_layer(&mut self) { pub fn next_layer(&mut self) {
self.layer = self.state.layers.next(self.layer); self.layer = self.drawer.layers.next(self.layer);
} }
pub fn label(&self) -> &str { pub fn label(&self) -> &str {
&self.state.widgets.data(self.id).unwrap().label &self.drawer.widgets.data(self.id).unwrap().label
} }
pub fn id(&self) -> &WidgetId { pub fn id(&self) -> &WidgetId {
@@ -135,6 +135,6 @@ impl<'a, 'c> Painter<'a, 'c> {
} }
pub fn size_ctx(&mut self) -> SizeCtx<'_> { pub fn size_ctx(&mut self) -> SizeCtx<'_> {
self.state.size_ctx(self.id, self.region.size()) self.drawer.size_ctx(self.id, self.region.size())
} }
} }

116
core/src/ui/render_state.rs Normal file
View File

@@ -0,0 +1,116 @@
use crate::{
ActiveData, EventsLike, IdLike, PixelRegion, PrimitiveLayers, StrongWidget, WidgetId, Widgets,
ui::{cache::Cache, draw_state::Drawer},
util::{HashMap, Vec2},
};
pub struct UiRenderState {
pub active: HashMap<WidgetId, ActiveData>,
pub layers: PrimitiveLayers,
pub(super) output_size: Vec2,
pub cache: Cache,
old_root: Option<WidgetId>,
resized: bool,
}
impl UiRenderState {
pub fn new() -> Self {
Self {
active: Default::default(),
layers: Default::default(),
cache: Default::default(),
output_size: Vec2::ZERO,
old_root: None,
resized: false,
}
}
pub fn resize(&mut self, size: impl Into<Vec2>) {
self.output_size = size.into();
self.resized = true;
}
pub fn update<'a>(
&mut self,
root: impl Into<Option<&'a StrongWidget>>,
widgets: &mut Widgets,
events: &mut dyn EventsLike,
) {
// safety mechanism for memory leaks; might wanna return a result instead so user can
// decide whether to panic or not
if !widgets.waiting.is_empty() {
let len = widgets.waiting.len();
let all: Vec<_> = widgets
.waiting
.iter()
.map(|&w| format!("'{}' ({w:?})", widgets.label(w)))
.collect();
panic!(
"{len} widget(s) were never upgraded\n\
this is likely a memory leak; consider upgrading to strong if you plan on using it later\n\
weak widgets: {all:#?}"
);
}
if self.root_changed(root) {
Drawer::new(self, events).redraw_all();
self.old_root = root.into().map(|r| r.id());
} else if widgets.has_updates() {
Drawer::new(self, events).redraw_updates();
}
if self.resized {
self.resized = false;
Drawer::new(self, events).redraw_all();
}
}
pub fn root_changed<'a>(&self, root: impl Into<Option<&'a StrongWidget>>) -> bool {
root.into().map(|r| r.id()) != self.old_root
}
pub fn needs_redraw<'a>(
&self,
root: impl Into<Option<&'a StrongWidget>>,
widgets: &Widgets,
) -> bool {
self.root_changed(root) || widgets.has_updates()
}
pub fn active_widgets(&self) -> usize {
self.active.len()
}
pub fn debug(&self, widgets: &Widgets, label: &str) -> impl Iterator<Item = &ActiveData> {
self.active.iter().filter_map(move |(&id, inst)| {
let l = widgets.label(id);
if l == label { Some(inst) } else { None }
})
}
pub fn debug_layers(&self) {
for ((idx, depth), primitives) in self.layers.iter_depth() {
let indent = " ".repeat(depth * 2);
let len = primitives.instances().len();
print!("{indent}{idx}: {len} primitives");
if len >= 1 {
print!(" ({})", primitives.instances()[0].binding);
}
println!();
}
}
pub fn window_region(&self, id: &impl IdLike) -> Option<PixelRegion> {
let region = self.active.get(&id.id())?.region;
Some(region.to_px(self.output_size))
}
}
pub trait HasRoot {
fn set_root(&mut self, root: StrongWidget);
}
impl Default for UiRenderState {
fn default() -> Self {
Self::new()
}
}

View File

@@ -1,7 +1,7 @@
use std::{marker::Unsize, ops::CoerceUnsized, sync::mpsc::Sender}; use std::{marker::Unsize, ops::CoerceUnsized, sync::mpsc::Sender};
use crate::{ use crate::{
HasUi, Widget, UiRsc, Widget,
util::{RefCounter, SlotId}, util::{RefCounter, SlotId},
}; };
@@ -12,7 +12,7 @@ pub type WidgetId = SlotId;
/// a signal is sent to the owning UI to clean up the resources. /// a signal is sent to the owning UI to clean up the resources.
/// ///
/// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs? /// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs?
pub struct WidgetHandle<W: ?Sized = dyn Widget> { pub struct StrongWidget<W: ?Sized = dyn Widget> {
pub(super) id: WidgetId, pub(super) id: WidgetId,
counter: RefCounter, counter: RefCounter,
send: Sender<WidgetId>, send: Sender<WidgetId>,
@@ -21,24 +21,19 @@ pub struct WidgetHandle<W: ?Sized = dyn Widget> {
/// A weak handle to a widget. /// A weak handle to a widget.
/// Will not keep it alive, but can still be used for indexing like WidgetHandle. /// Will not keep it alive, but can still be used for indexing like WidgetHandle.
pub struct WidgetRef<W: ?Sized = dyn Widget> { pub struct WeakWidget<W: ?Sized = dyn Widget> {
pub(super) id: WidgetId, pub(super) id: WidgetId,
#[allow(unused)] #[allow(unused)]
ty: *const W, ty: *const W,
} }
pub struct WidgetHandles<W: ?Sized = dyn Widget> { impl<W: Widget + ?Sized + Unsize<dyn Widget>> StrongWidget<W> {
pub h: WidgetHandle<W>, pub fn any(self) -> StrongWidget<dyn Widget> {
pub r: WidgetRef<W>,
}
impl<W: Widget + ?Sized + Unsize<dyn Widget>> WidgetHandle<W> {
pub fn any(self) -> WidgetHandle<dyn Widget> {
self self
} }
} }
impl<W: ?Sized> WidgetHandle<W> { impl<W: ?Sized> StrongWidget<W> {
pub(crate) fn new(id: WidgetId, send: Sender<WidgetId>) -> Self { pub(crate) fn new(id: WidgetId, send: Sender<WidgetId>) -> Self {
Self { Self {
id, id,
@@ -56,18 +51,13 @@ impl<W: ?Sized> WidgetHandle<W> {
self.counter.refs() self.counter.refs()
} }
pub fn weak(&self) -> WidgetRef<W> { pub fn weak(&self) -> WeakWidget<W> {
let Self { ty, id, .. } = *self; let Self { ty, id, .. } = *self;
WidgetRef { ty, id } WeakWidget { ty, id }
}
pub fn handles(self) -> WidgetHandles<W> {
let r = self.weak();
WidgetHandles { h: self, r }
} }
} }
impl<W: ?Sized> WidgetRef<W> { impl<W: ?Sized> WeakWidget<W> {
pub(crate) fn new(id: WidgetId) -> Self { pub(crate) fn new(id: WidgetId) -> Self {
Self { id, ty: null_ptr() } Self { id, ty: null_ptr() }
} }
@@ -77,12 +67,12 @@ impl<W: ?Sized> WidgetRef<W> {
} }
#[track_caller] #[track_caller]
pub fn upgrade(self, ui: &mut impl HasUi) -> WidgetHandle<W> { pub fn upgrade(self, ui: &mut impl UiRsc) -> StrongWidget<W> {
ui.ui_mut().widgets.upgrade(self) ui.widgets_mut().upgrade(self)
} }
} }
impl<W: ?Sized> Drop for WidgetHandle<W> { impl<W: ?Sized> Drop for StrongWidget<W> {
fn drop(&mut self) { fn drop(&mut self) {
if self.counter.drop() { if self.counter.drop() {
let _ = self.send.send(self.id); let _ = self.send.send(self.id);
@@ -90,29 +80,29 @@ impl<W: ?Sized> Drop for WidgetHandle<W> {
} }
} }
pub trait WidgetIdFn<Rsc, W: ?Sized = dyn Widget>: FnOnce(&mut Rsc) -> WidgetRef<W> {} pub trait WidgetIdFn<Rsc, W: ?Sized = dyn Widget>: FnOnce(&mut Rsc) -> WeakWidget<W> {}
impl<Rsc, W: ?Sized, F: FnOnce(&mut Rsc) -> WidgetRef<W>> WidgetIdFn<Rsc, W> for F {} impl<Rsc, W: ?Sized, F: FnOnce(&mut Rsc) -> WeakWidget<W>> WidgetIdFn<Rsc, W> for F {}
pub trait IdLike { pub trait IdLike {
type Widget: ?Sized; type Widget: ?Sized;
fn id(&self) -> WidgetId; fn id(&self) -> WidgetId;
} }
impl<W: ?Sized> IdLike for &WidgetHandle<W> { impl<W: ?Sized> IdLike for &StrongWidget<W> {
type Widget = W; type Widget = W;
fn id(&self) -> WidgetId { fn id(&self) -> WidgetId {
self.id self.id
} }
} }
impl<W: ?Sized> IdLike for WidgetHandle<W> { impl<W: ?Sized> IdLike for StrongWidget<W> {
type Widget = W; type Widget = W;
fn id(&self) -> WidgetId { fn id(&self) -> WidgetId {
self.id self.id
} }
} }
impl<W: ?Sized> IdLike for WidgetRef<W> { impl<W: ?Sized> IdLike for WeakWidget<W> {
type Widget = W; type Widget = W;
fn id(&self) -> WidgetId { fn id(&self) -> WidgetId {
self.id self.id
@@ -126,38 +116,38 @@ impl IdLike for WidgetId {
} }
} }
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<WidgetHandle<U>> for WidgetHandle<T> {} impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<StrongWidget<U>> for StrongWidget<T> {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<WidgetRef<U>> for WidgetRef<T> {} impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<WeakWidget<U>> for WeakWidget<T> {}
impl<W: ?Sized> Clone for WidgetRef<W> { impl<W: ?Sized> Clone for WeakWidget<W> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
*self *self
} }
} }
impl<W: ?Sized> Copy for WidgetRef<W> {} impl<W: ?Sized> Copy for WeakWidget<W> {}
impl<W: ?Sized> PartialEq for WidgetRef<W> { impl<W: ?Sized> PartialEq for WeakWidget<W> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.id == other.id self.id == other.id
} }
} }
impl<W> PartialEq for WidgetHandle<W> { impl<W> PartialEq for StrongWidget<W> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.id == other.id self.id == other.id
} }
} }
impl<W> std::fmt::Debug for WidgetHandle<W> { impl<W> std::fmt::Debug for StrongWidget<W> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.id.fmt(f) self.id.fmt(f)
} }
} }
impl<'a, W: Widget + 'a, State: HasUi> FnOnce<(&'a mut State,)> for WidgetRef<W> { impl<'a, W: Widget + 'a, State: UiRsc> FnOnce<(&'a mut State,)> for WeakWidget<W> {
type Output = &'a mut W; type Output = &'a mut W;
extern "rust-call" fn call_once(self, args: (&'a mut State,)) -> Self::Output { extern "rust-call" fn call_once(self, args: (&'a mut State,)) -> Self::Output {
&mut args.0.ui_mut()[self] &mut args.0.widgets_mut()[self]
} }
} }
@@ -170,5 +160,5 @@ fn null_ptr<W: ?Sized>() -> *const W {
} }
} }
unsafe impl<W: ?Sized> Send for WidgetRef<W> {} unsafe impl<W: ?Sized> Send for WeakWidget<W> {}
unsafe impl<W: ?Sized> Sync for WidgetRef<W> {} unsafe impl<W: ?Sized> Sync for WeakWidget<W> {}

View File

@@ -1,20 +1,20 @@
use crate::HasUi; use crate::{HasRoot, UiRsc};
use super::*; use super::*;
use std::marker::Unsize; use std::marker::Unsize;
pub trait WidgetLike<Rsc: HasUi, Tag>: Sized { pub trait WidgetLike<Rsc: UiRsc, Tag>: Sized {
type Widget: Widget + ?Sized + Unsize<dyn Widget>; type Widget: Widget + ?Sized + Unsize<dyn Widget>;
fn add(self, rsc: &mut Rsc) -> WidgetRef<Self::Widget>; fn add(self, rsc: &mut Rsc) -> WeakWidget<Self::Widget>;
fn add_strong(self, rsc: &mut Rsc) -> WidgetHandle<Self::Widget> { fn add_strong(self, rsc: &mut Rsc) -> StrongWidget<Self::Widget> {
self.add(rsc).upgrade(rsc.ui_mut()) self.add(rsc).upgrade(rsc)
} }
fn with_id<W2>( fn with_id<W2>(
self, self,
f: impl FnOnce(&mut Rsc, WidgetRef<Self::Widget>) -> WidgetRef<W2>, f: impl FnOnce(&mut Rsc, WeakWidget<Self::Widget>) -> WeakWidget<W2>,
) -> impl WidgetIdFn<Rsc, W2> { ) -> impl WidgetIdFn<Rsc, W2> {
move |state| { move |state| {
let id = self.add(state); let id = self.add(state);
@@ -22,14 +22,12 @@ pub trait WidgetLike<Rsc: HasUi, Tag>: Sized {
} }
} }
fn set_root(self, state: &mut Rsc) { fn set_root(self, rsc: &mut Rsc)
let id = self.add(state); where
let ui = state.ui_mut(); Rsc: HasRoot,
ui.root = Some(id.upgrade(ui)); {
} let id = self.add_strong(rsc);
rsc.set_root(id);
fn handles(self, state: &mut Rsc) -> WidgetHandles<Self::Widget> {
self.add(state).upgrade(state.ui_mut()).handles()
} }
} }
@@ -50,12 +48,12 @@ macro_rules! impl_widget_arr {
impl_widget_arr!($n;$($W)*;$(${concat($W,Tag)})*); impl_widget_arr!($n;$($W)*;$(${concat($W,Tag)})*);
}; };
($n:expr;$($W:ident)*;$($Tag:ident)*) => { ($n:expr;$($W:ident)*;$($Tag:ident)*) => {
impl<Rsc: HasUi, $($W: WidgetLike<Rsc, $Tag>,$Tag,)*> WidgetArrLike<Rsc, $n, ($($Tag,)*)> for ($($W,)*) { impl<Rsc: UiRsc, $($W: WidgetLike<Rsc, $Tag>,$Tag,)*> WidgetArrLike<Rsc, $n, ($($Tag,)*)> for ($($W,)*) {
fn add(self, rsc: &mut Rsc) -> WidgetArr<$n> { fn add(self, rsc: &mut Rsc) -> WidgetArr<$n> {
#[allow(non_snake_case)] #[allow(non_snake_case)]
let ($($W,)*) = self; let ($($W,)*) = self;
WidgetArr::new( WidgetArr::new(
[$($W.add(rsc).upgrade(rsc.ui_mut()),)*], [$($W.add(rsc).upgrade(rsc),)*],
) )
} }
} }

View File

@@ -61,27 +61,27 @@ pub trait WidgetFn<State, W: Widget + ?Sized>: FnOnce(&mut State) -> W {}
impl<State, W: Widget + ?Sized, F: FnOnce(&mut State) -> W> WidgetFn<State, W> for F {} impl<State, W: Widget + ?Sized, F: FnOnce(&mut State) -> W> WidgetFn<State, W> for F {}
pub struct WidgetArr<const LEN: usize> { pub struct WidgetArr<const LEN: usize> {
pub arr: [WidgetHandle; LEN], pub arr: [StrongWidget; LEN],
} }
impl<const LEN: usize> WidgetArr<LEN> { impl<const LEN: usize> WidgetArr<LEN> {
pub fn new(arr: [WidgetHandle; LEN]) -> Self { pub fn new(arr: [StrongWidget; LEN]) -> Self {
Self { arr } Self { arr }
} }
} }
pub trait WidgetOption<State> { pub trait WidgetOption<State> {
fn get(self, state: &mut State) -> Option<WidgetHandle>; fn get(self, state: &mut State) -> Option<StrongWidget>;
} }
impl<State> WidgetOption<State> for () { impl<State> WidgetOption<State> for () {
fn get(self, _: &mut State) -> Option<WidgetHandle> { fn get(self, _: &mut State) -> Option<StrongWidget> {
None None
} }
} }
impl<State, F: FnOnce(&mut State) -> Option<WidgetHandle>> WidgetOption<State> for F { impl<State, F: FnOnce(&mut State) -> Option<StrongWidget>> WidgetOption<State> for F {
fn get(self, state: &mut State) -> Option<WidgetHandle> { fn get(self, state: &mut State) -> Option<StrongWidget> {
self(state) self(state)
} }
} }

View File

@@ -1,19 +1,19 @@
use super::*; use super::*;
use crate::HasUi; use crate::UiRsc;
use std::marker::Unsize; use std::marker::Unsize;
pub struct WidgetTag; pub struct WidgetTag;
impl<Rsc: HasUi, W: Widget> WidgetLike<Rsc, WidgetTag> for W { impl<Rsc: UiRsc, W: Widget> WidgetLike<Rsc, WidgetTag> for W {
type Widget = W; type Widget = W;
fn add(self, rsc: &mut Rsc) -> WidgetRef<W> { fn add(self, rsc: &mut Rsc) -> WeakWidget<W> {
rsc.ui_mut().widgets.add_weak(self) rsc.add_widget(self)
} }
} }
pub struct FnTag; pub struct FnTag;
impl<Rsc: HasUi, W: Widget, F: FnOnce(&mut Rsc) -> W> WidgetLike<Rsc, FnTag> for F { impl<Rsc: UiRsc, W: Widget, F: FnOnce(&mut Rsc) -> W> WidgetLike<Rsc, FnTag> for F {
type Widget = W; type Widget = W;
fn add(self, rsc: &mut Rsc) -> WidgetRef<W> { fn add(self, rsc: &mut Rsc) -> WeakWidget<W> {
self(rsc).add(rsc) self(rsc).add(rsc)
} }
} }
@@ -23,36 +23,38 @@ pub trait WidgetFnTrait<Rsc> {
fn run(self, rsc: &mut Rsc) -> Self::Widget; fn run(self, rsc: &mut Rsc) -> Self::Widget;
} }
pub struct FnTraitTag; pub struct FnTraitTag;
impl<Rsc: HasUi, T: WidgetFnTrait<Rsc>> WidgetLike<Rsc, FnTraitTag> for T { impl<Rsc: UiRsc, T: WidgetFnTrait<Rsc>> WidgetLike<Rsc, FnTraitTag> for T {
type Widget = T::Widget; type Widget = T::Widget;
#[track_caller] #[track_caller]
fn add(self, rsc: &mut Rsc) -> WidgetRef<T::Widget> { fn add(self, rsc: &mut Rsc) -> WeakWidget<T::Widget> {
self.run(rsc).add(rsc) self.run(rsc).add(rsc)
} }
} }
pub struct RefTag; pub struct RefTag;
impl<Rsc: HasUi, W: ?Sized + Widget + Unsize<dyn Widget>> WidgetLike<Rsc, RefTag> for WidgetRef<W> { impl<Rsc: UiRsc, W: ?Sized + Widget + Unsize<dyn Widget>> WidgetLike<Rsc, RefTag>
for WeakWidget<W>
{
type Widget = W; type Widget = W;
fn add(self, _: &mut Rsc) -> WidgetRef<W> { fn add(self, _: &mut Rsc) -> WeakWidget<W> {
self self
} }
} }
pub struct RefFnTag; pub struct RefFnTag;
impl<Rsc: HasUi, W: ?Sized + Widget + Unsize<dyn Widget>, F: FnOnce(&mut Rsc) -> WidgetRef<W>> impl<Rsc: UiRsc, W: ?Sized + Widget + Unsize<dyn Widget>, F: FnOnce(&mut Rsc) -> WeakWidget<W>>
WidgetLike<Rsc, RefFnTag> for F WidgetLike<Rsc, RefFnTag> for F
{ {
type Widget = W; type Widget = W;
fn add(self, rsc: &mut Rsc) -> WidgetRef<W> { fn add(self, rsc: &mut Rsc) -> WeakWidget<W> {
self(rsc) self(rsc)
} }
} }
pub struct ViewTag; pub struct ViewTag;
impl<Rsc: HasUi, V: WidgetView> WidgetLike<Rsc, ViewTag> for V { impl<Rsc: UiRsc, V: WidgetView> WidgetLike<Rsc, ViewTag> for V {
type Widget = V::Widget; type Widget = V::Widget;
fn add(self, _: &mut Rsc) -> WidgetRef<Self::Widget> { fn add(self, _: &mut Rsc) -> WeakWidget<Self::Widget> {
self.root() self.root()
} }
} }

View File

@@ -1,16 +1,16 @@
use std::marker::Unsize; use std::marker::Unsize;
use crate::{Widget, WidgetRef}; use crate::{Widget, WeakWidget};
pub trait WidgetView { pub trait WidgetView {
type Widget: Widget + ?Sized + Unsize<dyn Widget>; type Widget: Widget + ?Sized + Unsize<dyn Widget>;
fn root(&self) -> WidgetRef<Self::Widget>; fn root(&self) -> WeakWidget<Self::Widget>;
} }
pub trait HasWidget { pub trait HasWidget {
type Widget: Widget + ?Sized + Unsize<dyn Widget>; type Widget: Widget + ?Sized + Unsize<dyn Widget>;
} }
impl<W: Widget + Unsize<dyn Widget> + ?Sized> HasWidget for WidgetRef<W> { impl<W: Widget + Unsize<dyn Widget> + ?Sized> HasWidget for WeakWidget<W> {
type Widget = W; type Widget = W;
} }

View File

@@ -1,7 +1,7 @@
use std::sync::mpsc::Sender; use std::sync::mpsc::{Receiver, Sender, channel};
use crate::{ use crate::{
IdLike, Widget, WidgetData, WidgetHandle, WidgetId, WidgetRef, IdLike, StrongWidget, WeakWidget, Widget, WidgetData, WidgetId,
util::{DynBorrower, HashSet, SlotVec, forget_mut, to_mut}, util::{DynBorrower, HashSet, SlotVec, forget_mut, to_mut},
}; };
@@ -9,16 +9,19 @@ pub struct Widgets {
pub needs_redraw: HashSet<WidgetId>, pub needs_redraw: HashSet<WidgetId>,
vec: SlotVec<WidgetData>, vec: SlotVec<WidgetData>,
send: Sender<WidgetId>, send: Sender<WidgetId>,
recv: Receiver<WidgetId>,
pub(crate) waiting: HashSet<WidgetId>, pub(crate) waiting: HashSet<WidgetId>,
} }
impl Widgets { impl Widgets {
pub fn new(send: Sender<WidgetId>) -> Self { pub fn new() -> Self {
let (send, recv) = channel();
Self { Self {
needs_redraw: Default::default(), needs_redraw: Default::default(),
vec: Default::default(), vec: Default::default(),
waiting: Default::default(), waiting: Default::default(),
send, send,
recv,
} }
} }
@@ -61,25 +64,27 @@ impl Widgets {
self.get_dyn_mut(id.id())?.as_any_mut().downcast_mut() self.get_dyn_mut(id.id())?.as_any_mut().downcast_mut()
} }
pub fn add_strong<W: Widget>(&mut self, widget: W) -> WidgetHandle<W> { pub fn add_strong<W: Widget>(&mut self, widget: W) -> StrongWidget<W> {
let id = self.vec.add(WidgetData::new(widget)); let id = self.vec.add(WidgetData::new(widget));
WidgetHandle::new(id, self.send.clone()) StrongWidget::new(id, self.send.clone())
} }
pub fn add_weak<W: Widget>(&mut self, widget: W) -> WidgetRef<W> { pub fn add_weak<W: Widget>(&mut self, widget: W) -> WeakWidget<W> {
let id = self.vec.add(WidgetData::new(widget)); let id = self.vec.add(WidgetData::new(widget));
self.waiting.insert(id); self.waiting.insert(id);
WidgetRef::new(id) WeakWidget::new(id)
} }
#[track_caller] #[track_caller]
pub fn upgrade<W: ?Sized>(&mut self, rf: WidgetRef<W>) -> WidgetHandle<W> { pub fn upgrade<W: ?Sized>(&mut self, rf: WeakWidget<W>) -> StrongWidget<W> {
if !self.waiting.remove(&rf.id()) { if !self.waiting.remove(&rf.id()) {
let label = self.label(rf); let label = self.label(rf);
let id = rf.id(); let id = rf.id();
panic!("widget '{label}' ({id:?}) was already added\ncannot add a widget twice; consider creating two") panic!(
"widget '{label}' ({id:?}) was already added\ncannot add a widget twice; consider creating two"
)
} }
WidgetHandle::new(rf.id(), self.send.clone()) StrongWidget::new(rf.id(), self.send.clone())
} }
pub fn data(&self, id: impl IdLike) -> Option<&WidgetData> { pub fn data(&self, id: impl IdLike) -> Option<&WidgetData> {
@@ -90,14 +95,19 @@ impl Widgets {
&self.data(id.id()).unwrap().label &self.data(id.id()).unwrap().label
} }
/// useful for debugging
pub fn set_label(&mut self, id: impl IdLike, label: String) {
self.data_mut(id.id()).unwrap().label = label;
}
pub fn data_mut(&mut self, id: impl IdLike) -> Option<&mut WidgetData> { pub fn data_mut(&mut self, id: impl IdLike) -> Option<&mut WidgetData> {
self.vec.get_mut(id.id()) self.vec.get_mut(id.id())
} }
pub fn delete(&mut self, id: impl IdLike) { pub fn free(&mut self) {
self.vec.free(id.id()); for id in self.recv.try_iter() {
// not sure if there's any point in this self.vec.free(id.id());
// self.updates.remove(&id); }
} }
#[allow(clippy::len_without_is_empty)] #[allow(clippy::len_without_is_empty)]
@@ -106,4 +116,30 @@ impl Widgets {
} }
} }
impl Default for Widgets {
fn default() -> Self {
Self::new()
}
}
pub type WidgetWrapper<'a> = DynBorrower<'a, dyn Widget>; pub type WidgetWrapper<'a> = DynBorrower<'a, dyn Widget>;
impl<I: IdLike> std::ops::Index<I> for Widgets
where
I::Widget: Sized + Widget,
{
type Output = I::Widget;
fn index(&self, id: I) -> &Self::Output {
self.get(&id).unwrap()
}
}
impl<I: IdLike> std::ops::IndexMut<I> for Widgets
where
I::Widget: Sized + Widget,
{
fn index_mut(&mut self, id: I) -> &mut Self::Output {
self.get_mut(&id).unwrap()
}
}

View File

@@ -12,7 +12,7 @@ fn main() {
#[derive(DefaultUiState)] #[derive(DefaultUiState)]
pub struct Client { pub struct Client {
ui_state: DefaultUiState, ui_state: DefaultUiState,
info: WidgetRef<Text>, info: WeakWidget<Text>,
} }
impl DefaultAppState for Client { impl DefaultAppState for Client {
@@ -147,7 +147,7 @@ impl DefaultAppState for Client {
let main = WidgetPtr::new().add(rsc); let main = WidgetPtr::new().add(rsc);
let vals = Rc::new(RefCell::new((0, Vec::new()))); let vals = Rc::new(RefCell::new((0, Vec::new())));
let mut switch_button = |color, to: WidgetRef, label| { let mut switch_button = |color, to: WeakWidget, label| {
let to = to.upgrade(rsc); let to = to.upgrade(rsc);
let vec = &mut vals.borrow_mut().1; let vec = &mut vals.borrow_mut().1;
let i = vec.len(); let i = vec.len();

View File

@@ -14,7 +14,7 @@ type Rsc = DefaultRsc<State>;
#[derive(Clone, Copy, WidgetView)] #[derive(Clone, Copy, WidgetView)]
struct Test { struct Test {
#[root] #[root]
root: WidgetRef<Rect>, root: WeakWidget<Rect>,
} }
impl Test { impl Test {

View File

@@ -8,9 +8,9 @@ impl<Rsc: HasEvents, W: Widget + 'static> WidgetAttr<Rsc, W> for Selector
where where
Rsc::State: HasDefaultUiState, Rsc::State: HasDefaultUiState,
{ {
type Input = WidgetRef<TextEdit>; type Input = WeakWidget<TextEdit>;
fn run(rsc: &mut Rsc, container: WidgetRef<W>, id: Self::Input) { fn run(rsc: &mut Rsc, container: WeakWidget<W>, id: Self::Input) {
rsc.register_event(container, CursorSense::click_or_drag(), move |ctx, rsc| { rsc.register_event(container, CursorSense::click_or_drag(), move |ctx, rsc| {
let region = rsc.ui().window_region(&id).unwrap(); let region = rsc.ui().window_region(&id).unwrap();
let id_pos = region.top_left; let id_pos = region.top_left;
@@ -30,7 +30,7 @@ where
{ {
type Input = (); type Input = ();
fn run(rsc: &mut Rsc, id: WidgetRef<TextEdit>, _: Self::Input) { fn run(rsc: &mut Rsc, id: WeakWidget<TextEdit>, _: Self::Input) {
rsc.register_event(id, CursorSense::click_or_drag(), move |ctx, rsc| { rsc.register_event(id, CursorSense::click_or_drag(), move |ctx, rsc| {
select( select(
rsc, rsc,
@@ -47,7 +47,7 @@ where
fn select( fn select(
rsc: &mut impl HasUi, rsc: &mut impl HasUi,
state: &mut impl HasDefaultUiState, state: &mut impl HasDefaultUiState,
id: WidgetRef<TextEdit>, id: WeakWidget<TextEdit>,
pos: Vec2, pos: Vec2,
size: Vec2, size: Vec2,
dragging: bool, dragging: bool,

View File

@@ -32,7 +32,7 @@ pub type Proxy<Event> = EventLoopProxy<Event>;
pub struct DefaultUiState { pub struct DefaultUiState {
pub renderer: UiRenderer, pub renderer: UiRenderer,
pub input: Input, pub input: Input,
pub focus: Option<WidgetRef<TextEdit>>, pub focus: Option<WeakWidget<TextEdit>>,
pub clipboard: Clipboard, pub clipboard: Clipboard,
pub window: Arc<Window>, pub window: Arc<Window>,
pub ime: usize, pub ime: usize,

View File

@@ -75,7 +75,7 @@ impl<
} }
pub struct AsyncEventIdCtx<Rsc: HasEvents, Data, W: ?Sized> { pub struct AsyncEventIdCtx<Rsc: HasEvents, Data, W: ?Sized> {
pub widget: WidgetRef<W>, pub widget: WeakWidget<W>,
pub data: Data, pub data: Data,
pub task: TaskCtx<Rsc>, pub task: TaskCtx<Rsc>,
} }

View File

@@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
pub struct Masked { pub struct Masked {
pub inner: WidgetHandle, pub inner: StrongWidget,
} }
impl Widget for Masked { impl Widget for Masked {

View File

@@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
pub struct Aligned { pub struct Aligned {
pub inner: WidgetHandle, pub inner: StrongWidget,
pub align: Align, pub align: Align,
} }

View File

@@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
pub struct LayerOffset { pub struct LayerOffset {
pub inner: WidgetHandle, pub inner: StrongWidget,
pub offset: usize, pub offset: usize,
} }

View File

@@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
pub struct MaxSize { pub struct MaxSize {
pub inner: WidgetHandle, pub inner: StrongWidget,
pub x: Option<Len>, pub x: Option<Len>,
pub y: Option<Len>, pub y: Option<Len>,
} }

View File

@@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
pub struct Offset { pub struct Offset {
pub inner: WidgetHandle, pub inner: StrongWidget,
pub amt: UiVec2, pub amt: UiVec2,
} }

View File

@@ -2,7 +2,7 @@ use crate::prelude::*;
pub struct Pad { pub struct Pad {
pub padding: Padding, pub padding: Padding,
pub inner: WidgetHandle, pub inner: StrongWidget,
} }
impl Widget for Pad { impl Widget for Pad {

View File

@@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
pub struct Scroll { pub struct Scroll {
inner: WidgetHandle, inner: StrongWidget,
axis: Axis, axis: Axis,
amt: f32, amt: f32,
snap_end: bool, snap_end: bool,
@@ -41,7 +41,7 @@ impl Widget for Scroll {
} }
impl Scroll { impl Scroll {
pub fn new(inner: WidgetHandle, axis: Axis) -> Self { pub fn new(inner: StrongWidget, axis: Axis) -> Self {
Self { Self {
inner, inner,
axis, axis,

View File

@@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
pub struct Sized { pub struct Sized {
pub inner: WidgetHandle, pub inner: StrongWidget,
pub x: Option<Len>, pub x: Option<Len>,
pub y: Option<Len>, pub y: Option<Len>,
} }

View File

@@ -2,7 +2,7 @@ use crate::prelude::*;
use std::marker::PhantomData; use std::marker::PhantomData;
pub struct Span { pub struct Span {
pub children: Vec<WidgetHandle>, pub children: Vec<StrongWidget>,
pub dir: Dir, pub dir: Dir,
pub gap: f32, pub gap: f32,
} }
@@ -62,11 +62,11 @@ impl Span {
self self
} }
pub fn push(&mut self, w: WidgetHandle) { pub fn push(&mut self, w: StrongWidget) {
self.children.push(w); self.children.push(w);
} }
pub fn pop(&mut self) -> Option<WidgetHandle> { pub fn pop(&mut self) -> Option<StrongWidget> {
self.children.pop() self.children.pop()
} }
@@ -193,7 +193,7 @@ impl<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag>
} }
impl std::ops::Deref for Span { impl std::ops::Deref for Span {
type Target = Vec<WidgetHandle>; type Target = Vec<StrongWidget>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.children &self.children

View File

@@ -3,7 +3,7 @@ use std::marker::PhantomData;
use crate::prelude::*; use crate::prelude::*;
pub struct Stack { pub struct Stack {
pub children: Vec<WidgetHandle>, pub children: Vec<StrongWidget>,
pub size: StackSize, pub size: StackSize,
} }

View File

@@ -2,7 +2,7 @@ use crate::prelude::*;
use std::marker::{Sized, Unsize}; use std::marker::{Sized, Unsize};
pub struct WidgetPtr { pub struct WidgetPtr {
pub inner: Option<WidgetHandle>, pub inner: Option<StrongWidget>,
} }
impl Widget for WidgetPtr { impl Widget for WidgetPtr {
@@ -38,14 +38,14 @@ impl WidgetPtr {
inner: Default::default(), inner: Default::default(),
} }
} }
pub fn set<W: ?Sized + Unsize<dyn Widget>>(&mut self, to: WidgetHandle<W>) { pub fn set<W: ?Sized + Unsize<dyn Widget>>(&mut self, to: StrongWidget<W>) {
self.inner = Some(to) self.inner = Some(to)
} }
pub fn replace<W: ?Sized + Unsize<dyn Widget>>( pub fn replace<W: ?Sized + Unsize<dyn Widget>>(
&mut self, &mut self,
to: WidgetHandle<W>, to: StrongWidget<W>,
) -> Option<WidgetHandle> { ) -> Option<StrongWidget> {
self.inner.replace(to) self.inner.replace(to)
} }
} }

View File

@@ -22,11 +22,11 @@ pub struct TextView {
// cache // cache
tex: Option<RenderedText>, tex: Option<RenderedText>,
width: Option<f32>, width: Option<f32>,
pub hint: Option<WidgetHandle>, pub hint: Option<StrongWidget>,
} }
impl TextView { impl TextView {
pub fn new(buf: TextBuffer, attrs: TextAttrs, hint: Option<WidgetHandle>) -> Self { pub fn new(buf: TextBuffer, attrs: TextAttrs, hint: Option<StrongWidget>) -> Self {
Self { Self {
attrs: attrs.into(), attrs: attrs.into(),
buf: buf.into(), buf: buf.into(),

View File

@@ -125,7 +125,7 @@ widget_trait! {
|state| self.add(state) |state| self.add(state)
} }
fn set_ptr(self, ptr: WidgetRef<WidgetPtr>, state: &mut Rsc) { fn set_ptr(self, ptr: WeakWidget<WidgetPtr>, state: &mut Rsc) {
let id = self.add_strong(state); let id = self.add_strong(state);
state.ui_mut()[ptr].inner = Some(id); state.ui_mut()[ptr].inner = Some(id);
} }