159 lines
6.1 KiB
C#
159 lines
6.1 KiB
C#
using System.Collections.ObjectModel;
|
|
using System.ComponentModel;
|
|
using System.Runtime.CompilerServices;
|
|
using Microsoft.UI.Xaml.Media;
|
|
|
|
namespace FluidExplorer.Models;
|
|
|
|
/// <summary>轻量文件系统条目:由快速枚举一次性填充(不产生额外系统调用)。</summary>
|
|
public sealed class FileEntry
|
|
{
|
|
public required string Name { get; init; }
|
|
public required string FullPath { get; init; }
|
|
public bool IsDirectory { get; init; }
|
|
public long Size { get; init; }
|
|
public DateTime ModifiedUtc { get; init; }
|
|
public DateTime CreatedUtc { get; init; }
|
|
public DateTime AccessedUtc { get; init; }
|
|
public FileAttributes Attributes { get; init; }
|
|
|
|
public string Extension
|
|
{
|
|
get
|
|
{
|
|
if (IsDirectory) return string.Empty;
|
|
var i = Name.LastIndexOf('.');
|
|
return i > 0 && i < Name.Length - 1 ? Name[(i + 1)..] : string.Empty;
|
|
}
|
|
}
|
|
|
|
public string TypeKey => IsDirectory ? "folder" : (Extension.Length == 0 ? "file" : Extension.ToLowerInvariant());
|
|
|
|
public bool IsHidden => (Attributes & FileAttributes.Hidden) != 0;
|
|
public bool IsSystem => (Attributes & FileAttributes.System) != 0;
|
|
public bool IsReparsePoint => (Attributes & FileAttributes.ReparsePoint) != 0;
|
|
public bool IsReadOnly => (Attributes & FileAttributes.ReadOnly) != 0;
|
|
public bool IsOffline => (Attributes & FileAttributes.Offline) != 0;
|
|
|
|
public DateTime ModifiedLocal => ModifiedUtc.ToLocalTime();
|
|
public DateTime CreatedLocal => CreatedUtc.ToLocalTime();
|
|
|
|
/// <summary>Windows 资源管理器风格的尺寸文本(已按 1024 进制并为文件夹留空)。</summary>
|
|
public string SizeText => IsDirectory ? string.Empty : FormatSize(Size);
|
|
|
|
/// <summary>类型描述:优先使用注册表里的友好类型名(惰性、带缓存)。</summary>
|
|
public string TypeText => IsDirectory ? "文件夹" : Services.Shell.TypeNameResolver.GetTypeName(Extension, IsDirectory);
|
|
|
|
public static string FormatSize(long bytes)
|
|
{
|
|
if (bytes < 0) return string.Empty;
|
|
if (bytes < 1024) return $"{bytes} 字节";
|
|
string[] units = ["KB", "MB", "GB", "TB", "PB"];
|
|
double v = bytes;
|
|
int u = -1;
|
|
do { v /= 1024.0; u++; } while (v >= 1024 && u < units.Length - 1);
|
|
return v >= 100 ? $"{v:0} {units[u]}" : v >= 10 ? $"{v:0.0} {units[u]}" : $"{v:0.00} {units[u]}";
|
|
}
|
|
}
|
|
|
|
/// <summary>列表里的一行:可观察包装,缩略图/图标异步补齐,不阻塞滚动。</summary>
|
|
public sealed class ExplorerItem : INotifyPropertyChanged
|
|
{
|
|
private ImageSource? _icon;
|
|
private bool _isSelected;
|
|
private bool _isRenaming;
|
|
private string _renameText = string.Empty;
|
|
private string? _displayName;
|
|
private int _iconSize = 16;
|
|
|
|
public ExplorerItem(FileEntry entry) => Entry = entry;
|
|
|
|
public FileEntry Entry { get; }
|
|
public string Name => Entry.Name;
|
|
|
|
/// <summary>界面显示名("显示文件扩展名"关闭时隐藏扩展名,与资源管理器一致)。</summary>
|
|
public string DisplayName
|
|
{
|
|
get => _displayName ?? Entry.Name;
|
|
set { if (_displayName != value) { _displayName = value; OnPropertyChanged(); } }
|
|
}
|
|
|
|
/// <summary>是否为新建/重命名中的占位项(这些项要滚动到可见并进入编辑状态)。</summary>
|
|
public bool IsPending { get; set; }
|
|
public string FullPath => Entry.FullPath;
|
|
public bool IsDirectory => Entry.IsDirectory;
|
|
public long Size => Entry.Size;
|
|
public string SizeText => Entry.SizeText;
|
|
public string TypeText => Entry.TypeText;
|
|
public DateTime ModifiedLocal => Entry.ModifiedLocal;
|
|
public string ModifiedText => Entry.ModifiedLocal.ToString("yyyy/MM/dd HH:mm");
|
|
public string CreatedText => Entry.CreatedLocal.ToString("yyyy/MM/dd HH:mm");
|
|
|
|
/// <summary>图标或缩略图(ImageSource 由 UI 线程创建后写入;先给几何图标占位,避免跳动)。</summary>
|
|
public ImageSource? Icon
|
|
{
|
|
get => _icon;
|
|
set { if (!ReferenceEquals(_icon, value)) { _icon = value; OnPropertyChanged(); } }
|
|
}
|
|
|
|
/// <summary>是否已经替换为真实缩略图(用于淡入动画)。</summary>
|
|
public bool HasThumbnail { get; set; }
|
|
|
|
public bool IsSelected
|
|
{
|
|
get => _isSelected;
|
|
set
|
|
{
|
|
if (_isSelected == value) return;
|
|
_isSelected = value;
|
|
OnPropertyChanged();
|
|
OnPropertyChanged(nameof(ShowCheckBox));
|
|
}
|
|
}
|
|
|
|
/// <summary>复选框是否可见(对齐资源管理器:选中时显示,或用户开启了"始终显示项目复选框")。</summary>
|
|
public bool ShowCheckBox => _isSelected || AlwaysShowCheckBoxes;
|
|
|
|
/// <summary>全局设置:始终显示项目复选框。</summary>
|
|
public static bool AlwaysShowCheckBoxes { get; set; }
|
|
|
|
/// <summary>当前视图所需的图标像素尺寸(由窗格在切换视图方式时写入)。</summary>
|
|
public int IconSize
|
|
{
|
|
get => _iconSize;
|
|
set { if (_iconSize != value) { _iconSize = value; OnPropertyChanged(); } }
|
|
}
|
|
|
|
public bool IsRenaming
|
|
{
|
|
get => _isRenaming;
|
|
set { if (_isRenaming != value) { _isRenaming = value; OnPropertyChanged(); } }
|
|
}
|
|
|
|
public string RenameText
|
|
{
|
|
get => _renameText;
|
|
set { if (_renameText != value) { _renameText = value; OnPropertyChanged(); } }
|
|
}
|
|
|
|
public string Extension => Entry.Extension;
|
|
public bool IsHidden => Entry.IsHidden;
|
|
public bool IsReparsePoint => Entry.IsReparsePoint;
|
|
|
|
public event PropertyChangedEventHandler? PropertyChanged;
|
|
|
|
private void OnPropertyChanged([CallerMemberName] string? name = null)
|
|
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
|
}
|
|
|
|
/// <summary>当前文件夹的内容集合。</summary>
|
|
public sealed class FolderListing
|
|
{
|
|
public required string Path { get; set; }
|
|
public ObservableCollection<ExplorerItem> Items { get; } = [];
|
|
public bool IsLoading { get; set; }
|
|
public bool IsComplete { get; set; }
|
|
public string? Error { get; set; }
|
|
public int TotalCount => Items.Count;
|
|
}
|