diff --git a/src/core/image.rs b/src/core/image.rs index 3dc19e2..4cfd10a 100644 --- a/src/core/image.rs +++ b/src/core/image.rs @@ -15,7 +15,7 @@ impl Widget for Image { } } -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/core/text.rs b/src/core/text.rs index 093875b..da0e684 100644 --- a/src/core/text.rs +++ b/src/core/text.rs @@ -116,10 +116,10 @@ impl TextBuilder { } } -impl FnOnce<(&mut Ui,)> for TextBuilder { +impl FnOnce<(&mut Ui,)> for TextBuilder { type Output = Text; - extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { + extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { let mut buf = TextBuffer::new_empty(Metrics::new(self.attrs.font_size, self.attrs.line_height)); buf.set_text( diff --git a/src/core/text_edit.rs b/src/core/text_edit.rs index 2adb655..ad59e3d 100644 --- a/src/core/text_edit.rs +++ b/src/core/text_edit.rs @@ -255,10 +255,10 @@ impl TextInputResult { } } -impl FnOnce<(&mut Ui,)> for TextEditBuilder { +impl FnOnce<(&mut Ui,)> for TextEditBuilder { type Output = TextEdit; - extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { + extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { let mut text = TextEdit { buf: TextBuffer::new_empty(Metrics::new(self.attrs.font_size, self.attrs.line_height)), attrs: self.attrs, diff --git a/src/core/trait_fns.rs b/src/core/trait_fns.rs index dd75ab1..fac192c 100644 --- a/src/core/trait_fns.rs +++ b/src/core/trait_fns.rs @@ -1,34 +1,34 @@ use super::*; use crate::prelude::*; -pub trait CoreWidget { - fn pad(self, padding: impl Into) -> impl WidgetFn; - fn align(self, align: Align) -> impl WidgetFn; - fn center(self) -> impl WidgetFn; - fn label(self, label: impl Into) -> impl WidgetIdFn; - fn size(self, size: impl Into) -> impl WidgetFn; +pub trait CoreWidget { + fn pad(self, padding: impl Into) -> impl WidgetFn; + fn align(self, align: Align) -> impl WidgetFn; + fn center(self) -> impl WidgetFn; + fn label(self, label: impl Into) -> impl WidgetIdFn; + fn size(self, size: impl Into) -> impl WidgetFn; } -impl, Ctx, Tag> CoreWidget for W { - fn pad(self, padding: impl Into) -> impl WidgetFn { +impl, Tag> CoreWidget for W { + fn pad(self, padding: impl Into) -> impl WidgetFn { |ui| Padded { padding: padding.into(), inner: self.add(ui).any(), } } - fn align(self, align: Align) -> impl WidgetFn { + fn align(self, align: Align) -> impl WidgetFn { move |ui| Aligned { inner: self.add(ui).any(), align, } } - 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()); @@ -36,7 +36,7 @@ impl, Ctx, Tag> CoreWidget for W { } } - fn size(self, size: impl Into) -> impl WidgetFn { + fn size(self, size: impl Into) -> impl WidgetFn { move |ui| Sized { inner: self.add(ui).any(), size: size.into(), @@ -44,20 +44,20 @@ impl, Ctx, Tag> CoreWidget for W { } } -pub trait CoreWidgetArr { - fn span(self, dir: Dir, lengths: impl IntoSpanLens) -> impl WidgetFn; - fn stack(self) -> impl WidgetFn; +pub trait CoreWidgetArr { + fn span(self, dir: Dir, lengths: impl IntoSpanLens) -> impl WidgetFn; + fn stack(self) -> impl WidgetFn; } -impl, Ctx, Tag> CoreWidgetArr for Wa { - fn span(self, dir: Dir, lengths: impl IntoSpanLens) -> impl WidgetFn { +impl, Tag> CoreWidgetArr for Wa { + fn span(self, dir: Dir, lengths: impl IntoSpanLens) -> impl WidgetFn { let lengths = lengths.into_lens(); move |ui| Span { children: self.ui(ui).arr.into_iter().zip(lengths).collect(), dir, } } - fn stack(self) -> impl WidgetFn { + fn stack(self) -> impl WidgetFn { move |ui| Stack { children: self.ui(ui).arr.to_vec(), } diff --git a/src/layout/event.rs b/src/layout/event.rs index 8202d30..81fa5a5 100644 --- a/src/layout/event.rs +++ b/src/layout/event.rs @@ -1,10 +1,10 @@ use crate::{ - layout::{Ui, UiModule, Widget, WidgetId, WidgetIdFn, WidgetLike}, + layout::{IdFnTag, Ui, UiModule, Widget, WidgetId, WidgetIdFn, WidgetLike}, util::Id, }; pub trait UiCtx { - fn ui(&mut self) -> &mut Ui + fn ui(&mut self) -> &mut Ui where Self: Sized; } @@ -22,13 +22,17 @@ pub trait EventFn: FnMut(&mut Ctx, Data) + 'static {} impl EventFn for F {} pub trait Eventable { - fn on>(self, event: E, f: impl EventFn) -> impl WidgetIdFn; + fn on>( + self, + event: E, + f: impl EventFn, + ) -> impl WidgetIdFn + Eventable; fn id_on>( self, event: E, f: impl FnMut(&WidgetId, &mut Ctx, E::Data) + 'static, - ) -> impl WidgetIdFn + ) -> impl WidgetIdFn + Eventable where W: Widget; @@ -36,18 +40,29 @@ pub trait Eventable { self, event: E, f: impl FnMut(&mut W, E::Data) + 'static, - ) -> impl WidgetIdFn + ) -> impl WidgetIdFn + Eventable where W: Widget, Ctx: UiCtx; } -impl, Ctx, Tag> Eventable for W { +pub trait Ctxable { + /// sets context which lets event functions work without needing to specify generics + fn ctx(self) -> impl WidgetLike + Eventable; +} + +impl, Tag> Ctxable for W { + fn ctx(self) -> impl WidgetLike + Eventable { + self + } +} + +impl, Ctx, Tag> Eventable for W { fn on>( self, event: E, f: impl EventFn, - ) -> impl WidgetIdFn { + ) -> impl WidgetIdFn + Eventable { move |ui| { let id = self.add(ui); ui.modules @@ -61,7 +76,7 @@ impl, Ctx, Tag> Eventable for W { self, event: E, mut f: impl FnMut(&WidgetId, &mut Ctx, E::Data) + 'static, - ) -> impl WidgetIdFn + ) -> impl WidgetIdFn + Eventable where W::Widget: Widget, { @@ -75,7 +90,7 @@ impl, Ctx, Tag> Eventable for W { self, event: E, mut f: impl FnMut(&mut W::Widget, E::Data) + 'static, - ) -> impl WidgetIdFn + ) -> impl WidgetIdFn + Eventable where W::Widget: Widget, Ctx: UiCtx, diff --git a/src/layout/id.rs b/src/layout/id.rs index c04fba5..91c6241 100644 --- a/src/layout/id.rs +++ b/src/layout/id.rs @@ -131,14 +131,14 @@ impl Drop for WidgetId { pub struct IdTag; pub struct IdFnTag; -pub trait WidgetIdFn: FnOnce(&mut Ui) -> WidgetId {} -impl) -> WidgetId, Ctx> WidgetIdFn for F {} +pub trait WidgetIdFn: FnOnce(&mut Ui) -> WidgetId {} +impl WidgetId> WidgetIdFn for F {} /// TODO: does this ever make sense to use? it allows for invalid ids -pub trait Idable { +pub trait Idable { type Widget: Widget; - fn set(self, ui: &mut Ui, id: &WidgetId); - fn id(self, id: &WidgetId) -> impl WidgetIdFn + fn set(self, ui: &mut Ui, id: &WidgetId); + fn id(self, id: &WidgetId) -> impl WidgetIdFn where Self: Sized, { @@ -148,7 +148,7 @@ pub trait Idable { id } } - fn id_static(self, id: StaticWidgetId) -> impl WidgetIdFn + fn id_static(self, id: StaticWidgetId) -> impl WidgetIdFn where Self: Sized, { @@ -160,33 +160,33 @@ pub trait Idable { } } -impl Idable for W { +impl Idable for W { type Widget = W; - fn set(self, ui: &mut Ui, id: &WidgetId) { + fn set(self, ui: &mut Ui, id: &WidgetId) { ui.set(id, self); } } -impl) -> W, W: Widget, Ctx> Idable for F { +impl W, W: Widget> Idable for F { type Widget = W; - fn set(self, ui: &mut Ui, id: &WidgetId) { + fn set(self, ui: &mut Ui, id: &WidgetId) { let w = self(ui); ui.set(id, w); } } -impl WidgetLike for WidgetId { +impl WidgetLike for WidgetId { type Widget = W; - fn add(self, _: &mut Ui) -> WidgetId { + fn add(self, _: &mut Ui) -> WidgetId { self } } -impl) -> WidgetId, Ctx> WidgetLike for F { +impl WidgetId> WidgetLike for F { type Widget = W; - fn add(self, ui: &mut Ui) -> WidgetId { + fn add(self, ui: &mut Ui) -> WidgetId { self(ui) } } @@ -200,9 +200,9 @@ impl StaticWidgetId { } } -impl WidgetLike for StaticWidgetId { +impl WidgetLike for StaticWidgetId { type Widget = W; - fn add(self, ui: &mut Ui) -> WidgetId { + fn add(self, ui: &mut Ui) -> WidgetId { self.id(&ui.send) } } diff --git a/src/layout/ui.rs b/src/layout/ui.rs index fdf181d..e3e2c43 100644 --- a/src/layout/ui.rs +++ b/src/layout/ui.rs @@ -10,13 +10,11 @@ use crate::{ }; use std::{ any::{Any, TypeId}, - marker::PhantomData, ops::{Index, IndexMut}, sync::mpsc::{Receiver, Sender, channel}, }; -// TODO: remove generic -pub struct Ui { +pub struct Ui { root: Option, pub(super) widgets: Widgets, updates: Vec, @@ -31,17 +29,16 @@ pub struct Ui { pub(crate) active: ActiveWidgets, pub modules: Modules, - _pd: PhantomData, } -impl Ui { - pub fn add(&mut self, w: impl WidgetLike) -> WidgetId { +impl Ui { + pub fn add(&mut self, w: impl WidgetLike) -> WidgetId { w.add(self) } pub fn add_static( &mut self, - w: impl WidgetLike, + w: impl WidgetLike, ) -> StaticWidgetId { let id = w.add(self); id.into_static() @@ -70,7 +67,7 @@ impl Ui { self.widgets.insert(id.id.duplicate(), w); } - pub fn set_root(&mut self, w: impl WidgetLike) { + pub fn set_root(&mut self, w: impl WidgetLike) { self.root = Some(w.add(self).any()); self.full_redraw = true; } @@ -184,7 +181,7 @@ impl Ui { } } -impl Index<&WidgetId> for Ui { +impl Index<&WidgetId> for Ui { type Output = W; fn index(&self, id: &WidgetId) -> &Self::Output { @@ -192,14 +189,14 @@ impl Index<&WidgetId> for Ui { } } -impl IndexMut<&WidgetId> for Ui { +impl IndexMut<&WidgetId> for Ui { fn index_mut(&mut self, id: &WidgetId) -> &mut Self::Output { self.updates.push(id.id.duplicate()); self.get_mut(id).unwrap() } } -impl Index> for Ui { +impl Index> for Ui { type Output = W; fn index(&self, id: StaticWidgetId) -> &Self::Output { @@ -207,7 +204,7 @@ impl Index> for Ui { } } -impl IndexMut> for Ui { +impl IndexMut> for Ui { fn index_mut(&mut self, id: StaticWidgetId) -> &mut Self::Output { self.updates.push(id.id.id()); self.widgets.get_static_mut(&id).unwrap() @@ -224,7 +221,7 @@ impl dyn Widget { } } -impl Default for Ui { +impl Default for Ui { fn default() -> Self { let (send, recv) = channel(); Self { @@ -240,7 +237,6 @@ impl Default for Ui { recv, size: Vec2::ZERO, modules: Modules::default(), - _pd: PhantomData, } } } diff --git a/src/layout/widget.rs b/src/layout/widget.rs index d00d924..9763ce9 100644 --- a/src/layout/widget.rs +++ b/src/layout/widget.rs @@ -12,13 +12,13 @@ pub trait Widget: Any { pub struct WidgetTag; pub struct FnTag; -pub trait WidgetLike { +pub trait WidgetLike { type Widget: 'static; - fn add(self, ui: &mut Ui) -> WidgetId; + fn add(self, ui: &mut Ui) -> WidgetId; fn with_id( self, - f: impl FnOnce(&mut Ui, WidgetId) -> WidgetId, - ) -> impl WidgetIdFn + f: impl FnOnce(&mut Ui, WidgetId) -> WidgetId, + ) -> impl WidgetIdFn where Self: Sized, { @@ -27,13 +27,13 @@ pub trait WidgetLike { f(ui, id) } } - fn add_static(self, ui: &mut Ui) -> StaticWidgetId + fn add_static(self, ui: &mut Ui) -> StaticWidgetId where Self: Sized, { self.add(ui).into_static() } - fn set_root(self, ui: &mut Ui) + fn set_root(self, ui: &mut Ui) where Self: Sized, { @@ -44,19 +44,19 @@ pub trait WidgetLike { /// 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, Ctx> WidgetFn for F {} +pub trait WidgetFn: FnOnce(&mut Ui) -> W {} +impl W> WidgetFn for F {} -impl) -> W, Ctx> WidgetLike for F { +impl W> WidgetLike for F { type Widget = W; - fn add(self, ui: &mut Ui) -> WidgetId { + fn add(self, ui: &mut Ui) -> WidgetId { self(ui).add(ui) } } -impl WidgetLike for W { +impl WidgetLike for W { type Widget = W; - fn add(self, ui: &mut Ui) -> WidgetId { + fn add(self, ui: &mut Ui) -> WidgetId { ui.add_widget(self) } } @@ -76,21 +76,21 @@ impl WidgetArr { } pub struct ArrTag; -pub trait WidgetArrLike { +pub trait WidgetArrLike { type Ws; - fn ui(self, ui: &mut Ui) -> WidgetArr; + fn ui(self, ui: &mut Ui) -> WidgetArr; } -impl WidgetArrLike for WidgetArr { +impl WidgetArrLike for WidgetArr { type Ws = Ws; - fn ui(self, _: &mut Ui) -> WidgetArr { + fn ui(self, _: &mut Ui) -> WidgetArr { self } } -impl, Ctx> WidgetArrLike<1, Ctx, WidgetTag> for W { +impl> WidgetArrLike<1, WidgetTag> for W { type Ws = (W::Widget,); - fn ui(self, ui: &mut Ui) -> WidgetArr<1, (W::Widget,)> { + fn ui(self, ui: &mut Ui) -> WidgetArr<1, (W::Widget,)> { WidgetArr::new([self.add(ui).any()]) } } @@ -101,9 +101,9 @@ macro_rules! impl_widget_arr { impl_widget_arr!($n;$($W)*;$(${concat($W,Tag)})*); }; ($n:expr;$($W:ident)*;$($Tag:ident)*) => { - impl,$Tag,)*> WidgetArrLike<$n, Ctx, ($($Tag,)*)> for ($($W,)*) { + impl<$($W: WidgetLike<$Tag>,$Tag,)*> WidgetArrLike<$n, ($($Tag,)*)> for ($($W,)*) { type Ws = ($($W::Widget,)*); - fn ui(self, ui: &mut Ui) -> WidgetArr<$n, ($($W::Widget,)*)> { + fn ui(self, ui: &mut Ui) -> WidgetArr<$n, ($($W::Widget,)*)> { #[allow(non_snake_case)] let ($($W,)*) = self; WidgetArr::new( diff --git a/src/render/mod.rs b/src/render/mod.rs index cacceb6..7764cee 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -57,7 +57,7 @@ impl UiRenderer { } } - 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, ulayer) in ui.layers.iter_mut() { self.active.push(i); diff --git a/src/testing/input.rs b/src/testing/input.rs index dd9014c..5998afc 100644 --- a/src/testing/input.rs +++ b/src/testing/input.rs @@ -9,7 +9,7 @@ pub struct Input { } impl Input { - pub fn event(&mut self, event: &WindowEvent) { + pub fn event(&mut self, event: &WindowEvent) -> bool { match event { WindowEvent::CursorMoved { position, .. } => { self.cursor.pos = Vec2::new(position.x as f32, position.y as f32); @@ -28,8 +28,9 @@ impl Input { WindowEvent::CursorLeft { .. } => { self.cursor.exists = false; } - _ => (), + _ => return false, } + true } pub fn end_frame(&mut self) { diff --git a/src/testing/mod.rs b/src/testing/mod.rs index 282a340..1cb814b 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -19,7 +19,7 @@ pub fn main() { pub struct Client { renderer: Renderer, input: Input, - ui: Ui, + ui: Ui, info: WidgetId, focus: Option>, } @@ -155,7 +155,8 @@ impl Client { let switch_button = |color, to, label| { let rect = rect(color) - .id_on(Sense::click(), move |id, ctx: &mut Client, _| { + .ctx::() + .id_on(Sense::click(), move |id, ctx, _| { ctx.ui[main].inner.set_static(to); ctx.ui[id].color = color.darker(0.3); }) @@ -201,16 +202,18 @@ impl Client { } pub fn event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop) { - self.input.event(&event); + let input_changed = self.input.event(&event); let cursor_state = self.cursor_state().clone(); - let window_size = self.window_size(); if let Some(focus) = &self.focus && cursor_state.buttons.left.is_start() { self.ui.text(focus).deselect(); self.focus = None; } - run_sensors(self, &cursor_state, window_size); + if input_changed { + let window_size = self.window_size(); + run_sensors(self, &cursor_state, window_size); + } match event { WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::RedrawRequested => { @@ -249,7 +252,7 @@ impl Client { } impl UiCtx for Client { - fn ui(&mut self) -> &mut Ui { + fn ui(&mut self) -> &mut Ui { &mut self.ui } } diff --git a/src/testing/render/mod.rs b/src/testing/render/mod.rs index adb4353..d57b4ad 100644 --- a/src/testing/render/mod.rs +++ b/src/testing/render/mod.rs @@ -21,7 +21,7 @@ pub struct Renderer { } impl Renderer { - pub fn update(&mut self, updates: &mut Ui) { + pub fn update(&mut self, updates: &mut Ui) { self.ui.update(&self.device, &self.queue, updates); }