-
Notifications
You must be signed in to change notification settings - Fork 373
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
More context menu 7: cleanup (#5456)
### What NOTE: review commit by commit (first commit is pure refactoring). Final PR in the series. This PR: - split `actions.rs` into many sub-files - fix capitalisation in some menu item - split the release check list in small checks Full series: - #5392 - #5397 - #5407 - #5411 - #5422 - #5433 - #5456 ### Checklist * [x] I have read and agree to [Contributor Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and the [Code of Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md) * [x] I've included a screenshot or gif (if applicable) * [x] I have tested the web demo (if applicable): * Using newly built examples: [app.rerun.io](https://app.rerun.io/pr/5456/index.html) * Using examples from latest `main` build: [app.rerun.io](https://app.rerun.io/pr/5456/index.html?manifest_url=https://app.rerun.io/version/main/examples_manifest.json) * Using full set of examples from `nightly` build: [app.rerun.io](https://app.rerun.io/pr/5456/index.html?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json) * [x] The PR title and labels are set such as to maximize their usefulness for the next release's CHANGELOG * [x] If applicable, add a new check to the [release checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)! - [PR Build Summary](https://build.rerun.io/pr/5456) - [Docs preview](https://rerun.io/preview/a36360f586494f6648fdc4bbb9d806ab12911358/docs) <!--DOCS-PREVIEW--> - [Examples preview](https://rerun.io/preview/a36360f586494f6648fdc4bbb9d806ab12911358/examples) <!--EXAMPLES-PREVIEW--> - [Recent benchmark results](https://build.rerun.io/graphs/crates.html) - [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)
- Loading branch information
Showing
16 changed files
with
1,060 additions
and
775 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
35 changes: 35 additions & 0 deletions
35
crates/re_viewport/src/context_menu/actions/add_container.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
use re_viewer_context::{ContainerId, Item}; | ||
|
||
use crate::context_menu::{ContextMenuAction, ContextMenuContext}; | ||
|
||
/// Add a container of a specific type | ||
pub(crate) struct AddContainerAction(pub egui_tiles::ContainerKind); | ||
|
||
impl ContextMenuAction for AddContainerAction { | ||
fn supports_selection(&self, ctx: &ContextMenuContext<'_>) -> bool { | ||
if let Some(Item::Container(container_id)) = ctx.selection.single_item() { | ||
if let Some(container) = ctx.viewport_blueprint.container(container_id) { | ||
// same-kind linear containers cannot be nested | ||
(container.container_kind != egui_tiles::ContainerKind::Vertical | ||
&& container.container_kind != egui_tiles::ContainerKind::Horizontal) | ||
|| container.container_kind != self.0 | ||
} else { | ||
// unknown container | ||
false | ||
} | ||
} else { | ||
false | ||
} | ||
} | ||
|
||
fn label(&self, _ctx: &ContextMenuContext<'_>) -> String { | ||
format!("{:?}", self.0) | ||
} | ||
|
||
fn process_container(&self, ctx: &ContextMenuContext<'_>, container_id: &ContainerId) { | ||
ctx.viewport_blueprint | ||
.add_container(self.0, Some(*container_id)); | ||
ctx.viewport_blueprint | ||
.mark_user_interaction(ctx.viewer_context); | ||
} | ||
} |
175 changes: 175 additions & 0 deletions
175
crates/re_viewport/src/context_menu/actions/add_entities_to_new_space_view.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
use egui::{Response, Ui}; | ||
use itertools::Itertools; | ||
use nohash_hasher::IntSet; | ||
|
||
use re_log_types::{EntityPath, EntityPathFilter, EntityPathRule, RuleEffect}; | ||
use re_space_view::{determine_visualizable_entities, SpaceViewBlueprint}; | ||
use re_viewer_context::{Item, SpaceViewClassIdentifier}; | ||
|
||
use crate::context_menu::{ContextMenuAction, ContextMenuContext}; | ||
|
||
/// Create a new space view containing the selected entities. | ||
/// | ||
/// The space view is created next to the clicked item's parent view (if a data result was clicked). | ||
pub(crate) struct AddEntitiesToNewSpaceViewAction; | ||
|
||
impl ContextMenuAction for AddEntitiesToNewSpaceViewAction { | ||
fn supports_multi_selection(&self, _ctx: &ContextMenuContext<'_>) -> bool { | ||
true | ||
} | ||
|
||
fn supports_item(&self, _ctx: &ContextMenuContext<'_>, item: &Item) -> bool { | ||
matches!(item, Item::DataResult(_, _) | Item::InstancePath(_)) | ||
} | ||
|
||
fn ui(&self, ctx: &ContextMenuContext<'_>, ui: &mut Ui) -> Response { | ||
let space_view_class_registry = ctx.viewer_context.space_view_class_registry; | ||
|
||
let recommended_space_view_classes = recommended_space_views_for_selection(ctx); | ||
let other_space_view_classes: IntSet<_> = space_view_class_registry | ||
.iter_registry() | ||
.map(|entry| entry.class.identifier()) | ||
.collect::<IntSet<SpaceViewClassIdentifier>>() | ||
.difference(&recommended_space_view_classes) | ||
.cloned() | ||
.collect(); | ||
|
||
ui.menu_button("Add to new Space View", |ui| { | ||
let buttons_for_space_view_classes = | ||
|ui: &mut egui::Ui, space_view_classes: &IntSet<SpaceViewClassIdentifier>| { | ||
for (identifier, display_name) in space_view_classes | ||
.iter() | ||
.map(|identifier| { | ||
( | ||
identifier, | ||
space_view_class_registry | ||
.get_class_or_log_error(identifier) | ||
.display_name(), | ||
) | ||
}) | ||
.sorted_by_key(|(_, display_name)| display_name.to_owned()) | ||
{ | ||
if ui.button(display_name).clicked() { | ||
create_space_view_for_selected_entities(ctx, *identifier); | ||
ui.close_menu(); | ||
} | ||
} | ||
}; | ||
|
||
ui.label(egui::WidgetText::from("Recommended:").italics()); | ||
if recommended_space_view_classes.is_empty() { | ||
ui.label("None"); | ||
} else { | ||
buttons_for_space_view_classes(ui, &recommended_space_view_classes); | ||
} | ||
|
||
if !other_space_view_classes.is_empty() { | ||
ui.label(egui::WidgetText::from("Others:").italics()); | ||
buttons_for_space_view_classes(ui, &other_space_view_classes); | ||
} | ||
}) | ||
.response | ||
} | ||
} | ||
|
||
/// Builds a list of compatible space views for the provided selection. | ||
fn recommended_space_views_for_selection( | ||
ctx: &ContextMenuContext<'_>, | ||
) -> IntSet<SpaceViewClassIdentifier> { | ||
re_tracing::profile_function!(); | ||
|
||
let entities_of_interest = ctx | ||
.selection | ||
.iter() | ||
.filter_map(|(item, _)| item.entity_path().cloned()) | ||
.collect::<IntSet<_>>(); | ||
|
||
let mut output: IntSet<SpaceViewClassIdentifier> = IntSet::default(); | ||
|
||
let space_view_class_registry = ctx.viewer_context.space_view_class_registry; | ||
let entity_db = ctx.viewer_context.entity_db; | ||
let applicable_entities_per_visualizer = | ||
space_view_class_registry.applicable_entities_for_visualizer_systems(entity_db.store_id()); | ||
|
||
for entry in space_view_class_registry.iter_registry() { | ||
let Some(suggested_root) = entry | ||
.class | ||
.recommended_root_for_entities(&entities_of_interest, entity_db) | ||
else { | ||
continue; | ||
}; | ||
|
||
let visualizable_entities = determine_visualizable_entities( | ||
&applicable_entities_per_visualizer, | ||
entity_db, | ||
&space_view_class_registry.new_visualizer_collection(entry.class.identifier()), | ||
&*entry.class, | ||
&suggested_root, | ||
); | ||
|
||
// We consider a space view class to be recommended if all selected entities are | ||
// "visualizable" with it. By "visualizable" we mean that either the entity itself, or any | ||
// of its sub-entities, are visualizable. | ||
|
||
let covered = entities_of_interest.iter().all(|entity| { | ||
visualizable_entities.0.iter().any(|(_, entities)| { | ||
entities | ||
.0 | ||
.iter() | ||
.any(|visualizable_entity| visualizable_entity.starts_with(entity)) | ||
}) | ||
}); | ||
|
||
if covered { | ||
output.insert(entry.class.identifier()); | ||
} | ||
} | ||
|
||
output | ||
} | ||
|
||
/// Creates a space view of the given class, with root set as origin, and a filter set to include all | ||
/// selected entities. Then, the selection is set to the new space view. | ||
fn create_space_view_for_selected_entities( | ||
ctx: &ContextMenuContext<'_>, | ||
identifier: SpaceViewClassIdentifier, | ||
) { | ||
let entities_of_interest = ctx | ||
.selection | ||
.iter() | ||
.filter_map(|(item, _)| item.entity_path().cloned()) | ||
.collect::<IntSet<_>>(); | ||
|
||
let origin = ctx | ||
.viewer_context | ||
.space_view_class_registry | ||
.get_class_or_log_error(&identifier) | ||
.recommended_root_for_entities(&entities_of_interest, ctx.viewer_context.entity_db) | ||
.unwrap_or_else(EntityPath::root); | ||
|
||
let mut filter = EntityPathFilter::default(); | ||
|
||
for path in entities_of_interest { | ||
filter.add_rule(RuleEffect::Include, EntityPathRule::including_subtree(path)); | ||
} | ||
|
||
let target_container_id = ctx | ||
.clicked_item_enclosing_container_id_and_position() | ||
.map(|(id, _)| id); | ||
|
||
let space_view = SpaceViewBlueprint::new(identifier, &origin, filter); | ||
|
||
let new_space_view = ctx.viewport_blueprint.add_space_views( | ||
std::iter::once(space_view), | ||
ctx.viewer_context, | ||
target_container_id, | ||
None, | ||
); | ||
if let Some(space_view_id) = new_space_view.first() { | ||
ctx.viewer_context | ||
.selection_state() | ||
.set_selection(Item::SpaceView(*space_view_id)); | ||
} | ||
ctx.viewport_blueprint | ||
.mark_user_interaction(ctx.viewer_context); | ||
} |
36 changes: 36 additions & 0 deletions
36
crates/re_viewport/src/context_menu/actions/add_space_view.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
use re_log_types::{EntityPath, EntityPathFilter}; | ||
use re_space_view::SpaceViewBlueprint; | ||
use re_viewer_context::{ContainerId, Item, SpaceViewClassIdentifier}; | ||
|
||
use crate::context_menu::{ContextMenuAction, ContextMenuContext}; | ||
|
||
/// Add a space view of the specific class | ||
pub(crate) struct AddSpaceViewAction(pub SpaceViewClassIdentifier); | ||
|
||
impl ContextMenuAction for AddSpaceViewAction { | ||
fn supports_item(&self, _ctx: &ContextMenuContext<'_>, item: &Item) -> bool { | ||
matches!(item, Item::Container(_)) | ||
} | ||
|
||
fn label(&self, ctx: &ContextMenuContext<'_>) -> String { | ||
ctx.viewer_context | ||
.space_view_class_registry | ||
.get_class_or_log_error(&self.0) | ||
.display_name() | ||
.to_owned() | ||
} | ||
|
||
fn process_container(&self, ctx: &ContextMenuContext<'_>, container_id: &ContainerId) { | ||
let space_view = | ||
SpaceViewBlueprint::new(self.0, &EntityPath::root(), EntityPathFilter::default()); | ||
|
||
ctx.viewport_blueprint.add_space_views( | ||
std::iter::once(space_view), | ||
ctx.viewer_context, | ||
Some(*container_id), | ||
None, | ||
); | ||
ctx.viewport_blueprint | ||
.mark_user_interaction(ctx.viewer_context); | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
crates/re_viewport/src/context_menu/actions/clone_space_view.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
use re_viewer_context::{Item, SpaceViewId}; | ||
|
||
use crate::context_menu::{ContextMenuAction, ContextMenuContext}; | ||
|
||
/// Clone a single space view | ||
pub(crate) struct CloneSpaceViewAction; | ||
|
||
impl ContextMenuAction for CloneSpaceViewAction { | ||
fn supports_item(&self, _ctx: &ContextMenuContext<'_>, item: &Item) -> bool { | ||
matches!(item, Item::SpaceView(_)) | ||
} | ||
|
||
fn label(&self, _ctx: &ContextMenuContext<'_>) -> String { | ||
"Clone".to_owned() | ||
} | ||
|
||
fn process_space_view(&self, ctx: &ContextMenuContext<'_>, space_view_id: &SpaceViewId) { | ||
if let Some(new_space_view_id) = ctx | ||
.viewport_blueprint | ||
.duplicate_space_view(space_view_id, ctx.viewer_context) | ||
{ | ||
ctx.viewer_context | ||
.selection_state() | ||
.set_selection(Item::SpaceView(new_space_view_id)); | ||
ctx.viewport_blueprint | ||
.mark_user_interaction(ctx.viewer_context); | ||
} | ||
} | ||
} |
Oops, something went wrong.