Fix scroll handle extending outside of `ScrollArea` (#5286)
* [x] I have followed the instructions in the PR template * Closes https://github.com/emilk/egui/issues/3734 ## Info This PR addresses an issue where resizing a scroll handle can lead to unwanted overlap. It is also happening on the egui-demo.  ## Cause *Note: The following explanation assumes a vertical scroll; however, the logic applies equally to horizontal scrolling.* When the scroll handle is positioned at the top or bottom of the scroll area and the handle is resized to fit the minimum handle size, there is a risk of overlap. This occurs if the handle’s new size extends beyond the bounds of the scroll area. ## Proposed Solution 1. Check whether increasing the handle size will cause it to overlap with the scroll area. 2. If an overlap is detected, adjust the handle’s center position by the overlap amount, moving it towards the center of the scroll area.
This commit is contained in:
parent
159ccb2fef
commit
1dea8fac9b
|
|
@ -1,8 +1,8 @@
|
|||
#![allow(clippy::needless_range_loop)]
|
||||
|
||||
use crate::{
|
||||
emath, epaint, lerp, pass_state, pos2, remap, remap_clamp, vec2, Context, Id, NumExt, Pos2,
|
||||
Rangef, Rect, Sense, Ui, UiBuilder, UiKind, UiStackInfo, Vec2, Vec2b,
|
||||
emath, epaint, lerp, pass_state, pos2, remap, remap_clamp, Context, Id, NumExt, Pos2, Rangef,
|
||||
Rect, Sense, Ui, UiBuilder, UiKind, UiStackInfo, Vec2, Vec2b,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
|
@ -1090,21 +1090,35 @@ impl Prepared {
|
|||
)
|
||||
};
|
||||
|
||||
let handle_rect = if d == 0 {
|
||||
Rect::from_min_max(
|
||||
pos2(from_content(state.offset.x), cross.min),
|
||||
pos2(from_content(state.offset.x + inner_rect.width()), cross.max),
|
||||
)
|
||||
} else {
|
||||
Rect::from_min_max(
|
||||
pos2(cross.min, from_content(state.offset.y)),
|
||||
pos2(
|
||||
cross.max,
|
||||
from_content(state.offset.y + inner_rect.height()),
|
||||
),
|
||||
)
|
||||
let calculate_handle_rect = |d, offset: &Vec2| {
|
||||
let handle_size = if d == 0 {
|
||||
from_content(offset.x + inner_rect.width()) - from_content(offset.x)
|
||||
} else {
|
||||
from_content(offset.y + inner_rect.height()) - from_content(offset.y)
|
||||
}
|
||||
.max(scroll_style.handle_min_length);
|
||||
|
||||
let handle_start_point = remap_clamp(
|
||||
offset[d],
|
||||
0.0..=max_offset[d],
|
||||
scroll_bar_rect.min[d]..=(scroll_bar_rect.max[d] - handle_size),
|
||||
);
|
||||
|
||||
if d == 0 {
|
||||
Rect::from_min_max(
|
||||
pos2(handle_start_point, cross.min),
|
||||
pos2(handle_start_point + handle_size, cross.max),
|
||||
)
|
||||
} else {
|
||||
Rect::from_min_max(
|
||||
pos2(cross.min, handle_start_point),
|
||||
pos2(cross.max, handle_start_point + handle_size),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let handle_rect = calculate_handle_rect(d, &state.offset);
|
||||
|
||||
let interact_id = id.with(d);
|
||||
let sense = if self.scrolling_enabled {
|
||||
Sense::click_and_drag()
|
||||
|
|
@ -1133,8 +1147,8 @@ impl Prepared {
|
|||
let new_handle_top = pointer_pos[d] - *scroll_start_offset_from_top_left;
|
||||
state.offset[d] = remap(
|
||||
new_handle_top,
|
||||
scroll_bar_rect.min[d]..=scroll_bar_rect.max[d],
|
||||
0.0..=content_size[d],
|
||||
scroll_bar_rect.min[d]..=(scroll_bar_rect.max[d] - handle_rect.size()[d]),
|
||||
0.0..=max_offset[d],
|
||||
);
|
||||
|
||||
// some manual action taken, scroll not stuck
|
||||
|
|
@ -1154,31 +1168,7 @@ impl Prepared {
|
|||
|
||||
if ui.is_rect_visible(outer_scroll_bar_rect) {
|
||||
// Avoid frame-delay by calculating a new handle rect:
|
||||
let mut handle_rect = if d == 0 {
|
||||
Rect::from_min_max(
|
||||
pos2(from_content(state.offset.x), cross.min),
|
||||
pos2(from_content(state.offset.x + inner_rect.width()), cross.max),
|
||||
)
|
||||
} else {
|
||||
Rect::from_min_max(
|
||||
pos2(cross.min, from_content(state.offset.y)),
|
||||
pos2(
|
||||
cross.max,
|
||||
from_content(state.offset.y + inner_rect.height()),
|
||||
),
|
||||
)
|
||||
};
|
||||
let min_handle_size = scroll_style.handle_min_length;
|
||||
if handle_rect.size()[d] < min_handle_size {
|
||||
handle_rect = Rect::from_center_size(
|
||||
handle_rect.center(),
|
||||
if d == 0 {
|
||||
vec2(min_handle_size, handle_rect.size().y)
|
||||
} else {
|
||||
vec2(handle_rect.size().x, min_handle_size)
|
||||
},
|
||||
);
|
||||
}
|
||||
let handle_rect = calculate_handle_rect(d, &state.offset);
|
||||
|
||||
let visuals = if scrolling_enabled {
|
||||
// Pick visuals based on interaction with the handle.
|
||||
|
|
|
|||
Loading…
Reference in New Issue