Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
b1157474c9 |
|||
|
9bdd556c9d |
|||
|
7ce87e2870 |
|||
|
97ce6754dd |
|||
|
f279c3aa15 |
2 changed files with 73 additions and 38 deletions
103
src/main.rs
103
src/main.rs
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{env, path::Path};
|
use std::{borrow::Cow, env, ffi::OsStr, path::Path};
|
||||||
|
|
||||||
use iced::{
|
use iced::{
|
||||||
Alignment, Color, Element, Length, Renderer, Subscription, Task, Theme, color, event,
|
Alignment, Color, Element, Length, Renderer, Subscription, Task, Theme, event,
|
||||||
keyboard::{self, Key, key},
|
keyboard::{self, Key, key},
|
||||||
theme, widget, window,
|
theme, widget, window,
|
||||||
};
|
};
|
||||||
|
|
@ -20,44 +20,53 @@ enum Message {
|
||||||
struct State {
|
struct State {
|
||||||
image: Option<RgbaImage>,
|
image: Option<RgbaImage>,
|
||||||
image_display: Option<widget::image::Allocation>,
|
image_display: Option<widget::image::Allocation>,
|
||||||
|
image_filter: widget::image::FilterMethod,
|
||||||
|
|
||||||
error: Option<String>,
|
error: Option<String>,
|
||||||
|
info: Option<String>,
|
||||||
|
infobar_shown: bool,
|
||||||
}
|
}
|
||||||
impl State {
|
impl State {
|
||||||
fn new() -> (Self, Task<Message>) {
|
fn new() -> (Self, Task<Message>) {
|
||||||
let mut state = State::default();
|
let mut state = State::default();
|
||||||
|
|
||||||
state.error = env::args().nth(1).and_then(|path| {
|
if let Some(path) = env::args().nth(1) {
|
||||||
utils::load_image(path)
|
match state.load_image(path) {
|
||||||
.map(|image| state.image = Some(image))
|
Err(e) => state.error = Some(e),
|
||||||
.err()
|
Ok(task) => return (state, task),
|
||||||
});
|
}
|
||||||
let allocate_image = state.allocate_image();
|
}
|
||||||
|
|
||||||
(state, allocate_image)
|
(state, Task::none())
|
||||||
}
|
}
|
||||||
fn view(&self) -> Element<'_, Message, Theme, Renderer> {
|
fn view(&self) -> Element<'_, Message, Theme, Renderer> {
|
||||||
widget::column([
|
let mut main = Vec::new();
|
||||||
if let Some(allocation) = self.image_display.as_ref() {
|
|
||||||
widget::image::viewer(allocation.handle().clone())
|
main.push(if let Some(allocation) = self.image_display.as_ref() {
|
||||||
.width(Length::Fill)
|
widget::image::viewer(allocation.handle().clone())
|
||||||
.height(Length::Fill)
|
.filter_method(self.image_filter)
|
||||||
.into()
|
.max_scale(50.)
|
||||||
} else {
|
.min_scale(1.)
|
||||||
widget::container(widget::text(include_str!("usage.txt")))
|
.width(Length::Fill)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.width(Length::Fill)
|
.into()
|
||||||
.align_x(Alignment::Center)
|
} else {
|
||||||
.align_y(Alignment::Center)
|
widget::container(widget::text(include_str!("usage.txt")))
|
||||||
.into()
|
.height(Length::Fill)
|
||||||
},
|
.width(Length::Fill)
|
||||||
if let Some(error) = self.error.as_ref() {
|
.align_x(Alignment::Center)
|
||||||
widget::text(error).style(widget::text::danger).into()
|
.align_y(Alignment::Center)
|
||||||
} else {
|
.into()
|
||||||
widget::space().into()
|
});
|
||||||
},
|
|
||||||
])
|
if let Some(error) = self.error.as_ref() {
|
||||||
.into()
|
main.push(widget::text(error).style(widget::text::danger).into());
|
||||||
|
}
|
||||||
|
if self.infobar_shown && self.info.is_some() {
|
||||||
|
main.push(widget::text(self.title()).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
widget::column(main).into()
|
||||||
}
|
}
|
||||||
fn update(&mut self, message: Message) -> Task<Message> {
|
fn update(&mut self, message: Message) -> Task<Message> {
|
||||||
match message {
|
match message {
|
||||||
|
|
@ -107,6 +116,15 @@ impl State {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
Key::Character("s") => {
|
Key::Character("s") => {
|
||||||
self.save_image();
|
self.save_image();
|
||||||
|
|
@ -136,7 +154,16 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_image(&mut self, path: impl AsRef<Path>) -> Result<Task<Message>, String> {
|
fn load_image(&mut self, path: impl AsRef<Path>) -> Result<Task<Message>, String> {
|
||||||
utils::load_image(path).map(|image| {
|
utils::load_image(&path).map(|image| {
|
||||||
|
self.info = Some(format!(
|
||||||
|
"{} {}x{}",
|
||||||
|
path.as_ref()
|
||||||
|
.file_name()
|
||||||
|
.map(OsStr::to_string_lossy)
|
||||||
|
.unwrap_or(Cow::Borrowed("[no file]")),
|
||||||
|
image.width(),
|
||||||
|
image.height(),
|
||||||
|
));
|
||||||
self.image = Some(image);
|
self.image = Some(image);
|
||||||
self.allocate_image()
|
self.allocate_image()
|
||||||
})
|
})
|
||||||
|
|
@ -167,8 +194,11 @@ impl State {
|
||||||
event::listen().map(Message::Event)
|
event::listen().map(Message::Event)
|
||||||
}
|
}
|
||||||
fn title(&self) -> String {
|
fn title(&self) -> String {
|
||||||
match self.image.as_ref() {
|
match self.info.as_ref() {
|
||||||
Some(image) => format!("imagey {}x{}", image.width(), image.height()),
|
Some(info) => format!(
|
||||||
|
"imagey {info} {}",
|
||||||
|
utils::string_from_filter_type(self.image_filter),
|
||||||
|
),
|
||||||
None => "imagey".into(),
|
None => "imagey".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -183,12 +213,9 @@ fn main() -> Result<(), iced::Error> {
|
||||||
.theme(Theme::custom(
|
.theme(Theme::custom(
|
||||||
"custom",
|
"custom",
|
||||||
theme::Palette {
|
theme::Palette {
|
||||||
background: color!(0x080808),
|
background: Color::BLACK,
|
||||||
text: Color::WHITE,
|
text: Color::WHITE,
|
||||||
primary: color!(0xff00ff),
|
..theme::Palette::DARK
|
||||||
success: color!(0x00ff00),
|
|
||||||
warning: color!(0x880000),
|
|
||||||
danger: color!(0xff0000),
|
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.run()
|
.run()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use iced::widget;
|
||||||
use image::{DynamicImage, ImageDecoder, ImageReader, RgbaImage};
|
use image::{DynamicImage, ImageDecoder, ImageReader, RgbaImage};
|
||||||
use rfd::FileDialog;
|
use rfd::FileDialog;
|
||||||
|
|
||||||
|
|
@ -79,3 +80,10 @@ pub fn save_image(image: Option<&RgbaImage>) -> Result<(), String> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn string_from_filter_type(f: widget::image::FilterMethod) -> &'static str {
|
||||||
|
match f {
|
||||||
|
widget::image::FilterMethod::Linear => "bilinear",
|
||||||
|
widget::image::FilterMethod::Nearest => "nearest neighbor",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue