Skip to content

Commit

Permalink
Filter entities in the UI (part 3): Move action to a menu in the blue…
Browse files Browse the repository at this point in the history
…print panel and keep default blueprint when using heuristics (#8672)

Co-authored-by: Emil Ernerfeldt <[email protected]>
  • Loading branch information
abey79 and emilk authored Jan 14, 2025
1 parent e4f8df4 commit f779d8b
Show file tree
Hide file tree
Showing 13 changed files with 172 additions and 134 deletions.
148 changes: 92 additions & 56 deletions crates/viewer/re_blueprint_tree/src/blueprint_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,25 @@ impl BlueprintTree {
ui: &mut egui::Ui,
) {
ui.panel_content(|ui| {
ui.panel_title_bar_with_buttons(
"Blueprint",
Some("The blueprint is where you can configure the Rerun Viewer"),
|ui| {
self.add_new_view_button_ui(ctx, viewport, ui);
reset_blueprint_button_ui(ctx, ui);
},
);
ui.full_span_separator();
ui.add_space(-1.);

ui.list_item_scope("blueprint_section_title", |ui| {
ui.list_item_flat_noninteractive(
list_item::CustomContent::new(|ui, _| {
ui.strong("Blueprint").on_hover_text(
"The blueprint is where you can configure the Rerun Viewer",
);
})
.menu_button(&re_ui::icons::MORE, |ui| {
add_new_view_or_container_menu_button(ctx, viewport, ui);
set_blueprint_to_default_menu_buttons(ctx, ui);
set_blueprint_to_auto_menu_button(ctx, viewport, ui);
}),
);
});

ui.full_span_separator();
});

// This call is excluded from `panel_content` because it has a ScrollArea, which should not be
Expand Down Expand Up @@ -602,32 +613,6 @@ impl BlueprintTree {
ctx.handle_select_hover_drag_interactions(&response, item, true);
}

/// Add a button to trigger the addition of a new view or container.
#[allow(clippy::unused_self)]
fn add_new_view_button_ui(
&self,
ctx: &ViewerContext<'_>,
viewport: &ViewportBlueprint,
ui: &mut egui::Ui,
) {
if ui
.small_icon_button(&re_ui::icons::ADD)
.on_hover_text("Add a new view or container")
.clicked()
{
// If a single container is selected, we use it as target. Otherwise, we target the
// root container.
let target_container_id =
if let Some(Item::Container(container_id)) = ctx.selection().single_item() {
*container_id
} else {
viewport.root_container
};

show_add_view_or_container_modal(target_container_id);
}
}

// ----------------------------------------------------------------------------
// drag and drop support

Expand Down Expand Up @@ -868,46 +853,97 @@ impl BlueprintTree {

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

fn reset_blueprint_button_ui(ctx: &ViewerContext<'_>, ui: &mut egui::Ui) {
/// Add a button to trigger the addition of a new view or container.
fn add_new_view_or_container_menu_button(
ctx: &ViewerContext<'_>,
viewport: &ViewportBlueprint,
ui: &mut egui::Ui,
) {
if ui
.add(egui::Button::image_and_text(
&re_ui::icons::ADD,
"Add view or container…",
))
.clicked()
{
ui.close_menu();

// If a single container is selected, we use it as target. Otherwise, we target the
// root container.
let target_container_id =
if let Some(Item::Container(container_id)) = ctx.selection().single_item() {
*container_id
} else {
viewport.root_container
};

show_add_view_or_container_modal(target_container_id);
}
}

fn set_blueprint_to_default_menu_buttons(ctx: &ViewerContext<'_>, ui: &mut egui::Ui) {
let default_blueprint_id = ctx
.store_context
.hub
.default_blueprint_id_for_app(&ctx.store_context.app_id);

let default_blueprint = default_blueprint_id.and_then(|id| ctx.store_context.bundle.get(id));

let mut disabled_reason = None;

if let Some(default_blueprint) = default_blueprint {
let active_is_clone_of_default = Some(default_blueprint.store_id()).as_ref()
== ctx.store_context.blueprint.cloned_from();
let last_modified_at_the_same_time =
default_blueprint.latest_row_id() == ctx.store_context.blueprint.latest_row_id();
if active_is_clone_of_default && last_modified_at_the_same_time {
disabled_reason = Some("No modifications have been made");
let disabled_reason = match default_blueprint {
None => Some("No default blueprint is set for this app"),
Some(default_blueprint) => {
let active_is_clone_of_default = Some(default_blueprint.store_id()).as_ref()
== ctx.store_context.blueprint.cloned_from();
let last_modified_at_the_same_time =
default_blueprint.latest_row_id() == ctx.store_context.blueprint.latest_row_id();
if active_is_clone_of_default && last_modified_at_the_same_time {
Some("No modifications have been made")
} else {
None // it is valid to reset to default
}
}
}
};

let enabled = disabled_reason.is_none();
let response = ui.add_enabled(enabled, ui.small_icon_button_widget(&re_ui::icons::RESET));

let response = if let Some(disabled_reason) = disabled_reason {
response.on_disabled_hover_text(disabled_reason)
} else {
let hover_text = if default_blueprint_id.is_some() {
"Reset to the default blueprint for this app"
} else {
"Re-populate viewport with automatically chosen views"
};
response.on_hover_text(hover_text)
let mut response = ui
.add_enabled(
enabled,
egui::Button::image_and_text(&re_ui::icons::RESET, "Reset to default blueprint"),
)
.on_hover_text("Reset to the default blueprint for this app");

if let Some(disabled_reason) = disabled_reason {
response = response.on_disabled_hover_text(disabled_reason);
};

if response.clicked() {
ui.close_menu();
ctx.command_sender
.send_system(re_viewer_context::SystemCommand::ClearActiveBlueprint);
}
}

fn set_blueprint_to_auto_menu_button(
ctx: &ViewerContext<'_>,
viewport: &ViewportBlueprint,
ui: &mut egui::Ui,
) {
let enabled = !viewport.auto_layout() || !viewport.auto_views();

if ui
.add_enabled(
enabled,
egui::Button::image_and_text(&re_ui::icons::RESET, "Reset to heuristic blueprint"),
)
.on_hover_text("Re-populate viewport with automatically chosen views")
.clicked()
{
ui.close_menu();
ctx.command_sender
.send_system(re_viewer_context::SystemCommand::ClearActiveBlueprintAndEnableHeuristics);
}
}

/// Expand all required items and compute which item we should scroll to.
fn handle_focused_item(
ctx: &ViewerContext<'_>,
Expand Down
48 changes: 1 addition & 47 deletions crates/viewer/re_data_ui/src/app_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use itertools::Itertools as _;

use re_entity_db::EntityDb;
use re_log_types::ApplicationId;
use re_viewer_context::{SystemCommandSender as _, UiLayout, ViewerContext};
use re_viewer_context::{UiLayout, ViewerContext};

use crate::item_ui::entity_db_button_ui;

Expand Down Expand Up @@ -54,51 +54,5 @@ impl crate::DataUi for ApplicationId {
}
});
}

// ---------------------------------------------------------------------
// do not show UI code in tooltips

if ui_layout != UiLayout::Tooltip {
ui.add_space(8.0);

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

// Blueprint section.
let active_blueprint = ctx.store_context.blueprint;
let default_blueprint = ctx.store_context.hub.default_blueprint_for_app(self);

let button =
egui::Button::image_and_text(&re_ui::icons::RESET, "Reset to default blueprint");

let is_same_as_default = default_blueprint.is_some_and(|default_blueprint| {
default_blueprint.latest_row_id() == active_blueprint.latest_row_id()
});

if is_same_as_default {
ui.add_enabled(false, button)
.on_disabled_hover_text("No modifications have been made");
} else if default_blueprint.is_none() {
ui.add_enabled(false, button)
.on_disabled_hover_text("There's no default blueprint");
} else {
// The active blueprint is different from the default blueprint
if ui
.add(button)
.on_hover_text("Reset to the default blueprint for this app")
.clicked()
{
ctx.command_sender
.send_system(re_viewer_context::SystemCommand::ClearActiveBlueprint);
}
}

if ui.add(egui::Button::image_and_text(
&re_ui::icons::RESET,
"Reset to heuristic blueprint",
)).on_hover_text("Clear both active and default blueprint, and auto-generate a new blueprint based on heuristics").clicked() {
ctx.command_sender
.send_system(re_viewer_context::SystemCommand::ClearAndGenerateBlueprint);
}
}
}
}
15 changes: 6 additions & 9 deletions crates/viewer/re_ui/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub enum UICommand {
OpenRerunDiscord,

ResetViewer,
ClearAndGenerateBlueprint,
ClearActiveBlueprintAndEnableHeuristics,

#[cfg(not(target_arch = "wasm32"))]
OpenProfiler,
Expand Down Expand Up @@ -121,7 +121,7 @@ impl UICommand {
),

Self::CloseAllRecordings => ("Close all recordings",
"Close all open current recording (unsaved data will be lost)"),
"Close all open current recording (unsaved data will be lost)"),

Self::Undo => ("Undo", "Undo the last blueprint edit for the open recording"),
Self::Redo => ("Redo", "Redo the last undone thing"),
Expand All @@ -137,12 +137,11 @@ impl UICommand {
"Reset the Viewer to how it looked the first time you ran it, forgetting all stored blueprints and UI state",
),

Self::ClearAndGenerateBlueprint => (
"Clear and generate new blueprint",
"Clear the current blueprint and generate a new one based on heuristics."
Self::ClearActiveBlueprintAndEnableHeuristics => (
"Reset to heuristic blueprint",
"Re-populate viewport with automatically chosen views"
),


#[cfg(not(target_arch = "wasm32"))]
Self::OpenProfiler => (
"Open profiler",
Expand Down Expand Up @@ -174,7 +173,6 @@ impl UICommand {
"View and change global egui style settings",
),


#[cfg(not(target_arch = "wasm32"))]
Self::ToggleFullscreen => (
"Toggle fullscreen",
Expand Down Expand Up @@ -256,7 +254,6 @@ impl UICommand {
"Restart with WebGPU",
"Reloads the webpage and force WebGPU for rendering. All data will be lost."
),

}
}

Expand Down Expand Up @@ -314,7 +311,7 @@ impl UICommand {
Self::Quit => smallvec![cmd(Key::Q)],

Self::ResetViewer => smallvec![ctrl_shift(Key::R)],
Self::ClearAndGenerateBlueprint => smallvec![],
Self::ClearActiveBlueprintAndEnableHeuristics => smallvec![],

#[cfg(not(target_arch = "wasm32"))]
Self::OpenProfiler => smallvec![ctrl_shift(Key::P)],
Expand Down
10 changes: 10 additions & 0 deletions crates/viewer/re_ui/src/ui_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,16 @@ pub trait UiExt {
self.ui().painter().add(shadow);
}

/// Convenience function to create a [`list_item::list_item_scope`].
#[inline]
fn list_item_scope<R>(
&mut self,
id_salt: impl std::hash::Hash,
content: impl FnOnce(&mut egui::Ui) -> R,
) -> R {
list_item::list_item_scope(self.ui_mut(), id_salt, content)
}

/// Convenience function to create a [`list_item::ListItem`].
#[allow(clippy::unused_self)]
fn list_item(&self) -> list_item::ListItem {
Expand Down
1 change: 1 addition & 0 deletions crates/viewer/re_view_graph/tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ fn run_graph_view_and_save_snapshot(
bundle: &Default::default(),
caches: &Default::default(),
hub: &Default::default(),
should_enable_heuristics: false,
};

// Execute the queries for every `View`
Expand Down
12 changes: 5 additions & 7 deletions crates/viewer/re_viewer/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -524,12 +524,10 @@ impl App {
}

SystemCommand::ResetViewer => self.reset_viewer(store_hub, egui_ctx),
SystemCommand::ClearAndGenerateBlueprint => {
SystemCommand::ClearActiveBlueprintAndEnableHeuristics => {
re_log::debug!("Clear and generate new blueprint");
// By clearing the default blueprint and the active blueprint
// it will be re-generated based on the default auto behavior.
store_hub.clear_default_blueprint();
store_hub.clear_active_blueprint();
store_hub.clear_active_blueprint_and_generate();
egui_ctx.request_repaint(); // Many changes take a frame delay to show up.
}
SystemCommand::ClearActiveBlueprint => {
// By clearing the blueprint the default blueprint will be restored
Expand Down Expand Up @@ -779,9 +777,9 @@ impl App {
}

UICommand::ResetViewer => self.command_sender.send_system(SystemCommand::ResetViewer),
UICommand::ClearAndGenerateBlueprint => {
UICommand::ClearActiveBlueprintAndEnableHeuristics => {
self.command_sender
.send_system(SystemCommand::ClearAndGenerateBlueprint);
.send_system(SystemCommand::ClearActiveBlueprintAndEnableHeuristics);
}

#[cfg(not(target_arch = "wasm32"))]
Expand Down
7 changes: 7 additions & 0 deletions crates/viewer/re_viewer/src/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,13 @@ impl AppState {
drag_and_drop_manager: &drag_and_drop_manager,
};

// enable the heuristics if we must this frame
if store_context.should_enable_heuristics {
viewport_ui.blueprint.set_auto_layout(true, &ctx);
viewport_ui.blueprint.set_auto_views(true, &ctx);
egui_ctx.request_repaint();
}

// We move the time at the very start of the frame,
// so that we always show the latest data when we're in "follow" mode.
move_time(&ctx, recording, rx);
Expand Down
Loading

0 comments on commit f779d8b

Please sign in to comment.