Skip to content

Commit

Permalink
Nicer toast notifications (#1621)
Browse files Browse the repository at this point in the history
* Replace egui-notify with an in-house library with line wraps

* Click to close notification

* Warn about installing additional loggers without setting up re_log

* Test the new toast notification in re_ui_example

* info -> debug level for GC events

* misc cleanup

* Only show toast notifications for log messages from rerun crates

* Don't repaint too often

* Fix crash on negative ttl

* Use the correct dt

* Fix clearing of toasts

* Nicer formatting of connecting client ip
  • Loading branch information
emilk committed Mar 20, 2023
1 parent ed689ed commit 4717659
Show file tree
Hide file tree
Showing 11 changed files with 280 additions and 57 deletions.
11 changes: 1 addition & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions crates/re_arrow_store/src/store_gc.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::collections::HashMap;

use arrow2::array::{Array, ListArray};
use re_log::{info, trace};

use re_log::trace;
use re_log_types::{ComponentName, TimeRange, Timeline};

use crate::{ComponentBucket, DataStore};
Expand Down Expand Up @@ -59,7 +60,7 @@ impl DataStore {
let drop_at_least_size_bytes = initial_size_bytes * p;
let target_size_bytes = initial_size_bytes - drop_at_least_size_bytes;

info!(
re_log::debug!(
kind = "gc",
id = self.gc_id,
%target,
Expand All @@ -86,7 +87,7 @@ impl DataStore {
let new_nb_rows = self.total_temporal_component_rows();
let new_size_bytes = self.total_temporal_component_size_bytes() as f64;

info!(
re_log::debug!(
kind = "gc",
id = self.gc_id,
%target,
Expand Down
7 changes: 7 additions & 0 deletions crates/re_log/src/channel_logger.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
//! Capture log messages and send them to some receiver over a channel.

pub struct LogMsg {
/// The verbosity level.
pub level: log::Level,

/// The module, starting with the crate name.
pub target: String,

/// The contents of the log message.
pub msg: String,
}

Expand Down Expand Up @@ -38,6 +44,7 @@ impl log::Log for ChannelLogger {
.lock()
.send(LogMsg {
level: record.level(),
target: record.target().to_owned(),
msg: record.args().to_string(),
})
.ok();
Expand Down
9 changes: 9 additions & 0 deletions crates/re_log/src/multi_logger.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
//! Have multiple loggers implementing [`log::Log`] at once.

use std::sync::atomic::{AtomicBool, Ordering::SeqCst};

static MULTI_LOGGER: MultiLogger = MultiLogger::new();

static HAS_MULTI_LOGGER: AtomicBool = AtomicBool::new(false);

/// Install the multi-logger as the default logger.
pub fn init() -> Result<(), log::SetLoggerError> {
HAS_MULTI_LOGGER.store(true, SeqCst);
log::set_logger(&MULTI_LOGGER)
}

Expand All @@ -14,6 +19,10 @@ pub fn add_boxed_logger(logger: Box<dyn log::Log>) {

/// Install an additional global logger.
pub fn add_logger(logger: &'static dyn log::Log) {
debug_assert!(
HAS_MULTI_LOGGER.load(SeqCst),
"You forgot to setup multi-logging"
);
MULTI_LOGGER.loggers.write().push(logger);
}

Expand Down
9 changes: 6 additions & 3 deletions crates/re_sdk_comms/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub fn serve(port: u16, options: ServerOptions) -> anyhow::Result<Receiver<LogMs
let bind_addr = format!("0.0.0.0:{port}");

let listener = std::net::TcpListener::bind(&bind_addr)
.with_context(|| format!("Failed to bind address {bind_addr:?}"))?;
.with_context(|| format!("Failed to bind TCP address {bind_addr:?} for our WS server."))?;

let (tx, rx) = re_smart_channel::smart_channel(re_smart_channel::Source::TcpServer { port });

Expand Down Expand Up @@ -79,10 +79,13 @@ fn spawn_client(stream: std::net::TcpStream, tx: Sender<LogMsg>, options: Server
stream.peer_addr()
))
.spawn(move || {
let addr_string = stream
.peer_addr()
.map_or_else(|_| "(unknown ip)".to_owned(), |addr| addr.to_string());
if options.quiet {
re_log::debug!("New SDK client connected: {:?}", stream.peer_addr());
re_log::debug!("New SDK client connected: {addr_string}");
} else {
re_log::info!("New SDK client connected: {:?}", stream.peer_addr());
re_log::info!("New SDK client connected: {addr_string}");
}

if let Err(err) = run_client(stream, &tx, options) {
Expand Down
1 change: 1 addition & 0 deletions crates/re_ui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ egui_dock = { workspace = true, optional = true, features = ["serde"] }

[dev-dependencies]
eframe = { workspace = true, default-features = false, features = ["wgpu"] }
re_log.workspace = true
91 changes: 73 additions & 18 deletions crates/re_ui/examples/re_ui_example.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use re_ui::{Command, CommandPalette};
use re_ui::{toasts, Command, CommandPalette};

fn main() -> eframe::Result<()> {
re_log::setup_native_logging();

let native_options = eframe::NativeOptions {
initial_window_size: Some([1200.0, 800.0].into()),
follow_system_theme: false,
Expand All @@ -17,34 +19,22 @@ fn main() -> eframe::Result<()> {
..Default::default()
};

let tree = egui_dock::Tree::new(vec![1, 2, 3]);

eframe::run_native(
"re_ui example app",
native_options,
Box::new(move |cc| {
let re_ui = re_ui::ReUi::load_and_apply(&cc.egui_ctx);
Box::new(ExampleApp {
re_ui,

tree,

left_panel: true,
right_panel: true,
bottom_panel: true,

dummy_bool: true,

cmd_palette: CommandPalette::default(),
pending_commands: Default::default(),
latest_cmd: Default::default(),
})
Box::new(ExampleApp::new(re_ui))
}),
)
}

pub struct ExampleApp {
re_ui: re_ui::ReUi,
toasts: toasts::Toasts,

/// Listens to the local text log stream
text_log_rx: std::sync::mpsc::Receiver<re_log::LogMsg>,

tree: egui_dock::Tree<Tab>,

Expand All @@ -61,12 +51,67 @@ pub struct ExampleApp {
latest_cmd: String,
}

impl ExampleApp {
fn new(re_ui: re_ui::ReUi) -> Self {
let (logger, text_log_rx) = re_log::ChannelLogger::new(re_log::LevelFilter::Info);
re_log::add_boxed_logger(Box::new(logger));

let tree = egui_dock::Tree::new(vec![1, 2, 3]);

Self {
re_ui,
toasts: Default::default(),
text_log_rx,

tree,

left_panel: true,
right_panel: true,
bottom_panel: true,

dummy_bool: true,

cmd_palette: CommandPalette::default(),
pending_commands: Default::default(),
latest_cmd: Default::default(),
}
}

/// Show recent text log messages to the user as toast notifications.
fn show_text_logs_as_notifications(&mut self) {
while let Ok(re_log::LogMsg {
level,
target: _,
msg,
}) = self.text_log_rx.try_recv()
{
let kind = match level {
re_log::Level::Error => toasts::ToastKind::Error,
re_log::Level::Warn => toasts::ToastKind::Warning,
re_log::Level::Info => toasts::ToastKind::Info,
re_log::Level::Debug | re_log::Level::Trace => {
continue; // too spammy
}
};

self.toasts.add(toasts::Toast {
kind,
text: msg,
options: toasts::ToastOptions::with_ttl_in_seconds(4.0),
});
}
}
}

impl eframe::App for ExampleApp {
fn clear_color(&self, _visuals: &egui::Visuals) -> [f32; 4] {
[0.0; 4] // transparent so we can get rounded corners when doing [`re_ui::CUSTOM_WINDOW_DECORATIONS`]
}

fn update(&mut self, egui_ctx: &egui::Context, frame: &mut eframe::Frame) {
self.show_text_logs_as_notifications();
self.toasts.show(egui_ctx);

egui::gui_zoom::zoom_with_keyboard_shortcuts(
egui_ctx,
frame.info().native_pixels_per_point,
Expand Down Expand Up @@ -97,6 +142,16 @@ impl eframe::App for ExampleApp {
ui.horizontal_centered(|ui| {
ui.strong("Left bar");
});

if ui.button("Log info").clicked() {
re_log::info!("A lot of text on info level.\nA lot of text in fact. So much that we should ideally be auto-wrapping it at some point, much earlier than this.");
}
if ui.button("Log warn").clicked() {
re_log::warn!("A lot of text on warn level.\nA lot of text in fact. So much that we should ideally be auto-wrapping it at some point, much earlier than this.");
}
if ui.button("Log error").clicked() {
re_log::error!("A lot of text on error level.\nA lot of text in fact. So much that we should ideally be auto-wrapping it at some point, much earlier than this.");
}
});

egui::ScrollArea::both()
Expand Down
1 change: 1 addition & 0 deletions crates/re_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod command_palette;
mod design_tokens;
pub mod icons;
mod static_image_cache;
pub mod toasts;
mod toggle_switch;

pub use command::Command;
Expand Down

0 comments on commit 4717659

Please sign in to comment.