Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Metadata/Information display for files (and preview) #184

Merged
merged 92 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
a0ab0c3
initial implementation of file information display
hacknus Nov 10, 2024
2de8627
revert previous prototype and create information_panel.rs
hacknus Nov 11, 2024
8d920e5
add meta data grid
hacknus Nov 11, 2024
bb78374
update meta_data
hacknus Nov 11, 2024
d3a8c27
revert public config and selected item
hacknus Nov 11, 2024
9797e09
only read 1000 characters of text files and implement Markdown as an …
hacknus Nov 12, 2024
7c0a80f
add separators
hacknus Nov 12, 2024
4454089
introduce info_panel feature
hacknus Nov 12, 2024
9c34335
upload test_image
hacknus Nov 13, 2024
32eaea8
implement metadata, markdown and image preview
hacknus Nov 15, 2024
a6002a8
metadata in ScrollArea and only display size for files, not directories
hacknus Nov 15, 2024
656231c
clean up, add doc and add folder icon as preview
hacknus Nov 15, 2024
3182ae8
removed profiles, not used anymore
hacknus Nov 15, 2024
de8484f
image crate needs to be loaded always, because of the Image metadata …
hacknus Nov 15, 2024
2d3e521
only load image crate when using info_panel feature
hacknus Nov 15, 2024
27d5957
only load text content once. show icon if no preview is available
hacknus Nov 15, 2024
80e6c66
extend preview for jpeg and jpg
hacknus Nov 15, 2024
44df761
extension to lowercase, so it does not matter how they are stored on …
hacknus Nov 15, 2024
b760b37
rapidly decreased loading times of directories using image-meta crate…
hacknus Nov 15, 2024
5dc34fc
indexmap instead of hashmap, so that metadata is always in the same o…
hacknus Nov 15, 2024
fc2975e
cargo clippy
hacknus Nov 15, 2024
6416832
remove unused doc
hacknus Nov 15, 2024
2680506
Update Cargo.toml
hacknus Nov 15, 2024
41a8019
workflow fix
hacknus Nov 15, 2024
4207715
Merge remote-tracking branch 'origin/show_meta_data' into show_meta_data
hacknus Nov 15, 2024
e0853a6
reformat
hacknus Nov 15, 2024
71cd1c4
text content is now only loaded for the selected file and then stored…
hacknus Nov 16, 2024
e3eb4bb
not hard-coding max chars of text preview
hacknus Nov 16, 2024
133b308
fix width and compilation errors
hacknus Nov 17, 2024
c868a89
implement custom metadata loader
hacknus Nov 17, 2024
3b7d44b
remove unused dependency
hacknus Nov 18, 2024
ab82494
code review by bircni
hacknus Nov 18, 2024
5e4ae4f
required for image loader
hacknus Nov 21, 2024
e1acd29
add for cargo machete. image crate is required per documentation of e…
hacknus Nov 21, 2024
e65c266
remove md render
hacknus Nov 21, 2024
ee414b1
Merge branch 'develop' into show_meta_data
hacknus Nov 25, 2024
5ec9df8
use picked instead of selected
hacknus Nov 25, 2024
ce44a80
rename example
hacknus Nov 26, 2024
0bdb134
rename example
hacknus Nov 26, 2024
975a0e6
create metadata struct
hacknus Nov 26, 2024
ba85a77
rustfmt
hacknus Nov 26, 2024
ce279e2
implement content_mut
hacknus Nov 27, 2024
b9e6894
fix typo
hacknus Nov 28, 2024
f533077
implement `InfoPanelEntry` struct
hacknus Nov 28, 2024
f572257
change feature name to information_view
hacknus Nov 28, 2024
018fd7b
move format_pixels() to information_panel.rs
hacknus Nov 28, 2024
b04e94c
add screenshot and remove test_image
hacknus Nov 28, 2024
35a94c9
remove print
hacknus Nov 29, 2024
a4cfa33
change date formatting
hacknus Dec 1, 2024
8a56895
Update examples/select_file_with_information_view/README.md
hacknus Dec 1, 2024
87ab73a
Update src/data/information_panel.rs
hacknus Dec 1, 2024
ccda4d6
Update src/data/information_panel.rs
hacknus Dec 1, 2024
34989bd
add docs
hacknus Dec 1, 2024
65ff604
introduced path_buf variable
hacknus Dec 1, 2024
add61be
code review
hacknus Dec 1, 2024
091b002
Merge remote-tracking branch 'origin/show_meta_data' into show_meta_data
hacknus Dec 1, 2024
081a401
rename handler
hacknus Dec 1, 2024
a64cdd1
set image max width to be as high as the panel is wide
hacknus Dec 1, 2024
29c378e
Merge branch 'develop' into show_meta_data
hacknus Dec 3, 2024
697b398
show icon for all files
hacknus Dec 3, 2024
da2022f
Merge remote-tracking branch 'origin/show_meta_data' into show_meta_data
hacknus Dec 3, 2024
0c4eb84
don't set width of meta grid
hacknus Dec 3, 2024
6436382
Merge remote-tracking branch 'origin/develop' into show_meta_data
hacknus Dec 4, 2024
213504c
adapt for latest example structure
hacknus Dec 4, 2024
ba9fa5e
Revert "don't set width of meta grid"
hacknus Dec 4, 2024
0788aa3
add comment for image crate
hacknus Dec 4, 2024
475ba43
clippy
hacknus Dec 4, 2024
958a61e
remove images if more than 10 are loaded
hacknus Dec 4, 2024
ffebafa
move information_panel.rs
hacknus Dec 5, 2024
1aa59e4
rustfmt
hacknus Dec 5, 2024
b7bb764
better doc for example
hacknus Dec 5, 2024
6a3b2eb
update README.md
hacknus Dec 5, 2024
f950d1b
implement `forget_all_stored_images`
hacknus Dec 5, 2024
a1ece08
implement `forget_all_stored_images` in `MyApp::update()`
hacknus Dec 5, 2024
cd2d680
update Cargo.toml
hacknus Dec 5, 2024
ae88da4
Update examples/README.md
hacknus Dec 5, 2024
ec0ad2a
pick file rename
hacknus Dec 5, 2024
eef7736
remove hardcoded icon width
hacknus Dec 5, 2024
1638af8
Merge remote-tracking branch 'origin/show_meta_data' into show_meta_data
hacknus Dec 5, 2024
74dfaf8
id with `FileDialog` as parent
hacknus Dec 5, 2024
182de49
fix id
hacknus Dec 5, 2024
13ec6a8
windows should always take up the same amount of space
hacknus Dec 5, 2024
7673b4d
Update src/information_panel.rs
hacknus Dec 5, 2024
bda4cd5
Update src/information_panel.rs
hacknus Dec 5, 2024
4fdda5f
extract image display function
hacknus Dec 5, 2024
0c6ae17
Merge remote-tracking branch 'origin/show_meta_data' into show_meta_data
hacknus Dec 5, 2024
2260c5a
rustfmt
hacknus Dec 5, 2024
c694742
all previews should have the same size
hacknus Dec 5, 2024
c55350c
Update examples/pick_file_with_information_view.rs
hacknus Dec 5, 2024
f3e3d2d
Update Cargo.toml
hacknus Dec 5, 2024
b2177b2
fix typo
hacknus Dec 5, 2024
73b826b
Merge remote-tracking branch 'origin/show_meta_data' into show_meta_data
hacknus Dec 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,28 @@ dunce = "1.0.5"
sysinfo = { version = "0.32", default-features = false, features = ["disk"] }
# persistent storage
serde = { version = "1", features = ["derive"], optional = true }
# meta-data storage
indexmap = { version = "2.6.0", features = ["serde"], optional = true }

# info panel meta-data display
image-meta = { version = "0.1.2", optional = true }
chrono = { version = "0.4.38", optional = true }

[dev-dependencies]
eframe = { version = "0.29.1", default-features = false, features = [
"glow",
"persistence",
] }
egui-file-dialog = { path = "." }
egui-file-dialog = { path = "." , features = ["information_view"] }
egui_extras = { version = "0.29", features = ["all_loaders"] }
# required by the egui loaders
image = { version = "0.25.5", features = ["bmp", "jpeg", "gif", "png", "tiff", "rayon"] }

[features]
default = ["serde", "default_fonts"]
serde = ["dep:serde"]
default_fonts = ["egui/default_fonts"]
information_view = ["dep:chrono", "image-meta", "indexmap"]

[lints.rust]
unsafe_code = "warn"
Expand All @@ -54,3 +64,4 @@ struct_field_names = { level = "allow", priority = 13 }
missing_fields_in_debug = { level = "allow", priority = 14 }
missing_errors_doc = { level = "allow", priority = 15 }
module_name_repetitions = { level = "allow", priority = 16 }
cast_precision_loss = { level = "allow", priority = 17 }
22 changes: 22 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,25 @@ cargo run --example save_file
```

![Screenshot](../media/examples/save_file.png)


## Pick File with Information View

Example showing how to pick a file and display file information using the `InformationView`.
hacknus marked this conversation as resolved.
Show resolved Hide resolved

Requires the feature `information_view` as well as these dependencies:

```toml
[dependencies]
egui-file-dialog = { version = "*", features = ["information_view"] }
egui_extras = { version = "0.29", features = ["all_loaders"] }
# required by the egui loaders
image = { version = "0.25.5", features = ["bmp", "jpeg", "gif", "png", "tiff", "rayon"] }
```

```shell
cargo run --example pick_file_with_information_view
```

![Screenshot](../media/examples/information_view.png)

78 changes: 78 additions & 0 deletions examples/pick_file_with_information_view.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use std::path::PathBuf;

use eframe::egui;
use egui_file_dialog::information_panel::InformationPanel;
use egui_file_dialog::{DialogState, FileDialog};

struct MyApp {
file_dialog: FileDialog,
information_panel: InformationPanel,
selected_file: Option<PathBuf>,
}

impl MyApp {
pub fn new(_cc: &eframe::CreationContext) -> Self {
Self {
file_dialog: FileDialog::new(),
information_panel: InformationPanel::default()
.add_file_preview("csv", |ui, item| {
ui.label("CSV preview:");
if let Some(mut content) = item.content() {
egui::ScrollArea::vertical()
.max_height(ui.available_height())
.show(ui, |ui| {
ui.add(egui::TextEdit::multiline(&mut content).code_editor());
});
}
})
// add additional metadata loader
.add_metadata_loader("pdf", |other_meta_data, path| {
// as a simple example, just show the Filename of the PDF
other_meta_data.insert("PDF Filename".to_string(), format!("{path:?}"));
}),
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.pick_file();
}

self.file_dialog.set_right_panel_width(200.0);

if let Some(path) = self
.file_dialog
.update_with_right_panel_ui(ctx, &mut |ui, dia| {
self.information_panel.ui(ui, dia);
})
.picked()
{
self.selected_file = Some(path.to_path_buf());
}

match self.file_dialog.state() {
DialogState::Closed | DialogState::Cancelled => {
self.information_panel.forget_all_stored_images(ui);
}
_ => {}
}

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)))
}),
)
}
Binary file added media/examples/information_view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,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<f32>,

/// If the sidebar with the shortcut directories such as
/// “Home”, “Documents” etc. should be visible.
pub show_left_panel: bool,
Expand Down Expand Up @@ -260,6 +263,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,
Expand Down
34 changes: 29 additions & 5 deletions src/data/directory_content.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
use crate::config::{FileDialogConfig, FileFilter};
use egui::mutex::Mutex;
use std::path::{Path, PathBuf};
use std::sync::{mpsc, Arc};
use std::time::SystemTime;
use std::{fs, io, thread};

use egui::mutex::Mutex;

use crate::config::{FileDialogConfig, FileFilter};

/// Contains the metadata of a directory item.
#[derive(Debug, Default, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Metadata {
pub size: Option<u64>,
pub last_modified: Option<SystemTime>,
pub created: Option<SystemTime>,
pub file_type: Option<String>,
}

/// Contains the information of a directory item.
///
/// This struct is mainly there so that the metadata can be loaded once and not that
/// This struct is mainly there so that the information and metadata can be loaded once and not that
/// a request has to be sent to the OS every frame using, for example, `path.is_file()`.
#[derive(Debug, Default, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct DirectoryEntry {
path: PathBuf,
metadata: Metadata,
is_directory: bool,
is_system_file: bool,
icon: String,
Expand All @@ -25,15 +34,30 @@ pub struct DirectoryEntry {
impl DirectoryEntry {
/// Creates a new directory entry from a path
pub fn from_path(config: &FileDialogConfig, path: &Path) -> Self {
let mut metadata = Metadata::default();

if let Ok(md) = fs::metadata(path) {
metadata.size = Some(md.len());
metadata.last_modified = md.modified().ok();
metadata.created = md.created().ok();
metadata.file_type = Some(format!("{:?}", md.file_type()));
}

Self {
path: path.to_path_buf(),
metadata,
is_directory: path.is_dir(),
is_system_file: !path.is_dir() && !path.is_file(),
icon: gen_path_icon(config, path),
selected: false,
}
}

/// Returns the metadata of the directory entry.
pub const fn metadata(&self) -> &Metadata {
&self.metadata
}

/// Checks if the path of the current directory entry matches the other directory entry.
pub fn path_eq(&self, other: &Self) -> bool {
other.as_path() == self.as_path()
Expand Down
1 change: 1 addition & 0 deletions src/data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ mod disks;
pub use disks::{Disk, Disks};

mod user_directories;

pub use user_directories::UserDirectories;
28 changes: 23 additions & 5 deletions src/file_dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,16 @@ impl FileDialog {
self
}

/// Sets the width of the right panel.
pub fn set_right_panel_width(&mut self, width: f32) {
self.config.right_panel_width = Some(width);
}

/// Clears the width of the right panel by setting it to None.
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:
Expand Down Expand Up @@ -1168,6 +1178,11 @@ impl FileDialog {
pub fn state(&self) -> DialogState {
self.state.clone()
}

/// Get the window Id
pub const fn get_window_id(&self) -> egui::Id {
self.window_id
}
}

/// UI methods
Expand Down Expand Up @@ -1213,13 +1228,16 @@ impl FileDialog {

// 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"))
fluxxcode marked this conversation as resolved.
Show resolved Hide resolved
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.default_width(width);
}
right_panel.show_inside(ui, |ui| {
f(ui, self);
});
}

egui::TopBottomPanel::bottom(self.window_id.with("bottom_panel"))
Expand Down
Loading
Loading