Skip to content

Commit

Permalink
Implement table row selection
Browse files Browse the repository at this point in the history
  • Loading branch information
laurooyen committed Sep 16, 2023
1 parent a3dfd08 commit 058ccbd
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 79 deletions.
85 changes: 62 additions & 23 deletions crates/egui_demo_lib/src/demo/table_demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub struct TableDemo {
num_rows: usize,
scroll_to_row_slider: usize,
scroll_to_row: Option<usize>,
selection: std::collections::HashSet<usize>,
checked: bool,
}

impl Default for TableDemo {
Expand All @@ -26,6 +28,8 @@ impl Default for TableDemo {
num_rows: 10_000,
scroll_to_row_slider: 0,
scroll_to_row: None,
selection: Default::default(),
checked: false,
}
}
}
Expand Down Expand Up @@ -124,10 +128,12 @@ impl TableDemo {
let text_height = egui::TextStyle::Body.resolve(ui.style()).size;

let mut table = TableBuilder::new(ui)
.sense(egui::Sense::click())
.striped(self.striped)
.resizable(self.resizable)
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
.column(Column::auto())
.column(Column::auto())
.column(Column::initial(100.0).range(40.0..=300.0))
.column(Column::initial(100.0).at_least(40.0).clip(true))
.column(Column::remainder())
Expand All @@ -142,6 +148,9 @@ impl TableDemo {
header.col(|ui| {
ui.strong("Row");
});
header.col(|ui| {
ui.strong("Interaction");
});
header.col(|ui| {
ui.strong("Expanding content");
});
Expand All @@ -158,9 +167,14 @@ impl TableDemo {
let is_thick = thick_row(row_index);
let row_height = if is_thick { 30.0 } else { 18.0 };
body.row(row_height, |mut row| {
row.select(self.selection.contains(&row_index));

row.col(|ui| {
ui.label(row_index.to_string());
});
row.col(|ui| {
ui.checkbox(&mut self.checked, "Click me");
});
row.col(|ui| {
expanding_content(ui);
});
Expand All @@ -175,14 +189,22 @@ impl TableDemo {
ui.label("Normal row");
}
});

self.toggle_row_selection(row_index, row.response());
});
}
}
DemoType::ManyHomogeneous => {
body.rows(text_height, self.num_rows, |row_index, mut row| {
body.rows(text_height, self.num_rows, |mut row| {
let row_index = row.index();
row.select(self.selection.contains(&row_index));

row.col(|ui| {
ui.label(row_index.to_string());
});
row.col(|ui| {
ui.checkbox(&mut self.checked, "Click me");
});
row.col(|ui| {
expanding_content(ui);
});
Expand All @@ -194,6 +216,8 @@ impl TableDemo {
egui::Label::new("Thousands of rows of even height").wrap(false),
);
});

self.toggle_row_selection(row_index, row.response());
});
}
DemoType::ManyHeterogenous => {
Expand All @@ -204,31 +228,46 @@ impl TableDemo {
18.0
}
}
body.heterogeneous_rows(
(0..self.num_rows).map(row_thickness),
|row_index, mut row| {
row.col(|ui| {
ui.label(row_index.to_string());
});
row.col(|ui| {
expanding_content(ui);
});
row.col(|ui| {
ui.label(long_text(row_index));
});
row.col(|ui| {
ui.style_mut().wrap = Some(false);
if thick_row(row_index) {
ui.heading("Extra thick row");
} else {
ui.label("Normal row");
}
});
},
);
body.heterogeneous_rows((0..self.num_rows).map(row_thickness), |mut row| {
let row_index = row.index();
row.select(self.selection.contains(&row_index));

row.col(|ui| {
ui.label(row_index.to_string());
});
row.col(|ui| {
ui.checkbox(&mut self.checked, "Click me");
});
row.col(|ui| {
expanding_content(ui);
});
row.col(|ui| {
ui.label(long_text(row_index));
});
row.col(|ui| {
ui.style_mut().wrap = Some(false);
if thick_row(row_index) {
ui.heading("Extra thick row");
} else {
ui.label("Normal row");
}
});

self.toggle_row_selection(row_index, row.response());
});
}
});
}

fn toggle_row_selection(&mut self, row_index: usize, row_response: egui::Response) {
if row_response.clicked() {
if self.selection.contains(&row_index) {
self.selection.remove(&row_index);
} else {
self.selection.insert(row_index);
}
}
}
}

fn expanding_content(ui: &mut egui::Ui) {
Expand Down
33 changes: 26 additions & 7 deletions crates/egui_extras/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,16 @@ pub struct StripLayout<'l> {
max: Pos2,

cell_layout: egui::Layout,
sense: Sense,
}

impl<'l> StripLayout<'l> {
pub(crate) fn new(ui: &'l mut Ui, direction: CellDirection, cell_layout: egui::Layout) -> Self {
pub(crate) fn new(
ui: &'l mut Ui,
direction: CellDirection,
cell_layout: egui::Layout,
sense: Sense,
) -> Self {
let rect = ui.available_rect_before_wrap();
let pos = rect.left_top();

Expand All @@ -52,6 +58,7 @@ impl<'l> StripLayout<'l> {
cursor: pos,
max: pos,
cell_layout,
sense,
}
}

Expand Down Expand Up @@ -96,21 +103,33 @@ impl<'l> StripLayout<'l> {
&mut self,
clip: bool,
striped: bool,
highlighted: bool,
width: CellSize,
height: CellSize,
add_cell_contents: impl FnOnce(&mut Ui),
) -> (Rect, Response) {
let max_rect = self.cell_rect(&width, &height);

// Make sure we don't have a gap in the stripe/frame/selection background:
let gapless_rect = || max_rect.expand2(0.5 * self.ui.spacing().item_spacing);

if striped {
// Make sure we don't have a gap in the stripe background:
let stripe_rect = max_rect.expand2(0.5 * self.ui.spacing().item_spacing);
self.ui.painter().rect_filled(
gapless_rect(),
egui::Rounding::ZERO,
self.ui.visuals().faint_bg_color,
);
}

self.ui
.painter()
.rect_filled(stripe_rect, 0.0, self.ui.visuals().faint_bg_color);
if highlighted {
self.ui.painter().rect_filled(
gapless_rect(),
egui::Rounding::ZERO,
self.ui.visuals().selection.bg_fill,
);
}

let response = self.ui.allocate_rect(max_rect, self.sense);
let used_rect = self.cell(clip, max_rect, add_cell_contents);

self.set_pos(max_rect);
Expand All @@ -121,7 +140,7 @@ impl<'l> StripLayout<'l> {
max_rect.union(used_rect)
};

let response = self.ui.allocate_rect(allocation_rect, Sense::hover());
let response = response.with_new_rect(allocation_rect);

(used_rect, response)
}
Expand Down
27 changes: 22 additions & 5 deletions crates/egui_extras/src/strip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub struct StripBuilder<'a> {
sizing: Sizing,
clip: bool,
cell_layout: egui::Layout,
sense: egui::Sense,
}

impl<'a> StripBuilder<'a> {
Expand All @@ -55,8 +56,9 @@ impl<'a> StripBuilder<'a> {
Self {
ui,
sizing: Default::default(),
cell_layout,
clip: false,
cell_layout,
sense: egui::Sense::hover(),
}
}

Expand All @@ -72,6 +74,12 @@ impl<'a> StripBuilder<'a> {
self
}

/// What should strip cells sense for? Default: [`egui::Sense::hover()`].
pub fn sense(mut self, sense: egui::Sense) -> Self {
self.sense = sense;
self
}

/// Allocate space for one column/row.
pub fn size(mut self, size: Size) -> Self {
self.sizing.add(size);
Expand All @@ -98,7 +106,12 @@ impl<'a> StripBuilder<'a> {
self.ui.available_rect_before_wrap().width(),
self.ui.spacing().item_spacing.x,
);
let mut layout = StripLayout::new(self.ui, CellDirection::Horizontal, self.cell_layout);
let mut layout = StripLayout::new(
self.ui,
CellDirection::Horizontal,
self.cell_layout,
self.sense,
);
strip(Strip {
layout: &mut layout,
direction: CellDirection::Horizontal,
Expand All @@ -121,7 +134,12 @@ impl<'a> StripBuilder<'a> {
self.ui.available_rect_before_wrap().height(),
self.ui.spacing().item_spacing.y,
);
let mut layout = StripLayout::new(self.ui, CellDirection::Vertical, self.cell_layout);
let mut layout = StripLayout::new(
self.ui,
CellDirection::Vertical,
self.cell_layout,
self.sense,
);
strip(Strip {
layout: &mut layout,
direction: CellDirection::Vertical,
Expand Down Expand Up @@ -167,9 +185,8 @@ impl<'a, 'b> Strip<'a, 'b> {
#[cfg_attr(debug_assertions, track_caller)]
pub fn cell(&mut self, add_contents: impl FnOnce(&mut Ui)) {
let (width, height) = self.next_cell_size();
let striped = false;
self.layout
.add(self.clip, striped, width, height, add_contents);
.add(self.clip, false, false, width, height, add_contents);
}

/// Add an empty cell.
Expand Down
Loading

0 comments on commit 058ccbd

Please sign in to comment.