Skip to content

Commit

Permalink
More context menu 4: create a new space view with selected entities
Browse files Browse the repository at this point in the history
  • Loading branch information
abey79 committed Mar 6, 2024
1 parent 70fc212 commit 9ac7d5f
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 41 deletions.
84 changes: 63 additions & 21 deletions crates/re_viewport/src/context_menu/actions.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use re_entity_db::InstancePath;
use re_log_types::{EntityPath, EntityPathFilter, EntityPathRule};
use re_log_types::{EntityPath, EntityPathFilter, EntityPathRule, RuleEffect};
use re_space_view::{DataQueryBlueprint, SpaceViewBlueprint};
use re_viewer_context::{ContainerId, Item, SpaceViewClassIdentifier, SpaceViewId};

Expand Down Expand Up @@ -325,15 +325,12 @@ pub(super) struct MoveContentsToNewContainerAction(pub egui_tiles::ContainerKind

impl ContextMenuAction for MoveContentsToNewContainerAction {
fn supports_selection(&self, ctx: &ContextMenuContext<'_>) -> bool {
if let Some((parent_container_id, _)) = Self::target_container_id_and_position(ctx) {
if let Some(parent_container) = ctx.viewport_blueprint.container(&parent_container_id) {
// same-kind linear containers cannot be nested
if (parent_container.container_kind == egui_tiles::ContainerKind::Vertical
|| parent_container.container_kind == egui_tiles::ContainerKind::Horizontal)
&& parent_container.container_kind == self.0
{
return false;
}
if let Some((parent_container, _)) = ctx.clicked_item_parent_and_position() {
if (parent_container.container_kind == egui_tiles::ContainerKind::Vertical
|| parent_container.container_kind == egui_tiles::ContainerKind::Horizontal)
&& parent_container.container_kind == self.0
{
return false;
}
}

Expand Down Expand Up @@ -366,8 +363,9 @@ impl ContextMenuAction for MoveContentsToNewContainerAction {

fn process_selection(&self, ctx: &ContextMenuContext<'_>) {
if let Some(root_container_id) = ctx.viewport_blueprint.root_container {
let (target_container_id, target_position) =
Self::target_container_id_and_position(ctx).unwrap_or((root_container_id, 0));
let (target_container_id, target_position) = ctx
.clicked_item_parent_id_and_position()
.unwrap_or((root_container_id, 0));

let contents = ctx
.selection
Expand All @@ -388,14 +386,58 @@ impl ContextMenuAction for MoveContentsToNewContainerAction {
}
}

impl MoveContentsToNewContainerAction {
fn target_container_id_and_position(
ctx: &ContextMenuContext<'_>,
) -> Option<(ContainerId, usize)> {
ctx.clicked_item
.clone()
.try_into()
.ok()
.and_then(|c| ctx.viewport_blueprint.find_parent_and_position_index(&c))
// ---

/// 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(super) struct AddEntitiesToNewSpaceViewAction(pub SpaceViewClassIdentifier);

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 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_selection(&self, ctx: &ContextMenuContext<'_>) {
let root = EntityPath::root();
let origin = ctx.clicked_item.entity_path().unwrap_or(&root);

let mut filter = EntityPathFilter::subtree_entity_filter(origin);
ctx.selection
.iter()
.filter_map(|(item, _)| item.entity_path())
.filter(|path| !path.is_child_of(origin))
.for_each(|path| {
filter.add_rule(
RuleEffect::Include,
EntityPathRule::including_subtree(path.clone()),
);
});

let target_container_id = ctx.clicked_item_parent_id_and_position().map(|(id, _)| id);

let space_view =
SpaceViewBlueprint::new(self.0, origin, DataQueryBlueprint::new(self.0, filter));

ctx.viewport_blueprint.add_space_views(
std::iter::once(space_view),
ctx.viewer_context,
target_container_id,
None,
);
ctx.viewport_blueprint
.mark_user_interaction(ctx.viewer_context);
}
}
87 changes: 67 additions & 20 deletions crates/re_viewport/src/context_menu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ use once_cell::sync::OnceCell;
use re_entity_db::InstancePath;
use re_viewer_context::{ContainerId, Item, Selection, SpaceViewId, ViewerContext};

use crate::ViewportBlueprint;
use crate::{ContainerBlueprint, Contents, ViewportBlueprint};

mod actions;
mod sub_menu;

use actions::{
AddContainerAction, AddSpaceViewAction, CloneSpaceViewAction, HideAction,
MoveContentsToNewContainerAction, RemoveAction, ShowAction,
AddContainerAction, AddEntitiesToNewSpaceViewAction, AddSpaceViewAction, CloneSpaceViewAction,
HideAction, MoveContentsToNewContainerAction, RemoveAction, ShowAction,
};
use sub_menu::SubMenu;

Expand Down Expand Up @@ -126,23 +126,37 @@ fn action_list(
.collect(),
}),
],
vec![Box::new(SubMenu {
label: "Move to new container".to_owned(),
actions: vec![
Box::new(MoveContentsToNewContainerAction(
egui_tiles::ContainerKind::Tabs,
)),
Box::new(MoveContentsToNewContainerAction(
egui_tiles::ContainerKind::Horizontal,
)),
Box::new(MoveContentsToNewContainerAction(
egui_tiles::ContainerKind::Vertical,
)),
Box::new(MoveContentsToNewContainerAction(
egui_tiles::ContainerKind::Grid,
)),
],
})],
vec![
Box::new(SubMenu {
label: "Move to new container".to_owned(),
actions: vec![
Box::new(MoveContentsToNewContainerAction(
egui_tiles::ContainerKind::Tabs,
)),
Box::new(MoveContentsToNewContainerAction(
egui_tiles::ContainerKind::Horizontal,
)),
Box::new(MoveContentsToNewContainerAction(
egui_tiles::ContainerKind::Vertical,
)),
Box::new(MoveContentsToNewContainerAction(
egui_tiles::ContainerKind::Grid,
)),
],
}),
Box::new(SubMenu {
label: "Add to new space view".to_owned(),
actions: ctx
.space_view_class_registry
.iter_registry()
.sorted_by_key(|entry| entry.class.display_name())
.map(|entry| {
Box::new(AddEntitiesToNewSpaceViewAction(entry.class.identifier()))
as Box<dyn ContextMenuAction + Sync + Send>
})
.collect(),
}),
],
]
})
}
Expand Down Expand Up @@ -173,6 +187,11 @@ fn show_context_menu_for_selection(ctx: &ContextMenuContext<'_>, ui: &mut egui::

should_display_separator |= any_action_displayed;
}

// nothing was shown, make sure the context menu isn't empty
if !should_display_separator {
ui.label(egui::RichText::from("No action available for the current selection").italics());
}
}

/// Context information provided to context menu actions
Expand All @@ -183,6 +202,34 @@ struct ContextMenuContext<'a> {
clicked_item: &'a Item,
}

impl<'a> ContextMenuContext<'a> {
/// Return the clicked item's parent container id and position within it.
///
/// Valid only for space views, containers, and data results.
pub fn clicked_item_parent_id_and_position(&self) -> Option<(ContainerId, usize)> {
match self.clicked_item {
Item::SpaceView(space_view_id) | Item::DataResult(space_view_id, _) => {
Some(Contents::SpaceView(*space_view_id))
}
Item::Container(container_id) => Some(Contents::Container(*container_id)),
_ => None,
}
.and_then(|c: Contents| self.viewport_blueprint.find_parent_and_position_index(&c))
}

/// Return the clicked item's parent container and position within it.
///
/// Valid only for space views, containers, and data results.
pub fn clicked_item_parent_and_position(&self) -> Option<(&'a ContainerBlueprint, usize)> {
self.clicked_item_parent_id_and_position()
.and_then(|(container_id, pos)| {
self.viewport_blueprint
.container(&container_id)
.map(|container| (container, pos))
})
}
}

/// Context menu actions must implement this trait.
///
/// Actions must do three things, corresponding to three core methods:
Expand Down

0 comments on commit 9ac7d5f

Please sign in to comment.