Initial commit: FluidExplorer:从零实现的 WinUI 3 文件资源管理器替代品(Mica、命令栏、面包屑)
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using FluidExplorer.Navigation;
|
||||
using FluidExplorer.Services;
|
||||
using FluidExplorer.Services.FileSystem;
|
||||
using FluidExplorer.Services.ItemVisuals;
|
||||
using FluidExplorer.Services.Operations;
|
||||
using FluidExplorer.Services.Search;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using PathHelper = FluidExplorer.Services.FileSystem.PathHelper;
|
||||
|
||||
namespace FluidExplorer.ViewModels;
|
||||
|
||||
/// <summary>一个标签页:包含主窗格,可选第二窗格(双窗格模式,搬文件不用来回切)。</summary>
|
||||
public sealed partial class ExplorerTabViewModel : ObservableObject
|
||||
{
|
||||
private readonly IFileSystemService _fs;
|
||||
private readonly SearchService _search;
|
||||
private readonly IFileOperationService _operations;
|
||||
private readonly ItemVisualService _visuals;
|
||||
private readonly IFileClipboard _clipboard;
|
||||
private readonly AppSettings _settings;
|
||||
private readonly DispatcherQueue _ui;
|
||||
|
||||
[ObservableProperty] private bool _isDualPane;
|
||||
[ObservableProperty] private bool _isActivePaneSecondary;
|
||||
[ObservableProperty] private string _header = "新标签页";
|
||||
[ObservableProperty] private string _glyph = "\uE8B7";
|
||||
|
||||
public ExplorerTabViewModel(
|
||||
IFileSystemService fs,
|
||||
SearchService search,
|
||||
IFileOperationService operations,
|
||||
ItemVisualService visuals,
|
||||
IFileClipboard clipboard,
|
||||
AppSettings settings,
|
||||
DispatcherQueue ui,
|
||||
NavigationLocation start)
|
||||
{
|
||||
_fs = fs;
|
||||
_search = search;
|
||||
_operations = operations;
|
||||
_visuals = visuals;
|
||||
_clipboard = clipboard;
|
||||
_settings = settings;
|
||||
_ui = ui;
|
||||
|
||||
Primary = new ExplorerPaneViewModel(fs, search, operations, visuals, clipboard, settings, ui);
|
||||
Primary.PropertyChanged += (_, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(ExplorerPaneViewModel.Title)) UpdateHeader();
|
||||
};
|
||||
Primary.SettingsChanged += (_, _) => UpdateHeader();
|
||||
_ = Primary.NavigateAsync(start);
|
||||
UpdateHeader();
|
||||
}
|
||||
|
||||
public ExplorerPaneViewModel Primary { get; }
|
||||
|
||||
[ObservableProperty] private ExplorerPaneViewModel? _secondary;
|
||||
|
||||
public ExplorerPaneViewModel ActivePane => IsDualPane && IsActivePaneSecondary && Secondary is not null ? Secondary : Primary;
|
||||
|
||||
public ExplorerPaneViewModel EnsureSecondary()
|
||||
{
|
||||
if (Secondary is not null) return Secondary;
|
||||
var pane = new ExplorerPaneViewModel(_fs, _search, _operations, _visuals, _clipboard, _settings, _ui)
|
||||
{
|
||||
ShowSidebar = false
|
||||
};
|
||||
pane.PropertyChanged += (_, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(ExplorerPaneViewModel.Title)) UpdateHeader();
|
||||
};
|
||||
Secondary = pane;
|
||||
return pane;
|
||||
}
|
||||
|
||||
public void ToggleDualPane()
|
||||
{
|
||||
if (IsDualPane)
|
||||
{
|
||||
IsDualPane = false;
|
||||
IsActivePaneSecondary = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var secondary = EnsureSecondary();
|
||||
if (string.IsNullOrEmpty(secondary.CurrentPath) || secondary.CurrentPath.StartsWith("::", StringComparison.Ordinal))
|
||||
{
|
||||
// 第二个窗格默认停在与主窗格同级的目录,便于对拖
|
||||
var parent = PathHelper.GetParent(Primary.CurrentPath);
|
||||
_ = secondary.NavigateAsync(NavigationLocation.FromPath(
|
||||
Directory.Exists(parent) ? parent : Primary.CurrentPath));
|
||||
}
|
||||
IsDualPane = true;
|
||||
}
|
||||
|
||||
private void UpdateHeader()
|
||||
{
|
||||
Header = Primary.IsSearchActive && !string.IsNullOrWhiteSpace(Primary.SearchText)
|
||||
? $"搜索:{Primary.SearchText}"
|
||||
: Primary.Title;
|
||||
Glyph = Primary.Glyph;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Primary.Dispose();
|
||||
Secondary?.Dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user