Skip to content

Commit

Permalink
Nicer (& fixed) help texts for space views
Browse files Browse the repository at this point in the history
  • Loading branch information
Wumpf committed May 9, 2023
1 parent a7aad11 commit c6c62cd
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 36 deletions.
97 changes: 97 additions & 0 deletions crates/re_ui/src/layout_job_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/// Utility for building layout jobs.
pub struct LayoutJobBuilder<'a> {
pub layout_job: egui::text::LayoutJob,
pub re_ui: &'a crate::ReUi,
}

impl<'a> LayoutJobBuilder<'a> {
pub fn new(re_ui: &'a crate::ReUi) -> Self {
Self {
layout_job: egui::text::LayoutJob::default(),
re_ui,
}
}

/// Append a generic text block.
pub fn add<'b, T: Into<LayoutJobBuilderBuildingBlock<'b>>>(&mut self, text_block: T) {
let text_block: LayoutJobBuilderBuildingBlock<'_> = text_block.into();
match text_block {
LayoutJobBuilderBuildingBlock::Body(text) => self.add_body(text),
LayoutJobBuilderBuildingBlock::Key(key) => self.add_key(key),
LayoutJobBuilderBuildingBlock::Modifier(modifier) => self.add_modifier(modifier),
LayoutJobBuilderBuildingBlock::MouseButton(button) => self.add_mouse_button(button),
};
}

/// Append body text.
pub fn add_body(&mut self, text: &str) {
self.layout_job
.append(text, 0.0, self.re_ui.text_format_body());
}

/// Append text that has special formatting for a button.
pub fn add_button_text(&mut self, text: &str) {
self.layout_job
.append(&text.to_lowercase(), 0.0, self.re_ui.text_format_key());
}

/// Append text for a keyboard key.
pub fn add_key(&mut self, key: egui::Key) {
self.add_button_text(key.name());
}

/// Append text for one or more modifier keys.
pub fn add_modifier(&mut self, modifier: egui::Modifiers) {
let is_mac = matches!(
self.re_ui.egui_ctx.os(),
egui::os::OperatingSystem::Mac | egui::os::OperatingSystem::IOS
);
let text = egui::ModifierNames::NAMES.format(&modifier, is_mac);
self.add_button_text(&text);
}

/// Append text for a mouse button.
pub fn add_mouse_button(&mut self, button: egui::PointerButton) {
self.add_button_text(match button {
egui::PointerButton::Primary => "right mouse button",
egui::PointerButton::Secondary => "left mouse button",
egui::PointerButton::Middle => "middle mouse button",
egui::PointerButton::Extra1 => "extra mouse button 1",
egui::PointerButton::Extra2 => "extra mouse button 2",
});
}
}

/// Generic building block that the layout job builder can consume.
///
/// Not meant to be used directly, use [`LayoutJobBuilder::add`] instead.
pub enum LayoutJobBuilderBuildingBlock<'a> {
Body(&'a str),
Key(egui::Key),
Modifier(egui::Modifiers),
MouseButton(egui::PointerButton),
}

impl<'a> From<&'a str> for LayoutJobBuilderBuildingBlock<'a> {
fn from(text: &'a str) -> Self {
Self::Body(text)
}
}

impl From<egui::Key> for LayoutJobBuilderBuildingBlock<'_> {
fn from(key: egui::Key) -> Self {
Self::Key(key)
}
}

impl From<egui::Modifiers> for LayoutJobBuilderBuildingBlock<'_> {
fn from(modifier: egui::Modifiers) -> Self {
Self::Modifier(modifier)
}
}

impl From<egui::PointerButton> for LayoutJobBuilderBuildingBlock<'_> {
fn from(button: egui::PointerButton) -> Self {
Self::MouseButton(button)
}
}
20 changes: 20 additions & 0 deletions crates/re_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod command_palette;
mod design_tokens;
pub mod egui_helpers;
pub mod icons;
mod layout_job_builder;
mod static_image_cache;
pub mod toasts;
mod toggle_switch;
Expand All @@ -13,6 +14,7 @@ pub use command::Command;
pub use command_palette::CommandPalette;
pub use design_tokens::DesignTokens;
pub use icons::Icon;
pub use layout_job_builder::LayoutJobBuilder;
pub use static_image_cache::StaticImageCache;
pub use toggle_switch::toggle_switch;

Expand Down Expand Up @@ -606,6 +608,24 @@ impl ReUi {

response
}

/// Text format used for regular body.
pub fn text_format_body(&self) -> egui::TextFormat {
egui::TextFormat::simple(
egui::TextStyle::Body.resolve(&self.egui_ctx.style()),
self.egui_ctx.style().visuals.text_color(),
)
}

/// Text format used for labels referring to keys and buttons.
pub fn text_format_key(&self) -> egui::TextFormat {
let mut style = egui::TextFormat::simple(
egui::TextStyle::Monospace.resolve(&self.egui_ctx.style()),
self.egui_ctx.style().visuals.text_color(),
);
style.background = self.egui_ctx.style().visuals.widgets.noninteractive.bg_fill;
style
}
}

// ----------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion crates/re_viewer/src/ui/view_bar_chart/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ mod scene;
pub(crate) use self::scene::SceneBarChart;

mod ui;
pub(crate) use self::ui::{view_bar_chart, BarChartState, HELP_TEXT};
pub(crate) use self::ui::{help_text, view_bar_chart, BarChartState};
28 changes: 24 additions & 4 deletions crates/re_viewer/src/ui/view_bar_chart/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,30 @@ use super::SceneBarChart;

// ---

pub(crate) const HELP_TEXT: &str = "\
Pan by dragging, or scroll (+ shift = horizontal).\n\
Box zooming: Right click to zoom in and zoom out using a selection.\n\
Reset view with double-click.";
pub fn help_text(re_ui: &re_ui::ReUi) -> egui::WidgetText {
let horizontal_modifier = egui::Modifiers::SHIFT;
let zoom_modifier = egui::Modifiers::CTRL;
let select_zoom = egui::PointerButton::Secondary;

let mut layout = re_ui::LayoutJobBuilder::new(re_ui);

layout.add("Pan by dragging, or scroll (+ ");
layout.add(horizontal_modifier);
layout.add(" for horizontal).\n");

layout.add("Zoom with pinch gesture or scroll + ");
layout.add(zoom_modifier);
layout.add(".\n");

layout.add("Drag ");
layout.add(select_zoom);
layout.add(" to zoom in/out using a selection.\n\n");

layout.add_button_text("double-click");
layout.add(" to reset the view.");

layout.layout_job.into()
}

#[derive(Clone, Default, serde::Deserialize, serde::Serialize)]
pub struct BarChartState;
Expand Down
6 changes: 3 additions & 3 deletions crates/re_viewer/src/ui/view_spatial/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -431,10 +431,10 @@ impl ViewSpatialState {
}
}

pub fn help_text(&self) -> &str {
pub fn help_text(&self, re_ui: &re_ui::ReUi) -> egui::WidgetText {
match *self.nav_mode.get() {
SpatialNavigationMode::TwoD => super::ui_2d::HELP_TEXT_2D,
SpatialNavigationMode::ThreeD => super::ui_3d::HELP_TEXT_3D,
SpatialNavigationMode::TwoD => super::ui_2d::help_text(re_ui),
SpatialNavigationMode::ThreeD => super::ui_3d::help_text(re_ui),
}
}
}
Expand Down
18 changes: 15 additions & 3 deletions crates/re_viewer/src/ui/view_spatial/ui_2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,21 @@ impl View2DState {
}
}

pub const HELP_TEXT_2D: &str = "Ctrl-scroll to zoom (⌘-scroll or Mac).\n\
Drag to pan.\n\
Double-click to reset the view.";
pub fn help_text(re_ui: &re_ui::ReUi) -> egui::WidgetText {
let zoom_scroll_modifier = egui::Modifiers::COMMAND;

let mut layout = re_ui::LayoutJobBuilder::new(re_ui);

layout.add(zoom_scroll_modifier);
layout.add(" + scroll to zoom.\n");

layout.add("Drag to pan.\n");

layout.add_button_text("double-click");
layout.add(" to reset the view.");

layout.layout_job.into()
}

/// Create the outer 2D view, which consists of a scrollable region
/// TODO(andreas): Split into smaller parts, more re-use with `ui_3d`
Expand Down
61 changes: 49 additions & 12 deletions crates/re_viewer/src/ui/view_spatial/ui_3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,18 +238,55 @@ fn find_camera(space_cameras: &[SpaceCamera3D], needle: &EntityPath) -> Option<E

// ----------------------------------------------------------------------------

pub const HELP_TEXT_3D: &str = "Drag to rotate.\n\
Drag with secondary mouse button to pan.\n\
Drag with middle mouse button (or primary mouse button + holding ALT/OPTION) to roll the view.\n\
Scroll to zoom.\n\
\n\
While hovering the 3D view, navigate with WSAD and QE.\n\
CTRL slows down, SHIFT speeds up.\n\
\n\
Double-click an object to focus the view on it.\n\
For cameras, you can restore the view again with Escape.\n\
\n\
Double-click on empty space to reset the view.";
pub fn help_text(re_ui: &re_ui::ReUi) -> egui::WidgetText {
let pan_mouse = egui::PointerButton::Secondary;
let roll_mouse = egui::PointerButton::Middle;
let roll_mouse_alt = egui::PointerButton::Primary;
let roll_modifier = egui::Modifiers::ALT;
let speed_up = egui::Modifiers::SHIFT;
let slow_down = egui::Modifiers::CTRL;
let restore_key = egui::Key::Escape;

let mut layout = re_ui::LayoutJobBuilder::new(re_ui);

layout.add("Drag to rotate.\n");

layout.add("Drag with ");
layout.add(pan_mouse);
layout.add(" to pan.\n");

layout.add("Drag with ");
layout.add(roll_mouse);
layout.add(" ( ");
layout.add(roll_mouse_alt);
layout.add(" + holding ");
layout.add(roll_modifier);
layout.add(" ) to roll the view.\n");

layout.add("Scroll or pinch to zoom.\n\n");

layout.add("While hovering the 3D view, navigate with ");
layout.add_button_text("WASD");
layout.add(" and ");
layout.add_button_text("QE");
layout.add("\n");

layout.add(slow_down);
layout.add(" slows down, ");
layout.add(speed_up);
layout.add(" speeds up\n\n");

layout.add_button_text("double-click");
layout.add(" an object to focus the view on it.\n");
layout.add("For cameras, you can restore the view again with ");
layout.add(restore_key);
layout.add(" .\n\n");

layout.add_button_text("double-click");
layout.add(" on empty space to reset the view.");

layout.layout_job.into()
}

/// TODO(andreas): Split into smaller parts, more re-use with `ui_2d`
#[allow(clippy::too_many_arguments)]
Expand Down
2 changes: 1 addition & 1 deletion crates/re_viewer/src/ui/view_time_series/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ mod scene;
pub(crate) use self::scene::SceneTimeSeries;

mod ui;
pub(crate) use self::ui::{view_time_series, ViewTimeSeriesState, HELP_TEXT};
pub(crate) use self::ui::{help_text, view_time_series, ViewTimeSeriesState};
36 changes: 29 additions & 7 deletions crates/re_viewer/src/ui/view_time_series/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,35 @@ use crate::{

// ---

pub(crate) const HELP_TEXT: &str = "Pan by dragging, or scroll (+ shift = horizontal).\n\
Box zooming: Right click to zoom in and zoom out using a selection.\n\
Zoom with ctrl / ⌘ + pointer wheel, or with pinch gesture.\n\
You can also zoom by dragging a rectangle with the right mouse button.\n\
\n\
Reset view with double-click.\n\
Right click to move the time cursor to the current position.";
pub fn help_text(re_ui: &re_ui::ReUi) -> egui::WidgetText {
let horizontal_modifier = egui::Modifiers::SHIFT;
let zoom_modifier = egui::Modifiers::CTRL;
let select_zoom = egui::PointerButton::Secondary;
let move_time_cursor = egui::PointerButton::Secondary;

let mut layout = re_ui::LayoutJobBuilder::new(re_ui);

layout.add("Pan by dragging, or scroll (+ ");
layout.add(horizontal_modifier);
layout.add(" for horizontal).\n");

layout.add("Zoom with pinch gesture or scroll + ");
layout.add(zoom_modifier);
layout.add(".\n");

layout.add("Drag ");
layout.add(select_zoom);
layout.add(" to zoom in/out using a selection.\n");

layout.add("Click ");
layout.add(move_time_cursor);
layout.add(" to move the time cursor.\n\n");

layout.add_button_text("double-click");
layout.add(" to reset the view.");

layout.layout_job.into()
}

#[derive(Clone, Default, serde::Deserialize, serde::Serialize)]
pub struct ViewTimeSeriesState;
Expand Down
10 changes: 5 additions & 5 deletions crates/re_viewer/src/ui/viewport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -704,11 +704,11 @@ impl<'a, 'b> egui_dock::TabViewer for TabViewer<'a, 'b> {
}
}

fn help_text_ui(ui: &mut egui::Ui, space_view: &SpaceView) {
fn help_text_ui(ui: &mut egui::Ui, re_ui: &re_ui::ReUi, space_view: &SpaceView) {
let help_text = match space_view.category {
ViewCategory::TimeSeries => Some(crate::ui::view_time_series::HELP_TEXT),
ViewCategory::BarChart => Some(crate::ui::view_bar_chart::HELP_TEXT),
ViewCategory::Spatial => Some(space_view.view_state.state_spatial.help_text()),
ViewCategory::TimeSeries => Some(crate::ui::view_time_series::help_text(re_ui)),
ViewCategory::BarChart => Some(crate::ui::view_bar_chart::help_text(re_ui)),
ViewCategory::Spatial => Some(space_view.view_state.state_spatial.help_text(re_ui)),
ViewCategory::Text | ViewCategory::Tensor => None,
};

Expand Down Expand Up @@ -760,7 +760,7 @@ fn space_view_options_ui(
}

// Show help last, since not all space views have help text
help_text_ui(ui, space_view);
help_text_ui(ui, ctx.re_ui, space_view);

// Put a frame so that the buttons cover any labels they intersect with:
let rect = ui.min_rect().expand2(egui::vec2(1.0, -2.0));
Expand Down

0 comments on commit c6c62cd

Please sign in to comment.