Skip to content

Commit

Permalink
Add support for exact width to PropertyContent (#6325)
Browse files Browse the repository at this point in the history
### What

This PR adds support for `PropertyContent::exact_width(true)`, which
makes it possible to create lists which only use the needed width (as
opposed to using `ui.available_width()`. This was previous available for
`LabelContent` and used (in the future) in the streams view.

`LabelContent` exploits the fact that the content is mainly a label, the
width of which can be computed with `egui`. For `PropertyContent` it's
tricker because we delegate the second column to a closure. Instead, we
track the max width in each `list_item_context` on frame n, and request
that width in frame n+1.

The major drawback of this approach is the potential flicker on the
first frame. Since the plan is to use it for tooltip, it will greatly
help to have emilk/egui#4471

### 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/6325?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/6325?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/6325)
- [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`.
  • Loading branch information
abey79 authored May 22, 2024
1 parent ce3bda3 commit 415fa36
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 3 deletions.
46 changes: 43 additions & 3 deletions crates/re_ui/src/list_item2/property_content.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use egui::{text::TextWrapping, Align, Align2, NumExt as _, Ui};

use super::{ContentContext, DesiredWidth, ListItemContent};
use super::{ContentContext, DesiredWidth, LayoutInfoStack, ListItemContent};
use crate::{Icon, ReUi};

/// Closure to draw an icon left of the label.
Expand All @@ -20,6 +20,8 @@ struct PropertyActionButton<'a> {
pub struct PropertyContent<'a> {
label: egui::WidgetText,
min_desired_width: f32,
exact_width: bool,

icon_fn: Option<Box<IconFn<'a>>>,
show_only_when_collapsed: bool,
value_fn: Option<Box<PropertyValueFn<'a>>>,
Expand All @@ -37,6 +39,7 @@ impl<'a> PropertyContent<'a> {
Self {
label: label.into(),
min_desired_width: 200.0,
exact_width: false,
icon_fn: None,
show_only_when_collapsed: true,
value_fn: None,
Expand All @@ -54,6 +57,17 @@ impl<'a> PropertyContent<'a> {
self
}

/// Allocate the exact width required for the entire content.
///
/// Note: this is done by tracking the maximum width in the current [`super::list_item_scope`]
/// during the previous frame, so this is effective on the second frame only. If the first frame
/// is actually rendered, this can lead to a flicker.
#[inline]
pub fn exact_width(mut self, exact_width: bool) -> Self {
self.exact_width = exact_width;
self
}

/// Provide an [`Icon`] to be displayed on the left of the label.
#[inline]
pub fn with_icon(self, icon: &'a Icon) -> Self {
Expand Down Expand Up @@ -188,6 +202,7 @@ impl ListItemContent for PropertyContent<'_> {
show_only_when_collapsed,
value_fn,
action_buttons,
exact_width: _,
} = *self;

// │ │
Expand Down Expand Up @@ -313,6 +328,11 @@ impl ListItemContent for PropertyContent<'_> {
let mut child_ui =
ui.child_ui(value_rect, egui::Layout::left_to_right(egui::Align::Center));
value_fn(re_ui, &mut child_ui, visuals);

context.layout_info.register_property_content_max_width(
child_ui.ctx(),
child_ui.min_rect().right() - context.layout_info.left_x,
);
}
}

Expand All @@ -336,7 +356,27 @@ impl ListItemContent for PropertyContent<'_> {
}
}

fn desired_width(&self, _re_ui: &ReUi, _ui: &Ui) -> DesiredWidth {
DesiredWidth::AtLeast(self.min_desired_width)
fn desired_width(&self, _re_ui: &ReUi, ui: &Ui) -> DesiredWidth {
let layout_info = LayoutInfoStack::top(ui.ctx());
if self.exact_width {
if let Some(max_width) = layout_info.property_content_max_width {
let mut desired_width = max_width + layout_info.left_x - ui.max_rect().left();

// TODO(ab): ideally there wouldn't be as much code duplication with `Self::ui`
let action_button_dimension =
ReUi::small_icon_size().x + 2.0 * ui.spacing().button_padding.x;
let reserve_action_button_space =
self.action_buttons.is_some() || layout_info.reserve_action_button_space;
if reserve_action_button_space {
desired_width += action_button_dimension + ReUi::text_to_icon_padding();
}

DesiredWidth::Exact(desired_width.ceil())
} else {
DesiredWidth::AtLeast(self.min_desired_width)
}
} else {
DesiredWidth::AtLeast(self.min_desired_width)
}
}
}
22 changes: 22 additions & 0 deletions crates/re_ui/src/list_item2/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ struct LayoutStatistics {
///
/// The width is calculated from [`LayoutInfo::left_x`] to the right edge of the item.
max_item_width: Option<f32>,

/// `PropertyContent` only — max content width in the current scope.
///
/// This value is measured from `left_x`.
property_content_max_width: Option<f32>,
}

impl LayoutStatistics {
Expand Down Expand Up @@ -116,6 +121,11 @@ pub struct LayoutInfo {

/// Scope id, used to retrieve the corresponding [`LayoutStatistics`].
scope_id: egui::Id,

/// `PropertyContent` only — last frame's max content width, to be used in `desired_width()`
///
/// This value is measured from `left_x`.
pub(crate) property_content_max_width: Option<f32>,
}

impl Default for LayoutInfo {
Expand All @@ -125,6 +135,7 @@ impl Default for LayoutInfo {
left_column_width: None,
reserve_action_button_space: true,
scope_id: egui::Id::NULL,
property_content_max_width: None,
}
}
}
Expand Down Expand Up @@ -158,6 +169,16 @@ impl LayoutInfo {
stats.max_item_width = stats.max_item_width.map(|v| v.max(width)).or(Some(width));
});
}

/// `PropertyContent` only — register max content width in the current scope
pub(super) fn register_property_content_max_width(&self, ctx: &egui::Context, width: f32) {
LayoutStatistics::update(ctx, self.scope_id, |stats| {
stats.property_content_max_width = stats
.property_content_max_width
.map(|v| v.max(width))
.or(Some(width));
});
}
}

/// Stack of [`LayoutInfo`]s.
Expand Down Expand Up @@ -255,6 +276,7 @@ pub fn list_item_scope<R>(
left_column_width,
reserve_action_button_space: layout_stats.is_action_button_used,
scope_id,
property_content_max_width: layout_stats.property_content_max_width,
};

// push, run, pop
Expand Down

0 comments on commit 415fa36

Please sign in to comment.