Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add egui::Sides for adding UI on left and right sides #5036

Merged
merged 10 commits into from
Sep 2, 2024
Merged
2 changes: 2 additions & 0 deletions crates/egui/src/containers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod panel;
pub mod popup;
pub(crate) mod resize;
pub mod scroll_area;
mod sides;
pub(crate) mod window;

pub use {
Expand All @@ -21,5 +22,6 @@ pub use {
popup::*,
resize::Resize,
scroll_area::ScrollArea,
sides::Sides,
window::Window,
};
120 changes: 120 additions & 0 deletions crates/egui/src/containers/sides.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use emath::Align;

use crate::{Layout, Ui, UiBuilder};

/// Put some widgets on the left and right sides of a ui.
///
/// The result will look like this:
/// ```text
/// parent Ui
/// ______________________________________________________
/// | | | | ^
/// | -> left widgets -> | gap | <- right widgets <- | | height
/// |____________________| |_____________________| v
/// | |
/// | |
/// ```
///
/// The width of the gap is dynamic, based on the max width of the parent [`Ui`].
/// When the parent is being auto-sized ([`Ui::is_sizing_pass`]) the gap will be as small as possible.
///
/// If the parent is not wide enough to fit all widgets, the parent will be expanded to the right.
///
/// The left widgets are first added to the ui, left-to-right.
/// Then the right widgets are added, right-to-left.
///
/// ```
/// # egui::__run_test_ui(|ui| {
/// egui::containers::Sides::new().show(ui,
/// |ui| {
/// ui.label("Left");
/// },
/// |ui| {
/// ui.label("Right");
/// }
/// );
/// # });
/// ```
#[must_use = "You should call sides.show()"]
#[derive(Clone, Copy, Debug, Default)]
pub struct Sides {
height: Option<f32>,
spacing: Option<f32>,
}

impl Sides {
#[inline]
pub fn new() -> Self {
Default::default()
}

/// The minimum height of the sides.
///
/// The content will be centered vertically within this height.
/// The default height is [`crate::Spacing::interact_size`]`.y`.
#[inline]
pub fn height(mut self, height: f32) -> Self {
self.height = Some(height);
self
}

/// The horizontal spacing between the left and right UIs.
///
/// This is the minimum gap.
/// The default is [`crate::Spacing::item_spacing`]`.x`.
#[inline]
pub fn spacing(mut self, spacing: f32) -> Self {
self.spacing = Some(spacing);
self
}

pub fn show(
self,
ui: &mut Ui,
add_left: impl FnOnce(&mut Ui),
add_right: impl FnOnce(&mut Ui),
) {
let Self { height, spacing } = self;
let height = height.unwrap_or_else(|| ui.spacing().interact_size.y);
let spacing = spacing.unwrap_or_else(|| ui.spacing().item_spacing.x);

let mut top_rect = ui.max_rect();
top_rect.max.y = top_rect.min.y + height;

let left_rect = {
let left_max_rect = top_rect;
let mut left_ui = ui.new_child(
UiBuilder::new()
.max_rect(left_max_rect)
.layout(Layout::left_to_right(Align::Center)),
);
add_left(&mut left_ui);
left_ui.min_rect()
};

let right_rect = {
let right_max_rect = top_rect.with_min_x(left_rect.max.x);
let mut right_ui = ui.new_child(
UiBuilder::new()
.max_rect(right_max_rect)
.layout(Layout::right_to_left(Align::Center)),
);
add_right(&mut right_ui);
right_ui.min_rect()
};

let mut final_rect = left_rect.union(right_rect);
let min_width = left_rect.width() + spacing + right_rect.width();

if ui.is_sizing_pass() {
// Make as small as possible:
final_rect.max.x = left_rect.min.x + min_width;
} else {
// If the rects overlap, make sure we expand the allocated rect so that the parent
// ui knows we overflowed, and resizes:
final_rect.max.x = final_rect.max.x.max(left_rect.min.x + min_width);
}

ui.advance_cursor_after_rect(final_rect);
}
}
File renamed without changes.
2 changes: 1 addition & 1 deletion crates/egui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ pub use self::{
painter::Painter,
response::{InnerResponse, Response},
sense::Sense,
style::{FontSelection, Style, TextStyle, Visuals},
style::{FontSelection, Spacing, Style, TextStyle, Visuals},
text::{Galley, TextFormat},
ui::Ui,
ui_builder::UiBuilder,
Expand Down
File renamed without changes.
33 changes: 30 additions & 3 deletions crates/egui_demo_lib/src/demo/table_demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub struct TableDemo {
scroll_to_row: Option<usize>,
selection: std::collections::HashSet<usize>,
checked: bool,
reversed: bool,
}

impl Default for TableDemo {
Expand All @@ -34,6 +35,7 @@ impl Default for TableDemo {
scroll_to_row: None,
selection: Default::default(),
checked: false,
reversed: false,
}
}
}
Expand Down Expand Up @@ -173,7 +175,16 @@ impl TableDemo {
table
.header(20.0, |mut header| {
header.col(|ui| {
ui.strong("Row");
egui::Sides::new().show(
ui,
|ui| {
ui.strong("Row");
},
|ui| {
self.reversed ^=
ui.button(if self.reversed { "⬆" } else { "⬇" }).clicked();
},
);
});
header.col(|ui| {
ui.strong("Clipped text");
Expand All @@ -191,6 +202,12 @@ impl TableDemo {
.body(|mut body| match self.demo {
DemoType::Manual => {
for row_index in 0..NUM_MANUAL_ROWS {
let row_index = if self.reversed {
NUM_MANUAL_ROWS - 1 - row_index
} else {
row_index
};

let is_thick = thick_row(row_index);
let row_height = if is_thick { 30.0 } else { 18.0 };
body.row(row_height, |mut row| {
Expand Down Expand Up @@ -223,7 +240,12 @@ impl TableDemo {
}
DemoType::ManyHomogeneous => {
body.rows(text_height, self.num_rows, |mut row| {
let row_index = row.index();
let row_index = if self.reversed {
self.num_rows - 1 - row.index()
} else {
row.index()
};

row.set_selected(self.selection.contains(&row_index));

row.col(|ui| {
Expand Down Expand Up @@ -251,7 +273,12 @@ impl TableDemo {
DemoType::ManyHeterogenous => {
let row_height = |i: usize| if thick_row(i) { 30.0 } else { 18.0 };
body.heterogeneous_rows((0..self.num_rows).map(row_height), |mut row| {
let row_index = row.index();
let row_index = if self.reversed {
self.num_rows - 1 - row.index()
} else {
row.index()
};

row.set_selected(self.selection.contains(&row_index));

row.col(|ui| {
Expand Down
22 changes: 11 additions & 11 deletions crates/egui_extras/src/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,14 +451,23 @@ impl<'a> TableBuilder<'a> {
let Self {
ui,
id_salt,
columns,
mut columns,
striped,
resizable,
cell_layout,
scroll_options,
sense,
} = self;

for (i, column) in columns.iter_mut().enumerate() {
let column_resize_id = ui.id().with("resize_column").with(i);
if let Some(response) = ui.ctx().read_response(column_resize_id) {
if response.double_clicked() {
column.auto_size_this_frame = true;
}
}
}

let striped = striped.unwrap_or(ui.visuals().striped);

let state_id = ui.id().with(id_salt);
Expand Down Expand Up @@ -695,7 +704,7 @@ impl<'a> Table<'a> {
ui,
table_top,
state_id,
mut columns,
columns,
resizable,
mut available_width,
mut state,
Expand All @@ -719,15 +728,6 @@ impl<'a> Table<'a> {
scroll_bar_visibility,
} = scroll_options;

for (i, column) in columns.iter_mut().enumerate() {
let column_resize_id = ui.id().with("resize_column").with(i);
if let Some(response) = ui.ctx().read_response(column_resize_id) {
if response.double_clicked() {
column.auto_size_this_frame = true;
}
}
}

let cursor_position = ui.cursor().min;

let mut scroll_area = ScrollArea::new([false, vscroll])
Expand Down
2 changes: 1 addition & 1 deletion scripts/check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ cargo fmt --all -- --check
cargo doc --quiet --lib --no-deps --all-features
cargo doc --quiet --document-private-items --no-deps --all-features
cargo clippy --quiet --all-targets --all-features -- -D warnings
cargo clippy --all-targets --all-features --release -- -D warnings # we need to check release mode too
cargo clippy --quiet --all-targets --all-features --release -- -D warnings # we need to check release mode too

./scripts/clippy_wasm.sh

Expand Down
Loading