diff --git a/locales/en-US/main.ftl b/locales/en-US/main.ftl index 2828030..92429bc 100644 --- a/locales/en-US/main.ftl +++ b/locales/en-US/main.ftl @@ -39,3 +39,7 @@ show-layer-manager = Show Layer Manager frame-timestep = Frame Timestep specctra-session-file = Specctra session file + +title-error-messages = Error Messages +reset-error-messages = Reset Messages +discard-item = Discard diff --git a/src/bin/topola-egui/app.rs b/src/bin/topola-egui/app.rs index fc451cf..72c0567 100644 --- a/src/bin/topola-egui/app.rs +++ b/src/bin/topola-egui/app.rs @@ -40,6 +40,7 @@ use topola::{ use crate::{ activity::{ActivityStatus, ActivityWithStatus}, bottom::Bottom, + error_dialog::ErrorDialog, file_receiver::FileReceiver, layers::Layers, overlay::Overlay, @@ -79,6 +80,9 @@ pub struct App { #[serde(skip)] bottom: Bottom, + #[serde(skip)] + error_dialog: ErrorDialog, + #[serde(skip)] maybe_layers: Option, @@ -101,6 +105,7 @@ impl Default for App { viewport: Viewport::new(), top: Top::new(), bottom: Bottom::new(), + error_dialog: ErrorDialog::new(), maybe_layers: None, maybe_design: None, update_counter: 0.0, @@ -206,6 +211,9 @@ impl eframe::App for App { } } + self.error_dialog + .update(ctx, &self.translator, &self.viewport); + let viewport_rect = self.viewport.update( ctx, &self.top, diff --git a/src/bin/topola-egui/error_dialog.rs b/src/bin/topola-egui/error_dialog.rs new file mode 100644 index 0000000..a75fd18 --- /dev/null +++ b/src/bin/topola-egui/error_dialog.rs @@ -0,0 +1,90 @@ +//! dialog for error messages (e.g. for displaying file parser errors) + +use std::collections::BTreeSet; +use std::sync::Arc; + +use crate::{translator::Translator, viewport::Viewport}; + +pub struct ErrorDialog { + pub messages: Vec<(&'static str, String)>, + pub window_open: bool, +} + +impl ErrorDialog { + pub fn new() -> Self { + Self { + messages: Vec::new(), + window_open: false, + } + } + + // this is a separate method in order to intercept error messages + // and also print them to the console. + pub fn push_error(&mut self, component_id: &'static str, message: String) { + // note that the message here is already localized, and perhaps less useful. + log::info!("{}: {}", component_id, &message); + self.messages.push((component_id, message)); + self.window_open = true; + } + + pub fn update(&mut self, ctx: &egui::Context, tr: &Translator, viewport: &Viewport) { + let mut messages_cleared = false; + egui::Window::new(tr.text("title-error-messages")) + .id("error-messages-dialog".into()) + .open(&mut self.window_open) + .scroll(true) + .show(ctx, |ui| { + if ui.button(tr.text("reset-error-messages")).clicked() { + self.messages.clear(); + messages_cleared = true; + } + + egui::Grid::new("error-messages-grid").show(ui, |ui| { + let mut messages_to_discard = BTreeSet::::new(); + let style = Arc::clone(ui.style()); + for (msg_id, msg) in self.messages.iter().enumerate() { + use egui::style::{FontSelection, TextStyle}; + use egui::text::{LayoutJob, TextWrapping}; + use egui::widget_text::{RichText, WidgetText}; + use egui::{Align, FontFamily, FontId, TextFormat}; + + let mut loj = LayoutJob::default(); + loj.break_on_newline = true; + loj.wrap.max_width = 200.0; + RichText::new(&(tr.text(msg.0) + ": ")) + .strong() + .color(style.visuals.text_color()) + .append_to(&mut loj, &style, FontSelection::Default, Align::Min); + RichText::new(&msg.1) + .color(style.visuals.text_color()) + .append_to( + &mut loj, + &style, + FontSelection::Style(TextStyle::Monospace), + Align::Min, + ); + + // TODO: perhaps alternatively, use small icon instead? + // (provide alt text in that case!) + if ui.add(egui::Button::new(tr.text("discard-item"))).clicked() { + messages_to_discard.insert(msg_id); + } + ui.label(WidgetText::LayoutJob(loj)); + ui.end_row(); + } + if !messages_to_discard.is_empty() { + let mut count = 0; + self.messages.retain(|_| { + let ret = !messages_to_discard.contains(&count); + count += 1; + ret + }); + } + }); + }); + + if messages_cleared { + self.window_open = false; + } + } +} diff --git a/src/bin/topola-egui/main.rs b/src/bin/topola-egui/main.rs index f7bfbdb..c2d61bc 100644 --- a/src/bin/topola-egui/main.rs +++ b/src/bin/topola-egui/main.rs @@ -4,6 +4,7 @@ mod action; mod activity; mod app; mod bottom; +mod error_dialog; mod file_receiver; mod file_sender; mod layers;