Files

152 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Fluid 文件资源管理器(WinUI 3 重构版)
用 **Windows App SDK / WinUI 3** 从零实现的 Windows 11 文件资源管理器替代品。
视觉与布局对齐原版(标签栏在标题栏内、Mica、命令栏、面包屑地址栏、导航窗格、详情/图标视图、状态栏),
但针对原版被吐槽最多的几个痛点做了结构性改造。
> 只用 **Windows 11 原生那一套**:WinUI 3 自带控件(NavigationView 风格侧边栏用 TreeView、TabView、BreadcrumbBar、AutoSuggestBox、InfoBar、ContentDialog…)、
> **Segoe Fluent Icons** 官方图标字体、**外壳原版文件图标**(`SHGetFileInfo` / `IShellItemImageFactory`,即资源管理器显示的同一批 imageres.dll 图标)、
> 以及系统自带的 Mica 材质与 WinUI 原生动画。**没有引入任何第三方 UI/图标/动画库**。
---
## 1. 直接运行
```powershell
dotnet build E:\deepseek\FluidExplorer\FluidExplorer.csproj -c Debug
E:\deepseek\FluidExplorer\bin\Debug\net8.0-windows10.0.26100.0\win-x64\FluidExplorer.exe
```
- 目标框架 `net8.0-windows10.0.26100.0`,x64,非打包(`WindowsPackageType=None`)+ **Windows App SDK 自包含**,
因此不需要预装 Windows App SDK 运行时,双击 exe 即可。
- 本机 NuGet 离线(`nuget.config` 里 `<clear/>`):所有依赖已在全局包缓存中,可直接还原。
依赖仅 `Microsoft.WindowsAppSDK 2.2.0` 与 `CommunityToolkit.Mvvm 8.4.2`(后者只用于减少 `INotifyPropertyChanged` 样板代码)。
- 设置文件:`%LOCALAPPDATA%\FluidExplorer\settings.json`(写入失败时退回程序目录)。
- 启动日志:程序目录下 `startup.log`(用于定位启动期异常)。
---
## 2. 针对原版痛点的改造
| 原版痛点 | 本实现的做法 |
|---|---|
| **搜索慢/搜不到**(原版走 Windows Search 索引,往往等半天甚至永远"正在搜索") | Everything 式 **NTFS 索引**:`FSCTL_ENUM_USN_DATA` 直读 MFT 全集 + 原始 MFT 解析取真实大小/时间 + `FSCTL_READ_USN_JOURNAL` 增量监听;查询是内存并行扫描,**20 万条目下 0.3–9 ms**(见 §5)。没有索引时自动退化为可取消的实时扫描,并在界面上明确标注"实时扫描",绝不假装在搜 |
| **搜索框必须回车、没有即时反馈** | 键入即搜(110 ms 防抖)+ 搜索框下拉即时建议 + 结果区实时显示"N 项结果 · X 毫秒 · 数据来源" |
| **搬文件很麻烦**(开两个窗口来回拖) | ① **双窗格**(Ctrl+Shift+D,中间可拖动分隔条)② 跨窗格剪贴板 ③ 拖到文件夹行/侧边栏即可移动(按住 Ctrl 复制)④ "移动到/复制到"以外的批量操作统一进队列 |
| **复制/移动卡死、无法暂停/取消** | 独立**文件操作引擎**:后台队列、**可暂停/继续/取消**、字节级进度 + 速度 + 剩余时间、失败重试(100/300/900 ms)、单文件失败不中断整批、长路径(`\\?\`)支持、同卷移动走 `File.Move`(1000 个文件 15 ms) |
| **覆盖冲突弹系统模态框、还打断操作** | 应用内冲突对话框(保留两者/替换/跳过 + "对后续所有冲突应用"),作业在等待用户选择时**保持运行态、UI 完全不卡** |
| **误操作无法挽回** | **Ctrl+Z 一步撤销**:移动/重命名搬回原位;删除默认进回收站并记录 `$I`→`$R` 映射,撤销即从回收站还原(不依赖系统弹窗) |
| **打开大文件夹/网络盘就"无响应"** | 枚举走 `FileSystemEnumerable`(底层 NtQueryDirectoryFile 批量缓冲,一次拿回名称+属性+大小+时间),**分批回调**(首批立刻上屏)、全程可取消、错误只在 InfoBar 提示不阻塞;属性列的真类型名走注册表并缓存 |
| **状态栏信息少** | 底部状态栏常驻:项目数 / 选中项数与合计体积 / 索引进度与条数 / 一键建索引 / 视图切换 / **操作队列入口** |
| **深浅色与强调色不跟随** | 全部颜色取自 WinUI 内置主题资源(不写死任何色值),`ElementTheme.Default` + `MicaBackdrop` 跟随系统;标题栏按钮颜色随主题切换;设置里可手动覆盖为浅/深色 |
| **动画要么没有要么过度** | 只用 WinUI 原生动画(列表项增删、悬停/选中反馈、TabView、对话框、Mica 过渡);设置里可一键关闭(内部用 `Timeline.AllowDependentAnimations` 总开关),不额外叠加自造动效 |
其他对齐原版的细节:真实路径面包屑(此电脑 › 本地磁盘 › …)、侧边栏(主页/图库/快速访问/此电脑/网络/回收站,可展开、跟随当前路径高亮)、
按文件夹记住视图方式与排序(写入 `settings.json`)、"名称"列用系统 `StrCmpLogicalW` 自然排序(文件2 排在 文件10 前)、文件夹恒排在文件前、
显示/隐藏隐藏项与扩展名、**系统原版右键菜单**(外壳 `IContextMenu`,第三方扩展条目也在)。
键盘:`Ctrl+T/W/Tab` 标签页、`Ctrl+Shift+D` 双窗格、`Alt+←/→/↑` 前进后退上级、`Backspace` 上级、`F5` 刷新、`F2` 重命名、
`Delete`/`Shift+Delete` 删除/彻底删除、`Ctrl+X/C/V`、`Ctrl+Shift+C` 复制路径、`Ctrl+Shift+N` 新建文件夹、`Ctrl+Z` 撤销、`Ctrl+F` 聚焦搜索、`Ctrl+L` 编辑地址。
---
## 3. 代码结构
```
FluidExplorer/
├─ App.xaml(.cs) 应用入口、启动阶段日志、全局异常兜底
├─ MainWindow.xaml(.cs) 窗口外壳:Mica、扩展标题栏、标题栏按钮配色、快捷键、冲突对话框
├─ Views/
│ ├─ ShellView.xaml(.cs) 标签栏(位于标题栏区域内)+ 操作队列面板
│ ├─ ExplorerTabView.xaml(.cs) 单/双窗格布局与分隔条
│ ├─ ExplorerPaneView.xaml(.cs) 一个浏览窗格:命令栏 / 地址栏 / 导航窗格 / 内容区 / 状态栏
│ └─ SettingsDialog.xaml(.cs) 设置
├─ ViewModels/
│ ├─ MainViewModel.cs 标签页、侧边栏、索引状态、作业快照、设置
│ ├─ ExplorerTabViewModel.cs 标签页 = 1~2 个窗格
│ ├─ ExplorerPaneViewModel.cs 导航状态机:枚举、排序、搜索、选择、文件操作、回收站、图库
│ ├─ JobRowViewModel.cs UI 线程的作业快照(作业在后台线程更新,不能直接绑 UI)
│ └─ SidebarNode.cs 侧边栏节点 / 面包屑段 / 搜索结果行
├─ Models/ FileEntry / ExplorerItem / FolderListing / 排序与视图枚举
├─ Navigation/ 位置模型(本机/图库/此电脑/回收站/驱动器/路径)+ 前进后退历史
├─ Services/
│ ├─ AppServices.cs 组合根
│ ├─ AppSettings.cs 设置持久化(含按文件夹视图状态)
│ ├─ FileSystem/ 快速枚举(NtQueryDirectoryFile 批量缓冲)+ 路径工具
│ ├─ Search/ 查询语法解析、搜索门面、NTFS 索引(Usn/:MFT 解析、名字池、索引存储、通配符)
│ ├─ Icons/ 外壳原版图标与缩略图(SHGetFileInfo / IShellItemImageFactory,LRU + 同键合并)
│ ├─ Operations/ 文件操作队列引擎(复制/移动/删除/重命名/撤销/回收站定位)
│ ├─ ItemVisuals/ 列表行的图标/缩略图按需加载(并发上限 + 去重)
│ └─ Shell/ 外壳能力:已知文件夹、类型名、打开/属性/剪贴板、回收站视图、原版右键菜单
├─ Themes/ Styles.xaml(尺寸/样式)、Glyphs.xaml(Segoe Fluent Icons 码点)
└─ Helpers/ DispatcherQueue 异步封装、值转换器
```
设计要点:
1. **接口先冻结再并行开发**:`IFileIndex` / `IIconService` / `IFileOperationService` / `IFileSystemService` 先定义,
索引、图标、操作三个模块独立实现(互不依赖),最后由 `AppServices` 装配。
2. **UI 线程零阻塞**:所有文件系统交互都是异步 + 可取消;集合更新经 `DispatcherQueue` 分批提交。
3. **后台对象不直接绑 UI**:作业在后台线程更新,界面绑的是 250 ms 刷新的 UI 线程快照(`JobRowViewModel`)。
4. **失败降级而不是崩溃**:假索引不可用时搜索自动退化;外壳取图失败返回 null 并回退到扩展名图标;启动异常写入 `startup.log`。
---
## 4. 搜索语法(对齐 Everything 习惯)
```
keyword 名称包含(大小写不敏感,多个词 = AND)
"两个 词" 带空格的短语
!keyword 排除
*.json / pre* 通配符
ext:log;txt 扩展名(可多值)
size:>100mb 大小(kb/mb/gb,支持 > < =)
dm:today / dm:7d 修改时间(today/yesterday/thisweek/thismonth/thisyear/Nd/Nh/Nw 或具体日期)
dc:today 创建时间(需要索引提供创建时间,当前版本未启用)
folder: / file: 只看文件夹 / 只看文件
path:Windows 在完整路径中匹配
```
---
## 5. 验证情况(都基于本机实测,不采信"应该能用")
**实测通过**
- 工程编译:`0 error`;XAML 全部编译为 xbf(含主题字典)。
- 端到端启动:进程存活、窗口标题「文件资源管理器」、UI Automation 树确认——标签栏在标题栏区域内(系统按钮区已正确让位)、
命令栏 12 个按钮、地址栏(后退/前进/上级/刷新 + 2 段面包屑 + 搜索框)、导航窗格 16 个节点、
详情视图 4 个列头(名称/修改日期/类型/大小)、**真实列出 107 行文件**、状态栏与操作队列入口在位。
- 搜索:通过 UI Automation 向搜索框写入关键字,界面返回结果行并显示「N 项结果 · X 毫秒 · 数据来源」。
- 文件操作引擎(独立探针,62/62 断言 PASS):300 MB 复制的字节级进度;**暂停 1 秒增长 0 字节**;取消保留已完成部分;
同卷移动 1000 文件 **15 ms**(零字节流量);`KeepBoth/Replace/Skip/Ask+ApplyToAll` 全部正确;
308→312 字符长路径复制/移动成功;跨卷移动可撤销;永久删除不入撤销栈。
- NTFS 索引(独立探针,走生产代码路径):20 万条目合成数据集 **77 字节/条**;
查询 `*.json` 3.3 ms、`ext:log` 1.4 ms、`size:>100mb` 0.66 ms、`dm:today` 0.3 ms、`path:Windows` 9.3 ms;
2000 条路径还原 1.66 ms;增量(创建/改名/删除)与墓碑正确;大小未知的条目不会被 `size:` 误判为 0 字节。
过程中修掉一个会**静默丢 64% 结果**的扩容 bug(名字池被换新导致老记录名字失效)。
- 图标:`.txt/.exe/目录/驱动器` 均取到系统原生图标(32×32 原生尺寸、真 alpha);`shell:RecycleBinFolder` 等外壳对象正常;
图片缩略图 256×160 正常;1000 次扩展名取图 86 ms;300 次取图后 GDI/USER 句柄**零增长**。
**已知限制(不隐瞒)**
- **当前会话没有管理员权限**:`CreateFile(@"\\.\C:")` 被拒(`FSCTL_*` 返回 `ERROR_INVALID_FUNCTION`),
因此 NTFS 索引在本机本次验证中走的是 `RequiresElevation` 分支;界面状态栏会提示并可一键「以管理员身份重启」。
索引本身的正确性用同一份生产代码 + 合成数据集验证(见上)。**要拿到真实全盘索引,请以管理员身份运行一次。**
- 非管理员降级模式下文件大小全部为"未知"(USN 记录本身不含 size,真实大小依赖原始 MFT 解析),此时 `size:` 过滤会偏宽松。
- 拖放到**其他应用程序**、拖出标签新建窗口、网络邻居枚举为简化实现;图库依赖索引(无索引时给提示而非空列表)。
- 缩略图/图标语义、字体字形只做了"码点在字体中存在"的校验(60/60 存在,不会出现方框),
但**字形语义未经人眼确认**(本会话的模型不能读图),个别按钮图标若观感不佳可直接改 `Themes/Glyphs.xaml` 里的码点。
---
## 6. 复现验证
```powershell
# 编译
dotnet build E:\deepseek\FluidExplorer\FluidExplorer.csproj -c Debug
# 启动 + UI 自动化结构检查 + 截图(截图落在 E:\deepseek\artifacts\shell_window.png)
powershell -NoProfile -ExecutionPolicy Bypass -File E:\deepseek\tools\smoke_test.ps1
# 字形存在性校验(Segoe Fluent Icons)
powershell -NoProfile -ExecutionPolicy Bypass -File E:\deepseek\tools\check_glyphs.ps1
```