Skip to content

Commit

Permalink
Make sure scroll bars are always visible (#2371)
Browse files Browse the repository at this point in the history
* Nicer debug rectangles

* Move scrollbars into the clip-rect so they are always visible

* Improve table demo

* Add options for controlling inner and outer margin of the scroll bars

* Add line to changelog

* Update egui_extras changelog with recent Table improvements

* Refactor Table:s scroll options

* Add Table::auto_size

* Rename it auto_shrink
  • Loading branch information
emilk committed Nov 30, 2022
1 parent 85f8eeb commit 7133818
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 92 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Expand Up @@ -34,7 +34,8 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG
* Improved text rendering ([#2071](https://github.com/emilk/egui/pull/2071)).
* Less jitter when calling `Context::set_pixels_per_point` ([#2239](https://github.com/emilk/egui/pull/2239)).
* Fixed popups and color edit going outside the screen.
* Fixed keyboard support in `DragValue`. ([#2342](https://github.com/emilk/egui/pull/2342)).
* Fixed keyboard support in `DragValue` ([#2342](https://github.com/emilk/egui/pull/2342)).
* If you nest `ScrollAreas` inside each other, the inner area will now move its scroll bar so it is always visible ([#2371](https://github.com/emilk/egui/pull/2371)).


## 0.19.0 - 2022-08-20
Expand Down
46 changes: 24 additions & 22 deletions crates/egui/src/containers/scroll_area.rs
Expand Up @@ -635,23 +635,7 @@ impl Prepared {
};
}

let mut inner_rect = Rect::from_min_size(inner_rect.min, inner_size);

// The window that egui sits in can't be expanded by egui, so we need to respect it:
for d in 0..2 {
if !has_bar[d] {
// HACK for when we have a vertical-only scroll area in a top level panel,
// and that panel is not wide enough for the contents.
// This code ensures we still see the scroll bar!
let max = ui.input().screen_rect().max[d]
- current_bar_use[d]
- ui.spacing().item_spacing[d];
inner_rect.max[d] = inner_rect.max[d].at_most(max);
// TODO(emilk): maybe auto-enable horizontal/vertical scrolling if this limit is reached
}
}

inner_rect
Rect::from_min_size(inner_rect.min, inner_size)
};

let outer_rect = Rect::from_min_size(inner_rect.min, inner_rect.size() + current_bar_use);
Expand Down Expand Up @@ -703,13 +687,29 @@ impl Prepared {
continue;
}

// margin between contents and scroll bar
let margin = animation_t * ui.spacing().item_spacing.x;
let min_cross = inner_rect.max[1 - d] + margin; // left of vertical scroll (d == 1)
let max_cross = outer_rect.max[1 - d]; // right of vertical scroll (d == 1)
// margin on either side of the scroll bar
let inner_margin = animation_t * ui.spacing().scroll_bar_inner_margin;
let outer_margin = animation_t * ui.spacing().scroll_bar_outer_margin;
let mut min_cross = inner_rect.max[1 - d] + inner_margin; // left of vertical scroll (d == 1)
let mut max_cross = outer_rect.max[1 - d] - outer_margin; // right of vertical scroll (d == 1)
let min_main = inner_rect.min[d]; // top of vertical scroll (d == 1)
let max_main = inner_rect.max[d]; // bottom of vertical scroll (d == 1)

if ui.clip_rect().max[1 - d] < max_cross + 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 = max_cross - min_cross;
max_cross = ui.clip_rect().max[1 - d] - outer_margin;
min_cross = max_cross - width;
}

let outer_scroll_rect = if d == 0 {
Rect::from_min_max(
pos2(inner_rect.left(), min_cross),
Expand Down Expand Up @@ -865,5 +865,7 @@ impl Prepared {

/// Width of a vertical scrollbar, or height of a horizontal scroll bar
fn max_scroll_bar_width_with_margin(ui: &Ui) -> f32 {
ui.spacing().item_spacing.x + ui.spacing().scroll_bar_width
ui.spacing().scroll_bar_inner_margin
+ ui.spacing().scroll_bar_width
+ ui.spacing().scroll_bar_outer_margin
}
7 changes: 6 additions & 1 deletion crates/egui/src/painter.rs
Expand Up @@ -208,7 +208,12 @@ impl Painter {
impl Painter {
#[allow(clippy::needless_pass_by_value)]
pub fn debug_rect(&self, rect: Rect, color: Color32, text: impl ToString) {
self.rect_stroke(rect, 0.0, (1.0, color));
self.rect(
rect,
0.0,
color.additive().linear_multiply(0.015),
(1.0, color),
);
self.text(
rect.min,
Align2::LEFT_TOP,
Expand Down
19 changes: 18 additions & 1 deletion crates/egui/src/style.rs
Expand Up @@ -292,6 +292,11 @@ pub struct Spacing {
pub combo_height: f32,

pub scroll_bar_width: f32,

/// Margin between contents and scroll bar.
pub scroll_bar_inner_margin: f32,
/// Margin between scroll bar and the outer container (e.g. right of a vertical scroll bar).
pub scroll_bar_outer_margin: f32,
}

impl Spacing {
Expand Down Expand Up @@ -658,6 +663,8 @@ impl Default for Spacing {
tooltip_width: 600.0,
combo_height: 200.0,
scroll_bar_width: 8.0,
scroll_bar_inner_margin: 4.0,
scroll_bar_outer_margin: 0.0,
indent_ends_with_horizontal_line: false,
}
}
Expand Down Expand Up @@ -941,6 +948,8 @@ impl Spacing {
indent_ends_with_horizontal_line,
combo_height,
scroll_bar_width,
scroll_bar_inner_margin,
scroll_bar_outer_margin,
} = self;

ui.add(slider_vec2(item_spacing, 0.0..=20.0, "Item spacing"));
Expand Down Expand Up @@ -1021,7 +1030,15 @@ impl Spacing {
});
ui.horizontal(|ui| {
ui.add(DragValue::new(scroll_bar_width).clamp_range(0.0..=32.0));
ui.label("Scroll-bar width width");
ui.label("Scroll-bar width");
});
ui.horizontal(|ui| {
ui.add(DragValue::new(scroll_bar_inner_margin).clamp_range(0.0..=32.0));
ui.label("Scroll-bar inner margin");
});
ui.horizontal(|ui| {
ui.add(DragValue::new(scroll_bar_outer_margin).clamp_range(0.0..=32.0));
ui.label("Scroll-bar outer margin");
});

ui.horizontal(|ui| {
Expand Down
10 changes: 10 additions & 0 deletions crates/egui_demo_lib/src/demo/scrolling.rs
Expand Up @@ -7,6 +7,7 @@ enum ScrollDemo {
ManyLines,
LargeCanvas,
StickToEnd,
Bidirectional,
}

impl Default for ScrollDemo {
Expand Down Expand Up @@ -55,6 +56,7 @@ impl super::View for Scrolling {
"Scroll a large canvas",
);
ui.selectable_value(&mut self.demo, ScrollDemo::StickToEnd, "Stick to end");
ui.selectable_value(&mut self.demo, ScrollDemo::Bidirectional, "Bidirectional");
});
ui.separator();
match self.demo {
Expand All @@ -70,6 +72,14 @@ impl super::View for Scrolling {
ScrollDemo::StickToEnd => {
self.scroll_stick_to.ui(ui);
}
ScrollDemo::Bidirectional => {
egui::ScrollArea::both().show(ui, |ui| {
ui.style_mut().wrap = Some(false);
for _ in 0..100 {
ui.label(crate::LOREM_IPSUM);
}
});
}
}
}
}
Expand Down
11 changes: 7 additions & 4 deletions crates/egui_demo_lib/src/demo/table_demo.rs
Expand Up @@ -47,7 +47,7 @@ impl super::Demo for TableDemo {
}
}

const NUM_MANUAL_ROWS: usize = 32;
const NUM_MANUAL_ROWS: usize = 20;

impl super::View for TableDemo {
fn ui(&mut self, ui: &mut egui::Ui) {
Expand Down Expand Up @@ -101,11 +101,13 @@ impl super::View for TableDemo {
// Leave room for the source code link after the table demo:
use egui_extras::{Size, StripBuilder};
StripBuilder::new(ui)
.size(Size::remainder()) // for the table
.size(Size::remainder().at_least(100.0)) // for the table
.size(Size::exact(10.0)) // for the source code link
.vertical(|mut strip| {
strip.cell(|ui| {
self.table_ui(ui);
egui::ScrollArea::horizontal().show(ui, |ui| {
self.table_ui(ui);
});
});
strip.cell(|ui| {
ui.vertical_centered(|ui| {
Expand Down Expand Up @@ -133,7 +135,8 @@ impl TableDemo {
.resizable(true)
.clip(true),
)
.column(Column::remainder());
.column(Column::remainder())
.min_scrolled_height(0.0);

if let Some(row_nr) = self.scroll_to_row.take() {
table = table.scroll_to_row(row_nr, None);
Expand Down
15 changes: 15 additions & 0 deletions crates/egui_extras/CHANGELOG.md
Expand Up @@ -5,6 +5,21 @@ All notable changes to the `egui_extras` integration will be noted in this file.
## Unreleased
* Added `TableBuilder::vertical_scroll_offset`: method to set vertical scroll offset position for a table ([#1946](https://github.com/emilk/egui/pull/1946)).
* Added `RetainedImage::from_svg_bytes_with_size` to be able to specify a size for SVGs to be rasterized at.
* Big `Table` improvements ([#2369](https://github.com/emilk/egui/pull/2369)):
* Double-click column separators to auto-size the column.
* All `Table` now store state. You may see warnings about reused table ids. Use `ui.push_id` to fix this.
* `TableBuilder::column` takes a `Column` instead of a `Size`.
* `Column` controls default size, size range, resizing, and clipping of columns.
* `Column::auto` will pick a size automatically
* Added `Table::scroll_to_row`.
* Added `Table::min_scrolled_height` and `Table::max_scroll_height`.
* Added `TableBody::max_size`.
* `Table::scroll` renamed to `Table::vscroll`.
* `egui_extras::Strip` now has `clip: false` by default.
* Fix bugs when putting `Table` inside of a horizontal `ScrollArea`.
* Many other bug fixes.
* Add `Table::auto_shrink` - set to `false` to expand table to fit its containing `Ui` ([#2371](https://github.com/emilk/egui/pull/2371)).


## 0.19.0 - 2022-08-20
* MSRV (Minimum Supported Rust Version) is now `1.61.0` ([#1846](https://github.com/emilk/egui/pull/1846)).
Expand Down

0 comments on commit 7133818

Please sign in to comment.