From 434e3c3af7c726ec869ce9ed4a1fcd7a4adc2afc Mon Sep 17 00:00:00 2001 From: shadow cat Date: Mon, 8 Dec 2025 00:12:11 -0500 Subject: [PATCH] view + a bunch of fixes or smth idek man --- src/bin/test/main.rs | 7 +--- src/core/sense.rs | 30 ++++++++------- src/core/text/build.rs | 4 +- src/core/text/edit.rs | 4 +- src/core/text/mod.rs | 4 +- src/layout/color.rs | 42 +++++++++++++++++---- src/layout/event.rs | 79 ++++++++++++++++++++++++++++++++++------ src/layout/mod.rs | 2 + src/layout/painter.rs | 4 +- src/layout/text.rs | 13 +++---- src/layout/ui.rs | 14 +------ src/layout/view.rs | 17 +++++++++ src/layout/widget.rs | 2 +- src/layout/widget_ref.rs | 61 ++++++++++++------------------- src/lib.rs | 3 ++ src/util/handle.rs | 60 ++++++++++++++++++++++++++++++ src/util/mod.rs | 2 + src/winit/mod.rs | 14 ++++--- 18 files changed, 253 insertions(+), 109 deletions(-) create mode 100644 src/layout/view.rs create mode 100644 src/util/handle.rs diff --git a/src/bin/test/main.rs b/src/bin/test/main.rs index ba10e70..a13e41d 100644 --- a/src/bin/test/main.rs +++ b/src/bin/test/main.rs @@ -1,8 +1,5 @@ use cosmic_text::Family; -use iris::{ - prelude::*, - winit::{attr::Selectable, event::Submit, *}, -}; +use iris::prelude::*; use len_fns::*; use winit::event::WindowEvent; @@ -109,7 +106,7 @@ impl DefaultAppState for Client { .text_align(Align::LEFT) .size(30) .attr::(()) - .on(Submit, move |ctx| { + .on(iris::winit::Submit, move |ctx| { let content = ctx.widget.get_mut().take(); let text = wtext(content) .editable(false) diff --git a/src/core/sense.rs b/src/core/sense.rs index 423bb90..a1714ca 100644 --- a/src/core/sense.rs +++ b/src/core/sense.rs @@ -11,7 +11,7 @@ use crate::{ }; #[derive(Clone, Copy, PartialEq)] -pub enum Button { +pub enum CursorButton { Left, Right, Middle, @@ -19,9 +19,9 @@ pub enum Button { #[derive(Clone, Copy, PartialEq)] pub enum CursorSense { - PressStart(Button), - Pressing(Button), - PressEnd(Button), + PressStart(CursorButton), + Pressing(CursorButton), + PressEnd(CursorButton), HoverStart, Hovering, HoverEnd, @@ -32,16 +32,16 @@ pub struct CursorSenses(Vec); impl CursorSense { pub fn click() -> Self { - Self::PressStart(Button::Left) + Self::PressStart(CursorButton::Left) } pub fn click_or_drag() -> CursorSenses { - Self::click() | Self::Pressing(Button::Left) + Self::click() | Self::Pressing(CursorButton::Left) } pub fn unclick() -> Self { - Self::PressEnd(Button::Left) + Self::PressEnd(CursorButton::Left) } pub fn is_dragging(&self) -> bool { - matches!(self, CursorSense::Pressing(Button::Left)) + matches!(self, CursorSense::Pressing(CursorButton::Left)) } } @@ -61,11 +61,11 @@ pub struct CursorButtons { } impl CursorButtons { - pub fn select(&self, button: &Button) -> &ActivationState { + pub fn select(&self, button: &CursorButton) -> &ActivationState { match button { - Button::Left => &self.left, - Button::Right => &self.right, - Button::Middle => &self.middle, + CursorButton::Left => &self.left, + CursorButton::Right => &self.right, + CursorButton::Middle => &self.middle, } } @@ -205,7 +205,11 @@ impl CursorModule { scroll_delta: cursor.scroll_delta, sense, }; - (sensor.f)(EventCtx { ui, state: ctx, data }); + (sensor.f)(EventCtx { + ui, + state: ctx, + data, + }); } } } diff --git a/src/core/text/build.rs b/src/core/text/build.rs index cdc9872..b56a09c 100644 --- a/src/core/text/build.rs +++ b/src/core/text/build.rs @@ -75,7 +75,7 @@ impl TextBuilderOutput for TextOutput { builder.attrs.line_height, )); let hint = builder.hint.get(ui); - let font_system = &mut ui.data.text.borrow_mut().font_system; + let font_system = &mut ui.data.text.get_mut().font_system; buf.set_text(font_system, &builder.content, &Attrs::new(), SHAPING, None); let mut text = Text { content: builder.content.into(), @@ -103,7 +103,7 @@ impl TextBuilderOutput for TextEditOutput { builder.output.single_line, ui.data.text.clone(), ); - let font_system = &mut ui.data.text.borrow_mut().font_system; + let font_system = &mut ui.data.text.get_mut().font_system; text.buf .set_text(font_system, &builder.content, &Attrs::new(), SHAPING, None); builder.attrs.apply(font_system, &mut text.buf, None); diff --git a/src/core/text/edit.rs b/src/core/text/edit.rs index 66da6cb..c108fe2 100644 --- a/src/core/text/edit.rs +++ b/src/core/text/edit.rs @@ -60,7 +60,7 @@ impl TextEdit { pub fn set(&mut self, text: &str) { let text = self.string(text); self.view.buf.set_text( - &mut self.data.borrow_mut().font_system, + &mut self.data.get_mut().font_system, &text, &Attrs::new(), SHAPING, @@ -238,7 +238,7 @@ impl TextEdit { self.view .buf .cursor_motion( - &mut self.data.borrow_mut().font_system, + &mut self.data.get_mut().font_system, cursor, None, motion, diff --git a/src/core/text/mod.rs b/src/core/text/mod.rs index bd1fd45..8fd671d 100644 --- a/src/core/text/mod.rs +++ b/src/core/text/mod.rs @@ -67,7 +67,7 @@ impl TextView { return tex.clone(); } self.width = width; - let mut text_data = ctx.text.borrow_mut(); + let mut text_data = ctx.text.get_mut(); self.attrs .apply(&mut text_data.font_system, &mut self.buf, width); self.buf @@ -139,7 +139,7 @@ impl Text { if self.content.changed { self.content.changed = false; self.view.buf.set_text( - &mut ctx.text.borrow_mut().font_system, + &mut ctx.text.get_mut().font_system, &self.content, &Attrs::new().family(self.view.attrs.family), SHAPING, diff --git a/src/layout/color.rs b/src/layout/color.rs index 345ef0c..01ef1de 100644 --- a/src/layout/color.rs +++ b/src/layout/color.rs @@ -1,5 +1,7 @@ #![allow(clippy::multiple_bound_locations)] +use std::marker::Destruct; + /// stored in linear for sane manipulation #[repr(C)] #[derive(Clone, Copy, Hash, PartialEq, Eq, bytemuck::Zeroable, Debug)] @@ -56,23 +58,47 @@ pub trait ColorNum { const MAX: Self; } -impl Color { - pub fn mul_rgb(self, amt: impl F32Conversion) -> Self { +macro_rules! map_rgb { + ($x:ident,$self:ident, $e:tt) => { + #[allow(unused_braces)] + Self { + r: { + let $x = $self.r; + $e + }, + g: { + let $x = $self.g; + $e + }, + b: { + let $x = $self.b; + $e + }, + a: $self.a, + } + }; +} + +impl Color +where + Self: const Destruct, +{ + pub const fn mul_rgb(self, amt: impl const F32Conversion) -> Self { let amt = amt.to(); - self.map_rgb(|x| T::from(x.to() * amt)) + map_rgb!(x, self, { T::from(x.to() * amt) }) } - pub fn add_rgb(self, amt: impl F32Conversion) -> Self { + pub const fn add_rgb(self, amt: impl const F32Conversion) -> Self { let amt = amt.to(); - self.map_rgb(|x| T::from(x.to() + amt)) + map_rgb!(x, self, { T::from(x.to() + amt) }) } - pub fn darker(self, amt: f32) -> Self { + pub const fn darker(self, amt: f32) -> Self { self.mul_rgb(1.0 - amt) } - pub fn brighter(self, amt: f32) -> Self { - self.map_rgb(|x| { + pub const fn brighter(self, amt: f32) -> Self { + map_rgb!(x, self, { let x = x.to(); T::from(x + (1.0 - x) * amt) }) diff --git a/src/layout/event.rs b/src/layout/event.rs index 66dc3ad..c6a4f01 100644 --- a/src/layout/event.rs +++ b/src/layout/event.rs @@ -28,7 +28,10 @@ pub trait EventFn: Fn(EventCtx) + 'static {} impl) + 'static, Ctx, Data> EventFn for F {} pub trait WidgetEventFn: Fn(EventIdCtx) + 'static {} -impl) + 'static, Ctx, Data, W> WidgetEventFn for F {} +impl) + 'static, Ctx, Data, W: ?Sized> WidgetEventFn + for F +{ +} // TODO: naming in here is a bit weird like eventable #[macro_export] @@ -40,6 +43,7 @@ macro_rules! event_ctx { #[allow(unused_imports)] use $crate::prelude::*; + #[allow(unused)] pub trait EventableCtx { fn on( self, @@ -57,6 +61,33 @@ macro_rules! event_ctx { eventable::Eventable::on(self, event, f) } } + + #[allow(unused)] + pub trait EventableCtxUi + where + WidgetRef: EventableCtx, + { + fn on( + &mut self, + widget: &WidgetRef, + event: E, + f: impl WidgetEventFn, + ); + } + + impl EventableCtxUi for Ui + where + WidgetRef: EventableCtx, + { + fn on( + &mut self, + widget: &WidgetRef, + event: E, + f: impl WidgetEventFn<$ty, E::Data, W>, + ) { + self.register_widget_event(&widget, event, f); + } + } } use local_event_trait::*; }; @@ -82,21 +113,47 @@ pub mod eventable { ) -> impl WidgetIdFn { move |ui| { let id = self.add(ui); - let id_ = id.weak(); - ui.register_event(&id, event, move |ctx| { - f(EventIdCtx { - widget: &id_.expect_strong(), - state: ctx.state, - data: ctx.data, - ui: ctx.ui, - }); - }); + ui.register_widget_event(&id, event, f); id } } } } +impl Ui { + pub fn register_event( + &mut self, + id: &WidgetRef, + event: E, + f: impl EventFn, + ) { + self.data + .modules + .get_mut::>() + .register(id.id(), event, f); + } + + pub fn register_widget_event( + &mut self, + id: &WidgetRef, + event: E, + f: impl WidgetEventFn, + ) { + let id_ = id.weak(); + self.data + .modules + .get_mut::>() + .register(id.id(), event, move |ctx| { + f(EventIdCtx { + widget: &id_.expect_strong(), + ui: ctx.ui, + state: ctx.state, + data: ctx.data, + }) + }); + } +} + pub trait DefaultEvent: Hash + Eq + 'static { type Data: Clone; } @@ -193,7 +250,7 @@ impl Default for DefaultEventModule { } impl Ui { - pub fn run_event( + pub fn run_event( &mut self, ctx: &mut Ctx, id: &WidgetRef, diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 9665692..89ba84c 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -9,6 +9,7 @@ mod painter; mod text; mod texture; mod ui; +mod view; mod widget; mod widget_ref; mod widgets; @@ -24,6 +25,7 @@ pub use painter::*; pub use text::*; pub use texture::*; pub use ui::*; +pub use view::*; pub use widget::*; pub use widget_ref::*; pub use widgets::*; diff --git a/src/layout/painter.rs b/src/layout/painter.rs index c1b23c6..2bc56c9 100644 --- a/src/layout/painter.rs +++ b/src/layout/painter.rs @@ -450,7 +450,7 @@ impl<'a, 'c> Painter<'a, 'c> { pub fn render_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> RenderedText { self.ctx .text - .borrow_mut() + .get_mut() .draw(buffer, attrs, self.ctx.textures) } @@ -604,6 +604,6 @@ impl SizeCtx<'_> { } pub fn draw_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> RenderedText { - self.text.borrow_mut().draw(buffer, attrs, self.textures) + self.text.get_mut().draw(buffer, attrs, self.textures) } } diff --git a/src/layout/text.rs b/src/layout/text.rs index 457e7ec..25d850c 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -1,10 +1,9 @@ -use std::{ - cell::RefCell, - rc::Rc, - simd::{Simd, num::SimdUint}, -}; +use std::simd::{Simd, num::SimdUint}; -use crate::layout::{Align, RegionAlign, TextureHandle, Textures, UiColor, Vec2}; +use crate::{ + layout::{Align, RegionAlign, TextureHandle, Textures, UiColor, Vec2}, + util::Handle, +}; use cosmic_text::{ Attrs, AttrsList, Buffer, CacheKey, Color, Family, FontSystem, Metrics, Placement, SwashCache, SwashContent, @@ -16,7 +15,7 @@ pub mod text_lib { pub use cosmic_text::*; } -pub type TextData = Rc>; +pub type TextData = Handle; pub struct TextDataInner { pub font_system: FontSystem, diff --git a/src/layout/ui.rs b/src/layout/ui.rs index 153ec33..52c23dc 100644 --- a/src/layout/ui.rs +++ b/src/layout/ui.rs @@ -3,7 +3,7 @@ use image::DynamicImage; use crate::{ layout::{ Event, EventFn, EventModule, IdLike, PainterData, PixelRegion, TextureHandle, Vec2, Widget, - WidgetInstance, WidgetLike, WidgetRef, WidgetUpdate, + WidgetEventFn, WidgetInstance, WidgetLike, WidgetRef, WidgetUpdate, }, util::{HashSet, Id}, }; @@ -42,18 +42,6 @@ impl Ui { self.data.textures.add(image) } - pub fn register_event( - &mut self, - id: &WidgetRef, - event: E, - f: impl EventFn, - ) { - self.data - .modules - .get_mut::>() - .register(id.id(), event, f); - } - pub fn resize(&mut self, size: impl Into) { self.data.output_size = size.into(); self.resized = true; diff --git a/src/layout/view.rs b/src/layout/view.rs new file mode 100644 index 0000000..74c706a --- /dev/null +++ b/src/layout/view.rs @@ -0,0 +1,17 @@ +use crate::layout::{Widget, WidgetLike, WidgetRef}; +use std::marker::Unsize; + +pub trait WidgetView { + type Widget: Widget + ?Sized + Unsize = dyn Widget; + fn view(&self) -> &WidgetRef; +} + +pub struct ViewTag; + +impl WidgetLike for WV { + type Widget = WV::Widget; + + fn add(self, _ui: &mut super::Ui) -> WidgetRef { + self.view().clone() + } +} diff --git a/src/layout/widget.rs b/src/layout/widget.rs index 008637a..cdd04d7 100644 --- a/src/layout/widget.rs +++ b/src/layout/widget.rs @@ -61,7 +61,7 @@ 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 {} +pub trait WidgetFn: FnOnce(&mut Ui) -> W {} impl W> WidgetFn for F {} impl W> WidgetLike for F { diff --git a/src/layout/widget_ref.rs b/src/layout/widget_ref.rs index c788e52..738d09f 100644 --- a/src/layout/widget_ref.rs +++ b/src/layout/widget_ref.rs @@ -1,24 +1,20 @@ use std::{ - cell::{Ref, RefCell, RefMut}, + cell::{Ref, RefMut}, marker::Unsize, - rc::{Rc, Weak}, + rc::Rc, sync::mpsc::Sender, }; use crate::{ layout::{Ui, Widget, WidgetLike}, - util::Id, + util::{Handle, Id, WeakHandle}, }; -/// An identifier for a widget that can index a UI to get the associated widget. -/// It should always remain valid; it keeps a ref count and removes the widget from the UI if all -/// references are dropped. -/// -/// W does not need to implement widget so that AnyWidget is valid; -/// Instead, add generic bounds on methods that take an ID if they need specific data. +/// An handle for a widget in a UI. /// /// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs? -pub struct WidgetRef(Rc>>); +pub struct WidgetRef(Handle>); +pub struct WeakWidgetRef(WeakHandle>); struct Inner { id: Id, @@ -32,8 +28,6 @@ pub enum WidgetUpdate { Mutate(Id), } -pub struct WeakWidgetRef(Weak>>); - impl PartialEq for WidgetRef { fn eq(&self, other: &Self) -> bool { self.id() == other.id() @@ -58,12 +52,15 @@ impl WidgetRef { if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) { label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1; } - Self(Rc::new(RefCell::new(Inner { - widget, - id, - send, - label, - }))) + Self( + Inner { + widget, + id, + send, + label, + } + .into(), + ) } } @@ -78,44 +75,44 @@ impl> WidgetRef { impl WidgetRef { pub fn id(&self) -> Id { - self.0.borrow().id + self.0.get().id } pub fn get(&self) -> Ref<'_, W> { - Ref::map(self.0.borrow(), |i| &i.widget) + Ref::map(self.0.get(), |i| &i.widget) } pub fn get_mut(&self) -> RefMut<'_, W> { - let inner = self.0.borrow_mut(); + let inner = self.0.get_mut(); let _ = inner.send.send(WidgetUpdate::Mutate(inner.id)); RefMut::map(inner, |i| &mut i.widget) } pub fn get_mut_quiet(&self) -> RefMut<'_, W> { - RefMut::map(self.0.borrow_mut(), |i| &mut i.widget) + RefMut::map(self.0.get_mut(), |i| &mut i.widget) } pub fn get_label(&self) -> Ref<'_, String> { - Ref::map(self.0.borrow(), |i| &i.label) + Ref::map(self.0.get(), |i| &i.label) } pub fn set_label(&self, label: impl Into) { - self.0.borrow_mut().label = label.into(); + self.0.get_mut().label = label.into(); } pub fn refs(&self) -> usize { - Rc::strong_count(&self.0) + self.0.refs() } pub fn weak(&self) -> WeakWidgetRef { - WeakWidgetRef(Rc::downgrade(&self.0)) + WeakWidgetRef(self.0.weak()) } } impl WeakWidgetRef { /// should guarantee that widget is still valid to prevent indexing failures pub(crate) fn expect_strong(&self) -> WidgetRef { - WidgetRef(self.0.upgrade().expect("widget should not be dropped")) + WidgetRef(self.0.strong().expect("widget should not be dropped")) } } @@ -156,16 +153,6 @@ impl + 'static, F: FnOnce(&mut Ui) -> Wi } } -pub trait WidgetIdLike { - fn rf(self, send: &Sender) -> WidgetRef; -} - -impl WidgetIdLike for &WidgetRef { - fn rf(self, _: &Sender) -> WidgetRef { - self.clone() - } -} - pub trait IdLike { fn id(&self) -> Id; } diff --git a/src/lib.rs b/src/lib.rs index dc76b9b..f80ef24 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ #![feature(gen_blocks)] #![feature(associated_type_defaults)] #![feature(unsize)] +#![feature(coerce_unsized)] pub mod core; pub mod layout; @@ -22,4 +23,6 @@ pub mod prelude { pub use crate::core::*; pub use crate::layout::*; pub use crate::render::*; + pub use crate::util::Handle; + pub use crate::winit::*; } diff --git a/src/util/handle.rs b/src/util/handle.rs new file mode 100644 index 0000000..7c4c604 --- /dev/null +++ b/src/util/handle.rs @@ -0,0 +1,60 @@ +use std::{ + cell::{Ref, RefCell, RefMut}, + marker::Unsize, + ops::CoerceUnsized, + rc::{Rc, Weak}, +}; + +pub struct Handle(Rc>); +pub struct WeakHandle(Weak>); + +impl Handle { + pub fn get(&self) -> Ref { + self.0.borrow() + } + + pub fn get_mut(&self) -> RefMut<'_, T> { + self.0.borrow_mut() + } + + pub fn refs(&self) -> usize { + Rc::strong_count(&self.0) + } + + pub fn weak(&self) -> WeakHandle { + WeakHandle(Rc::downgrade(&self.0)) + } +} + +impl WeakHandle { + pub fn strong(&self) -> Option> { + Some(Handle(self.0.upgrade()?)) + } +} + +impl Clone for Handle { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl Clone for WeakHandle { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl Default for Handle { + fn default() -> Self { + Self(Default::default()) + } +} + +impl From for Handle { + fn from(value: T) -> Self { + Self(Rc::new(RefCell::new(value))) + } +} + +impl, U: ?Sized> CoerceUnsized> for Handle {} +impl, U: ?Sized> CoerceUnsized> for WeakHandle {} diff --git a/src/util/mod.rs b/src/util/mod.rs index 01215d7..95bc474 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -4,6 +4,7 @@ mod id; mod math; mod refcount; mod vec2; +mod handle; pub(crate) use arena::*; pub use change::*; @@ -11,6 +12,7 @@ pub(crate) use id::*; pub(crate) use math::*; pub(crate) use refcount::*; pub use vec2::*; +pub use handle::*; pub type HashMap = fxhash::FxHashMap; pub type HashSet = fxhash::FxHashSet; diff --git a/src/winit/mod.rs b/src/winit/mod.rs index ab1c0e0..f20fc4b 100644 --- a/src/winit/mod.rs +++ b/src/winit/mod.rs @@ -1,6 +1,4 @@ use crate::prelude::*; -use crate::winit::event::{Edited, Submit}; -use crate::winit::{input::Input, render::UiRenderer}; use arboard::Clipboard; use std::sync::Arc; use std::time::Instant; @@ -9,12 +7,16 @@ use winit::event_loop::{ActiveEventLoop, EventLoopProxy}; use winit::window::{Window, WindowAttributes}; mod app; -pub mod attr; -pub mod event; -pub mod input; -pub mod render; +mod attr; +mod event; +mod input; +mod render; pub use app::*; +pub use attr::*; +pub use event::*; +pub use input::*; +pub use render::*; pub type Proxy = EventLoopProxy; pub type DefaultApp = App>;