Skip to content

Commit

Permalink
Fix scroll pre-fetching
Browse files Browse the repository at this point in the history
  • Loading branch information
emilk committed Sep 4, 2024
1 parent 9a1069e commit a033638
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 24 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Work-in-progress. Pre-release.


### TODO/Bugs
* Fix row range prefetching vs scrolling
* Fix row range prefetching off-by-one-error
* Hierarchical headers
* Fix auto-sizing on parent resize
* Test with `egui::Sides`
Expand Down
15 changes: 15 additions & 0 deletions demo/src/table_demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ impl Default for TableDemo {
}
}

impl TableDemo {
fn was_prefetched(&self, row_nr: u64) -> bool {
self.prefetched_row_ranges
.iter()
.any(|range| range.contains(&row_nr))
}
}

impl TableDelegate for TableDemo {
fn prefetch_rows(&mut self, row_numbers: std::ops::Range<u64>) {
self.prefetched_row_ranges.push(row_numbers);
Expand All @@ -35,6 +43,13 @@ impl TableDelegate for TableDemo {

ui.add_space(4.0);

if !self.was_prefetched(row_nr) {
ui.painter()
.rect_filled(ui.max_rect(), 0.0, ui.visuals().error_fg_color);
ui.label("ERROR: row not prefetched");
return;
}

if row_nr == 0 {
ui.heading(format!("Column {col_nr}"));
} else {
Expand Down
34 changes: 21 additions & 13 deletions egui_table/src/split_scroll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,27 +75,35 @@ impl SplitScroll {

let bottom_right_rect = Rect::from_min_max(rect.min + fixed_size, rect.max);

let scroll_output = {
let scroll_offset = {
// RIGHT BOTTOM: fully scrollable.

// The entire thing is a `ScrollArea` that we then paint over.
// PROBLEM: scroll bars show up at the full rect, instead of just the bottom-right.
// We could add something like `ScrollArea::with_scroll_bar_rect(bottom_right_rect)`
let mut scroll_ui = ui.new_child(UiBuilder::new().max_rect(rect));
egui::ScrollArea::new(scroll_enabled).show(&mut scroll_ui, |ui| {
ui.set_min_size(fixed_size + scroll_content_size);

let mut shrunk_rect = ui.max_rect();
shrunk_rect.min += fixed_size;

let mut shrunk_ui = ui.new_child(UiBuilder::new().max_rect(shrunk_rect));
shrunk_ui.shrink_clip_rect(bottom_right_rect);
delegate.right_bottom_ui(&mut shrunk_ui);
})
let mut scroll_ui = ui.new_child(UiBuilder::new().max_rect(rect));
egui::ScrollArea::new(scroll_enabled)
.show_viewport(&mut scroll_ui, |ui, scroll_offset| {
ui.set_min_size(fixed_size + scroll_content_size);

let mut shrunk_rect = ui.max_rect();
shrunk_rect.min += fixed_size;

let mut shrunk_ui = ui.new_child(UiBuilder::new().max_rect(shrunk_rect));
shrunk_ui.shrink_clip_rect(bottom_right_rect);
delegate.right_bottom_ui(&mut shrunk_ui);

// It is very important that the scroll offset is synced between the
// right-bottom contents of the real scroll area,
// and the fake scroll areas we are painting later.
// The scroll offset that `ScrollArea` returns could be a newer one
// than was used for rendering, so we use the one _actually_ used for rendering instead:
scroll_offset.min
})
.inner
};

let scroll_offset = scroll_output.state.offset;

{
// LEFT TOP: Fixed
let left_top_rect = rect
Expand Down
35 changes: 25 additions & 10 deletions egui_table/src/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ impl Table {

let num_columns = self.columns.len();

table_delegate.prefetch_rows(0..self.sticky_row_heights.len() as u64);

SplitScroll {
scroll_enabled: Vec2b::new(true, true),
fixed_size: sticky_size,
Expand All @@ -186,7 +188,7 @@ impl Table {
sticky_row_y,
max_column_widths: vec![0.0; num_columns],
visible_column_lines: Default::default(),
prefetched_ranges: Default::default(),
has_prefetched: false,
},
);
});
Expand Down Expand Up @@ -217,7 +219,7 @@ struct TableSplitScrollDelegate<'a> {
/// Key is column number. The resizer is to the right of the column.
visible_column_lines: BTreeMap<usize, ColumnResizer>,

prefetched_ranges: Vec<std::ops::Range<u64>>,
has_prefetched: bool,
}

impl<'a> TableSplitScrollDelegate<'a> {
Expand Down Expand Up @@ -251,7 +253,7 @@ impl<'a> TableSplitScrollDelegate<'a> {
Rect::from_x_y_ranges(x_range, y_range)
}

fn region_ui(&mut self, ui: &mut Ui, offset: Vec2) {
fn region_ui(&mut self, ui: &mut Ui, offset: Vec2, do_prefetch: bool) {
// Find the visible range of columns and rows:
let viewport = ui.clip_rect().translate(offset);

Expand All @@ -260,10 +262,15 @@ impl<'a> TableSplitScrollDelegate<'a> {
let first_row = self.row_idx_at(viewport.min.y);
let last_row = self.row_idx_at(viewport.max.y);

let row_range = first_row..last_row + 1;
if !self.prefetched_ranges.contains(&row_range) {
if do_prefetch {
let row_range = first_row..last_row + 1;
self.table_delegate.prefetch_rows(row_range.clone());
self.prefetched_ranges.push(row_range);
self.has_prefetched = true;
} else {
debug_assert!(
self.has_prefetched,
"SplitScroll delegate methods called in unexpected ortder"
);
}

for col_nr in first_col..=last_col {
Expand Down Expand Up @@ -329,19 +336,27 @@ impl<'a> TableSplitScrollDelegate<'a> {

impl<'a> SplitScrollDelegate for TableSplitScrollDelegate<'a> {
fn left_top_ui(&mut self, ui: &mut Ui) {
self.region_ui(ui, Vec2::ZERO);
self.region_ui(ui, Vec2::ZERO, false);
}

fn right_top_ui(&mut self, ui: &mut Ui) {
self.region_ui(ui, vec2(ui.clip_rect().min.x - ui.min_rect().min.x, 0.0));
self.region_ui(
ui,
vec2(ui.clip_rect().min.x - ui.min_rect().min.x, 0.0),
false,
);
}

fn left_bottom_ui(&mut self, ui: &mut Ui) {
self.region_ui(ui, vec2(0.0, ui.clip_rect().min.y - ui.min_rect().min.y));
self.region_ui(
ui,
vec2(0.0, ui.clip_rect().min.y - ui.min_rect().min.y),
false,
);
}

fn right_bottom_ui(&mut self, ui: &mut Ui) {
self.region_ui(ui, ui.clip_rect().min - ui.min_rect().min);
self.region_ui(ui, ui.clip_rect().min - ui.min_rect().min, true);
}

fn finish(&mut self, ui: &mut Ui) {
Expand Down

0 comments on commit a033638

Please sign in to comment.