From e838712fef3daa3506c09d318e2a677a6b3ff829 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 13 Nov 2024 16:55:50 +0100 Subject: [PATCH 01/13] Use `error_label` in more places --- crates/viewer/re_data_ui/src/component.rs | 6 +++--- crates/viewer/re_data_ui/src/component_path.rs | 7 ++----- crates/viewer/re_data_ui/src/instance_path.rs | 8 ++------ .../viewer/re_space_view_tensor/src/space_view_class.rs | 4 ++-- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/crates/viewer/re_data_ui/src/component.rs b/crates/viewer/re_data_ui/src/component.rs index 22c10422524f..569e14121307 100644 --- a/crates/viewer/re_data_ui/src/component.rs +++ b/crates/viewer/re_data_ui/src/component.rs @@ -3,7 +3,7 @@ use egui::NumExt; use re_chunk_store::UnitChunkShared; use re_entity_db::InstancePath; use re_log_types::{ComponentPath, Instance, TimeInt}; -use re_ui::{ContextExt as _, SyntaxHighlighting as _}; +use re_ui::{ContextExt as _, SyntaxHighlighting as _, UiExt}; use re_viewer_context::{UiLayout, ViewerContext}; use super::DataUi; @@ -81,11 +81,11 @@ impl<'a> DataUi for ComponentPathLatestAtResults<'a> { *component_name, ); if temporal_message_count > 0 { - ui.label(ui.ctx().error_text(format!( + ui.error_label(&format!( "Static component has {} event{} logged on timelines", temporal_message_count, if temporal_message_count > 1 { "s" } else { "" } - ))) + )) .on_hover_text( "Components should be logged either as static or on timelines, but \ never both. Values for static components logged to timelines cannot be \ diff --git a/crates/viewer/re_data_ui/src/component_path.rs b/crates/viewer/re_data_ui/src/component_path.rs index c4cdf023acb0..5296f03d2a06 100644 --- a/crates/viewer/re_data_ui/src/component_path.rs +++ b/crates/viewer/re_data_ui/src/component_path.rs @@ -1,5 +1,5 @@ use re_log_types::ComponentPath; -use re_ui::ContextExt as _; +use re_ui::UiExt; use re_viewer_context::{UiLayout, ViewerContext}; use super::DataUi; @@ -47,10 +47,7 @@ impl DataUi for ComponentPath { )); } } else { - ui.label( - ui.ctx() - .error_text(format!("Unknown component path: {self}")), - ); + ui.error_label(&format!("Unknown component path: {self}")); } } } diff --git a/crates/viewer/re_data_ui/src/instance_path.rs b/crates/viewer/re_data_ui/src/instance_path.rs index 8ccf894bf97d..7cfebdc0f0c5 100644 --- a/crates/viewer/re_data_ui/src/instance_path.rs +++ b/crates/viewer/re_data_ui/src/instance_path.rs @@ -10,7 +10,7 @@ use re_types::{ image::ImageKind, static_assert_struct_has_fields, Archetype, ComponentName, Loggable, }; -use re_ui::{ContextExt as _, UiExt as _}; +use re_ui::UiExt as _; use re_viewer_context::{ gpu_bridge::image_data_range_heuristic, ColormapWithRange, HoverHighlight, ImageInfo, ImageStatsCache, Item, UiLayout, ViewerContext, @@ -46,11 +46,7 @@ impl DataUi for InstancePath { .store() .all_components_on_timeline(&query.timeline(), entity_path) } else { - ui_layout.label( - ui, - ui.ctx() - .error_text(format!("Unknown entity: {entity_path:?}")), - ); + ui.error_label(&format!("Unknown entity: {entity_path:?}")); return; }; let Some(components) = component else { diff --git a/crates/viewer/re_space_view_tensor/src/space_view_class.rs b/crates/viewer/re_space_view_tensor/src/space_view_class.rs index 1b3c770a135e..4a6a84ca6bb9 100644 --- a/crates/viewer/re_space_view_tensor/src/space_view_class.rs +++ b/crates/viewer/re_space_view_tensor/src/space_view_class.rs @@ -13,7 +13,7 @@ use re_types::{ datatypes::{TensorData, TensorDimension}, SpaceViewClassIdentifier, View, }; -use re_ui::{list_item, ContextExt as _, UiExt as _}; +use re_ui::{list_item, UiExt as _}; use re_viewer_context::{ gpu_bridge, ApplicableEntities, ColormapWithRange, IdentifiedViewSystem as _, IndicatedEntities, PerVisualizer, SpaceViewClass, SpaceViewClassRegistryError, SpaceViewId, @@ -282,7 +282,7 @@ impl TensorSpaceView { if let Err(err) = self.tensor_slice_ui(ctx, ui, state, view_id, dimension_labels, &slice_selection) { - ui.label(ui.ctx().error_text(err.to_string())); + ui.error_label(&err.to_string()); } }); From 2f2708cd5794b2fab6f2db8e06d4a81fe0fda5d6 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 13 Nov 2024 16:56:24 +0100 Subject: [PATCH 02/13] Add much nicer error label --- crates/viewer/re_ui/src/ui_ext.rs | 65 +++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/crates/viewer/re_ui/src/ui_ext.rs b/crates/viewer/re_ui/src/ui_ext.rs index 840f86841816..aaca109cf04f 100644 --- a/crates/viewer/re_ui/src/ui_ext.rs +++ b/crates/viewer/re_ui/src/ui_ext.rs @@ -13,6 +13,41 @@ use crate::{ static FULL_SPAN_TAG: &str = "rerun_full_span"; +fn error_label_bg_color(fg_color: Color32) -> Color32 { + fg_color.gamma_multiply(0.35) +} + +fn warning_or_error_label( + ui: &mut egui::Ui, + fg_color: Color32, + visible_text: &str, + full_text: &str, +) -> egui::Response { + egui::Frame::none() + .stroke((1.0, fg_color)) + .fill(error_label_bg_color(fg_color)) + .rounding(4.0) + .inner_margin(4.0) + .show(ui, |ui| { + ui.horizontal(|ui| { + ui.spacing_mut().item_spacing.x = 4.0; + ui.colored_label(fg_color, "⚠"); + ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Wrap); + let response = ui.strong(visible_text).on_hover_ui(|ui| { + if visible_text != full_text { + ui.label(full_text); + ui.add_space(8.0); + } + ui.label("Click to copy text."); + }); + if response.clicked() { + ui.ctx().copy_text(full_text.to_owned()); + }; + }); + }) + .response +} + /// Rerun custom extensions to [`egui::Ui`]. pub trait UiExt { fn ui(&self) -> &egui::Ui; @@ -32,28 +67,26 @@ pub trait UiExt { /// Shows a small error label with the given text on hover and copies the text to the clipboard on click. fn error_with_details_on_hover(&mut self, error_text: &str) -> egui::Response { - let label = egui::Label::new(self.ui().ctx().error_text("Error")) - .selectable(false) - .sense(egui::Sense::click()); - let response = self.ui_mut().add(label); - if response.clicked() { - self.ui().ctx().copy_text(error_text.to_owned()); + let ui = self.ui_mut(); + warning_or_error_label(ui, ui.style().visuals.error_fg_color, "Error", error_text) } - response.on_hover_text(error_text) + + fn error_label_background_color(&self) -> egui::Color32 { + error_label_bg_color(self.ui().style().visuals.error_fg_color) } /// Shows an error label with the entire error text and copies the text to the clipboard on click. /// - /// Use this only if you have a lot of space to spare. + /// Use this only if the error message is short, or you have a lot of room. + /// Otherwise, use [`Self::error_with_details_on_hover`]. fn error_label(&mut self, error_text: &str) -> egui::Response { - let label = egui::Label::new(self.ui().ctx().error_text(error_text)) - .selectable(true) - .sense(egui::Sense::click()); - let response = self.ui_mut().add(label).on_hover_text("Click to copy."); - if response.clicked() { - self.ui().ctx().copy_text(error_text.to_owned()); - } - response + let ui = self.ui_mut(); + warning_or_error_label( + ui, + ui.style().visuals.error_fg_color, + error_text, + error_text, + ) } fn small_icon_button(&mut self, icon: &Icon) -> egui::Response { From cae5e88170fbf4cc8a8708af60427ec5b8d25beb Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 13 Nov 2024 16:56:36 +0100 Subject: [PATCH 03/13] Add new error label to re_ui_example --- .../re_ui/examples/re_ui_example/main.rs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/crates/viewer/re_ui/examples/re_ui_example/main.rs b/crates/viewer/re_ui/examples/re_ui_example/main.rs index 1ebd8672cac0..6f0cea91f006 100644 --- a/crates/viewer/re_ui/examples/re_ui_example/main.rs +++ b/crates/viewer/re_ui/examples/re_ui_example/main.rs @@ -457,14 +457,19 @@ impl egui_tiles::Behavior for MyTileTreeBehavior { _tile_id: egui_tiles::TileId, _pane: &mut Tab, ) -> egui_tiles::UiResponse { - egui::warn_if_debug_build(ui); - ui.label("Hover me for a tooltip") - .on_hover_text("This is a tooltip"); - - ui.label( - egui::RichText::new("Welcome to the ReUi example") - .text_style(DesignTokens::welcome_screen_h1()), - ); + egui::Frame::none().inner_margin(4.0).show(ui, |ui| { + egui::warn_if_debug_build(ui); + ui.label("Hover me for a tooltip") + .on_hover_text("This is a tooltip"); + + ui.label( + egui::RichText::new("Welcome to the ReUi example") + .text_style(DesignTokens::welcome_screen_h1()), + ); + + ui.error_label("This is an example of a long error label."); + ui.warning_label("This is an example of a long warning label."); + }); Default::default() } From 4b29410dae0972e6e39a57a720ba3dcccb900f00 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 13 Nov 2024 16:56:59 +0100 Subject: [PATCH 04/13] Use a similar error label for error labels in video view --- crates/viewer/re_space_view_spatial/src/ui.rs | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/crates/viewer/re_space_view_spatial/src/ui.rs b/crates/viewer/re_space_view_spatial/src/ui.rs index 1b9ce1c9cc45..77608d03f4dc 100644 --- a/crates/viewer/re_space_view_spatial/src/ui.rs +++ b/crates/viewer/re_space_view_spatial/src/ui.rs @@ -7,7 +7,7 @@ use re_types::{ archetypes::Pinhole, blueprint::components::VisualBounds2D, components::ViewCoordinates, image::ImageKind, }; -use re_ui::{ContextExt as _, UiExt as _}; +use re_ui::UiExt as _; use re_viewer_context::{ HoverHighlight, SelectionHighlight, SpaceViewHighlights, SpaceViewState, ViewerContext, }; @@ -214,11 +214,12 @@ pub fn create_labels( }; let font_id = egui::TextStyle::Body.resolve(parent_ui.style()); - let format = match label.style { - UiLabelStyle::Color(color) => egui::TextFormat::simple(font_id, color), - UiLabelStyle::Error => parent_ui.ctx().error_text_format(), + let is_error = matches!(label.style, UiLabelStyle::Error); + let text_color = match label.style { + UiLabelStyle::Color(color) => color, + UiLabelStyle::Error => parent_ui.style().visuals.strong_text_color(), }; - let text_color = format.color; + let format = egui::TextFormat::simple(font_id, text_color); let galley = parent_ui.fonts(|fonts| { fonts.layout_job({ @@ -249,7 +250,13 @@ pub fn create_labels( .index_highlight(label.labeled_instance.instance); let background_color = match highlight.hover { HoverHighlight::None => match highlight.selection { - SelectionHighlight::None => parent_ui.style().visuals.panel_fill, + SelectionHighlight::None => { + if is_error { + parent_ui.error_label_background_color() + } else { + parent_ui.style().visuals.panel_fill + } + } SelectionHighlight::SiblingSelection => { parent_ui.style().visuals.widgets.active.bg_fill } @@ -258,7 +265,16 @@ pub fn create_labels( HoverHighlight::Hovered => parent_ui.style().visuals.widgets.hovered.bg_fill, }; - label_shapes.push(egui::Shape::rect_filled(bg_rect, 3.0, background_color)); + let rect_stroke = if is_error { + egui::Stroke::new(1.0, parent_ui.style().visuals.error_fg_color) + } else { + egui::Stroke::NONE + }; + + label_shapes.push( + egui::epaint::RectShape::new(bg_rect.expand(4.0), 4.0, background_color, rect_stroke) + .into(), + ); label_shapes.push(egui::Shape::galley( text_rect.center_top(), galley, From 6934b9b82cdab17cf75d14d761585243383ec3ba Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 13 Nov 2024 16:57:11 +0100 Subject: [PATCH 05/13] Remove error_text_format --- crates/viewer/re_ui/src/context_ext.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/crates/viewer/re_ui/src/context_ext.rs b/crates/viewer/re_ui/src/context_ext.rs index 1132bd7e585d..aa97f735fe3b 100644 --- a/crates/viewer/re_ui/src/context_ext.rs +++ b/crates/viewer/re_ui/src/context_ext.rs @@ -70,24 +70,12 @@ pub trait ContextExt { egui::RichText::new(text).color(style.visuals.warn_fg_color) } - /// NOTE: duplicated in [`Self::error_text_format`] #[must_use] fn error_text(&self, text: impl Into) -> egui::RichText { let style = self.ctx().style(); egui::RichText::new(text).color(style.visuals.error_fg_color) } - /// NOTE: duplicated in [`Self::error_text`] - fn error_text_format(&self) -> egui::TextFormat { - let style = self.ctx().style(); - let font_id = egui::TextStyle::Body.resolve(&style); - egui::TextFormat { - font_id, - color: style.visuals.error_fg_color, - ..Default::default() - } - } - fn top_bar_style(&self, style_like_web: bool) -> TopBarStyle { let egui_zoom_factor = self.ctx().zoom_factor(); let fullscreen = self From 03594e13148f15dc5181b0dd1591ab34a592fa4d Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 13 Nov 2024 16:57:27 +0100 Subject: [PATCH 06/13] Tweak error color --- crates/viewer/re_ui/src/design_tokens.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/viewer/re_ui/src/design_tokens.rs b/crates/viewer/re_ui/src/design_tokens.rs index 4f67fc7c709d..5105f28299cf 100644 --- a/crates/viewer/re_ui/src/design_tokens.rs +++ b/crates/viewer/re_ui/src/design_tokens.rs @@ -242,6 +242,8 @@ impl DesignTokens { egui_style.visuals.image_loading_spinners = false; + egui_style.visuals.error_fg_color = egui::Color32::from_rgb(0xAB, 0x01, 0x16); + ctx.set_style(egui_style); } From 588365a103d17c979fd0e0ba013155a491ae8f92 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 13 Nov 2024 17:09:50 +0100 Subject: [PATCH 07/13] Use error_text in setting screen --- .../re_viewer/src/ui/settings_screen.rs | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/crates/viewer/re_viewer/src/ui/settings_screen.rs b/crates/viewer/re_viewer/src/ui/settings_screen.rs index 61833896562d..77a91754ed86 100644 --- a/crates/viewer/re_viewer/src/ui/settings_screen.rs +++ b/crates/viewer/re_viewer/src/ui/settings_screen.rs @@ -204,40 +204,39 @@ fn video_section_ui(ui: &mut Ui, app_options: &mut AppOptions) { #[cfg(not(target_arch = "wasm32"))] fn ffmpeg_path_status_ui(ui: &mut Ui, app_options: &AppOptions) { + use re_ui::ContextExt as _; use re_video::decode::{FFmpegVersion, FFmpegVersionParseError}; let path = app_options .video_decoder_override_ffmpeg_path .then(|| std::path::Path::new(&app_options.video_decoder_ffmpeg_path)); - if let Some(path) = path { - if !path.is_file() { - ui.error_label("The specified FFmpeg binary path does not exist or is not a file."); - return; - } - } - - let res = FFmpegVersion::for_executable(path); - match res { - Ok(version) => { - if version.is_compatible() { - ui.success_label(&format!("FFmpeg found (version {version})")); - } else { - ui.error_label(&format!("Incompatible FFmpeg version: {version}")); + let ctx = ui.ctx(); + let label_text = if path.is_some_and(|path| !path.is_file()) { + ctx.error_text("The specified FFmpeg binary path does not exist or is not a file.") + } else { + let res = FFmpegVersion::for_executable(path); + + match res { + Ok(version) => { + if version.is_compatible() { + ctx.success_text(format!("FFmpeg found (version {version})")) + } else { + ctx.error_text(format!("Incompatible FFmpeg version: {version}")) + } + } + Err(FFmpegVersionParseError::ParseVersion { raw_version }) => { + // We make this one a warning instead of an error because version parsing is flaky, and + // it might end up still working. + ctx.warning_text(format!( + "FFmpeg binary found but unable to parse version: {raw_version}" + )) } - } - Err(FFmpegVersionParseError::ParseVersion { raw_version }) => { - // We make this one a warning instead of an error because version parsing is flaky, and - // it might end up still working. - ui.warning_label(&format!( - "FFmpeg binary found but unable to parse version: {raw_version}" - )); - } - Err(err) => { - ui.error_label(&format!("Unable to check FFmpeg version: {err}")); + Err(err) => ctx.error_text(format!("Unable to check FFmpeg version: {err}")), } - } + }; + ui.label(label_text); } fn separator_with_some_space(ui: &mut egui::Ui) { From 9f6d3fca782ddc48d530be092ab3a97f0421b52f Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 13 Nov 2024 17:10:02 +0100 Subject: [PATCH 08/13] Add cmd+, shortcut for setting screen --- crates/viewer/re_ui/src/command.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/viewer/re_ui/src/command.rs b/crates/viewer/re_ui/src/command.rs index ed50f3c22a8c..c58b06069dc2 100644 --- a/crates/viewer/re_ui/src/command.rs +++ b/crates/viewer/re_ui/src/command.rs @@ -310,7 +310,14 @@ impl UICommand { Self::ToggleSelectionPanel => Some(ctrl_shift(Key::S)), Self::ToggleTimePanel => Some(ctrl_shift(Key::T)), Self::ToggleChunkStoreBrowser => Some(ctrl_shift(Key::D)), - Self::Settings => None, + Self::Settings => { + if cfg!(target_os = "macos") { + Some(KeyboardShortcut::new(Modifiers::MAC_CMD, Key::Comma)) + } else { + // TODO(emilk): shortcut for web and non-mac too + None + } + } #[cfg(debug_assertions)] Self::ToggleBlueprintInspectionPanel => Some(ctrl_shift(Key::I)), From b9140c402e377c7076353bc7d526de80e6dbe205 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 13 Nov 2024 17:12:44 +0100 Subject: [PATCH 09/13] Also change `warning_label` to match --- crates/viewer/re_ui/src/context_ext.rs | 9 ++++++++ crates/viewer/re_ui/src/design_tokens.rs | 1 + crates/viewer/re_ui/src/ui_ext.rs | 27 ++++++++++++++---------- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/crates/viewer/re_ui/src/context_ext.rs b/crates/viewer/re_ui/src/context_ext.rs index aa97f735fe3b..7d83f269cbdd 100644 --- a/crates/viewer/re_ui/src/context_ext.rs +++ b/crates/viewer/re_ui/src/context_ext.rs @@ -59,17 +59,26 @@ pub trait ContextExt { // egui::Stroke::new(stroke_width, color) } + /// Text colored to indicate success. #[must_use] fn success_text(&self, text: impl Into) -> egui::RichText { egui::RichText::new(text).color(SUCCESS_COLOR) } + /// Text colored to indicate a warning. + /// + /// For most cases, you should use [`crate::UiExt::warning_label`] instead, + /// which has a nice fat border around it. #[must_use] fn warning_text(&self, text: impl Into) -> egui::RichText { let style = self.ctx().style(); egui::RichText::new(text).color(style.visuals.warn_fg_color) } + /// Text colored to indicate an error. + /// + /// For most cases, you should use [`crate::UiExt::error_label`] instead, + /// which has a nice fat border around it. #[must_use] fn error_text(&self, text: impl Into) -> egui::RichText { let style = self.ctx().style(); diff --git a/crates/viewer/re_ui/src/design_tokens.rs b/crates/viewer/re_ui/src/design_tokens.rs index 5105f28299cf..274d20f0c567 100644 --- a/crates/viewer/re_ui/src/design_tokens.rs +++ b/crates/viewer/re_ui/src/design_tokens.rs @@ -243,6 +243,7 @@ impl DesignTokens { egui_style.visuals.image_loading_spinners = false; egui_style.visuals.error_fg_color = egui::Color32::from_rgb(0xAB, 0x01, 0x16); + egui_style.visuals.warn_fg_color = egui::Color32::from_rgb(0xFF, 0x7A, 0x0C); ctx.set_style(egui_style); } diff --git a/crates/viewer/re_ui/src/ui_ext.rs b/crates/viewer/re_ui/src/ui_ext.rs index aaca109cf04f..e678b4169750 100644 --- a/crates/viewer/re_ui/src/ui_ext.rs +++ b/crates/viewer/re_ui/src/ui_ext.rs @@ -53,23 +53,26 @@ pub trait UiExt { fn ui(&self) -> &egui::Ui; fn ui_mut(&mut self) -> &mut egui::Ui; - /// Show the label with a success color. - fn success_label(&mut self, text: &str) -> egui::Response { - let label = egui::Label::new(self.ui().ctx().success_text(text)); - self.ui_mut().add(label) - } - - /// Shows a warning label. + /// Shows a warning label with a large border. + /// + /// If you don't want a border, use [`crate::ContextExt::warning_text`]. fn warning_label(&mut self, warning_text: &str) -> egui::Response { - let label = egui::Label::new(self.ui().ctx().warning_text(warning_text)); - self.ui_mut().add(label) + let ui = self.ui_mut(); + warning_or_error_label( + ui, + ui.style().visuals.warn_fg_color, + warning_text, + warning_text, + ) } - /// Shows a small error label with the given text on hover and copies the text to the clipboard on click. + /// Shows a small error label with the given text on hover and copies the text to the clipboard on click with a large border. + /// + /// This has a large border! If you don't want a border, use [`crate::ContextExt::error_text`]. fn error_with_details_on_hover(&mut self, error_text: &str) -> egui::Response { let ui = self.ui_mut(); warning_or_error_label(ui, ui.style().visuals.error_fg_color, "Error", error_text) - } + } fn error_label_background_color(&self) -> egui::Color32 { error_label_bg_color(self.ui().style().visuals.error_fg_color) @@ -79,6 +82,8 @@ pub trait UiExt { /// /// Use this only if the error message is short, or you have a lot of room. /// Otherwise, use [`Self::error_with_details_on_hover`]. + /// + /// This has a large border! If you don't want a border, use [`crate::ContextExt::error_text`]. fn error_label(&mut self, error_text: &str) -> egui::Response { let ui = self.ui_mut(); warning_or_error_label( From 4341aceabe91177012dc67ae5ead30f6c2a8aa4f Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 13 Nov 2024 17:15:01 +0100 Subject: [PATCH 10/13] Compilatio nfix --- crates/viewer/re_ui/src/ui_ext.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/viewer/re_ui/src/ui_ext.rs b/crates/viewer/re_ui/src/ui_ext.rs index e678b4169750..044e157a7358 100644 --- a/crates/viewer/re_ui/src/ui_ext.rs +++ b/crates/viewer/re_ui/src/ui_ext.rs @@ -8,7 +8,7 @@ use egui::{ use crate::{ design_tokens, icons, list_item, list_item::{LabelContent, ListItem}, - ContextExt, DesignTokens, Icon, LabelStyle, + DesignTokens, Icon, LabelStyle, }; static FULL_SPAN_TAG: &str = "rerun_full_span"; From 2a46054e953804aceb22f3d26bf9a4906339a658 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 14 Nov 2024 09:48:22 +0100 Subject: [PATCH 11/13] Use same warning/error colors for toasts --- crates/viewer/re_ui/src/toasts.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/viewer/re_ui/src/toasts.rs b/crates/viewer/re_ui/src/toasts.rs index c78ca288c382..82773d03a38b 100644 --- a/crates/viewer/re_ui/src/toasts.rs +++ b/crates/viewer/re_ui/src/toasts.rs @@ -5,9 +5,7 @@ use std::collections::HashMap; use egui::Color32; pub const INFO_COLOR: Color32 = Color32::from_rgb(0, 155, 255); -pub const WARNING_COLOR: Color32 = Color32::from_rgb(255, 212, 0); -pub const ERROR_COLOR: Color32 = Color32::from_rgb(255, 32, 0); -pub const SUCCESS_COLOR: Color32 = Color32::from_rgb(0, 255, 32); +pub const SUCCESS_COLOR: Color32 = Color32::from_rgb(0, 240, 32); #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ToastKind { @@ -139,8 +137,8 @@ fn default_toast_contents(ui: &mut egui::Ui, toast: &Toast) -> egui::Response { if toast.options.show_icon { let (icon, icon_color) = match toast.kind { - ToastKind::Warning => ("⚠", WARNING_COLOR), - ToastKind::Error => ("❗", ERROR_COLOR), + ToastKind::Warning => ("⚠", ui.style().visuals.warn_fg_color), + ToastKind::Error => ("❗", ui.style().visuals.error_fg_color), ToastKind::Success => ("✔", SUCCESS_COLOR), _ => ("ℹ", INFO_COLOR), }; From 7f74d206fe6cf0f3666a34593547070f0c66b5b7 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 14 Nov 2024 09:48:42 +0100 Subject: [PATCH 12/13] Use a "success" label for settings screen --- crates/viewer/re_ui/src/ui_ext.rs | 36 ++++++++++++++----- .../re_viewer/src/ui/settings_screen.rs | 19 +++++----- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/crates/viewer/re_ui/src/ui_ext.rs b/crates/viewer/re_ui/src/ui_ext.rs index 044e157a7358..da1b4ba39e1d 100644 --- a/crates/viewer/re_ui/src/ui_ext.rs +++ b/crates/viewer/re_ui/src/ui_ext.rs @@ -6,8 +6,9 @@ use egui::{ }; use crate::{ - design_tokens, icons, list_item, - list_item::{LabelContent, ListItem}, + design_tokens, icons, + list_item::{self, LabelContent, ListItem}, + toasts::SUCCESS_COLOR, DesignTokens, Icon, LabelStyle, }; @@ -17,9 +18,11 @@ fn error_label_bg_color(fg_color: Color32) -> Color32 { fg_color.gamma_multiply(0.35) } -fn warning_or_error_label( +/// success, warning, error… +fn notification_label( ui: &mut egui::Ui, fg_color: Color32, + icon: &str, visible_text: &str, full_text: &str, ) -> egui::Response { @@ -27,11 +30,12 @@ fn warning_or_error_label( .stroke((1.0, fg_color)) .fill(error_label_bg_color(fg_color)) .rounding(4.0) - .inner_margin(4.0) + .inner_margin(3.0) + .outer_margin(1.0) // HACK because we set clip_rect_margin. TODO(emilk): https://github.com/emilk/egui/issues/4019 .show(ui, |ui| { ui.horizontal(|ui| { ui.spacing_mut().item_spacing.x = 4.0; - ui.colored_label(fg_color, "⚠"); + ui.colored_label(fg_color, icon); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Wrap); let response = ui.strong(visible_text).on_hover_ui(|ui| { if visible_text != full_text { @@ -53,14 +57,23 @@ pub trait UiExt { fn ui(&self) -> &egui::Ui; fn ui_mut(&mut self) -> &mut egui::Ui; + /// Shows a success label with a large border. + /// + /// If you don't want a border, use [`crate::ContextExt::success_text`]. + fn success_label(&mut self, success_text: &str) -> egui::Response { + let ui = self.ui_mut(); + notification_label(ui, SUCCESS_COLOR, "✅", success_text, success_text) + } + /// Shows a warning label with a large border. /// /// If you don't want a border, use [`crate::ContextExt::warning_text`]. fn warning_label(&mut self, warning_text: &str) -> egui::Response { let ui = self.ui_mut(); - warning_or_error_label( + notification_label( ui, ui.style().visuals.warn_fg_color, + "⚠", warning_text, warning_text, ) @@ -71,7 +84,13 @@ pub trait UiExt { /// This has a large border! If you don't want a border, use [`crate::ContextExt::error_text`]. fn error_with_details_on_hover(&mut self, error_text: &str) -> egui::Response { let ui = self.ui_mut(); - warning_or_error_label(ui, ui.style().visuals.error_fg_color, "Error", error_text) + notification_label( + ui, + ui.style().visuals.error_fg_color, + "⚠", + "Error", + error_text, + ) } fn error_label_background_color(&self) -> egui::Color32 { @@ -86,9 +105,10 @@ pub trait UiExt { /// This has a large border! If you don't want a border, use [`crate::ContextExt::error_text`]. fn error_label(&mut self, error_text: &str) -> egui::Response { let ui = self.ui_mut(); - warning_or_error_label( + notification_label( ui, ui.style().visuals.error_fg_color, + "⚠", error_text, error_text, ) diff --git a/crates/viewer/re_viewer/src/ui/settings_screen.rs b/crates/viewer/re_viewer/src/ui/settings_screen.rs index 77a91754ed86..6053d0550eab 100644 --- a/crates/viewer/re_viewer/src/ui/settings_screen.rs +++ b/crates/viewer/re_viewer/src/ui/settings_screen.rs @@ -204,39 +204,38 @@ fn video_section_ui(ui: &mut Ui, app_options: &mut AppOptions) { #[cfg(not(target_arch = "wasm32"))] fn ffmpeg_path_status_ui(ui: &mut Ui, app_options: &AppOptions) { - use re_ui::ContextExt as _; use re_video::decode::{FFmpegVersion, FFmpegVersionParseError}; let path = app_options .video_decoder_override_ffmpeg_path .then(|| std::path::Path::new(&app_options.video_decoder_ffmpeg_path)); - let ctx = ui.ctx(); - let label_text = if path.is_some_and(|path| !path.is_file()) { - ctx.error_text("The specified FFmpeg binary path does not exist or is not a file.") + if path.is_some_and(|path| !path.is_file()) { + ui.error_label("The specified FFmpeg binary path does not exist or is not a file."); } else { let res = FFmpegVersion::for_executable(path); match res { Ok(version) => { if version.is_compatible() { - ctx.success_text(format!("FFmpeg found (version {version})")) + ui.success_label(&format!("FFmpeg found (version {version})")); } else { - ctx.error_text(format!("Incompatible FFmpeg version: {version}")) + ui.error_label(&format!("Incompatible FFmpeg version: {version}")); } } Err(FFmpegVersionParseError::ParseVersion { raw_version }) => { // We make this one a warning instead of an error because version parsing is flaky, and // it might end up still working. - ctx.warning_text(format!( + ui.warning_label(&format!( "FFmpeg binary found but unable to parse version: {raw_version}" - )) + )); } - Err(err) => ctx.error_text(format!("Unable to check FFmpeg version: {err}")), + Err(err) => { + ui.error_label(&format!("Unable to check FFmpeg version: {err}")); + } } }; - ui.label(label_text); } fn separator_with_some_space(ui: &mut egui::Ui) { From 8fdd0ea46a22240a53fb61253ef0af7468f38cf9 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 14 Nov 2024 09:49:58 +0100 Subject: [PATCH 13/13] HACK -> TODO --- crates/viewer/re_ui/src/ui_ext.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/viewer/re_ui/src/ui_ext.rs b/crates/viewer/re_ui/src/ui_ext.rs index da1b4ba39e1d..58b7cfcf804a 100644 --- a/crates/viewer/re_ui/src/ui_ext.rs +++ b/crates/viewer/re_ui/src/ui_ext.rs @@ -31,7 +31,7 @@ fn notification_label( .fill(error_label_bg_color(fg_color)) .rounding(4.0) .inner_margin(3.0) - .outer_margin(1.0) // HACK because we set clip_rect_margin. TODO(emilk): https://github.com/emilk/egui/issues/4019 + .outer_margin(1.0) // Needed because we set clip_rect_margin. TODO(emilk): https://github.com/emilk/egui/issues/4019 .show(ui, |ui| { ui.horizontal(|ui| { ui.spacing_mut().item_spacing.x = 4.0;