From 1a2e08196eb072d6d5e81c1457a8801303f02602 Mon Sep 17 00:00:00 2001 From: hacknus Date: Mon, 30 Dec 2024 12:27:04 +0100 Subject: [PATCH] move helper functions to `utils.rs` --- src/file_dialog.rs | 129 +++-------------------------------- src/information_panel.rs | 29 +------- src/lib.rs | 1 + src/utils.rs | 142 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 147 deletions(-) create mode 100644 src/utils.rs diff --git a/src/file_dialog.rs b/src/file_dialog.rs index 735ff82e..a2a9d485 100644 --- a/src/file_dialog.rs +++ b/src/file_dialog.rs @@ -6,9 +6,8 @@ use crate::create_directory_dialog::CreateDirectoryDialog; use crate::data::{ DirectoryContent, DirectoryContentState, DirectoryEntry, Disk, Disks, UserDirectories, }; -use crate::information_panel::format_bytes; use crate::modals::{FileDialogModal, ModalAction, ModalState, OverwriteFileModal}; -use chrono::{DateTime, Local}; +use crate::utils::{calc_text_width, format_bytes, truncate_date, truncate_filename}; use egui::text::{CCursor, CCursorRange}; use egui::TextStyle; use egui_extras::{Column, TableBuilder, TableRow}; @@ -16,7 +15,6 @@ use std::cmp::PartialEq; use std::fmt::{Debug, Display, Formatter}; use std::io; use std::path::{Path, PathBuf}; -use std::time::SystemTime; /// Enum to set what we sort the directory entry by #[derive(PartialEq, Eq, Debug)] @@ -1989,12 +1987,12 @@ impl FileDialog { // Calculate the width of the action buttons let label_submit_width = match self.mode { DialogMode::SelectDirectory | DialogMode::SelectFile | DialogMode::SelectMultiple => { - Self::calc_text_width(ui, &self.config.labels.open_button) + calc_text_width(ui, &self.config.labels.open_button) } - DialogMode::SaveFile => Self::calc_text_width(ui, &self.config.labels.save_button), + DialogMode::SaveFile => calc_text_width(ui, &self.config.labels.save_button), }; - let mut btn_width = Self::calc_text_width(ui, &self.config.labels.cancel_button); + let mut btn_width = calc_text_width(ui, &self.config.labels.cancel_button); if label_submit_width > btn_width { btn_width = label_submit_width; } @@ -2530,9 +2528,9 @@ impl FileDialog { row.set_selected(primary_selected || selected); row.col(|ui| { - let icons_width = Self::calc_text_width(ui, &icons); + let icons_width = calc_text_width(ui, &icons); - let text_width = Self::calc_text_width(ui, file_name); + let text_width = calc_text_width(ui, file_name); // Calc available width for the file name and include a small margin let available_width = ui.available_width() - icons_width - 15.0; @@ -2540,7 +2538,7 @@ impl FileDialog { truncate = self.config.truncate_filenames && available_width < text_width; let text = if truncate { - Self::truncate_filename(ui, item, available_width) + truncate_filename(ui, item, available_width) } else { file_name.to_owned() }; @@ -2567,7 +2565,7 @@ impl FileDialog { // Calc available width for the file name and include a small margin let available_width = ui.available_width() - 10.0; - let text = Self::truncate_date(ui, created, available_width); + let text = truncate_date(ui, created, available_width); ui.add(egui::Label::new(text).selectable(false)); } else { @@ -2580,7 +2578,7 @@ impl FileDialog { // Calc available width for the file name and include a small margin let available_width = ui.available_width() - 10.0; - let text = Self::truncate_date(ui, last_modified, available_width); + let text = truncate_date(ui, last_modified, available_width); ui.add(egui::Label::new(text).selectable(false)); } else { @@ -2723,115 +2721,6 @@ impl FileDialog { state.store(&re.ctx, re.id); } } - - /// Calculates the width of a single char. - fn calc_char_width(ui: &egui::Ui, char: char) -> f32 { - ui.fonts(|f| f.glyph_width(&egui::TextStyle::Body.resolve(ui.style()), char)) - } - - /// Calculates the width of the specified text using the current font configuration. - /// Does not take new lines or text breaks into account! - fn calc_text_width(ui: &egui::Ui, text: &str) -> f32 { - let mut width = 0.0; - - for char in text.chars() { - width += Self::calc_char_width(ui, char); - } - - width - } - - fn truncate_date(ui: &egui::Ui, date: SystemTime, max_length: f32) -> String { - let date: DateTime = date.into(); - let today = Local::now().date_naive(); // NaiveDate for today - let yesterday = today.pred_opt().map_or(today, |day| day); // NaiveDate for yesterday - - let text = if date.date_naive() == today { - date.format("Today, %H:%M").to_string() - } else if date.date_naive() == yesterday { - date.format("Yesterday, %H:%M").to_string() - } else { - date.format("%d.%m.%Y, %H:%M").to_string() - }; - - let text_width = Self::calc_text_width(ui, &text); - - if max_length <= text_width { - if date.date_naive() == today { - date.format("%H:%M").to_string() - } else if date.date_naive() == yesterday { - "Yesterday".to_string() - } else { - date.format("%d.%m.%y").to_string() - } - } else { - text - } - } - - fn truncate_filename(ui: &egui::Ui, item: &DirectoryEntry, max_length: f32) -> String { - const TRUNCATE_STR: &str = "..."; - - let path = item.as_path(); - - let file_stem = if path.is_file() { - path.file_stem().and_then(|f| f.to_str()).unwrap_or("") - } else { - item.file_name() - }; - - let extension = if path.is_file() { - path.extension().map_or(String::new(), |ext| { - format!(".{}", ext.to_str().unwrap_or("")) - }) - } else { - String::new() - }; - - let extension_width = Self::calc_text_width(ui, &extension); - let reserved = extension_width + Self::calc_text_width(ui, TRUNCATE_STR); - - if max_length <= reserved { - return format!("{TRUNCATE_STR}{extension}"); - } - - let mut width = reserved; - let mut front = String::new(); - let mut back = String::new(); - - for (i, char) in file_stem.chars().enumerate() { - let w = Self::calc_char_width(ui, char); - - if width + w > max_length { - break; - } - - front.push(char); - width += w; - - let back_index = file_stem.len() - i - 1; - - if back_index <= i { - break; - } - - if let Some(char) = file_stem.chars().nth(back_index) { - let w = Self::calc_char_width(ui, char); - - if width + w > max_length { - break; - } - - back.push(char); - width += w; - } - } - - format!( - "{front}{TRUNCATE_STR}{}{extension}", - back.chars().rev().collect::() - ) - } } /// Keybindings diff --git a/src/information_panel.rs b/src/information_panel.rs index 9fb118df..80b5d02c 100644 --- a/src/information_panel.rs +++ b/src/information_panel.rs @@ -8,6 +8,7 @@ use indexmap::{IndexMap, IndexSet}; use std::fs::File; use std::io::{self, Read}; use std::path::PathBuf; +use crate::utils::format_bytes; type SupportedPreviewFilesMap = HashMap>; type SupportedPreviewImagesMap = @@ -430,30 +431,4 @@ impl InformationPanel { }); }); } -} - -/// Formats a file size (in bytes) into a human-readable string (e.g., KB, MB). -/// -/// # Arguments -/// - `bytes`: The file size in bytes. -/// -/// # Returns -/// A string representing the file size in an appropriate unit. -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!("{bytes} B") - } -} +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 0aa52518..99e79cdd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -201,6 +201,7 @@ mod file_dialog; /// Information panel showing the preview and metadata of the selected item pub mod information_panel; mod modals; +mod utils; pub use config::{ FileDialogConfig, FileDialogKeyBindings, FileDialogLabels, FileDialogStorage, IconFilter, diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 00000000..a507d571 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,142 @@ +use crate::DirectoryEntry; +use chrono::{DateTime, Local}; +use std::time::SystemTime; + +/// Calculates the width of a single char. +fn calc_char_width(ui: &egui::Ui, char: char) -> f32 { + ui.fonts(|f| f.glyph_width(&egui::TextStyle::Body.resolve(ui.style()), char)) +} + +/// Calculates the width of the specified text using the current font configuration. +/// Does not take new lines or text breaks into account! +pub fn calc_text_width(ui: &egui::Ui, text: &str) -> f32 { + let mut width = 0.0; + + for char in text.chars() { + width += calc_char_width(ui, char); + } + + width +} + +/// Truncates a date to a specified maximum length `max_length` +/// Returns the truncated date as a string +pub fn truncate_date(ui: &egui::Ui, date: SystemTime, max_length: f32) -> String { + let date: DateTime = date.into(); + let today = Local::now().date_naive(); // NaiveDate for today + let yesterday = today.pred_opt().map_or(today, |day| day); // NaiveDate for yesterday + + let text = if date.date_naive() == today { + date.format("Today, %H:%M").to_string() + } else if date.date_naive() == yesterday { + date.format("Yesterday, %H:%M").to_string() + } else { + date.format("%d.%m.%Y, %H:%M").to_string() + }; + + let text_width = calc_text_width(ui, &text); + + if max_length <= text_width { + if date.date_naive() == today { + date.format("%H:%M").to_string() + } else if date.date_naive() == yesterday { + "Yesterday".to_string() + } else { + date.format("%d.%m.%y").to_string() + } + } else { + text + } +} + +/// Truncates a date to a specified maximum length `max_length` +/// Returns the truncated filename as a string +pub fn truncate_filename(ui: &egui::Ui, item: &DirectoryEntry, max_length: f32) -> String { + const TRUNCATE_STR: &str = "..."; + + let path = item.as_path(); + + let file_stem = if path.is_file() { + path.file_stem().and_then(|f| f.to_str()).unwrap_or("") + } else { + item.file_name() + }; + + let extension = if path.is_file() { + path.extension().map_or(String::new(), |ext| { + format!(".{}", ext.to_str().unwrap_or("")) + }) + } else { + String::new() + }; + + let extension_width = calc_text_width(ui, &extension); + let reserved = extension_width + calc_text_width(ui, TRUNCATE_STR); + + if max_length <= reserved { + return format!("{TRUNCATE_STR}{extension}"); + } + + let mut width = reserved; + let mut front = String::new(); + let mut back = String::new(); + + for (i, char) in file_stem.chars().enumerate() { + let w = calc_char_width(ui, char); + + if width + w > max_length { + break; + } + + front.push(char); + width += w; + + let back_index = file_stem.len() - i - 1; + + if back_index <= i { + break; + } + + if let Some(char) = file_stem.chars().nth(back_index) { + let w = calc_char_width(ui, char); + + if width + w > max_length { + break; + } + + back.push(char); + width += w; + } + } + + format!( + "{front}{TRUNCATE_STR}{}{extension}", + back.chars().rev().collect::() + ) +} + +/// Formats a file size (in bytes) into a human-readable string (e.g., KB, MB). +/// +/// # Arguments +/// - `bytes`: The file size in bytes. +/// +/// # Returns +/// A string representing the file size in an appropriate unit. +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!("{bytes} B") + } +}