Initial commit: FluidExplorer:从零实现的 WinUI 3 文件资源管理器替代品(Mica、命令栏、面包屑)

This commit is contained in:
WpyQwq
2026-09-19 11:54:03 +08:00
commit 5780fde61a
60 changed files with 15035 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using FluidExplorer.Navigation;
using FluidExplorer.Services.FileSystem;
using FluidExplorer.Services.Search;
using FluidExplorer.Services.Shell;
using Microsoft.UI.Xaml.Media;
namespace FluidExplorer.ViewModels;
/// <summary>侧边栏的一个节点(可展开的"此电脑"、可收藏的文件夹、驱动器等)。</summary>
public sealed partial class SidebarNode : ObservableObject
{
[ObservableProperty] private bool _isExpanded;
[ObservableProperty] private bool _isSelected;
public SidebarNode(string displayName, string glyph, NavigationLocation location, bool isExpandable = false)
{
DisplayName = displayName;
Glyph = glyph;
Location = location;
IsExpandable = isExpandable;
}
public string DisplayName { get; }
public string Glyph { get; }
public NavigationLocation Location { get; }
public bool IsExpandable { get; }
public bool CanPin { get; init; }
public ObservableCollection<SidebarNode> Children { get; } = [];
public bool HasUnrealizedChildren { get; set; }
/// <summary>当前文件夹是否属于该节点(用于侧边栏高亮,对齐资源管理器)。</summary>
public bool Contains(string path)
{
if (string.IsNullOrEmpty(path)) return false;
if (Location.Kind is LocationKind.ThisPc or LocationKind.RecycleBin or LocationKind.Network or LocationKind.Home)
return string.Equals(Location.Path, path, StringComparison.OrdinalIgnoreCase);
var root = Location.Path.TrimEnd('\\');
return path.StartsWith(root, StringComparison.OrdinalIgnoreCase);
}
public static SidebarNode Create(string name, string glyph, NavigationLocation location, bool expandable = false, bool canPin = false)
=> new(name, glyph, location, expandable) { CanPin = canPin };
}
/// <summary>面包屑的一段。</summary>
public sealed partial class BreadcrumbSegment : ObservableObject
{
public BreadcrumbSegment(string displayName, string path, bool isDriveRoot)
{
DisplayName = displayName;
Path = path;
IsDriveRoot = isDriveRoot;
}
public string DisplayName { get; }
public string Path { get; }
public bool IsDriveRoot { get; }
}
/// <summary>搜索结果列表里的一行。</summary>
public sealed partial class SearchResultItem : ObservableObject
{
private ImageSource? _icon;
private ImageSource? _thumbnail;
public SearchResultItem(SearchHit hit) => Hit = hit;
public SearchHit Hit { get; }
public string Name => Hit.Name;
public string Directory => Hit.Directory;
public string FullPath => Hit.Path;
public bool IsDirectory => Hit.IsDirectory;
public long Size => Hit.Size;
public string SizeText => Hit.Size < 0 ? "—" : Hit.IsDirectory ? string.Empty : Models.FileEntry.FormatSize(Hit.Size);
public string ModifiedText => Hit.ModifiedUtc == DateTime.MinValue || Hit.ModifiedUtc == default
? "—"
: Hit.ModifiedUtc.ToLocalTime().ToString("yyyy/MM/dd HH:mm");
public string TypeText => Hit.IsDirectory ? "文件夹" : TypeNameResolver.GetTypeName(Hit.Extension, false);
public ImageSource? Icon
{
get => _icon;
set { if (!ReferenceEquals(_icon, value)) { _icon = value; OnPropertyChanged(); } }
}
public ImageSource? Thumbnail
{
get => _thumbnail;
set { if (!ReferenceEquals(_thumbnail, value)) { _thumbnail = value; OnPropertyChanged(); } }
}
public DateTime SortModified => Hit.ModifiedUtc;
}