Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split Layout::find_id, renaming to probe, try_probe #462

Merged
merged 4 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 39 additions & 33 deletions crates/kas-core/src/core/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use kas_macros::autoimpl;
/// cached by `size_rules`.
/// 4. The widget is updated again after any data change (see [`ConfigCx::update`]).
/// 5. The widget is ready for event-handling and drawing ([`Events`],
/// [`Self::find_id`], [`Self::draw`]).
/// [`Self::try_probe`], [`Self::draw`]).
///
/// Widgets are responsible for ensuring that their children may observe this
/// lifecycle. Usually this simply involves inclusion of the child in layout
Expand Down Expand Up @@ -176,7 +176,7 @@ pub trait Layout {
/// Required: [`Self::size_rules`] is called for both axes before this
/// method is called, and that this method has been called *after* the last
/// call to [`Self::size_rules`] *before* any of the following methods:
/// [`Layout::find_id`], [`Layout::draw`], [`Events::handle_event`].
/// [`Layout::try_probe`], [`Layout::draw`], [`Events::handle_event`].
///
/// Default implementation when not using the `layout` property: set `rect`
/// field of `widget_core!()` to the input `rect`.
Expand Down Expand Up @@ -216,7 +216,7 @@ pub trait Layout {
/// *and* child widgets need to implement this.
/// Such widgets must also implement [`Events::handle_scroll`].
///
/// Affects event handling via [`Layout::find_id`] and affects the positioning
/// Affects event handling via [`Layout::probe`] and affects the positioning
/// of pop-up menus. [`Layout::draw`] must be implemented directly using
/// [`DrawCx::with_clip_region`] to offset contents.
///
Expand All @@ -226,59 +226,65 @@ pub trait Layout {
Offset::ZERO
}

/// Translate a coordinate to an [`Id`]
/// Probe a coordinate for a widget's [`Id`]
///
/// This method is used to determine which widget reacts to the mouse cursor
/// or a touch event. The result affects mouse-hover highlighting, event
/// handling by the target, and potentially also event handling by other
/// widgets (e.g. a `Label` widget will not handle touch events, but if it
/// is contained by a `ScrollRegion`, that widget may capture these via
/// [`Events::handle_event`] to implement touch scrolling).
/// Returns the [`Id`] of the lowest descendant (leaf-most element of the
/// widget tree) occupying `coord` (exceptions possible; see below).
///
/// The callee may assume that it occupies `coord`.
/// Callers should prefer to call [`Tile::try_probe`] instead.
///
/// This method is used to determine which widget reacts to the mouse and
/// touch events at the given coordinates. The widget identified by this
/// method may be highlighted (if hovered by the mouse) and may respond to
/// click/touch events. Unhandled click/touch events are passed to the
/// parent widget and so on up the widget tree.
///
/// The result is usually the widget which draws at the given `coord`, but
/// does not have to be. For example, a `Button` widget will return its own
/// `id` for coordinates drawn by internal content, while the `CheckButton`
/// widget uses an internal component for event handling and thus reports
/// this component's `id` even over its own area.
///
/// ### Call order
///
/// It is expected that [`Layout::set_rect`] is called before this method,
/// but failure to do so should not cause a fatal error.
///
/// The default implementation suffices for widgets without children as well
/// as widgets using the `layout` property of [`#[widget]`](crate::widget).
/// Custom implementations may be required if:
///
/// - A custom [`Layout`] implementation is used
/// - Event stealing or donation is desired (but note that
/// `layout = button: ..;` does this already)
/// ### Default implementation
///
/// When writing a custom implementation:
///
/// - Widgets should test `self.rect().contains(coord)`, returning `None`
/// if this test is `false`; otherwise, they should always return *some*
/// [`Id`], either a childs or their own.
/// - If the Widget uses a translated coordinate space (i.e.
/// `self.translation() != Offset::ZERO`) then pass
/// `coord + self.translation()` to children.
///
/// The default implementation is non-trivial:
/// The default macro-generated implementation considers all children of the
/// `layout` property and of [`#[widget]`](crate::widget) fields:
/// ```ignore
/// if !self.rect().contains(coord) {
/// return None;
/// }
/// let coord = coord + self.translation();
/// for child in ITER_OVER_CHILDREN {
/// if let Some(id) = child.find_id(coord) {
/// if let Some(id) = child.try_probe(coord) {
/// return Some(id);
/// }
/// }
/// Some(self.id())
/// self.id()
/// ```
fn find_id(&mut self, coord: Coord) -> Option<Id> {
fn probe(&mut self, coord: Coord) -> Id {
let _ = coord;
unimplemented!() // make rustdoc show that this is a provided method
}

/// Probe a coordinate for a widget's [`Id`]
///
/// Returns the [`Id`] of the lowest descendant (leaf-most element of the
/// widget tree) occupying `coord`, if any.
///
/// This method returns `None` if `!self.rect().contains(coord)`, otherwise
/// returning the result of [`Layout::probe`].
///
/// ### Call order
///
/// It is expected that [`Tile::set_rect`] is called before this method,
/// but failure to do so should not cause a fatal error.
fn try_probe(&mut self, coord: Coord) -> Option<Id> {
self.rect().contains(coord).then(|| self.probe(coord))
}

/// Draw a widget and its children
///
/// This method is invoked each frame to draw visible widgets. It should
Expand Down
10 changes: 5 additions & 5 deletions crates/kas-core/src/core/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ 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 find_id(&mut self, coord: Coord) -> Option<Id>;
fn try_probe(&mut self, coord: Coord) -> Option<Id>;
fn _draw(&mut self, draw: DrawCx);

fn _configure(&mut self, cx: &mut ConfigCx, id: Id);
Expand Down Expand Up @@ -80,8 +80,8 @@ 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 find_id(&mut self, coord: Coord) -> Option<Id> {
self.0.find_id(coord)
fn try_probe(&mut self, coord: Coord) -> Option<Id> {
self.0.try_probe(coord)
}
fn _draw(&mut self, mut draw: DrawCx) {
draw.recurse(&mut self.0);
Expand Down Expand Up @@ -303,8 +303,8 @@ impl<'a> Node<'a> {
}

/// Translate a coordinate to an [`Id`]
pub(crate) fn find_id(&mut self, coord: Coord) -> Option<Id> {
self.0.find_id(coord)
pub(crate) fn try_probe(&mut self, coord: Coord) -> Option<Id> {
self.0.try_probe(coord)
}

cfg_if::cfg_if! {
Expand Down
6 changes: 3 additions & 3 deletions crates/kas-core/src/core/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use kas_macros::autoimpl;
/// cached by `size_rules`.
/// 4. The widget is updated again after any data change (see [`ConfigCx::update`]).
/// 5. The widget is ready for event-handling and drawing
/// ([`Events::handle_event`], [`Layout::find_id`], [`Layout::draw`]).
/// ([`Events::handle_event`], [`Layout::try_probe`], [`Layout::draw`]).
///
/// Widgets are responsible for ensuring that their children may observe this
/// lifecycle. Usually this simply involves inclusion of the child in layout
Expand Down Expand Up @@ -256,7 +256,7 @@ pub enum NavAdvance {
/// cached by `size_rules`.
/// 4. The widget is updated again after any data change (see [`ConfigCx::update`]).
/// 5. The widget is ready for event-handling and drawing
/// ([`Events::handle_event`], [`Layout::find_id`], [`Layout::draw`]).
/// ([`Events::handle_event`], [`Layout::try_probe`], [`Layout::draw`]).
///
/// Widgets are responsible for ensuring that their children may observe this
/// lifecycle. Usually this simply involves inclusion of the child in layout
Expand Down Expand Up @@ -310,7 +310,7 @@ pub enum NavAdvance {
/// - **Layout** is specified either via [layout syntax](macros::widget#layout-1)
/// or via implementation of at least [`Layout::size_rules`] and
/// [`Layout::draw`] (optionally also `set_rect`, `nav_next`, `translation`
/// and `find_id`).
/// and `probe`).
///- **Event handling** is optional, implemented through [`Events`].
///
/// For examples, check the source code of widgets in the widgets library
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/event/cx/cx_pub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ impl<'a> EventCx<'a> {
///
/// When calling this method, be aware that some widgets use an inner
/// component to handle events, thus calling with the outer widget's `id`
/// may not have the desired effect. [`Layout::find_id`] and
/// may not have the desired effect. [`Layout::try_probe`] and
/// [`EventState::next_nav_focus`] are usually able to find the appropriate
/// event-handling target.
pub fn send_command(&mut self, id: Id, cmd: Command) {
Expand Down
10 changes: 5 additions & 5 deletions crates/kas-core/src/event/cx/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,11 @@ impl EventState {
cx.action.remove(Action::REGION_MOVED);

// Update hovered widget
let hover = win.find_id(data, cx.last_mouse_coord);
let hover = win.try_probe(data, cx.last_mouse_coord);
cx.set_hover(win.as_node(data), hover);

for grab in cx.touch_grab.iter_mut() {
grab.cur_id = win.find_id(data, grab.coord);
grab.cur_id = win.try_probe(data, grab.coord);
}
}
});
Expand Down Expand Up @@ -408,7 +408,7 @@ impl<'a> EventCx<'a> {
let coord = position.cast_approx();

// Update hovered win
let id = win.find_id(data, coord);
let id = win.try_probe(data, coord);
self.set_hover(win.as_node(data), id.clone());

if let Some(grab) = self.state.mouse_grab.as_mut() {
Expand Down Expand Up @@ -527,7 +527,7 @@ impl<'a> EventCx<'a> {
let coord = touch.location.cast_approx();
match touch.phase {
TouchPhase::Started => {
let start_id = win.find_id(data, coord);
let start_id = win.try_probe(data, coord);
if let Some(id) = start_id.as_ref() {
if self.config.event().touch_nav_focus() {
if let Some(id) =
Expand All @@ -547,7 +547,7 @@ impl<'a> EventCx<'a> {
}
}
TouchPhase::Moved => {
let cur_id = win.find_id(data, coord);
let cur_id = win.try_probe(data, coord);

let mut redraw = false;
let mut pan_grab = None;
Expand Down
4 changes: 2 additions & 2 deletions crates/kas-core/src/event/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ pub enum Event {
/// Motion events for the grabbed mouse pointer or touched finger are sent.
///
/// If `cur_id` is `None`, no widget was found at the coordinate (either
/// outside the window or [`crate::Layout::find_id`] failed).
/// outside the window or [`crate::Layout::try_probe`] failed).
PressMove { press: Press, delta: Offset },
/// End of a click/touch press
///
Expand All @@ -168,7 +168,7 @@ pub enum Event {
/// sent.
///
/// If `cur_id` is `None`, no widget was found at the coordinate (either
/// outside the window or [`crate::Layout::find_id`] failed).
/// outside the window or [`crate::Layout::try_probe`] failed).
PressEnd { press: Press, success: bool },
/// Update from a timer
///
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
//!
//! 1. Determine the target's [`Id`]. For example, this may be
//! the [`nav_focus`](EventState::nav_focus) or may be determined from
//! from mouse/touch coordinates by calling [`find_id`](crate::Layout::find_id).
//! from mouse/touch coordinates by calling [`try_probe`](crate::Layout::try_probe).
//! 2. If the target is [disabled](EventState::is_disabled), then find the
//! top-most ancestor which is disabled and make that the target, but
//! inhibit calling of [`Events::handle_event`] on this widget (but still
Expand Down
Loading
Loading