diff --git a/src/appstate.rs b/src/appstate.rs index e081e9fc..95bc0703 100644 --- a/src/appstate.rs +++ b/src/appstate.rs @@ -20,6 +20,25 @@ pub struct ImageGeometry { pub offset: Vector2, } +#[derive(Debug, Clone)] +pub enum Message { + Info(String), + Warning(String), + Error(String), +} + +impl Message { + pub fn info(m: &str) -> Self { + Self::Info(m.into()) + } + pub fn warn(m: &str) -> Self { + Self::Warning(m.into()) + } + pub fn err(m: &str) -> Self { + Self::Error(m.into()) + } +} + /// The state of the application #[derive(Debug, AppState)] pub struct OculanteState { @@ -27,7 +46,7 @@ pub struct OculanteState { pub compare_list: HashMap, pub drag_enabled: bool, pub reset_image: bool, - pub message: Option, + pub message: Option, /// Is the image fully loaded? pub is_loaded: bool, pub window_size: Vector2, @@ -37,7 +56,7 @@ pub struct OculanteState { pub sampled_color: [f32; 4], pub mouse_delta: Vector2, pub texture_channel: (Sender, Receiver), - pub message_channel: (Sender, Receiver), + pub message_channel: (Sender, Receiver), pub extended_info_channel: (Sender, Receiver), pub extended_info_loading: bool, /// The Player, responsible for loading and sending Frames @@ -68,7 +87,11 @@ pub struct OculanteState { impl OculanteState { pub fn send_message(&self, msg: &str) { - _ = self.message_channel.0.send(msg.into()); + _ = self.message_channel.0.send(Message::info(msg)); + } + + pub fn send_message_err(&self, msg: &str) { + _ = self.message_channel.0.send(Message::err(msg)); } } diff --git a/src/main.rs b/src/main.rs index 067d722f..e3b9ff29 100644 --- a/src/main.rs +++ b/src/main.rs @@ -222,7 +222,7 @@ fn init(gfx: &mut Graphics, plugins: &mut Plugins) -> OculanteState { if let Some(port) = matches.value_of("l") { match port.parse::() { Ok(p) => { - state.message = Some(format!("Listening on {p}")); + state.message = Some(Message::info(&format!("Listening on {p}"))); recv(p, state.texture_channel.0.clone()); state.current_path = Some(PathBuf::from(&format!("network port {p}"))); state.network_mode = true; @@ -533,9 +533,8 @@ fn event(app: &mut App, state: &mut OculanteState, evt: Event) { state.current_image = None; state.player.load(&p, state.message_channel.0.clone()); state.current_path = Some(p); - } else { - state.message = Some("Unsupported image".into()); + state.message = Some(Message::warn("Unsupported image!")); } } } @@ -591,8 +590,6 @@ fn update(app: &mut App, state: &mut OculanteState) { ); } - - // make sure that in edit mode, RGBA is set. // This is a bit lazy. but instead of writing lots of stuff for an ubscure feature, // let's disable it here. @@ -620,12 +617,18 @@ fn update(app: &mut App, state: &mut OculanteState) { // check if a new message has been sent if let Ok(msg) = state.message_channel.1.try_recv() { - debug!("Received message: {msg}"); - if msg.to_lowercase().contains("error") || msg.to_lowercase().contains("unsupported") { - if state.current_image.is_none() { - state.current_path = None; + debug!("Received message: {:?}", msg); + match msg { + Message::Info(_) => {} + Message::Warning(_) => {} + Message::Error(_) => { + if state.current_image.is_none() { + state.current_path = None; + state.is_loaded = false; + } } } + state.message = Some(msg); } } @@ -907,22 +910,32 @@ fn drawe(app: &mut App, gfx: &mut Graphics, plugins: &mut Plugins, state: &mut O ctx, state.message.is_some(), |ui| { - ui.ctx().request_repaint(); - ui.horizontal(|ui| { - ui.label(format!("💬 {message}")); + match message { + Message::Info(txt) => { + ui.label(format!("💬 {txt}")); + } + Message::Warning(txt) => { + ui.colored_label(Color32::GOLD, format!("⚠ {txt}")); + } + Message::Error(txt) => { + ui.colored_label(Color32::DARK_RED, format!("🕱 {txt}")); + } + } ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| { if ui.small_button("🗙").clicked() { state.message = None } }); }); + + ui.ctx().request_repaint(); }, ); let max_anim_len = if state.persistent_settings.vsync { - 3.5 + 2.5 } else { - 70. + 50. }; // using delta does not work with rfd @@ -994,7 +1007,11 @@ fn drawe(app: &mut App, gfx: &mut Graphics, plugins: &mut Plugins, state: &mut O // } let c = state.persistent_settings.background_color; // draw.clear(Color:: from_bytes(c[0], c[1], c[2], 255)); - draw.clear(Color::from_rgb(c[0] as f32 / 255., c[1] as f32 / 255., c[1] as f32 / 255.)); + draw.clear(Color::from_rgb( + c[0] as f32 / 255., + c[1] as f32 / 255., + c[1] as f32 / 255., + )); gfx.render(&draw); gfx.render(&egui_output); if egui_output.needs_repaint() { diff --git a/src/ui.rs b/src/ui.rs index 05f3c075..c5c0044c 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1118,7 +1118,7 @@ pub fn edit_ui(app: &mut App, ctx: &Context, state: &mut OculanteState, gfx: &mu set_title(app, state); } Err(e) => { - state.send_message(&format!("Error: Could not save: {e}")); + state.send_message_err(&format!("Error: Could not save: {e}")); } } state.toast_cooldown = 0.0; @@ -1858,7 +1858,7 @@ pub fn main_menu(ui: &mut Ui, state: &mut OculanteState, app: &mut App, gfx: &mu state.send_message("Image pasted"); } } else { - state.send_message("Clipboard did not contain image") + state.send_message_err("Clipboard did not contain image") } } ui.close_menu(); diff --git a/src/update.rs b/src/update.rs index 5cc4742b..d7dcde4c 100644 --- a/src/update.rs +++ b/src/update.rs @@ -1,6 +1,8 @@ use self_update::cargo_crate_version; use std::{sync::mpsc::Sender, thread}; +use crate::appstate::Message; + fn gh_update() -> Result> { #[cfg(not(target_os = "linux"))] let target = ""; @@ -22,13 +24,13 @@ fn gh_update() -> Result> { Ok(format!("{status:?}")) } -pub fn update(sender: Option>) { +pub fn update(sender: Option>) { thread::spawn(move || match gh_update() { Ok(res) => { - _ = sender.map(|s| s.send(res)); + _ = sender.map(|s| s.send(Message::Info(res))); } Err(e) => { - _ = sender.map(|s| s.send(format!("{e:?}"))); + _ = sender.map(|s| s.send(Message::Error(format!("{e:?}")))); } }); } diff --git a/src/utils.rs b/src/utils.rs index 10bdadec..046be9fe 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -24,7 +24,7 @@ use std::sync::mpsc::{Receiver, Sender}; use strum::Display; use strum_macros::EnumIter; -use crate::appstate::{ImageGeometry, OculanteState}; +use crate::appstate::{ImageGeometry, OculanteState, Message}; use crate::cache::Cache; use crate::image_editing::{self, ImageOperation}; use crate::image_loader::open_image; @@ -147,7 +147,7 @@ impl Player { } } - pub fn load(&mut self, img_location: &Path, message_sender: Sender) { + pub fn load(&mut self, img_location: &Path, message_sender: Sender) { debug!("Stopping player on load"); self.stop(); let (stop_sender, stop_receiver): (Sender<()>, Receiver<()>) = mpsc::channel(); @@ -176,7 +176,7 @@ impl Player { pub fn send_image_threaded( img_location: &Path, texture_sender: Sender, - message_sender: Sender, + message_sender: Sender, stop_receiver: Receiver<()>, max_texture_size: u32, ) { @@ -204,7 +204,7 @@ pub fn send_image_threaded( // Check if texture is too large to fit on the texture if largest_side > max_texture_size { - _ = message_sender.send("This image exceeded the maximum resolution and will be be scaled down.".to_string()); + _ = message_sender.send(Message::warn("This image exceeded the maximum resolution and will be be scaled down.")); let scale_factor = max_texture_size as f32 / largest_side as f32; let new_dimensions = ( (f.buffer.dimensions().0 as f32 * scale_factor) @@ -268,7 +268,7 @@ pub fn send_image_threaded( } Err(e) => { error!("{e}"); - _ = message_sender.send(e.to_string()); + _ = message_sender.send(Message::Error(e.to_string())); } } }); @@ -715,10 +715,10 @@ pub fn fit(oldvalue: f32, oldmin: f32, oldmax: f32, newmin: f32, newmax: f32) -> pub fn toggle_zen_mode(state: &mut OculanteState, app: &mut App) { state.persistent_settings.zen_mode = !state.persistent_settings.zen_mode; if state.persistent_settings.zen_mode { - _ = state.message_channel.0.send(format!( + _ = state.message_channel.0.send(Message::Info(format!( "Zen mode on. Press '{}' to toggle.", lookup(&state.persistent_settings.shortcuts, &InputEvent::ZenMode) - )); + ))); } set_title(app, state); }