diff --git a/examples/multilingual/src/main.rs b/examples/multilingual/src/main.rs index 761c56c4..debb0665 100644 --- a/examples/multilingual/src/main.rs +++ b/examples/multilingual/src/main.rs @@ -23,7 +23,6 @@ fn get_labels_german() -> FileDialogLabels { show_hidden: " Versteckte Dateien anzeigen".to_string(), show_system_files: " Systemdateien anzeigen".to_string(), - heading_meta: "Metadaten".to_string(), heading_pinned: "Angeheftet".to_string(), heading_places: "Orte".to_string(), heading_devices: "Medien".to_string(), diff --git a/examples/select_file/Cargo.toml b/examples/select_file/Cargo.toml index c501349e..c5c6f30a 100644 --- a/examples/select_file/Cargo.toml +++ b/examples/select_file/Cargo.toml @@ -7,6 +7,4 @@ edition = "2021" [dependencies] eframe = { workspace = true } -egui_extras = { version = "0.29", features = ["all_loaders", "image", "file"] } -image = { version = "0.25", default-features = false, features = ["png", "jpeg"] } -egui-file-dialog = { path = "../../" } +egui-file-dialog = { path = "../../"} \ No newline at end of file diff --git a/examples/select_file/src/main.rs b/examples/select_file/src/main.rs index 664c4565..f537ea04 100644 --- a/examples/select_file/src/main.rs +++ b/examples/select_file/src/main.rs @@ -37,10 +37,6 @@ fn main() -> eframe::Result<()> { eframe::run_native( "File dialog example", eframe::NativeOptions::default(), - Box::new(|ctx| - { - egui_extras::install_image_loaders(&ctx.egui_ctx); - Ok(Box::new(MyApp::new(ctx))) - }), + Box::new(|ctx| Ok(Box::new(MyApp::new(ctx)))), ) -} +} \ No newline at end of file diff --git a/examples/select_file_with_information_panel/Cargo.toml b/examples/select_file_with_information_panel/Cargo.toml new file mode 100644 index 00000000..3e2226d7 --- /dev/null +++ b/examples/select_file_with_information_panel/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "select_file_with_information_panel" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +eframe = { workspace = true } +egui_extras = { version = "0.29", features = ["all_loaders", "image", "file"] } +image = { version = "0.25", default-features = false, features = ["png", "jpeg"] } +egui-file-dialog = { path = "../../" } \ No newline at end of file diff --git a/examples/select_file_with_information_panel/README.md b/examples/select_file_with_information_panel/README.md new file mode 100644 index 00000000..9ff7ace9 --- /dev/null +++ b/examples/select_file_with_information_panel/README.md @@ -0,0 +1,7 @@ +Example showing how to select a file using the file dialog. + +``` +cargo run -p select_file_with_information_panel +``` + +![](screenshot.png) diff --git a/examples/select_file_with_information_panel/screenshot.png b/examples/select_file_with_information_panel/screenshot.png new file mode 100644 index 00000000..96c62090 Binary files /dev/null and b/examples/select_file_with_information_panel/screenshot.png differ diff --git a/examples/select_file_with_information_panel/src/main.rs b/examples/select_file_with_information_panel/src/main.rs new file mode 100644 index 00000000..2ee231c1 --- /dev/null +++ b/examples/select_file_with_information_panel/src/main.rs @@ -0,0 +1,65 @@ +use std::path::PathBuf; + +use eframe::egui; +use egui_file_dialog::information_panel::InformationPanel; +use egui_file_dialog::FileDialog; + +struct MyApp { + file_dialog: FileDialog, + information_panel: InformationPanel, + selected_file: Option, +} + +impl MyApp { + pub fn new(_cc: &eframe::CreationContext) -> Self { + Self { + file_dialog: FileDialog::new(), + information_panel: InformationPanel::new().add_file_preview( + "csv", + |ui, text, image| { + ui.label("CSV preview:"); + if let Some(content) = text { + egui::ScrollArea::vertical() + .max_height(100.0) + .show(ui, |ui| { + ui.add( + egui::TextEdit::multiline(&mut content.clone()).code_editor(), + ); + }); + } + }, + ), + selected_file: None, + } + } +} + +impl eframe::App for MyApp { + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + egui::CentralPanel::default().show(ctx, |ui| { + if ui.button("Select file").clicked() { + self.file_dialog.select_file(); + } + + self.file_dialog.set_right_panel_width(200.0); + + self.file_dialog + .update_with_right_panel_ui(ctx, &mut |ui, dia| { + self.information_panel.ui(ui, dia); + }); + + ui.label(format!("Selected file: {:?}", self.selected_file)); + }); + } +} + +fn main() -> eframe::Result<()> { + eframe::run_native( + "File dialog example", + eframe::NativeOptions::default(), + Box::new(|ctx| { + egui_extras::install_image_loaders(&ctx.egui_ctx); + Ok(Box::new(MyApp::new(ctx))) + }), + ) +} diff --git a/src/config/labels.rs b/src/config/labels.rs index d73dc054..7f1f5124 100644 --- a/src/config/labels.rs +++ b/src/config/labels.rs @@ -45,11 +45,6 @@ pub struct FileDialogLabels { pub show_hidden: String, /// Text used for the option to show or hide system files. pub show_system_files: String, - - // ------------------------------------------------------------------------ - // Right panel: - /// Heading of the "Pinned" sections in the left panel - pub heading_meta: String, // ------------------------------------------------------------------------ // Left panel: @@ -136,9 +131,7 @@ impl Default for FileDialogLabels { reload: "⟲ Reload".to_string(), show_hidden: " Show hidden".to_string(), show_system_files: " Show system files".to_string(), - - heading_meta: "Information".to_string(), - + heading_pinned: "Pinned".to_string(), heading_places: "Places".to_string(), heading_devices: "Devices".to_string(), diff --git a/src/config/mod.rs b/src/config/mod.rs index b4d1efff..947923a8 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -186,6 +186,9 @@ pub struct FileDialogConfig { /// If the search input in the top panel should be visible. pub show_search: bool, + /// Set the width of the right panel, if used + pub right_panel_width: Option, + /// If the sidebar with the shortcut directories such as /// “Home”, “Documents” etc. should be visible. pub show_left_panel: bool, @@ -257,6 +260,7 @@ impl Default for FileDialogConfig { show_system_files_option: true, show_search: true, + right_panel_width: None, show_left_panel: true, show_pinned_folders: true, show_places: true, diff --git a/src/data/information_panel.rs b/src/data/information_panel.rs new file mode 100644 index 00000000..e531bab2 --- /dev/null +++ b/src/data/information_panel.rs @@ -0,0 +1,345 @@ +use crate::{DialogState, FileDialog}; +use chrono::{DateTime, Local}; +use egui::ahash::{HashMap, HashMapExt}; +use egui::Ui; +use image::{DynamicImage, GenericImageView, RgbaImage}; +use std::fs; + +pub struct InformationPanel { + pub meta_data: MetaData, + pub load_text_content: bool, + pub load_image_content: bool, + supported_files: HashMap, Option)>>, +} + +impl InformationPanel { + pub fn new() -> Self { + let mut supported_files = HashMap::new(); + for text_extension in [ + "txt", "json", "md", "toml", "rtf", "xml", "rs", "py", "c", "h", "cpp", "hpp", + ] { + supported_files.insert( + text_extension.to_string(), + Box::new( + |ui: &mut Ui, text: Option, image: Option| { + if let Some(content) = text { + egui::ScrollArea::vertical() + .max_height(100.0) + .show(ui, |ui| { + ui.add( + egui::TextEdit::multiline(&mut content.clone()) + .code_editor(), + ); + }); + } + }, + ) as Box, Option)>, + ); + } + // supported_files.insert( + // "png".to_string(), + // Box::new( + // |ui: &mut Ui, text: Option, image: Option| { + // ui.label("Image"); + // if let Some(img) = image { + // dbg!(&img.height()); + // dbg!(&img.width()); + // let color_image = egui::ColorImage::from_rgba_unmultiplied( + // [img.width() as usize, img.height() as usize], + // img.as_flat_samples().as_slice(), + // ); + // + // // Load the image as a texture in `egui` + // let texture = ui.ctx().load_texture( + // "loaded_image", + // color_image, + // egui::TextureOptions::default(), + // ); + // + // ui.vertical_centered(|ui| { + // // Display the image + // ui.image(&texture); + // }); + // } + // }, + // ) as Box, Option)>, + // ); + Self { + meta_data: MetaData::default(), + load_text_content: true, + load_image_content: true, + supported_files, + } + } + + pub fn add_file_preview( + mut self, + extension: &str, + add_contents: impl FnMut(&mut Ui, Option, Option) + 'static, + ) -> Self { + self.supported_files + .insert(extension.to_string(), Box::new(add_contents)); + self + } + + pub fn ui(&mut self, ui: &mut Ui, file_dialog: &mut FileDialog) { + ui.label("Information"); + + if let Some(item) = &file_dialog.selected_item { + let path = item.as_path(); + if let Some(ext) = path.extension().and_then(|ext| ext.to_str()) { + if let Some(content) = self.supported_files.get_mut(ext) { + let text = if self.load_text_content { + fs::read_to_string(path).ok() + } else { + None + }; + let image = if self.load_image_content { + image::open(path) + .map(|img| { + let new_width = 100; + let (original_width, original_height) = img.dimensions(); + let new_height = (original_height as f32 * new_width as f32 + / original_width as f32) + as u32; + + // Resize the image to the new dimensions (100px width) + let img = img.resize( + new_width, + new_height, + image::imageops::FilterType::Lanczos3, + ); + img.into_rgba8() + }) + .ok() + } else { + None + }; + content(ui, text, image) + } + } + } + + // // Spacing multiplier used between sections in the right sidebar + // const SPACING_MULTIPLIER: f32 = 4.0; + // + // egui::containers::ScrollArea::vertical() + // .auto_shrink([false, false]) + // .show(ui, |ui| { + // // Spacing for the first section in the right sidebar + // let mut spacing = ui.ctx().style().spacing.item_spacing.y * 2.0; + // + // // Update paths pinned to the left sidebar by the user + // if file_dialog.config.show_pinned_folders + // && file_dialog.ui_update_pinned_paths(ui, spacing) + // { + // spacing = ui.ctx().style().spacing.item_spacing.y * SPACING_MULTIPLIER; + // } + // + // ui.add_space(spacing); + // ui.label(file_dialog.config.labels.heading_meta.as_str()); + // ui.add_space(spacing); + // + // if let Some(item) = &file_dialog.selected_item { + // let file_name = item.file_name(); + // if file_dialog.metadata.file_name != file_name { + // // update metadata + // let metadata = fs::metadata(item.as_path()).unwrap(); + // // Display creation and last modified dates + // if let Ok(created) = metadata.created() { + // let created: DateTime = created.into(); + // file_dialog.metadata.file_created = + // Some(created.format("%Y-%m-%d %H:%M:%S").to_string()); + // } + // if let Ok(modified) = metadata.modified() { + // let modified: DateTime = modified.into(); + // file_dialog.metadata.file_modified = + // Some(modified.format("%Y-%m-%d %H:%M:%S").to_string()); + // } + // file_dialog.metadata.file_size = Some(format_bytes(metadata.len())); + // file_dialog.metadata.file_name = file_name.to_string(); + // + // // Determine the file type and display relevant metadata + // if let Some(ext) = item.as_path().extension().and_then(|e| e.to_str()) { + // match ext.to_lowercase().as_str() { + // "png" | "jpg" | "jpeg" | "bmp" | "gif" | "tiff" => { + // file_dialog.metadata.file_type = Some("Image".to_string()); + // + // // For image files, show dimensions and color space + // if let Ok(img) = image::open(item.as_path()) { + // let (width, height) = img.dimensions(); + // file_dialog.metadata.dimensions = + // Some((width as usize, height as usize)); + // + // let new_width = 100; + // let (original_width, original_height) = img.dimensions(); + // let new_height = (original_height as f32 * new_width as f32 + // / original_width as f32) + // as u32; + // + // // Resize the image to the new dimensions (100px width) + // let img = img.resize( + // new_width, + // new_height, + // image::imageops::FilterType::Lanczos3, + // ); + // file_dialog.metadata.scaled_dimensions = + // Some((img.width() as usize, img.height() as usize)); + // + // file_dialog.metadata.preview_image_bytes = + // Some(img.into_rgba8()); + // file_dialog.metadata.preview_text = None; + // } + // } + // "txt" | "json" | "md" | "toml" | "csv" | "rtf" | "xml" | "rs" + // | "py" | "c" | "h" | "cpp" | "hpp" => { + // file_dialog.metadata.file_type = Some("Textfile".to_string()); + // + // // For text files, show content + // if let Ok(content) = fs::read_to_string(item.as_path()) { + // file_dialog.metadata.preview_text = Some(content); + // } + // file_dialog.metadata.preview_image_bytes = None; + // file_dialog.metadata.dimensions = None; + // } + // _ => { + // file_dialog.metadata.file_type = Some("Unknown".to_string()); + // + // file_dialog.metadata.preview_image_bytes = None; + // file_dialog.metadata.dimensions = None; + // file_dialog.metadata.preview_text = None; + // } + // } + // } else { + // file_dialog.metadata.file_type = Some("Unknown".to_string()); + // + // file_dialog.metadata.preview_image_bytes = None; + // file_dialog.metadata.dimensions = None; + // file_dialog.metadata.preview_text = None; + // } + // } + // + // file_dialog.metadata.file_name = file_name.to_string(); + // ui.add_space(spacing); + // if let Some(content) = &file_dialog.metadata.preview_text { + // egui::ScrollArea::vertical() + // .max_height(100.0) + // .show(ui, |ui| { + // ui.add( + // egui::TextEdit::multiline(&mut content.clone()).code_editor(), + // ); + // }); + // } else if let Some(img) = &file_dialog.metadata.preview_image_bytes { + // if let Some((width, height)) = &file_dialog.metadata.scaled_dimensions { + // // Convert image into `egui::ColorImage` + // let color_image = egui::ColorImage::from_rgba_unmultiplied( + // [*width, *height], + // img.as_flat_samples().as_slice(), + // ); + // + // // Load the image as a texture in `egui` + // let texture = ui.ctx().load_texture( + // "loaded_image", + // color_image, + // egui::TextureOptions::default(), + // ); + // + // ui.vertical_centered(|ui| { + // // Display the image + // ui.image(&texture); + // }); + // } + // } else { + // ui.vertical_centered(|ui| { + // ui.label(egui::RichText::from("📁").size(120.0)); + // }); + // } + // ui.add_space(spacing); + // egui::Grid::new("meta_data") + // .num_columns(2) + // .striped(true) + // .min_col_width(200.0 / 2.0) + // .max_col_width(200.0 / 2.0) + // .show(ui, |ui| { + // ui.label("File name: "); + // ui.label(format!("{}", file_dialog.metadata.file_name)); + // ui.end_row(); + // ui.label("File type: "); + // ui.label(format!( + // "{}", + // file_dialog + // .metadata + // .file_type + // .clone() + // .unwrap_or("None".to_string()) + // )); + // ui.end_row(); + // ui.label("File size: "); + // ui.label(format!( + // "{}", + // file_dialog + // .metadata + // .file_size + // .clone() + // .unwrap_or("NAN".to_string()) + // )); + // ui.end_row(); + // if let Some((width, height)) = file_dialog.metadata.dimensions { + // ui.label("Dimensions: "); + // + // ui.label(format!("{} x {}", width, height)); + // ui.end_row(); + // ui.label("Pixel count: "); + // + // ui.label(format!("{}", format_pixels(width * height))); + // ui.end_row() + // } + // }); + // } + // }); + } +} + +#[derive(Debug, Default)] +pub struct MetaData { + pub file_name: String, + pub dimensions: Option<(usize, usize)>, + pub scaled_dimensions: Option<(usize, usize)>, + pub preview_image_bytes: Option, + pub preview_text: Option, + pub file_size: Option, + pub file_type: Option, + pub file_modified: Option, + pub file_created: Option, +} + +pub fn format_bytes(bytes: u64) -> String { + const KB: u64 = 1024; + const MB: u64 = KB * 1024; + const GB: u64 = MB * 1024; + const TB: u64 = GB * 1024; + + if bytes >= TB { + format!("{:.2} TB", bytes as f64 / TB as f64) + } else if bytes >= GB { + format!("{:.2} GB", bytes as f64 / GB as f64) + } else if bytes >= MB { + format!("{:.2} MB", bytes as f64 / MB as f64) + } else if bytes >= KB { + format!("{:.2} KB", bytes as f64 / KB as f64) + } else { + format!("{} B", bytes) + } +} + +pub fn format_pixels(pixels: usize) -> String { + const K: usize = 1_000; + const M: usize = K * 1_000; + const G: usize = M * 1_000; + + if pixels >= K { + format!("{:.2} MPx", pixels as f64 / M as f64) + } else { + format!("{} Px", pixels) + } +} diff --git a/src/data/meta_data.rs b/src/data/meta_data.rs deleted file mode 100644 index f8ba4153..00000000 --- a/src/data/meta_data.rs +++ /dev/null @@ -1,43 +0,0 @@ -use image::RgbaImage; - -#[derive(Debug, Default)] -pub struct MetaData { - pub file_name: String, - pub dimensions: Option<(usize, usize)>, - pub scaled_dimensions: Option<(usize, usize)>, - pub preview_image_bytes: Option, - pub preview_text: Option, - pub file_size: Option, - pub file_type: Option, - pub file_modified: Option, - pub file_created: Option, -} - -pub fn format_bytes(bytes: u64) -> String { - const KB: u64 = 1024; - const MB: u64 = KB * 1024; - const GB: u64 = MB * 1024; - const TB: u64 = GB * 1024; - - if bytes >= TB { - format!("{:.2} TB", bytes as f64 / TB as f64) - } else if bytes >= GB { - format!("{:.2} GB", bytes as f64 / GB as f64) - } else if bytes >= MB { - format!("{:.2} MB", bytes as f64 / MB as f64) - } else if bytes >= KB { - format!("{:.2} KB", bytes as f64 / KB as f64) - } else { - format!("{} B", bytes) - } -} - -pub fn format_pixels(pixels: usize) -> String { - const K: usize = 1_000; - const M: usize = K * 1_000; - if pixels >= K { - format!("{:.2} MPx", pixels as f64 / M as f64) - } else { - format!("{} Px", pixels) - } -} diff --git a/src/data/mod.rs b/src/data/mod.rs index 847236cf..95f0c8c2 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -5,6 +5,8 @@ mod disks; pub use disks::{Disk, Disks}; mod user_directories; -pub mod meta_data; +pub mod information_panel; + +pub use information_panel::InformationPanel; pub use user_directories::UserDirectories; diff --git a/src/file_dialog.rs b/src/file_dialog.rs index ff457ff2..03bd9e99 100644 --- a/src/file_dialog.rs +++ b/src/file_dialog.rs @@ -1,19 +1,18 @@ +use std::fmt::Debug; +use std::io; +use std::path::{Path, PathBuf}; + +use egui::text::{CCursor, CCursorRange}; + use crate::config::{ FileDialogConfig, FileDialogKeyBindings, FileDialogLabels, FileDialogStorage, FileFilter, Filter, QuickAccess, }; use crate::create_directory_dialog::CreateDirectoryDialog; -use crate::data::meta_data::{format_bytes, format_pixels, MetaData}; use crate::data::{ DirectoryContent, DirectoryContentState, DirectoryEntry, Disk, Disks, UserDirectories, }; use crate::modals::{FileDialogModal, ModalAction, ModalState, OverwriteFileModal}; -use chrono::{DateTime, Local}; -use egui::text::{CCursor, CCursorRange}; -use image::GenericImageView; -use std::fmt::Debug; -use std::path::{Path, PathBuf}; -use std::{fs, io}; /// Represents the mode the file dialog is currently in. #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -80,9 +79,6 @@ pub struct FileDialog { /// The configuration of the file dialog config: FileDialogConfig, - /// Metadata for selected file - metadata: MetaData, - /// Stack of modal windows to be displayed. /// The top element is what is currently being rendered. modals: Vec>, @@ -138,7 +134,7 @@ pub struct FileDialog { /// The item that the user currently selected. /// Can be a directory or a folder. - selected_item: Option, + pub selected_item: Option, /// Buffer for the input of the file name when the dialog is in `SaveFile` mode. file_name_input: String, /// This variables contains the error message if the `file_name_input` is invalid. @@ -200,7 +196,6 @@ impl FileDialog { Self { config: FileDialogConfig::default(), - metadata: MetaData::default(), modals: Vec::new(), mode: DialogMode::SelectDirectory, @@ -401,6 +396,14 @@ impl FileDialog { self } + pub fn set_right_panel_width(&mut self, width: f32) { + self.config.right_panel_width = Some(width); + } + + pub fn clear_right_panel_width(&mut self) { + self.config.right_panel_width = None; + } + /// Do an [update](`Self::update`) with a custom right panel ui. /// /// Example use cases: @@ -1099,25 +1102,19 @@ impl FileDialog { self.ui_update_left_panel(ui); }); } - if self.config.show_left_panel { - egui::SidePanel::right(self.window_id.with("right_panel")) - .resizable(true) - .default_width(150.0) - .width_range(90.0..=250.0) - .show_inside(ui, |ui| { - self.ui_update_right_panel(ui); - }); - } // Optionally, show a custom right panel (see `update_with_custom_right_panel`) if let Some(f) = right_panel_fn { - egui::SidePanel::right(self.window_id.with("right_panel")) + let mut right_panel = egui::SidePanel::right(self.window_id.with("right_panel")) // Unlike the left panel, we have no control over the contents, so // we don't restrict the width. It's up to the user to make the UI presentable. - .resizable(true) - .show_inside(ui, |ui| { - f(ui, self); - }); + .resizable(true); + if let Some(width) = self.config.right_panel_width { + right_panel = right_panel.exact_width(width); + } + right_panel.show_inside(ui, |ui| { + f(ui, self); + }); } egui::TopBottomPanel::bottom(self.window_id.with("bottom_panel")) @@ -1549,180 +1546,6 @@ impl FileDialog { }); } - /// Updates the right panel of the dialog. This contains the metadata of the selected file. - fn ui_update_right_panel(&mut self, ui: &mut egui::Ui) { - ui.with_layout(egui::Layout::top_down_justified(egui::Align::LEFT), |ui| { - // Spacing multiplier used between sections in the right sidebar - const SPACING_MULTIPLIER: f32 = 4.0; - - egui::containers::ScrollArea::vertical() - .auto_shrink([false, false]) - .show(ui, |ui| { - // Spacing for the first section in the right sidebar - let mut spacing = ui.ctx().style().spacing.item_spacing.y * 2.0; - - // Update paths pinned to the left sidebar by the user - if self.config.show_pinned_folders && self.ui_update_pinned_paths(ui, spacing) { - spacing = ui.ctx().style().spacing.item_spacing.y * SPACING_MULTIPLIER; - } - - ui.add_space(spacing); - ui.label(self.config.labels.heading_meta.as_str()); - ui.add_space(spacing); - - if let Some(item) = &self.selected_item { - let file_name = item.file_name(); - if self.metadata.file_name != file_name { - // update metadata - let metadata = fs::metadata(item.as_path()).unwrap(); - // Display creation and last modified dates - if let Ok(created) = metadata.created() { - let created: DateTime = created.into(); - self.metadata.file_created = - Some(created.format("%Y-%m-%d %H:%M:%S").to_string()); - } - if let Ok(modified) = metadata.modified() { - let modified: DateTime = modified.into(); - self.metadata.file_modified = - Some(modified.format("%Y-%m-%d %H:%M:%S").to_string()); - } - self.metadata.file_size = Some(format_bytes(metadata.len())); - self.metadata.file_name = file_name.to_string(); - - // Determine the file type and display relevant metadata - if let Some(ext) = item.as_path().extension().and_then(|e| e.to_str()) { - match ext.to_lowercase().as_str() { - "png" | "jpg" | "jpeg" | "bmp" | "gif" | "tiff" => { - self.metadata.file_type = Some("Image".to_string()); - - // For image files, show dimensions and color space - if let Ok(img) = image::open(item.as_path()) { - let (width, height) = img.dimensions(); - self.metadata.dimensions = - Some((width as usize, height as usize)); - - let new_width = 100; - let (original_width, original_height) = - img.dimensions(); - let new_height = (original_height as f32 - * new_width as f32 - / original_width as f32) - as u32; - - // Resize the image to the new dimensions (100px width) - let img = img.resize( - new_width, - new_height, - image::imageops::FilterType::Lanczos3, - ); - self.metadata.scaled_dimensions = - Some((img.width() as usize, img.height() as usize)); - - self.metadata.preview_image_bytes = - Some(img.into_rgba8()); - self.metadata.preview_text = None; - } - } - "txt" | "json" | "md" | "toml" | "csv" | "rtf" | "xml" - | "rs" | "py" | "c" | "h" | "cpp" | "hpp" => { - self.metadata.file_type = Some("Textfile".to_string()); - - // For text files, show content - if let Ok(content) = fs::read_to_string(item.as_path()) { - self.metadata.preview_text = Some(content); - } - self.metadata.preview_image_bytes = None; - self.metadata.dimensions = None; - } - _ => { - self.metadata.file_type = Some("Unknown".to_string()); - - self.metadata.preview_image_bytes = None; - self.metadata.dimensions = None; - self.metadata.preview_text = None; - } - } - } else { - self.metadata.file_type = Some("Unknown".to_string()); - - self.metadata.preview_image_bytes = None; - self.metadata.dimensions = None; - self.metadata.preview_text = None; - } - } - - self.metadata.file_name = file_name.to_string(); - ui.add_space(spacing); - if let Some(content) = &self.metadata.preview_text { - egui::ScrollArea::vertical() - .max_height(100.0) - .show(ui, |ui| { - ui.add( - egui::TextEdit::multiline(&mut content.clone()) - .code_editor(), - ); - }); - } - else if let Some(img) = &self.metadata.preview_image_bytes { - if let Some((width, height)) = &self.metadata.scaled_dimensions { - // Convert image into `egui::ColorImage` - let color_image = egui::ColorImage::from_rgba_unmultiplied( - [*width, *height], - img.as_flat_samples().as_slice(), - ); - - // Load the image as a texture in `egui` - let texture = ui.ctx().load_texture( - "loaded_image", - color_image, - egui::TextureOptions::default(), - ); - - ui.vertical_centered(|ui| { - // Display the image - ui.image(&texture); - }); - } - } else { - ui.vertical_centered(|ui| { - ui.label(egui::RichText::from("📁").size(120.0)); - }); - } - ui.add_space(spacing); - egui::Grid::new("meta_data") - .num_columns(2) - .striped(true) - .min_col_width(200.0 / 2.0) - .max_col_width(200.0 / 2.0) - .show(ui, |ui| { - ui.label("File name: "); - ui.label(format!("{}", self.metadata.file_name)); - ui.end_row(); - ui.label("File type: "); - ui.label(format!("{}", self.metadata.file_type.clone().unwrap_or("None".to_string()))); - ui.end_row(); - ui.label("File size: "); - ui.label(format!( - "{}", - self.metadata.file_size.clone().unwrap_or("NAN".to_string()) - )); - ui.end_row(); - if let Some((width, height)) = self.metadata.dimensions { - ui.label("Dimensions: "); - - ui.label(format!("{} x {}", width, height)); - ui.end_row(); - ui.label("Pixel count: "); - - ui.label(format!("{}", format_pixels(width * height))); - ui.end_row() - } - }); - } - }); - }); - } - /// Updates the left panel of the dialog. Including the list of the user directories (Places) /// and system disks (Devices, Removable Devices). fn ui_update_left_panel(&mut self, ui: &mut egui::Ui) { diff --git a/src/lib.rs b/src/lib.rs index 2602f7e2..0234af41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,5 +204,6 @@ pub use config::{ FileDialogConfig, FileDialogKeyBindings, FileDialogLabels, FileDialogStorage, IconFilter, KeyBinding, QuickAccess, QuickAccessPath, }; +pub use data::information_panel; pub use data::DirectoryEntry; pub use file_dialog::{DialogMode, DialogState, FileDialog};