Files

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;
}