diff --git a/crates/kas-core/src/core/layout.rs b/crates/kas-core/src/core/layout.rs
index 5f2e6f257..2b440b7f9 100644
--- a/crates/kas-core/src/core/layout.rs
+++ b/crates/kas-core/src/core/layout.rs
@@ -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
@@ -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`.
@@ -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.
     ///
@@ -226,14 +226,19 @@ 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
@@ -241,47 +246,43 @@ pub trait Layout {
     /// 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 may assume that `self.rect().contains(coord)`.
-    /// -   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
     /// 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);
     ///     }
     /// }
     /// self.id()
     /// ```
-    fn l_find_id(&mut self, coord: Coord) -> Id {
+    fn probe(&mut self, coord: Coord) -> Id {
         let _ = coord;
         unimplemented!() // make rustdoc show that this is a provided method
     }
 
-    /// Translate a coordinate to an [`Id`]
+    /// 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`].
     ///
-    /// This tests whether `self.rect().contains(coord)`, then calls [`Layout::l_find_id`].
+    /// ### 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 find_id(&mut self, coord: Coord) -> Option<Id> {
-        self.rect().contains(coord).then(|| self.l_find_id(coord))
+    fn try_probe(&mut self, coord: Coord) -> Option<Id> {
+        self.rect().contains(coord).then(|| self.probe(coord))
     }
 
     /// Draw a widget and its children
diff --git a/crates/kas-core/src/core/node.rs b/crates/kas-core/src/core/node.rs
index 0019fb2fc..b3f4c288c 100644
--- a/crates/kas-core/src/core/node.rs
+++ b/crates/kas-core/src/core/node.rs
@@ -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);
@@ -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);
@@ -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! {
diff --git a/crates/kas-core/src/core/widget.rs b/crates/kas-core/src/core/widget.rs
index 3cde1570a..92982ef4a 100644
--- a/crates/kas-core/src/core/widget.rs
+++ b/crates/kas-core/src/core/widget.rs
@@ -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
@@ -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
@@ -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 `l_find_id`).
+///     and `probe`).
 ///-    **Event handling** is optional, implemented through [`Events`].
 ///
 /// For examples, check the source code of widgets in the widgets library
diff --git a/crates/kas-core/src/event/cx/cx_pub.rs b/crates/kas-core/src/event/cx/cx_pub.rs
index 5a355748e..d36e05655 100644
--- a/crates/kas-core/src/event/cx/cx_pub.rs
+++ b/crates/kas-core/src/event/cx/cx_pub.rs
@@ -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) {
diff --git a/crates/kas-core/src/event/cx/platform.rs b/crates/kas-core/src/event/cx/platform.rs
index fd2bcb12c..8ce22141a 100644
--- a/crates/kas-core/src/event/cx/platform.rs
+++ b/crates/kas-core/src/event/cx/platform.rs
@@ -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);
                 }
             }
         });
@@ -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() {
@@ -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) =
@@ -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;
diff --git a/crates/kas-core/src/event/events.rs b/crates/kas-core/src/event/events.rs
index 611ff0e60..3c8af3c96 100644
--- a/crates/kas-core/src/event/events.rs
+++ b/crates/kas-core/src/event/events.rs
@@ -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
     ///
@@ -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
     ///
diff --git a/crates/kas-core/src/event/mod.rs b/crates/kas-core/src/event/mod.rs
index 5ec4f7f74..c869bf5f9 100644
--- a/crates/kas-core/src/event/mod.rs
+++ b/crates/kas-core/src/event/mod.rs
@@ -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
diff --git a/crates/kas-core/src/layout/visitor.rs b/crates/kas-core/src/layout/visitor.rs
index 753ea9582..661d67cc8 100644
--- a/crates/kas-core/src/layout/visitor.rs
+++ b/crates/kas-core/src/layout/visitor.rs
@@ -41,7 +41,7 @@ pub trait Visitable {
     /// Returns the [`Id`] of a child when some child occupies `coord`. Returns
     /// [`None`] when there is no (probable) child widget at `coord`, in which
     /// case the caller may use its own [`Id`].
-    fn find_id(&mut self, coord: Coord) -> Option<Id>;
+    fn try_probe(&mut self, coord: Coord) -> Option<Id>;
 
     /// Draw a widget and its children
     ///
@@ -225,14 +225,14 @@ impl<V: Visitable> Visitor<V> {
     ///
     /// 1.  Return `None` if `!self.rect().contains(coord)`
     /// 2.  Translate `coord`: `let coord = coord + self.translation();`
-    /// 3.  Call `find_id` (this method), returning its result if not `None`
+    /// 3.  Call `try_probe` (this method), returning its result if not `None`
     /// 4.  Otherwise return `Some(self.id())`
     #[inline]
-    pub fn find_id(mut self, coord: Coord) -> Option<Id> {
-        self.find_id_(coord)
+    pub fn try_probe(mut self, coord: Coord) -> Option<Id> {
+        self.try_probe_(coord)
     }
-    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)
     }
 
     /// Draw a widget and its children
@@ -256,8 +256,8 @@ impl<V: Visitable> Visitable for Visitor<V> {
         self.set_rect_(cx, rect, hints);
     }
 
-    fn find_id(&mut self, coord: Coord) -> Option<Id> {
-        self.find_id_(coord)
+    fn try_probe(&mut self, coord: Coord) -> Option<Id> {
+        self.try_probe_(coord)
     }
 
     fn draw(&mut self, draw: DrawCx) {
@@ -278,8 +278,8 @@ impl<'a> Visitable for Single<'a> {
         self.widget.set_rect(cx, rect, hints);
     }
 
-    fn find_id(&mut self, coord: Coord) -> Option<Id> {
-        self.widget.find_id(coord)
+    fn try_probe(&mut self, coord: Coord) -> Option<Id> {
+        self.widget.try_probe(coord)
     }
 
     fn draw(&mut self, mut draw: DrawCx) {
@@ -302,8 +302,8 @@ impl<C: Visitable> Visitable for Align<C> {
         self.child.set_rect(cx, rect, hints);
     }
 
-    fn find_id(&mut self, coord: Coord) -> Option<Id> {
-        self.child.find_id(coord)
+    fn try_probe(&mut self, coord: Coord) -> Option<Id> {
+        self.child.try_probe(coord)
     }
 
     fn draw(&mut self, draw: DrawCx) {
@@ -333,8 +333,8 @@ impl<'a, C: Visitable> Visitable for Pack<'a, C> {
         self.child.set_rect(cx, rect, hints);
     }
 
-    fn find_id(&mut self, coord: Coord) -> Option<Id> {
-        self.child.find_id(coord)
+    fn try_probe(&mut self, coord: Coord) -> Option<Id> {
+        self.child.try_probe(coord)
     }
 
     fn draw(&mut self, draw: DrawCx) {
@@ -369,8 +369,8 @@ impl<C: Visitable> Visitable for Margins<C> {
         self.child.set_rect(cx, rect, hints);
     }
 
-    fn find_id(&mut self, coord: Coord) -> Option<Id> {
-        self.child.find_id(coord)
+    fn try_probe(&mut self, coord: Coord) -> Option<Id> {
+        self.child.try_probe(coord)
     }
 
     fn draw(&mut self, draw: DrawCx) {
@@ -402,8 +402,8 @@ impl<'a, C: Visitable> Visitable for Frame<'a, C> {
         self.child.set_rect(cx, child_rect, hints);
     }
 
-    fn find_id(&mut self, coord: Coord) -> Option<Id> {
-        self.child.find_id(coord)
+    fn try_probe(&mut self, coord: Coord) -> Option<Id> {
+        self.child.try_probe(coord)
     }
 
     fn draw(&mut self, mut draw: DrawCx) {
@@ -435,7 +435,7 @@ impl<'a, C: Visitable> Visitable for Button<'a, C> {
         self.child.set_rect(cx, child_rect, AlignHints::CENTER);
     }
 
-    fn find_id(&mut self, _: Coord) -> Option<Id> {
+    fn try_probe(&mut self, _: Coord) -> Option<Id> {
         // Buttons steal clicks, hence Button never returns ID of content
         None
     }
@@ -483,11 +483,11 @@ where
         }
     }
 
-    fn find_id(&mut self, coord: Coord) -> Option<Id> {
+    fn try_probe(&mut self, coord: Coord) -> Option<Id> {
         // TODO(opt): more efficient search strategy?
         for i in 0..self.children.len() {
             if let Some(child) = self.children.get_item(i) {
-                if let Some(id) = child.find_id(coord) {
+                if let Some(id) = child.try_probe(coord) {
                     return Some(id);
                 }
             }
@@ -531,10 +531,10 @@ where
         }
     }
 
-    fn find_id(&mut self, coord: Coord) -> Option<Id> {
+    fn try_probe(&mut self, coord: Coord) -> Option<Id> {
         for i in 0..self.children.len() {
             if let Some(child) = self.children.get_item(i) {
-                if let Some(id) = child.find_id(coord) {
+                if let Some(id) = child.try_probe(coord) {
                     return Some(id);
                 }
             }
@@ -587,11 +587,11 @@ where
         }
     }
 
-    fn find_id(&mut self, coord: Coord) -> Option<Id> {
+    fn try_probe(&mut self, coord: Coord) -> Option<Id> {
         // TODO(opt): more efficient search strategy?
         for i in 0..self.children.len() {
             if let Some(child) = self.children.get_item(i) {
-                if let Some(id) = child.find_id(coord) {
+                if let Some(id) = child.try_probe(coord) {
                     return Some(id);
                 }
             }
diff --git a/crates/kas-core/src/root.rs b/crates/kas-core/src/root.rs
index 293db07d0..d8c73ef6d 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 l_find_id(&mut self, _: Coord) -> Id {
+        fn probe(&mut self, _: Coord) -> Id {
             unimplemented!()
         }
 
@@ -156,29 +156,29 @@ impl_scope! {
     }
 
     impl Self {
-        pub(crate) fn find_id(&mut self, data: &Data, coord: Coord) -> Option<Id> {
+        pub(crate) fn try_probe(&mut self, data: &Data, coord: Coord) -> Option<Id> {
             if !self.core.rect.contains(coord) {
                 return None;
             }
             for (_, popup, translation) in self.popups.iter_mut().rev() {
-                if let Some(Some(id)) = self.inner.as_node(data).find_node(&popup.id, |mut node| node.find_id(coord + *translation)) {
+                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 self.bar_h > 0 {
-                if let Some(id) = self.title_bar.find_id(coord) {
+                if let Some(id) = self.title_bar.try_probe(coord) {
                     return Some(id);
                 }
             }
-            self.inner.find_id(coord)
-                .or_else(|| self.b_w.find_id(coord))
-                .or_else(|| self.b_e.find_id(coord))
-                .or_else(|| self.b_n.find_id(coord))
-                .or_else(|| self.b_s.find_id(coord))
-                .or_else(|| self.b_nw.find_id(coord))
-                .or_else(|| self.b_ne.find_id(coord))
-                .or_else(|| self.b_sw.find_id(coord))
-                .or_else(|| self.b_se.find_id(coord))
+            self.inner.try_probe(coord)
+                .or_else(|| self.b_w.try_probe(coord))
+                .or_else(|| self.b_e.try_probe(coord))
+                .or_else(|| self.b_n.try_probe(coord))
+                .or_else(|| self.b_s.try_probe(coord))
+                .or_else(|| self.b_nw.try_probe(coord))
+                .or_else(|| self.b_ne.try_probe(coord))
+                .or_else(|| self.b_sw.try_probe(coord))
+                .or_else(|| self.b_se.try_probe(coord))
                 .or_else(|| Some(self.id()))
         }
 
diff --git a/crates/kas-macros/src/make_layout.rs b/crates/kas-macros/src/make_layout.rs
index 72bcc579d..072fcec98 100644
--- a/crates/kas-macros/src/make_layout.rs
+++ b/crates/kas-macros/src/make_layout.rs
@@ -195,14 +195,14 @@ impl Tree {
                     ::kas::layout::LayoutVisitor::layout_visitor(self).set_rect(cx, rect, hints);
                 }
 
-                fn l_find_id(&mut self, coord: ::kas::geom::Coord) -> ::kas::Id {
+                fn probe(&mut self, coord: ::kas::geom::Coord) -> ::kas::Id {
                     use ::kas::{Layout, LayoutExt, layout::LayoutVisitor};
                     #[cfg(debug_assertions)]
                     #core_path.status.require_rect(&#core_path.id);
 
                     let coord = coord + self.translation();
                     self.layout_visitor()
-                        .find_id(coord)
+                        .try_probe(coord)
                         .unwrap_or_else(|| self.id())
                 }
 
diff --git a/crates/kas-macros/src/widget.rs b/crates/kas-macros/src/widget.rs
index 8299bdf05..6708151d4 100644
--- a/crates/kas-macros/src/widget.rs
+++ b/crates/kas-macros/src/widget.rs
@@ -383,7 +383,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul
     let mut fn_nav_next_err = None;
     let mut fn_size_rules = None;
     let mut set_rect = quote! { self.#core.rect = rect; };
-    let mut l_find_id = quote! {
+    let mut probe = quote! {
         use ::kas::{Layout, LayoutExt};
             self.id()
     };
@@ -422,12 +422,12 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul
             #core_path.rect = rect;
             ::kas::layout::LayoutVisitor::layout_visitor(self).set_rect(cx, rect, hints);
         };
-        l_find_id = quote! {
+        probe = quote! {
             use ::kas::{Layout, LayoutExt, layout::LayoutVisitor};
 
             let coord = coord + self.translation();
             self.layout_visitor()
-                .find_id(coord)
+                .try_probe(coord)
                     .unwrap_or_else(|| self.id())
         };
         fn_draw = Some(quote! {
@@ -457,12 +457,12 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul
             #set_rect
         }
     };
-    let fn_l_find_id = quote! {
-        fn l_find_id(&mut self, coord: ::kas::geom::Coord) -> ::kas::Id {
+    let fn_probe = quote! {
+        fn probe(&mut self, coord: ::kas::geom::Coord) -> ::kas::Id {
             #[cfg(debug_assertions)]
             #core_path.status.require_rect(&#core_path.id);
 
-            #l_find_id
+            #probe
         }
     };
 
@@ -598,7 +598,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul
             }
         }
 
-        if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "l_find_id") {
+        if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "probe") {
             if let Some(ref core) = core_data {
                 if let ImplItem::Fn(f) = &mut layout_impl.items[*index] {
                     f.block.stmts.insert(0, parse_quote! {
@@ -608,14 +608,14 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul
                 }
             }
         } else {
-            layout_impl.items.push(Verbatim(fn_l_find_id));
+            layout_impl.items.push(Verbatim(fn_probe));
         }
 
-        if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "find_id") {
+        if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "try_probe") {
             if let ImplItem::Fn(f) = &mut layout_impl.items[*index] {
                 emit_warning!(
                     f,
-                    "Implementations are expected to impl `fn l_find_id`, not `find_id`"
+                    "Implementations are expected to impl `fn probe`, not `try_probe`"
                 );
             }
         }
@@ -645,7 +645,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul
                 #fn_size_rules
                 #fn_set_rect
                 #fn_nav_next
-                #fn_l_find_id
+                #fn_probe
                 #fn_draw
             }
         });
diff --git a/crates/kas-macros/src/widget_derive.rs b/crates/kas-macros/src/widget_derive.rs
index 8cf339238..55b43cb5c 100644
--- a/crates/kas-macros/src/widget_derive.rs
+++ b/crates/kas-macros/src/widget_derive.rs
@@ -161,10 +161,10 @@ pub fn widget(_attr_span: Span, args: WidgetArgs, scope: &mut Scope) -> Result<(
             self.#inner.translation()
         }
     };
-    let fn_find_id_forward = quote! {
+    let fn_try_probe_forward = quote! {
         #[inline]
-        fn find_id(&mut self, coord: ::kas::geom::Coord) -> Option<::kas::Id> {
-            self.#inner.find_id(coord)
+        fn try_probe(&mut self, coord: ::kas::geom::Coord) -> Option<::kas::Id> {
+            self.#inner.try_probe(coord)
         }
     };
     let fn_draw = quote! {
@@ -280,18 +280,18 @@ pub fn widget(_attr_span: Span, args: WidgetArgs, scope: &mut Scope) -> Result<(
             layout_impl.items.push(Verbatim(fn_translation));
         }
 
-        if has_item("l_find_id") {
-            // Use default Layout::find_id impl
+        if has_item("probe") {
+            // Use default Layout::try_probe impl
         } else {
-            // Use default Layout::l_find_id (unimplemented)
-            layout_impl.items.push(Verbatim(fn_find_id_forward));
+            // Use default Layout::probe (unimplemented)
+            layout_impl.items.push(Verbatim(fn_try_probe_forward));
         }
 
-        if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "find_id") {
+        if let Some((index, _)) = item_idents.iter().find(|(_, ident)| *ident == "try_probe") {
             if let syn::ImplItem::Fn(f) = &mut layout_impl.items[*index] {
                 emit_warning!(
                     f,
-                    "Implementations are expected to impl `fn l_find_id`, not `find_id`"
+                    "Implementations are expected to impl `fn probe`, not `try_probe`"
                 );
             }
         }
@@ -307,7 +307,7 @@ pub fn widget(_attr_span: Span, args: WidgetArgs, scope: &mut Scope) -> Result<(
                 #fn_set_rect
                 #fn_nav_next
                 #fn_translation
-                #fn_find_id_forward
+                #fn_try_probe_forward
                 #fn_draw
             }
         });
diff --git a/crates/kas-view/src/list_view.rs b/crates/kas-view/src/list_view.rs
index ad2d9a5e1..e3739747e 100644
--- a/crates/kas-view/src/list_view.rs
+++ b/crates/kas-view/src/list_view.rs
@@ -553,11 +553,11 @@ impl_scope! {
             self.scroll_offset()
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
+        fn probe(&mut self, coord: Coord) -> Id {
             let coord = coord + self.scroll.offset();
             for child in &mut self.widgets[..self.cur_len.cast()] {
                 if child.key.is_some() {
-                    if let Some(id) = child.widget.find_id(coord) {
+                    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 32e27ac28..3c0b08e42 100644
--- a/crates/kas-view/src/matrix_view.rs
+++ b/crates/kas-view/src/matrix_view.rs
@@ -487,12 +487,12 @@ impl_scope! {
             self.scroll_offset()
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
+        fn probe(&mut self, coord: Coord) -> Id {
             let num = self.num_children();
             let coord = coord + self.scroll.offset();
             for child in &mut self.widgets[..num] {
                 if child.key.is_some() {
-                    if let Some(id) = child.widget.find_id(coord) {
+                    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 c8f1c352f..436db984d 100644
--- a/crates/kas-widgets/src/adapt/with_label.rs
+++ b/crates/kas-widgets/src/adapt/with_label.rs
@@ -113,7 +113,7 @@ impl_scope! {
     }
 
     impl Layout for Self {
-        fn l_find_id(&mut self, _: Coord) -> Id {
+        fn probe(&mut self, _: Coord) -> Id {
             self.inner.id()
         }
     }
diff --git a/crates/kas-widgets/src/check_box.rs b/crates/kas-widgets/src/check_box.rs
index 69e4bb5a2..a9465a524 100644
--- a/crates/kas-widgets/src/check_box.rs
+++ b/crates/kas-widgets/src/check_box.rs
@@ -192,7 +192,7 @@ impl_scope! {
             shrink_to_text(&mut self.core.rect, dir, &self.label);
         }
 
-        fn l_find_id(&mut self, _: Coord) -> Id {
+        fn probe(&mut self, _: Coord) -> Id {
             self.inner.id()
         }
     }
diff --git a/crates/kas-widgets/src/edit.rs b/crates/kas-widgets/src/edit.rs
index f12c9d484..810b04491 100644
--- a/crates/kas-widgets/src/edit.rs
+++ b/crates/kas-widgets/src/edit.rs
@@ -396,9 +396,9 @@ impl_scope! {
             self.update_scroll_bar(cx);
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
+        fn probe(&mut self, coord: Coord) -> Id {
             if self.max_scroll_offset().1 > 0 {
-                if let Some(id) = self.bar.find_id(coord) {
+                if let Some(id) = self.bar.try_probe(coord) {
                     return id;
                 }
             }
@@ -688,7 +688,7 @@ impl_scope! {
             self.view_offset = self.view_offset.min(self.max_scroll_offset());
         }
 
-        fn l_find_id(&mut self, _: Coord) -> Id {
+        fn probe(&mut self, _: Coord) -> Id {
             self.id()
         }
 
diff --git a/crates/kas-widgets/src/grid.rs b/crates/kas-widgets/src/grid.rs
index 7252daa5f..ec2452dbf 100644
--- a/crates/kas-widgets/src/grid.rs
+++ b/crates/kas-widgets/src/grid.rs
@@ -100,10 +100,10 @@ impl_scope! {
             }
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
+        fn probe(&mut self, coord: Coord) -> Id {
             for n in 0..self.widgets.len() {
                 if let Some(child) = self.widgets.get_mut_layout(n) {
-                    if let Some(id) = child.find_id(coord) {
+                    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 5644f47cf..50e1213f9 100644
--- a/crates/kas-widgets/src/list.rs
+++ b/crates/kas-widgets/src/list.rs
@@ -119,11 +119,11 @@ impl_scope! {
             }
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
+        fn probe(&mut self, coord: Coord) -> Id {
             let solver = RowPositionSolver::new(self.direction);
             solver
                 .find_child_mut(&mut self.widgets, coord)
-                .and_then(|child| child.find_id(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 5590c86c1..14f458cda 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 l_find_id(&mut self, _: Coord) -> Id {
+        fn probe(&mut self, _: Coord) -> Id {
             self.id()
         }
 
@@ -120,7 +120,7 @@ impl_scope! {
     }
 
     impl Layout for Self {
-        fn l_find_id(&mut self, _: Coord) -> Id {
+        fn probe(&mut 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 617f2fdf6..047b47a23 100644
--- a/crates/kas-widgets/src/menu/menubar.rs
+++ b/crates/kas-widgets/src/menu/menubar.rs
@@ -99,11 +99,11 @@ impl_scope! {
             }
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
+        fn probe(&mut self, coord: Coord) -> Id {
             let solver = RowPositionSolver::new(self.direction);
             solver
                 .find_child_mut(&mut self.widgets, coord)
-                .and_then(|child| child.find_id(coord))
+                .and_then(|child| child.try_probe(coord))
                 .unwrap_or_else(|| self.id())
         }
 
diff --git a/crates/kas-widgets/src/menu/mod.rs b/crates/kas-widgets/src/menu/mod.rs
index edfd89883..17509c2c9 100644
--- a/crates/kas-widgets/src/menu/mod.rs
+++ b/crates/kas-widgets/src/menu/mod.rs
@@ -48,7 +48,7 @@ pub struct SubItems<'a> {
 /// Trait governing menus, sub-menus and menu-entries
 ///
 /// Implementations will automatically receive nav focus on mouse-hover, thus
-/// should ensure that [`Layout::l_find_id`] returns the identifier of the widget
+/// should ensure that [`Layout::probe`] returns the identifier of the widget
 /// which should be focussed, and that this widget has
 /// [`Events::navigable`] return true.
 #[autoimpl(for<T: trait + ?Sized> Box<T>)]
diff --git a/crates/kas-widgets/src/menu/submenu.rs b/crates/kas-widgets/src/menu/submenu.rs
index c087d9338..66b078c90 100644
--- a/crates/kas-widgets/src/menu/submenu.rs
+++ b/crates/kas-widgets/src/menu/submenu.rs
@@ -101,7 +101,7 @@ impl_scope! {
             None
         }
 
-        fn l_find_id(&mut self, _: Coord) -> Id {
+        fn probe(&mut self, _: Coord) -> Id {
             self.id()
         }
 
@@ -354,9 +354,9 @@ impl_scope! {
             }
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
+        fn probe(&mut self, coord: Coord) -> Id {
             for child in self.list.iter_mut() {
-                if let Some(id) = child.find_id(coord) {
+                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 877eeaf6d..053d08c6c 100644
--- a/crates/kas-widgets/src/radio_box.rs
+++ b/crates/kas-widgets/src/radio_box.rs
@@ -153,7 +153,7 @@ impl_scope! {
             crate::check_box::shrink_to_text(&mut self.core.rect, dir, &self.label);
         }
 
-        fn l_find_id(&mut self, _: Coord) -> Id {
+        fn probe(&mut self, _: Coord) -> Id {
             self.inner.id()
         }
     }
diff --git a/crates/kas-widgets/src/scroll.rs b/crates/kas-widgets/src/scroll.rs
index 323ed2bbc..5d8395ffb 100644
--- a/crates/kas-widgets/src/scroll.rs
+++ b/crates/kas-widgets/src/scroll.rs
@@ -123,8 +123,8 @@ impl_scope! {
             self.scroll_offset()
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
-            self.inner.find_id(coord + self.translation())
+        fn probe(&mut 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 3da9adca3..3fac1add1 100644
--- a/crates/kas-widgets/src/scroll_bar.rs
+++ b/crates/kas-widgets/src/scroll_bar.rs
@@ -290,11 +290,11 @@ impl_scope! {
             let _ = self.update_widgets();
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
+        fn probe(&mut self, coord: Coord) -> Id {
             if self.invisible && self.max_value == 0 {
                 return self.id();
             }
-            self.grip.find_id(coord).unwrap_or_else(|| self.id())
+            self.grip.try_probe(coord).unwrap_or_else(|| self.id())
         }
 
         fn draw(&mut self, mut draw: DrawCx) {
@@ -497,10 +497,10 @@ impl_scope! {
             }
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
-            self.vert_bar.find_id(coord)
-                .or_else(|| self.horiz_bar.find_id(coord))
-                .or_else(|| self.inner.find_id(coord))
+        fn probe(&mut 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))
                 .unwrap_or_else(|| self.id())
         }
 
diff --git a/crates/kas-widgets/src/scroll_label.rs b/crates/kas-widgets/src/scroll_label.rs
index 9420982ca..072eb5542 100644
--- a/crates/kas-widgets/src/scroll_label.rs
+++ b/crates/kas-widgets/src/scroll_label.rs
@@ -61,8 +61,8 @@ impl_scope! {
             self.bar.set_value(cx, self.view_offset.1);
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
-            self.bar.find_id(coord).unwrap_or_else(|| self.id())
+        fn probe(&mut self, coord: Coord) -> Id {
+            self.bar.try_probe(coord).unwrap_or_else(|| self.id())
         }
 
         fn draw(&mut self, mut draw: DrawCx) {
diff --git a/crates/kas-widgets/src/scroll_text.rs b/crates/kas-widgets/src/scroll_text.rs
index 18a4510d8..d42d1a91e 100644
--- a/crates/kas-widgets/src/scroll_text.rs
+++ b/crates/kas-widgets/src/scroll_text.rs
@@ -61,8 +61,8 @@ impl_scope! {
             self.bar.set_value(cx, self.view_offset.1);
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
-            self.bar.find_id(coord).unwrap_or_else(|| self.id())
+        fn probe(&mut self, coord: Coord) -> Id {
+            self.bar.try_probe(coord).unwrap_or_else(|| self.id())
         }
 
         fn draw(&mut self, mut draw: DrawCx) {
diff --git a/crates/kas-widgets/src/slider.rs b/crates/kas-widgets/src/slider.rs
index 90b12f64b..ebfa1906a 100644
--- a/crates/kas-widgets/src/slider.rs
+++ b/crates/kas-widgets/src/slider.rs
@@ -311,9 +311,9 @@ impl_scope! {
             let _ = self.grip.set_size_and_offset(size, self.offset());
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
+        fn probe(&mut self, coord: Coord) -> Id {
             if self.on_move.is_some() {
-                if let Some(id) = self.grip.find_id(coord) {
+                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 a635577d5..6d8e4dc23 100644
--- a/crates/kas-widgets/src/spinner.rs
+++ b/crates/kas-widgets/src/spinner.rs
@@ -286,9 +286,9 @@ impl_scope! {
             self.edit.set_outer_rect(rect, FrameStyle::EditBox);
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
-            self.b_up.find_id(coord)
-                .or_else(|| self.b_down.find_id(coord))
+        fn probe(&mut 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 391513b44..44241efbf 100644
--- a/crates/kas-widgets/src/splitter.rs
+++ b/crates/kas-widgets/src/splitter.rs
@@ -213,7 +213,7 @@ impl_scope! {
             }
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
+        fn probe(&mut self, coord: Coord) -> Id {
             if !self.size_solved {
                 debug_assert!(false);
                 return self.id();
@@ -225,12 +225,12 @@ impl_scope! {
 
             let solver = layout::RowPositionSolver::new(self.direction);
             if let Some(child) = solver.find_child_mut(&mut self.widgets, coord) {
-                return child.find_id(coord).unwrap_or_else(|| self.id());
+                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) {
-                return child.find_id(coord).unwrap_or_else(|| self.id());
+                return child.try_probe(coord).unwrap_or_else(|| self.id());
             }
 
             self.id()
@@ -241,7 +241,7 @@ impl_scope! {
                 debug_assert!(false);
                 return;
             }
-            // as with l_find_id, there's not much harm in invoking the solver twice
+            // as with probe, there's not much harm in invoking the solver twice
 
             let solver = layout::RowPositionSolver::new(self.direction);
             solver.for_children_mut(&mut self.widgets, draw.get_clip_rect(), |w| {
diff --git a/crates/kas-widgets/src/stack.rs b/crates/kas-widgets/src/stack.rs
index c14943447..fefbe737b 100644
--- a/crates/kas-widgets/src/stack.rs
+++ b/crates/kas-widgets/src/stack.rs
@@ -127,10 +127,10 @@ impl_scope! {
             }
         }
 
-        fn l_find_id(&mut self, coord: Coord) -> Id {
+        fn probe(&mut self, coord: Coord) -> Id {
             if let Some(entry) = self.widgets.get_mut(self.active) {
                 debug_assert_eq!(entry.1, State::Sized);
-                if let Some(id) = entry.0.find_id(coord) {
+                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 3dc332d1b..e89d78794 100644
--- a/crates/kas-widgets/src/tab_stack.rs
+++ b/crates/kas-widgets/src/tab_stack.rs
@@ -44,7 +44,7 @@ impl_scope! {
     }
 
     impl Layout for Self {
-        fn l_find_id(&mut self, _: Coord) -> Id {
+        fn probe(&mut self, _: Coord) -> Id {
             self.id()
         }
     }
diff --git a/examples/cursors.rs b/examples/cursors.rs
index 3f405730c..de34e1db9 100644
--- a/examples/cursors.rs
+++ b/examples/cursors.rs
@@ -22,7 +22,7 @@ impl_scope! {
         cursor: CursorIcon,
     }
     impl Layout for Self {
-        fn l_find_id(&mut self, _: Coord) -> Id {
+        fn probe(&mut self, _: Coord) -> Id {
             // Steal mouse focus: hover points to self, not self.label
             self.id()
         }