Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Revise GripPart position/size setting #463

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 48 additions & 28 deletions crates/kas-widgets/src/grip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand All @@ -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) {}
}

Expand Down Expand Up @@ -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`
Expand All @@ -145,26 +143,48 @@ 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
}

/// 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)
}

/// 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;
Expand Down
15 changes: 8 additions & 7 deletions crates/kas-widgets/src/scroll_bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
}
Expand Down
13 changes: 9 additions & 4 deletions crates/kas-widgets/src/slider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
12 changes: 6 additions & 6 deletions crates/kas-widgets/src/splitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -336,9 +336,9 @@ impl<C: Collection, D: Directional> Splitter<C, D> {

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;
}
Expand Down
Loading