diff --git a/crates/kas-widgets/src/grip.rs b/crates/kas-widgets/src/grip.rs index 0ee802e98..dfd458c00 100644 --- a/crates/kas-widgets/src/grip.rs +++ b/crates/kas-widgets/src/grip.rs @@ -43,13 +43,18 @@ impl_scope! { /// /// This widget is unusual in several ways: /// - /// 1. [`Layout::size_rules`] does not request any size; the parent is expected - /// to do this. (Calling this method is still required to comply with - /// widget model.) - /// 2. [`Layout::set_rect`] sets the *track* within which this grip may move; - /// the parent should always call [`GripPart::set_size_and_offset`] - /// afterwards to set the grip position. - /// 3. [`Layout::draw`] does nothing. The parent should handle all drawing. + /// [`Layout::size_rules`] does not request any size; the parent is expected + /// to determine the grip's size. + /// (Calling `size_rules` is still required to comply with widget model.) + /// + /// [`Layout::set_rect`] sets the grip's rect directly. + /// [`Self::set_track`] must be called first. + /// + /// Often it is preferable to use [`Self::set_size`] to set the grip's size + /// then [`Self::set_offset`] to set the position. + /// (Calling `set_rect` is still required to comply with widget model.) + /// + /// [`Layout::draw`] does nothing. The parent should handle all drawing. /// /// # Event handling /// @@ -71,22 +76,12 @@ impl_scope! { press_coord: Coord, } - /// This implementation is unusual in that: - /// - /// 1. `size_rules` always returns [`SizeRules::EMPTY`] - /// 2. `set_rect` sets the *track* within which this grip may move; the - /// parent should call [`GripPart::set_size_and_offset`] after - /// `set_rect` (otherwise the grip's position will not be updated) - /// 3. `draw` does nothing: the parent is expected to do all drawing + /// This implementation is unusual (see [`GripPart`] documentation). impl Layout for GripPart { fn size_rules(&mut self, _: SizeCx, _axis: AxisInfo) -> SizeRules { SizeRules::EMPTY } - fn set_rect(&mut self, _: &mut ConfigCx, rect: Rect, _: AlignHints) { - self.track = rect; - } - fn draw(&mut self, _: DrawCx) {} } @@ -131,12 +126,15 @@ impl GripPart { } } - /// Set a new grip size and position + /// Set the track /// - /// Returns [`Action::REDRAW`] if a redraw is required. - pub fn set_size_and_offset(&mut self, size: Size, offset: Offset) -> Action { - self.core.rect.size = size; - self.set_offset(offset).1 + /// The `track` is the region within which the grip may be moved. + /// + /// This method must be called to set the `track`, presumably from the + /// parent widget's [`Layout::set_rect`] method. + /// It is expected that [`GripPart::set_offset`] is called after this. + pub fn set_track(&mut self, track: Rect) { + self.track = track; } /// Get the current track `Rect` @@ -145,7 +143,24 @@ impl GripPart { self.track } + /// Set the grip's size + /// + /// It is expected that for each axis the `size` is no larger than the size + /// of the `track` (see [`GripPart::set_track`]). If equal, then the grip + /// may not be moved on this axis. + /// + /// This method must be called at least once. + /// It is expected that [`GripPart::set_offset`] is called after this. + /// + /// This size may be read via `self.rect().size`. + pub fn set_size(&mut self, size: Size) { + self.core.rect.size = size; + } + /// Get the current grip position + /// + /// The position returned is relative to `self.track().pos` and is always + /// between [`Offset::ZERO`] and [`Self::max_offset`]. #[inline] pub fn offset(&self) -> Offset { self.core.rect.pos - self.track.pos @@ -153,8 +168,8 @@ impl GripPart { /// Get the maximum allowed offset /// - /// The grip position is clamped between `ZERO` and this offset relative to - /// the track. This value depends on size of the grip and the track. + /// This is the maximum allowed [`Self::offset`], equal to the size of the + /// track minus the size of the grip. #[inline] pub fn max_offset(&self) -> Offset { Offset::conv(self.track.size) - Offset::conv(self.core.rect.size) @@ -162,9 +177,14 @@ impl GripPart { /// Set a new grip position /// - /// Returns the new position (after clamping input) and an action: empty if - /// the grip hasn't moved; `REDRAW` if it has (though this widget is - /// not directly responsible for drawing, so this may not be accurate). + /// The input `offset` is clamped between [`Offset::ZERO`] and + /// [`Self::max_offset`]. + /// + /// The return value is a tuple of the new offest and an action: + /// [`Action::REDRAW`] if the grip has moved, otherwise an empty action. + /// + /// It is expected that [`Self::set_track`] and [`Self::set_size`] are + /// called before this method. pub fn set_offset(&mut self, offset: Offset) -> (Offset, Action) { let offset = offset.min(self.max_offset()).max(Offset::ZERO); let grip_pos = self.track.pos + offset; diff --git a/crates/kas-widgets/src/scroll_bar.rs b/crates/kas-widgets/src/scroll_bar.rs index 3fac1add1..1127d8507 100644 --- a/crates/kas-widgets/src/scroll_bar.rs +++ b/crates/kas-widgets/src/scroll_bar.rs @@ -218,12 +218,9 @@ impl_scope! { let grip_len = i64::from(self.grip_size) * i64::conv(len) / total; self.grip_len = i32::conv(grip_len).max(self.min_grip_len).min(len); let mut size = self.core.rect.size; - if self.direction.is_horizontal() { - size.0 = self.grip_len; - } else { - size.1 = self.grip_len; - } - self.grip.set_size_and_offset(size, self.offset()) + size.set_component(self.direction, self.grip_len); + self.grip.set_size(size); + self.grip.set_offset(self.offset()).1 } // translate value to offset in local coordinates @@ -285,7 +282,11 @@ impl_scope! { }; let rect = cx.align_feature(Feature::ScrollBar(self.direction()), rect, align); self.core.rect = rect; - self.grip.set_rect(cx, rect, AlignHints::NONE); + self.grip.set_track(rect); + + // We call grip.set_rect only for compliance with the widget model: + self.grip.set_rect(cx, Rect::ZERO, AlignHints::NONE); + self.min_grip_len = cx.size_cx().grip_len(); let _ = self.update_widgets(); } diff --git a/crates/kas-widgets/src/slider.rs b/crates/kas-widgets/src/slider.rs index ebfa1906a..f31fb0c92 100644 --- a/crates/kas-widgets/src/slider.rs +++ b/crates/kas-widgets/src/slider.rs @@ -299,16 +299,21 @@ impl_scope! { } fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) { + eprintln!("Slider::set_rect({rect:?}"); let align = match self.direction.is_vertical() { false => AlignPair::new(Align::Stretch, hints.vert.unwrap_or(Align::Center)), true => AlignPair::new(hints.horiz.unwrap_or(Align::Center), Align::Stretch), }; - let rect = cx.align_feature(Feature::Slider(self.direction()), rect, align); + let mut rect = cx.align_feature(Feature::Slider(self.direction()), rect, align); self.core.rect = rect; + self.grip.set_track(rect); + + // Set the grip size (we could instead call set_size but the widget + // model requires we call set_rect anyway): + rect.size.set_component(self.direction, cx.size_cx().grip_len()); self.grip.set_rect(cx, rect, AlignHints::NONE); - let mut size = rect.size; - size.set_component(self.direction, cx.size_cx().grip_len()); - let _ = self.grip.set_size_and_offset(size, self.offset()); + // Correct the position: + let _ = self.grip.set_offset(self.offset()); } fn probe(&mut self, coord: Coord) -> Id { diff --git a/crates/kas-widgets/src/splitter.rs b/crates/kas-widgets/src/splitter.rs index 44241efbf..259331327 100644 --- a/crates/kas-widgets/src/splitter.rs +++ b/crates/kas-widgets/src/splitter.rs @@ -205,9 +205,9 @@ impl_scope! { // TODO(opt): calculate all maximal sizes simultaneously let index = (n << 1) + 1; let track = setter.maximal_rect_of(&mut self.data, index); - self.grips[n].set_rect(cx, track, AlignHints::NONE); - let grip = setter.child_rect(&mut self.data, index); - let _ = self.grips[n].set_size_and_offset(grip.size, grip.pos - track.pos); + self.grips[n].set_track(track); + let rect = setter.child_rect(&mut self.data, index); + self.grips[n].set_rect(cx, rect, AlignHints::NONE); n += 1; } @@ -336,9 +336,9 @@ impl Splitter { let index = (n << 1) + 1; let track = self.grips[n].track(); - self.grips[n].set_rect(cx, track, AlignHints::NONE); - let grip = setter.child_rect(&mut self.data, index); - let _ = self.grips[n].set_size_and_offset(grip.size, grip.pos - track.pos); + self.grips[n].set_track(track); + let rect = setter.child_rect(&mut self.data, index); + let _ = self.grips[n].set_rect(cx, rect, AlignHints::NONE); n += 1; }