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

Nicer (& fixed up) help texts for space views #2070

Merged
merged 6 commits into from
May 15, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Copy link
Member

Choose a reason for hiding this comment

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

Would be nice if these semantic constants were shared with the values used here:

pub fn update(&mut self, response: &egui::Response, drag_threshold: f32) -> bool {
let mut did_interact = false;
if response.drag_delta().length() > drag_threshold {
if response.dragged_by(egui::PointerButton::Middle)
|| (response.dragged_by(egui::PointerButton::Primary)
&& response.ctx.input(|i| i.modifiers.alt))
{
if let Some(pointer_pos) = response.ctx.pointer_latest_pos() {
self.roll(&response.rect, pointer_pos, response.drag_delta());
did_interact = true;
}
} else if response.dragged_by(egui::PointerButton::Primary) {
self.rotate(response.drag_delta());
did_interact = true;
} else if response.dragged_by(egui::PointerButton::Secondary) {
self.translate(response.drag_delta());
did_interact = true;
}
}

Copy link
Member Author

Choose a reason for hiding this comment

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

was too lazy to go that extra mile, but yeah at least some of those are pretty easy. Let's put them somewhere central!

Copy link
Member

Choose a reason for hiding this comment

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

that would be nice

Copy link
Member Author

Choose a reason for hiding this comment

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

done that now. Sadly lots of controls are hidden inside other egui constructs like scrollview or plot


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