Local: on Windows the folder trees leave out hidden folders such as AppData

The Local trees left out folders whose names start with a dot, but on
Windows a folder is hidden by its attribute. Home's tree listed
AppData, and browsing a folder inside the temp folder opened Home's
tree down through AppData\Local\Temp, drawing one row for every folder
there on each frame. On a machine with 148,400 folders in Temp each
frame took about 4 s, and
tests_panels::local_photos_cannot_be_added_to_albums_until_they_join_my_photos
timed out on its ui.drag.

list_subfolders now also skips folders with FILE_ATTRIBUTE_HIDDEN set.
Test: panels::left::tests::subfolders_leave_out_hidden_folders.
This commit is contained in:
dotCooCoo
2026-10-26 03:03:39 -07:00
parent bb08a697a4
commit 1bb11ff321
+36 -1
View File
@@ -1040,7 +1040,7 @@ fn list_subfolders(path: &str) -> Vec<(String, String)> {
let mut v: Vec<(String, String)> = std::fs::read_dir(path)
.map(|rd| {
rd.flatten()
.filter(|e| e.file_type().is_ok_and(|t| t.is_dir()))
.filter(|e| e.file_type().is_ok_and(|t| t.is_dir()) && !hidden_attribute(e))
.filter_map(|e| {
let name = e.file_name().to_string_lossy().to_string();
(!name.starts_with('.')).then(|| (name, e.path().to_string_lossy().to_string()))
@@ -1052,6 +1052,20 @@ fn list_subfolders(path: &str) -> Vec<(String, String)> {
v
}
/// On Windows a folder is hidden by an attribute (AppData in the home folder is), not by a
/// leading dot.
#[cfg(windows)]
fn hidden_attribute(e: &std::fs::DirEntry) -> bool {
use std::os::windows::fs::MetadataExt;
const FILE_ATTRIBUTE_HIDDEN: u32 = 0x2;
e.metadata().is_ok_and(|m| m.file_attributes() & FILE_ATTRIBUTE_HIDDEN != 0)
}
#[cfg(not(windows))]
fn hidden_attribute(_: &std::fs::DirEntry) -> bool {
false
}
/// How many [`fs_cached`] answers for `ctx` are being worked out right now. Rows appear (and
/// the sidebar below them moves) when they land, so the headless driver counts them as pending
/// work and waits for them before acting on widget positions.
@@ -2370,6 +2384,27 @@ mod tests {
assert_eq!((names(&l.places), l.owner), (vec!["Pictures"], Some(0)));
}
/// A folder tree leaves out hidden subfolders: a leading dot everywhere, and on Windows the
/// hidden attribute, so Home's tree does not list AppData and open down into its temp folder.
#[test]
fn subfolders_leave_out_hidden_folders() {
let dir = std::env::temp_dir().join(format!("lc-ui-hidden-subfolders-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
for name in ["Shown", ".dot", "Hidden"] {
std::fs::create_dir_all(dir.join(name)).unwrap();
}
let expected = if cfg!(windows) {
let status = std::process::Command::new("attrib").arg("+h").arg(dir.join("Hidden")).status().unwrap();
assert!(status.success(), "attrib +h: {status}");
vec!["Shown"]
} else {
vec!["Hidden", "Shown"]
};
let listed = super::list_subfolders(&dir.to_string_lossy());
let _ = std::fs::remove_dir_all(&dir);
assert_eq!(listed.iter().map(|(n, _)| n.as_str()).collect::<Vec<_>>(), expected);
}
#[test]
fn a_local_folder_lines_up_with_the_rows_above_and_its_triangle_stays_left_of_the_icon() {
use crate::Services;