diff --git a/Cargo.lock b/Cargo.lock index bc4a4d2..0c6791e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -812,6 +812,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ "bitflags 2.13.0", + "block2 0.6.2", + "libc", "objc2 0.6.4", ] @@ -1647,6 +1649,7 @@ dependencies = [ "dirs", "iced", "image", + "rfd", ] [[package]] @@ -2719,6 +2722,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + [[package]] name = "portable-atomic" version = "1.13.1" @@ -3019,6 +3028,33 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "rfd" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20dafead71c16a34e1ff357ddefc8afc11e7d51d6d2b9fbd07eaa48e3e540220" +dependencies = [ + "block2 0.6.2", + "dispatch2", + "js-sys", + "libc", + "log", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "percent-encoding", + "pollster", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "web-sys", + "windows-sys 0.61.2", +] + [[package]] name = "rgb" version = "0.8.53" diff --git a/Cargo.toml b/Cargo.toml index 5e76228..668d6f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2024" iced = { version = "0.14.0", features = [ "image" ] } dirs = "6.0.0" image = "0.25.10" +rfd = "0.17.2" diff --git a/src/main.rs b/src/main.rs index 2e6973d..604f98b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,14 @@ -use std::env; +use std::{env, ffi::OsStr, fs}; use iced::{ Color, Element, Renderer, Subscription, Task, Theme, color, event, keyboard::{self, Key, key}, theme, widget, window, }; -use image::{EncodableLayout, ImageReader, Pixel, Rgba, RgbaImage, imageops}; +use image::{ + EncodableLayout, ImageReader, Pixel, Rgba, RgbaImage, codecs::webp::WebPEncoder, imageops, +}; +use rfd::FileDialog; #[derive(Clone, Debug)] enum Message { @@ -85,6 +88,35 @@ impl State { return self.allocate_image(); } + Key::Character("s") => { + let Some(path) = FileDialog::new().save_file() else { + eprintln!("no path to save provided"); + return Task::none(); + }; + + let Ok(file) = fs::File::create(&path) + .inspect_err(|e| eprintln!("failed to create file: {e}")) + else { + return Task::none(); + }; + + let maybe_encoder = + match path.extension().map(OsStr::to_string_lossy).as_deref() { + Some("webp") => Some(WebPEncoder::new_lossless(file)), + + None | Some(_) => None, + }; + + let result = if let Some(encoder) = maybe_encoder { + self.image.write_with_encoder(encoder) + } else { + self.image.save(path) + }; + + #[allow(unused_must_use)] + result.inspect_err(|e| eprintln!("failed to save image: {e}")); + } + // ignore unused keys _ => {} },