文档组件优化
This commit is contained in:
lincube
2026-03-19 08:39:25 +08:00
parent b436bfa884
commit b3a74aa072
7 changed files with 241 additions and 9 deletions

View File

@@ -64,6 +64,7 @@ public sealed class ComponentSettingsSnapshot
public string Stcn24ForumSourceType { get; set; } = Stcn24ForumSourceTypes.LatestCreated;
public ComponentSettingsSnapshot Clone()
{
var clone = (ComponentSettingsSnapshot)MemberwiseClone();
@@ -91,6 +92,9 @@ public sealed class ComponentSettingsSnapshot
clone.WorldClockTimeZoneIds = WorldClockTimeZoneIds is { Count: > 0 }
? new List<string>(WorldClockTimeZoneIds)
: [];
clone.OfficeRecentDocumentsEnabledSources = OfficeRecentDocumentsEnabledSources is not null
? new List<string>(OfficeRecentDocumentsEnabledSources)
: null;
return clone;
}

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace LanMountainDesktop.Models;
public static class OfficeRecentDocumentSourceTypes
{
public const string Registry = "registry";
public const string RecentFolders = "recent_folders";
public const string JumpLists = "jump_lists";
public static IReadOnlyList<string> SupportedValues { get; } =
[
Registry,
RecentFolders,
JumpLists
];
public static IReadOnlyList<string> DefaultValues => SupportedValues;
public static IReadOnlyList<string> NormalizeValues(IEnumerable<string>? values, bool useDefaultWhenEmpty)
{
if (values is null)
{
return useDefaultWhenEmpty ? DefaultValues : Array.Empty<string>();
}
var normalized = values
.Select(NormalizeValue)
.OfType<string>()
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
if (normalized.Length == 0 && useDefaultWhenEmpty)
{
return DefaultValues;
}
return normalized;
}
private static string? NormalizeValue(string? value)
{
return value?.Trim().ToLowerInvariant() switch
{
Registry => Registry,
RecentFolders => RecentFolders,
JumpLists => JumpLists,
_ => null
};
}
}

View File

@@ -75,6 +75,9 @@ public static class DesktopComponentEditorRegistryFactory
[BuiltInComponentIds.DesktopRemovableStorage] = new(
BuiltInComponentIds.DesktopRemovableStorage,
context => new RemovableStorageComponentEditor(context)),
[BuiltInComponentIds.DesktopOfficeRecentDocuments] = new(
BuiltInComponentIds.DesktopOfficeRecentDocuments,
context => new OfficeRecentDocumentsComponentEditor(context)),
[BuiltInComponentIds.DesktopWeather] = CreateWeatherRegistration(BuiltInComponentIds.DesktopWeather),
[BuiltInComponentIds.DesktopWeatherClock] = CreateWeatherRegistration(BuiltInComponentIds.DesktopWeatherClock),
[BuiltInComponentIds.DesktopHourlyWeather] = CreateWeatherRegistration(BuiltInComponentIds.DesktopHourlyWeather),

View File

@@ -9,6 +9,7 @@ using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using LanMountainDesktop.Models;
using Microsoft.Win32;
using MudTools.OfficeInterop;
using MudTools.OfficeInterop.Excel;
@@ -18,7 +19,7 @@ namespace LanMountainDesktop.Services;
public interface IOfficeRecentDocumentsService
{
List<OfficeRecentDocument> GetRecentDocuments(int maxCount = 20);
List<OfficeRecentDocument> GetRecentDocuments(int maxCount = 20, IReadOnlyCollection<string>? enabledSources = null);
void OpenDocument(string filePath);
}
@@ -48,20 +49,38 @@ public sealed class OfficeRecentDocumentsService : IOfficeRecentDocumentsService
@"\[T(?<filetime>[0-9A-F]+)\]",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
public List<OfficeRecentDocument> GetRecentDocuments(int maxCount = 20)
public List<OfficeRecentDocument> GetRecentDocuments(int maxCount = 20, IReadOnlyCollection<string>? enabledSources = null)
{
var documents = new List<OfficeRecentDocument>();
var normalizedSources = OfficeRecentDocumentSourceTypes.NormalizeValues(
enabledSources,
useDefaultWhenEmpty: enabledSources is null);
if (!OperatingSystem.IsWindows())
if (!OperatingSystem.IsWindows() || normalizedSources.Count == 0)
{
return documents;
}
TryGetFromRegistry(documents);
TryGetFromRecentFolders(documents);
TryGetFromJumpLists(documents);
var useRegistry = normalizedSources.Contains(OfficeRecentDocumentSourceTypes.Registry, StringComparer.OrdinalIgnoreCase);
var useRecentFolders = normalizedSources.Contains(OfficeRecentDocumentSourceTypes.RecentFolders, StringComparer.OrdinalIgnoreCase);
var useJumpLists = normalizedSources.Contains(OfficeRecentDocumentSourceTypes.JumpLists, StringComparer.OrdinalIgnoreCase);
if (documents.Count < maxCount)
if (useRegistry)
{
TryGetFromRegistry(documents);
}
if (useRecentFolders)
{
TryGetFromRecentFolders(documents);
}
if (useJumpLists)
{
TryGetFromJumpLists(documents);
}
if (useRegistry && documents.Count < maxCount)
{
TryGetFromMudToolsInterop(documents);
}

View File

@@ -0,0 +1,42 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="LanMountainDesktop.Views.ComponentEditors.OfficeRecentDocumentsComponentEditor">
<StackPanel Spacing="16">
<Border Classes="component-editor-hero-card"
Padding="24">
<StackPanel Spacing="8">
<TextBlock x:Name="HeadlineTextBlock"
Classes="component-editor-headline"
TextWrapping="Wrap" />
<TextBlock x:Name="DescriptionTextBlock"
Classes="component-editor-secondary-text"
TextWrapping="Wrap" />
</StackPanel>
</Border>
<Border Classes="component-editor-card"
Padding="20">
<StackPanel Spacing="12">
<TextBlock x:Name="SourcesHeaderTextBlock"
Classes="component-editor-section-title" />
<TextBlock x:Name="SourcesDescriptionTextBlock"
Classes="component-editor-secondary-text"
TextWrapping="Wrap" />
<CheckBox x:Name="RegistryCheckBox"
IsCheckedChanged="OnSourceSelectionChanged" />
<CheckBox x:Name="RecentFoldersCheckBox"
IsCheckedChanged="OnSourceSelectionChanged" />
<CheckBox x:Name="JumpListsCheckBox"
IsCheckedChanged="OnSourceSelectionChanged" />
</StackPanel>
</Border>
<TextBlock x:Name="HintTextBlock"
Classes="component-editor-secondary-text"
Margin="12,0"
TextWrapping="Wrap" />
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,85 @@
using System;
using System.Linq;
using Avalonia.Interactivity;
using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Models;
namespace LanMountainDesktop.Views.ComponentEditors;
public partial class OfficeRecentDocumentsComponentEditor : ComponentEditorViewBase
{
private bool _suppressEvents;
public OfficeRecentDocumentsComponentEditor()
: this(null)
{
}
public OfficeRecentDocumentsComponentEditor(DesktopComponentEditorContext? context)
: base(context)
{
InitializeComponent();
ApplyState();
}
private void ApplyState()
{
var snapshot = LoadSnapshot();
var enabledSources = OfficeRecentDocumentSourceTypes.NormalizeValues(
snapshot.OfficeRecentDocumentsEnabledSources,
useDefaultWhenEmpty: snapshot.OfficeRecentDocumentsEnabledSources is null);
HeadlineTextBlock.Text = Context?.Definition.DisplayName ?? "Office Recent Documents";
DescriptionTextBlock.Text = L(
"office_recent_documents.settings.desc",
"Choose which Windows and Office sources this widget should scan for recent documents.");
SourcesHeaderTextBlock.Text = L(
"office_recent_documents.settings.sources_title",
"Recent document sources");
SourcesDescriptionTextBlock.Text = L(
"office_recent_documents.settings.sources_desc",
"You can combine multiple sources. Registry selection also keeps the Office interop MRU fallback available.");
RegistryCheckBox.Content = L(
"office_recent_documents.settings.source.registry",
"Office registry MRU");
RecentFoldersCheckBox.Content = L(
"office_recent_documents.settings.source.recent_folders",
"Windows Recent folders");
JumpListsCheckBox.Content = L(
"office_recent_documents.settings.source.jump_lists",
"Windows Jump Lists");
HintTextBlock.Text = L(
"office_recent_documents.settings.hint",
"If you disable all sources, this widget will stay empty until at least one source is enabled again.");
_suppressEvents = true;
RegistryCheckBox.IsChecked = enabledSources.Contains(OfficeRecentDocumentSourceTypes.Registry, StringComparer.OrdinalIgnoreCase);
RecentFoldersCheckBox.IsChecked = enabledSources.Contains(OfficeRecentDocumentSourceTypes.RecentFolders, StringComparer.OrdinalIgnoreCase);
JumpListsCheckBox.IsChecked = enabledSources.Contains(OfficeRecentDocumentSourceTypes.JumpLists, StringComparer.OrdinalIgnoreCase);
_suppressEvents = false;
}
private void OnSourceSelectionChanged(object? sender, RoutedEventArgs e)
{
_ = sender;
_ = e;
if (_suppressEvents)
{
return;
}
var selectedSources = new[]
{
RegistryCheckBox.IsChecked == true ? OfficeRecentDocumentSourceTypes.Registry : null,
RecentFoldersCheckBox.IsChecked == true ? OfficeRecentDocumentSourceTypes.RecentFolders : null,
JumpListsCheckBox.IsChecked == true ? OfficeRecentDocumentSourceTypes.JumpLists : null
}
.Where(static value => !string.IsNullOrWhiteSpace(value))
.Cast<string>()
.ToList();
var snapshot = LoadSnapshot();
snapshot.OfficeRecentDocumentsEnabledSources = selectedSources;
SaveSnapshot(snapshot, nameof(ComponentSettingsSnapshot.OfficeRecentDocumentsEnabledSources));
}
}

View File

@@ -4,14 +4,20 @@ using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Models;
using LanMountainDesktop.Services;
namespace LanMountainDesktop.Views.Components;
public partial class OfficeRecentDocumentsWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget
public partial class OfficeRecentDocumentsWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, IComponentPlacementContextAware
{
private readonly IOfficeRecentDocumentsService _recentDocumentsService;
private readonly IComponentInstanceSettingsStore _componentSettingsStore = HostComponentSettingsStoreProvider.GetOrCreate();
private List<OfficeRecentDocument> _documents = new();
private string _componentId = BuiltInComponentIds.DesktopOfficeRecentDocuments;
private string _placementId = string.Empty;
private IReadOnlyList<string> _enabledSources = OfficeRecentDocumentSourceTypes.DefaultValues;
private bool _isOnActivePage;
private bool _isEditMode;
private bool _isLoading;
@@ -20,6 +26,7 @@ public partial class OfficeRecentDocumentsWidget : UserControl, IDesktopComponen
{
InitializeComponent();
_recentDocumentsService = new OfficeRecentDocumentsService();
ReloadSettings();
}
public void ApplyCellSize(double cellSize)
@@ -44,6 +51,15 @@ public partial class OfficeRecentDocumentsWidget : UserControl, IDesktopComponen
}
}
public void SetComponentPlacementContext(string componentId, string? placementId)
{
_componentId = string.IsNullOrWhiteSpace(componentId)
? BuiltInComponentIds.DesktopOfficeRecentDocuments
: componentId.Trim();
_placementId = placementId?.Trim() ?? string.Empty;
ReloadSettings();
}
private async void LoadDocuments()
{
if (_isLoading)
@@ -54,10 +70,12 @@ public partial class OfficeRecentDocumentsWidget : UserControl, IDesktopComponen
try
{
_isLoading = true;
ReloadSettings();
StatusTextBlock.IsVisible = false;
DocumentsItemsControl.ItemsSource = null;
_documents = await Task.Run(() => _recentDocumentsService.GetRecentDocuments(20));
var enabledSources = _enabledSources.ToArray();
_documents = await Task.Run(() => _recentDocumentsService.GetRecentDocuments(20, enabledSources));
if (_documents.Count == 0)
{
@@ -80,6 +98,14 @@ public partial class OfficeRecentDocumentsWidget : UserControl, IDesktopComponen
}
}
private void ReloadSettings()
{
var snapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
_enabledSources = OfficeRecentDocumentSourceTypes.NormalizeValues(
snapshot.OfficeRecentDocumentsEnabledSources,
useDefaultWhenEmpty: snapshot.OfficeRecentDocumentsEnabledSources is null);
}
private void UpdateDisplay()
{
var displayItems = _documents.Select(d => new OfficeRecentDocumentViewModel