Files
LanMountainDesktop/LanMountainDesktop/Views/ComponentEditorWindow.axaml.cs
lincube abfa64b3d7 Avalonia12 (#7)
* ava12升级

* Enable centralized package versioning

Add <Project> and <PropertyGroup> with <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> to Directory.Packages.props to enable centralized package version management across the repository. This allows package versions to be controlled from this single file instead of individual project files.

* Migrate codebase to Avalonia 12 APIs

Apply Avalonia 12 migration changes: replace SystemDecorations with WindowDecorations and remove ExtendClientAreaChromeHints/ExtendClientAreaTitleBarHeightHint usages; update BindingPlugins removal logic (no-op); switch clipboard usage to ClipboardExtensions.SetTextAsync; update Bitmap.CopyPixels calls to the new signature. Replace TextBox.Watermark with PlaceholderText, convert NumberBox styles to FANumberBox and adjust templates, change Checked/Unchecked handlers to IsCheckedChanged, and adapt FluentIcons usages (SymbolIconSource -> FASymbol/FAFont/FluentIcon equivalents). Fix MainWindow partial classes to inherit Window and correct missing variables/fields/usings. Add migration docs/specs/tasks under .trae and include a small TestFluentIcons project for icon testing.

* Migrate to Avalonia 12 and Plugin SDK v5

Upgrade project to the Avalonia 12 baseline and Plugin SDK v5: centralize Avalonia packages, remove legacy WebView.Avalonia usage (use NativeWebView/WebView2 EnvironmentRequested), and update Fluent/Material icon/package usages. Bump multiple package/project versions to 5.0.0 and Avalonia 12.0.1, update plugin template and README/docs to SDK v5, and add PLUGIN_SDK_V5_MIGRATION.md.

Also fix runtime/behavior bugs: make DataLocationResolver use a fixed bootstrap launcher data path and avoid recursive ResolveDataRoot; add legacy-state handling and extraction in OobeStateService; and update component settings tests to reflect migrated storage (DB/backup) and reset cache for test reloads. Various csproj, tests, and docs updated to reflect the migration and ensure build/test compatibility.

* Update icon glyphs and symbol mappings

Replace and refine icon sources across settings pages and controls: many FAFontIconSource glyphs were updated to specific Seagull Fluent Icons codepoints, some FASymbolIconSource usages were replaced with FAFontIconSource, and a number of symbol-to-Symbol enum mappings were adjusted (e.g. "Bell" -> AlertOn, "Shield" -> ShieldLock). Also clarified a comment in SettingsWindow and fixed a trailing newline in StudySettingsPage. Changes standardize icon visuals and bridge FluentIcons glyphs into FluentAvalonia icon sources.

* fix.修复合并产生的问题。
2026-04-29 12:14:29 +08:00

254 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Styling;
using FluentIcons.Common;
using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Services;
using LanMountainDesktop.Theme;
using Material.Styles.Themes;
using Material.Styles.Themes.Base;
using Material.Icons;
using Material.Icons.Avalonia;
namespace LanMountainDesktop.Views;
public partial class ComponentEditorWindow : Window
{
private readonly Dictionary<string, Size> _sizeCache = new(StringComparer.OrdinalIgnoreCase);
private readonly CustomMaterialTheme _materialTheme;
private DesktopComponentEditorDescriptor? _descriptor;
private string? _currentComponentId;
private bool _suppressAspectRatioCorrection;
private Size _lastStableSize;
public ComponentEditorWindow()
{
InitializeComponent();
_materialTheme = Styles.OfType<CustomMaterialTheme>().FirstOrDefault()
?? throw new InvalidOperationException("Component editor Material theme is missing.");
_lastStableSize = new Size(Width, Height);
ApplyChromeMode(useSystemChrome: false);
}
public void ApplyDescriptor(
DesktopComponentEditorDescriptor descriptor,
DesktopComponentEditorContext context)
{
_descriptor = descriptor ?? throw new ArgumentNullException(nameof(descriptor));
_currentComponentId = context.ComponentId;
var editor = descriptor.CreateEditor(context);
EditorContentHost.Content = editor;
TitleTextBlock.Text = descriptor.Definition.DisplayName;
HeaderIcon.Kind = ResolveSymbol(descriptor.Definition.IconKey);
Title = descriptor.Definition.DisplayName;
ApplyPreferredSize(descriptor);
}
public void ApplyChromeMode(bool useSystemChrome)
{
var preferSystemChrome = useSystemChrome || OperatingSystem.IsMacOS();
if (preferSystemChrome)
{
ExtendClientAreaToDecorationsHint = true;
WindowDecorations = WindowDecorations.Full;
CustomTitleBarHost.IsVisible = false;
return;
}
WindowDecorations = WindowDecorations.BorderOnly;
ExtendClientAreaToDecorationsHint = true;
CustomTitleBarHost.IsVisible = true;
}
internal void ApplyTheme(ComponentEditorThemePalette palette)
{
ArgumentNullException.ThrowIfNull(palette);
RequestedThemeVariant = palette.IsNightMode ? ThemeVariant.Dark : ThemeVariant.Light;
_materialTheme.BaseTheme = palette.IsNightMode ? BaseThemeMode.Dark : BaseThemeMode.Light;
_materialTheme.PrimaryColor = palette.PrimaryColor;
_materialTheme.SecondaryColor = palette.SecondaryColor;
SetBrushResource("EditorPrimaryBrush", palette.PrimaryColor);
SetBrushResource("EditorOnPrimaryBrush", palette.IsNightMode ? Colors.Black : Colors.White);
SetBrushResource("EditorSecondaryBrush", palette.SecondaryColor);
SetBrushResource("EditorTertiaryBrush", palette.TertiaryColor);
SetBrushResource("EditorWindowBackgroundBrush", palette.WindowBackgroundColor);
SetBrushResource("EditorSurfaceBrush", palette.SurfaceColor);
SetBrushResource("EditorSurfaceContainerBrush", palette.SurfaceContainerColor);
SetBrushResource("EditorSurfaceContainerHighBrush", palette.SurfaceContainerHighColor);
SetBrushResource("EditorSelectFieldBackgroundBrush", palette.SurfaceContainerHighColor);
SetBrushResource(
"EditorSelectFieldHoverBrush",
ColorMath.Blend(palette.SurfaceContainerHighColor, palette.PrimaryColor, palette.IsNightMode ? 0.18 : 0.08));
SetBrushResource(
"EditorSelectFieldFocusBrush",
ColorMath.Blend(palette.SurfaceContainerHighColor, palette.PrimaryColor, palette.IsNightMode ? 0.24 : 0.12));
SetBrushResource("EditorSelectOutlineBrush", palette.OutlineColor);
SetBrushResource(
"EditorSelectOutlineStrongBrush",
ColorMath.EnsureContrast(palette.PrimaryColor, palette.SurfaceContainerHighColor, 3.0));
SetBrushResource(
"EditorSelectMenuItemHoverBrush",
ColorMath.Blend(palette.SurfaceContainerColor, palette.PrimaryColor, palette.IsNightMode ? 0.20 : 0.10));
SetBrushResource(
"EditorSelectMenuItemSelectedBrush",
ColorMath.Blend(palette.SurfaceContainerColor, palette.PrimaryColor, palette.IsNightMode ? 0.30 : 0.16));
SetBrushResource("EditorTopAppBarBackgroundBrush", palette.TopAppBarColor);
SetBrushResource("EditorHeaderIconBackgroundBrush", palette.HeaderIconBackgroundColor);
SetBrushResource("EditorTitleBarButtonHoverBrush", palette.TitleBarButtonHoverColor);
SetBrushResource("ComponentEditorHeroBackgroundBrush", palette.SurfaceContainerHighColor);
SetBrushResource("ComponentEditorCardBackgroundBrush", palette.SurfaceContainerColor);
SetBrushResource("ComponentEditorCardBorderBrush", palette.OutlineColor);
SetBrushResource("ComponentEditorPrimaryTextBrush", palette.OnSurfaceColor);
SetBrushResource("ComponentEditorSecondaryTextBrush", palette.OnSurfaceVariantColor);
SetBrushResource("EditorDividerBrush", palette.DividerColor);
}
protected override void OnSizeChanged(SizeChangedEventArgs e)
{
base.OnSizeChanged(e);
if (_descriptor is null || _suppressAspectRatioCorrection)
{
_lastStableSize = e.NewSize;
return;
}
var correctedSize = CoerceSize(e.NewSize, e.PreviousSize, _descriptor);
if (Math.Abs(correctedSize.Width - e.NewSize.Width) < 0.5 &&
Math.Abs(correctedSize.Height - e.NewSize.Height) < 0.5)
{
_lastStableSize = correctedSize;
CacheCurrentSize();
return;
}
_suppressAspectRatioCorrection = true;
Width = correctedSize.Width;
Height = correctedSize.Height;
_suppressAspectRatioCorrection = false;
_lastStableSize = correctedSize;
CacheCurrentSize();
}
private void SetBrushResource(string key, Color color)
{
Resources[key] = new SolidColorBrush(color);
}
private void ApplyPreferredSize(DesktopComponentEditorDescriptor descriptor)
{
var width = descriptor.PreferredWidth;
var height = descriptor.PreferredHeight;
if (!string.IsNullOrWhiteSpace(_currentComponentId) &&
_sizeCache.TryGetValue(_currentComponentId, out var cached))
{
width = cached.Width;
height = cached.Height;
}
_suppressAspectRatioCorrection = true;
MinWidth = descriptor.PreferredWidth * descriptor.MinScale;
MinHeight = descriptor.PreferredHeight * descriptor.MinScale;
MaxWidth = descriptor.PreferredWidth * descriptor.MaxScale;
MaxHeight = descriptor.PreferredHeight * descriptor.MaxScale;
Width = width;
Height = height;
_lastStableSize = new Size(width, height);
_suppressAspectRatioCorrection = false;
}
private void CacheCurrentSize()
{
if (_descriptor is null || string.IsNullOrWhiteSpace(_currentComponentId))
{
return;
}
_sizeCache[_currentComponentId] = _lastStableSize;
}
private static Size CoerceSize(Size currentSize, Size previousSize, DesktopComponentEditorDescriptor descriptor)
{
var preferredWidth = descriptor.PreferredWidth;
var preferredHeight = descriptor.PreferredHeight;
var aspectRatio = descriptor.AspectRatio;
var minWidth = preferredWidth * descriptor.MinScale;
var maxWidth = preferredWidth * descriptor.MaxScale;
var minHeight = preferredHeight * descriptor.MinScale;
var maxHeight = preferredHeight * descriptor.MaxScale;
var deltaWidth = Math.Abs(currentSize.Width - previousSize.Width);
var deltaHeight = Math.Abs(currentSize.Height - previousSize.Height);
double width;
double height;
if (deltaWidth >= deltaHeight)
{
width = Math.Clamp(currentSize.Width, minWidth, maxWidth);
height = Math.Clamp(width / aspectRatio, minHeight, maxHeight);
width = Math.Clamp(height * aspectRatio, minWidth, maxWidth);
}
else
{
height = Math.Clamp(currentSize.Height, minHeight, maxHeight);
width = Math.Clamp(height * aspectRatio, minWidth, maxWidth);
height = Math.Clamp(width / aspectRatio, minHeight, maxHeight);
}
return new Size(width, height);
}
private void OnWindowTitleBarPointerPressed(object? sender, PointerPressedEventArgs e)
{
_ = sender;
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
{
BeginMoveDrag(e);
}
}
private static MaterialIconKind ResolveSymbol(string iconKey)
{
return iconKey switch
{
"Clock" => MaterialIconKind.Clock,
"Timer" => MaterialIconKind.Timer,
"WeatherSunny" => MaterialIconKind.WeatherSunny,
"CalendarDate" => MaterialIconKind.CalendarRange,
"CalendarMonth" => MaterialIconKind.CalendarMonth,
"MicOn" => MaterialIconKind.Microphone,
"News" => MaterialIconKind.Newspaper,
"Image" => MaterialIconKind.Image,
"Book" => MaterialIconKind.BookOpenVariant,
"History" => MaterialIconKind.History,
"DataLine" => MaterialIconKind.ChartLine,
"Edit" => MaterialIconKind.Pencil,
"Calculator" => MaterialIconKind.Calculator,
"Storage" => MaterialIconKind.UsbFlashDrive,
"Globe" => MaterialIconKind.Web,
"Play" => MaterialIconKind.Play,
_ => MaterialIconKind.Settings
};
}
private void OnCloseClick(object? sender, RoutedEventArgs e)
{
_ = sender;
_ = e;
Close();
}
}