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 Recordings section to the left panel #2938

Merged
merged 13 commits into from
Aug 11, 2023
94 changes: 82 additions & 12 deletions crates/re_ui/examples/re_ui_example.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use egui::Widget;
use re_ui::{toasts, CommandPalette, UICommand, UICommandSender};

/// Sender that queues up the execution of a command.
Expand Down Expand Up @@ -73,6 +74,8 @@ pub struct ExampleApp {
right_panel: bool,
bottom_panel: bool,

selected_list_item: Option<usize>,

dummy_bool: bool,

cmd_palette: CommandPalette,
Expand Down Expand Up @@ -103,6 +106,8 @@ impl ExampleApp {
right_panel: true,
bottom_panel: true,

selected_list_item: None,

dummy_bool: true,

cmd_palette: CommandPalette::default(),
Expand Down Expand Up @@ -219,29 +224,94 @@ impl eframe::App for ExampleApp {
});
});

// RIGHT PANEL
//
// This is the "idiomatic" panel structure for Rerun:
// - A top-level `SidePanel` without inner margins and which sets the clip rectangle.
// - Every piece of content (title bar, lists, etc.) are wrapped in a `Frame` with inner
// margins set to `ReUi::panel_margins()`. That can be done with `ReUi::panel_content()`.
// - If/when a scroll area is used, it must be applied without margin and outside of the
// `Frame`.
//
// This way, the content (titles, etc.) is properly inset and benefits from a properly set
// clip rectangle for full-span behaviour, without interference from the scroll areas.

let panel_frame = egui::Frame {
fill: egui_ctx.style().visuals.panel_fill,
inner_margin: re_ui::ReUi::view_padding().into(),
..Default::default()
};

egui::SidePanel::right("right_panel")
.frame(panel_frame)
.show_animated(egui_ctx, self.right_panel, |ui| {
// TODO(ab): use the proper `egui::Rect` function when egui 0.23 is released
let clip_rect = egui::Rect::from_min_max(
ui.max_rect().min - panel_frame.inner_margin.left_top(),
ui.max_rect().max + panel_frame.inner_margin.right_bottom(),
);
ui.set_clip_rect(clip_rect);

ui.strong("Right panel");
selection_buttons(ui);
self.re_ui
.large_collapsing_header(ui, "Large Collapsing Header", true, |ui| {
ui.set_clip_rect(ui.max_rect());

// first section - no scroll area, so a single outer "panel_content" can be used.
self.re_ui.panel_content(ui, |re_ui, ui| {
re_ui.panel_title_bar(
ui,
"Right panel",
Some("This is the title of the right panel"),
);
re_ui.large_collapsing_header(ui, "Large Collapsing Header", true, |ui| {
ui.label("Some data here");
ui.label("Some data there");

selection_buttons(ui);
});
});

// Second section. It's a list of `list_items`, so we need to remove the default
// spacing. Also, it uses a scroll area, so we must use several "panel_content".
ui.scope(|ui| {
ui.spacing_mut().item_spacing.y = 0.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we want to remove the spacing between the title and the items too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so, though the styling of the title has already changed in the meantime and that is all part of a whole lot of things that will see significant styling adjustment and fine-tuning in the coming weeks (when Mårten is back from holidays).


self.re_ui.panel_content(ui, |re_ui, ui| {
re_ui.panel_title_bar(ui, "Another section", None);
});

egui::ScrollArea::both()
.id_source("example_right_panel")
.auto_shrink([false, true])
.show(ui, |ui| {
self.re_ui.panel_content(ui, |re_ui, ui| {
for i in 0..10 {
let label = if i == 4 {
"That's one heck of a loooooooong label!".to_owned()
} else {
format!("Some item {i}")
};

let mut item = re_ui
.list_item(label)
.selected(Some(i) == self.selected_list_item)
.active(i != 3)
.with_buttons(|re_ui, ui| {
re_ui.small_icon_button(ui, &re_ui::icons::ADD)
| re_ui.small_icon_button(ui, &re_ui::icons::REMOVE)
});

// demo custom icon
item = if i == 6 {
item.with_icon_fn(|_re_ui, ui, rect, visuals| {
ui.painter().circle(
rect.center(),
rect.width() / 2.0,
visuals.fg_stroke.color,
egui::Stroke::NONE,
);
})
} else {
item.with_icon(&re_ui::icons::SPACE_VIEW_TEXT)
};

if item.ui(ui).clicked() {
self.selected_list_item = Some(i);
}
}
});
});
});
});

egui::CentralPanel::default()
Expand Down
8 changes: 8 additions & 0 deletions crates/re_ui/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,14 @@ impl UICommand {
Default::default()
}
}

pub fn tooltip_with_shortcut(self, egui_ctx: &egui::Context) -> String {
format!(
"{}{}",
self.tooltip(),
self.format_shortcut_tooltip_suffix(egui_ctx)
)
}
}

#[test]
Expand Down
83 changes: 82 additions & 1 deletion crates/re_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod design_tokens;
pub mod egui_helpers;
pub mod icons;
mod layout_job_builder;
pub mod list_item;
mod static_image_cache;
pub mod toasts;
mod toggle_switch;
Expand Down Expand Up @@ -50,6 +51,7 @@ use std::{ops::RangeInclusive, sync::Arc};

use parking_lot::Mutex;

use crate::list_item::ListItem;
use egui::{pos2, Align2, Color32, Mesh, NumExt, Rect, Shape, Vec2};

#[derive(Clone)]
Expand Down Expand Up @@ -91,6 +93,10 @@ impl ReUi {
12.0
}

pub fn panel_margin() -> egui::Margin {
egui::Margin::symmetric(Self::view_padding(), 0.0)
}

pub fn window_rounding() -> f32 {
12.0
}
Expand Down Expand Up @@ -130,6 +136,10 @@ impl ReUi {
28.0 // from figma 2022-02-03
}

pub fn list_item_height() -> f32 {
24.0
}

pub fn native_window_rounding() -> f32 {
10.0
}
Expand Down Expand Up @@ -444,6 +454,72 @@ impl ReUi {
response
}

pub fn panel_content<R>(
&self,
ui: &mut egui::Ui,
add_contents: impl FnOnce(&ReUi, &mut egui::Ui) -> R,
) -> R {
egui::Frame {
inner_margin: Self::panel_margin(),
..Default::default()
}
.show(ui, |ui| add_contents(self, ui))
.inner
}

/// Static title bar used to separate panels into section.
///
/// This title bar is meant to be used in a panel with proper inner margin and clip rectangle
/// set.
///
/// Use [`ReUi::panel_title_bar_with_buttons`] to display buttons in the title bar.
pub fn panel_title_bar(&self, ui: &mut egui::Ui, label: &str, hover_text: Option<&str>) {
self.panel_title_bar_with_buttons(ui, label, hover_text, |_ui| {});
}

/// Static title bar used to separate panels into section with custom buttons when hovered.
///
/// This title bar is meant to be used in a panel with proper inner margin and clip rectangle
/// set.
#[allow(clippy::unused_self)]
pub fn panel_title_bar_with_buttons<R>(
&self,
ui: &mut egui::Ui,
label: &str,
hover_text: Option<&str>,
add_right_buttons: impl FnOnce(&mut egui::Ui) -> R,
) -> R {
ui.allocate_ui_with_layout(
egui::vec2(ui.available_width(), Self::title_bar_height()),
egui::Layout::left_to_right(egui::Align::Center),
|ui| {
// draw horizontal separator lines
let mut rect = ui.available_rect_before_wrap();
let hline_stroke = ui.style().visuals.widgets.noninteractive.bg_stroke;
rect.extend_with_x(ui.clip_rect().right());
rect.extend_with_x(ui.clip_rect().left());
ui.painter().hline(rect.x_range(), rect.top(), hline_stroke);
ui.painter()
.hline(rect.x_range(), rect.bottom(), hline_stroke);

// draw label
let resp = ui.strong(label);
if let Some(hover_text) = hover_text {
resp.on_hover_text(hover_text);
}

// draw hover buttons
ui.allocate_ui_with_layout(
ui.available_size(),
egui::Layout::right_to_left(egui::Align::Center),
add_right_buttons,
)
.inner
},
)
.inner
}

/// Show a prominent collapsing header to be used as section delimitation in side panels.
///
/// Note that a clip rect must be set (typically by the panel) to avoid any overdraw.
Expand Down Expand Up @@ -592,6 +668,11 @@ impl ReUi {
ui.painter().add(shadow);
}

/// Convenience function to create a [`ListItem`] with the given text.
pub fn list_item(&self, text: impl Into<egui::WidgetText>) -> ListItem<'_> {
ListItem::new(self, text)
}

pub fn selectable_label_with_icon(
&self,
ui: &mut egui::Ui,
Expand Down Expand Up @@ -642,7 +723,7 @@ impl ReUi {
let image_rect = egui::Rect::from_min_size(
ui.painter().round_pos_to_pixels(egui::pos2(
rect.min.x.ceil(),
((rect.min.y + rect.max.y - Self::small_icon_size().y) * 0.5).ceil(),
(rect.center().y - 0.5 * ReUi::small_icon_size().y).ceil(),
)),
Self::small_icon_size(),
);
Expand Down
Loading