feat: pick folder dialog

This commit is contained in:
electria 2026-08-09 14:04:44 -07:00
commit d6549939a8
Signed by: electria
SSH key fingerprint: SHA256:8LlB3ucPbBHqozqkhsNbaV5oG3SlzzqUj8FZDL6IPQs
2 changed files with 65 additions and 30 deletions

View file

@ -32,6 +32,8 @@
libxcb
]
++ lib.optionals stdenv.hostPlatform.isLinux [
dbus.lib
vulkan-loader
wayland
];

View file

@ -1,4 +1,10 @@
use std::{borrow::Cow, collections::BTreeMap, env, path::PathBuf, process};
use std::{
borrow::Cow,
collections::BTreeMap,
env,
path::{Path, PathBuf},
process,
};
use iced::{
Color, Element, Event, Length, Renderer, Subscription, Task, Theme, application, event,
@ -6,6 +12,7 @@ use iced::{
};
use ignore::{WalkBuilder, types::TypesBuilder};
use rayon::iter::{ParallelBridge, ParallelIterator};
use rfd::AsyncFileDialog;
use crate::SUPPORTED_TYPES;
@ -16,6 +23,8 @@ enum Message {
OpenImage(PathBuf),
OpenPath(Option<PathBuf>),
KeyPressed(keyboard::Key, keyboard::Modifiers),
}
@ -29,37 +38,20 @@ struct State {
}
impl State {
fn new() -> Self {
let mut builder = TypesBuilder::new();
builder.add_defaults();
for ext in SUPPORTED_TYPES {
builder.add("supportedImages", ext).unwrap();
}
builder.select("supportedImages");
Self {
columns: 3,
error: None,
images: WalkBuilder::new(
fn new() -> (Self, Task<Message>) {
(
Self {
columns: 3,
error: None,
images: BTreeMap::new(),
},
Task::done(Message::OpenPath(Some(
env::args()
.nth(1)
.map(Cow::Owned)
.unwrap_or(Cow::Borrowed("./"))
.as_ref(),
)
.types(builder.build().unwrap())
.max_depth(Some(1))
.build()
.par_bridge()
.filter_map(|r| {
r.inspect_err(|e| eprintln!("{e}"))
.ok()
.map(|entry| entry.into_path())
})
.filter(|path| path.is_file())
.map(|path| (path, None))
.collect(),
}
.unwrap_or_else(|| String::from("./"))
.into(),
))),
)
}
fn update(&mut self, message: Message) -> Task<Message> {
match message {
@ -80,6 +72,17 @@ impl State {
.err()
}
Message::OpenPath(opt_path) => {
self.error = match opt_path {
Some(_) => None,
None => Some("no path to open given".into()),
};
if let Some(path) = opt_path {
self.open_path(path);
}
}
Message::KeyPressed(key, modifiers) => match key.as_ref() {
// input field cycling
keyboard::Key::Named(keyboard::key::Named::Tab) => {
@ -90,6 +93,13 @@ impl State {
};
}
keyboard::Key::Character("o") => {
return Task::perform(
async { AsyncFileDialog::new().pick_folder().await.map(Into::into) },
Message::OpenPath,
);
}
keyboard::Key::Character("=" | "+") => {
self.columns = (self.columns + 1).clamp(1, 10)
}
@ -157,6 +167,29 @@ impl State {
_ => None,
})
}
fn open_path(&mut self, path: impl AsRef<Path>) {
let mut builder = TypesBuilder::new();
builder.add_defaults();
for ext in SUPPORTED_TYPES {
builder.add("supportedImages", ext).unwrap();
}
builder.select("supportedImages");
self.images = WalkBuilder::new(path)
.types(builder.build().unwrap())
.max_depth(Some(1))
.build()
.par_bridge()
.filter_map(|r| {
r.inspect_err(|e| eprintln!("{e}"))
.ok()
.map(|entry| entry.into_path())
})
.filter(|path| path.is_file())
.map(|path| (path, None))
.collect();
}
}
pub fn run() -> Result<(), impl std::error::Error> {