diff --git a/Cargo.lock b/Cargo.lock index 77c6afb5ada0..d10a85d82f82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4879,6 +4879,7 @@ dependencies = [ "re_types", "re_ui", "re_viewer_context", + "re_viewport_blueprint", ] [[package]] diff --git a/crates/re_entity_db/src/entity_properties.rs b/crates/re_entity_db/src/entity_properties.rs index 63fb8d54501a..4493d2e0bba3 100644 --- a/crates/re_entity_db/src/entity_properties.rs +++ b/crates/re_entity_db/src/entity_properties.rs @@ -114,15 +114,6 @@ pub struct EntityProperties { /// Used to scale the radii of the points in the resulting point cloud. pub backproject_radius_scale: EditableAutoValue, // TODO(andreas): should be a component on the DepthImage archetype. - /// Should the legend be shown (for plot space views). - pub show_legend: EditableAutoValue, // TODO(andreas): BarChart is still using it, we already have the legend archteype! - - /// The location of the legend (for plot space views). - /// - /// This is an Option instead of an EditableAutoValue to let each space view class decide on - /// what's the best default. - pub legend_location: Option, // TODO(andreas): BarChart is still using it, we already have the legend archteype! - /// What kind of data aggregation to perform (for plot space views). pub time_series_aggregator: EditableAutoValue, // TODO(andreas): Should be a component probably on SeriesLine, but today it would become a view property. } @@ -136,8 +127,6 @@ impl Default for EntityProperties { backproject_depth: EditableAutoValue::Auto(true), depth_from_world_scale: EditableAutoValue::Auto(1.0), backproject_radius_scale: EditableAutoValue::Auto(1.0), - show_legend: EditableAutoValue::Auto(true), - legend_location: None, time_series_aggregator: EditableAutoValue::Auto(TimeSeriesAggregator::default()), } } @@ -162,8 +151,6 @@ impl EntityProperties { .or(&child.backproject_radius_scale) .clone(), - show_legend: self.show_legend.or(&child.show_legend).clone(), - legend_location: self.legend_location.or(child.legend_location), time_series_aggregator: self .time_series_aggregator .or(&child.time_series_aggregator) @@ -194,8 +181,6 @@ impl EntityProperties { .or(&self.backproject_radius_scale) .clone(), - show_legend: other.show_legend.or(&self.show_legend).clone(), - legend_location: other.legend_location.or(self.legend_location), time_series_aggregator: other .time_series_aggregator .or(&self.time_series_aggregator) @@ -211,8 +196,6 @@ impl EntityProperties { backproject_depth, depth_from_world_scale, backproject_radius_scale, - show_legend, - legend_location, time_series_aggregator, } = self; @@ -221,8 +204,6 @@ impl EntityProperties { || backproject_depth.has_edits(&other.backproject_depth) || depth_from_world_scale.has_edits(&other.depth_from_world_scale) || backproject_radius_scale.has_edits(&other.backproject_radius_scale) - || show_legend.has_edits(&other.show_legend) - || *legend_location != other.legend_location || time_series_aggregator.has_edits(&other.time_series_aggregator) } } diff --git a/crates/re_space_view_bar_chart/Cargo.toml b/crates/re_space_view_bar_chart/Cargo.toml index 8dcf8ba67bd3..1a84e18442c0 100644 --- a/crates/re_space_view_bar_chart/Cargo.toml +++ b/crates/re_space_view_bar_chart/Cargo.toml @@ -29,6 +29,7 @@ re_tracing.workspace = true re_types.workspace = true re_ui.workspace = true re_viewer_context.workspace = true +re_viewport_blueprint.workspace = true egui_plot.workspace = true egui.workspace = true diff --git a/crates/re_space_view_bar_chart/src/space_view_class.rs b/crates/re_space_view_bar_chart/src/space_view_class.rs index 8079d2b02c36..90b8a101f109 100644 --- a/crates/re_space_view_bar_chart/src/space_view_class.rs +++ b/crates/re_space_view_bar_chart/src/space_view_class.rs @@ -1,15 +1,19 @@ use egui::{ahash::HashMap, util::hash}; -use re_entity_db::{EditableAutoValue, EntityProperties, LegendCorner}; +use re_entity_db::EntityProperties; use re_log_types::EntityPath; -use re_space_view::{controls, suggest_space_view_for_each_entity}; +use re_space_view::{controls, suggest_space_view_for_each_entity, view_property_ui}; +use re_types::blueprint::archetypes::PlotLegend; +use re_types::blueprint::components::{Corner2D, Visible}; use re_types::View; use re_types::{datatypes::TensorBuffer, SpaceViewClassIdentifier}; -use re_ui::UiExt as _; +use re_ui::list_item; use re_viewer_context::{ auto_color, IdentifiedViewSystem as _, IndicatedEntities, PerVisualizer, SpaceViewClass, - SpaceViewClassRegistryError, SpaceViewId, SpaceViewState, SpaceViewSystemExecutionError, - ViewQuery, ViewerContext, VisualizableEntities, + SpaceViewClassRegistryError, SpaceViewId, SpaceViewState, SpaceViewStateExt, + SpaceViewSystemExecutionError, TypedComponentFallbackProvider, ViewQuery, ViewerContext, + VisualizableEntities, }; +use re_viewport_blueprint::ViewProperty; use super::visualizer_system::BarChartVisualizerSystem; @@ -106,54 +110,15 @@ impl SpaceViewClass for BarChartSpaceView { fn selection_ui( &self, - _ctx: &ViewerContext<'_>, + ctx: &ViewerContext<'_>, ui: &mut egui::Ui, - _state: &mut dyn SpaceViewState, + state: &mut dyn SpaceViewState, _space_origin: &EntityPath, - _space_view_id: SpaceViewId, - root_entity_properties: &mut EntityProperties, + space_view_id: SpaceViewId, + _root_entity_properties: &mut EntityProperties, ) -> Result<(), SpaceViewSystemExecutionError> { - ui.selection_grid("bar_chart_selection_ui").show(ui, |ui| { - ui.grid_left_hand_label("Legend"); - - ui.vertical(|ui| { - let mut selected = *root_entity_properties.show_legend.get(); - if ui.re_checkbox(&mut selected, "Visible").changed() { - root_entity_properties.show_legend = EditableAutoValue::UserEdited(selected); - } - - let mut corner = root_entity_properties - .legend_location - .unwrap_or(LegendCorner::RightTop); - - egui::ComboBox::from_id_source("legend_corner") - .selected_text(corner.to_string()) - .show_ui(ui, |ui| { - ui.selectable_value( - &mut corner, - LegendCorner::LeftTop, - LegendCorner::LeftTop.to_string(), - ); - ui.selectable_value( - &mut corner, - LegendCorner::RightTop, - LegendCorner::RightTop.to_string(), - ); - ui.selectable_value( - &mut corner, - LegendCorner::LeftBottom, - LegendCorner::LeftBottom.to_string(), - ); - ui.selectable_value( - &mut corner, - LegendCorner::RightBottom, - LegendCorner::RightBottom.to_string(), - ); - }); - - root_entity_properties.legend_location = Some(corner); - }); - ui.end_row(); + list_item::list_item_scope(ui, "time_series_selection_ui", |ui| { + view_property_ui::(ctx, ui, space_view_id, self, state); }); Ok(()) @@ -163,13 +128,18 @@ impl SpaceViewClass for BarChartSpaceView { &self, ctx: &ViewerContext<'_>, ui: &mut egui::Ui, - _state: &mut dyn SpaceViewState, - root_entity_properties: &EntityProperties, + state: &mut dyn SpaceViewState, + _root_entity_properties: &EntityProperties, query: &ViewQuery<'_>, system_output: re_viewer_context::SystemExecutionOutput, ) -> Result<(), SpaceViewSystemExecutionError> { use egui_plot::{Bar, BarChart, Legend, Plot}; + let state = state.downcast_mut::<()>()?; + + let blueprint_db = ctx.blueprint_db(); + let view_id = query.space_view_id; + let charts = &system_output .view_systems .get::()? @@ -177,19 +147,18 @@ impl SpaceViewClass for BarChartSpaceView { let zoom_both_axis = !ui.input(|i| i.modifiers.contains(controls::ASPECT_SCROLL_MODIFIER)); + let plot_legend = + ViewProperty::from_archetype::(blueprint_db, ctx.blueprint_query, view_id); + let legend_visible = plot_legend.component_or_fallback::(ctx, self, state)?; + let legend_corner = plot_legend.component_or_fallback::(ctx, self, state)?; + ui.scope(|ui| { let mut plot = Plot::new("bar_chart_plot") .clamp_grid(true) .allow_zoom([true, zoom_both_axis]); - if *root_entity_properties.show_legend { - plot = plot.legend( - Legend::default().position(to_egui_plot_corner( - root_entity_properties - .legend_location - .unwrap_or(LegendCorner::RightTop), - )), - ); + if *legend_visible { + plot = plot.legend(Legend::default().position(legend_corner.into())); } let mut plot_item_id_to_entity_path = HashMap::default(); @@ -311,11 +280,12 @@ impl SpaceViewClass for BarChartSpaceView { } } -fn to_egui_plot_corner(value: LegendCorner) -> egui_plot::Corner { - match value { - LegendCorner::LeftTop => egui_plot::Corner::LeftTop, - LegendCorner::RightTop => egui_plot::Corner::RightTop, - LegendCorner::LeftBottom => egui_plot::Corner::LeftBottom, - LegendCorner::RightBottom => egui_plot::Corner::RightBottom, +impl TypedComponentFallbackProvider for BarChartSpaceView { + fn fallback_for(&self, _ctx: &re_viewer_context::QueryContext<'_>) -> Corner2D { + // Explicitly pick RightCorner2D::RightTop, we don't want to make this dependent on the (arbitrary) + // default of Corner2D + Corner2D::RightTop } } + +re_viewer_context::impl_component_fallback_provider!(BarChartSpaceView => [Corner2D]); diff --git a/crates/re_types/definitions/rerun/blueprint/views/bar_chart.fbs b/crates/re_types/definitions/rerun/blueprint/views/bar_chart.fbs index b32ae55bcfc7..5ed8b9630175 100644 --- a/crates/re_types/definitions/rerun/blueprint/views/bar_chart.fbs +++ b/crates/re_types/definitions/rerun/blueprint/views/bar_chart.fbs @@ -8,4 +8,6 @@ namespace rerun.blueprint.views; table BarChartView ( "attr.rerun.view_identifier": "BarChart" ) { + /// Configures the legend of the plot. + plot_legend: rerun.blueprint.archetypes.PlotLegend (order: 2000); } diff --git a/crates/re_types/src/blueprint/views/bar_chart_view.rs b/crates/re_types/src/blueprint/views/bar_chart_view.rs index ab283545b8d8..1ebd5d6ab949 100644 --- a/crates/re_types/src/blueprint/views/bar_chart_view.rs +++ b/crates/re_types/src/blueprint/views/bar_chart_view.rs @@ -24,17 +24,51 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// **View**: A bar chart view. #[derive(Clone, Debug)] -pub struct BarChartView {} +pub struct BarChartView { + /// Configures the legend of the plot. + pub plot_legend: crate::blueprint::archetypes::PlotLegend, +} impl ::re_types_core::SizeBytes for BarChartView { #[inline] fn heap_size_bytes(&self) -> u64 { - 0 + self.plot_legend.heap_size_bytes() } #[inline] fn is_pod() -> bool { - true + ::is_pod() + } +} + +impl> From for BarChartView { + fn from(v: T) -> Self { + Self { + plot_legend: v.into(), + } + } +} + +impl std::borrow::Borrow for BarChartView { + #[inline] + fn borrow(&self) -> &crate::blueprint::archetypes::PlotLegend { + &self.plot_legend + } +} + +impl std::ops::Deref for BarChartView { + type Target = crate::blueprint::archetypes::PlotLegend; + + #[inline] + fn deref(&self) -> &crate::blueprint::archetypes::PlotLegend { + &self.plot_legend + } +} + +impl std::ops::DerefMut for BarChartView { + #[inline] + fn deref_mut(&mut self) -> &mut crate::blueprint::archetypes::PlotLegend { + &mut self.plot_legend } } diff --git a/docs/content/reference/types/views/bar_chart_view.md b/docs/content/reference/types/views/bar_chart_view.md index 2d1bb40f1364..1a70c24db850 100644 --- a/docs/content/reference/types/views/bar_chart_view.md +++ b/docs/content/reference/types/views/bar_chart_view.md @@ -5,6 +5,13 @@ title: "BarChartView" A bar chart view. +## Properties + +### `plot_legend` +Configures the legend of the plot. + +* `corner`: To what corner the legend is aligned. +* `visible`: Whether the legend is shown at all. ## API reference links * 🐍 [Python API docs for `BarChartView`](https://ref.rerun.io/docs/python/stable/common/blueprint_views#rerun.blueprint.views.BarChartView) diff --git a/rerun_py/rerun_sdk/rerun/blueprint/views/bar_chart_view.py b/rerun_py/rerun_sdk/rerun/blueprint/views/bar_chart_view.py index 948a9a7422a2..8489d66419e6 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/views/bar_chart_view.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/views/bar_chart_view.py @@ -8,7 +8,7 @@ from ..._baseclasses import AsComponents from ...datatypes import EntityPathLike, Utf8Like -from .. import components as blueprint_components +from .. import archetypes as blueprint_archetypes, components as blueprint_components from ..api import SpaceView, SpaceViewContentsLike @@ -52,6 +52,7 @@ def __init__( contents: SpaceViewContentsLike = "$origin/**", name: Utf8Like | None = None, visible: blueprint_components.VisibleLike | None = None, + plot_legend: blueprint_archetypes.PlotLegend | blueprint_components.Corner2D | None = None, ) -> None: """ Construct a blueprint for a new BarChartView view. @@ -71,10 +72,17 @@ def __init__( Whether this view is visible. Defaults to true if not specified. + plot_legend: + Configures the legend of the plot. """ properties: dict[str, AsComponents] = {} + if plot_legend is not None: + if not isinstance(plot_legend, blueprint_archetypes.PlotLegend): + plot_legend = blueprint_archetypes.PlotLegend(plot_legend) + properties["PlotLegend"] = plot_legend + super().__init__( class_identifier="BarChart", origin=origin,