Skip to content

Commit

Permalink
Merge pull request #480 from kas-gui/work3
Browse files Browse the repository at this point in the history
Non-mut Layout::{rect, try_probe, draw}
  • Loading branch information
dhardy authored Feb 19, 2025
2 parents 681e845 + 9f9d92e commit 5aaecae
Show file tree
Hide file tree
Showing 68 changed files with 396 additions and 378 deletions.
6 changes: 3 additions & 3 deletions crates/kas-core/src/core/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -25,14 +25,14 @@ pub fn _send<W: Events>(
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;
}
_ => (),
}
Expand Down
19 changes: 15 additions & 4 deletions crates/kas-core/src/core/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ use kas_macros::autoimpl;
/// [`#widget`]: macros::widget
#[autoimpl(for<T: trait + ?Sized> &'_ mut T, Box<T>)]
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
Expand Down Expand Up @@ -156,7 +164,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<Id> {
fn try_probe(&self, coord: Coord) -> Option<Id> {
let _ = coord;
None
}
Expand Down Expand Up @@ -189,7 +197,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
Expand All @@ -201,15 +209,18 @@ 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;

/// Set size and position
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<Id>;
fn try_probe(&self, coord: Coord) -> Option<Id>;

/// Draw a widget and its children
fn draw(&mut self, draw: DrawCx);
fn draw(&self, draw: DrawCx);
}
31 changes: 2 additions & 29 deletions crates/kas-core/src/core/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
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::{DrawCx, SizeCx};
use crate::theme::SizeCx;
use crate::{Id, NavAdvance, Tile};

#[cfg(not(feature = "unsafe_node"))]
Expand All @@ -28,8 +28,6 @@ trait NodeT {
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints);

fn nav_next(&self, reverse: bool, from: Option<usize>) -> Option<usize>;
fn try_probe(&mut self, coord: Coord) -> Option<Id>;
fn _draw(&mut self, draw: DrawCx);

fn _configure(&mut self, cx: &mut ConfigCx, id: Id);
fn _update(&mut self, cx: &mut ConfigCx);
Expand Down Expand Up @@ -80,12 +78,6 @@ impl<'a, T> NodeT for (&'a mut dyn Widget<Data = T>, &'a T) {
fn nav_next(&self, reverse: bool, from: Option<usize>) -> Option<usize> {
self.0.nav_next(reverse, from)
}
fn try_probe(&mut self, coord: Coord) -> Option<Id> {
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);
Expand Down Expand Up @@ -302,25 +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<Id> {
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! {
Expand Down
12 changes: 3 additions & 9 deletions crates/kas-core/src/core/tile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -197,7 +191,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,
{
Expand Down
10 changes: 5 additions & 5 deletions crates/kas-core/src/core/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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`]
Expand Down
5 changes: 2 additions & 3 deletions crates/kas-core/src/decorations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl_scope! {
SizeRules::EMPTY
}

fn draw(&mut self, _: DrawCx) {}
fn draw(&self, _: DrawCx) {}
}

impl Events for Self {
Expand Down Expand Up @@ -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));
}
}
Expand Down Expand Up @@ -178,7 +177,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);
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/draw/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use std::time::Instant;
/// # _pd: std::marker::PhantomData<DS>,
/// # }
/// 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<kas_wgpu::draw::DrawPipe<()>>;
/// if let Some(mut draw) = DrawIface::downcast_from(draw.draw_device()) {
Expand Down
8 changes: 4 additions & 4 deletions crates/kas-core/src/event/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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
}
Expand Down
32 changes: 9 additions & 23 deletions crates/kas-core/src/event/cx/cx_pub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -200,34 +189,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
Expand Down Expand Up @@ -888,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<String> {
#[cfg(all(wayland_platform, feature = "clipboard"))]
if let Some(cb) = self.window.wayland_clipboard() {
Expand All @@ -905,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() {
Expand All @@ -932,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<String> {
#[cfg(all(wayland_platform, feature = "clipboard"))]
if let Some(cb) = self.window.wayland_clipboard() {
Expand All @@ -952,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() {
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/event/cx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ pub struct EventState {
// For each: (WindowId of popup, popup descriptor, old nav focus)
popups: SmallVec<[(WindowId, crate::PopupDescriptor, Option<Id>); 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
Expand Down
Loading

0 comments on commit 5aaecae

Please sign in to comment.