From be6d679777791ff5567778da953f6caa48c29f40 Mon Sep 17 00:00:00 2001
From: Antoine Beyeler <49431240+abey79@users.noreply.github.com>
Date: Thu, 23 May 2024 13:12:19 +0200
Subject: [PATCH 01/10] Remove the ability to display multiple tensors in a
single space view (#6392)
### What
- Fixes https://github.com/rerun-io/rerun/issues/6387
Configuring a tensor space view with a query returning more than 1
tensor is now displayed as follows:
This is consistent with the existing behaviour of TextDocument:
This PR is very minimal and aims at status quo consistency. This could
be further improved:
- https://github.com/rerun-io/rerun/issues/6393
### 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 examples from latest `main` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6392?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6392?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
* [x] If applicable, add a new check to the [release
checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)!
- [PR Build Summary](https://build.rerun.io/pr/6392)
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)
To run all checks from `main`, comment on the PR with `@rerun-bot
full-check`.
---
.../src/space_view_class.rs | 204 +++++++-----------
.../src/tensor_slice_to_gpu.rs | 12 +-
.../src/visualizer_system.rs | 5 +-
.../src/space_view_class.rs | 4 +-
docs/snippets/all/views/tensor.py | 10 +-
.../rerun/blueprint/views/tensor_view.py | 10 +-
.../check_mono_entity_views.py | 59 +++++
7 files changed, 153 insertions(+), 151 deletions(-)
create mode 100644 tests/python/release_checklist/check_mono_entity_views.py
diff --git a/crates/re_space_view_tensor/src/space_view_class.rs b/crates/re_space_view_tensor/src/space_view_class.rs
index e2b6d57008c6..50af4a4557b5 100644
--- a/crates/re_space_view_tensor/src/space_view_class.rs
+++ b/crates/re_space_view_tensor/src/space_view_class.rs
@@ -32,10 +32,20 @@ type ViewType = re_types::blueprint::views::TensorView;
#[derive(Default)]
pub struct ViewTensorState {
- /// Selects in [`Self::state_tensors`].
- pub selected_tensor: Option,
+ /// What slice are we viewing?
+ ///
+ /// This get automatically reset if/when the current tensor shape changes.
+ pub(crate) slice: SliceSelection,
- pub state_tensors: ahash::HashMap,
+ /// How we map values to colors.
+ pub(crate) color_mapping: ColorMapping,
+
+ /// Scaling, filtering, aspect ratio, etc for the rendered texture.
+ texture_settings: TextureSettings,
+
+ /// Last viewed tensor, copied each frame.
+ /// Used for the selection view.
+ tensor: Option<(RowId, DecodedTensor)>,
}
impl SpaceViewState for ViewTensorState {
@@ -58,88 +68,6 @@ pub struct SliceSelection {
pub selector_values: BTreeMap,
}
-pub struct PerTensorState {
- /// What slice are we vieiwing?
- slice: SliceSelection,
-
- /// How we map values to colors.
- color_mapping: ColorMapping,
-
- /// Scaling, filtering, aspect ratio, etc for the rendered texture.
- texture_settings: TextureSettings,
-
- /// Last viewed tensor, copied each frame.
- /// Used for the selection view.
- tensor: Option<(RowId, DecodedTensor)>,
-}
-
-impl PerTensorState {
- pub fn create(tensor_data_row_id: RowId, tensor: &DecodedTensor) -> Self {
- Self {
- slice: SliceSelection {
- dim_mapping: DimensionMapping::create(tensor.shape()),
- selector_values: Default::default(),
- },
- color_mapping: ColorMapping::default(),
- texture_settings: TextureSettings::default(),
- tensor: Some((tensor_data_row_id, tensor.clone())),
- }
- }
-
- pub fn slice(&self) -> &SliceSelection {
- &self.slice
- }
-
- pub fn color_mapping(&self) -> &ColorMapping {
- &self.color_mapping
- }
-
- pub fn ui(&mut self, ctx: &ViewerContext<'_>, ui: &mut egui::Ui) {
- let Some((tensor_data_row_id, tensor)) = &self.tensor else {
- ui.label("No Tensor shown in this Space View.");
- return;
- };
-
- let tensor_stats = ctx
- .cache
- .entry(|c: &mut TensorStatsCache| c.entry(*tensor_data_row_id, tensor));
- ctx.re_ui
- .selection_grid(ui, "tensor_selection_ui")
- .show(ui, |ui| {
- // We are in a bare Tensor view -- meaning / meter is unknown.
- let meaning = TensorDataMeaning::Unknown;
- let meter = None;
- tensor_summary_ui_grid_contents(
- ctx.re_ui,
- ui,
- tensor,
- tensor,
- meaning,
- meter,
- &tensor_stats,
- );
- self.texture_settings.ui(ctx.re_ui, ui);
- self.color_mapping.ui(ctx.render_ctx, ctx.re_ui, ui);
- });
-
- ui.separator();
- ui.strong("Dimension Mapping");
- dimension_mapping_ui(ctx.re_ui, ui, &mut self.slice.dim_mapping, tensor.shape());
- let default_mapping = DimensionMapping::create(tensor.shape());
- if ui
- .add_enabled(
- self.slice.dim_mapping != default_mapping,
- egui::Button::new("Reset mapping"),
- )
- .on_disabled_hover_text("The default is already set up")
- .on_hover_text("Reset dimension mapping to the default")
- .clicked()
- {
- self.slice.dim_mapping = DimensionMapping::create(tensor.shape());
- }
- }
-}
-
impl SpaceViewClass for TensorSpaceView {
fn identifier() -> SpaceViewClassIdentifier {
ViewType::identifier()
@@ -207,11 +135,51 @@ impl SpaceViewClass for TensorSpaceView {
_root_entity_properties: &mut EntityProperties,
) -> Result<(), SpaceViewSystemExecutionError> {
let state = state.downcast_mut::()?;
- if let Some(selected_tensor) = &state.selected_tensor {
- if let Some(state_tensor) = state.state_tensors.get_mut(selected_tensor) {
- state_tensor.ui(ctx, ui);
+
+ ctx.re_ui
+ .selection_grid(ui, "tensor_selection_ui")
+ .show(ui, |ui| {
+ if let Some((tensor_data_row_id, tensor)) = &state.tensor {
+ let tensor_stats = ctx
+ .cache
+ .entry(|c: &mut TensorStatsCache| c.entry(*tensor_data_row_id, tensor));
+
+ // We are in a bare Tensor view -- meaning / meter is unknown.
+ let meaning = TensorDataMeaning::Unknown;
+ let meter = None;
+ tensor_summary_ui_grid_contents(
+ ctx.re_ui,
+ ui,
+ tensor,
+ tensor,
+ meaning,
+ meter,
+ &tensor_stats,
+ );
+ }
+
+ state.texture_settings.ui(ctx.re_ui, ui);
+ state.color_mapping.ui(ctx.render_ctx, ctx.re_ui, ui);
+ });
+
+ if let Some((_, tensor)) = &state.tensor {
+ ui.separator();
+ ui.strong("Dimension Mapping");
+ dimension_mapping_ui(ctx.re_ui, ui, &mut state.slice.dim_mapping, tensor.shape());
+ let default_mapping = DimensionMapping::create(tensor.shape());
+ if ui
+ .add_enabled(
+ state.slice.dim_mapping != default_mapping,
+ egui::Button::new("Reset mapping"),
+ )
+ .on_disabled_hover_text("The default is already set up")
+ .on_hover_text("Reset dimension mapping to the default")
+ .clicked()
+ {
+ state.slice.dim_mapping = DimensionMapping::create(tensor.shape());
}
}
+
Ok(())
}
@@ -238,40 +206,26 @@ impl SpaceViewClass for TensorSpaceView {
let tensors = &system_output.view_systems.get::()?.tensors;
- if tensors.is_empty() {
- ui.centered_and_justified(|ui| ui.label("(empty)"));
- state.selected_tensor = None;
- } else {
- if let Some(selected_tensor) = &state.selected_tensor {
- if !tensors.contains_key(selected_tensor) {
- state.selected_tensor = None;
- }
- }
- if state.selected_tensor.is_none() {
- state.selected_tensor = Some(tensors.iter().next().unwrap().0.clone());
- }
-
- if tensors.len() > 1 {
- // Show radio buttons for the different tensors we have in this view - better than nothing!
- ui.horizontal(|ui| {
- for instance_path in tensors.keys() {
- let is_selected = state.selected_tensor.as_ref() == Some(instance_path);
- if ui.radio(is_selected, instance_path.to_string()).clicked() {
- state.selected_tensor = Some(instance_path.clone());
- }
- }
- });
- }
+ if tensors.len() > 1 {
+ state.tensor = None;
- if let Some(selected_tensor) = &state.selected_tensor {
- if let Some((tensor_data_row_id, tensor)) = tensors.get(selected_tensor) {
- let state_tensor = state
- .state_tensors
- .entry(selected_tensor.clone())
- .or_insert_with(|| PerTensorState::create(*tensor_data_row_id, tensor));
- view_tensor(ctx, ui, state_tensor, *tensor_data_row_id, tensor);
- }
+ egui::Frame {
+ inner_margin: re_ui::ReUi::view_padding().into(),
+ ..egui::Frame::default()
}
+ .show(ui, |ui| {
+ ui.label(format!(
+ "Can only show one tensor at a time; was given {}. Update the query so that it \
+ returns a single tensor entity and create additional views for the others.",
+ tensors.len()
+ ));
+ });
+ } else if let Some((tensor_data_row_id, tensor)) = tensors.first() {
+ state.tensor = Some((*tensor_data_row_id, tensor.clone()));
+ view_tensor(ctx, ui, state, *tensor_data_row_id, tensor);
+ } else {
+ state.tensor = None;
+ ui.centered_and_justified(|ui| ui.label("(empty)"));
}
Ok(())
@@ -281,14 +235,12 @@ impl SpaceViewClass for TensorSpaceView {
fn view_tensor(
ctx: &ViewerContext<'_>,
ui: &mut egui::Ui,
- state: &mut PerTensorState,
+ state: &mut ViewTensorState,
tensor_data_row_id: RowId,
tensor: &DecodedTensor,
) {
re_tracing::profile_function!();
- state.tensor = Some((tensor_data_row_id, tensor.clone()));
-
if !state.slice.dim_mapping.is_valid(tensor.num_dim()) {
state.slice.dim_mapping = DimensionMapping::create(tensor.shape());
}
@@ -339,7 +291,7 @@ fn view_tensor(
fn tensor_slice_ui(
ctx: &ViewerContext<'_>,
ui: &mut egui::Ui,
- state: &PerTensorState,
+ state: &ViewTensorState,
tensor_data_row_id: RowId,
tensor: &DecodedTensor,
dimension_labels: [(String, bool); 2],
@@ -358,7 +310,7 @@ fn tensor_slice_ui(
fn paint_tensor_slice(
ctx: &ViewerContext<'_>,
ui: &mut egui::Ui,
- state: &PerTensorState,
+ state: &ViewTensorState,
tensor_data_row_id: RowId,
tensor: &DecodedTensor,
) -> anyhow::Result<(egui::Response, egui::Painter, egui::Rect)> {
@@ -750,7 +702,7 @@ fn paint_axis_names(
}
}
-fn selectors_ui(ui: &mut egui::Ui, state: &mut PerTensorState, tensor: &TensorData) {
+fn selectors_ui(ui: &mut egui::Ui, state: &mut ViewTensorState, tensor: &TensorData) {
for selector in &state.slice.dim_mapping.selectors {
if !selector.visible {
continue;
diff --git a/crates/re_space_view_tensor/src/tensor_slice_to_gpu.rs b/crates/re_space_view_tensor/src/tensor_slice_to_gpu.rs
index 0f124d92dc40..71bddf04d4e7 100644
--- a/crates/re_space_view_tensor/src/tensor_slice_to_gpu.rs
+++ b/crates/re_space_view_tensor/src/tensor_slice_to_gpu.rs
@@ -12,7 +12,7 @@ use re_viewer_context::{
TensorStats,
};
-use crate::space_view_class::{selected_tensor_slice, PerTensorState, SliceSelection};
+use crate::space_view_class::{selected_tensor_slice, SliceSelection, ViewTensorState};
#[derive(thiserror::Error, Debug, PartialEq)]
pub enum TensorUploadError {
@@ -31,24 +31,22 @@ pub fn colormapped_texture(
tensor_data_row_id: RowId,
tensor: &DecodedTensor,
tensor_stats: &TensorStats,
- state: &PerTensorState,
+ state: &ViewTensorState,
) -> Result> {
re_tracing::profile_function!();
let range = tensor_data_range_heuristic(tensor_stats, tensor.dtype())
.map_err(|err| TextureManager2DError::DataCreation(err.into()))?;
let texture =
- upload_texture_slice_to_gpu(render_ctx, tensor_data_row_id, tensor, state.slice())?;
-
- let color_mapping = state.color_mapping();
+ upload_texture_slice_to_gpu(render_ctx, tensor_data_row_id, tensor, &state.slice)?;
Ok(ColormappedTexture {
texture,
range,
decode_srgb: false,
multiply_rgb_with_alpha: false,
- gamma: color_mapping.gamma,
- color_mapper: re_renderer::renderer::ColorMapper::Function(color_mapping.map),
+ gamma: state.color_mapping.gamma,
+ color_mapper: re_renderer::renderer::ColorMapper::Function(state.color_mapping.map),
shader_decoding: match tensor.buffer {
TensorBuffer::Nv12(_) => Some(ShaderDecoding::Nv12),
TensorBuffer::Yuy2(_) => Some(ShaderDecoding::Yuy2),
diff --git a/crates/re_space_view_tensor/src/visualizer_system.rs b/crates/re_space_view_tensor/src/visualizer_system.rs
index c0f0bfb05018..f1c2bc55cff9 100644
--- a/crates/re_space_view_tensor/src/visualizer_system.rs
+++ b/crates/re_space_view_tensor/src/visualizer_system.rs
@@ -9,7 +9,7 @@ use re_viewer_context::{
#[derive(Default)]
pub struct TensorSystem {
- pub tensors: std::collections::BTreeMap,
+ pub tensors: Vec<(RowId, DecodedTensor)>,
}
impl IdentifiedViewSystem for TensorSystem {
@@ -64,8 +64,7 @@ impl TensorSystem {
.entry(|c: &mut TensorDecodeCache| c.entry(row_id, tensor.value.0))
{
Ok(decoded_tensor) => {
- self.tensors
- .insert(ent_path.clone(), (row_id, decoded_tensor));
+ self.tensors.push((row_id, decoded_tensor));
}
Err(err) => {
re_log::warn_once!("Failed to decode decoding tensor at path {ent_path}: {err}");
diff --git a/crates/re_space_view_text_document/src/space_view_class.rs b/crates/re_space_view_text_document/src/space_view_class.rs
index 529a348a1b9a..2158c2e717f0 100644
--- a/crates/re_space_view_text_document/src/space_view_class.rs
+++ b/crates/re_space_view_text_document/src/space_view_class.rs
@@ -177,7 +177,9 @@ impl SpaceViewClass for TextDocumentSpaceView {
} else {
// TODO(jleibs): better handling for multiple results
ui.label(format!(
- "Can only show one text document at a time; was given {}.",
+ "Can only show one text document at a time; was given {}. Update \
+ the query so that it returns a single text document and create \
+ additional views for the others.",
text_document.text_entries.len()
));
}
diff --git a/docs/snippets/all/views/tensor.py b/docs/snippets/all/views/tensor.py
index 5f9d3c98c31c..8b31e6e69794 100644
--- a/docs/snippets/all/views/tensor.py
+++ b/docs/snippets/all/views/tensor.py
@@ -6,12 +6,8 @@
rr.init("rerun_example_tensor", spawn=True)
-tensor_one = np.random.randint(0, 256, (8, 6, 3, 5), dtype=np.uint8)
-rr.log("tensors/one", rr.Tensor(tensor_one, dim_names=("width", "height", "channel", "batch")))
-tensor_two = np.random.random_sample((10, 20, 30))
-rr.log("tensors/two", rr.Tensor(tensor_two))
-
-# Create a tensor view that displays both tensors (you can switch between them inside the view).
-blueprint = rrb.Blueprint(rrb.TensorView(origin="/tensors", name="Tensors"), collapse_panels=True)
+tensor = np.random.randint(0, 256, (8, 6, 3, 5), dtype=np.uint8)
+rr.log("tensor", rr.Tensor(tensor, dim_names=("width", "height", "channel", "batch")))
+blueprint = rrb.Blueprint(rrb.TensorView(origin="tensor", name="Tensor"), collapse_panels=True)
rr.send_blueprint(blueprint)
diff --git a/rerun_py/rerun_sdk/rerun/blueprint/views/tensor_view.py b/rerun_py/rerun_sdk/rerun/blueprint/views/tensor_view.py
index c1a08ca4a252..66241d5d6aef 100644
--- a/rerun_py/rerun_sdk/rerun/blueprint/views/tensor_view.py
+++ b/rerun_py/rerun_sdk/rerun/blueprint/views/tensor_view.py
@@ -26,14 +26,10 @@ class TensorView(SpaceView):
rr.init("rerun_example_tensor", spawn=True)
- tensor_one = np.random.randint(0, 256, (8, 6, 3, 5), dtype=np.uint8)
- rr.log("tensors/one", rr.Tensor(tensor_one, dim_names=("width", "height", "channel", "batch")))
- tensor_two = np.random.random_sample((10, 20, 30))
- rr.log("tensors/two", rr.Tensor(tensor_two))
-
- # Create a tensor view that displays both tensors (you can switch between them inside the view).
- blueprint = rrb.Blueprint(rrb.TensorView(origin="/tensors", name="Tensors"), collapse_panels=True)
+ tensor = np.random.randint(0, 256, (8, 6, 3, 5), dtype=np.uint8)
+ rr.log("tensor", rr.Tensor(tensor, dim_names=("width", "height", "channel", "batch")))
+ blueprint = rrb.Blueprint(rrb.TensorView(origin="tensor", name="Tensor"), collapse_panels=True)
rr.send_blueprint(blueprint)
```
diff --git a/tests/python/release_checklist/check_mono_entity_views.py b/tests/python/release_checklist/check_mono_entity_views.py
new file mode 100644
index 000000000000..676777b97e37
--- /dev/null
+++ b/tests/python/release_checklist/check_mono_entity_views.py
@@ -0,0 +1,59 @@
+from __future__ import annotations
+
+import os
+from argparse import Namespace
+from uuid import uuid4
+
+import numpy as np
+import rerun as rr
+import rerun.blueprint as rrb
+
+README = """
+# Mono-entity views
+
+This test checks that mono-entity views work as expected.
+
+- Reset the blueprint to default
+- Check each space view: when titled `ERROR`, they should display an error, and when titled `OK`, they should display the tensor or text document correctly.
+
+"""
+
+
+def log_readme() -> None:
+ rr.log("readme", rr.TextDocument(README, media_type=rr.MediaType.MARKDOWN), static=True)
+
+
+def log_data() -> None:
+ rr.log("tensor/one", rr.Tensor(np.random.rand(10, 10, 3, 5)))
+ rr.log("tensor/two", rr.Tensor(np.random.rand(3, 5, 7, 5)))
+
+ rr.log("txt/one", rr.TextDocument("Hello"))
+ rr.log("txt/two", rr.TextDocument("World"))
+
+
+def blueprint() -> rrb.BlueprintLike:
+ return rrb.Grid(
+ rrb.TextDocumentView(origin="readme"),
+ rrb.TensorView(origin="/tensor", name="ERROR"),
+ rrb.TensorView(origin="/tensor/one", name="OK"),
+ rrb.TensorView(origin="/tensor/two", name="OK"),
+ rrb.TextDocumentView(origin="/txt", name="ERROR"),
+ rrb.TextDocumentView(origin="/txt/one", name="OK"),
+ rrb.TextDocumentView(origin="/txt/two", name="OK"),
+ )
+
+
+def run(args: Namespace) -> None:
+ rr.script_setup(args, f"{os.path.basename(__file__)}", recording_id=uuid4(), default_blueprint=blueprint())
+
+ log_readme()
+ log_data()
+
+
+if __name__ == "__main__":
+ import argparse
+
+ parser = argparse.ArgumentParser(description="Interactive release checklist")
+ rr.script_add_args(parser)
+ args = parser.parse_args()
+ run(args)
From 0f3cfbfff03125339dc081ea0781371308b03ce5 Mon Sep 17 00:00:00 2001
From: Emil Ernerfeldt
Date: Fri, 24 May 2024 11:00:08 +0200
Subject: [PATCH 02/10] Smooth scrolling in 2D space views (#6422)
When scrolling with a discreet mouse wheel, the scrolling is now
smoothed.
### 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 examples from latest `main` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6422?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6422?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
* [x] If applicable, add a new check to the [release
checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)!
- [PR Build Summary](https://build.rerun.io/pr/6422)
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)
To run all checks from `main`, comment on the PR with `@rerun-bot
full-check`.
---
crates/re_space_view_spatial/src/ui_2d.rs | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/crates/re_space_view_spatial/src/ui_2d.rs b/crates/re_space_view_spatial/src/ui_2d.rs
index e406167b3e1a..3a24f7444bf8 100644
--- a/crates/re_space_view_spatial/src/ui_2d.rs
+++ b/crates/re_space_view_spatial/src/ui_2d.rs
@@ -80,12 +80,7 @@ fn ui_from_scene(
let mut pan_delta_in_ui = response.drag_delta();
if response.hovered() {
- // NOTE: we use `raw_scroll` instead of `smooth_scroll_delta` to avoid the
- // added latency of smoothing, which is really annoying on Mac trackpads.
- // The smoothing is only useful for users with discreet scroll wheels,
- // and they are likely to pan with dragging instead.
- // TODO(egui#4401): https://github.com/emilk/egui/issues/4401
- pan_delta_in_ui += response.ctx.input(|i| i.raw_scroll_delta);
+ pan_delta_in_ui += response.ctx.input(|i| i.smooth_scroll_delta);
}
if pan_delta_in_ui != Vec2::ZERO {
*visual_bounds =
From a6042f9a7c054b898f697524e136eae47fc7cebb Mon Sep 17 00:00:00 2001
From: Antoine Beyeler <49431240+abey79@users.noreply.github.com>
Date: Fri, 24 May 2024 12:27:45 +0200
Subject: [PATCH 03/10] Improve styling of list item's collapsing triangle
(#6426)
### What
- Part of #6418
Before:
![collapse-ui](https://github.com/rerun-io/rerun/assets/1148717/5bd9f55a-69a0-4d27-8393-26b8b376cf5d)
After:
![Export-1716534172555](https://github.com/rerun-io/rerun/assets/49431240/bc915eb9-9c45-4295-91f7-3ec889e957bc)
### 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 examples from latest `main` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6426?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6426?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
* [x] If applicable, add a new check to the [release
checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)!
- [PR Build Summary](https://build.rerun.io/pr/6426)
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)
To run all checks from `main`, comment on the PR with `@rerun-bot
full-check`.
---
crates/re_ui/src/list_item/list_item.rs | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/crates/re_ui/src/list_item/list_item.rs b/crates/re_ui/src/list_item/list_item.rs
index 1798a4307dba..df4a21e1922c 100644
--- a/crates/re_ui/src/list_item/list_item.rs
+++ b/crates/re_ui/src/list_item/list_item.rs
@@ -288,7 +288,12 @@ impl<'a> ListItem<'a> {
id.unwrap_or(ui.id()).with("collapsing_triangle"),
egui::Sense::click(),
);
- ReUi::paint_collapsing_triangle(ui, openness, triangle_rect.center(), &visuals);
+ ReUi::paint_collapsing_triangle(
+ ui,
+ openness,
+ triangle_rect.center(),
+ ui.style().interact(&triangle_response),
+ );
collapse_response = Some(triangle_response);
}
From 70dae59637a47566cd5b66ab37515687d9f7f15b Mon Sep 17 00:00:00 2001
From: Emil Ernerfeldt
Date: Fri, 24 May 2024 13:29:56 +0200
Subject: [PATCH 04/10] Fix `ListItem` bugs (#6398)
### What
First bug: scrolling would make columns expand/collapse
Second bug: id clashes (found by selecting multiple space views)
### 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 examples from latest `main` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6398?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6398?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
* [x] If applicable, add a new check to the [release
checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)!
- [PR Build Summary](https://build.rerun.io/pr/6398)
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)
To run all checks from `main`, comment on the PR with `@rerun-bot
full-check`.
---
crates/re_space_view/src/view_property_ui.rs | 2 +-
.../src/space_view_class.rs | 4 +-
crates/re_time_panel/src/lib.rs | 6 +-
.../hierarchical_drag_and_drop.rs | 6 +-
.../examples/re_ui_example/right_panel.rs | 8 +-
crates/re_ui/src/list_item/list_item.rs | 85 ++++----
crates/re_ui/src/list_item/scope.rs | 4 +-
crates/re_viewer/src/ui/selection_panel.rs | 2 +-
.../re_viewport/src/viewport_blueprint_ui.rs | 191 +++++++++---------
9 files changed, 154 insertions(+), 154 deletions(-)
diff --git a/crates/re_space_view/src/view_property_ui.rs b/crates/re_space_view/src/view_property_ui.rs
index 5801147cccf5..3b5a5ca70fe2 100644
--- a/crates/re_space_view/src/view_property_ui.rs
+++ b/crates/re_space_view/src/view_property_ui.rs
@@ -73,7 +73,7 @@ pub fn view_property_ui(
.interactive(false)
.show_hierarchical_with_children(
ui,
- A::name().full_name(),
+ ui.make_persistent_id(A::name().full_name()),
true,
list_item::LabelContent::new(A::display_name()),
sub_prop_ui,
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 e7c0bba54e3a..8af064c0340e 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
@@ -724,9 +724,9 @@ fn axis_ui(
.interactive(false)
.show_hierarchical_with_children(
ui,
- "time_series_selection_ui_y_axis",
+ ui.make_persistent_id("time_series_selection_ui_y_axis"),
true,
- list_item::LabelContent::new("Y Axis"),
+ list_item::LabelContent::new("Y axis"),
sub_prop_ui,
);
}
diff --git a/crates/re_time_panel/src/lib.rs b/crates/re_time_panel/src/lib.rs
index 00ed32636674..e13b0a24a64c 100644
--- a/crates/re_time_panel/src/lib.rs
+++ b/crates/re_time_panel/src/lib.rs
@@ -605,6 +605,10 @@ impl TimePanel {
.set_open(ui.ctx(), true);
}
+ // Globally unique id - should only be one of these in view at one time.
+ // We do this so that we can support "collapse/expand all" command.
+ let id = egui::Id::new(CollapseScope::StreamsTree.entity(tree.path.clone()));
+
let list_item::ShowCollapsingResponse {
item_response: response,
body_response,
@@ -613,7 +617,7 @@ impl TimePanel {
.force_hovered(is_item_hovered)
.show_hierarchical_with_children(
ui,
- CollapseScope::StreamsTree.entity(tree.path.clone()),
+ id,
default_open,
list_item::LabelContent::new(text)
.with_icon(guess_instance_path_icon(
diff --git a/crates/re_ui/examples/re_ui_example/hierarchical_drag_and_drop.rs b/crates/re_ui/examples/re_ui_example/hierarchical_drag_and_drop.rs
index 74d6ee43c9b0..a31793a582d0 100644
--- a/crates/re_ui/examples/re_ui_example/hierarchical_drag_and_drop.rs
+++ b/crates/re_ui/examples/re_ui_example/hierarchical_drag_and_drop.rs
@@ -276,13 +276,17 @@ impl HierarchicalDragAndDrop {
item_id: ItemId,
children: &Vec,
) {
+ // Globally unique id - should only be one of these in view at one time.
+ // We do this so that we can support "collapse/expand all" command.
+ let id = egui::Id::new(item_id);
+
let response = list_item::ListItem::new(re_ui)
.selected(self.selected(item_id))
.draggable(true)
.drop_target_style(self.target_container == Some(item_id))
.show_hierarchical_with_children(
ui,
- item_id,
+ id,
true,
list_item::LabelContent::new(format!("Container {item_id:?}")).subdued(true),
|re_ui, ui| {
diff --git a/crates/re_ui/examples/re_ui_example/right_panel.rs b/crates/re_ui/examples/re_ui_example/right_panel.rs
index 5aff241633ea..e9bf12da0dc8 100644
--- a/crates/re_ui/examples/re_ui_example/right_panel.rs
+++ b/crates/re_ui/examples/re_ui_example/right_panel.rs
@@ -131,7 +131,7 @@ impl RightPanel {
re_ui.list_item().show_hierarchical_with_children(
ui,
- "label content features",
+ ui.make_persistent_id("label content features"),
true,
list_item::LabelContent::new("LabelContent features:"),
|re_ui, ui| {
@@ -203,7 +203,7 @@ impl RightPanel {
re_ui.list_item().show_hierarchical_with_children(
ui,
- "property content features",
+ ui.make_persistent_id("property content features"),
true,
list_item::PropertyContent::new("PropertyContent features:")
.value_text("bunch of properties"),
@@ -258,7 +258,7 @@ impl RightPanel {
re_ui.list_item().show_hierarchical_with_children(
ui,
- "property content right button reserve",
+ ui.make_persistent_id("property content right button reserve"),
true,
list_item::PropertyContent::new("PropertyContent action button:")
.value_text("demo of right gutter"),
@@ -301,7 +301,7 @@ impl RightPanel {
re_ui.list_item().show_hierarchical_with_children(
ui,
- "other features",
+ ui.make_persistent_id("other features"),
true,
list_item::LabelContent::new("Other contents:"),
|re_ui, ui| {
diff --git a/crates/re_ui/src/list_item/list_item.rs b/crates/re_ui/src/list_item/list_item.rs
index df4a21e1922c..837f2492824a 100644
--- a/crates/re_ui/src/list_item/list_item.rs
+++ b/crates/re_ui/src/list_item/list_item.rs
@@ -146,17 +146,18 @@ impl<'a> ListItem<'a> {
/// Draw the item as a non-leaf node from a hierarchical list.
///
+ /// The `id` should be globally unique!
+ /// You can use `ui.make_persistent_id(…)` for that.
+ ///
/// *Important*: must be called while nested in a [`super::list_item_scope`].
pub fn show_hierarchical_with_children(
mut self,
ui: &mut Ui,
- id: impl Into,
+ id: egui::Id,
default_open: bool,
content: impl ListItemContent + 'a,
add_childrens: impl FnOnce(&ReUi, &mut egui::Ui) -> R,
) -> ShowCollapsingResponse {
- let id = id.into();
-
let mut state = egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
id,
@@ -270,48 +271,48 @@ impl<'a> ListItem<'a> {
let mut collapse_response = None;
- if ui.is_rect_visible(bg_rect) {
- let visuals = ui.style().interact_selectable(&style_response, selected);
-
- let background_frame = ui.painter().add(egui::Shape::Noop);
-
- // Draw collapsing triangle
- if let Some(openness) = collapse_openness {
- let triangle_pos = ui.painter().round_pos_to_pixels(egui::pos2(
- rect.min.x,
- rect.center().y - 0.5 * ReUi::collapsing_triangle_area().y,
- ));
- let triangle_rect =
- egui::Rect::from_min_size(triangle_pos, ReUi::collapsing_triangle_area());
- let triangle_response = ui.interact(
- triangle_rect.expand(3.0), // make it easier to click
- id.unwrap_or(ui.id()).with("collapsing_triangle"),
- egui::Sense::click(),
- );
- ReUi::paint_collapsing_triangle(
- ui,
- openness,
- triangle_rect.center(),
- ui.style().interact(&triangle_response),
- );
- collapse_response = Some(triangle_response);
- }
+ let visuals = ui.style().interact_selectable(&style_response, selected);
+
+ let background_frame = ui.painter().add(egui::Shape::Noop);
+
+ // Draw collapsing triangle
+ if let Some(openness) = collapse_openness {
+ let triangle_pos = ui.painter().round_pos_to_pixels(egui::pos2(
+ rect.min.x,
+ rect.center().y - 0.5 * ReUi::collapsing_triangle_area().y,
+ ));
+ let triangle_rect =
+ egui::Rect::from_min_size(triangle_pos, ReUi::collapsing_triangle_area());
+ let triangle_response = ui.interact(
+ triangle_rect.expand(3.0), // make it easier to click
+ id.unwrap_or(ui.id()).with("collapsing_triangle"),
+ egui::Sense::click(),
+ );
+ ReUi::paint_collapsing_triangle(
+ ui,
+ openness,
+ triangle_rect.center(),
+ ui.style().interact(&triangle_response),
+ );
+ collapse_response = Some(triangle_response);
+ }
- // Draw content
- let mut content_rect = rect;
- if collapse_openness.is_some() {
- content_rect.min.x += extra_indent + collapse_extra;
- }
+ // Draw content
+ let mut content_rect = rect;
+ if collapse_openness.is_some() {
+ content_rect.min.x += extra_indent + collapse_extra;
+ }
- let content_ctx = ContentContext {
- rect: content_rect,
- bg_rect,
- response: &style_response,
- list_item: &self,
- layout_info,
- };
- content.ui(re_ui, ui, &content_ctx);
+ let content_ctx = ContentContext {
+ rect: content_rect,
+ bg_rect,
+ response: &style_response,
+ list_item: &self,
+ layout_info,
+ };
+ content.ui(re_ui, ui, &content_ctx);
+ if ui.is_rect_visible(bg_rect) {
// Ensure the background highlight is drawn over round pixel coordinates. Otherwise,
// there could be artifact between consecutive highlighted items when drawn on
// fractional pixels.
diff --git a/crates/re_ui/src/list_item/scope.rs b/crates/re_ui/src/list_item/scope.rs
index 669cb5b95b84..ac4798a709c0 100644
--- a/crates/re_ui/src/list_item/scope.rs
+++ b/crates/re_ui/src/list_item/scope.rs
@@ -252,10 +252,10 @@ impl LayoutInfoStack {
/// list items.
pub fn list_item_scope(
ui: &mut egui::Ui,
- id_source: impl Into,
+ id_source: impl std::hash::Hash,
content: impl FnOnce(&mut egui::Ui) -> R,
) -> R {
- let scope_id = ui.id().with(id_source.into());
+ let scope_id = ui.id().with(id_source);
// read last frame layout statistics and reset for the new frame
let layout_stats = LayoutStatistics::read(ui.ctx(), scope_id);
diff --git a/crates/re_viewer/src/ui/selection_panel.rs b/crates/re_viewer/src/ui/selection_panel.rs
index 78fc653a5efc..2eff873a5023 100644
--- a/crates/re_viewer/src/ui/selection_panel.rs
+++ b/crates/re_viewer/src/ui/selection_panel.rs
@@ -124,7 +124,7 @@ impl SelectionPanel {
UiLayout::SelectionPanelFull
};
for (i, item) in selection.iter_items().enumerate() {
- ui.push_id(i, |ui| {
+ ui.push_id(item, |ui| {
what_is_selected_ui(ui, ctx, viewport.blueprint, item);
match item {
diff --git a/crates/re_viewport/src/viewport_blueprint_ui.rs b/crates/re_viewport/src/viewport_blueprint_ui.rs
index 6310bc5ccd80..c5638edbd800 100644
--- a/crates/re_viewport/src/viewport_blueprint_ui.rs
+++ b/crates/re_viewport/src/viewport_blueprint_ui.rs
@@ -338,6 +338,10 @@ impl Viewport<'_, '_> {
remove_response | vis_response
});
+ // Globally unique id - should only be one of these in view at one time.
+ // We do this so that we can support "collapse/expand all" command.
+ let id = egui::Id::new(CollapseScope::BlueprintTree.container(*container_id));
+
let list_item::ShowCollapsingResponse {
item_response: response,
body_response,
@@ -345,17 +349,11 @@ impl Viewport<'_, '_> {
.selected(ctx.selection().contains_item(&item))
.draggable(true)
.drop_target_style(self.state.is_candidate_drop_parent_container(container_id))
- .show_hierarchical_with_children(
- ui,
- CollapseScope::BlueprintTree.container(*container_id),
- default_open,
- item_content,
- |_, ui| {
- for child in &container_blueprint.contents {
- self.contents_ui(ctx, ui, child, container_visible);
- }
- },
- );
+ .show_hierarchical_with_children(ui, id, default_open, item_content, |_, ui| {
+ for child in &container_blueprint.contents {
+ self.contents_ui(ctx, ui, child, container_visible);
+ }
+ });
context_menu_ui_for_item(
ctx,
@@ -424,6 +422,11 @@ impl Viewport<'_, '_> {
response | vis_response
});
+
+ // Globally unique id - should only be one of these in view at one time.
+ // We do this so that we can support "collapse/expand all" command.
+ let id = egui::Id::new(CollapseScope::BlueprintTree.space_view(*space_view_id));
+
let list_item::ShowCollapsingResponse {
item_response: mut response,
body_response,
@@ -431,65 +434,56 @@ impl Viewport<'_, '_> {
.selected(ctx.selection().contains_item(&item))
.draggable(true)
.force_hovered(is_item_hovered)
- .show_hierarchical_with_children(
- ui,
- CollapseScope::BlueprintTree.space_view(*space_view_id),
- default_open,
- item_content,
- |_, ui| {
- // Always show the origin hierarchy first.
- self.space_view_entity_hierarchy_ui(
- ctx,
- ui,
- query_result,
- &DataResultNodeOrPath::from_path_lookup(
- result_tree,
- &space_view.space_origin,
- ),
- space_view,
- space_view_visible,
- false,
- );
-
- // Show 'projections' if there's any items that weren't part of the tree under origin but are directly included.
- // The latter is important since `+ image/camera/**` necessarily has `image` and `image/camera` in the data result tree.
- let mut projections = Vec::new();
- result_tree.visit(&mut |node| {
- if node
- .data_result
- .entity_path
- .starts_with(&space_view.space_origin)
- {
- false // If it's under the origin, we're not interested, stop recursing.
- } else if node.data_result.tree_prefix_only {
- true // Keep recursing until we find a projection.
- } else {
- projections.push(node);
- false // We found a projection, stop recursing as everything below is now included in the projections.
- }
- });
- if !projections.is_empty() {
- list_item::ListItem::new(ctx.re_ui)
- .interactive(false)
- .show_flat(
- ui,
- list_item::LabelContent::new("Projections:").italics(true),
- );
+ .show_hierarchical_with_children(ui, id, default_open, item_content, |_, ui| {
+ // Always show the origin hierarchy first.
+ self.space_view_entity_hierarchy_ui(
+ ctx,
+ ui,
+ query_result,
+ &DataResultNodeOrPath::from_path_lookup(result_tree, &space_view.space_origin),
+ space_view,
+ space_view_visible,
+ false,
+ );
- for projection in projections {
- self.space_view_entity_hierarchy_ui(
- ctx,
- ui,
- query_result,
- &DataResultNodeOrPath::DataResultNode(projection),
- space_view,
- space_view_visible,
- true,
- );
- }
+ // Show 'projections' if there's any items that weren't part of the tree under origin but are directly included.
+ // The latter is important since `+ image/camera/**` necessarily has `image` and `image/camera` in the data result tree.
+ let mut projections = Vec::new();
+ result_tree.visit(&mut |node| {
+ if node
+ .data_result
+ .entity_path
+ .starts_with(&space_view.space_origin)
+ {
+ false // If it's under the origin, we're not interested, stop recursing.
+ } else if node.data_result.tree_prefix_only {
+ true // Keep recursing until we find a projection.
+ } else {
+ projections.push(node);
+ false // We found a projection, stop recursing as everything below is now included in the projections.
}
- },
- );
+ });
+ if !projections.is_empty() {
+ list_item::ListItem::new(ctx.re_ui)
+ .interactive(false)
+ .show_flat(
+ ui,
+ list_item::LabelContent::new("Projections:").italics(true),
+ );
+
+ for projection in projections {
+ self.space_view_entity_hierarchy_ui(
+ ctx,
+ ui,
+ query_result,
+ &DataResultNodeOrPath::DataResultNode(projection),
+ space_view,
+ space_view_visible,
+ true,
+ );
+ }
+ }
+ });
response = response.on_hover_text("Space view");
@@ -634,39 +628,36 @@ impl Viewport<'_, '_> {
let default_open = entity_path.starts_with(&space_view.space_origin)
&& Self::default_open_for_data_result(node);
+ // Globally unique id - should only be one of these in view at one time.
+ // We do this so that we can support "collapse/expand all" command.
+ let id = egui::Id::new(
+ CollapseScope::BlueprintTree.data_result(space_view.id, entity_path.clone()),
+ );
+
list_item
- .show_hierarchical_with_children(
- ui,
- CollapseScope::BlueprintTree.data_result(space_view.id, entity_path.clone()),
- default_open,
- item_content,
- |_, ui| {
- for child in node.children.iter().sorted_by_key(|c| {
- query_result
- .tree
- .lookup_result(**c)
- .map_or(&space_view.space_origin, |c| &c.entity_path)
- }) {
- let Some(child_node) = query_result.tree.lookup_node(*child) else {
- debug_assert!(
- false,
- "DataResultNode {node:?} has an invalid child"
- );
- continue;
- };
-
- self.space_view_entity_hierarchy_ui(
- ctx,
- ui,
- query_result,
- &DataResultNodeOrPath::DataResultNode(child_node),
- space_view,
- space_view_visible,
- projection_mode,
- );
- }
- },
- )
+ .show_hierarchical_with_children(ui, id, default_open, item_content, |_, ui| {
+ for child in node.children.iter().sorted_by_key(|c| {
+ query_result
+ .tree
+ .lookup_result(**c)
+ .map_or(&space_view.space_origin, |c| &c.entity_path)
+ }) {
+ let Some(child_node) = query_result.tree.lookup_node(*child) else {
+ debug_assert!(false, "DataResultNode {node:?} has an invalid child");
+ continue;
+ };
+
+ self.space_view_entity_hierarchy_ui(
+ ctx,
+ ui,
+ query_result,
+ &DataResultNodeOrPath::DataResultNode(child_node),
+ space_view,
+ space_view_visible,
+ projection_mode,
+ );
+ }
+ })
.item_response
} else {
list_item.show_hierarchical(ui, item_content)
From 317dcda0bdad5b1e553ba856645fffc60b278fa0 Mon Sep 17 00:00:00 2001
From: Emil Ernerfeldt
Date: Fri, 24 May 2024 15:40:14 +0200
Subject: [PATCH 05/10] Improve welcome screen for small screens (#6421)
Makes the welcome screen use more of the available space.
Important for small screens, mobile, or embedded web viewers.
### What
* Hide blueprint panel
* Reduce vertical space
* Fix title casing
#### Before (0.16 viewer)
#### After
#### Mobile
![IMG_7872](https://github.com/rerun-io/rerun/assets/1148717/f4ba5a95-0c6e-43f0-aadd-b17f768ebaec)
### 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 examples from latest `main` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6421?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6421?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
* [x] If applicable, add a new check to the [release
checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)!
- [PR Build Summary](https://build.rerun.io/pr/6421)
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)
To run all checks from `main`, comment on the PR with `@rerun-bot
full-check`.
---
crates/re_time_panel/src/lib.rs | 8 +++++---
crates/re_viewer/src/app_blueprint.rs | 11 ++++++-----
.../src/ui/welcome_screen/example_section.rs | 5 ++++-
.../src/ui/welcome_screen/welcome_section.rs | 4 +---
4 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/crates/re_time_panel/src/lib.rs b/crates/re_time_panel/src/lib.rs
index e13b0a24a64c..414a322255f1 100644
--- a/crates/re_time_panel/src/lib.rs
+++ b/crates/re_time_panel/src/lib.rs
@@ -264,7 +264,9 @@ impl TimePanel {
) {
ui.spacing_mut().item_spacing.x = 18.0; // from figma
- if ui.max_rect().width() < 600.0 {
+ let has_any_data_on_timeline = entity_db.has_any_data_on_timeline(time_ctrl.timeline());
+
+ if ui.max_rect().width() < 600.0 && has_any_data_on_timeline {
// Responsive ui for narrow screens, e.g. mobile. Split the controls into two rows.
ui.vertical(|ui| {
ui.horizontal(|ui| {
@@ -273,7 +275,7 @@ impl TimePanel {
self.time_control_ui
.play_pause_ui(time_ctrl, re_ui, times_per_timeline, ui);
- if entity_db.has_any_data_on_timeline(time_ctrl.timeline()) {
+ if has_any_data_on_timeline {
self.time_control_ui.playback_speed_ui(time_ctrl, ui);
self.time_control_ui.fps_ui(time_ctrl, ui);
}
@@ -296,7 +298,7 @@ impl TimePanel {
self.time_control_ui
.timeline_selector_ui(time_ctrl, times_per_timeline, ui);
- if entity_db.has_any_data_on_timeline(time_ctrl.timeline()) {
+ if has_any_data_on_timeline {
self.time_control_ui.playback_speed_ui(time_ctrl, ui);
self.time_control_ui.fps_ui(time_ctrl, ui);
}
diff --git a/crates/re_viewer/src/app_blueprint.rs b/crates/re_viewer/src/app_blueprint.rs
index 088c2e6e0177..debfcf31cc72 100644
--- a/crates/re_viewer/src/app_blueprint.rs
+++ b/crates/re_viewer/src/app_blueprint.rs
@@ -4,10 +4,10 @@ use re_log_types::{DataRow, EntityPath, RowId};
use re_types::blueprint::components::PanelState;
use re_viewer_context::{CommandSender, StoreContext, SystemCommand, SystemCommandSender};
-pub const TOP_PANEL_PATH: &str = "top_panel";
-pub const BLUEPRINT_PANEL_PATH: &str = "blueprint_panel";
-pub const SELECTION_PANEL_PATH: &str = "selection_panel";
-pub const TIME_PANEL_PATH: &str = "time_panel";
+const TOP_PANEL_PATH: &str = "top_panel";
+const BLUEPRINT_PANEL_PATH: &str = "blueprint_panel";
+const SELECTION_PANEL_PATH: &str = "selection_panel";
+const TIME_PANEL_PATH: &str = "time_panel";
/// Blueprint for top-level application
pub struct AppBlueprint<'a> {
@@ -98,9 +98,10 @@ impl<'a> AppBlueprint<'a> {
}
pub fn setup_welcome_screen_blueprint(welcome_screen_blueprint: &mut EntityDb) {
+ // Most things are hidden in the welcome screen:
for (panel_name, value) in [
(TOP_PANEL_PATH, PanelState::Expanded),
- (BLUEPRINT_PANEL_PATH, PanelState::Expanded),
+ (BLUEPRINT_PANEL_PATH, PanelState::Hidden),
(SELECTION_PANEL_PATH, PanelState::Hidden),
(TIME_PANEL_PATH, PanelState::Hidden),
] {
diff --git a/crates/re_viewer/src/ui/welcome_screen/example_section.rs b/crates/re_viewer/src/ui/welcome_screen/example_section.rs
index 2007c4ba3067..5d333aa24fad 100644
--- a/crates/re_viewer/src/ui/welcome_screen/example_section.rs
+++ b/crates/re_viewer/src/ui/welcome_screen/example_section.rs
@@ -34,7 +34,8 @@ pub(super) const MIN_COLUMN_WIDTH: f32 = 250.0;
const MAX_COLUMN_WIDTH: f32 = 337.0;
const MAX_COLUMN_COUNT: usize = 3;
const COLUMN_HSPACE: f32 = 20.0;
-const TITLE_TO_GRID_VSPACE: f32 = 32.0;
+const AFTER_HEADER_VSPACE: f32 = 48.0;
+const TITLE_TO_GRID_VSPACE: f32 = 24.0;
const ROW_VSPACE: f32 = 20.0;
const THUMBNAIL_RADIUS: f32 = 12.0;
@@ -274,6 +275,8 @@ impl ExampleSection {
ui.vertical(|ui| {
header_ui(ui);
+ ui.add_space(AFTER_HEADER_VSPACE);
+
let Some(examples) = examples.ready_mut() else {
// Still waiting for example to load
ui.separator();
diff --git a/crates/re_viewer/src/ui/welcome_screen/welcome_section.rs b/crates/re_viewer/src/ui/welcome_screen/welcome_section.rs
index 4aa6251d725c..a28873a9f443 100644
--- a/crates/re_viewer/src/ui/welcome_screen/welcome_section.rs
+++ b/crates/re_viewer/src/ui/welcome_screen/welcome_section.rs
@@ -1,7 +1,7 @@
use egui::Ui;
pub(super) const DOCS_URL: &str = "https://www.rerun.io/docs";
-pub(super) const WELCOME_SCREEN_TITLE: &str = "Visualize Multimodal Data";
+pub(super) const WELCOME_SCREEN_TITLE: &str = "Visualize multimodal data";
pub(super) const WELCOME_SCREEN_BULLET_TEXT: &[&str] = &[
"Log data with the Rerun SDK in C++, Python, or Rust",
"Visualize and explore live or recorded data",
@@ -65,7 +65,5 @@ pub(super) fn welcome_section_ui(ui: &mut egui::Ui) {
new_tab: true,
});
}
-
- ui.add_space(83.0);
});
}
From 2e494b311d958672103caf62a9f86adb8ebdc093 Mon Sep 17 00:00:00 2001
From: Antoine Beyeler <49431240+abey79@users.noreply.github.com>
Date: Sun, 26 May 2024 20:52:42 +0200
Subject: [PATCH 06/10] Move context menu to new `re_context_menu` crate
(#6423)
### What
- Fixes #6414
Mostly pure refactor, except for moving
`determine_visualizable_entities` to a `SpaceViewClass` extension trait.
### 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 examples from latest `main` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6423?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6423?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
* [x] If applicable, add a new check to the [release
checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)!
- [PR Build Summary](https://build.rerun.io/pr/6423)
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)
To run all checks from `main`, comment on the PR with `@rerun-bot
full-check`.
---
ARCHITECTURE.md | 28 +++++++-----
Cargo.lock | 29 +++++++++---
Cargo.toml | 1 +
crates/re_context_menu/Cargo.toml | 37 +++++++++++++++
crates/re_context_menu/README.md | 10 +++++
.../src}/actions/add_container.rs | 2 +-
.../actions/add_entities_to_new_space_view.rs | 8 ++--
.../src}/actions/add_space_view.rs | 2 +-
.../src}/actions/clone_space_view.rs | 2 +-
.../src}/actions/collapse_expand_all.rs | 2 +-
.../src}/actions/mod.rs | 0
.../actions/move_contents_to_new_container.rs | 2 +-
.../src}/actions/remove.rs | 2 +-
.../src}/actions/show_hide.rs | 2 +-
.../mod.rs => re_context_menu/src/lib.rs} | 2 +
.../src}/sub_menu.rs | 2 +-
crates/re_space_view/Cargo.toml | 1 -
crates/re_space_view/src/lib.rs | 2 -
crates/re_space_view/src/visualizable.rs | 38 ----------------
crates/re_time_panel/Cargo.toml | 2 +-
crates/re_time_panel/src/lib.rs | 2 +-
crates/re_viewer/Cargo.toml | 2 +-
crates/re_viewer/src/app_state.rs | 39 ++++++++--------
crates/re_viewer/src/ui/override_ui.rs | 22 ++++-----
crates/re_viewer/src/ui/selection_panel.rs | 6 +--
crates/re_viewer_context/src/lib.rs | 2 +-
.../re_viewer_context/src/space_view/mod.rs | 4 +-
.../src/space_view/space_view_class.rs | 45 +++++++++++++++++--
crates/re_viewport/Cargo.toml | 3 +-
crates/re_viewport/src/lib.rs | 2 -
.../src/space_view_entity_picker.rs | 6 +--
crates/re_viewport/src/viewport.rs | 9 ++--
.../re_viewport/src/viewport_blueprint_ui.rs | 4 +-
crates/re_viewport_blueprint/Cargo.toml | 2 +-
34 files changed, 195 insertions(+), 127 deletions(-)
create mode 100644 crates/re_context_menu/Cargo.toml
create mode 100644 crates/re_context_menu/README.md
rename crates/{re_viewport/src/context_menu => re_context_menu/src}/actions/add_container.rs (96%)
rename crates/{re_viewport/src/context_menu => re_context_menu/src}/actions/add_entities_to_new_space_view.rs (96%)
rename crates/{re_viewport/src/context_menu => re_context_menu/src}/actions/add_space_view.rs (95%)
rename crates/{re_viewport/src/context_menu => re_context_menu/src}/actions/clone_space_view.rs (93%)
rename crates/{re_viewport/src/context_menu => re_context_menu/src}/actions/collapse_expand_all.rs (98%)
rename crates/{re_viewport/src/context_menu => re_context_menu/src}/actions/mod.rs (100%)
rename crates/{re_viewport/src/context_menu => re_context_menu/src}/actions/move_contents_to_new_container.rs (97%)
rename crates/{re_viewport/src/context_menu => re_context_menu/src}/actions/remove.rs (96%)
rename crates/{re_viewport/src/context_menu => re_context_menu/src}/actions/show_hide.rs (98%)
rename crates/{re_viewport/src/context_menu/mod.rs => re_context_menu/src/lib.rs} (99%)
rename crates/{re_viewport/src/context_menu => re_context_menu/src}/sub_menu.rs (94%)
delete mode 100644 crates/re_space_view/src/visualizable.rs
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index 665144876cc5..6388b7dd05ce 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -88,13 +88,14 @@ Of course, this will only take us so far. In the future we plan on caching queri
Here is an overview of the crates included in the project:
+
### What
Part of #6330
Removes or explicitly allows unwrap() where it makes sense.
Affected crates: `re_entity_db`, `re_format_arrow`, `and re_log_types`
### 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 examples from latest `main` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6380?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6380?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
* [x] If applicable, add a new check to the [release
checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)!
- [PR Build Summary](https://build.rerun.io/pr/6380)
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)
To run all checks from `main`, comment on the PR with `@rerun-bot
full-check`.
---------
Co-authored-by: Emil Ernerfeldt
---
crates/re_entity_db/src/entity_tree.rs | 3 +-
crates/re_entity_db/src/lib.rs | 3 --
crates/re_format_arrow/src/lib.rs | 8 +--
crates/re_log_types/src/arrow_msg.rs | 1 +
crates/re_log_types/src/data_cell.rs | 15 +++++-
crates/re_log_types/src/data_table.rs | 39 ++++++++------
crates/re_log_types/src/data_table_batcher.rs | 1 +
crates/re_log_types/src/example_components.rs | 14 +++--
crates/re_log_types/src/lib.rs | 3 --
crates/re_log_types/src/time.rs | 51 +++++++++++--------
crates/re_types_core/src/result.rs | 14 ++++-
11 files changed, 97 insertions(+), 55 deletions(-)
diff --git a/crates/re_entity_db/src/entity_tree.rs b/crates/re_entity_db/src/entity_tree.rs
index c26fe215eab6..5b7aebc803ec 100644
--- a/crates/re_entity_db/src/entity_tree.rs
+++ b/crates/re_entity_db/src/entity_tree.rs
@@ -362,8 +362,7 @@ impl EntityTree {
if cell.component_name() == ClearIsRecursive::name() {
let is_recursive = cell
.try_to_native_mono::()
- .unwrap()
- .map_or(false, |settings| settings.0);
+ .map_or(false, |opt| opt.map_or(false, |settings| settings.0));
self.on_added_clear(clear_cascade, store_diff, is_recursive);
}
diff --git a/crates/re_entity_db/src/lib.rs b/crates/re_entity_db/src/lib.rs
index fbcd2ee60ca1..6295ba7181d5 100644
--- a/crates/re_entity_db/src/lib.rs
+++ b/crates/re_entity_db/src/lib.rs
@@ -4,9 +4,6 @@
#![doc = document_features::document_features!()]
//!
-// TODO(#3408): remove unwrap()
-#![allow(clippy::unwrap_used)]
-
pub mod entity_db;
pub mod entity_properties;
pub mod entity_tree;
diff --git a/crates/re_format_arrow/src/lib.rs b/crates/re_format_arrow/src/lib.rs
index 573d924a9c89..e7f64b061b79 100644
--- a/crates/re_format_arrow/src/lib.rs
+++ b/crates/re_format_arrow/src/lib.rs
@@ -1,8 +1,5 @@
//! Formatting for tables of Arrow arrays
-// TODO(#3408): remove unwrap()
-#![allow(clippy::unwrap_used)]
-
use std::fmt::Formatter;
use arrow2::{
@@ -241,7 +238,10 @@ where
.iter()
.map(|disp| {
let mut string = String::new();
- (disp)(&mut string, row).unwrap();
+ if (disp)(&mut string, row).is_err() {
+ // Seems to be okay to silently ignore errors here, but reset the string just in case
+ string.clear();
+ }
let chars: Vec<_> = string.chars().collect();
if chars.len() > WIDTH_UPPER_BOUNDARY as usize {
Cell::new(
diff --git a/crates/re_log_types/src/arrow_msg.rs b/crates/re_log_types/src/arrow_msg.rs
index c62f60eed676..ea5e9059b4f7 100644
--- a/crates/re_log_types/src/arrow_msg.rs
+++ b/crates/re_log_types/src/arrow_msg.rs
@@ -187,6 +187,7 @@ impl<'de> serde::Deserialize<'de> for ArrowMsg {
chunks.len()
)));
}
+ #[allow(clippy::unwrap_used)] // is_empty check above
let chunk = chunks.into_iter().next().unwrap();
Ok(ArrowMsg {
diff --git a/crates/re_log_types/src/data_cell.rs b/crates/re_log_types/src/data_cell.rs
index fa10673455b7..f0a9cdec1345 100644
--- a/crates/re_log_types/src/data_cell.rs
+++ b/crates/re_log_types/src/data_cell.rs
@@ -231,6 +231,8 @@ impl DataCell {
where
C: Component + Clone + 'a,
{
+ // NOTE: see function description why it's okay here
+ #[allow(clippy::unwrap_used)]
Self::try_from_native(values).unwrap()
}
@@ -246,6 +248,8 @@ impl DataCell {
where
C: Component + Clone + 'a,
{
+ // NOTE: see function description why it's okay here
+ #[allow(clippy::unwrap_used)]
Self::try_from_native_sparse(values).unwrap()
}
@@ -295,6 +299,8 @@ impl DataCell {
/// See [`Self::try_from_arrow`] for the fallible alternative.
#[inline]
pub fn from_arrow(name: ComponentName, values: Box) -> Self {
+ // NOTE: see function description why it's okay here
+ #[allow(clippy::unwrap_used)]
Self::try_from_arrow(name, values).unwrap()
}
@@ -333,6 +339,8 @@ impl DataCell {
/// See [`Self::try_from_arrow_empty`] for a fallible alternative.
#[inline]
pub fn from_arrow_empty(name: ComponentName, datatype: arrow2::datatypes::DataType) -> Self {
+ // NOTE: see function description why it's okay here
+ #[allow(clippy::unwrap_used)]
Self::try_from_arrow_empty(name, datatype).unwrap()
}
@@ -381,7 +389,7 @@ impl DataCell {
let datatype = ListArray::::default_datatype(datatype);
let offsets = Offsets::try_from_lengths(std::iter::once(self.num_instances() as usize))
- .unwrap()
+ .unwrap_or_default()
.into();
let validity = None;
@@ -436,6 +444,8 @@ impl DataCell {
/// See [`Self::try_to_native`] for a fallible alternative.
#[inline]
pub fn to_native<'a, C: Component + 'a>(&'a self) -> Vec {
+ // NOTE: see function description why it's okay here
+ #[allow(clippy::unwrap_used)]
self.try_to_native().unwrap()
}
@@ -456,6 +466,8 @@ impl DataCell {
/// See [`Self::try_to_native_opt`] for a fallible alternative.
#[inline]
pub fn to_native_opt<'a, C: Component + 'a>(&'a self) -> Vec