diff --git a/crates/re_log_types/src/path/entity_path_expr.rs b/crates/re_log_types/src/path/entity_path_expr.rs index 3cc99e86dde8..4f2d5962dcd2 100644 --- a/crates/re_log_types/src/path/entity_path_expr.rs +++ b/crates/re_log_types/src/path/entity_path_expr.rs @@ -65,13 +65,14 @@ impl From<&str> for EntityPathExpr { #[inline] fn from(path: &str) -> Self { if let Some(path) = path.strip_suffix('/') { + let path = path.trim_start_matches('/'); if path.is_empty() { Self::Recursive(EntityPath::root()) } else { Self::Recursive(EntityPath::from(path)) } } else { - Self::Exact(EntityPath::from(path)) + Self::Exact(EntityPath::from(path.trim_start_matches('/'))) } } } diff --git a/crates/re_space_view/src/data_query_blueprint.rs b/crates/re_space_view/src/data_query_blueprint.rs index 0fe2d1b0237c..e887344dca21 100644 --- a/crates/re_space_view/src/data_query_blueprint.rs +++ b/crates/re_space_view/src/data_query_blueprint.rs @@ -3,10 +3,11 @@ use once_cell::sync::Lazy; use re_data_store::{ EntityProperties, EntityPropertiesComponent, EntityPropertyMap, EntityTree, StoreDb, }; -use re_log_types::{EntityPath, EntityPathExpr}; +use re_log_types::{DataRow, EntityPath, EntityPathExpr, RowId, TimePoint}; use re_viewer_context::{ DataQueryId, DataQueryResult, DataResult, DataResultHandle, DataResultNode, DataResultTree, EntitiesPerSystem, EntitiesPerSystemPerClass, SpaceViewClassName, SpaceViewId, StoreContext, + SystemCommand, SystemCommandSender as _, ViewerContext, }; use slotmap::SlotMap; use smallvec::SmallVec; @@ -96,6 +97,64 @@ impl DataQueryBlueprint { .join(&Self::RECURSIVE_OVERRIDES_PREFIX.into()), } } + + pub fn add_entity_exclusion(&self, ctx: &ViewerContext<'_>, expr: EntityPathExpr) { + let mut edited = false; + + let mut inclusions: Vec = self + .expressions + .inclusions + .iter() + .filter(|exp| !exp.as_str().is_empty()) + .map(|exp| EntityPathExpr::from(exp.as_str())) + .collect(); + + let mut exclusions: Vec = self + .expressions + .exclusions + .iter() + .filter(|exp| !exp.as_str().is_empty()) + .map(|exp| EntityPathExpr::from(exp.as_str())) + .collect(); + + inclusions.retain(|inc_expr| { + if inc_expr == &expr { + edited = true; + false + } else { + true + } + }); + + if !exclusions.iter().any(|exc_expr| exc_expr == &expr) { + edited = true; + exclusions.push(expr); + } + + if edited { + let timepoint = TimePoint::timeless(); + + let expressions_component = QueryExpressions { + inclusions: inclusions.iter().map(|s| s.to_string().into()).collect(), + exclusions: exclusions.iter().map(|s| s.to_string().into()).collect(), + }; + + let row = DataRow::from_cells1_sized( + RowId::random(), + self.id.as_entity_path(), + timepoint.clone(), + 1, + [expressions_component], + ) + .unwrap(); + + ctx.command_sender + .send_system(SystemCommand::UpdateBlueprint( + ctx.store_context.blueprint.store_id().clone(), + vec![row], + )); + } + } } impl DataQuery for DataQueryBlueprint { diff --git a/crates/re_viewer/src/ui/selection_panel.rs b/crates/re_viewer/src/ui/selection_panel.rs index e6777af83a9f..1e6c1b03a68e 100644 --- a/crates/re_viewer/src/ui/selection_panel.rs +++ b/crates/re_viewer/src/ui/selection_panel.rs @@ -392,6 +392,8 @@ fn blueprint_ui( match item { Item::SpaceView(space_view_id) => { ui.horizontal(|ui| { + // TODO(#4377): Don't bother showing add/remove entities dialog since it's broken + /* if ui .button("Add/remove Entities") .on_hover_text("Manually add or remove Entities from the Space View") @@ -400,6 +402,7 @@ fn blueprint_ui( viewport .show_add_remove_entities_window(*space_view_id); } + */ if ui .button("Clone Space View") @@ -415,7 +418,7 @@ fn blueprint_ui( } }); - if let Some(space_view) = viewport.blueprint.space_view(space_view_id) { + if let Some(space_view) = viewport.blueprint.space_view_mut(space_view_id) { if let Some(query) = space_view.queries.first() { let inclusions = query.expressions.inclusions.join("\n"); let mut edited_inclusions = inclusions.clone(); @@ -449,6 +452,8 @@ fn blueprint_ui( ctx.store_context.blueprint.store_id().clone(), vec![row], )); + + space_view.entities_determined_by_user = true; } } } diff --git a/crates/re_viewport/src/space_view.rs b/crates/re_viewport/src/space_view.rs index 85e599240a48..51fe3c33defd 100644 --- a/crates/re_viewport/src/space_view.rs +++ b/crates/re_viewport/src/space_view.rs @@ -2,6 +2,7 @@ use ahash::HashSet; use re_data_store::{EntityPath, EntityProperties, StoreDb, TimeInt, VisibleHistory}; use re_data_store::{EntityPropertiesComponent, EntityPropertyMap}; +use re_log_types::EntityPathExpr; use re_renderer::ScreenshotProcessor; use re_space_view::{ DataQueryBlueprint, EntityOverrides, PropertyResolver, ScreenshotMode, SpaceViewContents, @@ -327,6 +328,12 @@ impl SpaceViewBlueprint { override_path: entity_path, } } + + pub fn add_entity_exclusion(&self, ctx: &ViewerContext<'_>, expr: EntityPathExpr) { + if let Some(query) = self.queries.first() { + query.add_entity_exclusion(ctx, expr); + } + } } impl SpaceViewBlueprint { diff --git a/crates/re_viewport/src/viewport_blueprint.rs b/crates/re_viewport/src/viewport_blueprint.rs index 85154a69becb..35df60097cf6 100644 --- a/crates/re_viewport/src/viewport_blueprint.rs +++ b/crates/re_viewport/src/viewport_blueprint.rs @@ -429,13 +429,17 @@ pub fn sync_space_view( add_delta_from_single_component(deltas, &space_view.entity_path(), &timepoint, component); - for query in &space_view.queries { - add_delta_from_single_component( - deltas, - &query.id.as_entity_path(), - &timepoint, - query.expressions.clone(), - ); + // The only time we need to create a query is if this is a new space-view. All other edits + // happen directly via `UpdateBlueprint` commands. + if snapshot.is_none() { + for query in &space_view.queries { + add_delta_from_single_component( + deltas, + &query.id.as_entity_path(), + &timepoint, + query.expressions.clone(), + ); + } } } } diff --git a/crates/re_viewport/src/viewport_blueprint_ui.rs b/crates/re_viewport/src/viewport_blueprint_ui.rs index fadf14e04dea..10e489e2ea2a 100644 --- a/crates/re_viewport/src/viewport_blueprint_ui.rs +++ b/crates/re_viewport/src/viewport_blueprint_ui.rs @@ -3,6 +3,7 @@ use itertools::Itertools; use re_data_store::InstancePath; use re_data_ui::item_ui; +use re_log_types::EntityPathExpr; use re_ui::list_item::ListItem; use re_ui::ReUi; use re_viewer_context::{ @@ -311,8 +312,10 @@ impl ViewportBlueprint<'_> { let response = remove_button_ui(re_ui, ui, "Remove Entity from the Space View"); if response.clicked() { - // TODO(#4377): Fix entity removal - //space_view.contents.remove_entity(entity_path); + space_view.add_entity_exclusion( + ctx, + EntityPathExpr::Exact(entity_path.clone()), + ); space_view.entities_determined_by_user = true; } @@ -374,16 +377,9 @@ impl ViewportBlueprint<'_> { }); if remove_group { - // TODO(#4377): Fix group removal - /* - if let Some(group_handle) = space_view - .contents - .group_handle_for_entity_path(entity_path) - { - space_view.contents.remove_group(group_handle); - space_view.entities_determined_by_user = true; - } - */ + space_view + .add_entity_exclusion(ctx, EntityPathExpr::Recursive(entity_path.clone())); + space_view.entities_determined_by_user = true; } response