Skip to content

Commit f9f762e

Browse files
authored
Feature: Display hidden folders when clicking chevron on path bar (#18231)
1 parent e283536 commit f9f762e

File tree

1 file changed

+66
-9
lines changed

1 file changed

+66
-9
lines changed

src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -635,11 +635,18 @@ public void UpdateAdditionalActions()
635635
public async Task SetPathBoxDropDownFlyoutAsync(MenuFlyout flyout, PathBoxItem pathItem)
636636
{
637637
var nextPathItemTitle = PathComponents[PathComponents.IndexOf(pathItem) + 1].Title;
638-
IList<StorageFolderWithPath>? childFolders = null;
638+
var childFolders = GetSubfolders(pathItem.Path);
639639

640-
StorageFolderWithPath folder = await ContentPageContext.ShellPage.ShellViewModel.GetFolderWithPathFromPathAsync(pathItem.Path);
641-
if (folder is not null)
642-
childFolders = (await FilesystemTasks.Wrap(() => folder.GetFoldersWithPathAsync(string.Empty))).Result;
640+
// Fall back to StorageFolder API for non-filesystem paths (e.g. FTP)
641+
if (childFolders is null)
642+
{
643+
StorageFolderWithPath folder = await ContentPageContext.ShellPage.ShellViewModel.GetFolderWithPathFromPathAsync(pathItem.Path);
644+
if (folder is not null)
645+
{
646+
var result = (await FilesystemTasks.Wrap(() => folder.GetFoldersWithPathAsync(string.Empty))).Result;
647+
childFolders = result?.Select(f => (f.Item.Name, f.Path, false)).ToList();
648+
}
649+
}
643650

644651
flyout.Items?.Clear();
645652

@@ -663,28 +670,78 @@ public async Task SetPathBoxDropDownFlyoutAsync(MenuFlyout flyout, PathBoxItem p
663670
var workingPath =
664671
PathComponents[PathComponents.Count - 1].Path?.TrimEnd(Path.DirectorySeparatorChar);
665672

666-
foreach (var childFolder in childFolders)
673+
foreach (var (name, path, isHidden) in childFolders)
667674
{
668675
var flyoutItem = new MenuFlyoutItem
669676
{
670677
Icon = new FontIcon { Glyph = "\uE8B7" }, // Use font icon as placeholder
671-
Text = childFolder.Item.Name,
678+
Text = name,
679+
Opacity = isHidden ? Constants.UI.DimItemOpacity : 1.0,
672680
};
673681

674-
if (workingPath != childFolder.Path)
682+
if (workingPath != path)
675683
{
676684
flyoutItem.Click += (sender, args) =>
677685
{
678686
// Navigate to the directory
679-
ContentPageContext.ShellPage.NavigateToPath(childFolder.Path);
687+
ContentPageContext.ShellPage.NavigateToPath(path);
680688
};
681689
}
682690

683691
flyout.Items?.Add(flyoutItem);
684692

685693
// Start loading the thumbnail in the background
686-
_ = LoadFlyoutItemIconAsync(flyoutItem, childFolder.Path);
694+
_ = LoadFlyoutItemIconAsync(flyoutItem, path);
695+
}
696+
}
697+
698+
/// <summary>
699+
/// Enumerates subfolders using Win32 API, including hidden folders based on user settings.
700+
/// Returns null if the path cannot be enumerated with Win32.
701+
/// </summary>
702+
private List<(string Name, string Path, bool IsHidden)>? GetSubfolders(string parentPath)
703+
{
704+
IntPtr hFile = Win32PInvoke.FindFirstFileExFromApp(
705+
$"{parentPath}{Path.DirectorySeparatorChar}*.*",
706+
Win32PInvoke.FINDEX_INFO_LEVELS.FindExInfoBasic,
707+
out Win32PInvoke.WIN32_FIND_DATA findData,
708+
Win32PInvoke.FINDEX_SEARCH_OPS.FindExSearchNameMatch,
709+
IntPtr.Zero,
710+
Win32PInvoke.FIND_FIRST_EX_LARGE_FETCH);
711+
712+
if (hFile.ToInt64() == -1)
713+
return null;
714+
715+
var showHidden = UserSettingsService.FoldersSettingsService.ShowHiddenItems;
716+
var showSystem = UserSettingsService.FoldersSettingsService.ShowProtectedSystemFiles;
717+
var showDot = UserSettingsService.FoldersSettingsService.ShowDotFiles;
718+
var folders = new List<(string Name, string Path, bool IsHidden)>();
719+
720+
do
721+
{
722+
if (findData.cFileName is "." or "..")
723+
continue;
724+
725+
if (((FileAttributes)findData.dwFileAttributes & FileAttributes.Directory) == 0)
726+
continue;
727+
728+
bool isHidden = ((FileAttributes)findData.dwFileAttributes & FileAttributes.Hidden) != 0;
729+
bool isSystem = ((FileAttributes)findData.dwFileAttributes & FileAttributes.System) != 0;
730+
731+
if (isHidden && (!showHidden || (isSystem && !showSystem)))
732+
continue;
733+
734+
if (findData.cFileName.StartsWith('.') && !showDot)
735+
continue;
736+
737+
folders.Add((findData.cFileName, Path.Combine(parentPath, findData.cFileName), isHidden));
687738
}
739+
while (Win32PInvoke.FindNextFile(hFile, out findData));
740+
741+
Win32PInvoke.FindClose(hFile);
742+
folders.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase));
743+
744+
return folders;
688745
}
689746

690747
private async Task LoadFlyoutItemIconAsync(MenuFlyoutItem flyoutItem, string path)

0 commit comments

Comments
 (0)