Added info, warn, and error toasts

This commit is contained in:
2024-11-20 02:29:54 +02:00
parent fda77f6981
commit a5090e7251
15 changed files with 659 additions and 8 deletions

View File

@@ -6,6 +6,7 @@ pub mod left_nav;
pub mod song_list;
pub mod top_nav;
pub mod player;
pub mod toast;
pub trait CompUi {
fn draw(ui: &mut egui::Ui, state: &mut GuiState) -> crate::Result<()>;

View File

@@ -0,0 +1,107 @@
use std::{collections::VecDeque, time::SystemTime};
use egui::{epaint::Shadow, load::TexturePoll, Align2, Color32, Frame, Image, Margin, Pos2, Rect, RichText, Rounding, Stroke, Style, Vec2};
use super::{CompGetter, CompUi};
#[derive(Debug, Default, PartialEq, Clone, Copy)]
pub enum ToastType {
#[default]
Info,
Warn,
Error,
}
#[derive(Debug, Default)]
pub struct Toast {
queue: VecDeque<(String, String, ToastType, SystemTime)>
}
component_register!(Toast);
impl CompUi for Toast {
fn draw(ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> {
let screen_size = ui.ctx().screen_rect().size();
let (w, h) = (300.0, 100.0);
let theme = &xmpd_settings::Settings::get()?.theme;
let mut toastw = Toast::get()?;
let mut height_iter = 6.0;
let mut to_remove = Vec::new();
for (i, (title, description, toast_type, shown_since)) in toastw.queue.iter().enumerate() {
let area = egui::Area::new(egui::Id::new(format!("toast_{i}")))
.fixed_pos(Pos2::new(screen_size.x - w, height_iter))
.pivot(Align2::LEFT_TOP)
.show(ui.ctx(), |ui| {
ui.set_width(w);
let img;
let color;
match toast_type {
ToastType::Info => {
color = theme.accent_color;
img = Image::new(crate::data::INFO_ICON)
.max_size(Vec2::new(16.0, 16.0))
.tint(color);
}
ToastType::Warn => {
color = crate::data::C_WARN;
img = Image::new(crate::data::WARN_ICON)
.max_size(Vec2::new(16.0, 16.0))
.tint(color);
}
ToastType::Error => {
color = Color32::LIGHT_RED;
img = Image::new(crate::data::ERROR_ICON)
.max_size(Vec2::new(16.0, 16.0))
.tint(color);
}
}
Frame::none()
.stroke(Stroke::new(1.0, color))
.fill(theme.primary_bg_color)
.rounding(Rounding::same(3.0))
.inner_margin(Margin::same(3.0))
.show(ui, |ui| {
ui.set_width(w-9.0);
ui.style_mut().visuals.override_text_color = Some(theme.text_color);
ui.horizontal(|ui| {
ui.add(img);
ui.label(RichText::new(title));
});
ui.label(
RichText::new(description)
.size(10.0)
);
ui.shrink_height_to_current();
// height_iter += ui.available_height();
}
)
}
);
height_iter += area.response.rect.height() + 6.0;
// if shown for longer than 5 seconds remove it
if SystemTime::now().duration_since(*shown_since)?.as_secs() > 5 {
to_remove.push(i);
}
}
for idx in to_remove {
toastw.queue.remove(idx);
}
Ok(())
}
}
impl Toast {
pub fn show_toast(&mut self, title: &str, description: &str, toast_type: ToastType) {
self.queue.push_front((title.to_string(), description.to_string(), toast_type, SystemTime::now()));
}
}

View File

@@ -28,6 +28,12 @@ impl CompUi for TopNav {
ui.ctx().open_url(egui::OpenUrl::new_tab("https://git.mcorangehq.xyz/XOR64/music"));
ui.close_menu();
}
#[cfg(debug_assertions)]
if ui.button("Debug").clicked() {
state.windows.toggle(&WindowId::Debug, true);
ui.close_menu();
}
});

View File

@@ -8,4 +8,10 @@ pub const PLAY_ICON: egui::ImageSource = egui::include_image!("../../assets/pla
pub const PAUSE_ICON: egui::ImageSource = egui::include_image!("../../assets/pause.svg");
pub const CHECK_ICON: egui::ImageSource = egui::include_image!("../../assets/check.svg");
pub const DL_ICON: egui::ImageSource = egui::include_image!("../../assets/download.svg");
pub const INFO_ICON: egui::ImageSource = egui::include_image!("../../assets/info.svg");
pub const WARN_ICON: egui::ImageSource = egui::include_image!("../../assets/warning.svg");
pub const ERROR_ICON: egui::ImageSource = egui::include_image!("../../assets/error.svg");
pub const C_WARN: egui::Color32 = egui::Color32::from_rgb(255, 183, 0); // #ffb700

View File

@@ -16,7 +16,7 @@ const W_NAME: &str = "xmpd v2.0.0a";
type Result<T> = anyhow::Result<T>;
pub fn start() -> Result<()> {
xmpd_cache::Cache::get()?.init();
xmpd_cache::Cache::get()?.init()?;
let options = eframe::NativeOptions::default();
let mut state = GuiState::new()?;
let res = eframe::run_simple_native(W_NAME, options, move |ctx, _frame| {

View File

@@ -21,7 +21,15 @@ macro_rules! handle_error_ui {
match $val {
Ok(v) => v,
Err(e) => {
log::error!("Error in ui: {e:?}");
use crate::components::CompGetter;
log::error!("Error in {}:{}: {e}", std::file!(), std::line!());
if let Ok(mut toast) = crate::components::toast::Toast::get() {
toast.show_toast(
&format!("Error in {}:{}", std::file!(), std::line!()),
&format!("{e}"),
crate::components::toast::ToastType::Error,
);
}
return;
}
}

View File

@@ -1,10 +1,8 @@
use xmpd_settings::theme::Theme;
use crate::{components::{song_list, CompUi}, GuiState};
use crate::{components::{self, song_list, CompUi}, GuiState};
pub fn draw(ctx: &egui::Context, state: &mut GuiState) -> crate::Result<()> {
// The central panel the region left after adding TopPanel's and SidePanel's
// ui.heading(format!("Songs ({})", self.manifest.get_song_count()));
let theme = xmpd_settings::Settings::get()?.theme.clone();
egui::TopBottomPanel::new(egui::panel::TopBottomSide::Top, "top_nav")
.frame(get_themed_frame(&theme))
@@ -16,6 +14,7 @@ pub fn draw(ctx: &egui::Context, state: &mut GuiState) -> crate::Result<()> {
egui::CentralPanel::default()
.frame(get_themed_frame(&theme))
.show(ctx, |ui| {
handle_error_ui!(components::toast::Toast::draw(ui, state));
let avail = ui.available_size();
ui.vertical(|ui| {
crate::utils::super_separator(ui, theme.accent_color, avail.x, 2.0);
@@ -23,7 +22,7 @@ pub fn draw(ctx: &egui::Context, state: &mut GuiState) -> crate::Result<()> {
let main_height = avail.y * 0.91;
let left_nav_width = (avail.x * 0.25).clamp(0.0, 200.0);
let song_list_width = (avail.x - left_nav_width - 35.0);
let song_list_width = avail.x - left_nav_width - 35.0;
ui.horizontal(|ui| {
ui.set_height(main_height);
ui.group(|ui| {
@@ -54,6 +53,7 @@ pub fn draw(ctx: &egui::Context, state: &mut GuiState) -> crate::Result<()> {
handle_error_ui!(crate::components::player::Player::draw(ui, state));
}
);
Ok(())
}

View File

@@ -0,0 +1,74 @@
use egui::RichText;
use crate::components::{toast::{self, ToastType}, CompGetter};
use super::Window;
#[derive(Debug, Default)]
pub struct DebugW {
toast_title: String,
toast_descr: String,
toast_type: ToastType,
}
impl Window for DebugW {
fn draw(&mut self, ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> {
ui.group(|ui| {
ui.vertical(|ui| {
ui.label(
RichText::new("DEBUG")
.heading()
);
ui.horizontal(|ui| {
{
ui.group(|ui| {
ui.vertical(|ui| {
ui.label(
RichText::new("Toast")
.heading()
);
Self::add_input_field(&mut self.toast_title, ui, "Title");
Self::add_input_field(&mut self.toast_descr, ui, "Description");
ui.horizontal(|ui| {
ui.label("Type:");
egui::ComboBox::from_id_source("debug_combo")
.selected_text(format!("{:?}", self.toast_type))
.show_ui(ui, |ui| {
ui.selectable_value(&mut self.toast_type, ToastType::Info, "Info");
ui.selectable_value(&mut self.toast_type, ToastType::Warn, "Warn");
ui.selectable_value(&mut self.toast_type, ToastType::Error, "Error");
}
);
});
if ui.button("Add").clicked() {
toast::Toast::get().unwrap().show_toast(&self.toast_title, &self.toast_descr, self.toast_type);
}
if ui.button("Throw Error").clicked() {
handle_error_ui!(Err(anyhow::anyhow!("{}: {}", self.toast_title, self.toast_descr)));
}
});
});
}
});
});
});
Ok(())
}
}
impl DebugW {
fn add_input_field(inp: &mut String, ui: &mut egui::Ui, name: &str) {
ui.horizontal(|ui|{
ui.label(format!("{name}: "));
ui.text_edit_singleline(inp);
});
}
fn add_input_field_ml(inp: &mut String, ui: &mut egui::Ui, name: &str) {
ui.horizontal(|ui|{
ui.label(format!("{name}: "));
ui.text_edit_multiline(inp);
});
}
}

View File

@@ -2,6 +2,8 @@ use std::{collections::{HashMap, HashSet}, sync::{Arc, Mutex}};
use egui::{ViewportBuilder, ViewportId};
use crate::GuiState;
#[cfg(debug_assertions)]
mod debug;
mod error;
mod settings;
@@ -18,7 +20,9 @@ pub trait Window: std::fmt::Debug + Send {
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)]
pub enum WindowId {
Settings,
Error
Error,
#[cfg(debug_assertions)]
Debug
}
#[derive(Debug, Clone)]
@@ -38,6 +42,8 @@ impl Windows {
pub fn add_all_windows(&mut self) {
self.add_new_window(WindowId::Error, "Error!", Box::<error::ErrorW>::default());
self.add_new_window(WindowId::Settings, "Settings", Box::<settings::SettingsW>::default());
#[cfg(debug_assertions)]
self.add_new_window(WindowId::Debug, "Debug", Box::<debug::DebugW>::default());
}
pub fn add_new_window(&mut self, id: WindowId, title: &str, cb: Box<dyn Window>) {