From f4a62c9c32ad23256dca7ea9793172b25d6de130 Mon Sep 17 00:00:00 2001 From: WanderingPenwing Date: Fri, 22 Mar 2024 18:01:43 +0100 Subject: [PATCH] fixed zoom and tree, + working with nix --- project/calcifer.nix | 29 ++ calcifer.project => project/calcifer.project | 0 shell.nix | 38 ++ src/core/app.rs | 370 ++++++++++--------- src/core/state.rs | 1 + src/core/ui.rs | 2 +- src/main.rs | 13 +- src/panels/file_tree.rs | 248 ++++++------- 8 files changed, 391 insertions(+), 310 deletions(-) create mode 100644 project/calcifer.nix rename calcifer.project => project/calcifer.project (100%) create mode 100644 shell.nix diff --git a/project/calcifer.nix b/project/calcifer.nix new file mode 100644 index 0000000..4a4dd83 --- /dev/null +++ b/project/calcifer.nix @@ -0,0 +1,29 @@ +{ pkgs ? import {} }: + +pkgs.stdenv.mkDerivation rec { + pname = "Calcifer"; + version = "1.0"; + src = pkgs.fetchFromGitHub { + owner = "WanderingPenwing"; + repo = "Calcifer"; + rev = "v${version}"; + sha256 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + }; + + buildInputs = [ pkgs.rustc pkgs.cargo ]; + + nativeBuildInputs = [ pkgs.pkg-config ]; + + cargoSha256 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + + installPhase = '' + cargo install --path . --root $out + ''; + + meta = with pkgs.stdenv.lib; { + description = "MyApp - A simple desktop application written in Rust"; + homepage = "https://github.com/your-github-username/myapp"; + license = licenses.mit; + maintainers = [ maintainers.your-name ];s + }; +} \ No newline at end of file diff --git a/calcifer.project b/project/calcifer.project similarity index 100% rename from calcifer.project rename to project/calcifer.project diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..3f9454d --- /dev/null +++ b/shell.nix @@ -0,0 +1,38 @@ +{ pkgs ? import { overlays = [ (import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz)) ]; },}: +with pkgs; + +mkShell { + nativeBuildInputs = with xorg; [ + libxcb + libXcursor + libXrandr + libXi + pkg-config + ] ++ [ + cargo + rustc + atk + gdk-pixbuf + webkitgtk + glib + libGL + libGLU + libxkbcommon + gtk3-x11 + gnome.zenity + ]; + buildInputs = [ + latest.rustChannels.stable.rust + xorg.libX11 + wayland + libxkbcommon + python3Packages.virtualenv + python3Packages.plyer + python3Packages.pygobject3 + python3Packages.pillow + ]; + + shellHook = '' + export LD_LIBRARY_PATH=/run/opengl-driver/lib/:${lib.makeLibraryPath ([libGL libGLU libxkbcommon])} + ''; +} diff --git a/src/core/app.rs b/src/core/app.rs index 4d09403..9e96d8b 100644 --- a/src/core/app.rs +++ b/src/core/app.rs @@ -11,215 +11,221 @@ use crate::Calcifer; use crate::TIME_LABELS; impl Calcifer { - pub fn handle_confirm(&mut self) { - if self.close_tab_confirm.proceed { - self.close_tab_confirm.close(); - self.delete_tab(self.tab_to_close); - } + pub fn handle_confirm(&mut self) { + if self.close_tab_confirm.proceed { + self.close_tab_confirm.close(); + self.delete_tab(self.tab_to_close); + } - if self.refresh_confirm.proceed { - self.refresh_confirm.close(); - self.tabs[self.selected_tab].refresh(); - } - } + if self.refresh_confirm.proceed { + self.refresh_confirm.close(); + self.tabs[self.selected_tab].refresh(); + } + } - pub fn save_tab(&self) -> Option { - if self.tabs[self.selected_tab] - .path - .file_name() - .map_or(true, |name| name.to_string_lossy() == "untitled") - { - self.save_tab_as() - } else { - if let Err(err) = fs::write( - &self.tabs[self.selected_tab].path, - &self.tabs[self.selected_tab].code, - ) { - eprintln!("Error writing file: {}", err); - return None; - } - Some(self.tabs[self.selected_tab].path.clone()) - } - } + pub fn save_tab(&self) -> Option { + if self.tabs[self.selected_tab] + .path + .file_name() + .map_or(true, |name| name.to_string_lossy() == "untitled") + { + self.save_tab_as() + } else { + if let Err(err) = fs::write( + &self.tabs[self.selected_tab].path, + &self.tabs[self.selected_tab].code, + ) { + eprintln!("Error writing file: {}", err); + return None; + } + Some(self.tabs[self.selected_tab].path.clone()) + } + } - pub fn save_tab_as(&self) -> Option { - if let Some(path) = rfd::FileDialog::new() - .set_directory(self.home.as_path()) - .save_file() - { - if let Err(err) = fs::write(&path, &self.tabs[self.selected_tab].code) { - eprintln!("Error writing file: {}", err); - return None; - } - return Some(path); - } - None - } + pub fn save_tab_as(&self) -> Option { + if let Some(path) = rfd::FileDialog::new() + .set_directory(self.home.as_path()) + .save_file() + { + if let Err(err) = fs::write(&path, &self.tabs[self.selected_tab].code) { + eprintln!("Error writing file: {}", err); + return None; + } + return Some(path); + } + None + } - pub fn handle_save_file(&mut self, path_option: Option) { - if let Some(path) = path_option { - println!("File saved successfully at: {:?}", path); - self.tabs[self.selected_tab].path = path; - self.tabs[self.selected_tab].saved = true; - } else { - println!("File save failed."); - } - } + pub fn handle_save_file(&mut self, path_option: Option) { + if let Some(path) = path_option { + println!("File saved successfully at: {:?}", path); + self.tabs[self.selected_tab].path = path; + self.tabs[self.selected_tab].saved = true; + } else { + println!("File save failed."); + } + } - pub fn from_app_state(app_state: core::AppState) -> Self { - let mut new = Self { - theme: DEFAULT_THEMES[min(app_state.theme, DEFAULT_THEMES.len() - 1)], - tabs: Vec::new(), - settings_menu: sub_windows::SettingsWindow::new(DEFAULT_THEMES[app_state.theme]), - ..Default::default() - }; + pub fn from_app_state(app_state: core::AppState) -> Self { + let mut new = Self { + theme: DEFAULT_THEMES[min(app_state.theme, DEFAULT_THEMES.len() - 1)], + tabs: Vec::new(), + settings_menu: sub_windows::SettingsWindow::new(DEFAULT_THEMES[app_state.theme]), + ..Default::default() + }; - for path in app_state.tabs { - if !path - .file_name() - .map_or(true, |name| name.to_string_lossy() == "untitled") - { - new.open_file(Some(&path)); - } - } + println!("zoom : {}", app_state.zoom.clone()); + if app_state.zoom != 0.0 { + new.zoom = app_state.zoom; + } - if new.tabs == vec![] { - new.open_file(None); - } + for path in app_state.tabs { + if !path + .file_name() + .map_or(true, |name| name.to_string_lossy() == "untitled") + { + new.open_file(Some(&path)); + } + } - new - } + if new.tabs == vec![] { + new.open_file(None); + } - pub fn save_state(&self) { - let mut state_theme: usize = 0; - if let Some(theme) = DEFAULT_THEMES.iter().position(|&r| r == self.theme) { - state_theme = theme; - } + new + } - let mut state_tabs = vec![]; + pub fn save_state(&self) { + let mut state_theme: usize = 0; + if let Some(theme) = DEFAULT_THEMES.iter().position(|&r| r == self.theme) { + state_theme = theme; + } - for tab in &self.tabs { - state_tabs.push(tab.path.clone()); - } - let app_state = core::AppState { - tabs: state_tabs, - theme: state_theme, - }; + let mut state_tabs = vec![]; - let _ = core::save_state(&app_state, save_path().as_path()); - } + for tab in &self.tabs { + state_tabs.push(tab.path.clone()); + } + let app_state = core::AppState { + tabs: state_tabs, + theme: state_theme, + zoom: self.zoom, + }; - pub fn move_through_tabs(&mut self, forward: bool) { - let new_index = if forward { - (self.selected_tab + 1) % self.tabs.len() - } else { - self.selected_tab - .checked_sub(1) - .unwrap_or(self.tabs.len() - 1) - }; - self.selected_tab = new_index; - } + let _ = core::save_state(&app_state, save_path().as_path()); + } - pub fn open_file(&mut self, path_option: Option<&Path>) { - if let Some(path) = path_option { - for (index, tab) in self.tabs.clone().iter().enumerate() { - if tab.path == path { - self.selected_tab = index; - return; - } - } - } - if let Some(path) = path_option { - self.tabs.push(panels::Tab::new(path.to_path_buf())); - } else { - self.tabs.push(panels::Tab::default()); - } - self.selected_tab = self.tabs.len() - 1; - } + pub fn move_through_tabs(&mut self, forward: bool) { + let new_index = if forward { + (self.selected_tab + 1) % self.tabs.len() + } else { + self.selected_tab + .checked_sub(1) + .unwrap_or(self.tabs.len() - 1) + }; + self.selected_tab = new_index; + } - pub fn delete_tab(&mut self, index: usize) { - self.tabs.remove(index); - self.selected_tab = min(index, self.tabs.len() - 1); - } + pub fn open_file(&mut self, path_option: Option<&Path>) { + if let Some(path) = path_option { + for (index, tab) in self.tabs.clone().iter().enumerate() { + if tab.path == path { + self.selected_tab = index; + return; + } + } + } + if let Some(path) = path_option { + self.tabs.push(panels::Tab::new(path.to_path_buf())); + } else { + self.tabs.push(panels::Tab::default()); + } + self.selected_tab = self.tabs.len() - 1; + } - pub fn toggle(&self, ui: &mut egui::Ui, display: bool, title: &str) -> bool { - let bg_color: Color32; - let text_color: Color32; + pub fn delete_tab(&mut self, index: usize) { + self.tabs.remove(index); + self.selected_tab = min(index, self.tabs.len() - 1); + } - if display { - bg_color = hex_str_to_color(self.theme.functions); - text_color = hex_str_to_color(self.theme.bg); - } else { - bg_color = hex_str_to_color(self.theme.bg); - text_color = hex_str_to_color(self.theme.literals); - }; + pub fn toggle(&self, ui: &mut egui::Ui, display: bool, title: &str) -> bool { + let bg_color: Color32; + let text_color: Color32; - ui.style_mut().visuals.override_text_color = Some(text_color); + if display { + bg_color = hex_str_to_color(self.theme.functions); + text_color = hex_str_to_color(self.theme.bg); + } else { + bg_color = hex_str_to_color(self.theme.bg); + text_color = hex_str_to_color(self.theme.literals); + }; - if ui.add(egui::Button::new(title).fill(bg_color)).clicked() { - return !display; - } - ui.style_mut().visuals.override_text_color = None; + ui.style_mut().visuals.override_text_color = Some(text_color); - display - } + if ui.add(egui::Button::new(title).fill(bg_color)).clicked() { + return !display; + } + ui.style_mut().visuals.override_text_color = None; - pub fn profiler(&self) -> String { - if !self.profiler_visible { - return "".to_string(); - } - let combined_string: Vec = TIME_LABELS - .into_iter() - .zip(self.time_watch.clone()) - .map(|(s, v)| format!("{} : {:.1} ms", s, v)) - .collect(); + display + } - let mut result = combined_string.join(" ; "); - result.push_str(&format!( - " total : {:.1} ms", - self.time_watch.clone().iter().sum::() - )); - result - } + pub fn profiler(&self) -> String { + if !self.profiler_visible { + return "".to_string(); + } + let combined_string: Vec = TIME_LABELS + .into_iter() + .zip(self.time_watch.clone()) + .map(|(s, v)| format!("{} : {:.1} ms", s, v)) + .collect(); - pub fn list_files( - &mut self, - ui: &mut egui::Ui, - file: &panels::FileEntry, - n_files: &mut usize, - ) -> bool { - *n_files += 1; + let mut result = combined_string.join(" ; "); + result.push_str(&format!( + " total : {:.1} ms", + self.time_watch.clone().iter().sum::() + )); + result + } - if let Some(folder_content) = &file.folder_content { - let mut check_for_update: bool = false; - let collapsing_response = egui::CollapsingHeader::new(file.name.clone()) - .default_open(self.tree_dir_opened.contains(&file.name)) - .show(ui, |ui| { - if !self.tree_dir_opened.contains(&file.name) { - return; - } - for deeper_file in folder_content { - if self.list_files(ui, deeper_file, n_files) { - check_for_update = true; - } - } - }); - if collapsing_response.fully_closed() { - self.tree_dir_opened.retain(|s| s != &file.name); - } else if !self.tree_dir_opened.contains(&file.name) { - self.tree_dir_opened.push(file.name.clone()); - return !file.content_checked; - } - return check_for_update; - } else if ui.button(&file.name).clicked() { - self.open_file(Some(&file.path)); - } + pub fn list_files( + &mut self, + ui: &mut egui::Ui, + file: &panels::FileEntry, + n_files: &mut usize, + ) -> bool { + *n_files += 1; - false - } + if let Some(folder_content) = &file.folder_content { + let mut check_for_update: bool = false; + let collapsing_response = egui::CollapsingHeader::new(file.name.clone()) + .default_open(self.tree_dir_opened.contains(&file.name)) + .show(ui, |ui| { + if !self.tree_dir_opened.contains(&file.name) { + return; + } + for deeper_file in folder_content { + if self.list_files(ui, deeper_file, n_files) { + check_for_update = true; + } + } + }); + if collapsing_response.fully_closed() { + self.tree_dir_opened.retain(|s| s != &file.name); + } else if !self.tree_dir_opened.contains(&file.name) { + self.tree_dir_opened.push(file.name.clone()); + return !file.content_checked; + } + return check_for_update; + } else if ui.button(&file.name).clicked() { + self.open_file(Some(&file.path)); + } + + false + } } #[allow(clippy::unnecessary_lazy_evaluations)] pub fn hex_str_to_color(hex_str: &str) -> Color32 { - Color32::from_hex(hex_str).unwrap_or_else(|_| Color32::BLACK) + Color32::from_hex(hex_str).unwrap_or_else(|_| Color32::BLACK) } diff --git a/src/core/state.rs b/src/core/state.rs index 88292b9..2d8ef34 100644 --- a/src/core/state.rs +++ b/src/core/state.rs @@ -14,6 +14,7 @@ use serde::Deserialize; pub struct AppState { pub tabs: Vec, pub theme: usize, + pub zoom: f32, } pub fn save_state(state: &AppState, file_path: &Path) -> Result<(), std::io::Error> { diff --git a/src/core/ui.rs b/src/core/ui.rs index abe9abd..29613e2 100644 --- a/src/core/ui.rs +++ b/src/core/ui.rs @@ -191,7 +191,7 @@ impl Calcifer { core::hex_str_to_color(self.theme.bg), ); StripBuilder::new(ui) - .sizes(Size::remainder(), max(10, self.tabs.len() + 1)) + .sizes(Size::remainder(), max(6, self.tabs.len() + 1)) .sense(egui::Sense::click()) .horizontal(|mut strip| { for (index, tab) in self.tabs.clone().iter().enumerate() { diff --git a/src/main.rs b/src/main.rs index 36f1fae..be7bb9e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ const TITLE: &str = " debug"; #[cfg(not(debug_assertions))] const TITLE: &str = ""; -const ALLOWED_FILE_EXTENSIONS: [&str; 13] = ["", "rs", "toml", "txt", "project", "sh", "md", "html", "js", "css", "php", "py", "kv"]; +//const ALLOWED_FILE_EXTENSIONS: [&str; 14] = ["", "rs", "toml", "txt", "project", "sh", "md", "html", "js", "css", "php", "py", "kv", "nix"]; const PROJECT_EXTENSION: &str = "project"; const TERMINAL_HEIGHT: f32 = 200.0; const TERMINAL_RANGE: Range = 100.0..600.0; @@ -25,6 +25,7 @@ const RED: egui::Color32 = egui::Color32::from_rgb(235, 108, 99); const TIME_LABELS: [&str; 7] = [ "input", "settings", "tree", "terminal", "tabs", "content", "windows", ]; +const ZOOM_FACTOR: f32 = 1.1; const MAX_FPS: f32 = 30.0; const DISPLAY_PATH_DEPTH: usize = 3; const MAX_PROJECT_COLUMNS: usize = 8; @@ -67,6 +68,7 @@ struct Calcifer { theme: editor::ColorTheme, font_size: f32, + zoom: f32, project_content: panels::Project, @@ -104,6 +106,7 @@ impl Default for Calcifer { theme: editor::themes::DEFAULT_THEMES[0], font_size: 14.0, + zoom: 1.0, project_content: panels::Project::new(), @@ -166,6 +169,10 @@ impl eframe::App for Calcifer { .into(); ctx.set_style(style); + if ctx.zoom_factor() != self.zoom { + ctx.set_zoom_factor(self.zoom); + } + if ctx.input(|i| i.key_pressed(egui::Key::R) && i.modifiers.ctrl) && !self.refresh_confirm.visible { @@ -204,11 +211,11 @@ impl eframe::App for Calcifer { } if ctx.input(|i| i.zoom_delta() > 1.0) { - self.font_size = (self.font_size * 1.1).min(30.0); + self.zoom = (self.zoom*ZOOM_FACTOR).min(10.0); } if ctx.input(|i| i.zoom_delta() < 1.0) { - self.font_size = (self.font_size / 1.1).max(10.0); + self.zoom = (self.zoom/ZOOM_FACTOR).max(0.1); } if ctx.input(|i| i.key_pressed(egui::Key::F) && i.modifiers.ctrl) { diff --git a/src/panels/file_tree.rs b/src/panels/file_tree.rs index dfdd99c..8c84723 100644 --- a/src/panels/file_tree.rs +++ b/src/panels/file_tree.rs @@ -1,154 +1,154 @@ use std::{ - cmp::Ordering, - ffi::OsStr, - fs, io, - path::{Path, PathBuf}, + cmp::Ordering, + ffi::OsStr, + fs, io, + path::{Path, PathBuf}, }; -use crate::ALLOWED_FILE_EXTENSIONS; +//use crate::ALLOWED_FILE_EXTENSIONS; #[derive(Clone)] pub struct FileEntry { - pub name: String, - pub path: PathBuf, - pub folder_content: Option>, - pub content_checked: bool, + pub name: String, + pub path: PathBuf, + pub folder_content: Option>, + pub content_checked: bool, } impl FileEntry { - pub fn new_entry(name: String, path: PathBuf) -> Self { - Self { - name, - path, - folder_content: None, - content_checked: true, - } - } - pub fn end_of_branch(name: String, path: PathBuf) -> Self { - Self { - name, - path, - folder_content: Some(vec![]), - content_checked: false, - } - } + pub fn new_entry(name: String, path: PathBuf) -> Self { + Self { + name, + path, + folder_content: None, + content_checked: true, + } + } + pub fn end_of_branch(name: String, path: PathBuf) -> Self { + Self { + name, + path, + folder_content: Some(vec![]), + content_checked: false, + } + } } pub fn update_file_tree(file: FileEntry, opened_dirs: Vec) -> FileEntry { - if opened_dirs.contains(&file.name) { - if let Some(folder_content) = &file.folder_content { - if !file.content_checked { - return generate_folder_entry(&file.path); - } - let updated_content: Vec = folder_content - .iter() - .map(|entry| update_file_tree(entry.clone(), opened_dirs.clone())) - .collect(); - FileEntry { - name: file.name, - path: file.path, - folder_content: Some(updated_content), - content_checked: true, - } - } else { - file - } - } else { - file - } + if opened_dirs.contains(&file.name) { + if let Some(folder_content) = &file.folder_content { + if !file.content_checked { + return generate_folder_entry(&file.path); + } + let updated_content: Vec = folder_content + .iter() + .map(|entry| update_file_tree(entry.clone(), opened_dirs.clone())) + .collect(); + FileEntry { + name: file.name, + path: file.path, + folder_content: Some(updated_content), + content_checked: true, + } + } else { + file + } + } else { + file + } } pub fn generate_folder_entry(path: &Path) -> FileEntry { - if let Some(file_name) = path.file_name() { - let name = file_name.to_string_lossy().into_owned(); + if let Some(file_name) = path.file_name() { + let name = file_name.to_string_lossy().into_owned(); - match fs::read_dir(path) { - Err(err) => FileEntry::new_entry( - format!("Error reading directory: {}", err), - path.to_path_buf(), - ), - Ok(entries) => { - let mut paths: Vec> = entries - .map(|r| r.map_err(|e| io::Error::new(io::ErrorKind::Other, e))) - .collect(); + match fs::read_dir(path) { + Err(err) => FileEntry::new_entry( + format!("Error reading directory: {}", err), + path.to_path_buf(), + ), + Ok(entries) => { + let mut paths: Vec> = entries + .map(|r| r.map_err(|e| io::Error::new(io::ErrorKind::Other, e))) + .collect(); - paths.sort_by(|a, b| match (a, b) { - (Ok(entry_a), Ok(entry_b)) => sort_directories_first(entry_a, entry_b), - (Err(_), Ok(_)) => std::cmp::Ordering::Greater, - (Ok(_), Err(_)) => std::cmp::Ordering::Less, - (Err(_), Err(_)) => std::cmp::Ordering::Equal, - }); + paths.sort_by(|a, b| match (a, b) { + (Ok(entry_a), Ok(entry_b)) => sort_directories_first(entry_a, entry_b), + (Err(_), Ok(_)) => std::cmp::Ordering::Greater, + (Ok(_), Err(_)) => std::cmp::Ordering::Less, + (Err(_), Err(_)) => std::cmp::Ordering::Equal, + }); - let mut folder_content = Vec::new(); + let mut folder_content = Vec::new(); - for result in paths { - match result { - Ok(entry) => { - if let Some(file) = generate_entry(&entry.path()) { - folder_content.push(file); - } - } - Err(err) => { - folder_content.push(FileEntry::new_entry( - format!("Error reading entry: {}", err), - path.to_path_buf(), - )); - } - } - } + for result in paths { + match result { + Ok(entry) => { + if let Some(file) = generate_entry(&entry.path()) { + folder_content.push(file); + } + } + Err(err) => { + folder_content.push(FileEntry::new_entry( + format!("Error reading entry: {}", err), + path.to_path_buf(), + )); + } + } + } - FileEntry { - name, - path: path.to_path_buf(), - folder_content: Some(folder_content), - content_checked: true, - } - } - } - } else { - FileEntry::new_entry( - "Error reading directory name".to_string(), - path.to_path_buf(), - ) - } + FileEntry { + name, + path: path.to_path_buf(), + folder_content: Some(folder_content), + content_checked: true, + } + } + } + } else { + FileEntry::new_entry( + "Error reading directory name".to_string(), + path.to_path_buf(), + ) + } } fn generate_entry(path: &Path) -> Option { - if let Some(file_name) = path.file_name() { - if file_name.to_string_lossy().starts_with('.') { - return None; - } - let extension = path.extension().and_then(|ext| ext.to_str()); - if !ALLOWED_FILE_EXTENSIONS.contains(&extension.unwrap_or_default()) { - return None; - } - } else { - return None; - } + if let Some(file_name) = path.file_name() { + if file_name.to_string_lossy().starts_with('.') { + return None; + } +// let extension = path.extension().and_then(|ext| ext.to_str()); +// if !ALLOWED_FILE_EXTENSIONS.contains(&extension.unwrap_or_default()) { +// return None; +// } + } else { + return None; + } - let name = path - .file_name() - .unwrap_or_else(|| OsStr::new("")) - .to_string_lossy() - .into_owned(); + let name = path + .file_name() + .unwrap_or_else(|| OsStr::new("")) + .to_string_lossy() + .into_owned(); - if !path.is_dir() { - return Some(FileEntry::new_entry(name, path.to_path_buf())); - } - Some(FileEntry::end_of_branch(name, path.to_path_buf())) + if !path.is_dir() { + return Some(FileEntry::new_entry(name, path.to_path_buf())); + } + Some(FileEntry::end_of_branch(name, path.to_path_buf())) } fn sort_directories_first(a: &std::fs::DirEntry, b: &std::fs::DirEntry) -> Ordering { - let a_is_dir = a.path().is_dir(); - let b_is_dir = b.path().is_dir(); + let a_is_dir = a.path().is_dir(); + let b_is_dir = b.path().is_dir(); - // Directories come first, then files - if a_is_dir && !b_is_dir { - Ordering::Less - } else if !a_is_dir && b_is_dir { - Ordering::Greater - } else { - // Both are either directories or files, sort alphabetically - a.path().cmp(&b.path()) - } + // Directories come first, then files + if a_is_dir && !b_is_dir { + Ordering::Less + } else if !a_is_dir && b_is_dir { + Ordering::Greater + } else { + // Both are either directories or files, sort alphabetically + a.path().cmp(&b.path()) + } }