feat: usage info on start
This commit is contained in:
parent
f8ab334cd7
commit
ab8dc257e9
2 changed files with 65 additions and 46 deletions
106
src/main.rs
106
src/main.rs
|
|
@ -1,13 +1,11 @@
|
||||||
use std::{env, ffi::OsStr, fs};
|
use std::{env, ffi::OsStr, fs};
|
||||||
|
|
||||||
use iced::{
|
use iced::{
|
||||||
Color, Element, Length, Renderer, Subscription, Task, Theme, color, event,
|
Alignment, Color, Element, Length, Renderer, Subscription, Task, Theme, color, event,
|
||||||
keyboard::{self, Key, key},
|
keyboard::{self, Key, key},
|
||||||
theme, widget, window,
|
theme, widget, window,
|
||||||
};
|
};
|
||||||
use image::{
|
use image::{EncodableLayout, ImageReader, RgbaImage, codecs::webp::WebPEncoder, imageops};
|
||||||
EncodableLayout, ImageReader, Pixel, Rgba, RgbaImage, codecs::webp::WebPEncoder, imageops,
|
|
||||||
};
|
|
||||||
use rfd::FileDialog;
|
use rfd::FileDialog;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -19,32 +17,22 @@ enum Message {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct State {
|
struct State {
|
||||||
image: RgbaImage,
|
image: Option<RgbaImage>,
|
||||||
image_display: Option<widget::image::Allocation>,
|
image_display: Option<widget::image::Allocation>,
|
||||||
}
|
}
|
||||||
impl State {
|
impl State {
|
||||||
fn new() -> (Self, Task<Message>) {
|
fn new() -> (Self, Task<Message>) {
|
||||||
let image = if let Some(path) = env::args().nth(1) {
|
|
||||||
ImageReader::open(path)
|
|
||||||
.unwrap()
|
|
||||||
.decode()
|
|
||||||
.unwrap()
|
|
||||||
.into_rgba8()
|
|
||||||
} else {
|
|
||||||
let mut image = RgbaImage::new(100, 100);
|
|
||||||
|
|
||||||
imageops::horizontal_gradient(
|
|
||||||
&mut image,
|
|
||||||
Rgba::from_slice(&[0, 0, 0, 0]),
|
|
||||||
Rgba::from_slice(&[255, 255, 255, 255]),
|
|
||||||
);
|
|
||||||
|
|
||||||
image
|
|
||||||
};
|
|
||||||
|
|
||||||
let state = State {
|
let state = State {
|
||||||
image_display: None,
|
image_display: None,
|
||||||
image,
|
image: env::args().nth(1).and_then(|path| {
|
||||||
|
Some(
|
||||||
|
ImageReader::open(path)
|
||||||
|
.unwrap()
|
||||||
|
.decode()
|
||||||
|
.unwrap()
|
||||||
|
.into_rgba8(),
|
||||||
|
)
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
let allocate_image = state.allocate_image();
|
let allocate_image = state.allocate_image();
|
||||||
|
|
||||||
|
|
@ -57,7 +45,12 @@ impl State {
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.into()
|
.into()
|
||||||
} else {
|
} else {
|
||||||
widget::space().into()
|
widget::container(widget::text(include_str!("usage.txt")))
|
||||||
|
.height(Length::Fill)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.align_x(Alignment::Center)
|
||||||
|
.align_y(Alignment::Center)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn update(&mut self, message: Message) -> Task<Message> {
|
fn update(&mut self, message: Message) -> Task<Message> {
|
||||||
|
|
@ -80,17 +73,6 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Key::Character("q") => return window::latest().and_then(window::close),
|
|
||||||
|
|
||||||
Key::Character("r") => {
|
|
||||||
self.image = imageops::rotate90(&self.image);
|
|
||||||
return self.allocate_image();
|
|
||||||
}
|
|
||||||
Key::Character("i") => {
|
|
||||||
imageops::invert(&mut self.image);
|
|
||||||
return self.allocate_image();
|
|
||||||
}
|
|
||||||
|
|
||||||
Key::Character("o") => {
|
Key::Character("o") => {
|
||||||
let Some(path) = FileDialog::new()
|
let Some(path) = FileDialog::new()
|
||||||
.add_filter(
|
.add_filter(
|
||||||
|
|
@ -106,15 +88,40 @@ impl State {
|
||||||
return Task::none();
|
return Task::none();
|
||||||
};
|
};
|
||||||
|
|
||||||
self.image = ImageReader::open(path)
|
self.image = Some(
|
||||||
.unwrap()
|
ImageReader::open(path)
|
||||||
.decode()
|
.unwrap()
|
||||||
.unwrap()
|
.decode()
|
||||||
.into_rgba8();
|
.unwrap()
|
||||||
|
.into_rgba8(),
|
||||||
|
);
|
||||||
|
|
||||||
return self.allocate_image();
|
return self.allocate_image();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Key::Character("r") => {
|
||||||
|
let Some(image) = self.image.as_ref() else {
|
||||||
|
eprintln!("no image to rotate");
|
||||||
|
return Task::none();
|
||||||
|
};
|
||||||
|
self.image = Some(imageops::rotate90(image));
|
||||||
|
return self.allocate_image();
|
||||||
|
}
|
||||||
|
Key::Character("i") => {
|
||||||
|
let Some(image) = self.image.as_mut() else {
|
||||||
|
eprintln!("no image to invert");
|
||||||
|
return Task::none();
|
||||||
|
};
|
||||||
|
imageops::invert(image);
|
||||||
|
return self.allocate_image();
|
||||||
|
}
|
||||||
|
|
||||||
Key::Character("s") => {
|
Key::Character("s") => {
|
||||||
|
let Some(image) = self.image.as_ref() else {
|
||||||
|
eprintln!("no image to save");
|
||||||
|
return Task::none();
|
||||||
|
};
|
||||||
|
|
||||||
let Some(path) = FileDialog::new().save_file() else {
|
let Some(path) = FileDialog::new().save_file() else {
|
||||||
eprintln!("no path to save provided");
|
eprintln!("no path to save provided");
|
||||||
return Task::none();
|
return Task::none();
|
||||||
|
|
@ -134,15 +141,17 @@ impl State {
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = if let Some(encoder) = maybe_encoder {
|
let result = if let Some(encoder) = maybe_encoder {
|
||||||
self.image.write_with_encoder(encoder)
|
image.write_with_encoder(encoder)
|
||||||
} else {
|
} else {
|
||||||
self.image.save(path)
|
image.save(path)
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(unused_must_use)]
|
#[allow(unused_must_use)]
|
||||||
result.inspect_err(|e| eprintln!("failed to save image: {e}"));
|
result.inspect_err(|e| eprintln!("failed to save image: {e}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Key::Character("q") => return window::latest().and_then(window::close),
|
||||||
|
|
||||||
// ignore unused keys
|
// ignore unused keys
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
|
|
@ -155,10 +164,15 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn allocate_image(&self) -> Task<Message> {
|
fn allocate_image(&self) -> Task<Message> {
|
||||||
|
let Some(image) = self.image.as_ref() else {
|
||||||
|
eprintln!("no image to allocate");
|
||||||
|
return Task::none();
|
||||||
|
};
|
||||||
|
|
||||||
widget::image::allocate(widget::image::Handle::from_rgba(
|
widget::image::allocate(widget::image::Handle::from_rgba(
|
||||||
self.image.width(),
|
image.width(),
|
||||||
self.image.height(),
|
image.height(),
|
||||||
unsafe { std::mem::transmute::<_, &'static [u8]>(self.image.as_bytes()) },
|
unsafe { std::mem::transmute::<_, &'static [u8]>(image.as_bytes()) },
|
||||||
))
|
))
|
||||||
.map(Message::ImageDisplayReady)
|
.map(Message::ImageDisplayReady)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
src/usage.txt
Normal file
5
src/usage.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
"o" to open a new image
|
||||||
|
"r" to rotate
|
||||||
|
"i" to invert colors
|
||||||
|
"s" to save the image
|
||||||
|
"q" to quit
|
||||||
Loading…
Add table
Add a link
Reference in a new issue