Skip to content

Commit

Permalink
Merge pull request #462 from kas-gui/work2
Browse files Browse the repository at this point in the history
Split Layout::find_id, renaming to probe, try_probe
  • Loading branch information
dhardy authored Dec 30, 2024
2 parents 592e490 + 1425dc2 commit ce1576d
Show file tree
Hide file tree
Showing 34 changed files with 225 additions and 246 deletions.
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

0 comments on commit ce1576d

Please sign in to comment.