From 5ad4cb03e39422830680f0018be8f4a2432730b4 Mon Sep 17 00:00:00 2001 From: hacknus Date: Mon, 11 Nov 2024 23:40:44 +0100 Subject: [PATCH] revert previous prototype and create information_panel.rs --- examples/multilingual/src/main.rs | 1 - examples/select_file/Cargo.toml | 4 +- examples/select_file/src/main.rs | 8 +- .../Cargo.toml | 12 + .../README.md | 7 + .../screenshot.png | Bin 0 -> 45621 bytes .../src/main.rs | 65 ++++ src/config/labels.rs | 9 +- src/config/mod.rs | 4 + src/data/information_panel.rs | 345 ++++++++++++++++++ src/data/meta_data.rs | 43 --- src/data/mod.rs | 4 +- src/file_dialog.rs | 223 ++--------- src/lib.rs | 1 + 14 files changed, 464 insertions(+), 262 deletions(-) create mode 100644 examples/select_file_with_information_panel/Cargo.toml create mode 100644 examples/select_file_with_information_panel/README.md create mode 100644 examples/select_file_with_information_panel/screenshot.png create mode 100644 examples/select_file_with_information_panel/src/main.rs create mode 100644 src/data/information_panel.rs delete mode 100644 src/data/meta_data.rs 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 0000000000000000000000000000000000000000..96c6209065e650c74a58a6aa2fe5ceb327d88867 GIT binary patch literal 45621 zcmagG2{@Mh+BJTYDal-l5HchgO6DnUq?DmjsgMRk#z@9UWk@KJBC|r0gk&b65;7%( zkSUonW%{qnv)}#yzW06i@j3QBp1QkT*YElb=egE8*Lel%AJN`O&q+@rkv8faARCfM zl!+t~#Rwe@K4C9e{e=IlcQ|m$i9}+2PyC-EVizM9iL{-hL*9SP)#P`ln*)1m4DEz{ zeOiN5!T9L`_RVt_DXJb-2>Wi~;*xmNcUz9e|Cr0=)-2hqn!@90@1vg|6D*9rV&3Z8 zI&xL_$&YWY!E(R1+$!Hr(fehig4*2fTCbUvgqT^~%a^2CId#hBj%r2>%yl{EnJ{H^ z7tQ_6y%!rR?i+v~OW&P8Dfsb|7+6MrKzt%Z&1OJ+5XG)b4kW%|D9Iv7d_kxDUthR= zkjEkD&K>m&7k0dR_wJ`=-tO$SyCo$mC&f0qu@FDOdrJd{VjkY2^f{7pvuXGO|3NaD zEKRY*Slex-lNxQ59^p^iNrY-iD6y|UtC}a=zv{TPwdfn0Hs8Tm4@M@H`RydNr+?oh z$u=(v6Zg)VRFodCxwC1Pq|<M>Edm?O zQf?<-+jT1B$K+!7Dvwsu@pbzKY>Y<%bY_cM4v_m*{( z<8j0-g^a#9k-0cI@7=rS^Yl=pIx|(~>uO%_Z4C_#sV`oTU0vm5Y}Fv; zdygIkJbx}Ky>DOnrECh)lOw{$r%yMux8J#OgCeaYx~4zu)8gO1BvMm~USta=tK}*& zA<|jCV=Tqjmlf^=1!dW^?O|tUkAL=zM)=4RH7zX~egT2W*$(@4B+*^FCYQ&(d5tqR z8>JuTEU2%oojEO`tjwL2l{McveqGikwY$gR$M3dm6DhqTDNZNz2W5QQN?WWWez9S1 zG`ydP4*0~TPD*|GQtRr~eOH{EvaDM~ZQ66z*Vfj4vCefJ%O6a3_uamI=g#$q4A=wu7=F5L)Bp>VQi?9#<{!Mmtlp@9F<#9yI+E?9JN4xZOWO%sz@m`BpE~Cc7 z;3QttLIA>;NzP-M*=JzAR~U(b^~69>rs>ti#?e~y^t zbv(^AT#FS;`Qe5UN)%V8IZ8V_(@JHD1t&WWp1wX9U@W%TGS}E`{>@`r{n(y3%x zG3BGpFRE8^S4)6wP?QvA5L8KdvwS<#+D18iZ}9Qh`(p=mbk5E362EX*u2f^%Zi?X{ zF$R$>D}Fya{R9(rmDcX8^kMp-oB4A}zY|38;^(xFl~37Dr>{jO7fsquIwT!7Y<+R+ z=HI{WZm#dd&K9s+=W=>^d6`V!x8$$AH+YT(WlWmlmcI^cg&YA`vs(ERQ`DJH^iwe9*?`MZMxO zhlB~k&2pc;7IpCn2^3fXV#yFKN#Bcve*AXiW#Pn3t7%ekF*i1Uz@0msMtXIf6>>&fw{Dg5n0=wNP1<|0b=bM$jmOka z9fV%eN;k&~jD&a^Q_o3iT_HA-T85E;f`YM8I3{eU?q{8Vw&!a@8yhhi8k#RHEz}0= z#5SCrbs=f2|N1@ODWP@boa7dV;YPuo3Dcil*R>oKjd-MZP2Akv92-Xe#0h_F0g;G< z?3*`JlG-~u3{ITja8MMDpr@v$4hRgqQE7fdJz?VU)eXMeZSx8XxsD${e&EQFDoiQ` z)wS=K@#%qDjpN5#Ud?E6Gtw$3C?sEKuI;~~({Wsc{gJFakNNrY=k`3-KXoc_W#u}l zjF{in5v+%qG^b<#r%hJ8D7O5n8Pkn5q+6)nUhlVT>4-+ynHp)bAEYnwS?d%L58?)QVvf+|qYo@7x zxf!w*7m}7nb${0w&MIlUvT|qo_uB52#Kc4-OvYjpiI%Hunk?O()4>nS3eU$@Ul}i+ z<5y7N3=R%

4C1YrW}RMFj;ZKR@5n-d_9sd4UH91&v;uq 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, +} + +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};