Initial commit: FluidExplorer:从零实现的 WinUI 3 文件资源管理器替代品(Mica、命令栏、面包屑)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user