From 0b8a93c5ce22bb45a00874bba81d5bae1f7a989f Mon Sep 17 00:00:00 2001 From: shadow cat Date: Mon, 15 Dec 2025 21:50:53 -0500 Subject: [PATCH] RE ADD CONTEXT --- core/src/attr.rs | 15 +- core/src/event/ctx.rs | 34 ++- core/src/event/manager.rs | 79 ++++--- core/src/event/mod.rs | 10 +- core/src/painter.rs | 67 +++--- core/src/render/mod.rs | 2 +- core/src/ui.rs | 46 ++-- core/src/widget/data.rs | 8 +- core/src/widget/handle.rs | 82 ++++--- core/src/widget/like.rs | 28 +-- core/src/widget/mod.rs | 42 ++-- core/src/widget/tag.rs | 25 ++- core/src/widget/widgets.rs | 42 ++-- examples/minimal.rs | 18 +- macro/src/lib.rs | 128 ++++++++++- src/bin/test/main.rs | 364 +++++++++++++++++--------------- src/default/attr.rs | 49 ++--- src/default/input.rs | 2 +- src/default/mod.rs | 114 +++++----- src/default/render.rs | 2 +- src/default/sense.rs | 33 ++- src/event.rs | 35 ++- src/widget/image.rs | 10 +- src/widget/mask.rs | 12 +- src/widget/position/align.rs | 12 +- src/widget/position/layer.rs | 12 +- src/widget/position/max_size.rs | 16 +- src/widget/position/offset.rs | 12 +- src/widget/position/pad.rs | 12 +- src/widget/position/scroll.rs | 16 +- src/widget/position/sized.rs | 16 +- src/widget/position/span.rs | 42 ++-- src/widget/position/stack.rs | 28 +-- src/widget/ptr.rs | 34 +-- src/widget/rect.rs | 8 +- src/widget/text/build.rs | 54 +++-- src/widget/text/edit.rs | 36 ++-- src/widget/text/mod.rs | 40 ++-- src/widget/trait_fns.rs | 53 ++--- 39 files changed, 925 insertions(+), 713 deletions(-) diff --git a/core/src/attr.rs b/core/src/attr.rs index 47ab390..8151669 100644 --- a/core/src/attr.rs +++ b/core/src/attr.rs @@ -1,16 +1,19 @@ use crate::{Ui, WidgetIdFn, WidgetLike, WidgetRef}; -pub trait WidgetAttr { +pub trait WidgetAttr { type Input; - fn run(ui: &mut Ui, id: WidgetRef, input: Self::Input); + fn run(ui: &mut Ui, id: WidgetRef, input: Self::Input); } -pub trait Attrable { - fn attr>(self, input: A::Input) -> impl WidgetIdFn; +pub trait Attrable { + fn attr>(self, input: A::Input) -> impl WidgetIdFn; } -impl, Tag> Attrable for WL { - fn attr>(self, input: A::Input) -> impl WidgetIdFn { +impl, Tag> Attrable for WL { + fn attr>( + self, + input: A::Input, + ) -> impl WidgetIdFn { |ui| { let id = self.add(ui); A::run(ui, id.weak(), input); diff --git a/core/src/event/ctx.rs b/core/src/event/ctx.rs index fb10503..d002714 100644 --- a/core/src/event/ctx.rs +++ b/core/src/event/ctx.rs @@ -1,36 +1,34 @@ -use std::ops::{Index, IndexMut}; +use std::ops::{Deref, DerefMut}; -use crate::{Ui, Widget, WidgetRef}; +use crate::{HasUi, Ui, Widget, WidgetRef}; -pub struct EventCtx<'a, Ctx, Data> { - pub ui: &'a mut Ui, - pub state: &'a mut Ctx, +pub struct EventCtx<'a, State, Data> { + pub state: &'a mut State, pub data: &'a mut Data, } -pub struct EventIdCtx<'a, Ctx, Data, W: ?Sized> { - pub widget: WidgetRef, - pub ui: &'a mut Ui, - pub state: &'a mut Ctx, +pub struct EventIdCtx<'a, State, Data, W: ?Sized> { + pub widget: WidgetRef, + pub state: &'a mut State, pub data: &'a mut Data, } -impl<'a, Ctx, Data, W2, W: Widget> Index> for EventIdCtx<'a, Ctx, Data, W2> { - type Output = W; +impl Deref for EventIdCtx<'_, State, Data, W> { + type Target = Ui; - fn index(&self, index: WidgetRef) -> &Self::Output { - &self.ui[index] + fn deref(&self) -> &Self::Target { + self.state.ui_ref() } } -impl<'a, Ctx, Data, W2, W: Widget> IndexMut> for EventIdCtx<'a, Ctx, Data, W2> { - fn index_mut(&mut self, index: WidgetRef) -> &mut Self::Output { - &mut self.ui[index] +impl DerefMut for EventIdCtx<'_, State, Data, W> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.state.ui() } } -impl<'a, Ctx, Data, W: Widget> EventIdCtx<'a, Ctx, Data, W> { +impl<'a, State: HasUi + 'static, Data, W: Widget> EventIdCtx<'a, State, Data, W> { pub fn widget(&mut self) -> &mut W { - &mut self.ui[self.widget] + &mut self.state.ui()[self.widget] } } diff --git a/core/src/event/manager.rs b/core/src/event/manager.rs index 5cc37e9..4ba2a82 100644 --- a/core/src/event/manager.rs +++ b/core/src/event/manager.rs @@ -1,30 +1,37 @@ use crate::{ - ActiveData, Event, EventCtx, EventFn, EventLike, IdLike, LayerId, Ui, WidgetData, WidgetId, + ActiveData, Event, EventCtx, EventFn, EventLike, HasUi, IdLike, LayerId, WidgetData, WidgetId, util::{HashMap, TypeMap}, }; use std::{any::TypeId, rc::Rc}; -#[derive(Default)] -pub struct EventManager { - types: TypeMap, +pub struct EventManager { + types: TypeMap>, } -impl EventManager { - pub fn get_type(&mut self) -> &mut TypeEventManager { +impl Default for EventManager { + fn default() -> Self { + Self { + types: Default::default(), + } + } +} + +impl EventManager { + pub fn get_type(&mut self) -> &mut TypeEventManager { self.types.type_or_default() } - pub fn register( + pub fn register, E: EventLike>( &mut self, id: I, event: E, - f: impl for<'a> EventFn::Data<'a>>, + f: impl for<'a> EventFn::Data<'a>>, ) { - self.get_type::().register(id, event, f); + self.get_type::().register(id, event, f); } - pub fn type_key() -> TypeId { - TypeId::of::>() + pub fn type_key() -> TypeId { + TypeId::of::>() } pub fn remove(&mut self, id: WidgetId) { @@ -33,33 +40,33 @@ impl EventManager { } } - pub fn draw(&mut self, data: &WidgetData, active: &ActiveData) { + pub fn draw(&mut self, data: &WidgetData, active: &ActiveData) { for t in &data.event_mgrs { self.types.get_mut(t).unwrap().draw(active); } } - pub fn undraw(&mut self, data: &WidgetData, active: &ActiveData) { + pub fn undraw(&mut self, data: &WidgetData, active: &ActiveData) { for t in &data.event_mgrs { self.types.get_mut(t).unwrap().undraw(active); } } } -pub trait EventManagerLike { +pub trait EventManagerLike { fn remove(&mut self, id: WidgetId); fn draw(&mut self, data: &ActiveData); fn undraw(&mut self, data: &ActiveData); } -type EventData = (E, Rc EventFn::Data<'a>>>); -pub struct TypeEventManager { +type EventData = (E, Rc EventFn::Data<'a>>>); +pub struct TypeEventManager { // TODO: reduce visiblity!! pub active: HashMap>, - map: HashMap>>, + map: HashMap>>, } -impl EventManagerLike for TypeEventManager { +impl EventManagerLike for TypeEventManager { fn remove(&mut self, id: WidgetId) { self.map.remove(&id); for layer in self.active.values_mut() { @@ -80,7 +87,7 @@ impl EventManagerLike for TypeEventManager { } } -impl Default for TypeEventManager { +impl Default for TypeEventManager { fn default() -> Self { Self { active: Default::default(), @@ -89,12 +96,12 @@ impl Default for TypeEventManager { } } -impl TypeEventManager { +impl TypeEventManager { fn register( &mut self, - id: impl IdLike, + id: impl IdLike, event: impl EventLike, - f: impl for<'a> EventFn>, + f: impl for<'a> EventFn>, ) { let event = event.into_event(); self.map @@ -105,14 +112,13 @@ impl TypeEventManager { pub fn run_fn<'a>( &mut self, - id: impl IdLike, - ) -> impl for<'b> FnOnce(EventCtx>) + 'a { + id: impl IdLike, + ) -> impl for<'b> FnOnce(EventCtx>) + 'a { let fs = self.map.get(&id.id()).cloned().unwrap_or_default(); move |ctx| { for (e, f) in fs { if e.should_run(ctx.data) { f(EventCtx { - ui: ctx.ui, state: ctx.state, data: ctx.data, }) @@ -122,18 +128,21 @@ impl TypeEventManager { } } -impl Ui { - pub fn run_event( +pub trait HasEvents: Sized + HasUi { + fn run_event( &mut self, - ctx: &mut Ctx, - id: impl IdLike, + id: impl IdLike, + data: &mut ::Data<'_>, + ); +} + +impl HasEvents for State { + fn run_event( + &mut self, + id: impl IdLike, data: &mut ::Data<'_>, ) { - let f = self.data.events.get_type::().run_fn(id); - f(EventCtx { - ui: self, - state: ctx, - data, - }) + let f = self.ui().data.events.get_type::().run_fn(id); + f(EventCtx { state: self, data }) } } diff --git a/core/src/event/mod.rs b/core/src/event/mod.rs index c673159..4edcdb6 100644 --- a/core/src/event/mod.rs +++ b/core/src/event/mod.rs @@ -26,11 +26,11 @@ impl EventLike for E { } } -pub trait EventFn: Fn(EventCtx) + 'static {} -impl) + 'static, Ctx, Data> EventFn for F {} +pub trait EventFn: Fn(EventCtx) + 'static {} +impl) + 'static, Data> EventFn for F {} -pub trait WidgetEventFn: Fn(EventIdCtx) + 'static {} -impl) + 'static, Ctx, Data, W: ?Sized> WidgetEventFn - for F +pub trait WidgetEventFn: Fn(EventIdCtx) + 'static {} +impl) + 'static, Data, W: ?Sized> + WidgetEventFn for F { } diff --git a/core/src/painter.rs b/core/src/painter.rs index 8d37277..8830a82 100644 --- a/core/src/painter.rs +++ b/core/src/painter.rs @@ -6,8 +6,8 @@ use crate::{ }; /// makes your surfaces look pretty -pub struct Painter<'a, 'c> { - ctx: &'a mut PainterCtx<'c>, +pub struct Painter<'a, 'c, State> { + ctx: &'a mut PainterCtx<'c, State>, region: UiRegion, mask: MaskIdx, textures: Vec, @@ -20,15 +20,15 @@ pub struct Painter<'a, 'c> { } /// context for a painter; lets you draw and redraw widgets -struct PainterCtx<'a> { - pub widgets: &'a Widgets, +struct PainterCtx<'a, State> { + pub widgets: &'a Widgets, pub active: &'a mut HashMap, pub layers: &'a mut PrimitiveLayers, pub textures: &'a mut Textures, pub masks: &'a mut TrackedArena, pub text: &'a mut TextData, pub output_size: Vec2, - pub events: &'a mut EventManager, + pub events: &'a mut EventManager, pub cache_width: HashMap, pub cache_height: HashMap, pub needs_redraw: &'a mut HashSet, @@ -58,20 +58,35 @@ pub struct ActiveData { } /// data to be stored in Ui to create PainterCtxs easily -#[derive(Default)] -pub struct PainterData { - pub widgets: Widgets, +pub struct PainterData { + pub widgets: Widgets, pub active: HashMap, pub layers: PrimitiveLayers, pub textures: Textures, pub text: TextData, pub output_size: Vec2, - pub events: EventManager, + pub events: EventManager, pub px_dependent: HashSet, pub masks: TrackedArena, } -impl<'a> PainterCtx<'a> { +impl Default for PainterData { + fn default() -> Self { + Self { + widgets: Default::default(), + active: Default::default(), + layers: Default::default(), + textures: Default::default(), + text: Default::default(), + output_size: Default::default(), + events: Default::default(), + px_dependent: Default::default(), + masks: Default::default(), + } + } +} + +impl<'a, State: 'static> PainterCtx<'a, State> { /// redraws a widget that's currently active (drawn) /// can be called on something already drawn or removed, /// will just return if so @@ -317,8 +332,8 @@ impl<'a> PainterCtx<'a> { } } -impl PainterData { - fn ctx<'a>(&'a mut self, needs_redraw: &'a mut HashSet) -> PainterCtx<'a> { +impl PainterData { + fn ctx<'a>(&'a mut self, needs_redraw: &'a mut HashSet) -> PainterCtx<'a, State> { PainterCtx { widgets: &self.widgets, active: &mut self.active, @@ -351,7 +366,7 @@ impl PainterData { } } -impl<'a, 'c> Painter<'a, 'c> { +impl<'a, 'c, State: 'static> Painter<'a, 'c, State> { fn primitive_at(&mut self, primitive: P, region: UiRegion) { let h = self.ctx.layers.write( self.layer, @@ -384,17 +399,17 @@ impl<'a, 'c> Painter<'a, 'c> { } /// Draws a widget within this widget's region. - pub fn widget(&mut self, id: &WidgetHandle) { + pub fn widget(&mut self, id: &WidgetHandle) { self.widget_at(id, self.region); } /// Draws a widget somewhere within this one. /// Useful for drawing child widgets in select areas. - pub fn widget_within(&mut self, id: &WidgetHandle, region: UiRegion) { + pub fn widget_within(&mut self, id: &WidgetHandle, region: UiRegion) { self.widget_at(id, region.within(&self.region)); } - fn widget_at(&mut self, id: &WidgetHandle, region: UiRegion) { + fn widget_at(&mut self, id: &WidgetHandle, region: UiRegion) { self.children.push(id.id()); self.ctx .draw_inner(self.layer, id.id(), region, Some(self.id), self.mask, None); @@ -424,18 +439,18 @@ impl<'a, 'c> Painter<'a, 'c> { self.region } - pub fn size(&mut self, id: &WidgetHandle) -> Size { + pub fn size(&mut self, id: &WidgetHandle) -> Size { self.size_ctx().size(id) } - pub fn len_axis(&mut self, id: &WidgetHandle, axis: Axis) -> Len { + pub fn len_axis(&mut self, id: &WidgetHandle, axis: Axis) -> Len { match axis { Axis::X => self.size_ctx().width(id), Axis::Y => self.size_ctx().height(id), } } - pub fn size_ctx(&mut self) -> SizeCtx<'_> { + pub fn size_ctx(&mut self) -> SizeCtx<'_, State> { SizeCtx { text: self.ctx.text, textures: self.ctx.textures, @@ -480,11 +495,11 @@ impl<'a, 'c> Painter<'a, 'c> { } } -pub struct SizeCtx<'a> { +pub struct SizeCtx<'a, State> { pub text: &'a mut TextData, pub textures: &'a mut Textures, source: WidgetId, - widgets: &'a Widgets, + widgets: &'a Widgets, cache_width: &'a mut HashMap, cache_height: &'a mut HashMap, checked_width: &'a mut HashMap, @@ -495,7 +510,7 @@ pub struct SizeCtx<'a> { id: WidgetId, } -impl SizeCtx<'_> { +impl SizeCtx<'_, State> { pub fn id(&self) -> &WidgetId { &self.id } @@ -547,22 +562,22 @@ impl SizeCtx<'_> { len } - pub fn width(&mut self, id: &WidgetHandle) -> Len { + pub fn width(&mut self, id: &WidgetHandle) -> Len { self.width_inner(id.id()) } - pub fn height(&mut self, id: &WidgetHandle) -> Len { + pub fn height(&mut self, id: &WidgetHandle) -> Len { self.height_inner(id.id()) } - pub fn len_axis(&mut self, id: &WidgetHandle, axis: Axis) -> Len { + pub fn len_axis(&mut self, id: &WidgetHandle, axis: Axis) -> Len { match axis { Axis::X => self.width(id), Axis::Y => self.height(id), } } - pub fn size(&mut self, id: &WidgetHandle) -> Size { + pub fn size(&mut self, id: &WidgetHandle) -> Size { Size { x: self.width(id), y: self.height(id), diff --git a/core/src/render/mod.rs b/core/src/render/mod.rs index 6d43627..1df79f3 100644 --- a/core/src/render/mod.rs +++ b/core/src/render/mod.rs @@ -59,7 +59,7 @@ 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 Ui) { self.active.clear(); for (i, primitives) in ui.data.layers.iter_mut() { self.active.push(i); diff --git a/core/src/ui.rs b/core/src/ui.rs index a5fcdff..7e3ec09 100644 --- a/core/src/ui.rs +++ b/core/src/ui.rs @@ -8,35 +8,43 @@ use std::{ sync::mpsc::{Receiver, Sender, channel}, }; -pub struct Ui { +pub struct Ui { // TODO: make this at least pub(super) - pub data: PainterData, - root: Option, + pub data: PainterData, + root: Option>, recv: Receiver, pub(super) send: Sender, full_redraw: bool, resized: bool, } -impl Ui { - pub fn add(&mut self, w: impl WidgetLike) -> WidgetHandle { +pub trait HasUi: Sized { + fn ui_ref(&self) -> &Ui; + fn ui(&mut self) -> &mut Ui; +} + +impl Ui { + pub fn add, Tag>( + &mut self, + w: impl WidgetLike, + ) -> WidgetHandle { w.add(self) } /// useful for debugging - pub fn set_label(&mut self, id: impl IdLike, label: String) { + pub fn set_label(&mut self, id: impl IdLike, label: String) { self.data.widgets.data_mut(id.id()).unwrap().label = label; } - pub fn label(&self, id: impl IdLike) -> &String { + pub fn label(&self, id: impl IdLike) -> &String { &self.data.widgets.data(id.id()).unwrap().label } - pub fn add_widget(&mut self, w: W) -> WidgetHandle { + pub fn add_widget>(&mut self, w: W) -> WidgetHandle { WidgetHandle::new(self.data.widgets.add(w), self.send.clone()) } - pub fn set_root(&mut self, w: impl WidgetLike) { + pub fn set_root(&mut self, w: impl WidgetLike) { self.root = Some(w.add(self)); self.full_redraw = true; } @@ -45,14 +53,14 @@ impl Ui { Self::default() } - pub fn get(&self, id: &I) -> Option<&I::Widget> + pub fn get>(&self, id: &I) -> Option<&I::Widget> where I::Widget: Sized, { self.data.widgets.get(id) } - pub fn get_mut(&mut self, id: &I) -> Option<&mut I::Widget> + pub fn get_mut>(&mut self, id: &I) -> Option<&mut I::Widget> where I::Widget: Sized, { @@ -63,11 +71,11 @@ impl Ui { self.data.textures.add(image) } - pub fn register_event( + pub fn register_event( &mut self, - id: impl IdLike, + id: impl IdLike, event: E, - f: impl for<'a> EventFn::Data<'a>>, + f: impl for<'a> EventFn::Data<'a>>, ) { self.data.events.register(id.id(), event, f); self.data @@ -75,7 +83,7 @@ impl Ui { .data_mut(id) .unwrap() .event_mgrs - .insert(EventManager::type_key::()); + .insert(EventManager::::type_key::()); } pub fn resize(&mut self, size: impl Into) { @@ -148,7 +156,7 @@ impl Ui { } } - pub fn window_region(&self, id: &impl IdLike) -> Option { + pub fn window_region(&self, id: &impl IdLike) -> Option { let region = self.data.active.get(&id.id())?.region; Some(region.to_px(self.data.output_size)) } @@ -161,7 +169,7 @@ impl Ui { } } -impl Index for Ui +impl> Index for Ui where I::Widget: Sized, { @@ -172,7 +180,7 @@ where } } -impl IndexMut for Ui +impl> IndexMut for Ui where I::Widget: Sized, { @@ -181,7 +189,7 @@ where } } -impl Default for Ui { +impl Default for Ui { fn default() -> Self { let (send, recv) = channel(); Self { diff --git a/core/src/widget/data.rs b/core/src/widget/data.rs index 68ef4f3..b6481a7 100644 --- a/core/src/widget/data.rs +++ b/core/src/widget/data.rs @@ -2,16 +2,16 @@ use std::any::TypeId; use crate::{Widget, util::HashSet}; -pub struct WidgetData { - pub widget: Box, +pub struct WidgetData { + pub widget: Box>, pub label: String, pub event_mgrs: HashSet, /// dynamic borrow checking pub borrowed: bool, } -impl WidgetData { - pub fn new(widget: W) -> Self { +impl WidgetData { + pub fn new>(widget: W) -> Self { let mut label = std::any::type_name::().to_string(); if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) { label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1; diff --git a/core/src/widget/handle.rs b/core/src/widget/handle.rs index 6d61c13..49f7c3a 100644 --- a/core/src/widget/handle.rs +++ b/core/src/widget/handle.rs @@ -1,4 +1,9 @@ -use std::{marker::Unsize, mem::MaybeUninit, ops::CoerceUnsized, sync::mpsc::Sender}; +use std::{ + marker::{PhantomData, Unsize}, + mem::MaybeUninit, + ops::CoerceUnsized, + sync::mpsc::Sender, +}; use crate::{ Ui, Widget, @@ -12,43 +17,46 @@ pub type WidgetId = SlotId; /// 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? -pub struct WidgetHandle { +pub struct WidgetHandle> { pub(super) id: WidgetId, counter: RefCounter, send: Sender, ty: *const W, + state: PhantomData, } /// A weak handle to a widget. /// Will not keep it alive, but can still be used for indexing like WidgetHandle. -pub struct WidgetRef { +pub struct WidgetRef> { pub(super) id: WidgetId, #[allow(unused)] ty: *const W, + state: PhantomData, } -pub struct WidgetHandles { - pub h: WidgetHandle, - pub r: WidgetRef, +pub struct WidgetHandles> { + pub h: WidgetHandle, + pub r: WidgetRef, } -impl> WidgetHandle { - pub fn any(self) -> WidgetHandle { +impl + ?Sized + Unsize>> WidgetHandle { + pub fn any(self) -> WidgetHandle> { self } } -impl WidgetHandle { +impl WidgetHandle { pub(crate) fn new(id: WidgetId, send: Sender) -> Self { Self { id, counter: RefCounter::new(), send, ty: unsafe { MaybeUninit::zeroed().assume_init() }, + state: PhantomData, } } - pub fn as_any(&self) -> &WidgetHandle { + pub fn as_any(&self) -> &WidgetHandle> { // SAFETY: self is repr(C) and generic only used for phantom data unsafe { std::mem::transmute(self) } } @@ -61,24 +69,28 @@ impl WidgetHandle { self.counter.refs() } - pub fn weak(&self) -> WidgetRef { + pub fn weak(&self) -> WidgetRef { let Self { ty, id, .. } = *self; - WidgetRef { ty, id } + WidgetRef { + ty, + id, + state: PhantomData, + } } - pub fn handles(self) -> WidgetHandles { + pub fn handles(self) -> WidgetHandles { let r = self.weak(); WidgetHandles { h: self, r } } } -impl WidgetRef { +impl WidgetRef { pub fn id(&self) -> WidgetId { self.id } } -impl Drop for WidgetHandle { +impl Drop for WidgetHandle { fn drop(&mut self) { if self.counter.drop() { let _ = self.send.send(self.id); @@ -86,52 +98,64 @@ impl Drop for WidgetHandle { } } -pub trait WidgetIdFn: FnOnce(&mut Ui) -> WidgetHandle {} -impl WidgetHandle> WidgetIdFn for F {} +pub trait WidgetIdFn>: + FnOnce(&mut Ui) -> WidgetHandle +{ +} +impl) -> WidgetHandle> WidgetIdFn + for F +{ +} -pub trait IdLike { - type Widget: Widget + ?Sized + 'static; +pub trait IdLike { + type Widget: Widget + ?Sized + 'static; fn id(&self) -> WidgetId; } -impl IdLike for &WidgetHandle { +impl + ?Sized> IdLike for &WidgetHandle { type Widget = W; fn id(&self) -> WidgetId { self.id } } -impl IdLike for WidgetHandle { +impl + ?Sized> IdLike for WidgetHandle { type Widget = W; fn id(&self) -> WidgetId { self.id } } -impl IdLike for WidgetRef { +impl + ?Sized> IdLike for WidgetRef { type Widget = W; fn id(&self) -> WidgetId { self.id } } -impl IdLike for WidgetId { - type Widget = dyn Widget; +impl IdLike for WidgetId { + type Widget = dyn Widget; fn id(&self) -> WidgetId { *self } } -impl, U: ?Sized> CoerceUnsized> for WidgetHandle {} -impl, U: ?Sized> CoerceUnsized> for WidgetRef {} +impl, U: ?Sized, State> CoerceUnsized> + for WidgetHandle +{ +} +impl, U: ?Sized> CoerceUnsized> + for WidgetRef +{ +} -impl Clone for WidgetRef { +impl Clone for WidgetRef { fn clone(&self) -> Self { *self } } -impl Copy for WidgetRef {} -impl PartialEq for WidgetRef { +impl Copy for WidgetRef {} +impl PartialEq for WidgetRef { fn eq(&self, other: &Self) -> bool { self.id == other.id } diff --git a/core/src/widget/like.rs b/core/src/widget/like.rs index ada9bf7..6b41226 100644 --- a/core/src/widget/like.rs +++ b/core/src/widget/like.rs @@ -1,48 +1,48 @@ use super::*; use std::marker::Unsize; -pub trait WidgetLike: Sized { - type Widget: Widget + ?Sized + Unsize + 'static; +pub trait WidgetLike: Sized { + type Widget: Widget + ?Sized + Unsize> + 'static; - fn add(self, ui: &mut Ui) -> WidgetHandle; + fn add(self, ui: &mut Ui) -> WidgetHandle; fn with_id( self, - f: impl FnOnce(&mut Ui, WidgetHandle) -> WidgetHandle, - ) -> impl WidgetIdFn { + f: impl FnOnce(&mut Ui, WidgetHandle) -> WidgetHandle, + ) -> impl WidgetIdFn { move |ui| { let id = self.add(ui); f(ui, id) } } - fn set_root(self, ui: &mut Ui) { + fn set_root(self, ui: &mut Ui) { ui.set_root(self); } - fn handles(self, ui: &mut Ui) -> WidgetHandles { + fn handles(self, ui: &mut Ui) -> WidgetHandles { self.add(ui).handles() } } -pub trait WidgetArrLike { - fn ui(self, ui: &mut Ui) -> WidgetArr; +pub trait WidgetArrLike { + fn ui(self, ui: &mut Ui) -> WidgetArr; } -impl WidgetArrLike for WidgetArr { - fn ui(self, _: &mut Ui) -> WidgetArr { +impl WidgetArrLike for WidgetArr { + fn ui(self, _: &mut Ui) -> WidgetArr { self } } -// I hate this language it's so bad why do I even use it +// variadic generics please save us macro_rules! impl_widget_arr { ($n:expr;$($W:ident)*) => { impl_widget_arr!($n;$($W)*;$(${concat($W,Tag)})*); }; ($n:expr;$($W:ident)*;$($Tag:ident)*) => { - impl<$($W: WidgetLike<$Tag>,$Tag,)*> WidgetArrLike<$n, ($($Tag,)*)> for ($($W,)*) { - fn ui(self, ui: &mut Ui) -> WidgetArr<$n> { + impl,$Tag,)*> WidgetArrLike for ($($W,)*) { + fn ui(self, ui: &mut Ui) -> WidgetArr { #[allow(non_snake_case)] let ($($W,)*) = self; WidgetArr::new( diff --git a/core/src/widget/mod.rs b/core/src/widget/mod.rs index 75f790b..f9657d4 100644 --- a/core/src/widget/mod.rs +++ b/core/src/widget/mod.rs @@ -13,23 +13,23 @@ pub use like::*; pub use tag::*; pub use widgets::*; -pub trait Widget: Any { - fn draw(&mut self, painter: &mut Painter); - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len; - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len; +pub trait Widget: Any { + fn draw(&mut self, painter: &mut Painter); + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len; + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len; } -impl Widget for () { - fn draw(&mut self, _: &mut Painter) {} - fn desired_width(&mut self, _: &mut SizeCtx) -> Len { +impl Widget for () { + fn draw(&mut self, _: &mut Painter) {} + fn desired_width(&mut self, _: &mut SizeCtx) -> Len { Len::ZERO } - fn desired_height(&mut self, _: &mut SizeCtx) -> Len { + fn desired_height(&mut self, _: &mut SizeCtx) -> Len { Len::ZERO } } -impl dyn Widget { +impl dyn Widget { pub fn as_any(&self) -> &dyn Any { self } @@ -42,31 +42,31 @@ impl dyn Widget { /// A function that returns a widget given a UI. /// Useful for defining trait functions on widgets that create a parent widget so that the children /// don't need to be IDs yet -pub trait WidgetFn: FnOnce(&mut Ui) -> W {} -impl W> WidgetFn for F {} +pub trait WidgetFn + ?Sized>: FnOnce(&mut Ui) -> W {} +impl + ?Sized, F: FnOnce(&mut Ui) -> W> WidgetFn for F {} -pub struct WidgetArr { - pub arr: [WidgetHandle; LEN], +pub struct WidgetArr { + pub arr: [WidgetHandle; LEN], } -impl WidgetArr { - pub fn new(arr: [WidgetHandle; LEN]) -> Self { +impl WidgetArr { + pub fn new(arr: [WidgetHandle; LEN]) -> Self { Self { arr } } } -pub trait WidgetOption { - fn get(self, ui: &mut Ui) -> Option; +pub trait WidgetOption { + fn get(self, ui: &mut Ui) -> Option>; } -impl WidgetOption for () { - fn get(self, _: &mut Ui) -> Option { +impl WidgetOption for () { + fn get(self, _: &mut Ui) -> Option> { None } } -impl Option> WidgetOption for F { - fn get(self, ui: &mut Ui) -> Option { +impl) -> Option>> WidgetOption for F { + fn get(self, ui: &mut Ui) -> Option> { self(ui) } } diff --git a/core/src/widget/tag.rs b/core/src/widget/tag.rs index afe33c3..e3121e0 100644 --- a/core/src/widget/tag.rs +++ b/core/src/widget/tag.rs @@ -5,35 +5,42 @@ use super::*; pub struct ArrTag; pub struct WidgetTag; -impl WidgetLike for W { +impl> WidgetLike for W { type Widget = W; - fn add(self, ui: &mut Ui) -> WidgetHandle { + fn add(self, ui: &mut Ui) -> WidgetHandle { ui.add_widget(self) } } pub struct FnTag; -impl W> WidgetLike for F { +impl, F: FnOnce(&mut Ui) -> W> WidgetLike + for F +{ type Widget = W; - fn add(self, ui: &mut Ui) -> WidgetHandle { + fn add(self, ui: &mut Ui) -> WidgetHandle { self(ui).add(ui) } } pub struct IdTag; -impl + 'static> WidgetLike for WidgetHandle { +impl + Unsize> + 'static> + WidgetLike for WidgetHandle +{ type Widget = W; - fn add(self, _: &mut Ui) -> WidgetHandle { + fn add(self, _: &mut Ui) -> WidgetHandle { self } } pub struct IdFnTag; -impl + 'static, F: FnOnce(&mut Ui) -> WidgetHandle> - WidgetLike for F +impl< + State: 'static, + W: ?Sized + Widget + Unsize> + 'static, + F: FnOnce(&mut Ui) -> WidgetHandle, +> WidgetLike for F { type Widget = W; - fn add(self, ui: &mut Ui) -> WidgetHandle { + fn add(self, ui: &mut Ui) -> WidgetHandle { self(ui) } } diff --git a/core/src/widget/widgets.rs b/core/src/widget/widgets.rs index 8aaa8a2..b43afb8 100644 --- a/core/src/widget/widgets.rs +++ b/core/src/widget/widgets.rs @@ -3,34 +3,44 @@ use crate::{ util::{DynBorrower, HashSet, SlotVec}, }; -#[derive(Default)] -pub struct Widgets { +pub struct Widgets { pub updates: HashSet, - vec: SlotVec, + vec: SlotVec>, } -impl Widgets { +impl Default for Widgets { + fn default() -> Self { + Self { + updates: Default::default(), + vec: Default::default(), + } + } +} + +impl Widgets { pub fn has_updates(&self) -> bool { !self.updates.is_empty() } - pub fn get_dyn(&self, id: WidgetId) -> Option<&dyn Widget> { + pub fn get_dyn(&self, id: WidgetId) -> Option<&dyn Widget> { Some(self.vec.get(id)?.widget.as_ref()) } - pub fn get_dyn_mut(&mut self, id: WidgetId) -> Option<&mut dyn Widget> { + pub fn get_dyn_mut(&mut self, id: WidgetId) -> Option<&mut dyn Widget> { self.updates.insert(id); Some(self.vec.get_mut(id)?.widget.as_mut()) } /// get_dyn but dynamic borrow checking of widgets /// lets you do recursive (tree) operations, like the painter does - pub(crate) fn get_dyn_dynamic(&self, id: WidgetId) -> WidgetWrapper<'_> { + pub(crate) fn get_dyn_dynamic(&self, id: WidgetId) -> WidgetWrapper<'_, State> { // SAFETY: must guarantee no other mutable references to this widget exist // done through the borrow variable #[allow(mutable_transmutes)] let data = unsafe { - std::mem::transmute::<&WidgetData, &mut WidgetData>(self.vec.get(id).unwrap()) + std::mem::transmute::<&WidgetData, &mut WidgetData>( + self.vec.get(id).unwrap(), + ) }; if data.borrowed { panic!("tried to mutably borrow the same widget twice"); @@ -38,37 +48,37 @@ impl Widgets { WidgetWrapper::new(data.widget.as_mut(), &mut data.borrowed) } - pub fn get(&self, id: &I) -> Option<&I::Widget> + pub fn get>(&self, id: &I) -> Option<&I::Widget> where I::Widget: Sized, { self.get_dyn(id.id())?.as_any().downcast_ref() } - pub fn get_mut(&mut self, id: &I) -> Option<&mut I::Widget> + pub fn get_mut>(&mut self, id: &I) -> Option<&mut I::Widget> where I::Widget: Sized, { self.get_dyn_mut(id.id())?.as_any_mut().downcast_mut() } - pub fn add(&mut self, widget: W) -> WidgetId { + pub fn add>(&mut self, widget: W) -> WidgetId { self.vec.add(WidgetData::new(widget)) } - pub fn data(&self, id: impl IdLike) -> Option<&WidgetData> { + pub fn data(&self, id: impl IdLike) -> Option<&WidgetData> { self.vec.get(id.id()) } - pub fn label(&self, id: impl IdLike) -> &String { + pub fn label(&self, id: impl IdLike) -> &String { &self.data(id.id()).unwrap().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()) } - pub fn delete(&mut self, id: impl IdLike) { + pub fn delete(&mut self, id: impl IdLike) { self.vec.free(id.id()); // not sure if there's any point in this // self.updates.remove(&id); @@ -80,4 +90,4 @@ impl Widgets { } } -pub type WidgetWrapper<'a> = DynBorrower<'a, dyn Widget>; +pub type WidgetWrapper<'a, State> = DynBorrower<'a, dyn Widget>; diff --git a/examples/minimal.rs b/examples/minimal.rs index dd468f9..348fc6c 100644 --- a/examples/minimal.rs +++ b/examples/minimal.rs @@ -1,14 +1,22 @@ use iris::prelude::*; +use winit::event_loop::ActiveEventLoop; fn main() { - DefaultApp::::run(); + App::::run(); } -struct State; +#[derive(HasUi, HasUiState)] +struct State { + ui: Ui, + ui_state: UiState, +} impl DefaultAppState for State { - fn new(ui: &mut Ui, _state: &UiState, _proxy: Proxy) -> Self { - rect(Color::RED).set_root(ui); - Self + fn new(event_loop: &ActiveEventLoop, _proxy: Proxy) -> Self { + let mut ui = Ui::new(); + let window = event_loop.create_window(Default::default()).unwrap(); + let ui_state = UiState::new(window); + rect(Color::RED).set_root(&mut ui); + Self { ui, ui_state } } } diff --git a/macro/src/lib.rs b/macro/src/lib.rs index ede2261..9b0b123 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -2,15 +2,19 @@ extern crate proc_macro; use proc_macro::TokenStream; use quote::quote; use syn::{ - Attribute, Block, Ident, ItemTrait, Signature, Token, Visibility, + Attribute, Block, Error, GenericParam, Generics, Ident, ItemStruct, ItemTrait, Meta, Signature, + Token, Visibility, parse::{Parse, ParseStream, Result}, parse_macro_input, parse_quote, + punctuated::Punctuated, + spanned::Spanned, }; struct Input { attrs: Vec, vis: Visibility, name: Ident, + generics: Generics, fns: Vec, } @@ -25,6 +29,7 @@ impl Parse for Input { let vis = input.parse()?; input.parse::()?; let name = input.parse()?; + let generics = input.parse::()?; input.parse::()?; let mut fns = Vec::new(); while !input.is_empty() { @@ -39,6 +44,7 @@ impl Parse for Input { attrs, vis, name, + generics, fns, }) } @@ -50,6 +56,7 @@ pub fn widget_trait(input: TokenStream) -> TokenStream { attrs, vis, name, + mut generics, fns, } = parse_macro_input!(input as Input); @@ -59,19 +66,130 @@ pub fn widget_trait(input: TokenStream) -> TokenStream { .map(|InputFn { sig, body }| quote! { #sig #body }) .collect(); + let Some(GenericParam::Type(state)) = generics.params.first() else { + return Error::new(name.span(), "expected state generic parameter") + .into_compile_error() + .into(); + }; + + let state = &state.ident; + + generics + .params + .push(parse_quote!(WL: WidgetLike<#state, Tag>)); + generics.params.push(parse_quote!(Tag)); + let mut trai: ItemTrait = parse_quote!( - #vis trait #name, Tag> { + #vis trait #name #generics { #(#sigs;)* } ); trai.attrs = attrs; - TokenStream::from(quote! { + quote! { #trai - impl, Tag> #name for WL { + impl #generics #name for WL { #(#impls)* } - }) + } + .into() +} + +#[proc_macro_derive(GlobalState, attributes(has))] +pub fn derive_global_state(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as ItemStruct); + let name = input.ident; + let mut impls = TokenStream::new(); + for field in input.fields { + let Some(attr) = field.attrs.iter().find(|a| a.path().is_ident("has")) else { + continue; + }; + let error: TokenStream = Error::new( + attr.span(), + "invalid attribute format; usage: #[has(HasTrait, trait_fn)]", + ) + .into_compile_error() + .into(); + let Meta::List(list) = &attr.meta else { + return error; + }; + match list.parse_args_with(Punctuated::::parse_terminated) { + Ok(list) => { + if list.len() != 2 { + return error; + } + let traitt = &list[0]; + let fn_name = &list[1]; + let field_name = field.ident; + let ty = field.ty; + impls.extend::( + quote! { + impl #traitt for #name { + fn #fn_name(&mut self) -> &mut #ty { + &mut self.#field_name + } + } + } + .into(), + ); + } + Err(..) => { + return error; + } + } + } + impls +} + +#[proc_macro_derive(HasUi)] +pub fn derive_has_ui(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as ItemStruct); + let name = input.ident; + let Some(field) = input.fields.iter().find(|f| f.ty == parse_quote!(Ui)) else { + return Error::new(name.span(), "could not find a Ui field for HasUi") + .into_compile_error() + .into(); + }; + let field = &field.ident; + quote! { + impl HasUi for #name { + fn ui_ref(&self) -> &Ui { + &self.#field + } + + fn ui(&mut self) -> &mut Ui { + &mut self.#field + } + } + } + .into() +} + +#[proc_macro_derive(HasUiState)] +pub fn derive_has_ui_state(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as ItemStruct); + let name = input.ident; + let Some(field) = input + .fields + .iter() + .find(|f| f.ty == parse_quote!(UiState)) + else { + return Error::new( + name.span(), + "could not find a UiState field for HasUiState", + ) + .into_compile_error() + .into(); + }; + let field = &field.ident; + quote! { + impl HasUiState for #name { + fn ui_state(&mut self) -> &mut UiState { + &mut self.#field + } + } + } + .into() } diff --git a/src/bin/test/main.rs b/src/bin/test/main.rs index 0a63d4e..4728ed1 100644 --- a/src/bin/test/main.rs +++ b/src/bin/test/main.rs @@ -3,213 +3,227 @@ use std::{cell::RefCell, rc::Rc}; use cosmic_text::Family; use iris::prelude::*; use len_fns::*; -use winit::event::WindowEvent; +use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::WindowAttributes}; fn main() { - DefaultApp::::run(); + App::::run(); } +#[derive(HasUi, HasUiState)] pub struct Client { - info: WidgetRef, + ui: Ui, + ui_state: UiState, + info: WidgetRef>, } event_ctx!(Client); impl DefaultAppState for Client { - fn new(ui: &mut Ui, _state: &UiState, _proxy: Proxy) -> Self { - let rrect = rect(Color::WHITE).radius(20); - let pad_test = ( - rrect.color(Color::BLUE), - ( - rrect - .color(Color::RED) - .sized((100, 100)) - .center() - .width(rest(2)), + fn new(event_loop: &ActiveEventLoop, _proxy: Proxy) -> Self { + let mut ui = Ui::new(); + let window = event_loop + .create_window(WindowAttributes::default()) + .unwrap(); + let info; + { + let ui = &mut ui; + let rrect = rect(Color::WHITE).radius(20); + let pad_test = ( + rrect.color(Color::BLUE), ( - rrect.color(Color::ORANGE), - rrect.color(Color::LIME).pad(10.0), + rrect + .color(Color::RED) + .sized((100, 100)) + .center() + .width(rest(2)), + ( + rrect.color(Color::ORANGE), + rrect.color(Color::LIME).pad(10.0), + ) + .span(Dir::RIGHT) + .width(rest(2)), + rrect.color(Color::YELLOW), ) .span(Dir::RIGHT) - .width(rest(2)), - rrect.color(Color::YELLOW), + .pad(10) + .width(rest(3)), ) .span(Dir::RIGHT) - .pad(10) - .width(rest(3)), - ) - .span(Dir::RIGHT) - .handles(ui); + .handles(ui); - let span_test = ( - rrect.color(Color::GREEN).width(100), - rrect.color(Color::ORANGE), - rrect.color(Color::CYAN), - rrect.color(Color::BLUE).width(rel(0.5)), - rrect.color(Color::MAGENTA).width(100), - rrect.color(Color::RED).width(100), - ) - .span(Dir::LEFT) - .add(ui); - - let span_add = Span::empty(Dir::RIGHT).handles(ui); - - let add_button = rect(Color::LIME) - .radius(30) - .on(CursorSense::click(), move |mut ctx| { - let child = ctx - .ui - .add(image(include_bytes!("assets/sungals.png")).center()); - ctx[span_add.r].children.push(child); - }) - .sized((150, 150)) - .align(Align::BOT_RIGHT); - - let del_button = rect(Color::RED) - .radius(30) - .on(CursorSense::click(), move |mut ctx| { - ctx[span_add.r].children.pop(); - }) - .sized((150, 150)) - .align(Align::BOT_LEFT); - - let span_add_test = (span_add.h, add_button, del_button).stack().add(ui); - - let btext = |content| wtext(content).size(30); - - let text_test = ( - btext("this is a").align(Align::LEFT), - btext("teeeeeeeest").align(Align::RIGHT), - btext("okkk\nokkkkkk!").align(Align::LEFT), - btext("hmm"), - btext("a"), - ( - btext("'").family(Family::Monospace).align(Align::TOP), - btext("'").family(Family::Monospace), - btext(":gamer mode").family(Family::Monospace), - rect(Color::CYAN).sized((10, 10)).center(), - rect(Color::RED).sized((100, 100)).center(), - rect(Color::PURPLE).sized((50, 50)).align(Align::TOP), + let span_test = ( + rrect.color(Color::GREEN).width(100), + rrect.color(Color::ORANGE), + rrect.color(Color::CYAN), + rrect.color(Color::BLUE).width(rel(0.5)), + rrect.color(Color::MAGENTA).width(100), + rrect.color(Color::RED).width(100), ) - .span(Dir::RIGHT) - .center(), - wtext("pretty cool right?").size(50), - ) - .span(Dir::DOWN) - .add(ui); + .span(Dir::LEFT) + .add(ui); - let texts = Span::empty(Dir::DOWN).gap(10).handles(ui); - let msg_area = texts.h.scroll().masked().background(rect(Color::SKY)); - let add_text = wtext("add") - .editable(false) - .text_align(Align::LEFT) - .size(30) - .attr::(()) - .on(Submit, move |ctx| { - let content = ctx.widget.edit(ctx.ui).take(); - let text = wtext(content) - .editable(false) - .size(30) - .text_align(Align::LEFT) - .wrap(true) - .attr::(()); - let msg_box = text.background(rect(Color::WHITE.darker(0.5))).add(ctx.ui); - ctx.ui[texts.r].children.push(msg_box); - }) - .handles(ui); - let text_edit_scroll = ( - msg_area.height(rest(1)), - ( - Rect::new(Color::WHITE.darker(0.9)), - ( - add_text.h.width(rest(1)), - Rect::new(Color::GREEN) - .on(CursorSense::click(), move |ctx| { - ctx.ui - .run_event::(ctx.state, add_text.r, &mut ()); - }) - .sized((40, 40)), - ) - .span(Dir::RIGHT) - .pad(10), - ) - .stack() - .size(StackSize::Child(1)) - .layer_offset(1) - .align(Align::BOT), - ) - .span(Dir::DOWN) - .add(ui); + let span_add = Span::empty(Dir::RIGHT).handles(ui); - let main = WidgetPtr::new().handles(ui); - - let vals = Rc::new(RefCell::new((0, Vec::new()))); - let mut switch_button = |color, to: WidgetHandle, label| { - let vec = &mut vals.borrow_mut().1; - let i = vec.len(); - if vec.is_empty() { - vec.push(None); - ui[main.r].set(to); - } else { - vec.push(Some(to)); - } - let vals = vals.clone(); - let rect = rect(color) + let add_button = rect(Color::LIME) + .radius(30) .on(CursorSense::click(), move |mut ctx| { - let (prev, vec) = &mut *vals.borrow_mut(); - if let Some(h) = vec[i].take() { - vec[*prev] = ctx[main.r].replace(h); - *prev = i; - } - ctx.widget().color = color.darker(0.3); + let child = ctx.add(image(include_bytes!("assets/sungals.png")).center()); + ctx[span_add.r].children.push(child); }) - .on( - CursorSense::HoverStart | CursorSense::unclick(), - move |mut ctx| { - ctx.widget().color = color.brighter(0.2); - }, + .sized((150, 150)) + .align(Align::BOT_RIGHT); + + let del_button = rect(Color::RED) + .radius(30) + .on(CursorSense::click(), move |mut ctx| { + ctx[span_add.r].children.pop(); + }) + .sized((150, 150)) + .align(Align::BOT_LEFT); + + let span_add_test = (span_add.h, add_button, del_button).stack().add(ui); + + let btext = |content| wtext(content).size(30); + + let text_test = ( + btext("this is a").align(Align::LEFT), + btext("teeeeeeeest").align(Align::RIGHT), + btext("okkk\nokkkkkk!").align(Align::LEFT), + btext("hmm"), + btext("a"), + ( + btext("'").family(Family::Monospace).align(Align::TOP), + btext("'").family(Family::Monospace), + btext(":gamer mode").family(Family::Monospace), + rect(Color::CYAN).sized((10, 10)).center(), + rect(Color::RED).sized((100, 100)).center(), + rect(Color::PURPLE).sized((50, 50)).align(Align::TOP), ) - .on(CursorSense::HoverEnd, move |mut ctx| { - ctx.widget().color = color; - }); - (rect, wtext(label).size(30).text_align(Align::CENTER)).stack() - }; + .span(Dir::RIGHT) + .center(), + wtext("pretty cool right?").size(50), + ) + .span(Dir::DOWN) + .add(ui); - let tabs = ( - switch_button(Color::RED, pad_test.h, "pad"), - switch_button(Color::GREEN, span_test, "span"), - switch_button(Color::BLUE, span_add_test, "image span"), - switch_button(Color::MAGENTA, text_test, "text layout"), - switch_button( - Color::YELLOW.mul_rgb(0.5), - text_edit_scroll, - "text edit scroll", - ), - ) - .span(Dir::RIGHT); + let texts = Span::empty(Dir::DOWN).gap(10).handles(ui); + let msg_area = texts.h.scroll().masked().background(rect(Color::SKY)); + let add_text = wtext("add") + .editable(false) + .text_align(Align::LEFT) + .size(30) + .attr::(()) + .on(Submit, move |mut ctx| { + let content = ctx.widget.edit(ctx.state.ui()).take(); + let text = wtext(content) + .editable(false) + .size(30) + .text_align(Align::LEFT) + .wrap(true) + .attr::(()); + let msg_box = text + .background(rect(Color::WHITE.darker(0.5))) + .add(&mut ctx); + ctx[texts.r].children.push(msg_box); + }) + .handles(ui); + let text_edit_scroll = ( + msg_area.height(rest(1)), + ( + Rect::new(Color::WHITE.darker(0.9)), + ( + add_text.h.width(rest(1)), + Rect::new(Color::GREEN) + .on(CursorSense::click(), move |ctx| { + ctx.state.run_event::(add_text.r, &mut ()); + }) + .sized((40, 40)), + ) + .span(Dir::RIGHT) + .pad(10), + ) + .stack() + .size(StackSize::Child(1)) + .layer_offset(1) + .align(Align::BOT), + ) + .span(Dir::DOWN) + .add(ui); - let info = wtext("").handles(ui); - let info_sect = info.h.pad(10).align(Align::RIGHT); + let main = WidgetPtr::new().handles(ui); - ((tabs.height(40), main.h.pad(10)).span(Dir::DOWN), info_sect) - .stack() - .set_root(ui); + let vals = Rc::new(RefCell::new((0, Vec::new()))); + let mut switch_button = |color, to: WidgetHandle, label| { + let vec = &mut vals.borrow_mut().1; + let i = vec.len(); + if vec.is_empty() { + vec.push(None); + ui[main.r].set(to); + } else { + vec.push(Some(to)); + } + let vals = vals.clone(); + let rect = rect(color) + .on(CursorSense::click(), move |mut ctx| { + let (prev, vec) = &mut *vals.borrow_mut(); + if let Some(h) = vec[i].take() { + vec[*prev] = ctx[main.r].replace(h); + *prev = i; + } + ctx.widget().color = color.darker(0.3); + }) + .on( + CursorSense::HoverStart | CursorSense::unclick(), + move |mut ctx| { + ctx.widget().color = color.brighter(0.2); + }, + ) + .on(CursorSense::HoverEnd, move |mut ctx| { + ctx.widget().color = color; + }); + (rect, wtext(label).size(30).text_align(Align::CENTER)).stack() + }; - Self { info: info.r } + let tabs = ( + switch_button(Color::RED, pad_test.h, "pad"), + switch_button(Color::GREEN, span_test, "span"), + switch_button(Color::BLUE, span_add_test, "image span"), + switch_button(Color::MAGENTA, text_test, "text layout"), + switch_button( + Color::YELLOW.mul_rgb(0.5), + text_edit_scroll, + "text edit scroll", + ), + ) + .span(Dir::RIGHT); + + info = wtext("").handles(ui); + let info_sect = info.h.pad(10).align(Align::RIGHT); + + ((tabs.height(40), main.h.pad(10)).span(Dir::DOWN), info_sect) + .stack() + .set_root(ui); + } + + Self { + ui, + ui_state: UiState::new(window), + info: info.r, + } } - fn window_event(&mut self, _: WindowEvent, ui: &mut Ui, state: &UiState) { + fn window_event(&mut self, _: WindowEvent) { let new = format!( "widgets: {}\nactive:{}\nviews: {}", - ui.num_widgets(), - ui.active_widgets(), - state.renderer.ui.view_count() + self.ui.num_widgets(), + self.ui.active_widgets(), + self.ui_state.renderer.ui.view_count() ); - if new != *ui[self.info].content { - *ui[self.info].content = new; + if new != *self.ui[self.info].content { + *self.ui[self.info].content = new; } - if ui.needs_redraw() { - state.window.request_redraw(); + if self.ui.needs_redraw() { + self.ui_state.window.request_redraw(); } } } diff --git a/src/default/attr.rs b/src/default/attr.rs index 4cd4a4d..2d6a271 100644 --- a/src/default/attr.rs +++ b/src/default/attr.rs @@ -1,42 +1,34 @@ -use crate::{default::UiState, prelude::*}; +use crate::prelude::*; use std::time::{Duration, Instant}; use winit::dpi::{LogicalPosition, LogicalSize}; pub struct Selector; -impl WidgetAttr for Selector { - type Input = WidgetRef; +impl + 'static> WidgetAttr for Selector { + type Input = WidgetRef>; - fn run(ui: &mut Ui, container: WidgetRef, id: Self::Input) { + fn run(ui: &mut Ui, container: WidgetRef, id: Self::Input) { ui.register_event(container, CursorSense::click_or_drag(), move |ctx| { - let region = ctx.ui.window_region(&id).unwrap(); + let region = ctx.state.ui().window_region(&id).unwrap(); let id_pos = region.top_left; - let container_pos = ctx.ui.window_region(&container).unwrap().top_left; + let container_pos = ctx.state.ui().window_region(&container).unwrap().top_left; let pos = ctx.data.pos + container_pos - id_pos; let size = region.size(); - select( - ctx.ui, - id, - ctx.state, - pos, - size, - ctx.data.sense.is_dragging(), - ); + select(ctx.state, id, pos, size, ctx.data.sense.is_dragging()); }); } } pub struct Selectable; -impl WidgetAttr for Selectable { +impl WidgetAttr> for Selectable { type Input = (); - fn run(ui: &mut Ui, id: WidgetRef, _: Self::Input) { + fn run(ui: &mut Ui, id: WidgetRef>, _: Self::Input) { ui.register_event(id, CursorSense::click_or_drag(), move |ctx| { select( - ctx.ui, - id, ctx.state, + id, ctx.data.pos, ctx.data.size, ctx.data.sense.is_dragging(), @@ -45,24 +37,23 @@ impl WidgetAttr for Selectable { } } -fn select( - ui: &mut Ui, - id: WidgetRef, - state: &mut UiState, +fn select( + state: &mut State, + id: WidgetRef>, pos: Vec2, size: Vec2, dragging: bool, ) { let now = Instant::now(); - let recent = (now - state.last_click) < Duration::from_millis(300); - state.last_click = now; - id.edit(ui).select(pos, size, dragging, recent); - if let Some(region) = ui.window_region(&id) { - state.window.set_ime_allowed(true); - state.window.set_ime_cursor_area( + let recent = (now - state.ui_state().last_click) < Duration::from_millis(300); + state.ui_state().last_click = now; + id.edit(state.ui()).select(pos, size, dragging, recent); + if let Some(region) = state.ui().window_region(&id) { + state.ui_state().window.set_ime_allowed(true); + state.ui_state().window.set_ime_cursor_area( LogicalPosition::::from(region.top_left.tuple()), LogicalSize::::from(region.size().tuple()), ); } - state.focus = Some(id); + state.ui_state().focus = Some(id); } diff --git a/src/default/input.rs b/src/default/input.rs index 4aa96b7..53e8d45 100644 --- a/src/default/input.rs +++ b/src/default/input.rs @@ -66,7 +66,7 @@ impl Input { } } -impl UiState { +impl UiState { pub fn window_size(&self) -> Vec2 { let size = self.renderer.window().inner_size(); (size.width, size.height).into() diff --git a/src/default/mod.rs b/src/default/mod.rs index 7715313..1ee8412 100644 --- a/src/default/mod.rs +++ b/src/default/mod.rs @@ -1,10 +1,11 @@ use crate::prelude::*; use arboard::Clipboard; -use std::sync::Arc; -use std::time::Instant; -use winit::event::{Ime, WindowEvent}; -use winit::event_loop::{ActiveEventLoop, EventLoopProxy}; -use winit::window::{Window, WindowAttributes}; +use std::{marker::Sized, sync::Arc, time::Instant}; +use winit::{ + event::{Ime, WindowEvent}, + event_loop::{ActiveEventLoop, EventLoopProxy}, + window::Window, +}; mod app; mod attr; @@ -21,49 +22,21 @@ pub use render::*; pub use sense::*; pub type Proxy = EventLoopProxy; -pub type DefaultApp = App>; -pub struct DefaultState { - ui: Ui, - ui_state: UiState, - app_state: AppState, -} - -pub struct UiState { +pub struct UiState { pub renderer: UiRenderer, pub input: Input, - pub focus: Option>, + pub focus: Option>>, pub clipboard: Clipboard, pub window: Arc, pub ime: usize, pub last_click: Instant, } -pub trait DefaultAppState: 'static { - type Event: 'static = (); - fn new(ui: &mut Ui, state: &UiState, proxy: Proxy) -> Self; - #[allow(unused_variables)] - fn event(&mut self, event: Self::Event, ui: &mut Ui, state: &UiState) {} - #[allow(unused_variables)] - fn exit(&mut self, ui: &mut Ui, state: &UiState) {} - #[allow(unused_variables)] - fn window_event(&mut self, event: WindowEvent, ui: &mut Ui, state: &UiState) {} - fn window_attrs() -> WindowAttributes { - WindowAttributes::default() - } -} - -impl AppState for DefaultState { - type Event = State::Event; - - fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy) -> Self { - let window = Arc::new( - event_loop - .create_window(State::window_attrs()) - .expect("failed to create window "), - ); - let mut ui = Ui::new(); - let ui_state = UiState { +impl UiState { + pub fn new(window: impl Into>) -> Self { + let window = window.into(); + Self { renderer: UiRenderer::new(window.clone()), window, input: Input::default(), @@ -71,26 +44,42 @@ impl AppState for DefaultState { ime: 0, last_click: Instant::now(), focus: None, - }; - let app_state = State::new(&mut ui, &ui_state, proxy); - Self { - ui, - ui_state, - app_state, } } +} + +pub trait HasUiState: Sized + 'static + HasUi { + fn ui_state(&mut self) -> &mut UiState; + fn ui_with_ui_state(&mut self) -> (&mut Ui, &mut UiState) { + // as long as you're not doing anything actually unhinged this should always work safely + (unsafe { std::mem::transmute(self.ui()) }, self.ui_state()) + } +} + +pub trait DefaultAppState: Sized + 'static + HasUi + HasUiState { + type Event: 'static = (); + fn new(event_loop: &ActiveEventLoop, proxy: Proxy) -> Self; + #[allow(unused_variables)] + fn event(&mut self, event: Self::Event) {} + #[allow(unused_variables)] + fn exit(&mut self) {} + #[allow(unused_variables)] + fn window_event(&mut self, event: WindowEvent) {} +} + +impl AppState for State { + type Event = State::Event; + + fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy) -> Self { + Self::new(event_loop, proxy) + } fn event(&mut self, event: Self::Event, _: &ActiveEventLoop) { - self.app_state.event(event, &mut self.ui, &self.ui_state); + self.event(event); } fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop) { - let Self { - ui, - ui_state, - app_state, - } = self; - + let ui_state = self.ui_state(); let input_changed = ui_state.input.event(&event); let cursor_state = ui_state.cursor_state().clone(); let old = ui_state.focus; @@ -99,13 +88,9 @@ impl AppState for DefaultState { } if input_changed { let window_size = ui_state.window_size(); - // call sensors with all 3 important contexts - // TODO: allow user to specify custom contexts? - // and give them both states in case they need both - ui.run_sensors(&mut (), &cursor_state, window_size); - ui.run_sensors(ui_state, &cursor_state, window_size); - ui.run_sensors(app_state, &cursor_state, window_size); + self.run_sensors(&cursor_state, window_size); } + let (mut ui, mut ui_state) = self.ui_with_ui_state(); if old != ui_state.focus && let Some(old) = old { @@ -133,13 +118,13 @@ impl AppState for DefaultState { ui_state.window.set_ime_allowed(false); } TextInputResult::Submit => { - ui.run_event::(app_state, sel, &mut ()); + self.run_event::(sel, &mut ()); } TextInputResult::Paste => { if let Ok(t) = ui_state.clipboard.get_text() { text.insert(&t); } - ui.run_event::(app_state, sel, &mut ()); + self.run_event::(sel, &mut ()); } TextInputResult::Copy(text) => { if let Err(err) = ui_state.clipboard.set_text(text) { @@ -147,14 +132,14 @@ impl AppState for DefaultState { } } TextInputResult::Used => { - ui.run_event::(app_state, sel, &mut ()); + self.run_event::(sel, &mut ()); } TextInputResult::Unused => {} } } } WindowEvent::Ime(ime) => { - if let Some(sel) = &ui_state.focus { + if let Some(sel) = ui_state.focus { let mut text = sel.edit(ui); match ime { Ime::Enabled | Ime::Disabled => (), @@ -171,7 +156,8 @@ impl AppState for DefaultState { } _ => (), } - app_state.window_event(event, ui, ui_state); + self.window_event(event); + (ui, ui_state) = self.ui_with_ui_state(); if ui.needs_redraw() { ui_state.renderer.window().request_redraw(); } @@ -179,6 +165,6 @@ impl AppState for DefaultState { } fn exit(&mut self) { - self.app_state.exit(&mut self.ui, &self.ui_state); + self.exit(); } } diff --git a/src/default/render.rs b/src/default/render.rs index f9b4293..233e85c 100644 --- a/src/default/render.rs +++ b/src/default/render.rs @@ -18,7 +18,7 @@ pub struct UiRenderer { } impl UiRenderer { - pub fn update(&mut self, updates: &mut Ui) { + pub fn update(&mut self, updates: &mut Ui) { self.ui.update(&self.device, &self.queue, updates); } diff --git a/src/default/sense.rs b/src/default/sense.rs index 67bfc61..2cddb9a 100644 --- a/src/default/sense.rs +++ b/src/default/sense.rs @@ -138,28 +138,23 @@ pub struct CursorData<'a> { pub sense: CursorSense, } -pub trait SensorUi { - fn run_sensors(&mut self, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2); +pub trait SensorUi { + fn run_sensors(&mut self, cursor: &CursorState, window_size: Vec2); } -impl SensorUi for Ui { - fn run_sensors( - &mut self, - ctx: &mut Ctx, - cursor: &CursorState, - window_size: Vec2, - ) { - let layers = std::mem::take(&mut self.data.layers); +impl SensorUi for State { + fn run_sensors(&mut self, cursor: &CursorState, window_size: Vec2) { + let layers = std::mem::take(&mut self.ui().data.layers); let mut active = - std::mem::take(&mut self.data.events.get_type::().active); + std::mem::take(&mut self.ui().data.events.get_type::().active); for layer in layers.indices().rev() { let mut sensed = false; - for (id, state) in active.get_mut(&layer).into_iter().flatten() { - let shape = self.data.active.get(id).unwrap().region; + for (id, sensor) in active.get_mut(&layer).into_iter().flatten() { + let shape = self.ui().data.active.get(id).unwrap().region; let region = shape.to_px(window_size); let in_shape = cursor.exists && region.contains(cursor.pos); - state.hover.update(in_shape); - if state.hover == ActivationState::Off { + sensor.hover.update(in_shape); + if sensor.hover == ActivationState::Off { continue; } sensed = true; @@ -168,20 +163,20 @@ impl SensorUi for Ui { pos: cursor.pos - region.top_left, size: region.bot_right - region.top_left, scroll_delta: cursor.scroll_delta, - hover: state.hover, + hover: sensor.hover, cursor, // this does not have any meaning; // might wanna set up Event to have a prepare stage sense: CursorSense::Hovering, }; - self.run_event::(ctx, *id, &mut data); + self.run_event::(*id, &mut data); } if sensed { break; } } - self.data.events.get_type::().active = active; - self.data.layers = layers; + self.ui().data.events.get_type::().active = active; + self.ui().data.layers = layers; } } diff --git a/src/event.rs b/src/event.rs index bf168db..26767eb 100644 --- a/src/event.rs +++ b/src/event.rs @@ -2,14 +2,13 @@ use crate::prelude::*; pub mod eventable { use super::*; - widget_trait! { - pub trait Eventable; - fn on( + pub trait Eventable; + fn on( self, event: E, - f: impl for<'a> WidgetEventFn::Data<'a>, WL::Widget>, - ) -> impl WidgetIdFn { + f: impl for<'a> WidgetEventFn::Data<'a>, WL::Widget>, + ) -> impl WidgetIdFn { move |ui| { let id = self.handles(ui); ui.register_event(id.r, event.into_event(), move |ctx| { @@ -17,7 +16,6 @@ pub mod eventable { widget: id.r, state: ctx.state, data: ctx.data, - ui: ctx.ui, }); }); id.h @@ -35,25 +33,20 @@ macro_rules! event_ctx { #[allow(unused_imports)] use $crate::prelude::*; - widget_trait! { - pub trait EventableCtx; + pub trait EventableCtx, Tag> { fn on( self, event: E, f: impl for<'a> WidgetEventFn<$ty, ::Data<'a>, WL::Widget>, - ) -> impl WidgetIdFn { - move |ui| { - let id = self.handles(ui); - ui.register_event(id.r, event.into_event(), move |ctx| { - f(EventIdCtx { - widget: id.r, - state: ctx.state, - data: ctx.data, - ui: ctx.ui, - }); - }); - id.h - } + ) -> impl WidgetIdFn<$ty, WL::Widget>; + } + impl, Tag> EventableCtx for WL { + fn on( + self, + event: E, + f: impl for<'a> WidgetEventFn::Data<'a>, WL::Widget>, + ) -> impl WidgetIdFn { + eventable::Eventable::on(self, event, f) } } } diff --git a/src/widget/image.rs b/src/widget/image.rs index a2b58b9..1daa838 100644 --- a/src/widget/image.rs +++ b/src/widget/image.rs @@ -5,21 +5,21 @@ pub struct Image { handle: TextureHandle, } -impl Widget for Image { - fn draw(&mut self, painter: &mut Painter) { +impl Widget for Image { + fn draw(&mut self, painter: &mut Painter) { painter.texture(&self.handle); } - fn desired_width(&mut self, _: &mut SizeCtx) -> Len { + fn desired_width(&mut self, _: &mut SizeCtx) -> Len { Len::abs(self.handle.size().x) } - fn desired_height(&mut self, _: &mut SizeCtx) -> Len { + fn desired_height(&mut self, _: &mut SizeCtx) -> Len { Len::abs(self.handle.size().y) } } -pub fn image(image: impl LoadableImage) -> impl WidgetFn { +pub fn image(image: impl LoadableImage) -> impl WidgetFn { let image = image.get_image().expect("Failed to load image"); move |ui| Image { handle: ui.add_texture(image), diff --git a/src/widget/mask.rs b/src/widget/mask.rs index 5b16337..b8d9fcf 100644 --- a/src/widget/mask.rs +++ b/src/widget/mask.rs @@ -1,20 +1,20 @@ use crate::prelude::*; -pub struct Masked { - pub inner: WidgetHandle, +pub struct Masked { + pub inner: WidgetHandle, } -impl Widget for Masked { - fn draw(&mut self, painter: &mut Painter) { +impl Widget for Masked { + fn draw(&mut self, painter: &mut Painter) { painter.set_mask(painter.region()); painter.widget(&self.inner); } - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { ctx.width(&self.inner) } - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { ctx.height(&self.inner) } } diff --git a/src/widget/position/align.rs b/src/widget/position/align.rs index c475b4b..c6d513a 100644 --- a/src/widget/position/align.rs +++ b/src/widget/position/align.rs @@ -1,12 +1,12 @@ use crate::prelude::*; -pub struct Aligned { - pub inner: WidgetHandle, +pub struct Aligned { + pub inner: WidgetHandle, pub align: Align, } -impl Widget for Aligned { - fn draw(&mut self, painter: &mut Painter) { +impl Widget for Aligned { + fn draw(&mut self, painter: &mut Painter) { let region = match self.align.tuple() { (Some(x), Some(y)) => painter .size(&self.inner) @@ -25,11 +25,11 @@ impl Widget for Aligned { painter.widget_within(&self.inner, region); } - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { ctx.width(&self.inner) } - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { ctx.height(&self.inner) } } diff --git a/src/widget/position/layer.rs b/src/widget/position/layer.rs index ef55139..ee17010 100644 --- a/src/widget/position/layer.rs +++ b/src/widget/position/layer.rs @@ -1,23 +1,23 @@ use crate::prelude::*; -pub struct LayerOffset { - pub inner: WidgetHandle, +pub struct LayerOffset { + pub inner: WidgetHandle, pub offset: usize, } -impl Widget for LayerOffset { - fn draw(&mut self, painter: &mut Painter) { +impl Widget for LayerOffset { + fn draw(&mut self, painter: &mut Painter) { for _ in 0..self.offset { painter.next_layer(); } painter.widget(&self.inner); } - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { ctx.width(&self.inner) } - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { ctx.height(&self.inner) } } diff --git a/src/widget/position/max_size.rs b/src/widget/position/max_size.rs index 83071d8..85ccd45 100644 --- a/src/widget/position/max_size.rs +++ b/src/widget/position/max_size.rs @@ -1,13 +1,13 @@ use crate::prelude::*; -pub struct MaxSize { - pub inner: WidgetHandle, +pub struct MaxSize { + pub inner: WidgetHandle, pub x: Option, pub y: Option, } -impl MaxSize { - fn apply_to_outer(&self, ctx: &mut SizeCtx) { +impl MaxSize { + fn apply_to_outer(&self, ctx: &mut SizeCtx) { if let Some(x) = self.x { ctx.outer.x.select_len(x.apply_rest()); } @@ -17,12 +17,12 @@ impl MaxSize { } } -impl Widget for MaxSize { - fn draw(&mut self, painter: &mut Painter) { +impl Widget for MaxSize { + fn draw(&mut self, painter: &mut Painter) { painter.widget(&self.inner); } - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { self.apply_to_outer(ctx); let width = ctx.width(&self.inner); if let Some(x) = self.x { @@ -34,7 +34,7 @@ impl Widget for MaxSize { } } - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { self.apply_to_outer(ctx); let height = ctx.height(&self.inner); if let Some(y) = self.y { diff --git a/src/widget/position/offset.rs b/src/widget/position/offset.rs index e7a19d8..4e389d5 100644 --- a/src/widget/position/offset.rs +++ b/src/widget/position/offset.rs @@ -1,21 +1,21 @@ use crate::prelude::*; -pub struct Offset { - pub inner: WidgetHandle, +pub struct Offset { + pub inner: WidgetHandle, pub amt: UiVec2, } -impl Widget for Offset { - fn draw(&mut self, painter: &mut Painter) { +impl Widget for Offset { + fn draw(&mut self, painter: &mut Painter) { let region = UiRegion::FULL.offset(self.amt); painter.widget_within(&self.inner, region); } - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { ctx.width(&self.inner) } - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { ctx.height(&self.inner) } } diff --git a/src/widget/position/pad.rs b/src/widget/position/pad.rs index 26554ee..5a55508 100644 --- a/src/widget/position/pad.rs +++ b/src/widget/position/pad.rs @@ -1,16 +1,16 @@ use crate::prelude::*; -pub struct Pad { +pub struct Pad { pub padding: Padding, - pub inner: WidgetHandle, + pub inner: WidgetHandle, } -impl Widget for Pad { - fn draw(&mut self, painter: &mut Painter) { +impl Widget for Pad { + fn draw(&mut self, painter: &mut Painter) { painter.widget_within(&self.inner, self.padding.region()); } - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { let width = self.padding.left + self.padding.right; let height = self.padding.top + self.padding.bottom; ctx.outer.x.abs -= width; @@ -20,7 +20,7 @@ impl Widget for Pad { size } - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { let width = self.padding.left + self.padding.right; let height = self.padding.top + self.padding.bottom; ctx.outer.x.abs -= width; diff --git a/src/widget/position/scroll.rs b/src/widget/position/scroll.rs index c414b11..727132b 100644 --- a/src/widget/position/scroll.rs +++ b/src/widget/position/scroll.rs @@ -1,7 +1,7 @@ use crate::prelude::*; -pub struct Scroll { - inner: WidgetHandle, +pub struct Scroll { + inner: WidgetHandle, axis: Axis, amt: f32, snap_end: bool, @@ -9,8 +9,8 @@ pub struct Scroll { content_len: f32, } -impl Widget for Scroll { - fn draw(&mut self, painter: &mut Painter) { +impl Widget for Scroll { + fn draw(&mut self, painter: &mut Painter) { let output_len = painter.output_size().axis(self.axis); let container_len = painter.region().axis(self.axis).len(); let content_len = painter @@ -31,17 +31,17 @@ impl Widget for Scroll { painter.widget_within(&self.inner, region); } - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { ctx.width(&self.inner) } - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { ctx.height(&self.inner) } } -impl Scroll { - pub fn new(inner: WidgetHandle, axis: Axis) -> Self { +impl Scroll { + pub fn new(inner: WidgetHandle, axis: Axis) -> Self { Self { inner, axis, diff --git a/src/widget/position/sized.rs b/src/widget/position/sized.rs index 8aed65e..8c0aad1 100644 --- a/src/widget/position/sized.rs +++ b/src/widget/position/sized.rs @@ -1,13 +1,13 @@ use crate::prelude::*; -pub struct Sized { - pub inner: WidgetHandle, +pub struct Sized { + pub inner: WidgetHandle, pub x: Option, pub y: Option, } -impl Sized { - fn apply_to_outer(&self, ctx: &mut SizeCtx) { +impl Sized { + fn apply_to_outer(&self, ctx: &mut SizeCtx) { if let Some(x) = self.x { ctx.outer.x.select_len(x.apply_rest()); } @@ -17,17 +17,17 @@ impl Sized { } } -impl Widget for Sized { - fn draw(&mut self, painter: &mut Painter) { +impl Widget for Sized { + fn draw(&mut self, painter: &mut Painter) { painter.widget(&self.inner); } - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { self.apply_to_outer(ctx); self.x.unwrap_or_else(|| ctx.width(&self.inner)) } - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { self.apply_to_outer(ctx); self.y.unwrap_or_else(|| ctx.height(&self.inner)) } diff --git a/src/widget/position/span.rs b/src/widget/position/span.rs index 007455c..e68fbbf 100644 --- a/src/widget/position/span.rs +++ b/src/widget/position/span.rs @@ -1,14 +1,14 @@ use crate::prelude::*; use std::marker::PhantomData; -pub struct Span { - pub children: Vec, +pub struct Span { + pub children: Vec>, pub dir: Dir, pub gap: f32, } -impl Widget for Span { - fn draw(&mut self, painter: &mut Painter) { +impl Widget for Span { + fn draw(&mut self, painter: &mut Painter) { let total = self.len_sum(&mut painter.size_ctx()); let mut start = UiScalar::rel_min(); for child in &self.children { @@ -33,14 +33,14 @@ impl Widget for Span { } } - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { match self.dir.axis { Axis::X => self.desired_len(ctx), Axis::Y => self.desired_ortho(ctx), } } - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { match self.dir.axis { Axis::X => self.desired_ortho(ctx), Axis::Y => self.desired_len(ctx), @@ -48,7 +48,7 @@ impl Widget for Span { } } -impl Span { +impl Span { pub fn empty(dir: Dir) -> Self { Self { children: Vec::new(), @@ -62,7 +62,7 @@ impl Span { self } - fn len_sum(&mut self, ctx: &mut SizeCtx) -> Len { + fn len_sum(&mut self, ctx: &mut SizeCtx) -> Len { let gap = self.gap * self.children.len().saturating_sub(1) as f32; self.children.iter().fold(Len::abs(gap), |mut s, id| { // it's tempting to subtract the abs & rel from the ctx outer, @@ -78,7 +78,7 @@ impl Span { }) } - fn desired_len(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_len(&mut self, ctx: &mut SizeCtx) -> Len { let len = self.len_sum(ctx); if len.rest == 0.0 && len.rel == 0.0 { len @@ -87,7 +87,7 @@ impl Span { } } - fn desired_ortho(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_ortho(&mut self, ctx: &mut SizeCtx) -> Len { // this is a weird hack to get text wrapping to work properly when in a downward span // the correct solution here is to add a function to widget that lets them // request that ctx.outer has an axis "resolved" before checking the other, @@ -144,19 +144,19 @@ impl Span { } } -pub struct SpanBuilder, Tag> { +pub struct SpanBuilder, Tag> { pub children: Wa, pub dir: Dir, pub gap: f32, - _pd: PhantomData, + _pd: PhantomData<(State, Tag)>, } -impl, Tag> FnOnce<(&mut Ui,)> - for SpanBuilder +impl, Tag> FnOnce<(&mut Ui,)> + for SpanBuilder { - type Output = Span; + type Output = Span; - extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { + extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { Span { children: self.children.ui(args.0).arr.into_iter().collect(), dir: self.dir, @@ -165,7 +165,9 @@ impl, Tag> FnOnce<(&mut Ui,)> } } -impl, Tag> SpanBuilder { +impl, Tag> + SpanBuilder +{ pub fn new(children: Wa, dir: Dir) -> Self { Self { children, @@ -181,15 +183,15 @@ impl, Tag> SpanBuilder; +impl std::ops::Deref for Span { + type Target = Vec>; fn deref(&self) -> &Self::Target { &self.children } } -impl std::ops::DerefMut for Span { +impl std::ops::DerefMut for Span { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.children } diff --git a/src/widget/position/stack.rs b/src/widget/position/stack.rs index f25b874..f2a5d1c 100644 --- a/src/widget/position/stack.rs +++ b/src/widget/position/stack.rs @@ -2,13 +2,13 @@ use std::marker::PhantomData; use crate::prelude::*; -pub struct Stack { - pub children: Vec, +pub struct Stack { + pub children: Vec>, pub size: StackSize, } -impl Widget for Stack { - fn draw(&mut self, painter: &mut Painter) { +impl Widget for Stack { + fn draw(&mut self, painter: &mut Painter) { let mut iter = self.children.iter(); if let Some(child) = iter.next() { painter.child_layer(); @@ -20,14 +20,14 @@ impl Widget for Stack { } } - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { match self.size { StackSize::Default => Len::default(), StackSize::Child(i) => ctx.width(&self.children[i]), } } - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { match self.size { StackSize::Default => Len::default(), StackSize::Child(i) => ctx.height(&self.children[i]), @@ -42,18 +42,18 @@ pub enum StackSize { Child(usize), } -pub struct StackBuilder, Tag> { +pub struct StackBuilder, Tag> { pub children: Wa, pub size: StackSize, - _pd: PhantomData, + _pd: PhantomData<(State, Tag)>, } -impl, Tag> FnOnce<(&mut Ui,)> - for StackBuilder +impl, Tag> FnOnce<(&mut Ui,)> + for StackBuilder { - type Output = Stack; + type Output = Stack; - extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { + extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { Stack { children: self.children.ui(args.0).arr.into_iter().collect(), size: self.size, @@ -61,7 +61,9 @@ impl, Tag> FnOnce<(&mut Ui,)> } } -impl, Tag> StackBuilder { +impl, Tag> + StackBuilder +{ pub fn new(children: Wa) -> Self { Self { children, diff --git a/src/widget/ptr.rs b/src/widget/ptr.rs index 6856103..b59736b 100644 --- a/src/widget/ptr.rs +++ b/src/widget/ptr.rs @@ -1,19 +1,18 @@ use crate::prelude::*; use std::marker::{Sized, Unsize}; -#[derive(Default)] -pub struct WidgetPtr { - pub inner: Option, +pub struct WidgetPtr { + pub inner: Option>, } -impl Widget for WidgetPtr { - fn draw(&mut self, painter: &mut Painter) { +impl Widget for WidgetPtr { + fn draw(&mut self, painter: &mut Painter) { if let Some(id) = &self.inner { painter.widget(id); } } - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { if let Some(id) = &self.inner { ctx.width(id) } else { @@ -21,7 +20,7 @@ impl Widget for WidgetPtr { } } - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { if let Some(id) = &self.inner { ctx.height(id) } else { @@ -30,18 +29,29 @@ impl Widget for WidgetPtr { } } -impl WidgetPtr { +impl WidgetPtr { pub fn new() -> Self { Self::default() } - pub fn set>(&mut self, to: WidgetHandle) { + pub fn empty() -> Self { + Self { + inner: Default::default(), + } + } + pub fn set>>(&mut self, to: WidgetHandle) { self.inner = Some(to) } - pub fn replace>( + pub fn replace>>( &mut self, - to: WidgetHandle, - ) -> Option { + to: WidgetHandle, + ) -> Option> { self.inner.replace(to) } } + +impl Default for WidgetPtr { + fn default() -> Self { + Self::empty() + } +} diff --git a/src/widget/rect.rs b/src/widget/rect.rs index f72820e..64a5596 100644 --- a/src/widget/rect.rs +++ b/src/widget/rect.rs @@ -27,8 +27,8 @@ impl Rect { } } -impl Widget for Rect { - fn draw(&mut self, painter: &mut Painter) { +impl Widget for Rect { + fn draw(&mut self, painter: &mut Painter) { painter.primitive(RectPrimitive { color: self.color, radius: self.radius, @@ -37,11 +37,11 @@ impl Widget for Rect { }); } - fn desired_width(&mut self, _: &mut SizeCtx) -> Len { + fn desired_width(&mut self, _: &mut SizeCtx) -> Len { Len::rest(1) } - fn desired_height(&mut self, _: &mut SizeCtx) -> Len { + fn desired_height(&mut self, _: &mut SizeCtx) -> Len { Len::rest(1) } } diff --git a/src/widget/text/build.rs b/src/widget/text/build.rs index ac4468c..8df5f73 100644 --- a/src/widget/text/build.rs +++ b/src/widget/text/build.rs @@ -1,15 +1,16 @@ use crate::prelude::*; use cosmic_text::{Attrs, Family, Metrics}; -use std::marker::Sized; +use std::marker::{PhantomData, Sized}; -pub struct TextBuilder { +pub struct TextBuilder = ()> { pub content: String, pub attrs: TextAttrs, pub hint: H, pub output: O, + state: PhantomData, } -impl TextBuilder { +impl> TextBuilder { pub fn size(mut self, size: impl UiNum) -> Self { self.attrs.font_size = size.to_f32(); self.attrs.line_height = self.attrs.font_size * LINE_HEIGHT_MULT; @@ -39,37 +40,48 @@ impl TextBuilder { self.attrs.wrap = wrap; self } - pub fn editable(self, single_line: bool) -> TextBuilder { + pub fn editable(self, single_line: bool) -> TextBuilder { TextBuilder { content: self.content, attrs: self.attrs, hint: self.hint, output: TextEditOutput { single_line }, + state: PhantomData, } } } -impl TextBuilder { - pub fn hint, Tag>(self, hint: W) -> TextBuilder { +impl TextBuilder { + pub fn hint, Tag>( + self, + hint: W, + ) -> TextBuilder> { TextBuilder { content: self.content, attrs: self.attrs, - hint: move |ui: &mut Ui| Some(hint.add(ui).any()), + hint: move |ui: &mut Ui| Some(hint.add(ui).any()), output: self.output, + state: PhantomData, } } } -pub trait TextBuilderOutput: Sized { +pub trait TextBuilderOutput: Sized { type Output; - fn run(ui: &mut Ui, builder: TextBuilder) -> Self::Output; + fn run>( + ui: &mut Ui, + builder: TextBuilder, + ) -> Self::Output; } pub struct TextOutput; -impl TextBuilderOutput for TextOutput { - type Output = Text; +impl TextBuilderOutput for TextOutput { + type Output = Text; - fn run(ui: &mut Ui, builder: TextBuilder) -> Self::Output { + fn run>( + ui: &mut Ui, + builder: TextBuilder, + ) -> Self::Output { let mut buf = TextBuffer::new_empty(Metrics::new( builder.attrs.font_size, builder.attrs.line_height, @@ -90,10 +102,13 @@ impl TextBuilderOutput for TextOutput { pub struct TextEditOutput { single_line: bool, } -impl TextBuilderOutput for TextEditOutput { - type Output = TextEdit; +impl TextBuilderOutput for TextEditOutput { + type Output = TextEdit; - fn run(ui: &mut Ui, builder: TextBuilder) -> Self::Output { + fn run>( + ui: &mut Ui, + builder: TextBuilder, + ) -> Self::Output { let buf = TextBuffer::new_empty(Metrics::new( builder.attrs.font_size, builder.attrs.line_height, @@ -110,19 +125,22 @@ impl TextBuilderOutput for TextEditOutput { } } -impl FnOnce<(&mut Ui,)> for TextBuilder { +impl, H: WidgetOption> FnOnce<(&mut Ui,)> + for TextBuilder +{ type Output = O::Output; - extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { + extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { O::run(args.0, self) } } -pub fn wtext(content: impl Into) -> TextBuilder { +pub fn wtext(content: impl Into) -> TextBuilder { TextBuilder { content: content.into(), attrs: TextAttrs::default(), hint: (), output: TextOutput, + state: PhantomData, } } diff --git a/src/widget/text/edit.rs b/src/widget/text/edit.rs index 1a712ff..897624d 100644 --- a/src/widget/text/edit.rs +++ b/src/widget/text/edit.rs @@ -7,16 +7,16 @@ use winit::{ keyboard::{Key, NamedKey}, }; -pub struct TextEdit { - view: TextView, +pub struct TextEdit { + view: TextView, selection: TextSelection, history: Vec<(String, TextSelection)>, double_hit: Option, pub single_line: bool, } -impl TextEdit { - pub fn new(view: TextView, single_line: bool) -> Self { +impl TextEdit { + pub fn new(view: TextView, single_line: bool) -> Self { Self { view, selection: Default::default(), @@ -43,8 +43,8 @@ impl TextEdit { } } -impl Widget for TextEdit { - fn draw(&mut self, painter: &mut Painter) { +impl Widget for TextEdit { + fn draw(&mut self, painter: &mut Painter) { let base = painter.layer; painter.child_layer(); self.view.draw(painter); @@ -86,11 +86,11 @@ impl Widget for TextEdit { } } - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { self.view.desired_width(ctx) } - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { self.view.desired_height(ctx) } } @@ -174,12 +174,12 @@ fn cursor_pos(cursor: Cursor, buf: &TextBuffer) -> Option { prev } -pub struct TextEditCtx<'a> { - pub text: &'a mut TextEdit, +pub struct TextEditCtx<'a, State> { + pub text: &'a mut TextEdit, pub font_system: &'a mut FontSystem, } -impl<'a> TextEditCtx<'a> { +impl<'a, State: 'static> TextEditCtx<'a, State> { pub fn take(&mut self) -> String { let text = self .text @@ -596,26 +596,26 @@ impl TextInputResult { } } -impl Deref for TextEdit { - type Target = TextView; +impl Deref for TextEdit { + type Target = TextView; fn deref(&self) -> &Self::Target { &self.view } } -impl DerefMut for TextEdit { +impl DerefMut for TextEdit { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.view } } -pub trait TextEditable { - fn edit<'a>(&self, ui: &'a mut Ui) -> TextEditCtx<'a>; +pub trait TextEditable { + fn edit<'a>(&self, ui: &'a mut Ui) -> TextEditCtx<'a, State>; } -impl> TextEditable for I { - fn edit<'a>(&self, ui: &'a mut Ui) -> TextEditCtx<'a> { +impl>> TextEditable for I { + fn edit<'a>(&self, ui: &'a mut Ui) -> TextEditCtx<'a, State> { TextEditCtx { text: ui.data.widgets.get_mut(self).unwrap(), font_system: &mut ui.data.text.font_system, diff --git a/src/widget/text/mod.rs b/src/widget/text/mod.rs index 54375a1..b4d0760 100644 --- a/src/widget/text/mod.rs +++ b/src/widget/text/mod.rs @@ -11,22 +11,22 @@ use std::ops::{Deref, DerefMut}; pub const SHAPING: Shaping = Shaping::Advanced; -pub struct Text { +pub struct Text { pub content: MutDetect, - view: TextView, + view: TextView, } -pub struct TextView { +pub struct TextView { pub attrs: MutDetect, pub buf: MutDetect, // cache tex: Option, width: Option, - pub hint: Option, + pub hint: Option>, } -impl TextView { - pub fn new(buf: TextBuffer, attrs: TextAttrs, hint: Option) -> Self { +impl TextView { + pub fn new(buf: TextBuffer, attrs: TextAttrs, hint: Option>) -> Self { Self { attrs: attrs.into(), buf: buf.into(), @@ -54,7 +54,7 @@ impl TextView { region } - fn render(&mut self, ctx: &mut SizeCtx) -> RenderedText { + fn render(&mut self, ctx: &mut SizeCtx) -> RenderedText { let width = if self.attrs.wrap { Some(ctx.px_size().x) } else { @@ -80,7 +80,7 @@ impl TextView { pub fn tex(&self) -> Option<&RenderedText> { self.tex.as_ref() } - pub fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + pub fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { if let Some(hint) = &self.hint && let [line] = &self.buf.lines[..] && line.text().is_empty() @@ -90,7 +90,7 @@ impl TextView { Len::abs(self.render(ctx).size.x) } } - pub fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + pub fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { if let Some(hint) = &self.hint && let [line] = &self.buf.lines[..] && line.text().is_empty() @@ -100,7 +100,7 @@ impl TextView { Len::abs(self.render(ctx).size.y) } } - pub fn draw(&mut self, painter: &mut Painter) -> UiRegion { + pub fn draw(&mut self, painter: &mut Painter) -> UiRegion { let tex = self.render(&mut painter.size_ctx()); let region = self.tex_region(&tex); if let Some(hint) = &self.hint @@ -124,7 +124,7 @@ impl TextView { } } -impl Text { +impl Text { pub fn new(content: impl Into) -> Self { let attrs = TextAttrs::default(); let buf = TextBuffer::new_empty(Metrics::new(attrs.font_size, attrs.line_height)); @@ -133,7 +133,7 @@ impl Text { view: TextView::new(buf, attrs, None), } } - fn update_buf(&mut self, ctx: &mut SizeCtx) { + fn update_buf(&mut self, ctx: &mut SizeCtx) { if self.content.changed { self.content.changed = false; self.view.buf.set_text( @@ -147,18 +147,18 @@ impl Text { } } -impl Widget for Text { - fn draw(&mut self, painter: &mut Painter) { +impl Widget for Text { + fn draw(&mut self, painter: &mut Painter) { self.update_buf(&mut painter.size_ctx()); self.view.draw(painter); } - fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { self.update_buf(ctx); self.view.desired_width(ctx) } - fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { + fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { self.update_buf(ctx); self.view.desired_height(ctx) } @@ -174,7 +174,7 @@ pub fn edit_line(line: &mut BufferLine, text: String) { line.set_text(text, line.ending(), line.attrs_list().clone()); } -impl Deref for Text { +impl Deref for Text { type Target = TextAttrs; fn deref(&self) -> &Self::Target { @@ -182,13 +182,13 @@ impl Deref for Text { } } -impl DerefMut for Text { +impl DerefMut for Text { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.view } } -impl Deref for TextView { +impl Deref for TextView { type Target = TextAttrs; fn deref(&self) -> &Self::Target { @@ -196,7 +196,7 @@ impl Deref for TextView { } } -impl DerefMut for TextView { +impl DerefMut for TextView { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.attrs } diff --git a/src/widget/trait_fns.rs b/src/widget/trait_fns.rs index f70b26e..5513877 100644 --- a/src/widget/trait_fns.rs +++ b/src/widget/trait_fns.rs @@ -2,30 +2,28 @@ use super::*; use crate::prelude::*; // these methods should "not require any context" (require unit) because they're in core -event_ctx!(()); - widget_trait! { - pub trait CoreWidget; + pub trait CoreWidget; - fn pad(self, padding: impl Into) -> impl WidgetFn { + fn pad(self, padding: impl Into) -> impl WidgetFn> { |ui| Pad { padding: padding.into(), inner: self.add(ui), } } - fn align(self, align: impl Into) -> impl WidgetFn { + fn align(self, align: impl Into) -> impl WidgetFn> { move |ui| Aligned { inner: self.add(ui), align: align.into(), } } - fn center(self) -> impl WidgetFn { + fn center(self) -> impl WidgetFn> { self.align(Align::CENTER) } - fn label(self, label: impl Into) -> impl WidgetIdFn { + fn label(self, label: impl Into) -> impl WidgetIdFn { |ui| { let id = self.add(ui); ui.set_label(&id, label.into()); @@ -33,7 +31,7 @@ widget_trait! { } } - fn sized(self, size: impl Into) -> impl WidgetFn { + fn sized(self, size: impl Into) -> impl WidgetFn> { let size = size.into(); move |ui| Sized { inner: self.add(ui), @@ -42,7 +40,7 @@ widget_trait! { } } - fn max_width(self, len: impl Into) -> impl WidgetFn { + fn max_width(self, len: impl Into) -> impl WidgetFn> { let len = len.into(); move |ui| MaxSize { inner: self.add(ui), @@ -51,7 +49,7 @@ widget_trait! { } } - fn max_height(self, len: impl Into) -> impl WidgetFn { + fn max_height(self, len: impl Into) -> impl WidgetFn> { let len = len.into(); move |ui| MaxSize { inner: self.add(ui), @@ -60,7 +58,7 @@ widget_trait! { } } - fn width(self, len: impl Into) -> impl WidgetFn { + fn width(self, len: impl Into) -> impl WidgetFn> { let len = len.into(); move |ui| Sized { inner: self.add(ui), @@ -69,7 +67,7 @@ widget_trait! { } } - fn height(self, len: impl Into) -> impl WidgetFn { + fn height(self, len: impl Into) -> impl WidgetFn> { let len = len.into(); move |ui| Sized { inner: self.add(ui), @@ -78,66 +76,69 @@ widget_trait! { } } - fn offset(self, amt: impl Into) -> impl WidgetFn { + fn offset(self, amt: impl Into) -> impl WidgetFn> { move |ui| Offset { inner: self.add(ui), amt: amt.into(), } } - fn scroll(self) -> impl WidgetIdFn { + fn scroll(self) -> impl WidgetIdFn> { + use eventable::*; move |ui| { Scroll::new(self.add(ui), Axis::Y) .on(CursorSense::Scroll, |ctx| { - let s = &mut ctx.ui[ctx.widget]; + let s = &mut ctx.state.ui()[ctx.widget]; s.scroll(ctx.data.scroll_delta.y * 50.0); }) .add(ui) } } - fn masked(self) -> impl WidgetFn { + fn masked(self) -> impl WidgetFn> { move |ui| Masked { inner: self.add(ui), } } - fn background(self, w: impl WidgetLike) -> impl WidgetFn { + fn background(self, w: impl WidgetLike) -> impl WidgetFn> { move |ui| Stack { children: vec![w.add(ui), self.add(ui)], size: StackSize::Child(1), } } - fn foreground(self, w: impl WidgetLike) -> impl WidgetFn { + fn foreground(self, w: impl WidgetLike) -> impl WidgetFn> { move |ui| Stack { children: vec![self.add(ui), w.add(ui)], size: StackSize::Child(0), } } - fn layer_offset(self, offset: usize) -> impl WidgetFn { + fn layer_offset(self, offset: usize) -> impl WidgetFn> { move |ui| LayerOffset { inner: self.add(ui), offset, } } - fn to_any(self) -> impl WidgetIdFn { + fn to_any(self) -> impl WidgetIdFn { |ui| self.add(ui) } } -pub trait CoreWidgetArr, Tag> { - fn span(self, dir: Dir) -> SpanBuilder; - fn stack(self) -> StackBuilder; +pub trait CoreWidgetArr, Tag> { + fn span(self, dir: Dir) -> SpanBuilder; + fn stack(self) -> StackBuilder; } -impl, Tag> CoreWidgetArr for Wa { - fn span(self, dir: Dir) -> SpanBuilder { +impl, Tag> + CoreWidgetArr for Wa +{ + fn span(self, dir: Dir) -> SpanBuilder { SpanBuilder::new(self, dir) } - fn stack(self) -> StackBuilder { + fn stack(self) -> StackBuilder { StackBuilder::new(self) } }