using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; using Microsoft.UI.Xaml.Media; namespace FluidExplorer.Models; /// 轻量文件系统条目:由快速枚举一次性填充(不产生额外系统调用)。 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(); /// Windows 资源管理器风格的尺寸文本(已按 1024 进制并为文件夹留空)。 public string SizeText => IsDirectory ? string.Empty : FormatSize(Size); /// 类型描述:优先使用注册表里的友好类型名(惰性、带缓存)。 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]}"; } } /// 列表里的一行:可观察包装,缩略图/图标异步补齐,不阻塞滚动。 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; /// 界面显示名("显示文件扩展名"关闭时隐藏扩展名,与资源管理器一致)。 public string DisplayName { get => _displayName ?? Entry.Name; set { if (_displayName != value) { _displayName = value; OnPropertyChanged(); } } } /// 是否为新建/重命名中的占位项(这些项要滚动到可见并进入编辑状态)。 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"); /// 图标或缩略图(ImageSource 由 UI 线程创建后写入;先给几何图标占位,避免跳动)。 public ImageSource? Icon { get => _icon; set { if (!ReferenceEquals(_icon, value)) { _icon = value; OnPropertyChanged(); } } } /// 是否已经替换为真实缩略图(用于淡入动画)。 public bool HasThumbnail { get; set; } public bool IsSelected { get => _isSelected; set { if (_isSelected == value) return; _isSelected = value; OnPropertyChanged(); OnPropertyChanged(nameof(ShowCheckBox)); } } /// 复选框是否可见(对齐资源管理器:选中时显示,或用户开启了"始终显示项目复选框")。 public bool ShowCheckBox => _isSelected || AlwaysShowCheckBoxes; /// 全局设置:始终显示项目复选框。 public static bool AlwaysShowCheckBoxes { get; set; } /// 当前视图所需的图标像素尺寸(由窗格在切换视图方式时写入)。 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)); } /// 当前文件夹的内容集合。 public sealed class FolderListing { public required string Path { get; set; } public ObservableCollection Items { get; } = []; public bool IsLoading { get; set; } public bool IsComplete { get; set; } public string? Error { get; set; } public int TotalCount => Items.Count; }