From 25a652b415bb00434d216ace74c41258f87cbc85 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Mon, 6 Jan 2025 16:45:42 +0100 Subject: [PATCH 1/5] `CustomContent` wraps closure in an egui::Ui with the correct `max_rect`, and has support for action button --- .../src/item_heading_no_breadcrumbs.rs | 15 +- .../src/item_heading_with_breadcrumbs.rs | 49 +++--- .../src/view_entity_picker.rs | 10 +- .../examples/re_ui_example/right_panel.rs | 20 ++- .../re_ui/src/list_item/custom_content.rs | 143 ++++++++++++++++++ .../{other_contents.rs => debug_content.rs} | 32 ---- crates/viewer/re_ui/src/list_item/mod.rs | 6 +- .../re_ui/src/list_item/property_content.rs | 4 +- crates/viewer/re_ui/tests/list_item_tests.rs | 26 ++-- .../re_ui/tests/snapshots/list_items.png | 4 +- 10 files changed, 213 insertions(+), 96 deletions(-) create mode 100644 crates/viewer/re_ui/src/list_item/custom_content.rs rename crates/viewer/re_ui/src/list_item/{other_contents.rs => debug_content.rs} (51%) diff --git a/crates/viewer/re_selection_panel/src/item_heading_no_breadcrumbs.rs b/crates/viewer/re_selection_panel/src/item_heading_no_breadcrumbs.rs index c78d2176e8d4..366b3be55bc8 100644 --- a/crates/viewer/re_selection_panel/src/item_heading_no_breadcrumbs.rs +++ b/crates/viewer/re_selection_panel/src/item_heading_no_breadcrumbs.rs @@ -24,17 +24,10 @@ pub fn item_title_list_item( .interactive(true) .show_flat( ui, - list_item::CustomContent::new(|ui, context| { - ui.allocate_new_ui( - egui::UiBuilder::new() - .max_rect(context.rect) - .layout(egui::Layout::left_to_right(egui::Align::Center)), - |ui| { - ui.spacing_mut().item_spacing.x = 4.0; - ui.style_mut().interaction.selectable_labels = false; - item_heading_no_breadcrumbs(ctx, viewport, ui, item); - }, - ); + list_item::CustomContent::new(|ui, _| { + ui.spacing_mut().item_spacing.x = 4.0; + ui.style_mut().interaction.selectable_labels = false; + item_heading_no_breadcrumbs(ctx, viewport, ui, item); }), ); cursor_interact_with_selectable(ctx, response, item.clone()); diff --git a/crates/viewer/re_selection_panel/src/item_heading_with_breadcrumbs.rs b/crates/viewer/re_selection_panel/src/item_heading_with_breadcrumbs.rs index a006a2eac302..fc8315e41155 100644 --- a/crates/viewer/re_selection_panel/src/item_heading_with_breadcrumbs.rs +++ b/crates/viewer/re_selection_panel/src/item_heading_with_breadcrumbs.rs @@ -37,37 +37,30 @@ pub fn item_heading_with_breadcrumbs( .selected(true) .show_flat( ui, - list_item::CustomContent::new(|ui, context| { - ui.allocate_new_ui( - egui::UiBuilder::new() - .max_rect(context.rect) - .layout(egui::Layout::left_to_right(egui::Align::Center)), - |ui| { - ui.spacing_mut().item_spacing.x = 4.0; + list_item::CustomContent::new(|ui, _| { + ui.spacing_mut().item_spacing.x = 4.0; - { - // No background rectangles, even for hovered items - let visuals = ui.visuals_mut(); - visuals.widgets.active.bg_fill = egui::Color32::TRANSPARENT; - visuals.widgets.active.weak_bg_fill = egui::Color32::TRANSPARENT; - visuals.widgets.hovered.bg_fill = egui::Color32::TRANSPARENT; - visuals.widgets.hovered.weak_bg_fill = egui::Color32::TRANSPARENT; - } + { + // No background rectangles, even for hovered items + let visuals = ui.visuals_mut(); + visuals.widgets.active.bg_fill = egui::Color32::TRANSPARENT; + visuals.widgets.active.weak_bg_fill = egui::Color32::TRANSPARENT; + visuals.widgets.hovered.bg_fill = egui::Color32::TRANSPARENT; + visuals.widgets.hovered.weak_bg_fill = egui::Color32::TRANSPARENT; + } - // First the C>R>U>M>B>S> - { - let previous_style = ui.style().clone(); - // Dimmer colors for breadcrumbs - let visuals = ui.visuals_mut(); - visuals.widgets.inactive.fg_stroke.color = egui::hex_color!("#6A8CD0"); // TODO(#3133): use design tokens - item_bread_crumbs_ui(ctx, viewport, ui, item); - ui.set_style(previous_style); - } + // First the C>R>U>M>B>S> + { + let previous_style = ui.style().clone(); + // Dimmer colors for breadcrumbs + let visuals = ui.visuals_mut(); + visuals.widgets.inactive.fg_stroke.color = egui::hex_color!("#6A8CD0"); // TODO(#3133): use design tokens + item_bread_crumbs_ui(ctx, viewport, ui, item); + ui.set_style(previous_style); + } - // Then the full name of the main item: - last_part_of_item_heading(ctx, viewport, ui, item); - }, - ); + // Then the full name of the main item: + last_part_of_item_heading(ctx, viewport, ui, item); }), ); } diff --git a/crates/viewer/re_selection_panel/src/view_entity_picker.rs b/crates/viewer/re_selection_panel/src/view_entity_picker.rs index 74ed964631bc..65cebadc678d 100644 --- a/crates/viewer/re_selection_panel/src/view_entity_picker.rs +++ b/crates/viewer/re_selection_panel/src/view_entity_picker.rs @@ -90,15 +90,10 @@ fn add_entities_tree_ui( entity_path_filter: &ResolvedEntityPathFilter, entities_add_info: &IntMap, ) { - let item_content = list_item::CustomContent::new(|ui, content_ctx| { - let mut child_ui = ui.new_child( - egui::UiBuilder::new() - .max_rect(content_ctx.rect) - .layout(egui::Layout::left_to_right(egui::Align::Center)), - ); + let item_content = list_item::CustomContent::new(|ui, _| { add_entities_line_ui( ctx, - &mut child_ui, + ui, name, tree, view, @@ -191,6 +186,7 @@ fn add_entities_line_ui( } }); + //TODO(ab): use `CustomContent` support for action button to implement this. ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { if entity_path_filter.contains_rule_for_exactly(entity_path) { // Reset-button diff --git a/crates/viewer/re_ui/examples/re_ui_example/right_panel.rs b/crates/viewer/re_ui/examples/re_ui_example/right_panel.rs index 680015615141..5787dcf8aae4 100644 --- a/crates/viewer/re_ui/examples/re_ui_example/right_panel.rs +++ b/crates/viewer/re_ui/examples/re_ui_example/right_panel.rs @@ -324,14 +324,28 @@ impl RightPanel { ui.list_item().show_hierarchical( ui, - list_item::CustomContent::new(|ui, context| { + list_item::CustomContent::new(|ui, _| { ui.ctx().debug_painter().debug_rect( - context.rect, + ui.max_rect(), egui::Color32::LIGHT_RED, "CustomContent delegates to a closure", ); }), - ) + ); + + ui.list_item().show_hierarchical( + ui, + list_item::CustomContent::new(|ui, _| { + ui.ctx().debug_painter().debug_rect( + ui.max_rect(), + egui::Color32::LIGHT_RED, + "CustomContent with an action button", + ); + }) + .action_button(&re_ui::icons::ADD, || { + re_log::warn!("Add button clicked"); + }), + ); }, ); } diff --git a/crates/viewer/re_ui/src/list_item/custom_content.rs b/crates/viewer/re_ui/src/list_item/custom_content.rs new file mode 100644 index 000000000000..31473d1514ea --- /dev/null +++ b/crates/viewer/re_ui/src/list_item/custom_content.rs @@ -0,0 +1,143 @@ +use crate::list_item::{ContentContext, DesiredWidth, ListItemContent}; +use crate::DesignTokens; +use egui::{NumExt as _, Ui}; + +/// [`ListItemContent`] that mostly delegates to a closure. +#[expect(clippy::type_complexity)] +pub struct CustomContent<'a> { + ui: Box) + 'a>, + desired_width: DesiredWidth, + + //TODO(ab): in the future, that should be a `Vec`, with some auto expanding mini-toolbar + button: Option>, +} + +impl<'a> CustomContent<'a> { + /// Create a content with a custom UI closure. + /// + /// The closure will be called from withing a [`egui::Ui`] with its maximum width set as per the + /// list item geometry. Note that this may differ from [`crate::ContentContext::rect`] if a + /// button is set. + pub fn new(ui: impl FnOnce(&mut egui::Ui, &ContentContext<'_>) + 'a) -> Self { + Self { + ui: Box::new(ui), + desired_width: Default::default(), + button: None, + } + } + + /// Set the minimum desired width for the entire content. + #[inline] + pub fn with_desired_width(mut self, desired_width: DesiredWidth) -> Self { + self.desired_width = desired_width; + self + } + + /// Add a right-aligned [`super::ItemButton`]. + /// + /// Note: for aesthetics, space is always reserved for the action button. + // TODO(#6191): accept multiple calls for this function for multiple actions. + #[inline] + pub fn button(mut self, button: impl super::ItemButton + 'a) -> Self { + // TODO(#6191): support multiple action buttons + assert!( + self.button.is_none(), + "Only one action button is supported right now" + ); + + self.button = Some(Box::new(button)); + self + } + + /// Helper to add an [`super::ItemActionButton`] to the right of the item. + /// + /// See [`Self::button`] for more information. + #[inline] + pub fn action_button( + self, + icon: &'static crate::icons::Icon, + on_click: impl FnOnce() + 'a, + ) -> Self { + self.action_button_with_enabled(icon, true, on_click) + } + + /// Helper to add an enabled/disabled [`super::ItemActionButton`] to the right of the item. + /// + /// See [`Self::button`] for more information. + #[inline] + pub fn action_button_with_enabled( + self, + icon: &'static crate::icons::Icon, + enabled: bool, + on_click: impl FnOnce() + 'a, + ) -> Self { + self.button(super::ItemActionButton::new(icon, on_click).enabled(enabled)) + } + + /// Helper to add a [`super::ItemMenuButton`] to the right of the item. + /// + /// See [`Self::button`] for more information. + #[inline] + pub fn menu_button( + self, + icon: &'static crate::icons::Icon, + add_contents: impl FnOnce(&mut egui::Ui) + 'a, + ) -> Self { + self.button(super::ItemMenuButton::new(icon, add_contents)) + } +} + +impl ListItemContent for CustomContent<'_> { + fn ui(self: Box, ui: &mut egui::Ui, context: &ContentContext<'_>) { + let Self { + ui: content_ui, + desired_width: _, + button, + } = *self; + + let button_dimension = + DesignTokens::small_icon_size().x + 2.0 * ui.spacing().button_padding.x; + + let content_width = if button.is_some() { + (context.rect.width() - button_dimension - DesignTokens::text_to_icon_padding()) + .at_least(0.0) + } else { + context.rect.width() + }; + + let content_rect = egui::Rect::from_min_size( + context.rect.min, + egui::vec2(content_width, context.rect.height()), + ); + + ui.allocate_new_ui( + egui::UiBuilder::new() + .max_rect(content_rect) + .layout(egui::Layout::left_to_right(egui::Align::Center)), + |ui| { + content_ui(ui, context); + }, + ); + + if let Some(button) = button { + let action_button_rect = egui::Rect::from_center_size( + context.rect.right_center() - egui::vec2(button_dimension / 2.0, 0.0), + egui::Vec2::splat(button_dimension), + ); + + // the right to left layout is used to mimic LabelContent's buttons behavior and get a + // better alignment + let mut child_ui = ui.new_child( + egui::UiBuilder::new() + .max_rect(action_button_rect) + .layout(egui::Layout::right_to_left(egui::Align::Center)), + ); + + button.ui(&mut child_ui); + } + } + + fn desired_width(&self, _ui: &Ui) -> DesiredWidth { + self.desired_width + } +} diff --git a/crates/viewer/re_ui/src/list_item/other_contents.rs b/crates/viewer/re_ui/src/list_item/debug_content.rs similarity index 51% rename from crates/viewer/re_ui/src/list_item/other_contents.rs rename to crates/viewer/re_ui/src/list_item/debug_content.rs index 30d5141a2f57..1916b26b3905 100644 --- a/crates/viewer/re_ui/src/list_item/other_contents.rs +++ b/crates/viewer/re_ui/src/list_item/debug_content.rs @@ -2,38 +2,6 @@ use egui::Ui; use crate::list_item::{ContentContext, DesiredWidth, ListItemContent}; -/// [`ListItemContent`] that delegates to a closure. -#[allow(clippy::type_complexity)] -pub struct CustomContent<'a> { - ui: Box) + 'a>, - desired_width: DesiredWidth, -} - -impl<'a> CustomContent<'a> { - pub fn new(ui: impl FnOnce(&mut egui::Ui, &ContentContext<'_>) + 'a) -> Self { - Self { - ui: Box::new(ui), - desired_width: Default::default(), - } - } - - #[inline] - pub fn with_desired_width(mut self, desired_width: DesiredWidth) -> Self { - self.desired_width = desired_width; - self - } -} - -impl ListItemContent for CustomContent<'_> { - fn ui(self: Box, ui: &mut egui::Ui, context: &ContentContext<'_>) { - (self.ui)(ui, context); - } - - fn desired_width(&self, _ui: &Ui) -> DesiredWidth { - self.desired_width - } -} - /// [`ListItemContent`] that displays the content rect. #[derive(Debug, Clone, Default)] pub struct DebugContent { diff --git a/crates/viewer/re_ui/src/list_item/mod.rs b/crates/viewer/re_ui/src/list_item/mod.rs index 80178875716f..fd09e3c74742 100644 --- a/crates/viewer/re_ui/src/list_item/mod.rs +++ b/crates/viewer/re_ui/src/list_item/mod.rs @@ -3,19 +3,21 @@ //! TODO(ab): provide some top-level documentation here. mod button_content; +mod custom_content; +mod debug_content; mod item_button; mod label_content; #[allow(clippy::module_inception)] mod list_item; -mod other_contents; mod property_content; mod scope; pub use button_content::*; +pub use custom_content::*; +pub use debug_content::*; pub use item_button::*; pub use label_content::*; pub use list_item::*; -pub use other_contents::*; pub use property_content::*; pub use scope::*; diff --git a/crates/viewer/re_ui/src/list_item/property_content.rs b/crates/viewer/re_ui/src/list_item/property_content.rs index ebe1ea4c5a03..c081ff1cd9d4 100644 --- a/crates/viewer/re_ui/src/list_item/property_content.rs +++ b/crates/viewer/re_ui/src/list_item/property_content.rs @@ -42,7 +42,7 @@ impl<'a> PropertyContent<'a> { /// Set the minimum desired width for the entire content. /// - /// Since there is no possibly way to meaningfully collapse two to three columns worth of + /// Since there is no possible way to meaningfully collapse two to three columns worth of /// content, this is set to 200.0 by default. #[inline] pub fn min_desired_width(mut self, min_desired_width: f32) -> Self { @@ -78,7 +78,7 @@ impl<'a> PropertyContent<'a> { // TODO(#6191): support multiple action buttons assert!( self.button.is_none(), - "Only one action button supported right now" + "Only one action button is supported right now" ); self.button = Some(Box::new(button)); diff --git a/crates/viewer/re_ui/tests/list_item_tests.rs b/crates/viewer/re_ui/tests/list_item_tests.rs index 86db31082935..95a00c96cfcc 100644 --- a/crates/viewer/re_ui/tests/list_item_tests.rs +++ b/crates/viewer/re_ui/tests/list_item_tests.rs @@ -143,9 +143,7 @@ pub fn test_list_items_should_match_snapshot() { ui, list_item::PropertyContent::new("Color") .with_icon(&re_ui::icons::VIEW_TEXT) - .action_button(&re_ui::icons::ADD, || { - re_log::warn!("Add button clicked"); - }) + .action_button(&re_ui::icons::ADD, || {}) .value_color(&color), ); @@ -153,9 +151,7 @@ pub fn test_list_items_should_match_snapshot() { ui, list_item::PropertyContent::new("Color (editable)") .with_icon(&re_ui::icons::VIEW_TEXT) - .action_button(&re_ui::icons::ADD, || { - re_log::warn!("Add button clicked"); - }) + .action_button(&re_ui::icons::ADD, || {}) .value_color_mut(&mut color), ); }); @@ -176,14 +172,26 @@ pub fn test_list_items_should_match_snapshot() { ui.list_item().show_hierarchical( ui, - list_item::CustomContent::new(|ui, context| { + list_item::CustomContent::new(|ui, _| { ui.ctx().debug_painter().debug_rect( - context.rect, + ui.max_rect(), egui::Color32::LIGHT_RED, "CustomContent delegates to a closure", ); }), - ) + ); + + ui.list_item().show_hierarchical( + ui, + list_item::CustomContent::new(|ui, _| { + ui.ctx().debug_painter().debug_rect( + ui.max_rect(), + egui::Color32::LIGHT_RED, + "CustomContent with an action button", + ); + }) + .action_button(&re_ui::icons::ADD, || {}), + ); }, ); }; diff --git a/crates/viewer/re_ui/tests/snapshots/list_items.png b/crates/viewer/re_ui/tests/snapshots/list_items.png index 92329ffe8c55..755af9c5daa9 100644 --- a/crates/viewer/re_ui/tests/snapshots/list_items.png +++ b/crates/viewer/re_ui/tests/snapshots/list_items.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f8c3f52449a6240d67c66c9a04793742dc3b02380c7482ada8cc0f8be315968d -size 84773 +oid sha256:7a40a7dfe71c3265b5a94a0bc9a862f4d311ec9d2f526dee5ff871453462a3a4 +size 89536 From deca72253de55da2c6c4121d24bdb1df66afa19c Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Tue, 7 Jan 2025 19:26:02 +0100 Subject: [PATCH 2/5] Make `CustomContent` sizing more customisable --- .../re_ui/src/list_item/custom_content.rs | 48 ++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/crates/viewer/re_ui/src/list_item/custom_content.rs b/crates/viewer/re_ui/src/list_item/custom_content.rs index 31473d1514ea..0cba983f3931 100644 --- a/crates/viewer/re_ui/src/list_item/custom_content.rs +++ b/crates/viewer/re_ui/src/list_item/custom_content.rs @@ -1,12 +1,30 @@ +use egui::{NumExt as _, Ui}; + use crate::list_item::{ContentContext, DesiredWidth, ListItemContent}; use crate::DesignTokens; -use egui::{NumExt as _, Ui}; + +/// Control how the [`CustomContent`] advertises its width. +#[derive(Debug, Clone, Copy)] +enum CustomContentDesiredWidth { + /// Use the provided [`DesiredWidth`]. + DesiredWidth(DesiredWidth), + + /// Use [`DesiredWidth::AtLeast`] with a width computed from the provided content, plus any + /// extras such as a button. + ContentWidth(f32), +} + +impl Default for CustomContentDesiredWidth { + fn default() -> Self { + Self::DesiredWidth(Default::default()) + } +} /// [`ListItemContent`] that mostly delegates to a closure. #[expect(clippy::type_complexity)] pub struct CustomContent<'a> { ui: Box) + 'a>, - desired_width: DesiredWidth, + desired_width: CustomContentDesiredWidth, //TODO(ab): in the future, that should be a `Vec`, with some auto expanding mini-toolbar button: Option>, @@ -26,10 +44,17 @@ impl<'a> CustomContent<'a> { } } - /// Set the minimum desired width for the entire content. + /// Set the desired width for the entire content. #[inline] pub fn with_desired_width(mut self, desired_width: DesiredWidth) -> Self { - self.desired_width = desired_width; + self.desired_width = CustomContentDesiredWidth::DesiredWidth(desired_width); + self + } + + /// Set the desired width based on the provided content width. If a button is set, its width + /// will be taken into account and added to the content width. + pub fn with_content_width(mut self, desired_content_width: f32) -> Self { + self.desired_width = CustomContentDesiredWidth::ContentWidth(desired_content_width); self } @@ -137,7 +162,18 @@ impl ListItemContent for CustomContent<'_> { } } - fn desired_width(&self, _ui: &Ui) -> DesiredWidth { - self.desired_width + //TODO: add snapshot test for this logic + fn desired_width(&self, ui: &Ui) -> DesiredWidth { + match self.desired_width { + CustomContentDesiredWidth::DesiredWidth(desired_width) => desired_width, + CustomContentDesiredWidth::ContentWidth(mut content_width) => { + if self.button.is_some() { + content_width += DesignTokens::small_icon_size().x + + 2.0 * ui.spacing().button_padding.x + + DesignTokens::text_to_icon_padding(); + } + DesiredWidth::AtLeast(content_width) + } + } } } From 7d970b4845d04ba93edef5583d3684e055b36444 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Wed, 8 Jan 2025 09:57:03 +0100 Subject: [PATCH 3/5] Update list_item test snapshot --- crates/viewer/re_ui/tests/snapshots/list_items.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/viewer/re_ui/tests/snapshots/list_items.png b/crates/viewer/re_ui/tests/snapshots/list_items.png index 755af9c5daa9..a77e07f76e98 100644 --- a/crates/viewer/re_ui/tests/snapshots/list_items.png +++ b/crates/viewer/re_ui/tests/snapshots/list_items.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a40a7dfe71c3265b5a94a0bc9a862f4d311ec9d2f526dee5ff871453462a3a4 -size 89536 +oid sha256:086ea1d934e76962279b3d5b57fa0afdb626587ff4b302597d70ca7e2626df38 +size 89533 From cb3e696d9a25bc895965857bf4dc58146fab78bb Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 10 Jan 2025 15:48:17 +0100 Subject: [PATCH 4/5] lints --- crates/viewer/re_ui/src/list_item/custom_content.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/viewer/re_ui/src/list_item/custom_content.rs b/crates/viewer/re_ui/src/list_item/custom_content.rs index 0cba983f3931..af0f77a5ecc2 100644 --- a/crates/viewer/re_ui/src/list_item/custom_content.rs +++ b/crates/viewer/re_ui/src/list_item/custom_content.rs @@ -33,7 +33,7 @@ pub struct CustomContent<'a> { impl<'a> CustomContent<'a> { /// Create a content with a custom UI closure. /// - /// The closure will be called from withing a [`egui::Ui`] with its maximum width set as per the + /// The closure will be called from within a [`egui::Ui`] with its maximum width set as per the /// list item geometry. Note that this may differ from [`crate::ContentContext::rect`] if a /// button is set. pub fn new(ui: impl FnOnce(&mut egui::Ui, &ContentContext<'_>) + 'a) -> Self { @@ -53,6 +53,7 @@ impl<'a> CustomContent<'a> { /// Set the desired width based on the provided content width. If a button is set, its width /// will be taken into account and added to the content width. + #[inline] pub fn with_content_width(mut self, desired_content_width: f32) -> Self { self.desired_width = CustomContentDesiredWidth::ContentWidth(desired_content_width); self @@ -162,7 +163,6 @@ impl ListItemContent for CustomContent<'_> { } } - //TODO: add snapshot test for this logic fn desired_width(&self, ui: &Ui) -> DesiredWidth { match self.desired_width { CustomContentDesiredWidth::DesiredWidth(desired_width) => desired_width, From c3ee2e04a2b528e1e779dde024d8b1f433b9f3f0 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 10 Jan 2025 16:31:50 +0100 Subject: [PATCH 5/5] fix doclink --- crates/viewer/re_ui/src/list_item/custom_content.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/viewer/re_ui/src/list_item/custom_content.rs b/crates/viewer/re_ui/src/list_item/custom_content.rs index af0f77a5ecc2..31a85cc9a8ab 100644 --- a/crates/viewer/re_ui/src/list_item/custom_content.rs +++ b/crates/viewer/re_ui/src/list_item/custom_content.rs @@ -34,8 +34,8 @@ impl<'a> CustomContent<'a> { /// Create a content with a custom UI closure. /// /// The closure will be called from within a [`egui::Ui`] with its maximum width set as per the - /// list item geometry. Note that this may differ from [`crate::ContentContext::rect`] if a - /// button is set. + /// list item geometry. Note that this may differ from [`ContentContext::rect`] if a button is + /// set. pub fn new(ui: impl FnOnce(&mut egui::Ui, &ContentContext<'_>) + 'a) -> Self { Self { ui: Box::new(ui),