From 47c220ec416d6a6aafbf468ed99a0b4be93109d7 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 24 Jan 2025 11:36:46 +0000 Subject: [PATCH] Move fn Events::probe to Tile with where Self: Sized clause Also some reordering of impls. --- crates/kas-core/src/core/layout.rs | 54 +++++++++++++- crates/kas-core/src/core/widget.rs | 50 +------------ crates/kas-core/src/root.rs | 4 -- crates/kas-macros/src/make_layout.rs | 6 +- crates/kas-macros/src/widget.rs | 13 ++-- crates/kas-view/src/list_view.rs | 78 ++++++++++---------- crates/kas-view/src/matrix_view.rs | 82 +++++++++++----------- crates/kas-widgets/src/adapt/with_label.rs | 2 +- crates/kas-widgets/src/check_box.rs | 8 ++- crates/kas-widgets/src/edit.rs | 18 +++-- crates/kas-widgets/src/grid.rs | 70 +++++++++--------- crates/kas-widgets/src/list.rs | 62 ++++++++-------- crates/kas-widgets/src/menu/menu_entry.rs | 18 +++-- crates/kas-widgets/src/menu/menubar.rs | 48 ++++++------- crates/kas-widgets/src/menu/mod.rs | 2 +- crates/kas-widgets/src/menu/submenu.rs | 78 ++++++++++---------- crates/kas-widgets/src/radio_box.rs | 8 ++- crates/kas-widgets/src/scroll.rs | 10 +-- crates/kas-widgets/src/scroll_bar.rs | 12 ++-- crates/kas-widgets/src/scroll_label.rs | 10 +-- crates/kas-widgets/src/scroll_text.rs | 10 +-- crates/kas-widgets/src/slider.rs | 18 ++--- crates/kas-widgets/src/spinner.rs | 8 ++- crates/kas-widgets/src/splitter.rs | 82 +++++++++++----------- crates/kas-widgets/src/stack.rs | 66 ++++++++--------- crates/kas-widgets/src/tab_stack.rs | 4 +- examples/cursors.rs | 2 +- 27 files changed, 421 insertions(+), 402 deletions(-) diff --git a/crates/kas-core/src/core/layout.rs b/crates/kas-core/src/core/layout.rs index 9afb3331..349370c6 100644 --- a/crates/kas-core/src/core/layout.rs +++ b/crates/kas-core/src/core/layout.rs @@ -153,7 +153,7 @@ pub trait Layout { /// /// # Implementation /// - /// Widgets should implement [`Events::probe`] instead, in which case an + /// Widgets should implement [`Tile::probe`] instead, in which case an /// implemention of this method will be provided: /// ```ignore /// self.rect().contains(coord).then(|| self.probe(coord)) @@ -340,7 +340,7 @@ pub trait Tile: Layout { /// *and* child widgets need to implement this. /// Such widgets must also implement [`Events::handle_scroll`]. /// - /// Affects event handling via [`Events::probe`] and affects the positioning + /// Affects event handling via [`Tile::probe`] and affects the positioning /// of pop-up menus. [`Layout::draw`] must be implemented directly using /// [`DrawCx::with_clip_region`] to offset contents. /// @@ -349,6 +349,56 @@ pub trait Tile: Layout { fn translation(&self) -> Offset { Offset::ZERO } + + /// Probe a coordinate for a widget's [`Id`] + /// + /// Returns the [`Id`] of the widget expected to handle clicks and touch + /// events at the given `coord`. Typically this is the lowest descendant in + /// the widget tree at the given `coord`, but it is not required to be; e.g. + /// a `Button` may use an inner widget as a label but return its own [`Id`] + /// to indicate that the button (not the inner label) handles clicks. + /// + /// # Calling + /// + /// **Prefer to call [`Layout::try_probe`] instead**. + /// + /// ## 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. + /// + /// # Implementation + /// + /// The callee may usually assume that it occupies `coord` and may thus + /// return its own [`Id`] when no child occupies the input `coord`. + /// + /// ## Default implementation + /// + /// ## Default implementation + /// + /// The `#[widget]` macro + /// [may generate a default implementation](macros::widget#layout-1) by + /// implementing [`LayoutVisitor`] for `Self`. + /// In this case the default impl of this method is + /// `self.layout_visitor().set_rect(/* ... */)`. + /// The underlying implementation considers all children of the `layout` + /// property and of fields, like this: + /// ```ignore + /// let coord = coord + self.translation(); + /// for child in ITER_OVER_CHILDREN { + /// if let Some(id) = child.try_probe(coord) { + /// return Some(id); + /// } + /// } + /// self.id() + /// ``` + fn probe(&mut self, coord: Coord) -> Id + where + Self: Sized, + { + let _ = coord; + unimplemented!() // make rustdoc show that this is a provided method + } } impl HasId for &W { diff --git a/crates/kas-core/src/core/widget.rs b/crates/kas-core/src/core/widget.rs index d2d90f65..17afc188 100644 --- a/crates/kas-core/src/core/widget.rs +++ b/crates/kas-core/src/core/widget.rs @@ -9,7 +9,6 @@ use super::{Node, Tile}; #[allow(unused)] use crate::event::Used; use crate::event::{ConfigCx, Event, EventCx, IsUsed, Scroll, Unused}; -use crate::geom::Coord; #[allow(unused)] use crate::layout::LayoutVisitor; use crate::Id; #[allow(unused)] use kas_macros as macros; @@ -143,53 +142,6 @@ pub trait Events: Widget + Sized { false } - /// Probe a coordinate for a widget's [`Id`] - /// - /// Returns the [`Id`] of the widget expected to handle clicks and touch - /// events at the given `coord`. Typically this is the lowest descendant in - /// the widget tree at the given `coord`, but it is not required to be; e.g. - /// a `Button` may use an inner widget as a label but return its own [`Id`] - /// to indicate that the button (not the inner label) handles clicks. - /// - /// # Calling - /// - /// **Prefer to call [`Layout::try_probe`] instead**. - /// - /// ## 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. - /// - /// # Implementation - /// - /// The callee may usually assume that it occupies `coord` and may thus - /// return its own [`Id`] when no child occupies the input `coord`. - /// - /// ## Default implementation - /// - /// ## Default implementation - /// - /// The `#[widget]` macro - /// [may generate a default implementation](macros::widget#layout-1) by - /// implementing [`LayoutVisitor`] for `Self`. - /// In this case the default impl of this method is - /// `self.layout_visitor().set_rect(/* ... */)`. - /// The underlying implementation considers all children of the `layout` - /// property and of fields, like this: - /// ```ignore - /// let coord = coord + self.translation(); - /// for child in ITER_OVER_CHILDREN { - /// if let Some(id) = child.try_probe(coord) { - /// return Some(id); - /// } - /// } - /// self.id() - /// ``` - fn probe(&mut self, coord: Coord) -> Id { - let _ = coord; - unimplemented!() // make rustdoc show that this is a provided method - } - /// Mouse focus handler /// /// Called when mouse hover state changes. @@ -362,7 +314,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 [`Events::probe`]). +/// and [`Tile::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/root.rs b/crates/kas-core/src/root.rs index 64059664..543004b7 100644 --- a/crates/kas-core/src/root.rs +++ b/crates/kas-core/src/root.rs @@ -210,10 +210,6 @@ impl_scope! { } } - fn probe(&mut self, _: Coord) -> Id { - unimplemented!() - } - fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed { match event { Event::PressStart { .. } if self.drag_anywhere => { diff --git a/crates/kas-macros/src/make_layout.rs b/crates/kas-macros/src/make_layout.rs index 7cb2b8d8..9a8a6198 100644 --- a/crates/kas-macros/src/make_layout.rs +++ b/crates/kas-macros/src/make_layout.rs @@ -180,7 +180,7 @@ impl Tree { } fn try_probe(&mut self, coord: ::kas::geom::Coord) -> Option<::kas::Id> { - ::kas::Tile::rect(self).contains(coord).then(|| ::kas::Events::probe(self, coord)) + ::kas::Tile::rect(self).contains(coord).then(|| ::kas::Tile::probe(self, coord)) } fn draw(&mut self, mut draw: ::kas::theme::DrawCx) { @@ -206,9 +206,7 @@ impl Tree { fn nav_next(&self, reverse: bool, from: Option) -> Option { #nav_next } - } - impl #impl_generics ::kas::Events for #impl_target { #[inline] fn probe(&mut self, coord: ::kas::geom::Coord) -> ::kas::Id { #[cfg(debug_assertions)] @@ -219,7 +217,9 @@ impl Tree { .try_probe(coord) .unwrap_or_else(|| ::kas::Tile::id(self)) } + } + impl #impl_generics ::kas::Events for #impl_target { fn handle_event( &mut self, _: &mut ::kas::event::EventCx, diff --git a/crates/kas-macros/src/widget.rs b/crates/kas-macros/src/widget.rs index d7ce70d5..7d509930 100644 --- a/crates/kas-macros/src/widget.rs +++ b/crates/kas-macros/src/widget.rs @@ -506,16 +506,11 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul if let Some(index) = events_impl { let events_impl = &mut scope.impls[index]; let item_idents = collect_idents(events_impl); - let has_item = |name| item_idents.iter().any(|(_, ident)| ident == name); if let Some(method) = fn_navigable { events_impl.items.push(Verbatim(method)); } - if !has_item("probe") { - events_impl.items.push(Verbatim(fn_probe)); - } - events_impl.items.push(Verbatim(fn_handle_hover)); if let Some((index, _)) = item_idents @@ -538,7 +533,6 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul scope.generated.push(quote! { impl #impl_generics ::kas::Events for #impl_target { #fn_navigable - #fn_probe #fn_handle_hover #fn_handle_event } @@ -550,7 +544,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::Events::probe(self, coord)) + ::kas::Tile::rect(self).contains(coord).then(|| ::kas::Tile::probe(self, coord)) } }; @@ -655,6 +649,10 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul } } } + + if !has_item("probe") { + tile_impl.items.push(Verbatim(fn_probe)); + } } else { let fn_nav_next = match fn_nav_next { Ok(method) => Some(method), @@ -668,6 +666,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul impl #impl_generics ::kas::Tile for #impl_target { #required_tile_methods #fn_nav_next + #fn_probe } }); } diff --git a/crates/kas-view/src/list_view.rs b/crates/kas-view/src/list_view.rs index 6235d920..477814c7 100644 --- a/crates/kas-view/src/list_view.rs +++ b/crates/kas-view/src/list_view.rs @@ -419,33 +419,6 @@ impl_scope! { } } - impl Tile for Self { - #[inline] - fn num_children(&self) -> usize { - self.cur_len.cast() - } - fn get_child(&self, index: usize) -> Option<&dyn Tile> { - self.widgets.get(index).map(|w| w.widget.as_tile()) - } - fn find_child_index(&self, id: &Id) -> Option { - let key = A::Key::reconstruct_key(self.id_ref(), id); - if key.is_some() { - self.widgets - .iter() - .enumerate() - .filter_map(|(i, w)| (key == w.key).then_some(i)) - .next() - } else { - None - } - } - - #[inline] - fn translation(&self) -> Offset { - self.scroll_offset() - } - } - impl Layout for Self { fn size_rules(&mut self, sizer: SizeCx, mut axis: AxisInfo) -> SizeRules { // We use an invisible frame for highlighting selections, drawing into the margin @@ -570,6 +543,45 @@ impl_scope! { } } + impl Tile for Self { + #[inline] + fn num_children(&self) -> usize { + self.cur_len.cast() + } + fn get_child(&self, index: usize) -> Option<&dyn Tile> { + self.widgets.get(index).map(|w| w.widget.as_tile()) + } + fn find_child_index(&self, id: &Id) -> Option { + let key = A::Key::reconstruct_key(self.id_ref(), id); + if key.is_some() { + self.widgets + .iter() + .enumerate() + .filter_map(|(i, w)| (key == w.key).then_some(i)) + .next() + } else { + None + } + } + + #[inline] + fn translation(&self) -> Offset { + self.scroll_offset() + } + + 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.try_probe(coord) { + return id; + } + } + } + self.id() + } + } + impl Events for Self { #[inline] fn make_child_id(&mut self, _: usize) -> Id { @@ -616,18 +628,6 @@ impl_scope! { fn update_recurse(&mut self, _: &mut ConfigCx, _: &Self::Data) {} - 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.try_probe(coord) { - return id; - } - } - } - self.id() - } - fn handle_event(&mut self, cx: &mut EventCx, data: &A, event: Event) -> IsUsed { let is_used = match event { Event::Command(cmd, _) => { diff --git a/crates/kas-view/src/matrix_view.rs b/crates/kas-view/src/matrix_view.rs index 101acf59..c481c843 100644 --- a/crates/kas-view/src/matrix_view.rs +++ b/crates/kas-view/src/matrix_view.rs @@ -361,34 +361,6 @@ impl_scope! { } } - impl Tile for Self { - #[inline] - fn num_children(&self) -> usize { - usize::conv(self.cur_len.0) * usize::conv(self.cur_len.1) - } - fn get_child(&self, index: usize) -> Option<&dyn Tile> { - self.widgets.get(index).map(|w| w.widget.as_tile()) - } - fn find_child_index(&self, id: &Id) -> Option { - let num = self.num_children(); - let key = A::Key::reconstruct_key(self.id_ref(), id); - if key.is_some() { - self.widgets[0..num] - .iter() - .enumerate() - .filter_map(|(i, w)| (key == w.key).then_some(i)) - .next() - } else { - None - } - } - - #[inline] - fn translation(&self) -> Offset { - self.scroll_offset() - } - } - impl Layout for Self { fn size_rules(&mut self, sizer: SizeCx, mut axis: AxisInfo) -> SizeRules { // We use an invisible frame for highlighting selections, drawing into the margin @@ -510,6 +482,47 @@ impl_scope! { } } + impl Tile for Self { + #[inline] + fn num_children(&self) -> usize { + usize::conv(self.cur_len.0) * usize::conv(self.cur_len.1) + } + fn get_child(&self, index: usize) -> Option<&dyn Tile> { + self.widgets.get(index).map(|w| w.widget.as_tile()) + } + fn find_child_index(&self, id: &Id) -> Option { + let num = self.num_children(); + let key = A::Key::reconstruct_key(self.id_ref(), id); + if key.is_some() { + self.widgets[0..num] + .iter() + .enumerate() + .filter_map(|(i, w)| (key == w.key).then_some(i)) + .next() + } else { + None + } + } + + #[inline] + fn translation(&self) -> Offset { + self.scroll_offset() + } + + 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.try_probe(coord) { + return id; + } + } + } + self.id() + } + } + impl Events for Self { #[inline] fn make_child_id(&mut self, _: usize) -> Id { @@ -557,19 +570,6 @@ impl_scope! { fn update_recurse(&mut self, _: &mut ConfigCx, _: &Self::Data) {} - 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.try_probe(coord) { - return id; - } - } - } - self.id() - } - fn handle_event(&mut self, cx: &mut EventCx, data: &A, event: Event) -> IsUsed { let is_used = match event { Event::Command(cmd, _) => { diff --git a/crates/kas-widgets/src/adapt/with_label.rs b/crates/kas-widgets/src/adapt/with_label.rs index d604eec3..34ca5ce9 100644 --- a/crates/kas-widgets/src/adapt/with_label.rs +++ b/crates/kas-widgets/src/adapt/with_label.rs @@ -111,7 +111,7 @@ impl_scope! { } } - impl Events for Self { + impl Tile for Self { 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 a5072310..57331d06 100644 --- a/crates/kas-widgets/src/check_box.rs +++ b/crates/kas-widgets/src/check_box.rs @@ -193,12 +193,14 @@ impl_scope! { } } - impl Events for Self { - type Data = A; - + impl Tile for Self { fn probe(&mut self, _: Coord) -> Id { self.inner.id() } + } + + impl Events for Self { + type Data = A; fn handle_messages(&mut self, cx: &mut EventCx, data: &Self::Data) { if let Some(kas::messages::Activate(code)) = cx.try_pop() { diff --git a/crates/kas-widgets/src/edit.rs b/crates/kas-widgets/src/edit.rs index 7ae54c53..502c1b3f 100644 --- a/crates/kas-widgets/src/edit.rs +++ b/crates/kas-widgets/src/edit.rs @@ -404,9 +404,7 @@ impl_scope! { } } - impl Events for Self { - type Data = G::Data; - + impl Tile for Self { fn probe(&mut self, coord: Coord) -> Id { if self.inner.max_scroll_offset().1 > 0 { if let Some(id) = self.bar.try_probe(coord) { @@ -418,6 +416,10 @@ impl_scope! { // the event to self.inner without further question. self.inner.id() } + } + + impl Events for Self { + type Data = G::Data; fn handle_messages(&mut self, cx: &mut EventCx<'_>, _: &G::Data) { if let Some(ScrollMsg(y)) = cx.try_pop() { @@ -781,6 +783,12 @@ impl_scope! { } } + impl Tile for Self { + fn probe(&mut self, _: Coord) -> Id { + self.id() + } + } + impl Events for Self { type Data = G::Data; @@ -793,10 +801,6 @@ impl_scope! { G::update(self, cx, data); } - fn probe(&mut self, _: Coord) -> Id { - self.id() - } - fn handle_event(&mut self, cx: &mut EventCx, data: &G::Data, event: Event) -> IsUsed { match event { Event::NavFocus(source) if source.key_or_synthetic() => { diff --git a/crates/kas-widgets/src/grid.rs b/crates/kas-widgets/src/grid.rs index bbebaf4e..027a1808 100644 --- a/crates/kas-widgets/src/grid.rs +++ b/crates/kas-widgets/src/grid.rs @@ -56,42 +56,6 @@ impl_scope! { widgets: C, } - impl Events for Self { - fn probe(&mut self, coord: Coord) -> Id { - for n in 0..self.widgets.len() { - if let Some(child) = self.widgets.get_mut_tile(n) { - if let Some(id) = child.try_probe(coord) { - return id; - } - } - } - self.id() - } - } - - impl Widget for Self { - type Data = C::Data; - - fn for_child_node( - &mut self, - data: &C::Data, - index: usize, - closure: Box) + '_>, - ) { - self.widgets.for_node(data, index, closure); - } - } - - impl Tile for Self { - #[inline] - fn num_children(&self) -> usize { - self.widgets.len() - } - fn get_child(&self, index: usize) -> Option<&dyn Tile> { - self.widgets.get_tile(index).map(|w| w.as_tile()) - } - } - impl Layout for Self { fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules { let mut solver = GridSolver::, Vec<_>, _>::new(axis, self.dim, &mut self.layout); @@ -123,6 +87,40 @@ impl_scope! { } } } + + impl Tile for Self { + #[inline] + fn num_children(&self) -> usize { + self.widgets.len() + } + fn get_child(&self, index: usize) -> Option<&dyn Tile> { + self.widgets.get_tile(index).map(|w| w.as_tile()) + } + + fn probe(&mut self, coord: Coord) -> Id { + for n in 0..self.widgets.len() { + if let Some(child) = self.widgets.get_mut_tile(n) { + if let Some(id) = child.try_probe(coord) { + return id; + } + } + } + self.id() + } + } + + impl Widget for Self { + type Data = C::Data; + + fn for_child_node( + &mut self, + data: &C::Data, + index: usize, + closure: Box) + '_>, + ) { + self.widgets.for_node(data, index, closure); + } + } } impl Grid { diff --git a/crates/kas-widgets/src/list.rs b/crates/kas-widgets/src/list.rs index 85d10160..3a11d43f 100644 --- a/crates/kas-widgets/src/list.rs +++ b/crates/kas-widgets/src/list.rs @@ -81,22 +81,6 @@ impl_scope! { id_map: HashMap, // map key of Id to index } - impl Tile for Self { - #[inline] - fn num_children(&self) -> usize { - self.widgets.len() - } - - fn get_child(&self, index: usize) -> Option<&dyn Tile> { - self.widgets.get_tile(index) - } - - fn find_child_index(&self, id: &Id) -> Option { - id.next_key_after(self.id_ref()) - .and_then(|k| self.id_map.get(&k).cloned()) - } - } - impl Layout for Self { fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules { let dim = (self.direction, self.widgets.len()); @@ -127,16 +111,27 @@ impl_scope! { } } - impl Widget for Self { - type Data = C::Data; + impl Tile for Self { + #[inline] + fn num_children(&self) -> usize { + self.widgets.len() + } - fn for_child_node( - &mut self, - data: &C::Data, - index: usize, - closure: Box) + '_>, - ) { - self.widgets.for_node(data, index, closure); + fn get_child(&self, index: usize) -> Option<&dyn Tile> { + self.widgets.get_tile(index) + } + + fn find_child_index(&self, id: &Id) -> Option { + id.next_key_after(self.id_ref()) + .and_then(|k| self.id_map.get(&k).cloned()) + } + + 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.try_probe(coord)) + .unwrap_or_else(|| self.id()) } } @@ -168,13 +163,18 @@ impl_scope! { fn configure(&mut self, _: &mut ConfigCx) { self.id_map.clear(); } + } - 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.try_probe(coord)) - .unwrap_or_else(|| self.id()) + impl Widget for Self { + type Data = C::Data; + + fn for_child_node( + &mut self, + data: &C::Data, + index: usize, + closure: Box) + '_>, + ) { + self.widgets.for_node(data, index, closure); } } diff --git a/crates/kas-widgets/src/menu/menu_entry.rs b/crates/kas-widgets/src/menu/menu_entry.rs index 7e014f94..c00d0783 100644 --- a/crates/kas-widgets/src/menu/menu_entry.rs +++ b/crates/kas-widgets/src/menu/menu_entry.rs @@ -37,6 +37,12 @@ impl_scope! { } } + impl Tile for Self { + fn probe(&mut self, _: Coord) -> Id { + self.id() + } + } + impl Self { /// Construct a menu item with a given `label` and `msg` /// @@ -66,10 +72,6 @@ impl_scope! { impl Events for Self { type Data = (); - fn probe(&mut self, _: Coord) -> Id { - self.id() - } - fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed { match event { Event::Command(cmd, code) if cmd.is_activate() => { @@ -127,12 +129,14 @@ impl_scope! { } } - impl Events for Self { - type Data = A; - + impl Tile for Self { fn probe(&mut self, _: Coord) -> Id { self.checkbox.id() } + } + + impl Events for Self { + type Data = A; fn handle_messages(&mut self, cx: &mut EventCx, data: &Self::Data) { if let Some(kas::messages::Activate(code)) = cx.try_pop() { diff --git a/crates/kas-widgets/src/menu/menubar.rs b/crates/kas-widgets/src/menu/menubar.rs index dab4a75e..cba3c21d 100644 --- a/crates/kas-widgets/src/menu/menubar.rs +++ b/crates/kas-widgets/src/menu/menubar.rs @@ -62,16 +62,6 @@ impl_scope! { } } - impl Tile for Self { - #[inline] - fn num_children(&self) -> usize { - self.widgets.len() - } - fn get_child(&self, index: usize) -> Option<&dyn Tile> { - self.widgets.get(index).map(|w| w.as_tile()) - } - } - impl Layout for Self { fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules { // Unusual behaviour: children's SizeRules are padded with a frame, @@ -109,22 +99,15 @@ impl_scope! { } } - impl Widget for Self { - type Data = Data; - - fn for_child_node( - &mut self, - data: &Data, - index: usize, - closure: Box) + '_>, - ) { - if let Some(w) = self.widgets.get_mut(index) { - closure(w.as_node(data)); - } + impl Tile for Self { + #[inline] + fn num_children(&self) -> usize { + self.widgets.len() + } + fn get_child(&self, index: usize) -> Option<&dyn Tile> { + self.widgets.get(index).map(|w| w.as_tile()) } - } - impl Events for MenuBar { fn probe(&mut self, coord: Coord) -> Id { let solver = RowPositionSolver::new(self.direction); solver @@ -132,7 +115,9 @@ impl_scope! { .and_then(|child| child.try_probe(coord)) .unwrap_or_else(|| self.id()) } + } + impl Events for Self { fn handle_event(&mut self, cx: &mut EventCx, data: &Data, event: Event) -> IsUsed { match event { Event::Timer(id_code) => { @@ -254,6 +239,21 @@ impl_scope! { } } + impl Widget for Self { + type Data = Data; + + fn for_child_node( + &mut self, + data: &Data, + index: usize, + closure: Box) + '_>, + ) { + if let Some(w) = self.widgets.get_mut(index) { + closure(w.as_node(data)); + } + } + } + impl Self { fn set_menu_path( &mut self, diff --git a/crates/kas-widgets/src/menu/mod.rs b/crates/kas-widgets/src/menu/mod.rs index 63caea13..1c23b7a8 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 [`Events::probe`] returns the identifier of the widget +/// should ensure that [`Tile::probe`] returns the identifier of the widget /// which should be focussed, and that this widget has /// [`Events::navigable`] return true. #[autoimpl(for Box)] diff --git a/crates/kas-widgets/src/menu/submenu.rs b/crates/kas-widgets/src/menu/submenu.rs index 3e47f59e..f0b987a1 100644 --- a/crates/kas-widgets/src/menu/submenu.rs +++ b/crates/kas-widgets/src/menu/submenu.rs @@ -110,6 +110,10 @@ impl_scope! { // We have no child within our rect None } + + fn probe(&mut self, _: Coord) -> Id { + self.id() + } } impl Events for Self { @@ -119,10 +123,6 @@ impl_scope! { self.navigable } - fn probe(&mut self, _: Coord) -> Id { - self.id() - } - fn handle_event(&mut self, cx: &mut EventCx, data: &Data, event: Event) -> IsUsed { match event { Event::Command(cmd, code) if cmd.is_activate() => { @@ -209,42 +209,6 @@ impl_scope! { list: Vec, } - impl Events for Self { - fn probe(&mut self, coord: Coord) -> Id { - for child in self.list.iter_mut() { - if let Some(id) = child.try_probe(coord) { - return id; - } - } - self.id() - } - } - - impl kas::Widget for Self { - type Data = W::Data; - - fn for_child_node( - &mut self, - data: &W::Data, - index: usize, - closure: Box) + '_>, - ) { - if let Some(w) = self.list.get_mut(index) { - closure(w.as_node(data)); - } - } - } - - impl Tile for Self { - #[inline] - fn num_children(&self) -> usize { - self.list.len() - } - fn get_child(&self, index: usize) -> Option<&dyn Tile> { - self.list.get(index).map(|w| w.as_tile()) - } - } - impl kas::Layout for Self { fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules { self.dim = layout::GridDimensions { @@ -376,6 +340,40 @@ impl_scope! { } } + impl Tile for Self { + #[inline] + fn num_children(&self) -> usize { + self.list.len() + } + fn get_child(&self, index: usize) -> Option<&dyn Tile> { + self.list.get(index).map(|w| w.as_tile()) + } + + fn probe(&mut self, coord: Coord) -> Id { + for child in self.list.iter_mut() { + if let Some(id) = child.try_probe(coord) { + return id; + } + } + self.id() + } + } + + impl kas::Widget for Self { + type Data = W::Data; + + fn for_child_node( + &mut self, + data: &W::Data, + index: usize, + closure: Box) + '_>, + ) { + if let Some(w) = self.list.get_mut(index) { + closure(w.as_node(data)); + } + } + } + impl Self { /// Construct from a list of menu items pub fn new(list: Vec) -> Self { diff --git a/crates/kas-widgets/src/radio_box.rs b/crates/kas-widgets/src/radio_box.rs index ab4c2d5b..6f732fbd 100644 --- a/crates/kas-widgets/src/radio_box.rs +++ b/crates/kas-widgets/src/radio_box.rs @@ -154,12 +154,14 @@ impl_scope! { } } - impl Events for Self { - type Data = A; - + impl Tile for Self { fn probe(&mut self, _: Coord) -> Id { self.inner.id() } + } + + impl Events for Self { + type Data = A; fn handle_messages(&mut self, cx: &mut EventCx, data: &Self::Data) { if let Some(kas::messages::Activate(code)) = cx.try_pop() { diff --git a/crates/kas-widgets/src/scroll.rs b/crates/kas-widgets/src/scroll.rs index ad1af5b0..bd746e19 100644 --- a/crates/kas-widgets/src/scroll.rs +++ b/crates/kas-widgets/src/scroll.rs @@ -129,6 +129,11 @@ impl_scope! { fn translation(&self) -> Offset { self.scroll_offset() } + + fn probe(&mut self, coord: Coord) -> Id { + self.inner.try_probe(coord + self.translation()) + .unwrap_or_else(|| self.id()) + } } impl Events for Self { @@ -136,11 +141,6 @@ impl_scope! { cx.register_nav_fallback(self.id()); } - fn probe(&mut self, coord: Coord) -> Id { - self.inner.try_probe(coord + self.translation()) - .unwrap_or_else(|| self.id()) - } - fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed { self.scroll .scroll_by_event(cx, event, self.id(), self.rect()) diff --git a/crates/kas-widgets/src/scroll_bar.rs b/crates/kas-widgets/src/scroll_bar.rs index f0b5b7fd..93a38de9 100644 --- a/crates/kas-widgets/src/scroll_bar.rs +++ b/crates/kas-widgets/src/scroll_bar.rs @@ -306,15 +306,17 @@ impl_scope! { } } - impl Events for Self { - type Data = (); - + impl Tile for Self { fn probe(&mut self, coord: Coord) -> Id { if self.invisible && self.max_value == 0 { return self.id(); } self.grip.try_probe(coord).unwrap_or_else(|| self.id()) } + } + + impl Events for Self { + type Data = (); fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed { match event { @@ -510,14 +512,16 @@ impl_scope! { } } - impl Events for Self { + impl Tile for Self { 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()) } + } + impl Events for Self { fn handle_messages(&mut self, cx: &mut EventCx, _: &Self::Data) { let index = cx.last_child(); if index == Some(widget_index![self.horiz_bar]) { diff --git a/crates/kas-widgets/src/scroll_label.rs b/crates/kas-widgets/src/scroll_label.rs index 23013e29..2ffc25f5 100644 --- a/crates/kas-widgets/src/scroll_label.rs +++ b/crates/kas-widgets/src/scroll_label.rs @@ -79,6 +79,12 @@ impl_scope! { } } + impl Tile for Self { + fn probe(&mut self, coord: Coord) -> Id { + self.bar.try_probe(coord).unwrap_or_else(|| self.id()) + } + } + impl Self { /// Construct an `ScrollLabel` with the given inital `text` #[inline] @@ -214,10 +220,6 @@ impl_scope! { cx.text_configure(&mut self.text); } - fn probe(&mut self, coord: Coord) -> Id { - self.bar.try_probe(coord).unwrap_or_else(|| self.id()) - } - fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed { match event { Event::Command(cmd, _) => match cmd { diff --git a/crates/kas-widgets/src/scroll_text.rs b/crates/kas-widgets/src/scroll_text.rs index 6efeb1cc..e8c41b08 100644 --- a/crates/kas-widgets/src/scroll_text.rs +++ b/crates/kas-widgets/src/scroll_text.rs @@ -79,6 +79,12 @@ impl_scope! { } } + impl Tile for Self { + fn probe(&mut self, coord: Coord) -> Id { + self.bar.try_probe(coord).unwrap_or_else(|| self.id()) + } + } + impl Self { /// Construct an `ScrollText` with the given inital `text` #[inline] @@ -215,10 +221,6 @@ impl_scope! { cx.action(self, action); } - fn probe(&mut self, coord: Coord) -> Id { - self.bar.try_probe(coord).unwrap_or_else(|| self.id()) - } - fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed { match event { Event::Command(cmd, _) => match cmd { diff --git a/crates/kas-widgets/src/slider.rs b/crates/kas-widgets/src/slider.rs index f3eb5cc0..2ccd8e2c 100644 --- a/crates/kas-widgets/src/slider.rs +++ b/crates/kas-widgets/src/slider.rs @@ -321,14 +321,7 @@ impl_scope! { } } - impl Events for Self { - type Data = A; - - fn update(&mut self, cx: &mut ConfigCx, data: &A) { - let action = self.set_value((self.state_fn)(cx, data)); - cx.action(self, action); - } - + impl Tile for Self { fn probe(&mut self, coord: Coord) -> Id { if self.on_move.is_some() { if let Some(id) = self.grip.try_probe(coord) { @@ -337,6 +330,15 @@ impl_scope! { } self.id() } + } + + impl Events for Self { + type Data = A; + + fn update(&mut self, cx: &mut ConfigCx, data: &A) { + let action = self.set_value((self.state_fn)(cx, data)); + cx.action(self, action); + } fn handle_event(&mut self, cx: &mut EventCx, data: &A, event: Event) -> IsUsed { if self.on_move.is_none() { diff --git a/crates/kas-widgets/src/spinner.rs b/crates/kas-widgets/src/spinner.rs index 167ac5f0..d8985677 100644 --- a/crates/kas-widgets/src/spinner.rs +++ b/crates/kas-widgets/src/spinner.rs @@ -293,14 +293,16 @@ impl_scope! { } } - impl Events for Self { - type Data = A; - + impl Tile for Self { 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()) } + } + + impl Events for Self { + type Data = A; fn handle_event(&mut self, cx: &mut EventCx, data: &A, event: Event) -> IsUsed { let btn = match event { diff --git a/crates/kas-widgets/src/splitter.rs b/crates/kas-widgets/src/splitter.rs index bda7d220..f5d2350e 100644 --- a/crates/kas-widgets/src/splitter.rs +++ b/crates/kas-widgets/src/splitter.rs @@ -127,25 +127,6 @@ impl_scope! { } } - impl Tile for Self { - #[inline] - fn num_children(&self) -> usize { - self.widgets.len() + self.grips.len() - } - fn get_child(&self, index: usize) -> Option<&dyn Tile> { - if (index & 1) != 0 { - self.grips.get(index >> 1).map(|w| w.as_tile()) - } else { - self.widgets.get_tile(index >> 1) - } - } - - fn find_child_index(&self, id: &Id) -> Option { - id.next_key_after(self.id_ref()) - .and_then(|k| self.id_map.get(&k).cloned()) - } - } - impl Layout for Self { fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules { if self.widgets.is_empty() { @@ -236,33 +217,22 @@ impl_scope! { } } - impl Widget for Self { - type Data = C::Data; - - fn for_child_node( - &mut self, - data: &C::Data, - index: usize, - closure: Box) + '_>, - ) { + impl Tile for Self { + #[inline] + fn num_children(&self) -> usize { + self.widgets.len() + self.grips.len() + } + fn get_child(&self, index: usize) -> Option<&dyn Tile> { if (index & 1) != 0 { - if let Some(w) = self.grips.get_mut(index >> 1) { - closure(w.as_node(&())); - } + self.grips.get(index >> 1).map(|w| w.as_tile()) } else { - self.widgets.for_node(data, index >> 1, closure); + self.widgets.get_tile(index >> 1) } } - } - - impl Events for Self { - fn make_child_id(&mut self, child_index: usize) -> Id { - let is_grip = (child_index & 1) != 0; - self.make_next_id(is_grip, child_index / 2) - } - fn configure(&mut self, _: &mut ConfigCx) { - self.id_map.clear(); + fn find_child_index(&self, id: &Id) -> Option { + id.next_key_after(self.id_ref()) + .and_then(|k| self.id_map.get(&k).cloned()) } fn probe(&mut self, coord: Coord) -> Id { @@ -287,6 +257,17 @@ impl_scope! { self.id() } + } + + impl Events for Self { + fn make_child_id(&mut self, child_index: usize) -> Id { + let is_grip = (child_index & 1) != 0; + self.make_next_id(is_grip, child_index / 2) + } + + fn configure(&mut self, _: &mut ConfigCx) { + self.id_map.clear(); + } fn handle_messages(&mut self, cx: &mut EventCx, _: &Self::Data) { if let Some(index) = cx.last_child() { @@ -307,6 +288,25 @@ impl_scope! { } } } + + impl Widget for Self { + type Data = C::Data; + + fn for_child_node( + &mut self, + data: &C::Data, + index: usize, + closure: Box) + '_>, + ) { + if (index & 1) != 0 { + if let Some(w) = self.grips.get_mut(index >> 1) { + closure(w.as_node(&())); + } + } else { + self.widgets.for_node(data, index >> 1, closure); + } + } + } } impl Splitter { diff --git a/crates/kas-widgets/src/stack.rs b/crates/kas-widgets/src/stack.rs index 58192810..879eac3b 100644 --- a/crates/kas-widgets/src/stack.rs +++ b/crates/kas-widgets/src/stack.rs @@ -70,29 +70,6 @@ impl_scope! { } } - impl Tile for Self { - #[inline] - fn num_children(&self) -> usize { - self.widgets.len() - } - fn get_child(&self, index: usize) -> Option<&dyn Tile> { - self.widgets.get(index).map(|(w, _)| w.as_tile()) - } - - fn find_child_index(&self, id: &Id) -> Option { - id.next_key_after(self.id_ref()) - .and_then(|k| self.id_map.get(&k).cloned()) - } - - fn nav_next(&self, _: bool, from: Option) -> Option { - match from { - None => Some(self.active), - Some(active) if active != self.active => Some(self.active), - _ => None, - } - } - } - impl Layout for Self { fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules { let mut rules = SizeRules::EMPTY; @@ -137,6 +114,39 @@ impl_scope! { } } + impl Tile for Self { + #[inline] + fn num_children(&self) -> usize { + self.widgets.len() + } + fn get_child(&self, index: usize) -> Option<&dyn Tile> { + self.widgets.get(index).map(|(w, _)| w.as_tile()) + } + + fn find_child_index(&self, id: &Id) -> Option { + id.next_key_after(self.id_ref()) + .and_then(|k| self.id_map.get(&k).cloned()) + } + + fn nav_next(&self, _: bool, from: Option) -> Option { + match from { + None => Some(self.active), + Some(active) if active != self.active => Some(self.active), + _ => None, + } + } + + 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.try_probe(coord) { + return id; + } + } + self.id() + } + } + impl Events for Self { fn make_child_id(&mut self, index: usize) -> Id { if let Some((child, state)) = self.widgets.get(index) { @@ -204,16 +214,6 @@ impl_scope! { cx.update(w.as_node(data)); } } - - 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.try_probe(coord) { - return id; - } - } - self.id() - } } impl Index for Self { diff --git a/crates/kas-widgets/src/tab_stack.rs b/crates/kas-widgets/src/tab_stack.rs index b5ec3129..fb2c2020 100644 --- a/crates/kas-widgets/src/tab_stack.rs +++ b/crates/kas-widgets/src/tab_stack.rs @@ -43,11 +43,13 @@ impl_scope! { } } - impl Events for Self { + impl Tile for Self { fn probe(&mut self, _: Coord) -> Id { self.id() } + } + impl Events for Self { fn handle_event(&mut self, cx: &mut EventCx, _: &(), event: Event) -> IsUsed { event.on_activate(cx, self.id(), |cx| { cx.push(Select); diff --git a/examples/cursors.rs b/examples/cursors.rs index fcf6b7e7..e2e0e023 100644 --- a/examples/cursors.rs +++ b/examples/cursors.rs @@ -21,7 +21,7 @@ impl_scope! { label: Label<&'static str>, cursor: CursorIcon, } - impl Events for Self { + impl Tile for Self { fn probe(&mut self, _: Coord) -> Id { // Steal mouse focus: hover points to self, not self.label self.id()