Skip to content

Commit

Permalink
move helper functions to utils.rs
Browse files Browse the repository at this point in the history
  • Loading branch information
hacknus committed Dec 30, 2024
1 parent 6685bf2 commit 1a2e081
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 147 deletions.
129 changes: 9 additions & 120 deletions src/file_dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@ 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};
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)]
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -2530,17 +2528,17 @@ 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;

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()
};
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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<Local> = 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::<String>()
)
}
}

/// Keybindings
Expand Down
29 changes: 2 additions & 27 deletions src/information_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Box<dyn FnMut(&mut Ui, &InfoPanelEntry)>>;
type SupportedPreviewImagesMap =
Expand Down Expand Up @@ -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")
}
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
142 changes: 142 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -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<Local> = 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::<String>()
)
}

/// 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")
}
}

0 comments on commit 1a2e081

Please sign in to comment.