Skip to content
Merged
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
93 changes: 46 additions & 47 deletions crates/egui/src/containers/scroll_area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1241,70 +1241,77 @@ impl Prepared {
continue;
}

let interact_id = id.with(d);

// Margin on either side of the scroll bar:
let inner_margin = show_factor * scroll_style.bar_inner_margin;
let outer_margin = show_factor * scroll_style.bar_outer_margin;

// bottom of a horizontal scroll (d==0).
// right of a vertical scroll (d==1).
let mut max_cross = outer_rect.max[1 - d] - outer_margin;

if ui.clip_rect().max[1 - d] - outer_margin < max_cross {
// Move the scrollbar so it is visible. This is needed in some cases.
// For instance:
// * When we have a vertical-only scroll area in a top level panel,
// and that panel is not wide enough for the contents.
// * When one ScrollArea is nested inside another, and the outer
// is scrolled so that the scroll-bars of the inner ScrollArea (us)
// is outside the clip rectangle.
// Really this should use the tighter clip_rect that ignores clip_rect_margin, but we don't store that.
// clip_rect_margin is quite a hack. It would be nice to get rid of it.
max_cross = ui.clip_rect().max[1 - d] - outer_margin;
}

let full_width = scroll_style.bar_width;

// The bounding rect of a fully visible bar.
// When we hover this area, we should show the full bar:
let max_bar_rect = if d == 0 {
outer_rect.with_min_y(max_cross - full_width)
} else {
outer_rect.with_min_x(max_cross - full_width)
};

let sense = if scroll_source.scroll_bar && ui.is_enabled() {
Sense::click_and_drag()
} else {
Sense::hover()
};

// We always sense interaction with the full width, even if we antimate it growing/shrinking.
// This is to present a more consistent target for our hit test code,
// and to avoid producing jitter in "thin widget" heuristics there.
// Also: it make sense to detect any hover where the scroll bar _will_ be.
let response = ui.interact(max_bar_rect, interact_id, sense);

// top/bottom of a horizontal scroll (d==0).
// left/rigth of a vertical scroll (d==1).
let mut cross = if scroll_style.floating {
// The bounding rect of a fully visible bar.
// When we hover this area, we should show the full bar:
let max_bar_rect = if d == 0 {
outer_rect.with_min_y(outer_rect.max.y - outer_margin - scroll_style.bar_width)
} else {
outer_rect.with_min_x(outer_rect.max.x - outer_margin - scroll_style.bar_width)
};

let is_hovering_bar_area = is_hovering_outer_rect
&& ui.rect_contains_pointer(max_bar_rect)
&& !is_dragging_background
|| state.scroll_bar_interaction[d];
let cross = if scroll_style.floating {
let is_hovering_bar_area = response.hovered() || state.scroll_bar_interaction[d];

let is_hovering_bar_area_t = ui
.ctx()
.animate_bool_responsive(id.with((d, "bar_hover")), is_hovering_bar_area);

let width = show_factor
* lerp(
scroll_style.floating_width..=scroll_style.bar_width,
scroll_style.floating_width..=full_width,
is_hovering_bar_area_t,
);

let max_cross = outer_rect.max[1 - d] - outer_margin;
let min_cross = max_cross - width;
Rangef::new(min_cross, max_cross)
} else {
let min_cross = inner_rect.max[1 - d] + inner_margin;
let max_cross = outer_rect.max[1 - d] - outer_margin;
Rangef::new(min_cross, max_cross)
};

if ui.clip_rect().max[1 - d] < cross.max + outer_margin {
// Move the scrollbar so it is visible. This is needed in some cases.
// For instance:
// * When we have a vertical-only scroll area in a top level panel,
// and that panel is not wide enough for the contents.
// * When one ScrollArea is nested inside another, and the outer
// is scrolled so that the scroll-bars of the inner ScrollArea (us)
// is outside the clip rectangle.
// Really this should use the tighter clip_rect that ignores clip_rect_margin, but we don't store that.
// clip_rect_margin is quite a hack. It would be nice to get rid of it.
let width = cross.max - cross.min;
cross.max = ui.clip_rect().max[1 - d] - outer_margin;
cross.min = cross.max - width;
}

let outer_scroll_bar_rect = if d == 0 {
Rect::from_min_max(
pos2(scroll_bar_rect.left(), cross.min),
pos2(scroll_bar_rect.right(), cross.max),
)
Rect::from_x_y_ranges(scroll_bar_rect.x_range(), cross)
} else {
Rect::from_min_max(
pos2(cross.min, scroll_bar_rect.top()),
pos2(cross.max, scroll_bar_rect.bottom()),
)
Rect::from_x_y_ranges(cross, scroll_bar_rect.y_range())
};

let from_content = |content| {
Expand Down Expand Up @@ -1344,14 +1351,6 @@ impl Prepared {

let handle_rect = calculate_handle_rect(d, &state.offset);

let interact_id = id.with(d);
let sense = if scroll_source.scroll_bar && ui.is_enabled() {
Sense::click_and_drag()
} else {
Sense::hover()
};
let response = ui.interact(outer_scroll_bar_rect, interact_id, sense);

state.scroll_bar_interaction[d] = response.hovered() || response.dragged();

if let Some(pointer_pos) = response.interact_pointer_pos() {
Expand Down