Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 5 additions & 2 deletions crates/rmf_site_editor/src/interaction/select_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ use replace_side::*;

pub mod select_anchor;
use rmf_site_format::{LiftCabin, Pending};
use rmf_site_picking::{CommonNodeErrors, Hover, Select, Selectable, SelectionFilter};
use rmf_site_picking::{
CommonNodeErrors, Hover, InspectionSettings, Select, Selectable, SelectionFilter,
};
pub use select_anchor::*;

use anyhow::Error as Anyhow;
Expand All @@ -37,6 +39,7 @@ pub const SELECT_ANCHOR_MODE_LABEL: &'static str = "select_anchor";
#[derive(SystemParam)]
pub struct InspectorFilter<'w, 's> {
selectables: Query<'w, 's, &'static Selectable, (Without<Preview>, Without<Pending>)>,
inspection_settings: Res<'w, InspectionSettings>,
}

impl<'w, 's> SelectionFilter for InspectorFilter<'w, 's> {
Expand All @@ -50,7 +53,7 @@ impl<'w, 's> SelectionFilter for InspectorFilter<'w, 's> {
Some(target)
}
fn on_click(&mut self, hovered: Hover) -> Option<Select> {
Some(Select::new(hovered.0))
Some(Select::new(hovered.0).multi_select(self.inspection_settings.multi_select))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ pub fn build_anchor_selection_workflow<State: 'static + Send + Sync>(
.streams
.select
.chain(builder)
.map_block(|s| s.0)
.map_block(|s| s.candidate)
.dispose_on_none()
.with_access(buffer)
.then(update_current)
Expand Down
6 changes: 4 additions & 2 deletions crates/rmf_site_editor/src/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,10 @@ fn handle_keyboard_input(
if keyboard_input.just_pressed(KeyCode::Delete)
|| keyboard_input.just_pressed(KeyCode::Backspace)
{
if let Some(selection) = selection.0 {
delete.write(Delete::new(selection));
if !selection.selected.is_empty() {
for e in &selection.selected {
delete.write(Delete::new(*e));
}
} else {
warn!("No selected entity to delete");
}
Expand Down
8 changes: 4 additions & 4 deletions crates/rmf_site_editor/src/site/deletion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,8 @@ fn cautious_delete(element: Entity, params: &mut DeletionParams) {
}
}

if **params.selection == Some(e) {
params.select.write(Select(None));
if params.selection.get_single() == Some(e) {
params.select.write(Select::new(None));
}
}

Expand Down Expand Up @@ -422,8 +422,8 @@ fn perform_deletions(all_to_delete: HashSet<Entity>, params: &mut DeletionParams
}
}

if **params.selection == Some(e) {
params.select.write(Select(None));
if params.selection.get_single() == Some(e) {
params.select.write(Select::new(None));
}

if **params.current_level == Some(e) {
Expand Down
4 changes: 2 additions & 2 deletions crates/rmf_site_editor/src/site/drawing_editor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,10 @@ fn make_drawing_default_selected(
current: Res<CurrentEditDrawing>,
) {
if selection.is_changed() {
if selection.0.is_none() {
if selection.get_single().is_none() {
if let Some(c) = current.target() {
let drawing_entity = c.drawing;
selection.0 = Some(drawing_entity);
selection.selected.insert(drawing_entity);
} else {
error!("No drawing while spawning drawing anchor");
};
Expand Down
2 changes: 1 addition & 1 deletion crates/rmf_site_editor/src/site/inclusion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ pub fn check_selected_is_included(
selection: Res<Selection>,
inclusion: Query<&Inclusion>,
) {
if selection.0.is_some_and(|e| {
if selection.get_single().is_some_and(|e| {
inclusion.get(e).is_ok_and(|v| match v {
Inclusion::Hidden => true,
_ => false,
Expand Down
4 changes: 4 additions & 0 deletions crates/rmf_site_editor/src/widgets/icons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pub struct Icons {
pub hide: Icon,
pub show: Icon,
pub home: Icon,
pub deselect: Icon,
}

impl FromWorld for Icons {
Expand Down Expand Up @@ -115,6 +116,7 @@ impl FromWorld for Icons {
let hide = IconBuilder::new("widgets/icons/hide.png", &asset_server);
let show = IconBuilder::new("widgets/icons/show.png", &asset_server);
let home = IconBuilder::new("widgets/icons/home.png", &asset_server);
let deselect = IconBuilder::new("widgets/icons/deselect.png", &asset_server);

// Note: Building the icons is a two-stage process because we cannot
// get the mutable EguiContext resource at the same time as the
Expand Down Expand Up @@ -146,6 +148,7 @@ impl FromWorld for Icons {
hide: hide.build(&mut egui_context),
show: show.build(&mut egui_context),
home: home.build(&mut egui_context),
deselect: deselect.build(&mut egui_context),
}
}
}
Expand Down Expand Up @@ -182,6 +185,7 @@ fn add_widgets_icons(app: &mut App) {
embedded_asset!(app, "src/", "icons/alignment.png");
embedded_asset!(app, "src/", "icons/alpha.png");
embedded_asset!(app, "src/", "icons/confirm.png");
embedded_asset!(app, "src/", "icons/deselect.png");
embedded_asset!(app, "src/", "icons/down.png");
embedded_asset!(app, "src/", "icons/edit.png");
embedded_asset!(app, "src/", "icons/empty.png");
Expand Down
1 change: 1 addition & 0 deletions crates/rmf_site_editor/src/widgets/icons/attribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* [`search.png`](https://thenounproject.com/icon/search-3743008/)
* [`merge.png`](https://thenounproject.com/icon/merge-3402180/)
* [`hide.png`](https://thenounproject.com/icon/hide-eye-796162/)
* [`deselect.png`](https://thenounproject.com/icon/delete-2261639/)
* `trash.png`: @mxgrey
* `selected.png`: @mxgrey
* `alignment.png`: @mxgrey
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright (C) 2025 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

use crate::{
site::{Delete, NameInSite},
widgets::{prelude::*, MultiEditPoseWidget},
Icons,
};
use bevy::{ecs::system::SystemParam, prelude::*};
use bevy_egui::egui::{Button, ImageButton, ScrollArea, Ui};
use rmf_site_egui::WidgetSystem;
use rmf_site_format::{InstanceMarker, SiteID};
use rmf_site_picking::{Hover, Select};

use smallvec::SmallVec;

const INSTANCES_VIEWER_HEIGHT: f32 = 200.0;

#[derive(SystemParam)]
pub struct InspectMultiSelection<'w, 's> {
icons: Res<'w, Icons>,
model_instances:
Query<'w, 's, (Entity, &'static NameInSite, &'static SiteID), With<InstanceMarker>>,
delete: EventWriter<'w, Delete>,
select: EventWriter<'w, Select>,
multi_edit_pose_widget: MultiEditPoseWidget<'w, 's>,
hover: EventWriter<'w, Hover>,
}

impl<'w, 's> WidgetSystem<SmallVec<[Entity; 16]>, ()> for InspectMultiSelection<'w, 's> {
fn show(
instances: SmallVec<[Entity; 16]>,
ui: &mut Ui,
state: &mut SystemState<Self>,
world: &mut World,
) -> () {
let mut params = state.get_mut(world);

params.show_widget(instances, ui);
}
}

impl<'w, 's> InspectMultiSelection<'w, 's> {
pub fn show_widget(&mut self, instances: SmallVec<[Entity; 16]>, ui: &mut Ui) {
ScrollArea::vertical()
.max_height(INSTANCES_VIEWER_HEIGHT)
.show(ui, |ui| {
for instance in &instances {
let Ok((instance_entity, instance_name, site_id)) =
self.model_instances.get(*instance)
else {
continue;
};

ui.horizontal(|ui| {
// Button for deselecting instance from current selection
let response = ui
.add(Button::image_and_text(
self.icons.deselect.egui(),
format!("#{}", site_id.0),
))
.on_hover_text("Deselect instance from current selections");

if response.clicked() {
self.select
.write(Select::new(Some(instance_entity)).multi_select(true));
} else if response.hovered() {
self.hover.write(Hover(Some(instance_entity)));
}

// Button for deleting instance from this site (all scenarios)
let response = ui
.add(ImageButton::new(self.icons.trash.egui()))
.on_hover_text("Remove instance from all scenarios");

if response.clicked() {
self.delete.write(Delete::new(instance_entity));
} else if response.hovered() {
self.hover.write(Hover(Some(instance_entity)));
}

// Name of selected model instance
ui.label(format!("{}", instance_name.0));
});
}

ui.separator();

self.multi_edit_pose_widget.show_widget(instances, ui);
});
}
}
18 changes: 15 additions & 3 deletions crates/rmf_site_editor/src/widgets/inspector/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ pub use inspect_model_description::*;
pub mod inspect_motion;
pub use inspect_motion::*;

pub mod inspect_multi_selection;
pub use inspect_multi_selection::*;

pub mod inspect_mutex;
pub use inspect_mutex::*;

Expand Down Expand Up @@ -325,6 +328,7 @@ pub struct Inspector<'w, 's> {
children: Query<'w, 's, &'static Children>,
heading: Query<'w, 's, (Option<&'static Category>, Option<&'static SiteID>)>,
inspect_for_query: Query<'w, 's, &'static InspectFor>,
inspect_multi_selection: InspectMultiSelection<'w, 's>,
}

impl<'w, 's> WidgetSystem<Tile> for Inspector<'w, 's> {
Expand All @@ -350,8 +354,16 @@ impl<'w, 's> WidgetSystem<Tile> for Inspector<'w, 's> {
return;
};

let Some(mut selection) = selection.0 else {
ui.label("Nothing selected");
if selection.selected.len() > 1 {
let instances: SmallVec<[Entity; 16]> =
selection.selected.iter().cloned().collect();

let mut inspect_multi_selection = state.get_mut(world).inspect_multi_selection;
inspect_multi_selection.show_widget(instances, ui);
return;
}

let Some(mut selection) = selection.get_single() else {
return;
};

Expand All @@ -361,7 +373,7 @@ impl<'w, 's> WidgetSystem<Tile> for Inspector<'w, 's> {
selection = inspect_for.entity;
}

let params = state.get(world);
let params = state.get_mut(world);

let (label, site_id) =
if let Ok((category, site_id)) = params.heading.get(selection) {
Expand Down
3 changes: 3 additions & 0 deletions crates/rmf_site_editor/src/widgets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ use view_levels::*;
pub mod view_model_instances;
use view_model_instances::*;

pub mod multi_edit_pose;
use multi_edit_pose::*;

pub mod view_scenarios;
use view_scenarios::*;

Expand Down
Loading
Loading