Skip to content

Commit

Permalink
Make Visible History UI more ergonomic and show inherited values (#4222)
Browse files Browse the repository at this point in the history
### What

Update the Visible History UI:
- It now shows up in its own collapsible section in the Selection Panel.
- It makes it more clear that the item is either inheriting or
overriding the settings.
- When inheriting, the inherited settings are displayed.
- When overriding, the editor as a more intuitive UI (fixes #4190)
- The editor also addresses a bug for timeline with large values (e.g.
actual dates) but small spans, like `log_time` (fixes #4210)
- The test script now has a whole lot of timelines with all sorts of
spans and offsets, for test purposes.

Side effects on the code base:
- slight tweak on the querying (latest at is used whenever visible time
range boundaries resolve to the same absolute time)
- added `individual_properties` to `DataResult`, to allow UI to
display/edit it
- added support to erase property overrides in
`DataResult::save_override`
- added a new collapsing header widget in re_ui, to match our style

Playground RRD:
[test_visible_history.rrd.zip](https://github.com/rerun-io/rerun/files/13348321/test_visible_history.rrd.zip)
(same as the output of
`tests/python/visible_history_playground/main.py`).

### Screenshots

New "Visible Time Range" UI in the blueprint section:

<img width="429" alt="image"
src="https://github.com/rerun-io/rerun/assets/49431240/c215560f-7679-442a-afe9-561d6cae6285">

When Override is selected:

<img width="379" alt="image"
src="https://github.com/rerun-io/rerun/assets/49431240/f9b1651f-0d29-4085-952b-3f55b0c3c469">

When operating on "large" time (e.g. `log_time`):

<img width="393" alt="image"
src="https://github.com/rerun-io/rerun/assets/49431240/67f69dd9-1e90-4d04-9c8b-5fe2696c92bc">

Same when using the Default/Inherited value:

<img width="378" alt="image"
src="https://github.com/rerun-io/rerun/assets/49431240/ffbfa49a-54bb-472b-be47-25a800d4f771">

<br/>
<br/>
<details><summary>Older screenshots which aged like milk, for
posterity.</summary>

On (supported) space views, the choice is "Default vs. Override":

<img width="336" alt="image"
src="https://github.com/rerun-io/rerun/assets/49431240/69ab4063-cd79-4c56-a01a-9bf54d74070a">

On groups and entities, the choice is "Inherited vs. Override":

<img width="343" alt="image"
src="https://github.com/rerun-io/rerun/assets/49431240/3911c456-97b1-48ed-aa69-9cbb3cf92611">

On timeseries space views, the default reflects the fact that the
default behaviour is, in fact, different from 2D/3D space views

<img width="345" alt="image"
src="https://github.com/rerun-io/rerun/assets/49431240/35692f66-dcb3-419b-8246-2823d802c39b">

When overriding, the text becomes "editable":

<img width="338" alt="image"
src="https://github.com/rerun-io/rerun/assets/49431240/641758ec-2762-4c13-b87f-de1cec144ea0">

Same, when inherited:

<img width="313" alt="image"
src="https://github.com/rerun-io/rerun/assets/49431240/52272daa-d215-40e4-8f7d-2caeea522434">

With timelines with "large" times, an offset is extracted and explicitly
displayed, while the unit in the time editor adapts to the actual span:

<img width="405" alt="image"
src="https://github.com/rerun-io/rerun/assets/49431240/dfa887d5-aa6c-49aa-be1c-704045788422">

Same inherited:

<img width="438" alt="image"
src="https://github.com/rerun-io/rerun/assets/49431240/ea748355-5293-422a-8f25-6092529b6452">

</details>

### 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 [demo.rerun.io](https://demo.rerun.io/pr/4222) (if
applicable)
* [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/4222)
- [Docs
preview](https://rerun.io/preview/69df7cdce2fa9c3899b2b04aa2bf487807485c02/docs)
<!--DOCS-PREVIEW-->
- [Examples
preview](https://rerun.io/preview/69df7cdce2fa9c3899b2b04aa2bf487807485c02/examples)
<!--EXAMPLES-PREVIEW-->
- [Recent benchmark results](https://ref.rerun.io/dev/bench/)
- [Wasm size tracking](https://ref.rerun.io/dev/sizes/)
  • Loading branch information
abey79 authored Nov 16, 2023
1 parent 19f9071 commit f586c04
Show file tree
Hide file tree
Showing 13 changed files with 1,025 additions and 401 deletions.
22 changes: 3 additions & 19 deletions crates/re_data_store/src/entity_properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,26 +256,8 @@ pub enum VisibleHistoryBoundary {
}

impl VisibleHistoryBoundary {
/// UI label to use when [`VisibleHistoryBoundary::RelativeToTimeCursor`] is selected.
pub const RELATIVE_LABEL: &'static str = "Relative";

/// UI label to use when [`VisibleHistoryBoundary::Absolute`] is selected.
pub const ABSOLUTE_LABEL: &'static str = "Absolute";

/// UI label to use when [`VisibleHistoryBoundary::Infinite`] is selected.
pub const INFINITE_LABEL: &'static str = "Infinite";

/// Value when the boundary is set to the current time cursor.
pub const AT_CURSOR: Self = Self::RelativeToTimeCursor(0);

/// Label to use in the UI.
pub fn label(&self) -> &'static str {
match self {
Self::RelativeToTimeCursor(_) => Self::RELATIVE_LABEL,
Self::Absolute(_) => Self::ABSOLUTE_LABEL,
Self::Infinite => Self::INFINITE_LABEL,
}
}
}

impl Default for VisibleHistoryBoundary {
Expand Down Expand Up @@ -345,8 +327,10 @@ impl ExtraQueryHistory {
fn with_child(&self, child: &Self) -> Self {
if child.enabled {
*child
} else {
} else if self.enabled {
*self
} else {
Self::default()
}
}
}
Expand Down
11 changes: 6 additions & 5 deletions crates/re_query/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use re_arrow_store::{DataStore, LatestAtQuery, RangeQuery, TimeInt, TimeRange, Timeline};
use re_data_store::{ExtraQueryHistory, VisibleHistory};
use re_data_store::ExtraQueryHistory;
use re_log_types::EntityPath;
use re_types_core::Archetype;

Expand All @@ -17,14 +17,15 @@ pub fn query_archetype_with_history<'a, A: Archetype + 'a, const N: usize>(
re_log_types::TimeType::Sequence => history.sequences,
};

if !history.enabled || visible_history == VisibleHistory::OFF {
let latest_query = LatestAtQuery::new(*timeline, *time);
let min_time = visible_history.from(*time);
let max_time = visible_history.to(*time);

if !history.enabled || min_time == max_time {
let latest_query = LatestAtQuery::new(*timeline, min_time);
let latest = query_archetype::<A>(store, &latest_query, ent_path)?;

Ok(itertools::Either::Left(std::iter::once(latest)))
} else {
let min_time = visible_history.from(*time);
let max_time = visible_history.to(*time);
let range_query = RangeQuery::new(*timeline, TimeRange::new(min_time, max_time));

let range = range_archetype::<A, N>(store, &range_query, ent_path);
Expand Down
5 changes: 5 additions & 0 deletions crates/re_space_view/src/data_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ impl DataQuery for SpaceViewContents {
entity_path: entity_path.clone(),
view_parts,
resolved_properties,
individual_properties: entity_overrides.get_opt(entity_path).cloned(),
override_path: self
.entity_path()
.join(&SpaceViewContents::PROPERTIES_PREFIX.into())
Expand Down Expand Up @@ -219,12 +220,14 @@ impl DataBlueprintGroup {
}

let override_path = base_entity_path.join(&props_path).join(&entity_path);
let individual_properties = overrides.get_opt(&entity_path).cloned();

data_results.insert(DataResultNode {
data_result: DataResult {
entity_path,
view_parts,
resolved_properties,
individual_properties,
override_path,
},
children: Default::default(),
Expand All @@ -251,11 +254,13 @@ impl DataBlueprintGroup {

children.append(&mut recursive_children);

let individual_properties = overrides.get_opt(&group_path).cloned();
data_results.insert(DataResultNode {
data_result: DataResult {
entity_path: group_path,
view_parts: group_view_parts,
resolved_properties: group_resolved_properties,
individual_properties,
override_path: group_override_path,
},
children,
Expand Down
1 change: 1 addition & 0 deletions crates/re_space_view/src/space_view_contents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ impl SpaceViewContents {
let parent_group = self.groups.insert(DataBlueprintGroup {
display_name: path_to_group_name(&parent_path),
children: smallvec![new_group],
group_path: parent_path.clone(),
..Default::default()
});
vacant_mapping.insert(parent_group);
Expand Down
5 changes: 5 additions & 0 deletions crates/re_ui/examples/re_ui_example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ impl eframe::App for ExampleApp {
ui.style_mut().wrap = Some(false);
ui.label("Some blueprint stuff here, that might be wide.");
self.re_ui.checkbox(ui, &mut self.dummy_bool, "Checkbox");

self.re_ui.collapsing_header(ui, "Collapsing header", true, |ui| {
ui.label("Some data here");
self.re_ui.checkbox(ui, &mut self.dummy_bool, "Checkbox");
});
});
});
});
Expand Down
133 changes: 96 additions & 37 deletions crates/re_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ pub use command_palette::CommandPalette;
pub use design_tokens::DesignTokens;
pub use icons::Icon;
pub use layout_job_builder::LayoutJobBuilder;
use std::ops::RangeInclusive;
pub use toggle_switch::toggle_switch;

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -49,7 +48,7 @@ pub struct TopBarStyle {
use crate::list_item::ListItem;
use egui::emath::{Rangef, Rot2};
use egui::epaint::util::FloatOrd;
use egui::{pos2, Align2, Color32, Mesh, NumExt, Rect, Shape, Vec2};
use egui::{pos2, Align2, CollapsingResponse, Color32, Mesh, NumExt, Rect, Shape, Vec2};

#[derive(Clone)]
pub struct ReUi {
Expand Down Expand Up @@ -446,41 +445,6 @@ impl ReUi {
.inner
}

/// Shows a `egui::DragValue` for a time in nanoseconds.
///
/// The value and unit displayed adapts to the provided allowable time range.
#[allow(clippy::unused_self)]
pub fn time_drag_value(
&self,
ui: &mut egui::Ui,
value: &mut i64,
time_range: &RangeInclusive<i64>,
) {
let span = time_range.end() - time_range.start();

let (unit, factor) = if span / 1_000_000_000 > 0 {
("s", 1_000_000_000.)
} else if span / 1_000_000 > 0 {
("ms", 1_000_000.)
} else if span / 1_000 > 0 {
("μs", 1_000.)
} else {
("ns", 1.)
};

let mut time_unit = *value as f32 / factor;
let time_range = *time_range.start() as f32 / factor..=*time_range.end() as f32 / factor;
let speed = (time_range.end() - time_range.start()) * 0.005;

ui.add(
egui::DragValue::new(&mut time_unit)
.clamp_range(time_range)
.speed(speed)
.suffix(unit),
);
*value = (time_unit * factor).round() as _;
}

pub fn large_button(&self, ui: &mut egui::Ui, icon: &Icon) -> egui::Response {
self.large_button_impl(ui, icon, None, None)
}
Expand Down Expand Up @@ -579,6 +543,101 @@ impl ReUi {
.inner
}

/// Replacement for [`egui::CollapsingHeader`] that respect our style.
///
/// The layout is fine-tuned to fit well in inspector panels (such as Rerun's Selection Panel)
/// where the collapsing header should align nicely with checkboxes and other controls.
#[allow(clippy::unused_self)]
pub fn collapsing_header<R>(
&self,
ui: &mut egui::Ui,
label: &str,
default_open: bool,
add_body: impl FnOnce(&mut egui::Ui) -> R,
) -> egui::CollapsingResponse<R> {
let id = ui.make_persistent_id(label);
let button_padding = ui.spacing().button_padding;

let available = ui.available_rect_before_wrap();
// TODO(ab): use design token for indent — cannot use the global indent value as we must
// align with checkbox, etc.
let indent = 18.0;
let text_pos = available.min + egui::vec2(indent, 0.0);
let wrap_width = available.right() - text_pos.x;
let wrap = Some(false);
let text = egui::WidgetText::from(label).into_galley(
ui,
wrap,
wrap_width,
egui::TextStyle::Button,
);
let text_max_x = text_pos.x + text.size().x;

let mut desired_width = text_max_x + button_padding.x - available.left();
if ui.visuals().collapsing_header_frame {
desired_width = desired_width.max(available.width()); // fill full width
}

let mut desired_size = egui::vec2(desired_width, text.size().y + 2.0 * button_padding.y);
desired_size = desired_size.at_least(ui.spacing().interact_size);
let (_, rect) = ui.allocate_space(desired_size);

let mut header_response = ui.interact(rect, id, egui::Sense::click());
let text_pos = pos2(
text_pos.x,
header_response.rect.center().y - text.size().y / 2.0,
);

let mut state = egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
id,
default_open,
);
if header_response.clicked() {
state.toggle(ui);
header_response.mark_changed();
}

let openness = state.openness(ui.ctx());

if ui.is_rect_visible(rect) {
let visuals = ui.style().interact(&header_response);

{
let space_around_icon = 3.0;
let icon_width = ui.spacing().icon_width_inner;

let icon_rect = egui::Rect::from_center_size(
header_response.rect.left_center()
+ egui::vec2(space_around_icon + icon_width / 2.0, 0.0),
egui::Vec2::splat(icon_width),
);

let icon_response = header_response.clone().with_new_rect(icon_rect);
Self::paint_collapsing_triangle(ui, openness, icon_rect, &icon_response);
}

text.paint_with_visuals(ui.painter(), text_pos, visuals);
}

let ret_response = ui
.vertical(|ui| {
ui.spacing_mut().indent = indent;
state.show_body_indented(&header_response, ui, add_body)
})
.inner;

let (body_response, body_returned) =
ret_response.map_or((None, None), |r| (Some(r.response), Some(r.inner)));

CollapsingResponse {
header_response,
body_response,
body_returned,
openness,
}
}

/// Show a prominent collapsing header to be used as section delimitation in side panels.
///
/// Note that a clip rect must be set (typically by the panel) to avoid any overdraw.
Expand Down
2 changes: 2 additions & 0 deletions crates/re_viewer/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ mod welcome_screen;
pub(crate) mod memory_panel;
pub(crate) mod selection_panel;

pub(crate) mod visible_history;

pub use blueprint_panel::blueprint_panel_ui;
pub use recordings_panel::recordings_panel_ui;
// ----
Expand Down
Loading

1 comment on commit f586c04

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Rust Benchmark'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.25.

Benchmark suite Current: f586c04 Previous: f30ffe4 Ratio
mono_points_arrow/generate_message_bundles 29013786 ns/iter (± 827135) 18885391 ns/iter (± 154605) 1.54
mono_points_arrow_batched/generate_message_bundles 19002850 ns/iter (± 1005353) 14629999 ns/iter (± 31790) 1.30
mono_points_arrow_batched/encode_total 28574722 ns/iter (± 1908661) 20637940 ns/iter (± 125526) 1.38

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.