From 590122737679a8805d45df368301f3f26bed86e0 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Tue, 4 Jun 2024 12:03:02 +0200 Subject: [PATCH] Use egui's `UiStack` to implement full span widgets --- Cargo.lock | 26 +++---- Cargo.toml | 18 ++--- crates/re_data_ui/src/app_id.rs | 15 +--- crates/re_data_ui/src/data_source.rs | 19 +----- crates/re_data_ui/src/item_ui.rs | 44 ++++++------ crates/re_edit_ui/src/marker_shape.rs | 6 +- .../re_selection_panel/src/selection_panel.rs | 68 +++++++++---------- crates/re_time_panel/src/lib.rs | 28 ++++---- crates/re_ui/examples/re_ui_example/main.rs | 41 +++++------ crates/re_ui/src/full_span.rs | 60 ---------------- crates/re_ui/src/lib.rs | 67 ++++++++++++------ crates/re_ui/src/list_item/label_content.rs | 7 +- crates/re_ui/src/list_item/list_item.rs | 5 +- .../re_ui/src/list_item/property_content.rs | 8 ++- crates/re_ui/src/modal.rs | 57 ++++++++-------- crates/re_viewer/src/app_state.rs | 26 ++++--- .../src/gpu_bridge/colormap.rs | 8 +-- 17 files changed, 212 insertions(+), 291 deletions(-) delete mode 100644 crates/re_ui/src/full_span.rs diff --git a/Cargo.lock b/Cargo.lock index 0860ec1d8348..8ec08fb31765 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1564,7 +1564,7 @@ checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" [[package]] name = "ecolor" version = "0.27.2" -source = "git+https://github.com/emilk/egui.git?rev=c0a9800d051f2d23fb63e26cbc87d35e7e17d13e#c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" +source = "git+https://github.com/emilk/egui.git?rev=a28792194ddacbdb8012dafa49cdfb84c6a3ba65#a28792194ddacbdb8012dafa49cdfb84c6a3ba65" dependencies = [ "bytemuck", "serde", @@ -1573,7 +1573,7 @@ dependencies = [ [[package]] name = "eframe" version = "0.27.2" -source = "git+https://github.com/emilk/egui.git?rev=c0a9800d051f2d23fb63e26cbc87d35e7e17d13e#c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" +source = "git+https://github.com/emilk/egui.git?rev=a28792194ddacbdb8012dafa49cdfb84c6a3ba65#a28792194ddacbdb8012dafa49cdfb84c6a3ba65" dependencies = [ "ahash", "bytemuck", @@ -1609,7 +1609,7 @@ dependencies = [ [[package]] name = "egui" version = "0.27.2" -source = "git+https://github.com/emilk/egui.git?rev=c0a9800d051f2d23fb63e26cbc87d35e7e17d13e#c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" +source = "git+https://github.com/emilk/egui.git?rev=a28792194ddacbdb8012dafa49cdfb84c6a3ba65#a28792194ddacbdb8012dafa49cdfb84c6a3ba65" dependencies = [ "accesskit", "ahash", @@ -1626,7 +1626,7 @@ dependencies = [ [[package]] name = "egui-wgpu" version = "0.27.2" -source = "git+https://github.com/emilk/egui.git?rev=c0a9800d051f2d23fb63e26cbc87d35e7e17d13e#c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" +source = "git+https://github.com/emilk/egui.git?rev=a28792194ddacbdb8012dafa49cdfb84c6a3ba65#a28792194ddacbdb8012dafa49cdfb84c6a3ba65" dependencies = [ "ahash", "bytemuck", @@ -1645,7 +1645,7 @@ dependencies = [ [[package]] name = "egui-winit" version = "0.27.2" -source = "git+https://github.com/emilk/egui.git?rev=c0a9800d051f2d23fb63e26cbc87d35e7e17d13e#c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" +source = "git+https://github.com/emilk/egui.git?rev=a28792194ddacbdb8012dafa49cdfb84c6a3ba65#a28792194ddacbdb8012dafa49cdfb84c6a3ba65" dependencies = [ "accesskit_winit", "ahash", @@ -1664,7 +1664,7 @@ dependencies = [ [[package]] name = "egui_commonmark" version = "0.16.1" -source = "git+https://github.com/rerun-io/egui_commonmark?rev=1c08d5ea58d60c6a61487b5eecefdfdb35c47036#1c08d5ea58d60c6a61487b5eecefdfdb35c47036" +source = "git+https://github.com/rerun-io/egui_commonmark?rev=11a04af9177214f4cb7dce3733d1e14f6890ceb9#11a04af9177214f4cb7dce3733d1e14f6890ceb9" dependencies = [ "egui", "egui_commonmark_backend", @@ -1675,7 +1675,7 @@ dependencies = [ [[package]] name = "egui_commonmark_backend" version = "0.16.1" -source = "git+https://github.com/rerun-io/egui_commonmark?rev=1c08d5ea58d60c6a61487b5eecefdfdb35c47036#1c08d5ea58d60c6a61487b5eecefdfdb35c47036" +source = "git+https://github.com/rerun-io/egui_commonmark?rev=11a04af9177214f4cb7dce3733d1e14f6890ceb9#11a04af9177214f4cb7dce3733d1e14f6890ceb9" dependencies = [ "egui", "egui_extras", @@ -1685,7 +1685,7 @@ dependencies = [ [[package]] name = "egui_extras" version = "0.27.2" -source = "git+https://github.com/emilk/egui.git?rev=c0a9800d051f2d23fb63e26cbc87d35e7e17d13e#c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" +source = "git+https://github.com/emilk/egui.git?rev=a28792194ddacbdb8012dafa49cdfb84c6a3ba65#a28792194ddacbdb8012dafa49cdfb84c6a3ba65" dependencies = [ "ahash", "egui", @@ -1701,7 +1701,7 @@ dependencies = [ [[package]] name = "egui_glow" version = "0.27.2" -source = "git+https://github.com/emilk/egui.git?rev=c0a9800d051f2d23fb63e26cbc87d35e7e17d13e#c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" +source = "git+https://github.com/emilk/egui.git?rev=a28792194ddacbdb8012dafa49cdfb84c6a3ba65#a28792194ddacbdb8012dafa49cdfb84c6a3ba65" dependencies = [ "ahash", "bytemuck", @@ -1719,7 +1719,7 @@ dependencies = [ [[package]] name = "egui_plot" version = "0.27.2" -source = "git+https://github.com/emilk/egui.git?rev=c0a9800d051f2d23fb63e26cbc87d35e7e17d13e#c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" +source = "git+https://github.com/emilk/egui.git?rev=a28792194ddacbdb8012dafa49cdfb84c6a3ba65#a28792194ddacbdb8012dafa49cdfb84c6a3ba65" dependencies = [ "ahash", "egui", @@ -1728,7 +1728,7 @@ dependencies = [ [[package]] name = "egui_tiles" version = "0.8.0" -source = "git+https://github.com/rerun-io/egui_tiles?rev=7a9ef17dd873efc2c803a0a91a64246ab1a2fa83#7a9ef17dd873efc2c803a0a91a64246ab1a2fa83" +source = "git+https://github.com/rerun-io/egui_tiles?rev=7ed84adfa20e1a62d1a1da75a6083d3fc194164d#7ed84adfa20e1a62d1a1da75a6083d3fc194164d" dependencies = [ "ahash", "egui", @@ -1762,7 +1762,7 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "emath" version = "0.27.2" -source = "git+https://github.com/emilk/egui.git?rev=c0a9800d051f2d23fb63e26cbc87d35e7e17d13e#c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" +source = "git+https://github.com/emilk/egui.git?rev=a28792194ddacbdb8012dafa49cdfb84c6a3ba65#a28792194ddacbdb8012dafa49cdfb84c6a3ba65" dependencies = [ "bytemuck", "serde", @@ -1863,7 +1863,7 @@ dependencies = [ [[package]] name = "epaint" version = "0.27.2" -source = "git+https://github.com/emilk/egui.git?rev=c0a9800d051f2d23fb63e26cbc87d35e7e17d13e#c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" +source = "git+https://github.com/emilk/egui.git?rev=a28792194ddacbdb8012dafa49cdfb84c6a3ba65#a28792194ddacbdb8012dafa49cdfb84c6a3ba65" dependencies = [ "ab_glyph", "ahash", diff --git a/Cargo.toml b/Cargo.toml index 6c15d63c478f..dc64f929824d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -454,13 +454,13 @@ missing_errors_doc = "allow" # As a last resport, patch with a commit to our own repository. # ALWAYS document what PR the commit hash is part of, or when it was merged into the upstream trunk. -ecolor = { git = "https://github.com/emilk/egui.git", rev = "c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" } # egui master 2024-06-03 -eframe = { git = "https://github.com/emilk/egui.git", rev = "c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" } # egui master 2024-06-03 -egui = { git = "https://github.com/emilk/egui.git", rev = "c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" } # egui master 2024-06-03 -egui_extras = { git = "https://github.com/emilk/egui.git", rev = "c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" } # egui master 2024-06-03 -egui_plot = { git = "https://github.com/emilk/egui.git", rev = "c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" } # egui master 2024-06-03 -egui-wgpu = { git = "https://github.com/emilk/egui.git", rev = "c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" } # egui master 2024-06-03 -emath = { git = "https://github.com/emilk/egui.git", rev = "c0a9800d051f2d23fb63e26cbc87d35e7e17d13e" } # egui master 2024-06-03 +ecolor = { git = "https://github.com/emilk/egui.git", rev = "a28792194ddacbdb8012dafa49cdfb84c6a3ba65" } # egui master 2024-06-04 +eframe = { git = "https://github.com/emilk/egui.git", rev = "a28792194ddacbdb8012dafa49cdfb84c6a3ba65" } # egui master 2024-06-04 +egui = { git = "https://github.com/emilk/egui.git", rev = "a28792194ddacbdb8012dafa49cdfb84c6a3ba65" } # egui master 2024-06-04 +egui_extras = { git = "https://github.com/emilk/egui.git", rev = "a28792194ddacbdb8012dafa49cdfb84c6a3ba65" } # egui master 2024-06-04 +egui_plot = { git = "https://github.com/emilk/egui.git", rev = "a28792194ddacbdb8012dafa49cdfb84c6a3ba65" } # egui master 2024-06-04 +egui-wgpu = { git = "https://github.com/emilk/egui.git", rev = "a28792194ddacbdb8012dafa49cdfb84c6a3ba65" } # egui master 2024-06-04 +emath = { git = "https://github.com/emilk/egui.git", rev = "a28792194ddacbdb8012dafa49cdfb84c6a3ba65" } # egui master 2024-06-04 # Useful while developing: # ecolor = { path = "../../egui/crates/ecolor" } @@ -471,6 +471,6 @@ emath = { git = "https://github.com/emilk/egui.git", rev = "c0a9800d051f2d23fb63 # egui-wgpu = { path = "../../egui/crates/egui-wgpu" } # emath = { path = "../../egui/crates/emath" } -egui_tiles = { git = "https://github.com/rerun-io/egui_tiles", rev = "7a9ef17dd873efc2c803a0a91a64246ab1a2fa83" } # main 2024-05-28, which works with egui master +egui_tiles = { git = "https://github.com/rerun-io/egui_tiles", rev = "7ed84adfa20e1a62d1a1da75a6083d3fc194164d" } # main 2024-06-04, which works with egui master -egui_commonmark = { git = "https://github.com/rerun-io/egui_commonmark", rev = "1c08d5ea58d60c6a61487b5eecefdfdb35c47036" } # https://github.com/lampsitter/egui_commonmark/pull/51 +egui_commonmark = { git = "https://github.com/rerun-io/egui_commonmark", rev = "11a04af9177214f4cb7dce3733d1e14f6890ceb9" } # https://github.com/lampsitter/egui_commonmark/pull/51 diff --git a/crates/re_data_ui/src/app_id.rs b/crates/re_data_ui/src/app_id.rs index 57589dbdcd2b..b14add0549bf 100644 --- a/crates/re_data_ui/src/app_id.rs +++ b/crates/re_data_ui/src/app_id.rs @@ -44,7 +44,7 @@ impl crate::DataUi for ApplicationId { // Using the same content ui also for tooltips even if it can't be interacted with. // (still displays the content we want) if !recordings.is_empty() { - let content_ui = |ui: &mut egui::Ui| { + ui.scope(|ui| { ui.spacing_mut().item_spacing.y = 0.0; ui.add_space(8.0); @@ -52,19 +52,6 @@ impl crate::DataUi for ApplicationId { for entity_db in recordings { entity_db_button_ui(ctx, ui, entity_db, true); } - }; - - ui.scope(|ui| { - // TODO(#6246): this test is needed because we're called in a context that may or may - // not have a full span defined. - if ui_layout == UiLayout::Tooltip { - // This typically happens in tooltips, so a scope is needed - //TODO(ab): in the context of tooltips, ui.max_rect() doesn't provide the correct width - re_ui::full_span::full_span_scope(ui, ui.max_rect().x_range(), content_ui); - } else { - // This only happens from the selection panel, so the full span scope is already set. - content_ui(ui); - } }); } diff --git a/crates/re_data_ui/src/data_source.rs b/crates/re_data_ui/src/data_source.rs index b9177885dfaf..8e49e6b183eb 100644 --- a/crates/re_data_ui/src/data_source.rs +++ b/crates/re_data_ui/src/data_source.rs @@ -50,9 +50,8 @@ impl crate::DataUi for re_smart_channel::SmartChannelSource { recordings.sort_by_key(|entity_db| entity_db.store_info().map(|info| info.started)); blueprints.sort_by_key(|entity_db| entity_db.store_info().map(|info| info.started)); - // Using the same content ui also for tooltips even if it can't be interacted with. - // (still displays the content we want) - let content_ui = |ui: &mut egui::Ui| { + ui.scope(|ui| { + ui.spacing_mut().item_spacing.y = 0.0; if !recordings.is_empty() { ui.add_space(8.0); ui.strong("Recordings from this data source"); @@ -68,20 +67,6 @@ impl crate::DataUi for re_smart_channel::SmartChannelSource { entity_db_button_ui(ctx, ui, entity_db, true); } } - }; - ui.scope(|ui| { - ui.spacing_mut().item_spacing.y = 0.0; - - // TODO(#6246): this test is needed because we're called in a context that may or may - // not have a full span defined. - if ui_layout == UiLayout::Tooltip { - // This typically happens in tooltips, so a scope is needed - //TODO(ab): in the context of tooltips, ui.max_rect() doesn't provide the correct width - re_ui::full_span::full_span_scope(ui, ui.max_rect().x_range(), content_ui); - } else { - // This only happens from the selection panel, so the full span scope is already set. - content_ui(ui); - } }); } } diff --git a/crates/re_data_ui/src/item_ui.rs b/crates/re_data_ui/src/item_ui.rs index 1991ff8a9c12..9324cac3969c 100644 --- a/crates/re_data_ui/src/item_ui.rs +++ b/crates/re_data_ui/src/item_ui.rs @@ -411,31 +411,27 @@ pub fn component_path_button_to( let response = response.on_hover_ui(|ui| { ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); // Make tooltip as wide as needed - let background_x_range = (ui.max_rect() + ui.spacing().menu_margin).x_range(); - list_item::list_item_scope(ui, "component_path_tooltip", |ui| { - re_ui::full_span::full_span_scope(ui, background_x_range, |ui| { - list_item::ListItem::new(ctx.re_ui) - .interactive(false) - .show_flat( - ui, - list_item::LabelContent::new(if is_static { - "Static component" - } else { - "Temporal component" - }) - .with_icon(icon) - .exact_width(true), - ); - - let component_name = component_path.component_name; - - ui.label(format!("Full name: {}", component_name.full_name())); - - if let Some(url) = component_name.doc_url() { - list_item::hyperlink_to_ui(ctx.re_ui, ui, "Documentation", url); - } - }); + list_item::ListItem::new(ctx.re_ui) + .interactive(false) + .show_flat( + ui, + list_item::LabelContent::new(if is_static { + "Static component" + } else { + "Temporal component" + }) + .with_icon(icon) + .exact_width(true), + ); + + let component_name = component_path.component_name; + + ui.label(format!("Full name: {}", component_name.full_name())); + + if let Some(url) = component_name.doc_url() { + list_item::hyperlink_to_ui(ctx.re_ui, ui, "Documentation", url); + } }); }); diff --git a/crates/re_edit_ui/src/marker_shape.rs b/crates/re_edit_ui/src/marker_shape.rs index 761cd8541ac0..776ef6b06ef5 100644 --- a/crates/re_edit_ui/src/marker_shape.rs +++ b/crates/re_edit_ui/src/marker_shape.rs @@ -17,8 +17,6 @@ pub(crate) fn edit_marker_shape_ui( .selected_text(marker_text) .height(320.0) .show_ui(ui, |ui| { - let background_x_range = (ui.max_rect() + ui.spacing().menu_margin).x_range(); - let list_ui = |ui: &mut egui::Ui| { let mut combined_response: Option = None; for marker in MarkerShape::ALL { @@ -48,9 +46,7 @@ pub(crate) fn edit_marker_shape_ui( combined_response.expect("At least one marker shape should be available") }; - re_ui::full_span::full_span_scope(ui, background_x_range, |ui| { - re_ui::list_item::list_item_scope(ui, "marker_shape", list_ui) - }) + re_ui::list_item::list_item_scope(ui, "marker_shape", list_ui) }), ) } diff --git a/crates/re_selection_panel/src/selection_panel.rs b/crates/re_selection_panel/src/selection_panel.rs index ce967b2f48ad..676ac3b7d5cb 100644 --- a/crates/re_selection_panel/src/selection_panel.rs +++ b/crates/re_selection_panel/src/selection_panel.rs @@ -72,37 +72,35 @@ impl SelectionPanel { ctx.rec_cfg.time_ctrl.write().highlighted_range = None; panel.show_animated_inside(ui, expanded, |ui: &mut egui::Ui| { - re_ui::full_span::full_span_scope(ui, ui.max_rect().x_range(), |ui| { - ctx.re_ui.panel_content(ui, |_, ui| { - let hover = "The Selection View contains information and options about \ + ctx.re_ui.panel_content(ui, |_, ui| { + let hover = "The Selection View contains information and options about \ the currently selected object(s)"; - ctx.re_ui - .panel_title_bar_with_buttons(ui, "Selection", Some(hover), |ui| { - let mut history = ctx.selection_state().history.lock(); - if let Some(selection) = self.selection_state_ui.selection_ui( - ctx.re_ui, - ui, - blueprint, - &mut history, - ) { - ctx.selection_state().set_selection(selection); - } - }); - }); + ctx.re_ui + .panel_title_bar_with_buttons(ui, "Selection", Some(hover), |ui| { + let mut history = ctx.selection_state().history.lock(); + if let Some(selection) = self.selection_state_ui.selection_ui( + ctx.re_ui, + ui, + blueprint, + &mut history, + ) { + ctx.selection_state().set_selection(selection); + } + }); + }); - // move the vertical spacing between the title and the content to _inside_ the scroll - // area - ui.add_space(-ui.spacing().item_spacing.y); + // move the vertical spacing between the title and the content to _inside_ the scroll + // area + ui.add_space(-ui.spacing().item_spacing.y); - egui::ScrollArea::both() - .auto_shrink([false; 2]) - .show(ui, |ui| { - ui.add_space(ui.spacing().item_spacing.y); - ctx.re_ui.panel_content(ui, |_, ui| { - self.contents(ctx, blueprint, view_states, ui); - }); + egui::ScrollArea::both() + .auto_shrink([false; 2]) + .show(ui, |ui| { + ui.add_space(ui.spacing().item_spacing.y); + ctx.re_ui.panel_content(ui, |_, ui| { + self.contents(ctx, blueprint, view_states, ui); }); - }); + }); }); // run modals (these are noop if the modals are not active) @@ -529,16 +527,14 @@ fn container_children( ..Default::default() } .show(ui, |ui| { - re_ui::full_span::full_span_scope(ui, ui.max_rect().x_range(), |ui| { - list_item::list_item_scope(ui, "children list", |ui| { - ui.spacing_mut().item_spacing.y = 0.0; + list_item::list_item_scope(ui, "children list", |ui| { + ui.spacing_mut().item_spacing.y = 0.0; - egui::Frame { - inner_margin: egui::Margin::symmetric(4.0, 0.0), - ..Default::default() - } - .show(ui, show_content); - }); + egui::Frame { + inner_margin: egui::Margin::symmetric(4.0, 0.0), + ..Default::default() + } + .show(ui, show_content); }); }); } diff --git a/crates/re_time_panel/src/lib.rs b/crates/re_time_panel/src/lib.rs index 3834e1410e15..bd32470b1042 100644 --- a/crates/re_time_panel/src/lib.rs +++ b/crates/re_time_panel/src/lib.rs @@ -446,20 +446,24 @@ impl TimePanel { ui.min_rect().bottom()..=ui.max_rect().bottom(), )); + let old_clip_rect = ui.clip_rect(); + ui.set_clip_rect(egui::Rect::from_x_y_ranges( + 0.0..=time_x_left, + ui.max_rect().y_range(), + )); // All the entity rows and their data density graphs: - re_ui::full_span::full_span_scope(ui, (0.0..=time_x_left).into(), |ui| { - list_item::list_item_scope(ui, "streams_tree", |ui| { - self.tree_ui( - ctx, - viewport_blueprint, - entity_db, - time_ctrl, - &time_area_response, - &lower_time_area_painter, - ui, - ); - }); + list_item::list_item_scope(ui, "streams_tree", |ui| { + self.tree_ui( + ctx, + viewport_blueprint, + entity_db, + time_ctrl, + &time_area_response, + &lower_time_area_painter, + ui, + ); }); + ui.set_clip_rect(old_clip_rect); { // Paint a shadow between the stream names on the left diff --git a/crates/re_ui/examples/re_ui_example/main.rs b/crates/re_ui/examples/re_ui_example/main.rs index 25bdc14595d3..2c4db8c95635 100644 --- a/crates/re_ui/examples/re_ui_example/main.rs +++ b/crates/re_ui/examples/re_ui_example/main.rs @@ -269,25 +269,23 @@ impl eframe::App for ExampleApp { ..Default::default() }) .show_animated(egui_ctx, self.show_left_panel, |ui| { - re_ui::full_span::full_span_scope(ui, ui.max_rect().x_range(), |ui| { - egui::TopBottomPanel::top("left_panel_top_bar") - .exact_height(re_ui::ReUi::title_bar_height()) - .frame(egui::Frame { - inner_margin: egui::Margin::symmetric(re_ui::ReUi::view_padding(), 0.0), + egui::TopBottomPanel::top("left_panel_top_bar") + .exact_height(re_ui::ReUi::title_bar_height()) + .frame(egui::Frame { + inner_margin: egui::Margin::symmetric(re_ui::ReUi::view_padding(), 0.0), + ..Default::default() + }) + .show_inside(ui, left_panel_top_section_ui); + + egui::ScrollArea::both() + .auto_shrink([false; 2]) + .show(ui, |ui| { + egui::Frame { + inner_margin: egui::Margin::same(re_ui::ReUi::view_padding()), ..Default::default() - }) - .show_inside(ui, left_panel_top_section_ui); - - egui::ScrollArea::both() - .auto_shrink([false; 2]) - .show(ui, |ui| { - egui::Frame { - inner_margin: egui::Margin::same(re_ui::ReUi::view_padding()), - ..Default::default() - } - .show(ui, left_panel_bottom_section_ui); - }); - }); + } + .show(ui, left_panel_bottom_section_ui); + }); }); // RIGHT PANEL @@ -311,11 +309,8 @@ impl eframe::App for ExampleApp { .frame(panel_frame) .min_width(0.0) .show_animated(egui_ctx, self.show_right_panel, |ui| { - // define the hover/selection background highlight range for all nested `ListItem`s - re_ui::full_span::full_span_scope(ui, ui.max_rect().x_range(), |ui| { - ui.spacing_mut().item_spacing.y = 0.0; - self.right_panel.ui(&self.re_ui, ui); - }); + ui.spacing_mut().item_spacing.y = 0.0; + self.right_panel.ui(&self.re_ui, ui); }); egui::CentralPanel::default() diff --git a/crates/re_ui/src/full_span.rs b/crates/re_ui/src/full_span.rs deleted file mode 100644 index db812a33e5b7..000000000000 --- a/crates/re_ui/src/full_span.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! Support for full-span widgets. -//! -//! Full-span widgets are widgets which draw beyond the boundaries of `ui.max_rect()`, e.g. to -//! provide highlighting without margin, like [`crate::list_item`]. Typically, in the context of a -//! side panel, the full span is the entire width of the panel, excluding any margin. -//! -//! This module maintains a stack of full span values (effectively[`egui::Rangef`]) using nestable -//! scopes (via [`full_span_scope`]) and makes them available to widgets (via [`get_full_span`]). - -#[derive(Clone, Default)] -struct FullSpanStack(Vec); - -/// Set up a full-span scope. -/// -/// Note: -/// - Uses [`egui::Ui::scope`] internally, so it's safe to modify `ui` in the closure. -/// - Can be nested since the full-span range is stored in a stack. -pub fn full_span_scope( - ui: &mut egui::Ui, - background_x_range: egui::Rangef, - content: impl FnOnce(&mut egui::Ui) -> R, -) -> R { - // push - ui.ctx().data_mut(|writer| { - let stack: &mut FullSpanStack = writer.get_temp_mut_or_default(egui::Id::NULL); - stack.0.push(background_x_range); - }); - - // - let result = ui.scope(content).inner; - - // pop - ui.ctx().data_mut(|writer| { - let stack: &mut FullSpanStack = writer.get_temp_mut_or_default(egui::Id::NULL); - stack.0.pop(); - }); - - result -} - -/// Retrieve the current full-span scope. -/// -/// If called outside a [`full_span_scope`], this function emits a warning and returns the clip -/// rectangle width. In debug build, it panics. -pub fn get_full_span(ui: &egui::Ui) -> egui::Rangef { - let range = ui.ctx().data_mut(|writer| { - let stack: &mut FullSpanStack = writer.get_temp_mut_or_default(egui::Id::NULL); - stack.0.last().copied() - }); - - if range.is_none() { - re_log::warn_once!("Full span requested outside a `full_span_scope()`"); - } - debug_assert!( - range.is_some(), - "Full span requested outside a `full_span_scope()`" - ); - - range.unwrap_or(ui.clip_rect().x_range()) -} diff --git a/crates/re_ui/src/lib.rs b/crates/re_ui/src/lib.rs index c710cba43d8c..98caa0dbc84f 100644 --- a/crates/re_ui/src/lib.rs +++ b/crates/re_ui/src/lib.rs @@ -8,7 +8,6 @@ mod syntax_highlighting; mod toggle_switch; pub mod drag_and_drop; -pub mod full_span; pub mod icons; pub mod list_item; pub mod modal; @@ -541,7 +540,7 @@ impl ReUi { let painter = ui.painter(); painter.hline( - crate::full_span::get_full_span(ui), + ui.get_full_span(), painter.round_to_pixel(rect.center().y), stroke, ); @@ -584,16 +583,14 @@ impl ReUi { ui.with_layout(egui::Layout::top_down_justified(egui::Align::LEFT), |ui| { ui.set_width(widget_response.rect.width() - frame_margin.sum().x); - crate::full_span::full_span_scope(ui, ui.cursor().x_range(), |ui| { - crate::list_item::list_item_scope(ui, popup_id, |ui| { - egui::ScrollArea::vertical().show(ui, |ui| { - egui::Frame { - //TODO(ab): use design token - inner_margin: egui::Margin::symmetric(8.0, 0.0), - ..Default::default() - } - .show(ui, |ui| ret = Some(add_contents(ui))) - }) + crate::list_item::list_item_scope(ui, popup_id, |ui| { + egui::ScrollArea::vertical().show(ui, |ui| { + egui::Frame { + //TODO(ab): use design token + inner_margin: egui::Margin::symmetric(8.0, 0.0), + ..Default::default() + } + .show(ui, |ui| ret = Some(add_contents(ui))) }) }) }) @@ -647,7 +644,7 @@ impl ReUi { |ui| { // draw horizontal separator lines let rect = egui::Rect::from_x_y_ranges( - crate::full_span::get_full_span(ui), + ui.get_full_span(), ui.available_rect_before_wrap().y_range(), ); let hline_stroke = ui.style().visuals.widgets.noninteractive.bg_stroke; @@ -873,10 +870,7 @@ impl ReUi { ui.painter().galley(text_pos, galley, visuals.text_color()); // Let the rect cover the full panel width: - let bg_rect = egui::Rect::from_x_y_ranges( - crate::full_span::get_full_span(ui), - rect.y_range(), - ); + let bg_rect = egui::Rect::from_x_y_ranges(ui.get_full_span(), rect.y_range()); ui.painter().set( background_frame, @@ -1315,12 +1309,41 @@ pub fn drop_down_menu( egui::ComboBox::from_id_source(id_source) .selected_text(selected_text) .show_ui(ui, |ui| { - let background_x_range = (ui.max_rect() + ui.spacing().menu_margin).x_range(); - list_item::list_item_scope(ui, "inner_scope", |ui| { - full_span::full_span_scope(ui, background_x_range, |ui| { - content(ui); - }); + content(ui); }); }); } + +/// Rerun custom extensions to [`egui::Ui`]. +// TODO(#4569): move everything here +pub trait UiExt { + fn ui(&self) -> &egui::Ui; + fn ui_mut(&mut self) -> &mut egui::Ui; + + /// Retrieve the current full-span scope. + fn get_full_span(&self) -> egui::Rangef { + for node in self.ui().stack().iter() { + if node.has_visible_frame() + || node.is_panel_ui() + || node.is_root_ui() + || node.kind == Some(egui::UiKind::TableCell) + { + return (node.max_rect + node.frame.inner_margin).x_range(); + } + } + + // should never happen + egui::Rangef::EVERYTHING + } +} + +impl UiExt for egui::Ui { + fn ui(&self) -> &egui::Ui { + self + } + + fn ui_mut(&mut self) -> &mut egui::Ui { + self + } +} diff --git a/crates/re_ui/src/list_item/label_content.rs b/crates/re_ui/src/list_item/label_content.rs index de7a10e2703d..32a9ae5e0f11 100644 --- a/crates/re_ui/src/list_item/label_content.rs +++ b/crates/re_ui/src/list_item/label_content.rs @@ -214,8 +214,11 @@ impl ListItemContent for LabelContent<'_> { || always_show_buttons; let button_response = if should_show_buttons { if let Some(buttons) = buttons_fn { - let mut ui = - ui.child_ui(text_rect, egui::Layout::right_to_left(egui::Align::Center)); + let mut ui = ui.child_ui( + text_rect, + egui::Layout::right_to_left(egui::Align::Center), + None, + ); Some(buttons(re_ui, &mut ui)) } else { None diff --git a/crates/re_ui/src/list_item/list_item.rs b/crates/re_ui/src/list_item/list_item.rs index 837f2492824a..1072d892b3f5 100644 --- a/crates/re_ui/src/list_item/list_item.rs +++ b/crates/re_ui/src/list_item/list_item.rs @@ -3,7 +3,7 @@ use egui::{NumExt, Response, Shape, Ui}; use crate::list_item::{ContentContext, DesiredWidth, LayoutInfoStack, ListItemContent}; -use crate::ReUi; +use crate::{ReUi, UiExt as _}; struct ListItemResponse { /// Response of the whole [`ListItem`] @@ -246,8 +246,7 @@ impl<'a> ListItem<'a> { // We use the state set by ListItemContainer to determine how far the background should // extend. let layout_info = LayoutInfoStack::top(ui.ctx()); - let bg_rect = - egui::Rect::from_x_y_ranges(crate::full_span::get_full_span(ui), rect.y_range()); + let bg_rect = egui::Rect::from_x_y_ranges(ui.get_full_span(), rect.y_range()); // Record the max allocated width. layout_info.register_max_item_width(ui.ctx(), rect.right() - layout_info.left_x); diff --git a/crates/re_ui/src/list_item/property_content.rs b/crates/re_ui/src/list_item/property_content.rs index de81ad37ec61..f0a59fd107db 100644 --- a/crates/re_ui/src/list_item/property_content.rs +++ b/crates/re_ui/src/list_item/property_content.rs @@ -341,8 +341,11 @@ impl ListItemContent for PropertyContent<'_> { }; if let Some(value_fn) = value_fn { if should_show_value { - let mut child_ui = - ui.child_ui(value_rect, egui::Layout::left_to_right(egui::Align::Center)); + let mut child_ui = ui.child_ui( + value_rect, + egui::Layout::left_to_right(egui::Align::Center), + None, + ); value_fn(re_ui, &mut child_ui, visuals); context.layout_info.register_property_content_max_width( @@ -364,6 +367,7 @@ impl ListItemContent for PropertyContent<'_> { let mut child_ui = ui.child_ui( action_button_rect, egui::Layout::right_to_left(egui::Align::Center), + None, ); child_ui.add_enabled_ui(action_button.enabled, |ui| { diff --git a/crates/re_ui/src/modal.rs b/crates/re_ui/src/modal.rs index 25f4e44aa4e0..970abec83eda 100644 --- a/crates/re_ui/src/modal.rs +++ b/crates/re_ui/src/modal.rs @@ -204,40 +204,38 @@ impl Modal { let item_spacing_y = ui.spacing().item_spacing.y; ui.spacing_mut().item_spacing.y = 0.0; - crate::full_span::full_span_scope(ui, ui.clip_rect().x_range(), |ui| { - egui::Frame { - inner_margin: egui::Margin::symmetric(ReUi::view_padding(), 0.0), - ..Default::default() - } - .show(ui, |ui| { - ui.add_space(ReUi::view_padding()); - Self::title_bar(re_ui, ui, &self.title, &mut open); - ui.add_space(ReUi::view_padding()); - crate::ReUi::full_span_separator(ui); + egui::Frame { + inner_margin: egui::Margin::symmetric(ReUi::view_padding(), 0.0), + ..Default::default() + } + .show(ui, |ui| { + ui.add_space(ReUi::view_padding()); + Self::title_bar(re_ui, ui, &self.title, &mut open); + ui.add_space(ReUi::view_padding()); + crate::ReUi::full_span_separator(ui); - if self.full_span_content { - // no further spacing for the content UI - content_ui(re_ui, ui, &mut open) - } else { - // we must restore vertical spacing and add view padding at the bottom - ui.add_space(item_spacing_y); + if self.full_span_content { + // no further spacing for the content UI + content_ui(re_ui, ui, &mut open) + } else { + // we must restore vertical spacing and add view padding at the bottom + ui.add_space(item_spacing_y); - egui::Frame { - inner_margin: egui::Margin { - bottom: ReUi::view_padding(), - ..Default::default() - }, + egui::Frame { + inner_margin: egui::Margin { + bottom: ReUi::view_padding(), ..Default::default() - } - .show(ui, |ui| { - ui.spacing_mut().item_spacing.y = item_spacing_y; - content_ui(re_ui, ui, &mut open) - }) - .inner + }, + ..Default::default() } - }) - .inner + .show(ui, |ui| { + ui.spacing_mut().item_spacing.y = item_spacing_y; + content_ui(re_ui, ui, &mut open) + }) + .inner + } }) + .inner }); // Any click outside causes the window to close. @@ -283,6 +281,7 @@ impl Modal { let mut ui = ui.child_ui( ui.max_rect(), egui::Layout::right_to_left(egui::Align::Center), + None, ); if re_ui .small_icon_button(&mut ui, &crate::icons::CLOSE) diff --git a/crates/re_viewer/src/app_state.rs b/crates/re_viewer/src/app_state.rs index 1cdd220987a2..4f541623daf2 100644 --- a/crates/re_viewer/src/app_state.rs +++ b/crates/re_viewer/src/app_state.rs @@ -402,23 +402,21 @@ impl AppState { ); ui.set_clip_rect(max_rect); - re_ui::full_span::full_span_scope(ui, ui.max_rect().x_range(), |ui| { - // ListItem don't need vertical spacing so we disable it, but restore it - // before drawing the blueprint panel. - ui.spacing_mut().item_spacing.y = 0.0; + // ListItem don't need vertical spacing so we disable it, but restore it + // before drawing the blueprint panel. + ui.spacing_mut().item_spacing.y = 0.0; - let pre_cursor = ui.cursor(); - recordings_panel_ui(&ctx, rx, ui, welcome_screen_state); - let any_recording_shows = pre_cursor == ui.cursor(); + let pre_cursor = ui.cursor(); + recordings_panel_ui(&ctx, rx, ui, welcome_screen_state); + let any_recording_shows = pre_cursor == ui.cursor(); - if any_recording_shows { - ui.add_space(4.0); - } + if any_recording_shows { + ui.add_space(4.0); + } - if !show_welcome { - blueprint_tree.show(&ctx, &viewport_blueprint, ui); - } - }); + if !show_welcome { + blueprint_tree.show(&ctx, &viewport_blueprint, ui); + } }, ); diff --git a/crates/re_viewer_context/src/gpu_bridge/colormap.rs b/crates/re_viewer_context/src/gpu_bridge/colormap.rs index c5b7ebced71e..5d49125d0fb2 100644 --- a/crates/re_viewer_context/src/gpu_bridge/colormap.rs +++ b/crates/re_viewer_context/src/gpu_bridge/colormap.rs @@ -1,5 +1,5 @@ use crate::gpu_bridge::{get_or_create_texture, render_image}; -use re_ui::{full_span, list_item}; +use re_ui::list_item; /// Show the given colormap as a horizontal bar. fn colormap_preview_ui( @@ -93,10 +93,6 @@ pub fn colormap_dropdown_button_ui( egui::ComboBox::from_id_source("color map select") .selected_text(selected_text) .show_ui(ui, |ui| { - let background_x_range = (ui.max_rect() + ui.spacing().menu_margin).x_range(); - - list_item::list_item_scope(ui, "inner_scope", |ui| { - full_span::full_span_scope(ui, background_x_range, content_ui); - }); + list_item::list_item_scope(ui, "inner_scope", content_ui); }); }