Skip to content

Commit ffe8a10

Browse files
authored
Allow selection of entitites directly in the plot space view (#4959)
### What * depends on emilk/egui#3920 * Fixes #4952 * Closes #4828 Wires up the new plot item ids to our hover/selection mechanism. Moved a utility method from item_ui to ViewerContext since I didn't want to take a dependency on `re_data_ui` here (and this method had a todo about being in the wrong spot anyways 😄 ) https://github.com/rerun-io/rerun/assets/1220815/1ef61a4f-7e13-4661-90c6-fa53ea008823 DRAFT: Uses unpublished egui version! ### 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/4959/index.html) * Using examples from latest `main` build: [app.rerun.io](https://app.rerun.io/pr/4959/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/4959/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 - [PR Build Summary](https://build.rerun.io/pr/4959) - [Docs preview](https://rerun.io/preview/f3f871cc9d2504b9c12a445787116a69af91f47f/docs) <!--DOCS-PREVIEW--> - [Examples preview](https://rerun.io/preview/f3f871cc9d2504b9c12a445787116a69af91f47f/examples) <!--EXAMPLES-PREVIEW--> - [Recent benchmark results](https://build.rerun.io/graphs/crates.html) - [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)
1 parent d60c233 commit ffe8a10

File tree

16 files changed

+134
-88
lines changed

16 files changed

+134
-88
lines changed

Cargo.lock

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -277,13 +277,13 @@ debug = true
277277
# As a last resport, patch with a commit to our own repository.
278278
# ALWAYS document what PR the commit hash is part of, or when it was merged into the upstream trunk.
279279

280-
ecolor = { git = "https://github.com/emilk/egui.git", rev = "ab39420c2933d2e402999043375b64e7cf0ee9ed" } # egui master 2024-01-29
281-
eframe = { git = "https://github.com/emilk/egui.git", rev = "ab39420c2933d2e402999043375b64e7cf0ee9ed" } # egui master 2024-01-29
282-
egui = { git = "https://github.com/emilk/egui.git", rev = "ab39420c2933d2e402999043375b64e7cf0ee9ed" } # egui master 2024-01-29
283-
egui_extras = { git = "https://github.com/emilk/egui.git", rev = "ab39420c2933d2e402999043375b64e7cf0ee9ed" } # egui master 2024-01-29
284-
egui_plot = { git = "https://github.com/emilk/egui.git", rev = "ab39420c2933d2e402999043375b64e7cf0ee9ed" } # egui master 2024-01-29
285-
egui-wgpu = { git = "https://github.com/emilk/egui.git", rev = "ab39420c2933d2e402999043375b64e7cf0ee9ed" } # egui master 2024-01-29
286-
emath = { git = "https://github.com/emilk/egui.git", rev = "ab39420c2933d2e402999043375b64e7cf0ee9ed" } # egui master 2024-01-29
280+
ecolor = { git = "https://github.com/emilk/egui.git", rev = "ca513ce241c6511275e92b05b2953c38fa6086e1" } # egui Ids on plot items. 2024-01-30
281+
eframe = { git = "https://github.com/emilk/egui.git", rev = "ca513ce241c6511275e92b05b2953c38fa6086e1" } # egui Ids on plot items. 2024-01-30
282+
egui = { git = "https://github.com/emilk/egui.git", rev = "ca513ce241c6511275e92b05b2953c38fa6086e1" } # egui Ids on plot items. 2024-01-30
283+
egui_extras = { git = "https://github.com/emilk/egui.git", rev = "ca513ce241c6511275e92b05b2953c38fa6086e1" } # egui Ids on plot items. 2024-01-30
284+
egui_plot = { git = "https://github.com/emilk/egui.git", rev = "ca513ce241c6511275e92b05b2953c38fa6086e1" } # egui Ids on plot items. 2024-01-30
285+
egui-wgpu = { git = "https://github.com/emilk/egui.git", rev = "ca513ce241c6511275e92b05b2953c38fa6086e1" } # egui Ids on plot items. 2024-01-30
286+
emath = { git = "https://github.com/emilk/egui.git", rev = "ca513ce241c6511275e92b05b2953c38fa6086e1" } # egui Ids on plot items. 2024-01-30
287287

288288
# Useful while developing:
289289
# ecolor = { path = "../../egui/crates/ecolor" }

crates/re_data_ui/src/item_ui.rs

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use re_entity_db::{EntityTree, InstancePath};
66
use re_log_types::{ComponentPath, EntityPath, TimeInt, Timeline};
77
use re_ui::SyntaxHighlighting;
88
use re_viewer_context::{
9-
DataQueryId, HoverHighlight, Item, Selection, SpaceViewId, SystemCommandSender, UiVerbosity,
10-
ViewerContext,
9+
DataQueryId, HoverHighlight, Item, SpaceViewId, UiVerbosity, ViewerContext,
1110
};
1211

1312
use super::DataUi;
@@ -331,7 +330,7 @@ pub fn cursor_interact_with_selectable(
331330
let is_item_hovered =
332331
ctx.selection_state().highlight_for_ui_element(&item) == HoverHighlight::Hovered;
333332

334-
select_hovered_on_click(ctx, &response, item);
333+
ctx.select_hovered_on_click(&response, item);
335334
// TODO(andreas): How to deal with shift click for selecting ranges?
336335

337336
if is_item_hovered {
@@ -341,38 +340,6 @@ pub fn cursor_interact_with_selectable(
341340
}
342341
}
343342

344-
// TODO(andreas): Move elsewhere, this is not directly part of the item_ui.
345-
pub fn select_hovered_on_click(
346-
ctx: &ViewerContext<'_>,
347-
response: &egui::Response,
348-
selection: impl Into<Selection>,
349-
) {
350-
re_tracing::profile_function!();
351-
352-
let mut selection = selection.into();
353-
selection.resolve_mono_instance_path_items(ctx);
354-
let selection_state = ctx.selection_state();
355-
356-
if response.hovered() {
357-
selection_state.set_hovered(selection.clone());
358-
}
359-
360-
if response.double_clicked() {
361-
if let Some((item, _)) = selection.first() {
362-
ctx.command_sender
363-
.send_system(re_viewer_context::SystemCommand::SetFocus(item.clone()));
364-
}
365-
}
366-
367-
if response.clicked() {
368-
if response.ctx.input(|i| i.modifiers.command) {
369-
selection_state.toggle_selection(selection);
370-
} else {
371-
selection_state.set_selection(selection);
372-
}
373-
}
374-
}
375-
376343
/// Displays the "hover card" (i.e. big tooltip) for an instance or an entity.
377344
///
378345
/// The entity hover card is displayed the provided instance path is a splat.

crates/re_space_view_bar_chart/src/space_view_class.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use egui::util::hash;
1+
use egui::{ahash::HashMap, util::hash};
22
use re_entity_db::{EditableAutoValue, EntityProperties, LegendCorner};
33
use re_log_types::EntityPath;
44
use re_space_view::{controls, suggest_space_view_for_each_entity};
@@ -132,11 +132,11 @@ impl SpaceViewClass for BarChartSpaceView {
132132

133133
fn ui(
134134
&self,
135-
_ctx: &ViewerContext<'_>,
135+
ctx: &ViewerContext<'_>,
136136
ui: &mut egui::Ui,
137137
_state: &mut Self::State,
138138
root_entity_properties: &EntityProperties,
139-
_query: &ViewQuery<'_>,
139+
query: &ViewQuery<'_>,
140140
system_output: re_viewer_context::SystemExecutionOutput,
141141
) -> Result<(), SpaceViewSystemExecutionError> {
142142
use egui_plot::{Bar, BarChart, Legend, Plot};
@@ -164,7 +164,13 @@ impl SpaceViewClass for BarChartSpaceView {
164164
);
165165
}
166166

167-
plot.show(ui, |plot_ui| {
167+
let mut plot_item_id_to_entity_path = HashMap::default();
168+
169+
let egui_plot::PlotResponse {
170+
response,
171+
hovered_plot_item,
172+
..
173+
} = plot.show(ui, |plot_ui| {
168174
fn create_bar_chart<N: Into<f64>>(
169175
ent_path: &EntityPath,
170176
values: impl Iterator<Item = N>,
@@ -251,9 +257,26 @@ impl SpaceViewClass for BarChartSpaceView {
251257
}
252258
};
253259

260+
let id = egui::Id::new(ent_path.hash());
261+
plot_item_id_to_entity_path.insert(id, ent_path.clone());
262+
let chart = chart.id(id);
263+
254264
plot_ui.bar_chart(chart);
255265
}
256266
});
267+
268+
// Interact with the plot items.
269+
if let Some(entity_path) = hovered_plot_item
270+
.and_then(|hovered_plot_item| plot_item_id_to_entity_path.get(&hovered_plot_item))
271+
{
272+
ctx.select_hovered_on_click(
273+
&response,
274+
re_viewer_context::Item::InstancePath(
275+
Some(query.space_view_id),
276+
entity_path.clone().into(),
277+
),
278+
);
279+
}
257280
});
258281

259282
Ok(())

crates/re_space_view_spatial/src/ui.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -393,13 +393,13 @@ pub fn outline_config(gui_ctx: &egui::Context) -> OutlineConfig {
393393

394394
pub fn screenshot_context_menu(
395395
_ctx: &ViewerContext<'_>,
396-
response: egui::Response,
397-
) -> (egui::Response, Option<ScreenshotMode>) {
396+
_response: &egui::Response,
397+
) -> Option<ScreenshotMode> {
398398
#[cfg(not(target_arch = "wasm32"))]
399399
{
400400
if _ctx.app_options.experimental_space_view_screenshots {
401401
let mut take_screenshot = None;
402-
let response = response.context_menu(|ui| {
402+
_response.context_menu(|ui| {
403403
ui.style_mut().wrap = Some(false);
404404
if ui.button("Save screenshot to disk").clicked() {
405405
take_screenshot = Some(ScreenshotMode::SaveAndCopyToClipboard);
@@ -409,14 +409,14 @@ pub fn screenshot_context_menu(
409409
ui.close_menu();
410410
}
411411
});
412-
(response, take_screenshot)
412+
take_screenshot
413413
} else {
414-
(response, None)
414+
None
415415
}
416416
}
417417
#[cfg(target_arch = "wasm32")]
418418
{
419-
(response, None)
419+
None
420420
}
421421
}
422422

@@ -661,7 +661,7 @@ pub fn picking(
661661
});
662662
};
663663

664-
item_ui::select_hovered_on_click(ctx, &response, re_viewer_context::Selection(hovered_items));
664+
ctx.select_hovered_on_click(&response, re_viewer_context::Selection(hovered_items));
665665

666666
Ok(response)
667667
}

crates/re_space_view_spatial/src/ui_2d.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,8 +355,7 @@ pub fn view_2d(
355355
// ------------------------------------------------------------------------
356356

357357
// Screenshot context menu.
358-
let (_, screenshot_mode) = screenshot_context_menu(ctx, response);
359-
if let Some(mode) = screenshot_mode {
358+
if let Some(mode) = screenshot_context_menu(ctx, &response) {
360359
view_builder
361360
.schedule_screenshot(ctx.render_ctx, query.space_view_id.gpu_readback_id(), mode)
362361
.ok();

crates/re_space_view_spatial/src/ui_3d.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -581,8 +581,7 @@ pub fn view_3d(
581581
}
582582

583583
// Screenshot context menu.
584-
let (_, screenshot_mode) = screenshot_context_menu(ctx, response);
585-
if let Some(mode) = screenshot_mode {
584+
if let Some(mode) = screenshot_context_menu(ctx, &response) {
586585
view_builder
587586
.schedule_screenshot(ctx.render_ctx, query.space_view_id.gpu_readback_id(), mode)
588587
.ok();

crates/re_space_view_time_series/src/space_view_class.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use egui::ahash::HashSet;
1+
use egui::ahash::{HashMap, HashSet};
22
use egui_plot::{Legend, Line, Plot, PlotPoint, Points};
33

44
use re_data_store::TimeType;
@@ -353,10 +353,13 @@ impl SpaceViewClass for TimeSeriesSpaceView {
353353
plot = plot.x_grid_spacer(move |spacer| ns_grid_spacer(canvas_size, &spacer));
354354
}
355355

356+
let mut plot_item_id_to_entity_path = HashMap::default();
357+
356358
let egui_plot::PlotResponse {
357359
inner: _,
358360
response,
359361
transform,
362+
hovered_plot_item,
360363
} = plot.show(ui, |plot_ui| {
361364
if plot_ui.response().secondary_clicked() {
362365
let mut time_ctrl_write = ctx.rec_cfg.time_ctrl.write();
@@ -376,19 +379,23 @@ impl SpaceViewClass for TimeSeriesSpaceView {
376379
.collect::<Vec<_>>();
377380

378381
let color = line.color;
382+
let id = egui::Id::new(line.entity_path.hash());
383+
plot_item_id_to_entity_path.insert(id, line.entity_path.clone());
379384

380385
match line.kind {
381386
PlotSeriesKind::Continuous => plot_ui.line(
382387
Line::new(points)
383388
.name(&line.label)
384389
.color(color)
385-
.width(line.width),
390+
.width(line.width)
391+
.id(id),
386392
),
387393
PlotSeriesKind::Scatter => plot_ui.points(
388394
Points::new(points)
389395
.name(&line.label)
390396
.color(color)
391-
.radius(line.width),
397+
.radius(line.width)
398+
.id(id),
392399
),
393400
// Break up the chart. At some point we might want something fancier.
394401
PlotSeriesKind::Clear => {}
@@ -418,6 +425,19 @@ impl SpaceViewClass for TimeSeriesSpaceView {
418425
})
419426
.map(|x| transform.position_from_point(&PlotPoint::new(x, 0.0)).x);
420427

428+
// Interact with the plot items (lines, scatters, etc.)
429+
if let Some(entity_path) = hovered_plot_item
430+
.and_then(|hovered_plot_item| plot_item_id_to_entity_path.get(&hovered_plot_item))
431+
{
432+
ctx.select_hovered_on_click(
433+
&response,
434+
re_viewer_context::Item::InstancePath(
435+
Some(query.space_view_id),
436+
entity_path.clone().into(),
437+
),
438+
);
439+
}
440+
421441
if let Some(mut time_x) = time_x {
422442
let interact_radius = ui.style().interaction.resize_grab_radius_side;
423443
let line_rect = egui::Rect::from_x_y_ranges(time_x..=time_x, response.rect.y_range())

0 commit comments

Comments
 (0)