From 94397ca6d9c6200500c1dc5fb779056c7dd99bdb Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 18 Feb 2025 10:39:41 +0000 Subject: [PATCH 01/11] Add struct TimerHandle --- crates/kas-core/src/event/components.rs | 8 +++--- crates/kas-core/src/event/cx/cx_pub.rs | 17 +++++++------ crates/kas-core/src/event/cx/mod.rs | 2 +- crates/kas-core/src/event/events.rs | 31 +++++++++++++++++++++--- crates/kas-widgets/src/adapt/adapt.rs | 5 ++-- crates/kas-widgets/src/adapt/adapt_cx.rs | 13 +++++----- crates/kas-widgets/src/menu/menubar.rs | 12 ++++----- crates/kas-widgets/src/scroll_bar.rs | 8 +++--- examples/clock.rs | 9 ++++--- examples/stopwatch.rs | 9 ++++--- 10 files changed, 75 insertions(+), 39 deletions(-) diff --git a/crates/kas-core/src/event/components.rs b/crates/kas-core/src/event/components.rs index aeb9d1d6e..17d142c8e 100644 --- a/crates/kas-core/src/event/components.rs +++ b/crates/kas-core/src/event/components.rs @@ -15,8 +15,8 @@ use crate::{Action, Id}; use kas_macros::impl_default; use std::time::{Duration, Instant}; -const TIMER_SELECT: u64 = 1 << 60; -const TIMER_GLIDE: u64 = (1 << 60) + 1; +const TIMER_SELECT: TimerHandle = TimerHandle::new(1 << 60, true); +const TIMER_GLIDE: TimerHandle = TimerHandle::new((1 << 60) + 1, true); const GLIDE_POLL_MS: u64 = 3; const GLIDE_MAX_SAMPLES: usize = 8; @@ -336,7 +336,7 @@ impl ScrollComponent { let timeout = cx.config().event().scroll_flick_timeout(); let pan_dist_thresh = cx.config().event().pan_dist_thresh(); if self.glide.press_end(timeout, pan_dist_thresh) { - cx.request_timer(id.clone(), TIMER_GLIDE, Duration::new(0, 0)); + cx.request_timer(id.clone(), TIMER_GLIDE, Duration::ZERO); } } Event::Timer(pl) if pl == TIMER_GLIDE => { @@ -484,7 +484,7 @@ impl TextInput { || matches!(press.source, PressSource::Mouse(..) if cx.config_enable_mouse_text_pan())) { self.touch_phase = TouchPhase::None; - cx.request_timer(w_id, TIMER_GLIDE, Duration::new(0, 0)); + cx.request_timer(w_id, TIMER_GLIDE, Duration::ZERO); } Action::None } diff --git a/crates/kas-core/src/event/cx/cx_pub.rs b/crates/kas-core/src/event/cx/cx_pub.rs index b08628f65..ac3c19818 100644 --- a/crates/kas-core/src/event/cx/cx_pub.rs +++ b/crates/kas-core/src/event/cx/cx_pub.rs @@ -200,34 +200,35 @@ impl EventState { /// Widget updates may be used for animation and timed responses. See also /// [`Draw::animate`](crate::draw::Draw::animate) for animation. /// - /// Widget `id` will receive [`Event::Timer`] with this `payload` at + /// Widget `id` will receive [`Event::Timer`] with this `handle` at /// approximately `time = now + delay` (or possibly a little later due to /// frame-rate limiters and processing time). /// /// Requesting an update with `delay == 0` is valid, except from an /// [`Event::Timer`] handler (where it may cause an infinite loop). /// - /// Multiple timer requests with the same `id` and `payload` are merged - /// (choosing the earliest time). - pub fn request_timer(&mut self, id: Id, payload: u64, delay: Duration) { + /// Multiple timer requests with the same `id` and `handle` are merged + /// (see [`TimerHandle`] documentation). + pub fn request_timer(&mut self, id: Id, handle: TimerHandle, delay: Duration) { let time = Instant::now() + delay; if let Some(row) = self .time_updates .iter_mut() - .find(|row| row.1 == id && row.2 == payload) + .find(|row| row.1 == id && row.2 == handle) { - if row.0 <= time { + let earliest = handle.earliest(); + if earliest && row.0 <= time || !earliest && row.0 >= time { return; } row.0 = time; + } else { log::trace!( target: "kas_core::event", "request_timer: update {id} at now+{}ms", delay.as_millis() ); - } else { - self.time_updates.push((time, id, payload)); + self.time_updates.push((time, id, handle)); } self.time_updates.sort_by(|a, b| b.0.cmp(&a.0)); // reverse sort diff --git a/crates/kas-core/src/event/cx/mod.rs b/crates/kas-core/src/event/cx/mod.rs index ef8be83e5..419de62ea 100644 --- a/crates/kas-core/src/event/cx/mod.rs +++ b/crates/kas-core/src/event/cx/mod.rs @@ -209,7 +209,7 @@ pub struct EventState { // For each: (WindowId of popup, popup descriptor, old nav focus) popups: SmallVec<[(WindowId, crate::PopupDescriptor, Option); 16]>, popup_removed: SmallVec<[(Id, WindowId); 16]>, - time_updates: Vec<(Instant, Id, u64)>, + time_updates: Vec<(Instant, Id, TimerHandle)>, // Set of messages awaiting sending send_queue: VecDeque<(Id, Erased)>, // Set of futures of messages together with id of sending widget diff --git a/crates/kas-core/src/event/events.rs b/crates/kas-core/src/event/events.rs index 3c8af3c96..4364f2429 100644 --- a/crates/kas-core/src/event/events.rs +++ b/crates/kas-core/src/event/events.rs @@ -174,9 +174,7 @@ pub enum Event { /// /// This event is received after requesting timed wake-up(s) /// (see [`EventState::request_timer`]). - /// - /// The `u64` payload is copied from [`EventState::request_timer`]. - Timer(u64), + Timer(TimerHandle), /// Notification that a popup has been closed /// /// This is sent to the popup when closed. @@ -616,6 +614,33 @@ impl Command { } } +/// A timer handle +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct TimerHandle(i64); +impl TimerHandle { + /// Construct a new handle + /// + /// The code must be positive. If a widget uses multiple timers, each must + /// have a unique code. + /// + /// When a timer update is requested multiple times before delivery using + /// the same `TimerHandle`, these requests are merged, choosing the + /// earliest time if `earliest`, otherwise the latest time. + pub const fn new(code: i64, earliest: bool) -> Self { + assert!(code >= 0); + if earliest { + TimerHandle(-code - 1) + } else { + TimerHandle(code) + } + } + + /// Check whether this timer chooses the earliest time when merging + pub fn earliest(self) -> bool { + self.0 < 0 + } +} + /// Reason that navigation focus is received #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum FocusSource { diff --git a/crates/kas-widgets/src/adapt/adapt.rs b/crates/kas-widgets/src/adapt/adapt.rs index 6022ece4a..da1035b9a 100644 --- a/crates/kas-widgets/src/adapt/adapt.rs +++ b/crates/kas-widgets/src/adapt/adapt.rs @@ -6,6 +6,7 @@ //! Adapt widget use super::{AdaptConfigCx, AdaptEventCx}; +use kas::event::TimerHandle; use kas::prelude::*; use linear_map::LinearMap; use std::fmt::Debug; @@ -47,7 +48,7 @@ impl_scope! { inner: W, configure_handler: Option>, update_handler: Option>, - timer_handlers: LinearMap>, + timer_handlers: LinearMap>, message_handlers: Vec>, } @@ -93,7 +94,7 @@ impl_scope! { /// It is assumed that state is modified by this timer. Frequent usage /// of timers which don't do anything may be inefficient; prefer usage /// of [`EventState::push_async`](kas::event::EventState::push_async). - pub fn on_timer(mut self, timer_id: u64, handler: H) -> Self + pub fn on_timer(mut self, timer_id: TimerHandle, handler: H) -> Self where H: Fn(&mut AdaptEventCx, &mut W::Data, &A) + 'static, { diff --git a/crates/kas-widgets/src/adapt/adapt_cx.rs b/crates/kas-widgets/src/adapt/adapt_cx.rs index bab63a253..fda212c2e 100644 --- a/crates/kas-widgets/src/adapt/adapt_cx.rs +++ b/crates/kas-widgets/src/adapt/adapt_cx.rs @@ -5,6 +5,7 @@ //! Adapted contexts +use kas::event::TimerHandle; use kas::prelude::*; use std::time::Duration; @@ -46,10 +47,10 @@ impl<'a: 'b, 'b> AdaptEventCx<'a, 'b> { /// Requesting an update with `delay == 0` is valid except from a timer /// handler where it might cause an infinite loop. /// - /// Multiple timer requests with the same `timer_id` are merged - /// (choosing the earliest time). + /// Multiple timer requests with the same `id` and `handle` are merged + /// (see [`TimerHandle`] documentation). #[inline] - pub fn request_timer(&mut self, timer_id: u64, delay: Duration) { + pub fn request_timer(&mut self, timer_id: TimerHandle, delay: Duration) { self.cx.request_timer(self.id.clone(), timer_id, delay); } } @@ -103,10 +104,10 @@ impl<'a: 'b, 'b> AdaptConfigCx<'a, 'b> { /// Requesting an update with `delay == 0` is valid except from a timer /// handler where it might cause an infinite loop. /// - /// Multiple timer requests with the same `timer_id` are merged - /// (choosing the earliest time). + /// Multiple timer requests with the same `id` and `handle` are merged + /// (see [`TimerHandle`] documentation). #[inline] - pub fn request_timer(&mut self, timer_id: u64, delay: Duration) { + pub fn request_timer(&mut self, timer_id: TimerHandle, delay: Duration) { self.cx.request_timer(self.id.clone(), timer_id, delay); } } diff --git a/crates/kas-widgets/src/menu/menubar.rs b/crates/kas-widgets/src/menu/menubar.rs index cba3c21dc..3ec89e6c8 100644 --- a/crates/kas-widgets/src/menu/menubar.rs +++ b/crates/kas-widgets/src/menu/menubar.rs @@ -6,11 +6,13 @@ //! Menubar use super::{Menu, SubMenu, SubMenuBuilder}; -use kas::event::{Command, FocusSource}; +use kas::event::{Command, FocusSource, TimerHandle}; use kas::layout::{self, RowPositionSolver, RowSetter, RowSolver, RulesSetter, RulesSolver}; use kas::prelude::*; use kas::theme::FrameStyle; +const TIMER_SHOW: TimerHandle = TimerHandle::new(0, false); + impl_scope! { /// A menu-bar /// @@ -120,11 +122,9 @@ impl_scope! { impl Events for Self { fn handle_event(&mut self, cx: &mut EventCx, data: &Data, event: Event) -> IsUsed { match event { - Event::Timer(id_code) => { + Event::Timer(TIMER_SHOW) => { if let Some(id) = self.delayed_open.clone() { - if id.as_u64() == id_code { - self.set_menu_path(cx, data, Some(&id), false); - } + self.set_menu_path(cx, data, Some(&id), false); } Used } @@ -181,7 +181,7 @@ impl_scope! { } else if id != self.delayed_open { cx.set_nav_focus(id.clone(), FocusSource::Pointer); let delay = cx.config().event().menu_delay(); - cx.request_timer(self.id(), id.as_u64(), delay); + cx.request_timer(self.id(), TIMER_SHOW, delay); self.delayed_open = Some(id); } } else { diff --git a/crates/kas-widgets/src/scroll_bar.rs b/crates/kas-widgets/src/scroll_bar.rs index 07f0ffba6..b31d13de9 100644 --- a/crates/kas-widgets/src/scroll_bar.rs +++ b/crates/kas-widgets/src/scroll_bar.rs @@ -6,7 +6,7 @@ //! `ScrollBar` control use super::{GripMsg, GripPart, ScrollRegion}; -use kas::event::Scroll; +use kas::event::{Scroll, TimerHandle}; use kas::prelude::*; use kas::theme::Feature; use std::fmt::Debug; @@ -37,6 +37,8 @@ pub enum ScrollBarMode { #[derive(Copy, Clone, Debug)] pub struct ScrollMsg(pub i32); +const TIMER_HIDE: TimerHandle = TimerHandle::new(0, false); + impl_scope! { /// A scroll bar /// @@ -226,7 +228,7 @@ impl_scope! { fn force_visible(&mut self, cx: &mut EventState) { self.force_visible = true; let delay = cx.config().event().touch_select_delay(); - cx.request_timer(self.id(), 0, delay); + cx.request_timer(self.id(), TIMER_HIDE, delay); } #[inline] @@ -344,7 +346,7 @@ impl_scope! { fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed { match event { - Event::Timer(_) => { + Event::Timer(TIMER_HIDE) => { self.force_visible = false; cx.redraw(self); Used diff --git a/examples/clock.rs b/examples/clock.rs index 01cd88f91..2c6ae06b4 100644 --- a/examples/clock.rs +++ b/examples/clock.rs @@ -19,6 +19,7 @@ use std::time::Duration; use kas::draw::color::{Rgba, Rgba8Srgb}; use kas::draw::{Draw, DrawRounded}; +use kas::event::TimerHandle; use kas::geom::{Quad, Vec2}; use kas::prelude::*; use kas::runner::RunnerInherent; @@ -26,6 +27,8 @@ use kas::text::Text; type Runner = kas::runner::Default<(), kas::theme::SimpleTheme>; +const TIMER: TimerHandle = TimerHandle::new(0, true); + impl_scope! { #[derive(Clone)] #[widget] @@ -130,12 +133,12 @@ impl_scope! { self.date.configure().unwrap(); self.time.set_align(AlignPair::CENTER.into()); self.time.configure().unwrap(); - cx.request_timer(self.id(), 0, Duration::new(0, 0)); + cx.request_timer(self.id(), TIMER, Duration::ZERO); } fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed { match event { - Event::Timer(0) => { + Event::Timer(TIMER) => { self.now = Local::now(); let date = self.now.format("%Y-%m-%d").to_string(); let time = self.now.format("%H:%M:%S").to_string(); @@ -145,7 +148,7 @@ impl_scope! { self.time.prepare().expect("not configured"); let ns = 1_000_000_000 - (self.now.time().nanosecond() % 1_000_000_000); log::info!("Requesting update in {}ns", ns); - cx.request_timer(self.id(), 0, Duration::new(0, ns)); + cx.request_timer(self.id(), TIMER, Duration::from_nanos(ns as u64)); cx.redraw(self); Used } diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs index 8036e83ce..1fa10ec5b 100644 --- a/examples/stopwatch.rs +++ b/examples/stopwatch.rs @@ -8,6 +8,7 @@ use std::time::{Duration, Instant}; use kas::decorations::Decorations; +use kas::event::TimerHandle; use kas::prelude::*; use kas::widgets::{format_data, row, Button}; @@ -22,6 +23,8 @@ struct Timer { last: Option, } +const TIMER: TimerHandle = TimerHandle::new(0, true); + fn make_window() -> impl Widget { let ui = row![ format_data!(timer: &Timer, "{}.{:03}", timer.elapsed.as_secs(), timer.elapsed.subsec_millis()), @@ -38,15 +41,15 @@ fn make_window() -> impl Widget { timer.elapsed += now - last; } else { timer.last = Some(now); - cx.request_timer(0, Duration::new(0, 0)); + cx.request_timer(TIMER, Duration::ZERO); } }) - .on_timer(0, |cx, timer, _| { + .on_timer(TIMER, |cx, timer, _| { if let Some(last) = timer.last { let now = Instant::now(); timer.elapsed += now - last; timer.last = Some(now); - cx.request_timer(0, Duration::new(0, 1)); + cx.request_timer(TIMER, Duration::from_nanos(1)); } }) } From 295ac9188aa2f94a961e19e876c9c281f1c8532e Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 18 Feb 2025 11:15:05 +0000 Subject: [PATCH 02/11] Change handling of Event::MouseHover and NavFocus --- crates/kas-core/src/core/impls.rs | 6 +++--- crates/kas-core/src/core/widget.rs | 10 +++++----- crates/kas-core/src/event/events.rs | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/kas-core/src/core/impls.rs b/crates/kas-core/src/core/impls.rs index 91d174afd..80d597ee4 100644 --- a/crates/kas-core/src/core/impls.rs +++ b/crates/kas-core/src/core/impls.rs @@ -5,7 +5,7 @@ //! Widget method implementations -use crate::event::{ConfigCx, Event, EventCx, FocusSource, IsUsed, Scroll, Unused, Used}; +use crate::event::{ConfigCx, Event, EventCx, FocusSource, IsUsed, Scroll, Unused}; #[cfg(debug_assertions)] use crate::util::IdentifyWidget; use crate::{Events, Id, NavAdvance, Node, Tile, Widget}; @@ -25,14 +25,14 @@ pub fn _send( return is_used; } + // Side-effects of receiving events at the target widget. + // These actions do not affect is_used or event propagation. match &event { Event::MouseHover(state) => { widget.handle_hover(cx, *state); - return Used; } Event::NavFocus(FocusSource::Key) => { cx.set_scroll(Scroll::Rect(widget.rect())); - is_used |= Used; } _ => (), } diff --git a/crates/kas-core/src/core/widget.rs b/crates/kas-core/src/core/widget.rs index 5ea7879c5..d74721200 100644 --- a/crates/kas-core/src/core/widget.rs +++ b/crates/kas-core/src/core/widget.rs @@ -7,7 +7,7 @@ #[allow(unused)] use super::Layout; use super::{Node, Tile}; -#[allow(unused)] use crate::event::Used; +#[allow(unused)] use crate::event::EventState; use crate::event::{ConfigCx, Event, EventCx, IsUsed, Scroll, Unused}; use crate::Id; #[allow(unused)] use kas_macros as macros; @@ -150,13 +150,13 @@ pub trait Events: Widget + Sized { /// default implementation of this method does nothing. /// /// Note: to implement `hover_highlight`, simply request a redraw on - /// focus gain and loss. To implement `cursor_icon`, call - /// `cx.set_hover_cursor(EXPR);` on focus gain. + /// hover gain and loss. To implement `cursor_icon`, call + /// [`EventState::set_hover_cursor`] on hover gain only. /// /// [`#widget`]: macros::widget #[inline] - fn handle_hover(&mut self, cx: &mut EventCx, state: bool) { - let _ = (cx, state); + fn handle_hover(&mut self, cx: &mut EventCx, is_hovered: bool) { + let _ = (cx, is_hovered); } /// Handle an [`Event`] diff --git a/crates/kas-core/src/event/events.rs b/crates/kas-core/src/event/events.rs index 4364f2429..656ddc55e 100644 --- a/crates/kas-core/src/event/events.rs +++ b/crates/kas-core/src/event/events.rs @@ -306,8 +306,8 @@ impl Event { CursorMove { .. } | PressStart { .. } => false, Pan { .. } | PressMove { .. } | PressEnd { .. } => true, Timer(_) | PopupClosed(_) => true, - NavFocus { .. } | SelFocus(_) | KeyFocus | MouseHover(_) => false, - LostNavFocus | LostKeyFocus | LostSelFocus => true, + NavFocus { .. } | SelFocus(_) | KeyFocus | MouseHover(true) => false, + LostNavFocus | LostKeyFocus | LostSelFocus | MouseHover(false) => true, } } @@ -346,7 +346,7 @@ impl Event { NavFocus { .. } | LostNavFocus => false, SelFocus(_) | LostSelFocus => false, KeyFocus | LostKeyFocus => false, - MouseHover(_) => false, + MouseHover(_) => true, } } } From e691804188d6411415498666aea25689dde6d8f8 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 18 Feb 2025 11:16:16 +0000 Subject: [PATCH 03/11] Improve invisible ScrollBar behaviour This replaces the on-draw handler with an on-hover handler --- crates/kas-core/src/event/cx/cx_pub.rs | 11 -------- crates/kas-widgets/src/grip.rs | 8 +++--- crates/kas-widgets/src/scroll_bar.rs | 36 ++++++++++++++++---------- 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/crates/kas-core/src/event/cx/cx_pub.rs b/crates/kas-core/src/event/cx/cx_pub.rs index ac3c19818..99512573a 100644 --- a/crates/kas-core/src/event/cx/cx_pub.rs +++ b/crates/kas-core/src/event/cx/cx_pub.rs @@ -67,17 +67,6 @@ impl EventState { self.mouse_grab.is_none() && *w_id == self.hover } - /// Get whether widget `id` or any of its descendants are under the mouse cursor - #[inline] - pub fn is_hovered_recursive(&self, id: &Id) -> bool { - self.mouse_grab.is_none() - && self - .hover - .as_ref() - .map(|h| id.is_ancestor_of(h)) - .unwrap_or(false) - } - /// Check whether the given widget is visually depressed pub fn is_depressed(&self, w_id: &Id) -> bool { for (_, id) in &self.key_depress { diff --git a/crates/kas-widgets/src/grip.rs b/crates/kas-widgets/src/grip.rs index c74736e47..56d9d5dd5 100644 --- a/crates/kas-widgets/src/grip.rs +++ b/crates/kas-widgets/src/grip.rs @@ -175,7 +175,7 @@ impl_scope! { /// between [`Offset::ZERO`] and [`Self::max_offset`]. #[inline] pub fn offset(&self) -> Offset { - self.rect().pos - self.track.pos + self.rect.pos - self.track.pos } /// Get the maximum allowed offset @@ -184,7 +184,7 @@ impl_scope! { /// track minus the size of the grip. #[inline] pub fn max_offset(&self) -> Offset { - Offset::conv(self.track.size) - Offset::conv(self.rect().size) + Offset::conv(self.track.size) - Offset::conv(self.rect.size) } /// Set a new grip position @@ -199,7 +199,7 @@ impl_scope! { pub fn set_offset(&mut self, cx: &mut EventState, offset: Offset) -> Offset { let offset = offset.min(self.max_offset()).max(Offset::ZERO); let grip_pos = self.track.pos + offset; - if grip_pos != self.rect().pos { + if grip_pos != self.rect.pos { self.rect.pos = grip_pos; cx.redraw(self); } @@ -223,7 +223,7 @@ impl_scope! { .with_icon(CursorIcon::Grabbing) .with_cx(cx); - let offset = press.coord - self.track.pos - Offset::conv(self.rect().size / 2); + let offset = press.coord - self.track.pos - Offset::conv(self.rect.size / 2); let offset = offset.clamp(Offset::ZERO, self.max_offset()); self.press_coord = press.coord - offset; offset diff --git a/crates/kas-widgets/src/scroll_bar.rs b/crates/kas-widgets/src/scroll_bar.rs index b31d13de9..0f8e99db8 100644 --- a/crates/kas-widgets/src/scroll_bar.rs +++ b/crates/kas-widgets/src/scroll_bar.rs @@ -65,6 +65,7 @@ impl_scope! { max_value: i32, value: i32, invisible: bool, + is_hovered: bool, force_visible: bool, #[widget] grip: GripPart, @@ -114,6 +115,7 @@ impl_scope! { max_value: 0, value: 0, invisible: false, + is_hovered: false, force_visible: false, grip: GripPart::new(), } @@ -221,16 +223,14 @@ impl_scope! { self.value = value; self.grip.set_offset(cx, self.offset()); } - self.force_visible(cx); + if !self.is_hovered { + self.force_visible = true; + let delay = cx.config().event().touch_select_delay(); + cx.request_timer(self.id(), TIMER_HIDE, delay); + } changed } - fn force_visible(&mut self, cx: &mut EventState) { - self.force_visible = true; - let delay = cx.config().event().touch_select_delay(); - cx.request_timer(self.id(), TIMER_HIDE, delay); - } - #[inline] fn bar_len(&self) -> i32 { match self.direction.is_vertical() { @@ -318,10 +318,6 @@ impl_scope! { } fn draw(&mut self, mut draw: DrawCx) { - if draw.ev_state().is_hovered_recursive(self.id_ref()) { - self.force_visible(draw.ev_state()); - } - if !self.invisible || (self.max_value != 0 && self.force_visible) || draw.ev_state().is_depressed(self.grip.id_ref()) @@ -347,8 +343,10 @@ impl_scope! { fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed { match event { Event::Timer(TIMER_HIDE) => { - self.force_visible = false; - cx.redraw(self); + if !self.is_hovered { + self.force_visible = false; + cx.redraw(self); + } Used } Event::PressStart { press } => { @@ -356,6 +354,18 @@ impl_scope! { self.apply_grip_offset(cx, offset); Used } + Event::MouseHover(true) => { + self.is_hovered = true; + self.force_visible = true; + cx.redraw(self); + Used + } + Event::MouseHover(false) => { + self.is_hovered = false; + let delay = cx.config().event().touch_select_delay(); + cx.request_timer(self.id(), TIMER_HIDE, delay); + Used + } _ => Unused, } } From 467445ab0eca76b8f496f16f1454959e7fc417bc Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 18 Feb 2025 14:52:30 +0000 Subject: [PATCH 04/11] Canvas: use a RefCell --- crates/kas-resvg/src/canvas.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/crates/kas-resvg/src/canvas.rs b/crates/kas-resvg/src/canvas.rs index 4fd8ec2a5..35145bae8 100644 --- a/crates/kas-resvg/src/canvas.rs +++ b/crates/kas-resvg/src/canvas.rs @@ -8,6 +8,7 @@ use kas::draw::{ImageFormat, ImageHandle}; use kas::layout::{LogicalSize, PixmapScaling}; use kas::prelude::*; +use std::cell::RefCell; use std::future::Future; use tiny_skia::{Color, Pixmap}; @@ -103,7 +104,7 @@ impl_scope! { pub struct Canvas { core: widget_core!(), scaling: PixmapScaling, - inner: State

, + inner: RefCell>, image: Option, } @@ -120,7 +121,7 @@ impl_scope! { stretch: Stretch::High, ..Default::default() }, - inner: State::Initial(program), + inner: RefCell::new(State::Initial(program)), image: None, } } @@ -169,14 +170,16 @@ impl_scope! { widget_set_rect!(self.scaling.align_rect(rect, align, scale_factor)); let size = self.rect().size.cast(); - if let Some(fut) = self.inner.resize(size) { + if let Some(fut) = self.inner.get_mut().resize(size) { cx.push_spawn(self.id(), fut); } } fn draw(&mut self, mut draw: DrawCx) { - if let Some(fut) = self.inner.maybe_redraw() { - draw.ev_state().push_spawn(self.id(), fut); + if let Ok(mut state) = self.inner.try_borrow_mut() { + if let Some(fut) = state.maybe_redraw() { + draw.ev_state().push_spawn(self.id(), fut); + } } if let Some(id) = self.image.as_ref().map(|h| h.id()) { @@ -190,7 +193,7 @@ impl_scope! { fn handle_messages(&mut self, cx: &mut EventCx, _: &Self::Data) { if let Some((program, mut pixmap)) = cx.try_pop::<(P, Pixmap)>() { - debug_assert!(matches!(self.inner, State::Rendering)); + debug_assert!(matches!(self.inner.get_mut(), State::Rendering)); let size = (pixmap.width(), pixmap.height()); let ds = cx.draw_shared(); @@ -213,18 +216,19 @@ impl_scope! { cx.redraw(&self); let rect_size: (u32, u32) = self.rect().size.cast(); + let state = self.inner.get_mut(); if rect_size != size { // Possible if a redraw was in progress when set_rect was called pixmap = if let Some(px) = Pixmap::new(rect_size.0, rect_size.1) { px } else { - self.inner = State::Initial(program); + *state = State::Initial(program); return; }; cx.push_spawn(self.id(), draw(program, pixmap)); } else { - self.inner = State::Ready(program, pixmap); + *state = State::Ready(program, pixmap); } } } From 24250939e74821dccba2c15cad46a8ef681eda63 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 18 Feb 2025 15:13:31 +0000 Subject: [PATCH 05/11] Make Layout::draw take &self --- crates/kas-core/src/core/layout.rs | 4 ++-- crates/kas-core/src/core/node.rs | 20 +------------------- crates/kas-core/src/decorations.rs | 4 ++-- crates/kas-core/src/draw/draw.rs | 2 +- crates/kas-core/src/layout/row_solver.rs | 6 +++--- crates/kas-core/src/root.rs | 12 ++++++------ crates/kas-core/src/runner/window.rs | 2 +- crates/kas-core/src/theme/text.rs | 2 +- crates/kas-macros/src/make_layout.rs | 4 ++-- crates/kas-macros/src/widget.rs | 4 ++-- crates/kas-macros/src/widget_derive.rs | 2 +- crates/kas-resvg/src/canvas.rs | 6 +++--- crates/kas-resvg/src/svg.rs | 2 +- crates/kas-view/src/list_view.rs | 4 ++-- crates/kas-view/src/matrix_view.rs | 4 ++-- crates/kas-widgets/src/check_box.rs | 2 +- crates/kas-widgets/src/edit.rs | 4 ++-- crates/kas-widgets/src/filler.rs | 2 +- crates/kas-widgets/src/float.rs | 6 +++--- crates/kas-widgets/src/grid.rs | 4 ++-- crates/kas-widgets/src/grip.rs | 2 +- crates/kas-widgets/src/image.rs | 2 +- crates/kas-widgets/src/list.rs | 4 ++-- crates/kas-widgets/src/mark.rs | 4 ++-- crates/kas-widgets/src/menu/menu_entry.rs | 4 ++-- crates/kas-widgets/src/menu/menubar.rs | 4 ++-- crates/kas-widgets/src/menu/mod.rs | 2 +- crates/kas-widgets/src/menu/submenu.rs | 6 +++--- crates/kas-widgets/src/progress.rs | 2 +- crates/kas-widgets/src/radio_box.rs | 2 +- crates/kas-widgets/src/scroll.rs | 2 +- crates/kas-widgets/src/scroll_bar.rs | 4 ++-- crates/kas-widgets/src/scroll_label.rs | 2 +- crates/kas-widgets/src/scroll_text.rs | 2 +- crates/kas-widgets/src/separator.rs | 2 +- crates/kas-widgets/src/slider.rs | 2 +- crates/kas-widgets/src/spinner.rs | 2 +- crates/kas-widgets/src/splitter.rs | 6 +++--- crates/kas-widgets/src/stack.rs | 4 ++-- crates/kas-widgets/src/text.rs | 2 +- examples/clock.rs | 2 +- examples/gallery.rs | 2 +- examples/mandlebrot/mandlebrot.rs | 2 +- examples/proxy.rs | 2 +- 44 files changed, 73 insertions(+), 91 deletions(-) diff --git a/crates/kas-core/src/core/layout.rs b/crates/kas-core/src/core/layout.rs index 2b5018f23..a11837613 100644 --- a/crates/kas-core/src/core/layout.rs +++ b/crates/kas-core/src/core/layout.rs @@ -189,7 +189,7 @@ pub trait Layout { /// This method modification should never cause issues (besides the implied /// limitation that widgets cannot easily detect a parent's state while /// being drawn). - fn draw(&mut self, draw: DrawCx); + fn draw(&self, draw: DrawCx); } /// Macro-defined layout @@ -211,5 +211,5 @@ pub trait MacroDefinedLayout { fn try_probe(&mut self, coord: Coord) -> Option; /// Draw a widget and its children - fn draw(&mut self, draw: DrawCx); + fn draw(&self, draw: DrawCx); } diff --git a/crates/kas-core/src/core/node.rs b/crates/kas-core/src/core/node.rs index e78c5a0c1..7dcaa5078 100644 --- a/crates/kas-core/src/core/node.rs +++ b/crates/kas-core/src/core/node.rs @@ -9,7 +9,7 @@ use super::Widget; use crate::event::{ConfigCx, Event, EventCx, IsUsed}; use crate::geom::{Coord, Rect}; use crate::layout::{AlignHints, AxisInfo, SizeRules}; -use crate::theme::{DrawCx, SizeCx}; +use crate::theme::SizeCx; use crate::{Id, NavAdvance, Tile}; #[cfg(not(feature = "unsafe_node"))] @@ -29,7 +29,6 @@ trait NodeT { fn nav_next(&self, reverse: bool, from: Option) -> Option; fn try_probe(&mut self, coord: Coord) -> Option; - fn _draw(&mut self, draw: DrawCx); fn _configure(&mut self, cx: &mut ConfigCx, id: Id); fn _update(&mut self, cx: &mut ConfigCx); @@ -83,9 +82,6 @@ impl<'a, T> NodeT for (&'a mut dyn Widget, &'a T) { fn try_probe(&mut self, coord: Coord) -> Option { self.0.try_probe(coord) } - fn _draw(&mut self, mut draw: DrawCx) { - self.0.draw(draw.re()); - } fn _configure(&mut self, cx: &mut ConfigCx, id: Id) { self.0._configure(cx, self.1, id); @@ -307,20 +303,6 @@ impl<'a> Node<'a> { self.0.try_probe(coord) } - cfg_if::cfg_if! { - if #[cfg(feature = "unsafe_node")] { - /// Draw a widget and its children - pub(crate) fn _draw(&mut self, mut draw: DrawCx) { - self.0.draw(draw.re()); - } - } else { - /// Draw a widget and its children - pub(crate) fn _draw(&mut self, draw: DrawCx) { - self.0._draw(draw); - } - } - } - /// Internal method: configure recursively pub(crate) fn _configure(&mut self, cx: &mut ConfigCx, id: Id) { cfg_if::cfg_if! { diff --git a/crates/kas-core/src/decorations.rs b/crates/kas-core/src/decorations.rs index c42805f24..ec83c98c5 100644 --- a/crates/kas-core/src/decorations.rs +++ b/crates/kas-core/src/decorations.rs @@ -80,7 +80,7 @@ impl_scope! { SizeRules::EMPTY } - fn draw(&mut self, _: DrawCx) {} + fn draw(&self, _: DrawCx) {} } impl Events for Self { @@ -178,7 +178,7 @@ impl_scope! { sizer.feature(self.style.into(), axis) } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { draw.mark(self.rect(), self.style); } } diff --git a/crates/kas-core/src/draw/draw.rs b/crates/kas-core/src/draw/draw.rs index fa82b180d..16f3cec20 100644 --- a/crates/kas-core/src/draw/draw.rs +++ b/crates/kas-core/src/draw/draw.rs @@ -29,7 +29,7 @@ use std::time::Instant; /// # _pd: std::marker::PhantomData, /// # } /// impl CircleWidget { -/// fn draw(&mut self, mut draw: DrawCx) { +/// fn draw(&self, mut draw: DrawCx) { /// // This type assumes usage of kas_wgpu without a custom draw pipe: /// type DrawIface = DrawIface>; /// if let Some(mut draw) = DrawIface::downcast_from(draw.draw_device()) { diff --git a/crates/kas-core/src/layout/row_solver.rs b/crates/kas-core/src/layout/row_solver.rs index 8f49257b4..bb4b539b9 100644 --- a/crates/kas-core/src/layout/row_solver.rs +++ b/crates/kas-core/src/layout/row_solver.rs @@ -329,9 +329,9 @@ impl RowPositionSolver { } /// Call `f` on each child intersecting the given `rect` - pub fn for_children_mut( + pub fn for_children( self, - widgets: &mut C, + widgets: &C, rect: Rect, mut f: F, ) { @@ -360,7 +360,7 @@ impl RowPositionSolver { }; for i in start..widgets.len() { - if let Some(child) = widgets.get_mut_tile(i) { + if let Some(child) = widgets.get_tile(i) { let do_break = match self.direction.as_direction() { Direction::Right => child.rect().pos.0 >= end.0, Direction::Down => child.rect().pos.1 >= end.1, diff --git a/crates/kas-core/src/root.rs b/crates/kas-core/src/root.rs index a98abef47..5e28d2942 100644 --- a/crates/kas-core/src/root.rs +++ b/crates/kas-core/src/root.rs @@ -146,7 +146,7 @@ impl_scope! { self.inner.set_rect(cx, Rect::new(p_in, s_in), hints); } - fn draw(&mut self, _: DrawCx) { + fn draw(&self, _: DrawCx) { unimplemented!() } } @@ -179,7 +179,7 @@ impl_scope! { } #[cfg(winit)] - pub(crate) fn draw(&mut self, data: &Data, mut draw: DrawCx) { + pub(crate) fn draw(&self, mut draw: DrawCx) { if self.dec_size != Size::ZERO { draw.frame(self.rect(), FrameStyle::Window, Default::default()); if self.bar_h > 0 { @@ -188,12 +188,12 @@ impl_scope! { } self.inner.draw(draw.re()); for (_, popup, translation) in &self.popups { - self.inner.as_node(data).find_node(&popup.id, |mut node| { - let clip_rect = node.rect() - *translation; + if let Some(child) = self.inner.find_widget(&popup.id) { + let clip_rect = child.rect() - *translation; draw.with_overlay(clip_rect, *translation, |draw| { - node._draw(draw); + child.draw(draw); }); - }); + } } } } diff --git a/crates/kas-core/src/runner/window.rs b/crates/kas-core/src/runner/window.rs index 528c27bec..4165b6e58 100644 --- a/crates/kas-core/src/runner/window.rs +++ b/crates/kas-core/src/runner/window.rs @@ -491,7 +491,7 @@ impl> Window { .theme .draw(draw, &mut self.ev_state, &mut window.theme_window); let draw_cx = DrawCx::new(&mut draw, self.widget.id()); - self.widget.draw(&state.data, draw_cx); + self.widget.draw(draw_cx); } let time2 = Instant::now(); diff --git a/crates/kas-core/src/theme/text.rs b/crates/kas-core/src/theme/text.rs index 1495cbb61..7f0b1b38a 100644 --- a/crates/kas-core/src/theme/text.rs +++ b/crates/kas-core/src/theme/text.rs @@ -83,7 +83,7 @@ impl Layout for Text { self.prepare().expect("not configured"); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { draw.text(self.rect, self); } } diff --git a/crates/kas-macros/src/make_layout.rs b/crates/kas-macros/src/make_layout.rs index 392815e8c..3ea17b4a9 100644 --- a/crates/kas-macros/src/make_layout.rs +++ b/crates/kas-macros/src/make_layout.rs @@ -818,10 +818,10 @@ impl Layout { match self { Layout::Align(layout, _) | Layout::Pack(layout, _) => layout.draw(core_path), Layout::Single(expr) => quote! { - ::kas::Layout::draw(&mut #expr, draw.re()); + ::kas::Layout::draw(&#expr, draw.re()); }, Layout::Widget(stor, _) | Layout::Label(stor, _) => quote! { - ::kas::Layout::draw(&mut #core_path.#stor, draw.re()); + ::kas::Layout::draw(&#core_path.#stor, draw.re()); }, Layout::Frame(stor, layout, style, bg) => { let mut toks = quote! { diff --git a/crates/kas-macros/src/widget.rs b/crates/kas-macros/src/widget.rs index 04f6a8c7d..bae463b42 100644 --- a/crates/kas-macros/src/widget.rs +++ b/crates/kas-macros/src/widget.rs @@ -417,7 +417,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul } #[inline] - fn draw(&mut self, mut draw: ::kas::theme::DrawCx) { + fn draw(&self, mut draw: ::kas::theme::DrawCx) { draw.set_id(::kas::Tile::id(self)); #draw } @@ -461,7 +461,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul }; fn_draw = Some(quote! { - fn draw(&mut self, draw: ::kas::theme::DrawCx) { + fn draw(&self, draw: ::kas::theme::DrawCx) { #[cfg(debug_assertions)] #core_path.status.require_rect(&#core_path._id); diff --git a/crates/kas-macros/src/widget_derive.rs b/crates/kas-macros/src/widget_derive.rs index c77a07220..cc8a29c2a 100644 --- a/crates/kas-macros/src/widget_derive.rs +++ b/crates/kas-macros/src/widget_derive.rs @@ -197,7 +197,7 @@ pub fn widget(_attr_span: Span, args: WidgetArgs, scope: &mut Scope) -> Result<( }; let fn_draw = quote! { #[inline] - fn draw(&mut self, draw: ::kas::theme::DrawCx) { + fn draw(&self, draw: ::kas::theme::DrawCx) { self.#inner.draw(draw); } }; diff --git a/crates/kas-resvg/src/canvas.rs b/crates/kas-resvg/src/canvas.rs index 35145bae8..4a9a1da47 100644 --- a/crates/kas-resvg/src/canvas.rs +++ b/crates/kas-resvg/src/canvas.rs @@ -24,7 +24,7 @@ pub trait CanvasProgram: std::fmt::Debug + Send + 'static { /// /// Note that [`Layout::draw`] does not call this method, but instead draws /// from a copy of the `pixmap` (updated each time this method completes). - fn draw(&mut self, pixmap: &mut Pixmap); + fn draw(&self, pixmap: &mut Pixmap); /// This method is called each time a frame is drawn. Note that since /// redrawing is async and non-blocking, the result is expected to be at @@ -36,7 +36,7 @@ pub trait CanvasProgram: std::fmt::Debug + Send + 'static { } } -async fn draw(mut program: P, mut pixmap: Pixmap) -> (P, Pixmap) { +async fn draw(program: P, mut pixmap: Pixmap) -> (P, Pixmap) { pixmap.fill(Color::TRANSPARENT); program.draw(&mut pixmap); (program, pixmap) @@ -175,7 +175,7 @@ impl_scope! { } } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { if let Ok(mut state) = self.inner.try_borrow_mut() { if let Some(fut) = state.maybe_redraw() { draw.ev_state().push_spawn(self.id(), fut); diff --git a/crates/kas-resvg/src/svg.rs b/crates/kas-resvg/src/svg.rs index e87449b36..f3936e221 100644 --- a/crates/kas-resvg/src/svg.rs +++ b/crates/kas-resvg/src/svg.rs @@ -257,7 +257,7 @@ impl_scope! { } } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { if let Some(id) = self.image.as_ref().map(|h| h.id()) { draw.image(self.rect(), id); } diff --git a/crates/kas-view/src/list_view.rs b/crates/kas-view/src/list_view.rs index 9bafa92f2..5f16b55ed 100644 --- a/crates/kas-view/src/list_view.rs +++ b/crates/kas-view/src/list_view.rs @@ -524,10 +524,10 @@ impl_scope! { debug_assert!(self.widgets.len() >= req_widgets); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { let offset = self.scroll_offset(); draw.with_clip_region(self.rect(), offset, |mut draw| { - for child in &mut self.widgets[..self.cur_len.cast()] { + for child in &self.widgets[..self.cur_len.cast()] { if let Some(ref key) = child.key { if self.selection.contains(key) { draw.selection(child.widget.rect(), self.sel_style); diff --git a/crates/kas-view/src/matrix_view.rs b/crates/kas-view/src/matrix_view.rs index aef5fec52..6f8b059c9 100644 --- a/crates/kas-view/src/matrix_view.rs +++ b/crates/kas-view/src/matrix_view.rs @@ -459,12 +459,12 @@ impl_scope! { debug_assert!(self.widgets.len() >= req_widgets); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { let offset = self.scroll_offset(); let rect = self.rect() + offset; let num = self.num_children(); draw.with_clip_region(self.rect(), offset, |mut draw| { - for child in &mut self.widgets[..num] { + for child in &self.widgets[..num] { if let Some(ref key) = child.key { // Note: we don't know which widgets within 0..num are // visible, so check intersection before drawing: diff --git a/crates/kas-widgets/src/check_box.rs b/crates/kas-widgets/src/check_box.rs index 8ea93413b..d033a41ff 100644 --- a/crates/kas-widgets/src/check_box.rs +++ b/crates/kas-widgets/src/check_box.rs @@ -59,7 +59,7 @@ impl_scope! { widget_set_rect!(rect); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { draw.check_box(self.rect(), self.state, self.last_change); } } diff --git a/crates/kas-widgets/src/edit.rs b/crates/kas-widgets/src/edit.rs index 30dc69b01..4cf606742 100644 --- a/crates/kas-widgets/src/edit.rs +++ b/crates/kas-widgets/src/edit.rs @@ -387,7 +387,7 @@ impl_scope! { self.update_scroll_bar(cx); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { self.inner.draw(draw.re()); if self.inner.max_scroll_offset().1 > 0 { self.bar.draw(draw.re()); @@ -745,7 +745,7 @@ impl_scope! { self.view_offset = self.view_offset.min(self.max_scroll_offset()); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { let bg = if self.has_error() { Background::Error } else { diff --git a/crates/kas-widgets/src/filler.rs b/crates/kas-widgets/src/filler.rs index e394ab008..3047db2f7 100644 --- a/crates/kas-widgets/src/filler.rs +++ b/crates/kas-widgets/src/filler.rs @@ -28,7 +28,7 @@ impl_scope! { SizeRules::empty(stretch) } - fn draw(&mut self, _: DrawCx) {} + fn draw(&self, _: DrawCx) {} } } diff --git a/crates/kas-widgets/src/float.rs b/crates/kas-widgets/src/float.rs index 7bbd698bd..b9809c3cb 100644 --- a/crates/kas-widgets/src/float.rs +++ b/crates/kas-widgets/src/float.rs @@ -122,15 +122,15 @@ impl_scope! { } } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { let mut iter = (0..self.widgets.len()).rev(); if let Some(first) = iter.next() { - if let Some(child) = self.widgets.get_mut_tile(first) { + if let Some(child) = self.widgets.get_tile(first) { child.draw(draw.re()); } } for i in iter { - if let Some(child) = self.widgets.get_mut_tile(i) { + if let Some(child) = self.widgets.get_tile(i) { draw.with_pass(|draw| child.draw(draw)); } } diff --git a/crates/kas-widgets/src/grid.rs b/crates/kas-widgets/src/grid.rs index 54ce2a6da..216f3e093 100644 --- a/crates/kas-widgets/src/grid.rs +++ b/crates/kas-widgets/src/grid.rs @@ -197,9 +197,9 @@ impl_scope! { } } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { for n in 0..self.widgets.len() { - if let Some(child) = self.widgets.get_mut_tile(n) { + if let Some(child) = self.widgets.get_tile(n) { child.draw(draw.re()); } } diff --git a/crates/kas-widgets/src/grip.rs b/crates/kas-widgets/src/grip.rs index 56d9d5dd5..70c7a0e02 100644 --- a/crates/kas-widgets/src/grip.rs +++ b/crates/kas-widgets/src/grip.rs @@ -88,7 +88,7 @@ impl_scope! { self.rect = rect; } - fn draw(&mut self, _: DrawCx) {} + fn draw(&self, _: DrawCx) {} } impl Tile for Self { diff --git a/crates/kas-widgets/src/image.rs b/crates/kas-widgets/src/image.rs index 3c1253dfc..d35a62501 100644 --- a/crates/kas-widgets/src/image.rs +++ b/crates/kas-widgets/src/image.rs @@ -177,7 +177,7 @@ impl_scope! { widget_set_rect!(self.scaling.align_rect(rect, align, scale_factor)); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { if let Some(id) = self.handle.as_ref().map(|h| h.id()) { draw.image(self.rect(), id); } diff --git a/crates/kas-widgets/src/list.rs b/crates/kas-widgets/src/list.rs index b0e7ad3e0..5e413f2dd 100644 --- a/crates/kas-widgets/src/list.rs +++ b/crates/kas-widgets/src/list.rs @@ -285,9 +285,9 @@ impl_scope! { } } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { let solver = RowPositionSolver::new(self.direction); - solver.for_children_mut(&mut self.widgets, draw.get_clip_rect(), |w| w.draw(draw.re())); + solver.for_children(&self.widgets, draw.get_clip_rect(), |w| w.draw(draw.re())); } } diff --git a/crates/kas-widgets/src/mark.rs b/crates/kas-widgets/src/mark.rs index 51c75ca9c..cc945fb9c 100644 --- a/crates/kas-widgets/src/mark.rs +++ b/crates/kas-widgets/src/mark.rs @@ -50,7 +50,7 @@ impl_scope! { sizer.feature(self.style.into(), axis) } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { draw.mark(self.rect(), self.style); } } @@ -91,7 +91,7 @@ impl_scope! { sizer.feature(self.style.into(), axis) } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { draw.mark(self.rect(), self.style); } } diff --git a/crates/kas-widgets/src/menu/menu_entry.rs b/crates/kas-widgets/src/menu/menu_entry.rs index 5c856c45d..8c89ac206 100644 --- a/crates/kas-widgets/src/menu/menu_entry.rs +++ b/crates/kas-widgets/src/menu/menu_entry.rs @@ -31,7 +31,7 @@ impl_scope! { } impl Layout for Self { - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { draw.frame(self.rect(), FrameStyle::MenuEntry, Default::default()); self.label.draw(draw.re()); } @@ -121,7 +121,7 @@ impl_scope! { } impl Layout for Self { - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { draw.set_id(self.checkbox.id()); draw.frame(self.rect(), FrameStyle::MenuEntry, Default::default()); kas::MacroDefinedLayout::draw(self, draw); diff --git a/crates/kas-widgets/src/menu/menubar.rs b/crates/kas-widgets/src/menu/menubar.rs index 3ec89e6c8..f57897560 100644 --- a/crates/kas-widgets/src/menu/menubar.rs +++ b/crates/kas-widgets/src/menu/menubar.rs @@ -94,10 +94,10 @@ impl_scope! { } } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { let solver = RowPositionSolver::new(self.direction); let rect = self.rect(); - solver.for_children_mut(&mut self.widgets, rect, |w| w.draw(draw.re())); + solver.for_children(&self.widgets, rect, |w| w.draw(draw.re())); } } diff --git a/crates/kas-widgets/src/menu/mod.rs b/crates/kas-widgets/src/menu/mod.rs index 1c23b7a8e..4953ea1ec 100644 --- a/crates/kas-widgets/src/menu/mod.rs +++ b/crates/kas-widgets/src/menu/mod.rs @@ -69,7 +69,7 @@ pub trait Menu: Widget { /// # struct S; /// # impl S { /// # fn rect(&self) -> Rect { Rect::ZERO } - /// fn draw(&mut self, mut draw: DrawCx) { + /// fn draw(&self, mut draw: DrawCx) { /// draw.frame(self.rect(), FrameStyle::MenuEntry, Default::default()); /// // draw children here /// } diff --git a/crates/kas-widgets/src/menu/submenu.rs b/crates/kas-widgets/src/menu/submenu.rs index 0f24c91d0..978f2924f 100644 --- a/crates/kas-widgets/src/menu/submenu.rs +++ b/crates/kas-widgets/src/menu/submenu.rs @@ -101,7 +101,7 @@ impl_scope! { } impl kas::Layout for Self { - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { draw.frame(self.rect(), FrameStyle::MenuEntry, Default::default()); self.label.draw(draw.re()); if self.mark.rect().size != Size::ZERO { @@ -332,8 +332,8 @@ impl_scope! { } } - fn draw(&mut self, mut draw: DrawCx) { - for child in self.list.iter_mut() { + fn draw(&self, mut draw: DrawCx) { + for child in self.list.iter() { child.draw(draw.re()); } } diff --git a/crates/kas-widgets/src/progress.rs b/crates/kas-widgets/src/progress.rs index 8eecbf8f3..ce489e089 100644 --- a/crates/kas-widgets/src/progress.rs +++ b/crates/kas-widgets/src/progress.rs @@ -81,7 +81,7 @@ impl_scope! { widget_set_rect!(rect); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { let dir = self.direction.as_direction(); draw.progress_bar(self.rect(), dir, self.value); } diff --git a/crates/kas-widgets/src/radio_box.rs b/crates/kas-widgets/src/radio_box.rs index 7d35eff06..e1c279de9 100644 --- a/crates/kas-widgets/src/radio_box.rs +++ b/crates/kas-widgets/src/radio_box.rs @@ -59,7 +59,7 @@ impl_scope! { widget_set_rect!(rect); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { draw.radio_box(self.rect(), self.state, self.last_change); } } diff --git a/crates/kas-widgets/src/scroll.rs b/crates/kas-widgets/src/scroll.rs index 5cb9dc27b..9a2b457c7 100644 --- a/crates/kas-widgets/src/scroll.rs +++ b/crates/kas-widgets/src/scroll.rs @@ -116,7 +116,7 @@ impl_scope! { .set_sizes(rect.size, child_size + self.frame_size); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { draw.with_clip_region(self.rect(), self.scroll_offset(), |mut draw| { self.inner.draw(draw.re()); }); diff --git a/crates/kas-widgets/src/scroll_bar.rs b/crates/kas-widgets/src/scroll_bar.rs index 0f8e99db8..16b37426b 100644 --- a/crates/kas-widgets/src/scroll_bar.rs +++ b/crates/kas-widgets/src/scroll_bar.rs @@ -317,7 +317,7 @@ impl_scope! { self.update_widgets(cx); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { if !self.invisible || (self.max_value != 0 && self.force_visible) || draw.ev_state().is_depressed(self.grip.id_ref()) @@ -568,7 +568,7 @@ impl_scope! { } } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { self.inner.draw(draw.re()); draw.with_pass(|mut draw| { if self.show_bars.0 { diff --git a/crates/kas-widgets/src/scroll_label.rs b/crates/kas-widgets/src/scroll_label.rs index 896524e4d..cc6b17e20 100644 --- a/crates/kas-widgets/src/scroll_label.rs +++ b/crates/kas-widgets/src/scroll_label.rs @@ -61,7 +61,7 @@ impl_scope! { self.bar.set_value(cx, self.view_offset.1); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { let rect = Rect::new(self.rect().pos, self.text_size); draw.with_clip_region(self.rect(), self.view_offset, |mut draw| { if self.selection.is_empty() { diff --git a/crates/kas-widgets/src/scroll_text.rs b/crates/kas-widgets/src/scroll_text.rs index 357afcbac..6d1db65cc 100644 --- a/crates/kas-widgets/src/scroll_text.rs +++ b/crates/kas-widgets/src/scroll_text.rs @@ -61,7 +61,7 @@ impl_scope! { self.bar.set_value(cx, self.view_offset.1); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { let rect = Rect::new(self.rect().pos, self.text_size); draw.with_clip_region(self.rect(), self.view_offset, |mut draw| { if self.selection.is_empty() { diff --git a/crates/kas-widgets/src/separator.rs b/crates/kas-widgets/src/separator.rs index c43a589d1..a55127e2d 100644 --- a/crates/kas-widgets/src/separator.rs +++ b/crates/kas-widgets/src/separator.rs @@ -38,7 +38,7 @@ impl_scope! { sizer.feature(kas::theme::Feature::Separator, axis) } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { draw.separator(self.rect()); } } diff --git a/crates/kas-widgets/src/slider.rs b/crates/kas-widgets/src/slider.rs index 44127a6f4..6b92c2810 100644 --- a/crates/kas-widgets/src/slider.rs +++ b/crates/kas-widgets/src/slider.rs @@ -317,7 +317,7 @@ impl_scope! { self.grip.set_offset(cx, self.offset()); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { let dir = self.direction.as_direction(); draw.slider(self.rect(), &self.grip, dir); } diff --git a/crates/kas-widgets/src/spinner.rs b/crates/kas-widgets/src/spinner.rs index 5db570ed1..cb471a066 100644 --- a/crates/kas-widgets/src/spinner.rs +++ b/crates/kas-widgets/src/spinner.rs @@ -281,7 +281,7 @@ impl_scope! { self.edit.set_outer_rect(rect, FrameStyle::EditBox); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { self.edit.draw(draw.re()); self.b_up.draw(draw.re()); self.b_down.draw(draw.re()); diff --git a/crates/kas-widgets/src/splitter.rs b/crates/kas-widgets/src/splitter.rs index e188eccd9..ce669ddd3 100644 --- a/crates/kas-widgets/src/splitter.rs +++ b/crates/kas-widgets/src/splitter.rs @@ -196,7 +196,7 @@ impl_scope! { } } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { if !self.size_solved { debug_assert!(false); return; @@ -206,12 +206,12 @@ impl_scope! { // calling it twice. let solver = layout::RowPositionSolver::new(self.direction); - solver.for_children_mut(&mut self.widgets, draw.get_clip_rect(), |w| { + solver.for_children(&self.widgets, draw.get_clip_rect(), |w| { w.draw(draw.re()); }); let solver = layout::RowPositionSolver::new(self.direction); - solver.for_children_mut(&mut self.grips, draw.get_clip_rect(), |w| { + solver.for_children(&self.grips, draw.get_clip_rect(), |w| { draw.separator(w.rect()) }); } diff --git a/crates/kas-widgets/src/stack.rs b/crates/kas-widgets/src/stack.rs index 879eac3b9..37945146c 100644 --- a/crates/kas-widgets/src/stack.rs +++ b/crates/kas-widgets/src/stack.rs @@ -106,8 +106,8 @@ impl_scope! { } } - fn draw(&mut self, mut draw: DrawCx) { - if let Some(entry) = self.widgets.get_mut(self.active) { + fn draw(&self, mut draw: DrawCx) { + if let Some(entry) = self.widgets.get(self.active) { debug_assert_eq!(entry.1, State::Sized); entry.0.draw(draw.re()); } diff --git a/crates/kas-widgets/src/text.rs b/crates/kas-widgets/src/text.rs index 6e06d3097..871fef89a 100644 --- a/crates/kas-widgets/src/text.rs +++ b/crates/kas-widgets/src/text.rs @@ -114,7 +114,7 @@ impl_scope! { self.text.set_rect(cx, rect, hints.combine(AlignHints::VERT_CENTER)); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { draw.text(self.rect(), &self.text); } } diff --git a/examples/clock.rs b/examples/clock.rs index 2c6ae06b4..3a6a9754f 100644 --- a/examples/clock.rs +++ b/examples/clock.rs @@ -71,7 +71,7 @@ impl_scope! { self.time_rect = Rect::new(time_pos, text_size); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { let accent: Rgba = Rgba8Srgb::parse("d7916f").into(); let col_back = Rgba::ga(0.0, 0.5); let col_face = accent.multiply(0.4); diff --git a/examples/gallery.rs b/examples/gallery.rs index cc458acb4..f7dc65154 100644 --- a/examples/gallery.rs +++ b/examples/gallery.rs @@ -433,7 +433,7 @@ fn canvas() -> Box> { #[derive(Debug)] struct Program(Instant); impl CanvasProgram for Program { - fn draw(&mut self, pixmap: &mut Pixmap) { + fn draw(&self, pixmap: &mut Pixmap) { let size = (200.0, 200.0); let scale = Transform::from_scale( f32::conv(pixmap.width()) / size.0, diff --git a/examples/mandlebrot/mandlebrot.rs b/examples/mandlebrot/mandlebrot.rs index a0a932e71..6360a20fa 100644 --- a/examples/mandlebrot/mandlebrot.rs +++ b/examples/mandlebrot/mandlebrot.rs @@ -350,7 +350,7 @@ impl_scope! { self.rel_width = rel_width.0 as f32; } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { let draw = draw.draw_device(); let draw = DrawIface::>::downcast_from(draw).unwrap(); let p = (self.alpha, self.delta, self.rel_width, self.iters); diff --git a/examples/proxy.rs b/examples/proxy.rs index 9c037962a..a8967d4be 100644 --- a/examples/proxy.rs +++ b/examples/proxy.rs @@ -74,7 +74,7 @@ impl_scope! { self.loading_text.set_rect(cx, rect, hints.combine(AlignHints::CENTER)); } - fn draw(&mut self, mut draw: DrawCx) { + fn draw(&self, mut draw: DrawCx) { if let Some(color) = self.color { let draw = draw.draw_device(); draw.rect((self.rect()).cast(), color); From 6a6a754fc24dbf8d2c15cc94856bb7a2b9c7df63 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 18 Feb 2025 15:20:46 +0000 Subject: [PATCH 06/11] Make Layout::try_probe, Tile::probe take &self --- crates/kas-core/src/core/layout.rs | 4 ++-- crates/kas-core/src/core/node.rs | 11 +---------- crates/kas-core/src/core/tile.rs | 2 +- crates/kas-core/src/event/cx/platform.rs | 10 +++++----- crates/kas-core/src/root.rs | 8 +++++--- crates/kas-macros/src/make_layout.rs | 2 +- crates/kas-macros/src/widget.rs | 8 ++++---- crates/kas-macros/src/widget_derive.rs | 2 +- crates/kas-view/src/list_view.rs | 4 ++-- crates/kas-view/src/matrix_view.rs | 4 ++-- crates/kas-widgets/src/adapt/with_label.rs | 2 +- crates/kas-widgets/src/button.rs | 2 +- crates/kas-widgets/src/check_box.rs | 2 +- crates/kas-widgets/src/combobox.rs | 2 +- crates/kas-widgets/src/edit.rs | 4 ++-- crates/kas-widgets/src/float.rs | 4 ++-- crates/kas-widgets/src/grid.rs | 4 ++-- crates/kas-widgets/src/list.rs | 4 ++-- crates/kas-widgets/src/menu/menu_entry.rs | 4 ++-- crates/kas-widgets/src/menu/menubar.rs | 4 ++-- crates/kas-widgets/src/menu/submenu.rs | 6 +++--- crates/kas-widgets/src/radio_box.rs | 2 +- crates/kas-widgets/src/scroll.rs | 2 +- crates/kas-widgets/src/scroll_bar.rs | 4 ++-- crates/kas-widgets/src/scroll_label.rs | 2 +- crates/kas-widgets/src/scroll_text.rs | 2 +- crates/kas-widgets/src/slider.rs | 2 +- crates/kas-widgets/src/spinner.rs | 2 +- crates/kas-widgets/src/splitter.rs | 6 +++--- crates/kas-widgets/src/stack.rs | 4 ++-- crates/kas-widgets/src/tab_stack.rs | 2 +- examples/cursors.rs | 2 +- 32 files changed, 58 insertions(+), 65 deletions(-) diff --git a/crates/kas-core/src/core/layout.rs b/crates/kas-core/src/core/layout.rs index a11837613..723c3cce1 100644 --- a/crates/kas-core/src/core/layout.rs +++ b/crates/kas-core/src/core/layout.rs @@ -156,7 +156,7 @@ pub trait Layout { /// /// Non-widgets do not have an [`Id`], and therefore should use the default /// implementation which simply returns `None`. - fn try_probe(&mut self, coord: Coord) -> Option { + fn try_probe(&self, coord: Coord) -> Option { let _ = coord; None } @@ -208,7 +208,7 @@ pub trait MacroDefinedLayout { fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints); /// Probe a coordinate for a widget's [`Id`] - fn try_probe(&mut self, coord: Coord) -> Option; + fn try_probe(&self, coord: Coord) -> Option; /// Draw a widget and its children fn draw(&self, draw: DrawCx); diff --git a/crates/kas-core/src/core/node.rs b/crates/kas-core/src/core/node.rs index 7dcaa5078..6f7c9d700 100644 --- a/crates/kas-core/src/core/node.rs +++ b/crates/kas-core/src/core/node.rs @@ -7,7 +7,7 @@ use super::Widget; use crate::event::{ConfigCx, Event, EventCx, IsUsed}; -use crate::geom::{Coord, Rect}; +use crate::geom::Rect; use crate::layout::{AlignHints, AxisInfo, SizeRules}; use crate::theme::SizeCx; use crate::{Id, NavAdvance, Tile}; @@ -28,7 +28,6 @@ trait NodeT { fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints); fn nav_next(&self, reverse: bool, from: Option) -> Option; - fn try_probe(&mut self, coord: Coord) -> Option; fn _configure(&mut self, cx: &mut ConfigCx, id: Id); fn _update(&mut self, cx: &mut ConfigCx); @@ -79,9 +78,6 @@ impl<'a, T> NodeT for (&'a mut dyn Widget, &'a T) { fn nav_next(&self, reverse: bool, from: Option) -> Option { self.0.nav_next(reverse, from) } - fn try_probe(&mut self, coord: Coord) -> Option { - self.0.try_probe(coord) - } fn _configure(&mut self, cx: &mut ConfigCx, id: Id) { self.0._configure(cx, self.1, id); @@ -298,11 +294,6 @@ impl<'a> Node<'a> { self.0.nav_next(reverse, from) } - /// Translate a coordinate to an [`Id`] - pub(crate) fn try_probe(&mut self, coord: Coord) -> Option { - self.0.try_probe(coord) - } - /// Internal method: configure recursively pub(crate) fn _configure(&mut self, cx: &mut ConfigCx, id: Id) { cfg_if::cfg_if! { diff --git a/crates/kas-core/src/core/tile.rs b/crates/kas-core/src/core/tile.rs index c6d99ceeb..1663e23a4 100644 --- a/crates/kas-core/src/core/tile.rs +++ b/crates/kas-core/src/core/tile.rs @@ -197,7 +197,7 @@ pub trait Tile: Layout { /// let coord = coord + self.translation(); /// MacroDefinedLayout::try_probe(self, coord).unwrap_or_else(|| self.id()) /// ``` - fn probe(&mut self, coord: Coord) -> Id + fn probe(&self, coord: Coord) -> Id where Self: Sized, { diff --git a/crates/kas-core/src/event/cx/platform.rs b/crates/kas-core/src/event/cx/platform.rs index d1689817f..51855e72a 100644 --- a/crates/kas-core/src/event/cx/platform.rs +++ b/crates/kas-core/src/event/cx/platform.rs @@ -272,11 +272,11 @@ impl EventState { cx.action.remove(Action::REGION_MOVED); // Update hovered widget - let hover = win.try_probe(data, cx.last_mouse_coord); + let hover = win.try_probe(cx.last_mouse_coord); cx.set_hover(win.as_node(data), hover); for grab in cx.touch_grab.iter_mut() { - grab.cur_id = win.try_probe(data, grab.coord); + grab.cur_id = win.try_probe(grab.coord); } } }); @@ -422,7 +422,7 @@ impl<'a> EventCx<'a> { let coord = position.cast_approx(); // Update hovered win - let id = win.try_probe(data, coord); + let id = win.try_probe(coord); self.set_hover(win.as_node(data), id.clone()); if let Some(grab) = self.state.mouse_grab.as_mut() { @@ -541,7 +541,7 @@ impl<'a> EventCx<'a> { let coord = touch.location.cast_approx(); match touch.phase { TouchPhase::Started => { - let start_id = win.try_probe(data, coord); + let start_id = win.try_probe(coord); if let Some(id) = start_id.as_ref() { if self.config.event().touch_nav_focus() { if let Some(id) = @@ -561,7 +561,7 @@ impl<'a> EventCx<'a> { } } TouchPhase::Moved => { - let cur_id = win.try_probe(data, coord); + let cur_id = win.try_probe(coord); let mut redraw = false; let mut pan_grab = None; diff --git a/crates/kas-core/src/root.rs b/crates/kas-core/src/root.rs index 5e28d2942..964da82a1 100644 --- a/crates/kas-core/src/root.rs +++ b/crates/kas-core/src/root.rs @@ -152,13 +152,15 @@ impl_scope! { } impl Self { - pub(crate) fn try_probe(&mut self, data: &Data, coord: Coord) -> Option { + pub(crate) fn try_probe(&self, coord: Coord) -> Option { if !self.rect().contains(coord) { return None; } for (_, popup, translation) in self.popups.iter().rev() { - if let Some(Some(id)) = self.inner.as_node(data).find_node(&popup.id, |mut node| node.try_probe(coord + *translation)) { - return Some(id); + if let Some(widget) = self.inner.find_widget(&popup.id) { + if let Some(id) = widget.try_probe(coord + *translation) { + return Some(id); + } } } if self.bar_h > 0 { diff --git a/crates/kas-macros/src/make_layout.rs b/crates/kas-macros/src/make_layout.rs index 3ea17b4a9..10cd93d3e 100644 --- a/crates/kas-macros/src/make_layout.rs +++ b/crates/kas-macros/src/make_layout.rs @@ -64,7 +64,7 @@ impl Tree { let mut toks = Toks::new(); for target in &targets { toks.append_all(quote! { - if let Some(id) = ::kas::Layout::try_probe(&mut #target, coord) { + if let Some(id) = ::kas::Layout::try_probe(&#target, coord) { Some(id) } else }); diff --git a/crates/kas-macros/src/widget.rs b/crates/kas-macros/src/widget.rs index bae463b42..5ef1bf57f 100644 --- a/crates/kas-macros/src/widget.rs +++ b/crates/kas-macros/src/widget.rs @@ -412,7 +412,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul } #[inline] - fn try_probe(&mut self, coord: ::kas::geom::Coord) -> Option<::kas::Id> { + fn try_probe(&self, coord: ::kas::geom::Coord) -> Option<::kas::Id> { #try_probe } @@ -452,7 +452,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul }; fn_try_probe = quote! { - fn try_probe(&mut self, coord: ::kas::geom::Coord) -> Option<::kas::Id> { + fn try_probe(&self, coord: ::kas::geom::Coord) -> Option<::kas::Id> { #[cfg(debug_assertions)] self.#core.status.require_rect(&self.#core._id); @@ -490,7 +490,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul }; fn_try_probe = quote! { - fn try_probe(&mut self, coord: ::kas::geom::Coord) -> Option<::kas::Id> { + fn try_probe(&self, coord: ::kas::geom::Coord) -> Option<::kas::Id> { #[cfg(debug_assertions)] self.#core.status.require_rect(&self.#core._id); @@ -507,7 +507,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul let fn_probe = quote! { #[inline] - fn probe(&mut self, coord: ::kas::geom::Coord) -> ::kas::Id { + fn probe(&self, coord: ::kas::geom::Coord) -> ::kas::Id { #probe } }; diff --git a/crates/kas-macros/src/widget_derive.rs b/crates/kas-macros/src/widget_derive.rs index cc8a29c2a..84f449a10 100644 --- a/crates/kas-macros/src/widget_derive.rs +++ b/crates/kas-macros/src/widget_derive.rs @@ -191,7 +191,7 @@ pub fn widget(_attr_span: Span, args: WidgetArgs, scope: &mut Scope) -> Result<( }; let fn_try_probe = quote! { #[inline] - fn try_probe(&mut self, coord: ::kas::geom::Coord) -> Option<::kas::Id> { + fn try_probe(&self, coord: ::kas::geom::Coord) -> Option<::kas::Id> { self.#inner.try_probe(coord) } }; diff --git a/crates/kas-view/src/list_view.rs b/crates/kas-view/src/list_view.rs index 5f16b55ed..dbf9d87b9 100644 --- a/crates/kas-view/src/list_view.rs +++ b/crates/kas-view/src/list_view.rs @@ -565,9 +565,9 @@ impl_scope! { self.scroll_offset() } - fn probe(&mut self, coord: Coord) -> Id { + fn probe(&self, coord: Coord) -> Id { let coord = coord + self.scroll.offset(); - for child in &mut self.widgets[..self.cur_len.cast()] { + for child in &self.widgets[..self.cur_len.cast()] { if child.key.is_some() { if let Some(id) = child.widget.try_probe(coord) { return id; diff --git a/crates/kas-view/src/matrix_view.rs b/crates/kas-view/src/matrix_view.rs index 6f8b059c9..b046fbbbe 100644 --- a/crates/kas-view/src/matrix_view.rs +++ b/crates/kas-view/src/matrix_view.rs @@ -507,10 +507,10 @@ impl_scope! { self.scroll_offset() } - fn probe(&mut self, coord: Coord) -> Id { + fn probe(&self, coord: Coord) -> Id { let num = self.num_children(); let coord = coord + self.scroll.offset(); - for child in &mut self.widgets[..num] { + for child in &self.widgets[..num] { if child.key.is_some() { if let Some(id) = child.widget.try_probe(coord) { return id; diff --git a/crates/kas-widgets/src/adapt/with_label.rs b/crates/kas-widgets/src/adapt/with_label.rs index 8cec44b2d..e2f2b2c49 100644 --- a/crates/kas-widgets/src/adapt/with_label.rs +++ b/crates/kas-widgets/src/adapt/with_label.rs @@ -107,7 +107,7 @@ impl_scope! { from.xor(Some(widget_index!(self.inner))) } - fn probe(&mut self, _: Coord) -> Id { + fn probe(&self, _: Coord) -> Id { self.inner.id() } } diff --git a/crates/kas-widgets/src/button.rs b/crates/kas-widgets/src/button.rs index 51c4bd0a5..c7fe67440 100644 --- a/crates/kas-widgets/src/button.rs +++ b/crates/kas-widgets/src/button.rs @@ -95,7 +95,7 @@ impl_scope! { } impl Tile for Self { - fn probe(&mut self, _: Coord) -> Id { + fn probe(&self, _: Coord) -> Id { self.id() } } diff --git a/crates/kas-widgets/src/check_box.rs b/crates/kas-widgets/src/check_box.rs index d033a41ff..9fd1620b2 100644 --- a/crates/kas-widgets/src/check_box.rs +++ b/crates/kas-widgets/src/check_box.rs @@ -198,7 +198,7 @@ impl_scope! { from.xor(Some(widget_index!(self.inner))) } - fn probe(&mut self, _: Coord) -> Id { + fn probe(&self, _: Coord) -> Id { self.inner.id() } } diff --git a/crates/kas-widgets/src/combobox.rs b/crates/kas-widgets/src/combobox.rs index c80745279..fe4e1b53c 100644 --- a/crates/kas-widgets/src/combobox.rs +++ b/crates/kas-widgets/src/combobox.rs @@ -53,7 +53,7 @@ impl_scope! { None } - fn probe(&mut self, _: Coord) -> Id { + fn probe(&self, _: Coord) -> Id { self.id() } } diff --git a/crates/kas-widgets/src/edit.rs b/crates/kas-widgets/src/edit.rs index 4cf606742..8c561f120 100644 --- a/crates/kas-widgets/src/edit.rs +++ b/crates/kas-widgets/src/edit.rs @@ -396,7 +396,7 @@ impl_scope! { } impl Tile for Self { - fn probe(&mut self, coord: Coord) -> Id { + fn probe(&self, coord: Coord) -> Id { if self.inner.max_scroll_offset().1 > 0 { if let Some(id) = self.bar.try_probe(coord) { return id; @@ -780,7 +780,7 @@ impl_scope! { } impl Tile for Self { - fn probe(&mut self, _: Coord) -> Id { + fn probe(&self, _: Coord) -> Id { self.id() } } diff --git a/crates/kas-widgets/src/float.rs b/crates/kas-widgets/src/float.rs index b9809c3cb..36d014eda 100644 --- a/crates/kas-widgets/src/float.rs +++ b/crates/kas-widgets/src/float.rs @@ -147,9 +147,9 @@ impl_scope! { self.widgets.get_tile(index) } - fn probe(&mut self, coord: Coord) -> Id { + fn probe(&self, coord: Coord) -> Id { for i in 0..self.widgets.len() { - if let Some(child) = self.widgets.get_mut_tile(i) { + if let Some(child) = self.widgets.get_tile(i) { if let Some(id) = child.try_probe(coord) { return id; } diff --git a/crates/kas-widgets/src/grid.rs b/crates/kas-widgets/src/grid.rs index 216f3e093..77f06a1b6 100644 --- a/crates/kas-widgets/src/grid.rs +++ b/crates/kas-widgets/src/grid.rs @@ -215,9 +215,9 @@ impl_scope! { self.widgets.get_tile(index).map(|w| w.as_tile()) } - fn probe(&mut self, coord: Coord) -> Id { + fn probe(&self, coord: Coord) -> Id { for n in 0..self.widgets.len() { - if let Some(child) = self.widgets.get_mut_tile(n) { + if let Some(child) = self.widgets.get_tile(n) { if let Some(id) = child.try_probe(coord) { return id; } diff --git a/crates/kas-widgets/src/list.rs b/crates/kas-widgets/src/list.rs index 5e413f2dd..ef27b1821 100644 --- a/crates/kas-widgets/src/list.rs +++ b/crates/kas-widgets/src/list.rs @@ -306,10 +306,10 @@ impl_scope! { .and_then(|k| self.id_map.get(&k).cloned()) } - fn probe(&mut self, coord: Coord) -> Id { + fn probe(&self, coord: Coord) -> Id { let solver = RowPositionSolver::new(self.direction); solver - .find_child_mut(&mut self.widgets, coord) + .find_child(&self.widgets, coord) .and_then(|child| child.try_probe(coord)) .unwrap_or_else(|| self.id()) } diff --git a/crates/kas-widgets/src/menu/menu_entry.rs b/crates/kas-widgets/src/menu/menu_entry.rs index 8c89ac206..b06c9e406 100644 --- a/crates/kas-widgets/src/menu/menu_entry.rs +++ b/crates/kas-widgets/src/menu/menu_entry.rs @@ -38,7 +38,7 @@ impl_scope! { } impl Tile for Self { - fn probe(&mut self, _: Coord) -> Id { + fn probe(&self, _: Coord) -> Id { self.id() } } @@ -129,7 +129,7 @@ impl_scope! { } impl Tile for Self { - fn probe(&mut self, _: Coord) -> Id { + fn probe(&self, _: Coord) -> Id { self.checkbox.id() } } diff --git a/crates/kas-widgets/src/menu/menubar.rs b/crates/kas-widgets/src/menu/menubar.rs index f57897560..3abc55094 100644 --- a/crates/kas-widgets/src/menu/menubar.rs +++ b/crates/kas-widgets/src/menu/menubar.rs @@ -110,10 +110,10 @@ impl_scope! { self.widgets.get(index).map(|w| w.as_tile()) } - fn probe(&mut self, coord: Coord) -> Id { + fn probe(&self, coord: Coord) -> Id { let solver = RowPositionSolver::new(self.direction); solver - .find_child_mut(&mut self.widgets, coord) + .find_child(&self.widgets, coord) .and_then(|child| child.try_probe(coord)) .unwrap_or_else(|| self.id()) } diff --git a/crates/kas-widgets/src/menu/submenu.rs b/crates/kas-widgets/src/menu/submenu.rs index 978f2924f..98227a202 100644 --- a/crates/kas-widgets/src/menu/submenu.rs +++ b/crates/kas-widgets/src/menu/submenu.rs @@ -116,7 +116,7 @@ impl_scope! { None } - fn probe(&mut self, _: Coord) -> Id { + fn probe(&self, _: Coord) -> Id { self.id() } } @@ -348,8 +348,8 @@ impl_scope! { self.list.get(index).map(|w| w.as_tile()) } - fn probe(&mut self, coord: Coord) -> Id { - for child in self.list.iter_mut() { + fn probe(&self, coord: Coord) -> Id { + for child in self.list.iter() { if let Some(id) = child.try_probe(coord) { return id; } diff --git a/crates/kas-widgets/src/radio_box.rs b/crates/kas-widgets/src/radio_box.rs index e1c279de9..c98126512 100644 --- a/crates/kas-widgets/src/radio_box.rs +++ b/crates/kas-widgets/src/radio_box.rs @@ -159,7 +159,7 @@ impl_scope! { from.xor(Some(widget_index!(self.inner))) } - fn probe(&mut self, _: Coord) -> Id { + fn probe(&self, _: Coord) -> Id { self.inner.id() } } diff --git a/crates/kas-widgets/src/scroll.rs b/crates/kas-widgets/src/scroll.rs index 9a2b457c7..2c9dd55e1 100644 --- a/crates/kas-widgets/src/scroll.rs +++ b/crates/kas-widgets/src/scroll.rs @@ -129,7 +129,7 @@ impl_scope! { self.scroll_offset() } - fn probe(&mut self, coord: Coord) -> Id { + fn probe(&self, coord: Coord) -> Id { self.inner.try_probe(coord + self.translation()) .unwrap_or_else(|| self.id()) } diff --git a/crates/kas-widgets/src/scroll_bar.rs b/crates/kas-widgets/src/scroll_bar.rs index 16b37426b..3e7c0ffe7 100644 --- a/crates/kas-widgets/src/scroll_bar.rs +++ b/crates/kas-widgets/src/scroll_bar.rs @@ -329,7 +329,7 @@ impl_scope! { } impl Tile for Self { - fn probe(&mut self, coord: Coord) -> Id { + fn probe(&self, coord: Coord) -> Id { if self.invisible && self.max_value == 0 { return self.id(); } @@ -582,7 +582,7 @@ impl_scope! { } impl Tile for Self { - fn probe(&mut self, coord: Coord) -> Id { + fn probe(&self, coord: Coord) -> Id { self.vert_bar.try_probe(coord) .or_else(|| self.horiz_bar.try_probe(coord)) .or_else(|| self.inner.try_probe(coord)) diff --git a/crates/kas-widgets/src/scroll_label.rs b/crates/kas-widgets/src/scroll_label.rs index cc6b17e20..197c8c2e3 100644 --- a/crates/kas-widgets/src/scroll_label.rs +++ b/crates/kas-widgets/src/scroll_label.rs @@ -80,7 +80,7 @@ impl_scope! { } impl Tile for Self { - fn probe(&mut self, coord: Coord) -> Id { + fn probe(&self, coord: Coord) -> Id { self.bar.try_probe(coord).unwrap_or_else(|| self.id()) } } diff --git a/crates/kas-widgets/src/scroll_text.rs b/crates/kas-widgets/src/scroll_text.rs index 6d1db65cc..d845b5c0f 100644 --- a/crates/kas-widgets/src/scroll_text.rs +++ b/crates/kas-widgets/src/scroll_text.rs @@ -80,7 +80,7 @@ impl_scope! { } impl Tile for Self { - fn probe(&mut self, coord: Coord) -> Id { + fn probe(&self, coord: Coord) -> Id { self.bar.try_probe(coord).unwrap_or_else(|| self.id()) } } diff --git a/crates/kas-widgets/src/slider.rs b/crates/kas-widgets/src/slider.rs index 6b92c2810..94f8b988c 100644 --- a/crates/kas-widgets/src/slider.rs +++ b/crates/kas-widgets/src/slider.rs @@ -324,7 +324,7 @@ impl_scope! { } impl Tile for Self { - fn probe(&mut self, coord: Coord) -> Id { + fn probe(&self, coord: Coord) -> Id { if self.on_move.is_some() { if let Some(id) = self.grip.try_probe(coord) { return id; diff --git a/crates/kas-widgets/src/spinner.rs b/crates/kas-widgets/src/spinner.rs index cb471a066..344aee2c8 100644 --- a/crates/kas-widgets/src/spinner.rs +++ b/crates/kas-widgets/src/spinner.rs @@ -289,7 +289,7 @@ impl_scope! { } impl Tile for Self { - fn probe(&mut self, coord: Coord) -> Id { + fn probe(&self, coord: Coord) -> Id { self.b_up.try_probe(coord) .or_else(|| self.b_down.try_probe(coord)) .unwrap_or_else(|| self.edit.id()) diff --git a/crates/kas-widgets/src/splitter.rs b/crates/kas-widgets/src/splitter.rs index ce669ddd3..2cc606d00 100644 --- a/crates/kas-widgets/src/splitter.rs +++ b/crates/kas-widgets/src/splitter.rs @@ -235,7 +235,7 @@ impl_scope! { .and_then(|k| self.id_map.get(&k).cloned()) } - fn probe(&mut self, coord: Coord) -> Id { + fn probe(&self, coord: Coord) -> Id { if !self.size_solved { debug_assert!(false); return self.id(); @@ -246,12 +246,12 @@ impl_scope! { // calling it twice. let solver = layout::RowPositionSolver::new(self.direction); - if let Some(child) = solver.find_child_mut(&mut self.widgets, coord) { + if let Some(child) = solver.find_child(&self.widgets, coord) { return child.try_probe(coord).unwrap_or_else(|| self.id()); } let solver = layout::RowPositionSolver::new(self.direction); - if let Some(child) = solver.find_child_mut(&mut self.grips, coord) { + if let Some(child) = solver.find_child(&self.grips, coord) { return child.try_probe(coord).unwrap_or_else(|| self.id()); } diff --git a/crates/kas-widgets/src/stack.rs b/crates/kas-widgets/src/stack.rs index 37945146c..ef58665a1 100644 --- a/crates/kas-widgets/src/stack.rs +++ b/crates/kas-widgets/src/stack.rs @@ -136,8 +136,8 @@ impl_scope! { } } - fn probe(&mut self, coord: Coord) -> Id { - if let Some(entry) = self.widgets.get_mut(self.active) { + fn probe(&self, coord: Coord) -> Id { + if let Some(entry) = self.widgets.get(self.active) { debug_assert_eq!(entry.1, State::Sized); if let Some(id) = entry.0.try_probe(coord) { return id; diff --git a/crates/kas-widgets/src/tab_stack.rs b/crates/kas-widgets/src/tab_stack.rs index b2e6027cc..cd789bc84 100644 --- a/crates/kas-widgets/src/tab_stack.rs +++ b/crates/kas-widgets/src/tab_stack.rs @@ -48,7 +48,7 @@ impl_scope! { } impl Tile for Self { - fn probe(&mut self, _: Coord) -> Id { + fn probe(&self, _: Coord) -> Id { self.id() } } diff --git a/examples/cursors.rs b/examples/cursors.rs index e2e0e023b..04db0722b 100644 --- a/examples/cursors.rs +++ b/examples/cursors.rs @@ -22,7 +22,7 @@ impl_scope! { cursor: CursorIcon, } impl Tile for Self { - fn probe(&mut self, _: Coord) -> Id { + fn probe(&self, _: Coord) -> Id { // Steal mouse focus: hover points to self, not self.label self.id() } From b8324e4ee512ee4f739d97d38dd8742f291d6371 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 18 Feb 2025 15:35:46 +0000 Subject: [PATCH 07/11] Move fn Tile::rect to Layout --- crates/kas-core/src/core/layout.rs | 11 +++ crates/kas-core/src/core/tile.rs | 10 +-- crates/kas-core/src/theme/text.rs | 4 ++ crates/kas-macros/src/lib.rs | 6 +- crates/kas-macros/src/widget.rs | 97 ++++++++++++-------------- crates/kas-macros/src/widget_derive.rs | 15 ++-- crates/kas-widgets/src/grip.rs | 10 ++- 7 files changed, 80 insertions(+), 73 deletions(-) diff --git a/crates/kas-core/src/core/layout.rs b/crates/kas-core/src/core/layout.rs index 723c3cce1..d3b5fdb2d 100644 --- a/crates/kas-core/src/core/layout.rs +++ b/crates/kas-core/src/core/layout.rs @@ -51,6 +51,14 @@ use kas_macros::autoimpl; /// [`#widget`]: macros::widget #[autoimpl(for &'_ mut T, Box)] pub trait Layout { + /// Get the widget's region + /// + /// Coordinates are relative to the parent's coordinate space. + /// + /// This method is usually implemented by the `#[widget]` macro. + /// See also [`kas::widget_set_rect`]. + fn rect(&self) -> Rect; + /// Get size rules for the given axis /// /// # Calling @@ -201,6 +209,9 @@ pub trait Layout { /// /// TODO: add an example pub trait MacroDefinedLayout { + /// Get the widget's region + fn rect(&self) -> Rect; + /// Get size rules for the given axis fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules; diff --git a/crates/kas-core/src/core/tile.rs b/crates/kas-core/src/core/tile.rs index 1663e23a4..c48fa4a68 100644 --- a/crates/kas-core/src/core/tile.rs +++ b/crates/kas-core/src/core/tile.rs @@ -5,7 +5,7 @@ //! Layout, Tile and TileExt traits -use crate::geom::{Coord, Offset, Rect}; +use crate::geom::{Coord, Offset}; use crate::util::IdentifyWidget; use crate::{HasId, Id, Layout}; use kas_macros::autoimpl; @@ -21,7 +21,7 @@ use kas_macros::autoimpl; /// /// - Has no [`Data`](Widget::Data) parameter /// - Supports read-only tree reflection: [`Self::get_child`] -/// - Provides some basic operations: [`Self::id_ref`], [`Self::rect`] +/// - Provides some basic operations: [`Self::id_ref`] /// - Covers sizing and drawing operations from [`Layout`] /// /// `Tile` may not be implemented directly; it will be implemented by the @@ -74,12 +74,6 @@ pub trait Tile: Layout { self.id_ref().clone() } - /// Get the widget's region, relative to its parent. - /// - /// This method is usually implemented by the `#[widget]` macro. - /// See also [`kas::widget_set_rect`]. - fn rect(&self) -> Rect; - /// Get the name of the widget struct /// /// This method is implemented by the `#[widget]` macro. diff --git a/crates/kas-core/src/theme/text.rs b/crates/kas-core/src/theme/text.rs index 7f0b1b38a..354164013 100644 --- a/crates/kas-core/src/theme/text.rs +++ b/crates/kas-core/src/theme/text.rs @@ -66,6 +66,10 @@ impl Default for Text { /// Implement [`Layout`], using default alignment where alignment is not provided impl Layout for Text { + fn rect(&self) -> Rect { + self.rect + } + fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules { sizer.text_rules(self, axis) } diff --git a/crates/kas-macros/src/lib.rs b/crates/kas-macros/src/lib.rs index dbc37c37c..0482f1848 100644 --- a/crates/kas-macros/src/lib.rs +++ b/crates/kas-macros/src/lib.rs @@ -419,12 +419,12 @@ pub fn widget_index(input: TokenStream) -> TokenStream { /// Macro to set the `rect` stored in the widget core /// /// Widgets have a hidden field of type [`Rect`] in their `widget_core!()`, used -/// to implement method [`Tile::rect`]. This macro assigns to that field. +/// to implement method [`Layout::rect`]. This macro assigns to that field. /// /// This macro is usable only within the definition of `Layout::set_rect` within /// an [`impl_scope!`] macro using the [`widget`](macro@widget) attribute. /// -/// The method `Tile::rect` will be generated if this macro is used by the +/// The method `Layout::rect` will be generated if this macro is used by the /// widget, otherwise a definition of the method must be provided. /// /// Example usage: @@ -435,7 +435,7 @@ pub fn widget_index(input: TokenStream) -> TokenStream { /// ``` /// /// [`Rect`]: https://docs.rs/kas/latest/kas/geom/struct.Rect.html -/// [`Tile::rect`]: https://docs.rs/kas/latest/kas/trait.Tile.html#method.rect +/// [`Layout::rect`]: https://docs.rs/kas/latest/kas/trait.Layout.html#method.rect #[proc_macro_error] #[proc_macro] pub fn widget_set_rect(input: TokenStream) -> TokenStream { diff --git a/crates/kas-macros/src/widget.rs b/crates/kas-macros/src/widget.rs index 5ef1bf57f..28e1cab72 100644 --- a/crates/kas-macros/src/widget.rs +++ b/crates/kas-macros/src/widget.rs @@ -391,6 +391,11 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul scope.generated.push(quote! { impl #impl_generics ::kas::MacroDefinedLayout for #impl_target { + #[inline] + fn rect(&self) -> ::kas::geom::Rect { + #core_path._rect + } + #[inline] fn size_rules( &mut self, @@ -456,7 +461,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul #[cfg(debug_assertions)] self.#core.status.require_rect(&self.#core._id); - ::kas::Tile::rect(self).contains(coord).then(|| ::kas::Tile::probe(self, coord)) + self.rect().contains(coord).then(|| ::kas::Tile::probe(self, coord)) } }; @@ -494,7 +499,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul #[cfg(debug_assertions)] self.#core.status.require_rect(&self.#core._id); - ::kas::Tile::rect(self).contains(coord).then(|| ::kas::Tile::probe(self, coord)) + self.rect().contains(coord).then(|| ::kas::Tile::probe(self, coord)) } }; @@ -593,9 +598,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul }); } - let core_rect_is_set; let mut widget_set_rect_span = None; - let mut fn_set_rect_span = None; if let Some(index) = layout_impl { let layout_impl = &mut scope.impls[index]; let item_idents = collect_idents(layout_impl); @@ -620,6 +623,8 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul layout_impl.items.push(Verbatim(method)); } + let core_rect_is_set; + let mut fn_set_rect_span = None; if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "set_rect") { if let ImplItem::Fn(f) = &mut layout_impl.items[*index] { fn_set_rect_span = Some(f.span()); @@ -642,6 +647,40 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul core_rect_is_set = true; } + // TODO(opt): omit field widget.core._rect if !core_rect_is_set + if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "rect") { + if let Some(span) = widget_set_rect_span { + let fn_rect_span = layout_impl.items[*index].span(); + emit_warning!( + span, "assignment `widget_set_rect!` has no effect when `fn rect` is defined"; + note = fn_rect_span => "this `fn rect`"; + ); + } + if core_rect_is_set { + let fn_rect_span = layout_impl.items[*index].span(); + if let Some(span) = widget_set_rect_span { + emit_warning!( + span, "assignment `widget_set_rect!` has no effect when `fn rect` is defined"; + note = fn_rect_span => "this `fn rect`"; + ); + } else { + emit_warning!( + fn_rect_span, + "definition of `Layout::set_rect` is expected when `fn rect` is defined" + ); + } + } + } else if core_rect_is_set { + layout_impl.items.push(Verbatim(fn_rect(&core_path))); + } else { + if let Some(span) = fn_set_rect_span { + emit_warning!( + span, "`widget_set_rect!(/* rect */)` not found"; + note = "`fn Layout::rect()` implementation cannot be generated without `widget_set_rect!`"; + ); + } + } + if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "try_probe") { if let ImplItem::Fn(f) = &mut layout_impl.items[*index] { emit_error!( @@ -680,26 +719,19 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul layout_impl.items.push(Verbatim(method)); } } else if let Some(fn_size_rules) = fn_size_rules { + let fn_rect = fn_rect(&core_path); scope.generated.push(quote! { impl #impl_generics ::kas::Layout for #impl_target { + #fn_rect #fn_size_rules #fn_set_rect #fn_try_probe #fn_draw } }); - core_rect_is_set = true; - } else { - core_rect_is_set = false; } - let mut have_fn_rect = core_rect_is_set; let required_tile_methods = required_tile_methods(&name.to_string(), &core_path); - let fn_rect = if core_rect_is_set { - Some(fn_rect(&core_path)) - } else { - None - }; if let Some(index) = tile_impl { let tile_impl = &mut scope.impls[index]; @@ -708,33 +740,6 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul tile_impl.items.push(Verbatim(required_tile_methods)); - if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "rect") { - have_fn_rect = true; - if let Some(span) = widget_set_rect_span { - let fn_rect_span = tile_impl.items[*index].span(); - emit_warning!( - span, "assignment `widget_set_rect!` has no effect when `fn rect` is defined"; - note = fn_rect_span => "this `fn rect`"; - ); - } - if core_rect_is_set { - let fn_rect_span = tile_impl.items[*index].span(); - if let Some(span) = widget_set_rect_span { - emit_warning!( - span, "assignment `widget_set_rect!` has no effect when `fn rect` is defined"; - note = fn_rect_span => "this `fn rect`"; - ); - } else { - emit_warning!( - fn_rect_span, - "definition of `Layout::set_rect` is expected when `fn rect` is defined" - ); - } - } - } else if let Some(method) = fn_rect { - tile_impl.items.push(Verbatim(method)); - } - if let Some(methods) = fns_get_child { tile_impl.items.push(Verbatim(methods)); } @@ -764,7 +769,6 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul scope.generated.push(quote! { impl #impl_generics ::kas::Tile for #impl_target { #required_tile_methods - #fn_rect #fns_get_child #fn_nav_next #fn_probe @@ -772,17 +776,6 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul }); } - // TODO(opt): omit field widget.core._rect if !core_rect_is_set - - if !have_fn_rect { - if let Some(span) = fn_set_rect_span { - emit_warning!( - span, "`widget_set_rect!(/* rect */)` not found"; - note = "`fn Tile::rect()` implementation cannot be generated without `widget_set_rect!`"; - ); - } - } - if let Ok(val) = std::env::var("KAS_DEBUG_WIDGET") { if name == val.as_str() { println!("{}", scope.to_token_stream()); diff --git a/crates/kas-macros/src/widget_derive.rs b/crates/kas-macros/src/widget_derive.rs index 84f449a10..f6fb9f2fc 100644 --- a/crates/kas-macros/src/widget_derive.rs +++ b/crates/kas-macros/src/widget_derive.rs @@ -145,10 +145,6 @@ pub fn widget(_attr_span: Span, args: WidgetArgs, scope: &mut Scope) -> Result<( fn id_ref(&self) -> &::kas::Id { self.#inner.id_ref() } - #[inline] - fn rect(&self) -> ::kas::geom::Rect { - self.#inner.rect() - } #[inline] fn widget_name(&self) -> &'static str { @@ -169,6 +165,12 @@ pub fn widget(_attr_span: Span, args: WidgetArgs, scope: &mut Scope) -> Result<( } }; + let fn_rect = quote! { + #[inline] + fn rect(&self) -> ::kas::geom::Rect { + self.#inner.rect() + } + }; let fn_size_rules = quote! { #[inline] fn size_rules(&mut self, @@ -207,6 +209,10 @@ pub fn widget(_attr_span: Span, args: WidgetArgs, scope: &mut Scope) -> Result<( let item_idents = collect_idents(layout_impl); let has_item = |name| item_idents.iter().any(|(_, ident)| ident == name); + if !has_item("rect") { + layout_impl.items.push(Verbatim(fn_rect)); + } + if !has_item("size_rules") { layout_impl.items.push(Verbatim(fn_size_rules)); } @@ -225,6 +231,7 @@ pub fn widget(_attr_span: Span, args: WidgetArgs, scope: &mut Scope) -> Result<( } else { scope.generated.push(quote! { impl #impl_generics ::kas::Layout for #impl_target { + #fn_rect #fn_size_rules #fn_set_rect #fn_try_probe diff --git a/crates/kas-widgets/src/grip.rs b/crates/kas-widgets/src/grip.rs index 70c7a0e02..6ff979975 100644 --- a/crates/kas-widgets/src/grip.rs +++ b/crates/kas-widgets/src/grip.rs @@ -80,6 +80,10 @@ impl_scope! { /// This implementation is unusual (see [`GripPart`] documentation). impl Layout for GripPart { + fn rect(&self) -> Rect { + self.rect + } + fn size_rules(&mut self, _: SizeCx, _axis: AxisInfo) -> SizeRules { SizeRules::EMPTY } @@ -91,12 +95,6 @@ impl_scope! { fn draw(&self, _: DrawCx) {} } - impl Tile for Self { - fn rect(&self) -> Rect { - self.rect - } - } - impl Events for GripPart { type Data = (); From b8645b6193cafb7fce4f5d6e55e7158281a63eab Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 18 Feb 2025 16:46:44 +0000 Subject: [PATCH 08/11] Remove usages of widget_set_rect! where the layout easily provides the rect --- crates/kas-core/src/decorations.rs | 1 - crates/kas-core/src/hidden.rs | 3 +- crates/kas-macros/src/make_layout.rs | 18 ++++++ crates/kas-macros/src/widget.rs | 83 +++++++++++++--------------- crates/kas-widgets/src/check_box.rs | 1 - crates/kas-widgets/src/label.rs | 2 - crates/kas-widgets/src/radio_box.rs | 1 - crates/kas-widgets/src/spinner.rs | 1 - crates/kas-widgets/src/text.rs | 1 - 9 files changed, 57 insertions(+), 54 deletions(-) diff --git a/crates/kas-core/src/decorations.rs b/crates/kas-core/src/decorations.rs index ec83c98c5..e5df88e7a 100644 --- a/crates/kas-core/src/decorations.rs +++ b/crates/kas-core/src/decorations.rs @@ -123,7 +123,6 @@ impl_scope! { impl Layout for Self { fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) { - widget_set_rect!(rect); self.text.set_rect(cx, rect, hints.combine(AlignHints::CENTER)); } } diff --git a/crates/kas-core/src/hidden.rs b/crates/kas-core/src/hidden.rs index 1b839c4c5..8764428ec 100644 --- a/crates/kas-core/src/hidden.rs +++ b/crates/kas-core/src/hidden.rs @@ -15,7 +15,7 @@ use crate::layout::{AlignHints, AxisInfo, SizeRules}; use crate::theme::{SizeCx, Text, TextClass}; #[allow(unused)] use crate::Action; use crate::{Events, Layout, Widget}; -use kas_macros::{autoimpl, impl_scope, widget_set_rect}; +use kas_macros::{autoimpl, impl_scope}; impl_scope! { /// A simple text label @@ -51,7 +51,6 @@ impl_scope! { impl Layout for Self { fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) { - widget_set_rect!(rect); self.text.set_rect(cx, rect, hints.combine(AlignHints::VERT_CENTER)); } } diff --git a/crates/kas-macros/src/make_layout.rs b/crates/kas-macros/src/make_layout.rs index 10cd93d3e..c44770616 100644 --- a/crates/kas-macros/src/make_layout.rs +++ b/crates/kas-macros/src/make_layout.rs @@ -47,6 +47,11 @@ impl Tree { fields } + /// Yield an implementation of `fn rect`, if easy + pub fn rect(&self, core_path: &Toks) -> Option { + self.0.rect(core_path) + } + /// Yield an implementation of `fn size_rules` pub fn size_rules(&self, core_path: &Toks) -> Toks { self.0.size_rules(core_path) @@ -624,6 +629,19 @@ impl Layout { } } + /// Yield an implementation of `fn rect`, if easy + fn rect(&self, core_path: &Toks) -> Option { + match self { + Layout::Align(layout, _) | Layout::Pack(layout, _) => layout.rect(core_path), + Layout::Single(expr) => Some(quote! { ::kas::Layout::rect(&#expr) }), + Layout::Widget(stor, _) | Layout::Label(stor, _) => { + Some(quote! { ::kas::Layout::rect(&#core_path.#stor) }) + } + Layout::Frame(stor, _, _, _) => Some(quote! { #core_path.#stor.rect }), + Layout::List(_, _, _) | Layout::Float(_) | Layout::Grid(_, _, _) => None, + } + } + /// Yield an implementation of `fn size_rules` fn size_rules(&self, core_path: &Toks) -> Toks { match self { diff --git a/crates/kas-macros/src/widget.rs b/crates/kas-macros/src/widget.rs index 28e1cab72..a11448ada 100644 --- a/crates/kas-macros/src/widget.rs +++ b/crates/kas-macros/src/widget.rs @@ -375,6 +375,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul } let fn_nav_next; + let fn_rect; let mut fn_size_rules = None; let fn_set_rect; let mut probe = quote! { @@ -383,17 +384,25 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul let fn_try_probe; let mut fn_draw = None; if let Some(Layout { tree, .. }) = args.layout.take() { - let size_rules = tree.size_rules(&core_path); - let set_rect = tree.set_rect(&core_path); - let try_probe = tree.try_probe(&core_path); - let draw = tree.draw(&core_path); + // TODO(opt): omit field widget.core._rect if not set here + let mut set_rect = quote! {}; + let tree_rect = tree.rect(&core_path).unwrap_or_else(|| { + set_rect = quote! { + #core_path._rect = rect; + }; + quote! { #core_path._rect } + }); + let tree_size_rules = tree.size_rules(&core_path); + let tree_set_rect = tree.set_rect(&core_path); + let tree_try_probe = tree.try_probe(&core_path); + let tree_draw = tree.draw(&core_path); fn_nav_next = tree.nav_next(children.iter()); scope.generated.push(quote! { impl #impl_generics ::kas::MacroDefinedLayout for #impl_target { #[inline] fn rect(&self) -> ::kas::geom::Rect { - #core_path._rect + #tree_rect } #[inline] @@ -402,7 +411,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul sizer: ::kas::theme::SizeCx, axis: ::kas::layout::AxisInfo, ) -> ::kas::layout::SizeRules { - #size_rules + #tree_size_rules } #[inline] @@ -412,23 +421,30 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul rect: ::kas::geom::Rect, hints: ::kas::layout::AlignHints, ) { - #core_path._rect = rect; #set_rect + #tree_set_rect } #[inline] fn try_probe(&self, coord: ::kas::geom::Coord) -> Option<::kas::Id> { - #try_probe + #tree_try_probe } #[inline] fn draw(&self, mut draw: ::kas::theme::DrawCx) { draw.set_id(::kas::Tile::id(self)); - #draw + #tree_draw } } }); + fn_rect = quote! { + #[inline] + fn rect(&self) -> ::kas::geom::Rect { + ::kas::MacroDefinedLayout::rect(self) + } + }; + fn_size_rules = Some(quote! { fn size_rules( &mut self, @@ -480,6 +496,14 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul .unwrap_or_else(|| ::kas::Tile::id(self)) }; } else { + // TODO(opt): omit field widget.core._rect if a custom `fn rect` defintion is used + fn_rect = quote! { + #[inline] + fn rect(&self) -> ::kas::geom::Rect { + #core_path._rect + } + }; + fn_set_rect = quote! { fn set_rect( &mut self, @@ -623,7 +647,6 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul layout_impl.items.push(Verbatim(method)); } - let core_rect_is_set; let mut fn_set_rect_span = None; if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "set_rect") { if let ImplItem::Fn(f) = &mut layout_impl.items[*index] { @@ -631,7 +654,6 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul let path_rect = quote! { #core_path._rect }; widget_set_rect_span = crate::visitors::widget_set_rect(path_rect, &mut f.block); - core_rect_is_set = widget_set_rect_span.is_some(); if let Some(ref core) = core_data { f.block.stmts.insert(0, parse_quote! { @@ -639,15 +661,11 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul self.#core.status.set_rect(&self.#core._id); }); } - } else { - core_rect_is_set = false; } } else { layout_impl.items.push(Verbatim(fn_set_rect)); - core_rect_is_set = true; } - // TODO(opt): omit field widget.core._rect if !core_rect_is_set if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "rect") { if let Some(span) = widget_set_rect_span { let fn_rect_span = layout_impl.items[*index].span(); @@ -655,30 +673,15 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul span, "assignment `widget_set_rect!` has no effect when `fn rect` is defined"; note = fn_rect_span => "this `fn rect`"; ); - } - if core_rect_is_set { + } else if fn_set_rect_span.is_none() { let fn_rect_span = layout_impl.items[*index].span(); - if let Some(span) = widget_set_rect_span { - emit_warning!( - span, "assignment `widget_set_rect!` has no effect when `fn rect` is defined"; - note = fn_rect_span => "this `fn rect`"; - ); - } else { - emit_warning!( - fn_rect_span, - "definition of `Layout::set_rect` is expected when `fn rect` is defined" - ); - } - } - } else if core_rect_is_set { - layout_impl.items.push(Verbatim(fn_rect(&core_path))); - } else { - if let Some(span) = fn_set_rect_span { emit_warning!( - span, "`widget_set_rect!(/* rect */)` not found"; - note = "`fn Layout::rect()` implementation cannot be generated without `widget_set_rect!`"; + fn_rect_span, + "definition of `Layout::set_rect` is expected when `fn rect` is defined" ); } + } else { + layout_impl.items.push(Verbatim(fn_rect)); } if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "try_probe") { @@ -719,7 +722,6 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul layout_impl.items.push(Verbatim(method)); } } else if let Some(fn_size_rules) = fn_size_rules { - let fn_rect = fn_rect(&core_path); scope.generated.push(quote! { impl #impl_generics ::kas::Layout for #impl_target { #fn_rect @@ -815,15 +817,6 @@ pub fn required_tile_methods(name: &str, core_path: &Toks) -> Toks { } } -pub fn fn_rect(core_path: &Toks) -> Toks { - quote! { - #[inline] - fn rect(&self) -> ::kas::geom::Rect { - #core_path._rect - } - } -} - pub fn impl_widget( impl_generics: &Toks, impl_target: &Toks, diff --git a/crates/kas-widgets/src/check_box.rs b/crates/kas-widgets/src/check_box.rs index 9fd1620b2..d1cccf844 100644 --- a/crates/kas-widgets/src/check_box.rs +++ b/crates/kas-widgets/src/check_box.rs @@ -186,7 +186,6 @@ impl_scope! { impl Layout for Self { fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) { - widget_set_rect!(rect); kas::MacroDefinedLayout::set_rect(self, cx, rect, hints); let dir = self.direction(); shrink_to_text(&mut self.rect(), dir, &self.label); diff --git a/crates/kas-widgets/src/label.rs b/crates/kas-widgets/src/label.rs index 9422a0f62..67939e08a 100644 --- a/crates/kas-widgets/src/label.rs +++ b/crates/kas-widgets/src/label.rs @@ -116,7 +116,6 @@ impl_scope! { impl Layout for Self { fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) { - widget_set_rect!(rect); self.text.set_rect(cx, rect, hints.combine(AlignHints::VERT_CENTER)); } } @@ -258,7 +257,6 @@ impl_scope! { impl Layout for Self { fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) { - widget_set_rect!(rect); self.text.set_rect(cx, rect, hints.combine(AlignHints::VERT_CENTER)); } } diff --git a/crates/kas-widgets/src/radio_box.rs b/crates/kas-widgets/src/radio_box.rs index c98126512..79d7c7773 100644 --- a/crates/kas-widgets/src/radio_box.rs +++ b/crates/kas-widgets/src/radio_box.rs @@ -147,7 +147,6 @@ impl_scope! { impl Layout for Self { fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) { - widget_set_rect!(rect); kas::MacroDefinedLayout::set_rect(self, cx, rect, hints); let dir = self.direction(); crate::check_box::shrink_to_text(&mut self.rect(), dir, &self.label); diff --git a/crates/kas-widgets/src/spinner.rs b/crates/kas-widgets/src/spinner.rs index 344aee2c8..59755bb4d 100644 --- a/crates/kas-widgets/src/spinner.rs +++ b/crates/kas-widgets/src/spinner.rs @@ -276,7 +276,6 @@ impl_scope! { impl Layout for Self { fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) { - widget_set_rect!(rect); kas::MacroDefinedLayout::set_rect(self, cx, rect, hints); self.edit.set_outer_rect(rect, FrameStyle::EditBox); } diff --git a/crates/kas-widgets/src/text.rs b/crates/kas-widgets/src/text.rs index 871fef89a..ae8cb6e9d 100644 --- a/crates/kas-widgets/src/text.rs +++ b/crates/kas-widgets/src/text.rs @@ -110,7 +110,6 @@ impl_scope! { } fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) { - widget_set_rect!(rect); self.text.set_rect(cx, rect, hints.combine(AlignHints::VERT_CENTER)); } From 8486909a392e640fe0598cd7f79e7ce9875f59f9 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 18 Feb 2025 17:26:20 +0000 Subject: [PATCH 09/11] Additional error detection for macro-provided fn rect --- crates/kas-macros/src/widget.rs | 7 ++++++- crates/kas-widgets/src/text.rs | 13 +++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/crates/kas-macros/src/widget.rs b/crates/kas-macros/src/widget.rs index a11448ada..64d3b9280 100644 --- a/crates/kas-macros/src/widget.rs +++ b/crates/kas-macros/src/widget.rs @@ -626,6 +626,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul if let Some(index) = layout_impl { let layout_impl = &mut scope.impls[index]; let item_idents = collect_idents(layout_impl); + let mut fn_rect_is_provided = fn_size_rules.is_some(); if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "size_rules") { if let Some(ref core) = core_data { @@ -654,6 +655,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul let path_rect = quote! { #core_path._rect }; widget_set_rect_span = crate::visitors::widget_set_rect(path_rect, &mut f.block); + fn_rect_is_provided |= widget_set_rect_span.is_some(); if let Some(ref core) = core_data { f.block.stmts.insert(0, parse_quote! { @@ -664,6 +666,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul } } else { layout_impl.items.push(Verbatim(fn_set_rect)); + fn_rect_is_provided = true; } if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "rect") { @@ -680,8 +683,10 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul "definition of `Layout::set_rect` is expected when `fn rect` is defined" ); } - } else { + } else if fn_rect_is_provided { layout_impl.items.push(Verbatim(fn_rect)); + } else if let Some(span) = fn_set_rect_span { + emit_warning!(span, "cowardly refusing to provide an impl of `fn rect` with custom `fn set_rect` without usage of `widget_set_rect!` and without a property-defined layout"); } if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "try_probe") { diff --git a/crates/kas-widgets/src/text.rs b/crates/kas-widgets/src/text.rs index ae8cb6e9d..0e6239998 100644 --- a/crates/kas-widgets/src/text.rs +++ b/crates/kas-widgets/src/text.rs @@ -22,7 +22,9 @@ impl_scope! { /// Vertical alignment defaults to centred, horizontal alignment depends on /// the script direction if not specified. Line-wrapping is enabled by /// default. - #[widget] + #[widget { + layout = self.text; + }] pub struct Text { core: widget_core!(), text: theme::Text, @@ -104,18 +106,9 @@ impl_scope! { } impl Layout for Self { - #[inline] - fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules { - sizer.text_rules(&mut self.text, axis) - } - fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) { self.text.set_rect(cx, rect, hints.combine(AlignHints::VERT_CENTER)); } - - fn draw(&self, mut draw: DrawCx) { - draw.text(self.rect(), &self.text); - } } impl Events for Self { From 0db7a73a28bdf1641c15d756def6640b705cb6bd Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 18 Feb 2025 17:07:04 +0000 Subject: [PATCH 10/11] Remove some usages of #[inline] At a glance, these fns are non-trivial and not constructors --- crates/kas-core/src/event/cx/cx_pub.rs | 4 ---- crates/kas-core/src/geom/vector.rs | 1 - crates/kas-core/src/layout/size_rules.rs | 3 --- crates/kas-core/src/runner/shared.rs | 1 - crates/kas-core/src/theme/text.rs | 2 -- crates/kas-view/src/list_view.rs | 1 - crates/kas-view/src/matrix_view.rs | 1 - crates/kas-widgets/src/adapt/adapt_events.rs | 3 --- examples/clock.rs | 1 - examples/mandlebrot/mandlebrot.rs | 1 - 10 files changed, 18 deletions(-) diff --git a/crates/kas-core/src/event/cx/cx_pub.rs b/crates/kas-core/src/event/cx/cx_pub.rs index 99512573a..77347d8f4 100644 --- a/crates/kas-core/src/event/cx/cx_pub.rs +++ b/crates/kas-core/src/event/cx/cx_pub.rs @@ -878,7 +878,6 @@ impl<'a> EventCx<'a> { /// /// In case of failure, paste actions will simply fail. The implementation /// may wish to log an appropriate warning message. - #[inline] pub fn get_clipboard(&mut self) -> Option { #[cfg(all(wayland_platform, feature = "clipboard"))] if let Some(cb) = self.window.wayland_clipboard() { @@ -895,7 +894,6 @@ impl<'a> EventCx<'a> { } /// Attempt to set clipboard contents - #[inline] pub fn set_clipboard(&mut self, content: String) { #[cfg(all(wayland_platform, feature = "clipboard"))] if let Some(cb) = self.window.wayland_clipboard() { @@ -922,7 +920,6 @@ impl<'a> EventCx<'a> { /// /// Linux has a "primary buffer" with implicit copy on text selection and /// paste on middle-click. This method does nothing on other platforms. - #[inline] pub fn get_primary(&mut self) -> Option { #[cfg(all(wayland_platform, feature = "clipboard"))] if let Some(cb) = self.window.wayland_clipboard() { @@ -942,7 +939,6 @@ impl<'a> EventCx<'a> { /// /// Linux has a "primary buffer" with implicit copy on text selection and /// paste on middle-click. This method does nothing on other platforms. - #[inline] pub fn set_primary(&mut self, content: String) { #[cfg(all(wayland_platform, feature = "clipboard"))] if let Some(cb) = self.window.wayland_clipboard() { diff --git a/crates/kas-core/src/geom/vector.rs b/crates/kas-core/src/geom/vector.rs index 7b96eefc8..51955b6f4 100644 --- a/crates/kas-core/src/geom/vector.rs +++ b/crates/kas-core/src/geom/vector.rs @@ -94,7 +94,6 @@ impl Quad { } /// Calculate the intersection of two quads - #[inline] pub fn intersection(&self, rhs: &Quad) -> Option { let a = Vec2(self.a.0.max(rhs.a.0), self.a.1.max(rhs.a.1)); let x = (self.b.0.min(rhs.b.0) - a.0).max(0.0); diff --git a/crates/kas-core/src/layout/size_rules.rs b/crates/kas-core/src/layout/size_rules.rs index 2bc8647f3..cc4a3e7a6 100644 --- a/crates/kas-core/src/layout/size_rules.rs +++ b/crates/kas-core/src/layout/size_rules.rs @@ -224,7 +224,6 @@ impl SizeRules { } /// Use the maximum size of `self` and `rhs`. - #[inline] #[must_use = "method does not modify self but returns a new value"] pub fn max(self, rhs: Self) -> SizeRules { SizeRules { @@ -279,7 +278,6 @@ impl SizeRules { /// /// Note also that appending [`SizeRules::EMPTY`] does include interior /// margins (those between `EMPTY` and the other rules) within the result. - #[inline] #[must_use = "method does not modify self but returns a new value"] pub fn appended(self, rhs: SizeRules) -> Self { let c: i32 = self.m.1.max(rhs.m.0).into(); @@ -371,7 +369,6 @@ impl SizeRules { clippy::needless_range_loop, clippy::needless_return )] - #[inline] pub fn solve_seq_total(out: &mut [i32], rules: &[Self], total: Self, target: i32) { type Targets = SmallVec<[i32; 16]>; #[allow(non_snake_case)] diff --git a/crates/kas-core/src/runner/shared.rs b/crates/kas-core/src/runner/shared.rs index dad93c071..94faff52c 100644 --- a/crates/kas-core/src/runner/shared.rs +++ b/crates/kas-core/src/runner/shared.rs @@ -84,7 +84,6 @@ where }) } - #[inline] pub(crate) fn handle_messages(&mut self, messages: &mut MessageStack) { if messages.reset_and_has_any() { let count = messages.get_op_count(); diff --git a/crates/kas-core/src/theme/text.rs b/crates/kas-core/src/theme/text.rs index 354164013..47d89006c 100644 --- a/crates/kas-core/src/theme/text.rs +++ b/crates/kas-core/src/theme/text.rs @@ -409,7 +409,6 @@ impl Text { Ok(()) } - #[inline] fn prepare_runs(&mut self) -> Result<(), NotReady> { match self.status { Status::New => return Err(NotReady), @@ -521,7 +520,6 @@ impl Text { /// [`Action::empty()`] is returned without updating `self`. /// /// This is typically called after updating a `Text` object in a widget. - #[inline] pub fn reprepare_action(&mut self) -> Action { match self.prepare() { Err(NotReady) => Action::empty(), diff --git a/crates/kas-view/src/list_view.rs b/crates/kas-view/src/list_view.rs index dbf9d87b9..12844c5ad 100644 --- a/crates/kas-view/src/list_view.rs +++ b/crates/kas-view/src/list_view.rs @@ -406,7 +406,6 @@ impl_scope! { self.scroll.offset() } - #[inline] fn set_scroll_offset(&mut self, cx: &mut EventCx, offset: Offset) -> Offset { let act = self.scroll.set_offset(offset); cx.action(&self, act); diff --git a/crates/kas-view/src/matrix_view.rs b/crates/kas-view/src/matrix_view.rs index b046fbbbe..0ce70b3c2 100644 --- a/crates/kas-view/src/matrix_view.rs +++ b/crates/kas-view/src/matrix_view.rs @@ -350,7 +350,6 @@ impl_scope! { self.scroll.offset() } - #[inline] fn set_scroll_offset(&mut self, cx: &mut EventCx, offset: Offset) -> Offset { let action = self.scroll.set_offset(offset); cx.action(&self, action); diff --git a/crates/kas-widgets/src/adapt/adapt_events.rs b/crates/kas-widgets/src/adapt/adapt_events.rs index 44a9de719..724bc0989 100644 --- a/crates/kas-widgets/src/adapt/adapt_events.rs +++ b/crates/kas-widgets/src/adapt/adapt_events.rs @@ -140,7 +140,6 @@ kas::impl_scope! { self.inner.for_child_node(data, index, closure); } - #[inline] fn _configure(&mut self, cx: &mut ConfigCx, data: &Self::Data, id: Id) { self.inner._configure(cx, data, id); @@ -163,7 +162,6 @@ kas::impl_scope! { } } - #[inline] fn _send(&mut self, cx: &mut EventCx, data: &Self::Data, id: Id, event: Event) -> IsUsed { let is_used = self.inner._send(cx, data, id, event); @@ -177,7 +175,6 @@ kas::impl_scope! { is_used } - #[inline] fn _replay(&mut self, cx: &mut EventCx, data: &Self::Data, id: Id) { self.inner._replay(cx, data, id); diff --git a/examples/clock.rs b/examples/clock.rs index 3a6a9754f..d4cfd95af 100644 --- a/examples/clock.rs +++ b/examples/clock.rs @@ -48,7 +48,6 @@ impl_scope! { .with_stretch(Stretch::High) } - #[inline] fn set_rect(&mut self, _: &mut ConfigCx, rect: Rect, _: AlignHints) { // Force to square let size = rect.size.0.min(rect.size.1); diff --git a/examples/mandlebrot/mandlebrot.rs b/examples/mandlebrot/mandlebrot.rs index 6360a20fa..49d922d0f 100644 --- a/examples/mandlebrot/mandlebrot.rs +++ b/examples/mandlebrot/mandlebrot.rs @@ -340,7 +340,6 @@ impl_scope! { .with_stretch(Stretch::High) } - #[inline] fn set_rect(&mut self, _: &mut ConfigCx, rect: Rect, _: AlignHints) { widget_set_rect!(rect); let size = DVec2::conv(rect.size); From 9f9d92ed151621347f6488ac5342dab0a0e5b428 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 18 Feb 2025 19:12:32 +0000 Subject: [PATCH 11/11] Store rect within PixmapScaling --- crates/kas-core/src/layout/size_types.rs | 6 ++++-- crates/kas-resvg/src/canvas.rs | 8 ++++++-- crates/kas-resvg/src/svg.rs | 8 ++++++-- crates/kas-widgets/src/image.rs | 6 +++++- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/crates/kas-core/src/layout/size_types.rs b/crates/kas-core/src/layout/size_types.rs index 0f147d57e..6739b502f 100644 --- a/crates/kas-core/src/layout/size_types.rs +++ b/crates/kas-core/src/layout/size_types.rs @@ -238,6 +238,8 @@ impl_scope! { /// /// If is `None`, max size is limited to ideal size. pub stretch: Stretch, + /// The assigned [`Rect`] + pub rect: Rect, } } @@ -260,7 +262,7 @@ impl PixmapScaling { /// Constrains and aligns within `rect` /// /// The resulting size is then aligned using the `align` hints, defaulting to centered. - pub fn align_rect(&mut self, rect: Rect, align: AlignPair, scale_factor: f32) -> Rect { + pub fn set_rect(&mut self, rect: Rect, align: AlignPair, scale_factor: f32) { let mut size = rect.size; if self.stretch == Stretch::None { @@ -280,7 +282,7 @@ impl PixmapScaling { } } - align.aligned_rect(size, rect) + self.rect = align.aligned_rect(size, rect); } } diff --git a/crates/kas-resvg/src/canvas.rs b/crates/kas-resvg/src/canvas.rs index 4a9a1da47..c887bcaa4 100644 --- a/crates/kas-resvg/src/canvas.rs +++ b/crates/kas-resvg/src/canvas.rs @@ -160,6 +160,10 @@ impl_scope! { } impl Layout for Self { + fn rect(&self) -> Rect { + self.scaling.rect + } + fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules { self.scaling.size_rules(sizer, axis) } @@ -167,9 +171,9 @@ impl_scope! { fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) { let align = hints.complete_default(); let scale_factor = cx.size_cx().scale_factor(); - widget_set_rect!(self.scaling.align_rect(rect, align, scale_factor)); - let size = self.rect().size.cast(); + self.scaling.set_rect(rect, align, scale_factor); + let size = self.rect().size.cast(); if let Some(fut) = self.inner.get_mut().resize(size) { cx.push_spawn(self.id(), fut); } diff --git a/crates/kas-resvg/src/svg.rs b/crates/kas-resvg/src/svg.rs index f3936e221..466dffb0f 100644 --- a/crates/kas-resvg/src/svg.rs +++ b/crates/kas-resvg/src/svg.rs @@ -242,6 +242,10 @@ impl_scope! { } impl Layout for Self { + fn rect(&self) -> Rect { + self.scaling.rect + } + fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules { self.scaling.size_rules(sizer, axis) } @@ -249,9 +253,9 @@ impl_scope! { fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) { let align = hints.complete_default(); let scale_factor = cx.size_cx().scale_factor(); - widget_set_rect!(self.scaling.align_rect(rect, align, scale_factor)); - let size: (u32, u32) = self.rect().size.cast(); + self.scaling.set_rect(rect, align, scale_factor); + let size: (u32, u32) = self.rect().size.cast(); if let Some(fut) = self.inner.resize(size) { cx.push_spawn(self.id(), fut); } diff --git a/crates/kas-widgets/src/image.rs b/crates/kas-widgets/src/image.rs index d35a62501..c3d773e13 100644 --- a/crates/kas-widgets/src/image.rs +++ b/crates/kas-widgets/src/image.rs @@ -167,6 +167,10 @@ impl_scope! { } impl Layout for Image { + fn rect(&self) -> Rect { + self.scaling.rect + } + fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules { self.scaling.size_rules(sizer, axis) } @@ -174,7 +178,7 @@ impl_scope! { fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) { let align = hints.complete_default(); let scale_factor = cx.size_cx().scale_factor(); - widget_set_rect!(self.scaling.align_rect(rect, align, scale_factor)); + self.scaling.set_rect(rect, align, scale_factor); } fn draw(&self, mut draw: DrawCx) {