Skip to content

Commit 95b118f

Browse files
authored
Much nicer looking error and warning messages (#8127)
### What * Closes #8091 ## Before ![image](https://github.com/user-attachments/assets/e7df583a-0032-41e9-87b7-0a2a8a84579c) ![image](https://github.com/user-attachments/assets/8ca98429-e00d-4925-96c0-edfe0d986e11) ![image](https://github.com/user-attachments/assets/345d7023-066f-4741-a121-8ab372589ed6) ## After ![image](https://github.com/user-attachments/assets/1c86d97f-3fac-4f70-9ac5-d67b3418cf3d) ![image](https://github.com/user-attachments/assets/03f6d1e7-6474-43d3-aae3-d4573c416ee5) ![image](https://github.com/user-attachments/assets/07260f8b-a8a2-4223-92e5-da67521e88a6) ### 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/8127?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/8127?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)! * [x] If have noted any breaking changes to the log API in `CHANGELOG.md` and the migration guide - [PR Build Summary](https://build.rerun.io/pr/8127) - [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`. To deploy documentation changes immediately after merging this PR, add the `deploy docs` label.
1 parent fe840c0 commit 95b118f

File tree

12 files changed

+177
-102
lines changed

12 files changed

+177
-102
lines changed

crates/viewer/re_data_ui/src/component.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use egui::NumExt;
33
use re_chunk_store::UnitChunkShared;
44
use re_entity_db::InstancePath;
55
use re_log_types::{ComponentPath, Instance, TimeInt};
6-
use re_ui::{ContextExt as _, SyntaxHighlighting as _};
6+
use re_ui::{ContextExt as _, SyntaxHighlighting as _, UiExt};
77
use re_viewer_context::{UiLayout, ViewerContext};
88

99
use super::DataUi;
@@ -81,11 +81,11 @@ impl<'a> DataUi for ComponentPathLatestAtResults<'a> {
8181
*component_name,
8282
);
8383
if temporal_message_count > 0 {
84-
ui.label(ui.ctx().error_text(format!(
84+
ui.error_label(&format!(
8585
"Static component has {} event{} logged on timelines",
8686
temporal_message_count,
8787
if temporal_message_count > 1 { "s" } else { "" }
88-
)))
88+
))
8989
.on_hover_text(
9090
"Components should be logged either as static or on timelines, but \
9191
never both. Values for static components logged to timelines cannot be \

crates/viewer/re_data_ui/src/component_path.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use re_log_types::ComponentPath;
2-
use re_ui::ContextExt as _;
2+
use re_ui::UiExt;
33
use re_viewer_context::{UiLayout, ViewerContext};
44

55
use super::DataUi;
@@ -47,10 +47,7 @@ impl DataUi for ComponentPath {
4747
));
4848
}
4949
} else {
50-
ui.label(
51-
ui.ctx()
52-
.error_text(format!("Unknown component path: {self}")),
53-
);
50+
ui.error_label(&format!("Unknown component path: {self}"));
5451
}
5552
}
5653
}

crates/viewer/re_data_ui/src/instance_path.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use re_types::{
1010
image::ImageKind,
1111
static_assert_struct_has_fields, Archetype, ComponentName, Loggable,
1212
};
13-
use re_ui::{ContextExt as _, UiExt as _};
13+
use re_ui::UiExt as _;
1414
use re_viewer_context::{
1515
gpu_bridge::image_data_range_heuristic, ColormapWithRange, HoverHighlight, ImageInfo,
1616
ImageStatsCache, Item, UiLayout, ViewerContext,
@@ -46,11 +46,7 @@ impl DataUi for InstancePath {
4646
.store()
4747
.all_components_on_timeline(&query.timeline(), entity_path)
4848
} else {
49-
ui_layout.label(
50-
ui,
51-
ui.ctx()
52-
.error_text(format!("Unknown entity: {entity_path:?}")),
53-
);
49+
ui.error_label(&format!("Unknown entity: {entity_path:?}"));
5450
return;
5551
};
5652
let Some(components) = component else {

crates/viewer/re_space_view_spatial/src/ui.rs

+23-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use re_types::{
77
archetypes::Pinhole, blueprint::components::VisualBounds2D, components::ViewCoordinates,
88
image::ImageKind,
99
};
10-
use re_ui::{ContextExt as _, UiExt as _};
10+
use re_ui::UiExt as _;
1111
use re_viewer_context::{
1212
HoverHighlight, SelectionHighlight, SpaceViewHighlights, SpaceViewState, ViewerContext,
1313
};
@@ -214,11 +214,12 @@ pub fn create_labels(
214214
};
215215

216216
let font_id = egui::TextStyle::Body.resolve(parent_ui.style());
217-
let format = match label.style {
218-
UiLabelStyle::Color(color) => egui::TextFormat::simple(font_id, color),
219-
UiLabelStyle::Error => parent_ui.ctx().error_text_format(),
217+
let is_error = matches!(label.style, UiLabelStyle::Error);
218+
let text_color = match label.style {
219+
UiLabelStyle::Color(color) => color,
220+
UiLabelStyle::Error => parent_ui.style().visuals.strong_text_color(),
220221
};
221-
let text_color = format.color;
222+
let format = egui::TextFormat::simple(font_id, text_color);
222223

223224
let galley = parent_ui.fonts(|fonts| {
224225
fonts.layout_job({
@@ -249,7 +250,13 @@ pub fn create_labels(
249250
.index_highlight(label.labeled_instance.instance);
250251
let background_color = match highlight.hover {
251252
HoverHighlight::None => match highlight.selection {
252-
SelectionHighlight::None => parent_ui.style().visuals.panel_fill,
253+
SelectionHighlight::None => {
254+
if is_error {
255+
parent_ui.error_label_background_color()
256+
} else {
257+
parent_ui.style().visuals.panel_fill
258+
}
259+
}
253260
SelectionHighlight::SiblingSelection => {
254261
parent_ui.style().visuals.widgets.active.bg_fill
255262
}
@@ -258,7 +265,16 @@ pub fn create_labels(
258265
HoverHighlight::Hovered => parent_ui.style().visuals.widgets.hovered.bg_fill,
259266
};
260267

261-
label_shapes.push(egui::Shape::rect_filled(bg_rect, 3.0, background_color));
268+
let rect_stroke = if is_error {
269+
egui::Stroke::new(1.0, parent_ui.style().visuals.error_fg_color)
270+
} else {
271+
egui::Stroke::NONE
272+
};
273+
274+
label_shapes.push(
275+
egui::epaint::RectShape::new(bg_rect.expand(4.0), 4.0, background_color, rect_stroke)
276+
.into(),
277+
);
262278
label_shapes.push(egui::Shape::galley(
263279
text_rect.center_top(),
264280
galley,

crates/viewer/re_space_view_tensor/src/space_view_class.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use re_types::{
1313
datatypes::{TensorData, TensorDimension},
1414
SpaceViewClassIdentifier, View,
1515
};
16-
use re_ui::{list_item, ContextExt as _, UiExt as _};
16+
use re_ui::{list_item, UiExt as _};
1717
use re_viewer_context::{
1818
gpu_bridge, ApplicableEntities, ColormapWithRange, IdentifiedViewSystem as _,
1919
IndicatedEntities, PerVisualizer, SpaceViewClass, SpaceViewClassRegistryError, SpaceViewId,
@@ -282,7 +282,7 @@ impl TensorSpaceView {
282282
if let Err(err) =
283283
self.tensor_slice_ui(ctx, ui, state, view_id, dimension_labels, &slice_selection)
284284
{
285-
ui.label(ui.ctx().error_text(err.to_string()));
285+
ui.error_label(&err.to_string());
286286
}
287287
});
288288

crates/viewer/re_ui/examples/re_ui_example/main.rs

+13-8
Original file line numberDiff line numberDiff line change
@@ -457,14 +457,19 @@ impl egui_tiles::Behavior<Tab> for MyTileTreeBehavior {
457457
_tile_id: egui_tiles::TileId,
458458
_pane: &mut Tab,
459459
) -> egui_tiles::UiResponse {
460-
egui::warn_if_debug_build(ui);
461-
ui.label("Hover me for a tooltip")
462-
.on_hover_text("This is a tooltip");
463-
464-
ui.label(
465-
egui::RichText::new("Welcome to the ReUi example")
466-
.text_style(DesignTokens::welcome_screen_h1()),
467-
);
460+
egui::Frame::none().inner_margin(4.0).show(ui, |ui| {
461+
egui::warn_if_debug_build(ui);
462+
ui.label("Hover me for a tooltip")
463+
.on_hover_text("This is a tooltip");
464+
465+
ui.label(
466+
egui::RichText::new("Welcome to the ReUi example")
467+
.text_style(DesignTokens::welcome_screen_h1()),
468+
);
469+
470+
ui.error_label("This is an example of a long error label.");
471+
ui.warning_label("This is an example of a long warning label.");
472+
});
468473

469474
Default::default()
470475
}

crates/viewer/re_ui/src/command.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,14 @@ impl UICommand {
310310
Self::ToggleSelectionPanel => Some(ctrl_shift(Key::S)),
311311
Self::ToggleTimePanel => Some(ctrl_shift(Key::T)),
312312
Self::ToggleChunkStoreBrowser => Some(ctrl_shift(Key::D)),
313-
Self::Settings => None,
313+
Self::Settings => {
314+
if cfg!(target_os = "macos") {
315+
Some(KeyboardShortcut::new(Modifiers::MAC_CMD, Key::Comma))
316+
} else {
317+
// TODO(emilk): shortcut for web and non-mac too
318+
None
319+
}
320+
}
314321

315322
#[cfg(debug_assertions)]
316323
Self::ToggleBlueprintInspectionPanel => Some(ctrl_shift(Key::I)),

crates/viewer/re_ui/src/context_ext.rs

+9-12
Original file line numberDiff line numberDiff line change
@@ -59,35 +59,32 @@ pub trait ContextExt {
5959
// egui::Stroke::new(stroke_width, color)
6060
}
6161

62+
/// Text colored to indicate success.
6263
#[must_use]
6364
fn success_text(&self, text: impl Into<String>) -> egui::RichText {
6465
egui::RichText::new(text).color(SUCCESS_COLOR)
6566
}
6667

68+
/// Text colored to indicate a warning.
69+
///
70+
/// For most cases, you should use [`crate::UiExt::warning_label`] instead,
71+
/// which has a nice fat border around it.
6772
#[must_use]
6873
fn warning_text(&self, text: impl Into<String>) -> egui::RichText {
6974
let style = self.ctx().style();
7075
egui::RichText::new(text).color(style.visuals.warn_fg_color)
7176
}
7277

73-
/// NOTE: duplicated in [`Self::error_text_format`]
78+
/// Text colored to indicate an error.
79+
///
80+
/// For most cases, you should use [`crate::UiExt::error_label`] instead,
81+
/// which has a nice fat border around it.
7482
#[must_use]
7583
fn error_text(&self, text: impl Into<String>) -> egui::RichText {
7684
let style = self.ctx().style();
7785
egui::RichText::new(text).color(style.visuals.error_fg_color)
7886
}
7987

80-
/// NOTE: duplicated in [`Self::error_text`]
81-
fn error_text_format(&self) -> egui::TextFormat {
82-
let style = self.ctx().style();
83-
let font_id = egui::TextStyle::Body.resolve(&style);
84-
egui::TextFormat {
85-
font_id,
86-
color: style.visuals.error_fg_color,
87-
..Default::default()
88-
}
89-
}
90-
9188
fn top_bar_style(&self, style_like_web: bool) -> TopBarStyle {
9289
let egui_zoom_factor = self.ctx().zoom_factor();
9390
let fullscreen = self

crates/viewer/re_ui/src/design_tokens.rs

+3
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ impl DesignTokens {
242242

243243
egui_style.visuals.image_loading_spinners = false;
244244

245+
egui_style.visuals.error_fg_color = egui::Color32::from_rgb(0xAB, 0x01, 0x16);
246+
egui_style.visuals.warn_fg_color = egui::Color32::from_rgb(0xFF, 0x7A, 0x0C);
247+
245248
ctx.set_style(egui_style);
246249
}
247250

crates/viewer/re_ui/src/toasts.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ use std::collections::HashMap;
55
use egui::Color32;
66

77
pub const INFO_COLOR: Color32 = Color32::from_rgb(0, 155, 255);
8-
pub const WARNING_COLOR: Color32 = Color32::from_rgb(255, 212, 0);
9-
pub const ERROR_COLOR: Color32 = Color32::from_rgb(255, 32, 0);
10-
pub const SUCCESS_COLOR: Color32 = Color32::from_rgb(0, 255, 32);
8+
pub const SUCCESS_COLOR: Color32 = Color32::from_rgb(0, 240, 32);
119

1210
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1311
pub enum ToastKind {
@@ -139,8 +137,8 @@ fn default_toast_contents(ui: &mut egui::Ui, toast: &Toast) -> egui::Response {
139137

140138
if toast.options.show_icon {
141139
let (icon, icon_color) = match toast.kind {
142-
ToastKind::Warning => ("⚠", WARNING_COLOR),
143-
ToastKind::Error => ("❗", ERROR_COLOR),
140+
ToastKind::Warning => ("⚠", ui.style().visuals.warn_fg_color),
141+
ToastKind::Error => ("❗", ui.style().visuals.error_fg_color),
144142
ToastKind::Success => ("✔", SUCCESS_COLOR),
145143
_ => ("ℹ", INFO_COLOR),
146144
};

0 commit comments

Comments
 (0)