From cad34b871452cf29c762db98c45724b4128be547 Mon Sep 17 00:00:00 2001 From: v0x0g Date: Mon, 15 Jul 2024 19:19:34 +1000 Subject: [PATCH] Add `Ui::columns_const()` (#4764) # Changes - Adds a new function `egui::Ui::columns_const()`, which is the same as `egui::Ui::columns()` except that it uses a `const` parameter for the column count. - Backed by an array `[Ui; NUM_COL] instead of a `Vec`, so fewer allocations - Inner closure takes in an array reference, instead of a slice reference. This makes it possible to use pattern destructuring on the columns, as shown in the example, and makes it more ergonomic to use # Example ```rust // ORIGINAL ui.columns(2, |cols| { cols[0].label("one"); cols[1].label("two"); }); // NEW ui.columns_const(|[a,b]| { a.label("one"); b.label("two"); }); ``` # Checks - [X] `cargo fmt` - [X] `cargo clippy` - [X] `./scripts/check.sh` - [X] Docs - [ ] Review --- crates/egui/src/ui.rs | 52 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 74eecc846e43..2454c7cb1b62 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -2389,6 +2389,58 @@ impl Ui { result } + /// Temporarily split a [`Ui`] into several columns. + /// + /// The same as [`Self::columns()`], but uses a constant for the column count. + /// This allows for compile-time bounds checking, and makes the compiler happy. + /// + /// ``` + /// # egui::__run_test_ui(|ui| { + /// ui.columns_const(|[col_1, col_2]| { + /// col_1.label("First column"); + /// col_2.label("Second column"); + /// }); + /// # }); + /// ``` + #[inline] + pub fn columns_const( + &mut self, + add_contents: impl FnOnce(&mut [Self; NUM_COL]) -> R, + ) -> R { + // TODO(emilk): ensure there is space + let spacing = self.spacing().item_spacing.x; + let total_spacing = spacing * (NUM_COL as f32 - 1.0); + let column_width = (self.available_width() - total_spacing) / (NUM_COL as f32); + let top_left = self.cursor().min; + + let mut columns = std::array::from_fn(|col_idx| { + let pos = top_left + vec2((col_idx as f32) * (column_width + spacing), 0.0); + let child_rect = Rect::from_min_max( + pos, + pos2(pos.x + column_width, self.max_rect().right_bottom().y), + ); + let mut column_ui = + self.child_ui(child_rect, Layout::top_down_justified(Align::LEFT), None); + column_ui.set_width(column_width); + column_ui + }); + let result = add_contents(&mut columns); + + let mut max_column_width = column_width; + let mut max_height = 0.0; + for column in &columns { + max_column_width = max_column_width.max(column.min_rect().width()); + max_height = column.min_size().y.max(max_height); + } + + // Make sure we fit everything next frame: + let total_required_width = total_spacing + max_column_width * (NUM_COL as f32); + + let size = vec2(self.available_width().max(total_required_width), max_height); + self.advance_cursor_after_rect(Rect::from_min_size(top_left, size)); + result + } + /// Create something that can be drag-and-dropped. /// /// The `id` needs to be globally unique.