diff --git a/src/main.rs b/src/main.rs index d2e411c..dd94f79 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,24 +19,11 @@ mod utils; #[derive(Clone, Debug)] enum Message { - Open, - Rotate, - Invert, - Filter, - Bar, - Save, - Quit, - - Yank, - Put, - - FocusNext, - FocusPrevious, - - ImagePicked(Result>), + ImagePicked(Result), ImageDisplayReady(Result), - SavePathPicked(Result>), + SavePathPicked(Result), + KeyPressed(keyboard::Key, keyboard::Modifiers), FileDropped(PathBuf), } @@ -46,7 +33,7 @@ struct State { image_display: Option, image_filter: widget::image::FilterMethod, - error: Option>, + error: Option, info: Option, infobar_shown: bool, @@ -58,7 +45,7 @@ impl State { if let Some(path) = env::args().nth(1) { match state.load_image(path) { - Err(e) => state.error = Some(e.into()), + Err(e) => state.error = Some(e), Ok(task) => return (state, task), } } @@ -108,122 +95,129 @@ impl State { } fn update(&mut self, message: Message) -> Task { match message { - Message::ImagePicked(result) => { - match result.and_then(|path| self.load_image(path).map_err(Cow::from)) { - Err(e) => self.error = Some(e.into()), - Ok(task) => { - self.error = None; - return task; - } + Message::ImagePicked(result) => match result.and_then(|path| self.load_image(path)) { + Err(e) => self.error = Some(e), + Ok(task) => { + self.error = None; + return task; } - } + }, Message::ImageDisplayReady(result) => { self.image_display = Some(result.unwrap()); } Message::SavePathPicked(result) => { self.error = result - .map_err(Cow::from) .and_then(|path| { self.image.as_ref().map_or_else( || Err("no image to save".into()), - |image| image.save(path).map_err(|e| e.to_string().into()), + |image| image.save(path).map_err(|e| e.to_string()), ) }) .err(); } - Message::FocusNext => return widget::operation::focus_next(), - Message::FocusPrevious => return widget::operation::focus_previous(), - - Message::Open => { - return Task::perform(utils::pick_image(), Message::ImagePicked); - } - - Message::Rotate => match self.image.as_ref() { - None => self.error = Some("no image to rotate".into()), - Some(image) => { - self.error = None; - self.image = Some(imageops::rotate90(image)); - return self.allocate_image(); + Message::KeyPressed(key, modifiers) => match key.as_ref() { + // input field cycling + Key::Named(key::Named::Tab) => { + if modifiers.shift() { + return widget::operation::focus_previous(); + } + return widget::operation::focus_next(); } - }, - Message::Invert => match self.image.as_mut() { - None => self.error = Some("no image to invert".into()), - Some(image) => { - self.error = None; - imageops::invert(image); - return self.allocate_image(); - } - }, - Message::Filter => { - self.image_filter = match self.image_filter { - widget::image::FilterMethod::Linear => widget::image::FilterMethod::Nearest, - widget::image::FilterMethod::Nearest => widget::image::FilterMethod::Linear, - } - } - Message::Bar => { - self.infobar_shown = !self.infobar_shown; - } - Message::Save => { - if self.image.is_some() { - self.error = None; - return Task::perform(utils::pick_save_path(), Message::SavePathPicked); + Key::Character("o") => { + return Task::perform(utils::pick_image(), Message::ImagePicked); } - self.error = Some("no image to save".into()); - } - Message::Quit => return window::latest().and_then(window::close), + Key::Character("r") => match self.image.as_ref() { + None => self.error = Some("no image to rotate".into()), + Some(image) => { + self.error = None; + self.image = Some(imageops::rotate90(image)); + return self.allocate_image(); + } + }, + Key::Character("i") => match self.image.as_mut() { + None => self.error = Some("no image to invert".into()), + Some(image) => { + self.error = None; + imageops::invert(image); + return self.allocate_image(); + } + }, + Key::Character("f") => { + self.image_filter = match self.image_filter { + widget::image::FilterMethod::Linear => widget::image::FilterMethod::Nearest, + widget::image::FilterMethod::Nearest => widget::image::FilterMethod::Linear, + } + } + Key::Character("b") => { + self.infobar_shown = !self.infobar_shown; + } - Message::Yank => { - if let Some(image) = self.image.as_ref() { + Key::Character("s") => { + if self.image.is_some() { + self.error = None; + return Task::perform(utils::pick_save_path(), Message::SavePathPicked); + } + self.error = Some("no image to save".into()); + } + + Key::Character("q") => return window::latest().and_then(window::close), + + Key::Character("y") | Key::Character("c") => { + if let Some(image) = self.image.as_ref() { + match match self.clipboard.take() { + Some(c) => Ok(c), + None => Clipboard::new(), + } { + Ok(mut clipboard) => { + self.error = clipboard + .set_image(utils::arboard_from_rgbaimage(image)) + .map_err(|e| e.to_string()) + .err(); + self.clipboard = Some(clipboard); + } + Err(e) => self.error = Some(e.to_string()), + } + } else { + self.error = Some("no image to yank".into()); + } + } + Key::Character("p") | Key::Character("v") => { match match self.clipboard.take() { Some(c) => Ok(c), None => Clipboard::new(), } { Ok(mut clipboard) => { - self.error = clipboard - .set_image(utils::arboard_from_rgbaimage(image)) - .map_err(|e| e.to_string().into()) - .err(); + let result = clipboard.get_image().map(utils::rgbaimage_from_arboard); + + self.error = result.as_ref().map_err(|e| e.to_string()).err(); self.clipboard = Some(clipboard); - } - Err(e) => self.error = Some(e.to_string().into()), - } - } else { - self.error = Some("no image to yank".into()); - } - } - Message::Put => { - match match self.clipboard.take() { - Some(c) => Ok(c), - None => Clipboard::new(), - } { - Ok(mut clipboard) => { - let result = clipboard.get_image().map(utils::rgbaimage_from_arboard); - self.error = result.as_ref().map_err(|e| e.to_string().into()).err(); - self.clipboard = Some(clipboard); - - if let Ok(image) = result { - self.info = Some(format!("{}x{}", image.width(), image.height(),)); - if image.dimensions() < (100, 100) { - self.image_filter = widget::image::FilterMethod::Nearest; + if let Ok(image) = result { + self.info = Some(format!("{}x{}", image.width(), image.height(),)); + if image.dimensions() < (100, 100) { + self.image_filter = widget::image::FilterMethod::Nearest; + } + self.image = Some(image); + return self.allocate_image(); } - self.image = Some(image); - return self.allocate_image(); } + Err(e) => self.error = Some(e.to_string()), } - Err(e) => self.error = Some(e.to_string().into()), } - } + + // ignore unused keys + _ => {} + }, Message::FileDropped(path) => match self.load_image(path) { Ok(task) => { self.error = None; return task; } - Err(e) => self.error = Some(e.into()), + Err(e) => self.error = Some(e), }, } @@ -284,26 +278,7 @@ impl State { fn subscription(&self) -> Subscription { event::listen().filter_map(|event| match event { iced::Event::Keyboard(keyboard::Event::KeyPressed { key, modifiers, .. }) => { - match key.as_ref() { - Key::Named(key::Named::Tab) => Some(if modifiers.shift() { - Message::FocusPrevious - } else { - Message::FocusNext - }), - - Key::Character("o") => Some(Message::Open), - Key::Character("r") => Some(Message::Rotate), - Key::Character("i") => Some(Message::Invert), - Key::Character("f") => Some(Message::Filter), - Key::Character("b") => Some(Message::Bar), - Key::Character("s") => Some(Message::Save), - Key::Character("q") => Some(Message::Quit), - - Key::Character("y") | Key::Character("c") => Some(Message::Yank), - Key::Character("p") | Key::Character("v") => Some(Message::Put), - - _ => None, - } + Some(Message::KeyPressed(key, modifiers)) } iced::Event::Window(window::Event::FileDropped(path)) => { Some(Message::FileDropped(path)) diff --git a/src/utils.rs b/src/utils.rs index 8c1d427..f1684f7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -34,7 +34,7 @@ fn load_image_impl(path: impl AsRef) -> ImageResult { Ok(decoded_image.into_rgba8()) } -pub async fn pick_image() -> Result> { +pub async fn pick_image() -> Result { let Some(filehandle) = AsyncFileDialog::new() .add_filter( "image", @@ -52,7 +52,7 @@ pub async fn pick_image() -> Result> { Ok(filehandle.path().to_owned()) } -pub async fn pick_save_path() -> Result> { +pub async fn pick_save_path() -> Result { let Some(filehandle) = AsyncFileDialog::new().save_file().await else { return Err("no path to save provided".into()); };