diff --git a/Cargo.lock b/Cargo.lock index 3d92a03..084cbed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1336,6 +1336,20 @@ dependencies = [ "winit", ] +[[package]] +name = "egui_extras" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3c1f5cd8dfe2ade470a218696c66cf556fcfd701e7830fa2e9f4428292a2a1" +dependencies = [ + "ahash", + "egui", + "enum-map", + "image", + "log", + "mime_guess2", +] + [[package]] name = "egui_file" version = "0.19.0" @@ -1407,6 +1421,27 @@ dependencies = [ "thiserror", ] +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", + "serde", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.75", +] + [[package]] name = "enumflags2" version = "0.7.10" @@ -2512,6 +2547,7 @@ dependencies = [ "directories", "eframe", "egui", + "egui_extras", "egui_file", "enquote", "execute", @@ -2588,6 +2624,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess2" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a3333bb1609500601edc766a39b4c1772874a4ce26022f4d866854dc020c41" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -3293,18 +3339,18 @@ dependencies = [ [[package]] name = "profiling" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", "syn 2.0.75", @@ -4468,6 +4514,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + [[package]] name = "unicode-bidi" version = "0.3.15" diff --git a/Cargo.toml b/Cargo.toml index da50baf..97e3297 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ ui = [ "dep:indexmap", "dep:egui_file", "dep:rfd", + "dep:egui_extras", ] [dev-dependencies] @@ -79,6 +80,7 @@ reqwest = { version = "0.12.5", default-features = false, features = [ # gui feature egui = { version = "0.29.1", optional = true } +egui_extras = { version = "0.29.1", optional = true, features = ["image", "file"] } eframe = { version = "0.29.1", optional = true } indexmap = { version = "2.7.0", optional = true } egui_file = { version = "0.19.0", optional = true } diff --git a/src/gui/init.rs b/src/gui/init.rs index 4299531..8bbd10b 100644 --- a/src/gui/init.rs +++ b/src/gui/init.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{ffi::OsStr, path::PathBuf}; #[cfg(feature = "ui")] use indexmap::IndexMap; @@ -20,6 +20,7 @@ use crate::{template::TemplateFile, util::arguments::Cli, State}; #[cfg(feature = "ui")] #[derive(PartialEq)] pub enum Tabs { + Images, Settings, Colors, } @@ -33,6 +34,8 @@ pub struct MyApp { colors: ColorsMap, selected_tab: Tabs, app: State, + images_vec: Vec, + images_folder: Option, } #[cfg(feature = "ui")] @@ -41,6 +44,25 @@ pub struct ColorsMap { pub dark: Option>, } +fn get_images_in_folder(folder_path: &PathBuf) -> Vec { + let valid_extensions = ["jpg", "jpeg", "png", "gif", "bmp", "tiff", "webp"]; + + std::fs::read_dir(folder_path) + .ok() + .into_iter() + .flatten() + .flatten() + .map(|entry| entry.path()) + .filter(|path| path.is_file()) + .filter(|path| { + path.extension() + .and_then(|ext| ext.to_str()) + .map(|ext| valid_extensions.contains(&ext.to_lowercase().as_str())) + .unwrap_or(false) + }) + .collect() +} + // FIXME: Cleanup code, reorganize stuff into its own functions #[cfg(feature = "ui")] @@ -55,12 +77,15 @@ impl MyApp { dark: None, }, selected_tab: Tabs::Settings, + images_vec: vec![], + images_folder: None, } } fn body(&mut self, ui: &mut Ui) { match self.selected_tab { Tabs::Settings => self.settings(ui), Tabs::Colors => self.colors(ui), + Tabs::Images => self.images(ui), } } @@ -73,7 +98,7 @@ impl MyApp { )); }); ui.label("Scheme type"); - egui::ComboBox::from_label("Select one!") + egui::ComboBox::from_label("") .selected_text(format!("{:?}", self.app.args.r#type.unwrap())) .show_ui(ui, |ui| { ui.selectable_value( @@ -172,20 +197,80 @@ impl MyApp { }); } + fn images(&mut self, ui: &mut Ui) { + if self.images_vec.is_empty() { + ui.label("No image folder selected or no images to show."); + } else { + ui.with_layout( + egui::Layout::left_to_right(egui::Align::Max).with_cross_justify(true), + |ui| { + self.show_images(ui); + }, + ); + } + } + + fn show_images(&mut self, ui: &mut Ui) { + egui::ScrollArea::vertical().show(ui, |ui| { + egui::Grid::new("image_grid") + .num_columns(3) // Set number of columns + .spacing([10.0, 10.0]) // Spacing between items + .show(ui, |ui| { + for (i, path) in self.images_vec.clone().iter().enumerate() { + let img_widget = egui::Image::from_uri(format!( + "file://{}", + path.clone().into_os_string().into_string().unwrap() + )) + .max_height(300.0) + .max_width(300.0) + .maintain_aspect_ratio(true) + .fit_to_original_size(1.0); + + if ui.add(img_widget.sense(egui::Sense::click())).clicked() { + self.selected_file = Some(path.to_path_buf()); + self.run(); + } + + if (i + 1) % 3 == 0 { + ui.end_row(); + } + } + }); + }); + } + + fn update_images_tab(&mut self, ui: &mut Ui, image_folder: Option) { + if image_folder == Some("".into()) || image_folder.is_none() { + return; + } + let images = get_images_in_folder(&image_folder.as_ref().unwrap()); + self.images_vec = images; + } + fn top_buttons(&mut self, ui: &mut Ui) { + if ui.button("Image Folder").clicked() { + if let Some(path) = rfd::FileDialog::new().pick_folder() { + self.update_images_tab(ui, Some(path)) + } + } if ui.button("Select image").clicked() { if let Some(path) = rfd::FileDialog::new().pick_file() { self.selected_file = Some(path); } } if ui.button("Run").clicked() { - if self.selected_file != Some("".into()) || self.selected_file.is_some() { - self.update_colors_tab(); - self.generate_tempalates(); - }; + self.run() } } + fn run(&mut self) { + if self.selected_file == Some("".into()) || self.selected_file.is_none() { + return; + }; + self.generate_tempalates(); + self.update_colors_tab(); + } + fn generate_tempalates(&mut self) { let mut engine = self.app.init_engine(); let mut render_data = self.app.init_render_data().unwrap(); @@ -225,9 +310,11 @@ fn argb_to_color32(color: &Argb) -> Color32 { #[cfg(feature = "ui")] impl eframe::App for MyApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + egui_extras::install_image_loaders(ctx); egui::TopBottomPanel::top("my_panel").show(ctx, |ui| { ui.horizontal(|ui| { ui.horizontal(|ui| { + ui.selectable_value(&mut self.selected_tab, Tabs::Images, "Images"); ui.selectable_value(&mut self.selected_tab, Tabs::Settings, "Settings"); ui.selectable_value(&mut self.selected_tab, Tabs::Colors, "Colors"); });