diff --git a/crates/re_space_view_time_series/src/space_view_class.rs b/crates/re_space_view_time_series/src/space_view_class.rs index 1155bc1439af..aaeb20eb0f7e 100644 --- a/crates/re_space_view_time_series/src/space_view_class.rs +++ b/crates/re_space_view_time_series/src/space_view_class.rs @@ -426,6 +426,8 @@ It can greatly improve performance (and readability) in such situations as it pr let mut plot_item_id_to_entity_path = HashMap::default(); + let mut is_resetting = false; + let egui_plot::PlotResponse { inner: _, response, @@ -444,7 +446,7 @@ It can greatly improve performance (and readability) in such situations as it pr let range_was_edited = state.last_range != y_range; state.last_range = y_range; - let is_resetting = plot_ui.response().double_clicked(); + is_resetting = plot_ui.response().double_clicked(); let current_auto = plot_ui.auto_bounds(); if let Some(y_range) = y_range { @@ -529,17 +531,26 @@ It can greatly improve performance (and readability) in such situations as it pr }) .map(|x| transform.position_from_point(&PlotPoint::new(x, 0.0)).x); - // Interact with the plot items (lines, scatters, etc.) - if let Some(entity_path) = hovered_plot_item - .and_then(|hovered_plot_item| plot_item_id_to_entity_path.get(&hovered_plot_item)) - { - ctx.select_hovered_on_click( - &response, - re_viewer_context::Item::InstancePath( - Some(query.space_view_id), - entity_path.clone().into(), - ), - ); + // If we are not resetting on this frame, interact with the plot items (lines, scatters, etc.) + if !is_resetting { + if let Some(hovered) = hovered_plot_item + .and_then(|hovered_plot_item| plot_item_id_to_entity_path.get(&hovered_plot_item)) + .map(|entity_path| { + re_viewer_context::Item::InstancePath( + Some(query.space_view_id), + entity_path.clone().into(), + ) + }) + .or_else(|| { + if response.hovered() { + Some(re_viewer_context::Item::SpaceView(query.space_view_id)) + } else { + None + } + }) + { + ctx.select_hovered_on_click(&response, hovered); + } } if let Some(mut time_x) = time_x { diff --git a/tests/python/release_checklist/check_hover_select_reset.py b/tests/python/release_checklist/check_hover_select_reset.py new file mode 100644 index 000000000000..eaa9a2b1ebba --- /dev/null +++ b/tests/python/release_checklist/check_hover_select_reset.py @@ -0,0 +1,96 @@ +from __future__ import annotations + +import os +from argparse import Namespace +from uuid import uuid4 + +import rerun as rr + +README = """ +# Hover, Select, and Reset + +This checks whether different UIs behave correctly with hover and selection. + +### Hover +For each of the views: +* Hover the view and verify it shows up as highlighted in the blueprint tree. +* Hover the entity and verify it shows up highlighted in the blueprint tree. + * For 2D and 3D views the entity itself should be outlined and show a hover element. + * For plot view, the line-series will not highlight, but the plot should show info about the point. + +### 2D/3D Select +For each of the views: +* Click on the background of the view, and verify the view becomes selected. +* Click on an entity, and verify the it becomes selected. + * For 2D and 3D views the selected instance will not be visible in the blueprint tree. + * If you think this is unexpected, create an issue. + * Double-click the entity and verify that it becomes selected and highlighted in the blueprint tree. + +### Reset +For each of the views: +* Zoom and/or pan the view +* Double-click the background of the view and verify it resets the view to its default state. +""" + + +def log_readme() -> None: + rr.log("readme", rr.TextDocument(README, media_type=rr.MediaType.MARKDOWN), timeless=True) + + +def log_plots() -> None: + from math import cos, sin, tau + + rr.log("plots/cos", rr.SeriesPoint()) + + for t in range(0, int(tau * 2 * 10.0)): + rr.set_time_sequence("frame_nr", t) + + sin_of_t = sin(float(t) / 10.0) + rr.log("plots/sin", rr.Scalar(sin_of_t)) + + cos_of_t = cos(float(t) / 10.0) + rr.log("plots/cos", rr.Scalar(cos_of_t)) + + +def log_points_3d() -> None: + from numpy.random import default_rng + + rng = default_rng(12345) + + positions = rng.uniform(-5, 5, size=[10, 3]) + colors = rng.uniform(0, 255, size=[10, 3]) + radii = rng.uniform(0, 1, size=[10]) + + rr.log("3d/points", rr.Points3D(positions, colors=colors, radii=radii)) + + +def log_points_2d() -> None: + from numpy.random import default_rng + + rng = default_rng(12345) + + positions = rng.uniform(-5, 5, size=[10, 2]) + colors = rng.uniform(0, 255, size=[10, 3]) + radii = rng.uniform(0, 1, size=[10]) + + rr.log("2d/points", rr.Points2D(positions, colors=colors, radii=radii)) + + +def run(args: Namespace) -> None: + # TODO(cmc): I have no idea why this works without specifying a `recording_id`, but + # I'm not gonna rely on it anyway. + rr.script_setup(args, f"{os.path.basename(__file__)}", recording_id=uuid4()) + + log_readme() + log_plots() + log_points_3d() + log_points_2d() + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Interactive release checklist") + rr.script_add_args(parser) + args = parser.parse_args() + run(args)