fix.修智教hub组件

This commit is contained in:
lincube
2026-04-02 15:24:59 +08:00
parent 964cef27ee
commit ff014717fa
6 changed files with 315 additions and 14 deletions

View File

@@ -481,9 +481,9 @@ public partial class App : Application
RestoreOrCreateMainWindow(showSingleInstanceNotice: true, source: "SingleInstance"); RestoreOrCreateMainWindow(showSingleInstanceNotice: true, source: "SingleInstance");
} }
private void RestoreOrCreateMainWindow(bool showSingleInstanceNotice, string source) private async void RestoreOrCreateMainWindow(bool showSingleInstanceNotice, string source)
{ {
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(async () =>
{ {
if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
{ {
@@ -498,6 +498,13 @@ public partial class App : Application
if (!mainWindow.IsVisible) if (!mainWindow.IsVisible)
{ {
mainWindow.Show(); mainWindow.Show();
if (mainWindow._isFirstLaunchAfterOpen)
{
mainWindow._isFirstLaunchAfterOpen = false;
mainWindow.ForceDesktopPageToFirst();
}
await mainWindow.SlideInAsync();
} }
if (mainWindow.WindowState == WindowState.Minimized) if (mainWindow.WindowState == WindowState.Minimized)
@@ -879,10 +886,11 @@ public partial class App : Application
SetDesktopShellState(DesktopShellState.ForegroundDesktop, "MainWindowRestored"); SetDesktopShellState(DesktopShellState.ForegroundDesktop, "MainWindowRestored");
} }
private void HideMainWindowToTray(MainWindow mainWindow, string source) private async void HideMainWindowToTray(MainWindow mainWindow, string source)
{ {
try try
{ {
await mainWindow.SlideOutAsync();
mainWindow.ShowInTaskbar = false; mainWindow.ShowInTaskbar = false;
mainWindow.Hide(); mainWindow.Hide();
SetDesktopShellState(DesktopShellState.TrayOnly, source); SetDesktopShellState(DesktopShellState.TrayOnly, source);

View File

@@ -0,0 +1,147 @@
using System;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.Threading;
using LanMountainDesktop.Theme;
namespace LanMountainDesktop.Behaviors;
public static class WindowSlideAnimationBehavior
{
private static readonly Easing DecelerateEasing = Easing.Parse(FluttermotionToken.StandardBezier);
private static readonly Easing AccelerateEasing = new CubicEaseIn();
public static readonly TimeSpan SlideInDuration = TimeSpan.FromMilliseconds(350);
public static readonly TimeSpan SlideOutDuration = TimeSpan.FromMilliseconds(280);
public static async Task SlideInAsync(Window window, Border desktopHost)
{
if (window is null || desktopHost is null)
{
return;
}
var screenWidth = Math.Max(1, window.Bounds.Width > 1 ? window.Bounds.Width : PrimaryScreenWidth(window));
var transform = EnsureTranslateTransform(desktopHost);
transform.X = screenWidth;
desktopHost.Opacity = 1;
window.Show();
if (screenWidth <= 1)
{
transform.X = 0;
return;
}
var animation = new Animation
{
Duration = SlideInDuration,
Easing = DecelerateEasing,
Children =
{
new KeyFrame
{
Cue = new Cue(0d),
Setters = { new Setter(TranslateTransform.XProperty, screenWidth) }
},
new KeyFrame
{
Cue = new Cue(1d),
Setters = { new Setter(TranslateTransform.XProperty, 0d) }
}
}
};
await animation.RunAsync(desktopHost);
}
public static async Task SlideOutAsync(Window window, Border desktopHost, Action? onCompleted = null)
{
if (window is null || desktopHost is null)
{
onCompleted?.Invoke();
return;
}
var screenWidth = Math.Max(1, window.Bounds.Width > 1 ? window.Bounds.Width : PrimaryScreenWidth(window));
var transform = EnsureTranslateTransform(desktopHost);
if (screenWidth <= 1)
{
onCompleted?.Invoke();
return;
}
var animation = new Animation
{
Duration = SlideOutDuration,
Easing = AccelerateEasing,
Children =
{
new KeyFrame
{
Cue = new Cue(0d),
Setters = { new Setter(TranslateTransform.XProperty, 0d) }
},
new KeyFrame
{
Cue = new Cue(1d),
Setters = { new Setter(TranslateTransform.XProperty, screenWidth) }
}
}
};
await animation.RunAsync(desktopHost);
onCompleted?.Invoke();
}
public static void ResetSlidePosition(Border desktopHost)
{
if (desktopHost is null)
{
return;
}
var transform = desktopHost.RenderTransform as TranslateTransform;
if (transform is not null)
{
transform.X = 0;
}
desktopHost.Opacity = 1;
}
private static TranslateTransform EnsureTranslateTransform(Border desktopHost)
{
if (desktopHost.RenderTransform is TranslateTransform existingTransform)
{
return existingTransform;
}
var newTransform = new TranslateTransform();
desktopHost.RenderTransform = newTransform;
return newTransform;
}
private static double PrimaryScreenWidth(Window window)
{
try
{
if (window.Screens?.Primary is { } screen)
{
return screen.WorkingArea.Width;
}
}
catch
{
}
return 1920;
}
}

View File

@@ -47,6 +47,10 @@ public partial class ZhiJiaoHubWidget : UserControl,
private bool _autoRefreshEnabled = true; private bool _autoRefreshEnabled = true;
private int _pendingImageIndex = 0; private int _pendingImageIndex = 0;
private string _lastLoadedSource = string.Empty;
private bool _lastLoadedAutoRefreshEnabled = true;
private int _lastLoadedRefreshIntervalMinutes = 30;
private IReadOnlyList<ZhiJiaoHubHybridImageItem> _images = []; private IReadOnlyList<ZhiJiaoHubHybridImageItem> _images = [];
private int _currentImageIndex = 0; private int _currentImageIndex = 0;
@@ -147,12 +151,40 @@ public partial class ZhiJiaoHubWidget : UserControl,
_placementId = context.PlacementId ?? string.Empty; _placementId = context.PlacementId ?? string.Empty;
_componentSettingsAccessor = context.ComponentSettingsAccessor; _componentSettingsAccessor = context.ComponentSettingsAccessor;
try
{
var snapshot = _componentSettingsAccessor?.LoadSnapshot<ComponentSettingsSnapshot>();
LoadSettings(); LoadSettings();
if (_isAttached) if (_isAttached)
{
if (snapshot is not null && NeedsReinitialization(snapshot))
{ {
_ = InitializeAsync(); _ = InitializeAsync();
} }
else if (_images.Count > 0)
{
_pendingImageIndex = snapshot?.ZhiJiaoHubCurrentImageIndex ?? 0;
_currentImageIndex = Math.Clamp(_pendingImageIndex, 0, Math.Max(0, _images.Count - 1));
_pendingImageIndex = 0;
if (TryDisplayCachedImage(_currentImageIndex))
{
UpdateIndicators();
}
}
else
{
_ = InitializeAsync();
}
}
}
catch
{
if (_isAttached)
{
_ = InitializeAsync();
}
}
} }
public void SetComponentPlacementContext(string componentId, string? placementId) public void SetComponentPlacementContext(string componentId, string? placementId)
@@ -163,12 +195,29 @@ public partial class ZhiJiaoHubWidget : UserControl,
public void RefreshFromSettings() public void RefreshFromSettings()
{ {
try
{
var snapshot = _componentSettingsAccessor?.LoadSnapshot<ComponentSettingsSnapshot>();
if (snapshot is null)
{
return;
}
LoadSettings(); LoadSettings();
UpdateTimers(); UpdateTimers();
if (_isAttached)
if (_isAttached && NeedsReinitialization(snapshot))
{ {
_ = InitializeAsync(); _ = InitializeAsync();
} }
else
{
_pendingImageIndex = snapshot.ZhiJiaoHubCurrentImageIndex;
}
}
catch
{
}
} }
private void LoadSettings() private void LoadSettings()
@@ -192,6 +241,24 @@ public partial class ZhiJiaoHubWidget : UserControl,
} }
} }
private bool NeedsReinitialization(ComponentSettingsSnapshot snapshot)
{
var newSource = ZhiJiaoHubSources.Normalize(snapshot.ZhiJiaoHubSource);
var newAutoRefreshEnabled = snapshot.ZhiJiaoHubAutoRefreshEnabled;
var newRefreshIntervalMinutes = Math.Clamp(snapshot.ZhiJiaoHubAutoRefreshIntervalMinutes, 5, 1440);
return newSource != _lastLoadedSource ||
newAutoRefreshEnabled != _lastLoadedAutoRefreshEnabled ||
newRefreshIntervalMinutes != _lastLoadedRefreshIntervalMinutes;
}
private void UpdateLastLoadedSettings(ComponentSettingsSnapshot snapshot)
{
_lastLoadedSource = ZhiJiaoHubSources.Normalize(snapshot.ZhiJiaoHubSource);
_lastLoadedAutoRefreshEnabled = snapshot.ZhiJiaoHubAutoRefreshEnabled;
_lastLoadedRefreshIntervalMinutes = Math.Clamp(snapshot.ZhiJiaoHubAutoRefreshIntervalMinutes, 5, 1440);
}
private void SaveCurrentImageIndex() private void SaveCurrentImageIndex()
{ {
try try
@@ -259,6 +326,12 @@ public partial class ZhiJiaoHubWidget : UserControl,
_currentImageIndex = Math.Clamp(_pendingImageIndex, 0, Math.Max(0, _images.Count - 1)); _currentImageIndex = Math.Clamp(_pendingImageIndex, 0, Math.Max(0, _images.Count - 1));
_pendingImageIndex = 0; _pendingImageIndex = 0;
var snapshot = _componentSettingsAccessor?.LoadSnapshot<ComponentSettingsSnapshot>();
if (snapshot is not null)
{
UpdateLastLoadedSettings(snapshot);
}
await Dispatcher.UIThread.InvokeAsync(() => await Dispatcher.UIThread.InvokeAsync(() =>
{ {
UpdateIndicators(); UpdateIndicators();

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@@ -375,6 +375,18 @@ public partial class MainWindow
UpdateDesktopPageAwareComponentContext(); UpdateDesktopPageAwareComponentContext();
} }
public void ForceDesktopPageToFirst()
{
if (_currentDesktopSurfaceIndex == 0)
{
return;
}
_currentDesktopSurfaceIndex = 0;
ApplyDesktopSurfaceOffset();
SchedulePersistSettings(delayMs: 120);
}
private void SetDesktopPagesHostSnapAnimationEnabled(bool enabled) private void SetDesktopPagesHostSnapAnimationEnabled(bool enabled)
{ {
if (_desktopPagesHostTransform is null) if (_desktopPagesHostTransform is null)

View File

@@ -38,6 +38,12 @@ public partial class MainWindow
return; return;
} }
// 组件实例范围的设置变更不应触发整个桌面重新加载(比如翻页保存图片索引)
if (e.Scope == SettingsScope.ComponentInstance)
{
return;
}
if (e.Scope == SettingsScope.App && e.ChangedKeys is { Count: > 0 }) if (e.Scope == SettingsScope.App && e.ChangedKeys is { Count: > 0 })
{ {
var changedKeys = e.ChangedKeys.ToArray(); var changedKeys = e.ChangedKeys.ToArray();

View File

@@ -18,6 +18,7 @@ using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using FluentAvalonia.Styling; using FluentAvalonia.Styling;
using LanMountainDesktop.Behaviors;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.PluginSdk; using LanMountainDesktop.PluginSdk;
@@ -108,6 +109,9 @@ public partial class MainWindow : Window, ISettingsWindowAnchorProvider
private bool _suppressWeatherLocationEvents; private bool _suppressWeatherLocationEvents;
private bool _suppressSettingsPersistence; private bool _suppressSettingsPersistence;
private bool _isComponentLibraryOpen; private bool _isComponentLibraryOpen;
private bool _isSlideAnimating;
private int _slideAnimationGuard;
internal bool _isFirstLaunchAfterOpen = true;
private Border? _selectedDesktopComponentHost; private Border? _selectedDesktopComponentHost;
private bool _reopenSettingsAfterComponentLibraryClose; private bool _reopenSettingsAfterComponentLibraryClose;
private TranslateTransform? _settingsContentPanelTransform; private TranslateTransform? _settingsContentPanelTransform;
@@ -785,9 +789,60 @@ public partial class MainWindow : Window, ISettingsWindowAnchorProvider
_ = cellSize; _ = cellSize;
} }
private void OnMinimizeClick(object? sender, RoutedEventArgs e) private async void OnMinimizeClick(object? sender, RoutedEventArgs e)
{
if (_isSlideAnimating)
{
return;
}
await SlideOutAsync();
}
public async Task SlideInAsync()
{
if (_isSlideAnimating || DesktopHost is null)
{
return;
}
var guard = System.Threading.Interlocked.Increment(ref _slideAnimationGuard);
_isSlideAnimating = true;
try
{
await WindowSlideAnimationBehavior.SlideInAsync(this, DesktopHost);
}
finally
{
_isSlideAnimating = false;
System.Threading.Interlocked.Decrement(ref _slideAnimationGuard);
}
}
public async Task SlideOutAsync()
{
if (_isSlideAnimating || DesktopHost is null)
{
return;
}
var guard = System.Threading.Interlocked.Increment(ref _slideAnimationGuard);
_isSlideAnimating = true;
try
{
await WindowSlideAnimationBehavior.SlideOutAsync(this, DesktopHost, () =>
{ {
WindowState = WindowState.Minimized; WindowState = WindowState.Minimized;
WindowSlideAnimationBehavior.ResetSlidePosition(DesktopHost!);
});
}
finally
{
_isSlideAnimating = false;
System.Threading.Interlocked.Decrement(ref _slideAnimationGuard);
}
} }
private void OnWindowPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) private void OnWindowPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)