diff --git a/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj b/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj index de158af..7b5f45f 100644 --- a/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj +++ b/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj @@ -25,6 +25,7 @@ + diff --git a/LanMountainDesktop.Launcher/Services/ThemeService.cs b/LanMountainDesktop.Launcher/Services/ThemeService.cs new file mode 100644 index 0000000..d5818e3 --- /dev/null +++ b/LanMountainDesktop.Launcher/Services/ThemeService.cs @@ -0,0 +1,68 @@ +using Avalonia; +using Avalonia.Styling; +using FluentAvalonia.Styling; + +namespace LanMountainDesktop.Launcher.Services; + +/// +/// 主题服务,管理启动器的主题设置 +/// +public static class ThemeService +{ + private static ThemeVariant _currentTheme = ThemeVariant.Light; + private static string _accentColor = "#0078D4"; + + /// + /// 获取当前主题 + /// + public static ThemeVariant CurrentTheme => _currentTheme; + + /// + /// 获取当前主题色 + /// + public static string AccentColor => _accentColor; + + /// + /// 应用主题设置 + /// + public static void ApplyTheme(ThemeMode mode, string accentColor) + { + _currentTheme = mode switch + { + ThemeMode.Dark => ThemeVariant.Dark, + _ => ThemeVariant.Light + }; + _accentColor = accentColor; + + // 应用到当前应用程序 + if (Application.Current is { } app) + { + app.RequestedThemeVariant = _currentTheme; + } + } + + /// + /// 应用浅色主题 + /// + public static void ApplyLightTheme(string accentColor) + { + ApplyTheme(ThemeMode.Light, accentColor); + } + + /// + /// 应用深色主题 + /// + public static void ApplyDarkTheme(string accentColor) + { + ApplyTheme(ThemeMode.Dark, accentColor); + } +} + +/// +/// 主题模式 +/// +public enum ThemeMode +{ + Light, + Dark +} diff --git a/LanMountainDesktop.Launcher/Services/UpdateEngineService.cs b/LanMountainDesktop.Launcher/Services/UpdateEngineService.cs index 8295942..7c008d7 100644 --- a/LanMountainDesktop.Launcher/Services/UpdateEngineService.cs +++ b/LanMountainDesktop.Launcher/Services/UpdateEngineService.cs @@ -22,6 +22,7 @@ internal sealed class UpdateEngineService private readonly DeploymentLocator _deploymentLocator; private readonly string _appRoot; + private readonly string _dataRoot; private readonly string _launcherRoot; private readonly string _incomingRoot; private readonly string _snapshotsRoot; @@ -30,7 +31,8 @@ internal sealed class UpdateEngineService { _deploymentLocator = deploymentLocator; _appRoot = deploymentLocator.GetAppRoot(); - _launcherRoot = Path.Combine(_appRoot, LauncherDirectoryName); + _dataRoot = new DataLocationResolver(_appRoot).ResolveDataRoot(); + _launcherRoot = Path.Combine(_dataRoot, LauncherDirectoryName); _incomingRoot = Path.Combine(_launcherRoot, UpdateDirectoryName, IncomingDirectoryName); _snapshotsRoot = Path.Combine(_launcherRoot, SnapshotsDirectoryName); } diff --git a/LanMountainDesktop.Launcher/ViewModels/DevDebugWindowViewModel.cs b/LanMountainDesktop.Launcher/ViewModels/DevDebugWindowViewModel.cs index e7a0239..b149e5a 100644 --- a/LanMountainDesktop.Launcher/ViewModels/DevDebugWindowViewModel.cs +++ b/LanMountainDesktop.Launcher/ViewModels/DevDebugWindowViewModel.cs @@ -13,6 +13,7 @@ public sealed class DevDebugWindowViewModel : INotifyPropertyChanged private bool _isErrorEnabled = true; private bool _isUpdateEnabled = true; private bool _isOobeEnabled = true; + private bool _isDataLocationEnabled = true; private string _statusMessage = "就绪"; public event PropertyChangedEventHandler? PropertyChanged; @@ -87,6 +88,23 @@ public sealed class DevDebugWindowViewModel : INotifyPropertyChanged } } + /// + /// 数据位置选择页面是否启用实际功能 + /// + public bool IsDataLocationEnabled + { + get => _isDataLocationEnabled; + set + { + if (_isDataLocationEnabled != value) + { + _isDataLocationEnabled = value; + OnPropertyChanged(); + UpdateStatus($"数据位置选择: {(value ? "功能模式" : "仅查看")}"); + } + } + } + #endregion #region 状态信息 @@ -131,6 +149,11 @@ public sealed class DevDebugWindowViewModel : INotifyPropertyChanged /// public ICommand OpenOobeCommand { get; } + /// + /// 打开数据位置选择页面命令 + /// + public ICommand OpenDataLocationCommand { get; } + /// /// 全部切换到查看模式命令 /// @@ -170,6 +193,11 @@ public sealed class DevDebugWindowViewModel : INotifyPropertyChanged /// public event EventHandler? OpenOobeRequested; + /// + /// 请求打开数据位置选择页面 + /// + public event EventHandler? OpenDataLocationRequested; + /// /// 请求关闭窗口 /// @@ -199,12 +227,18 @@ public sealed class DevDebugWindowViewModel : INotifyPropertyChanged OpenOobeRequested?.Invoke(this, new OobeOpenEventArgs(IsOobeEnabled)); }); + OpenDataLocationCommand = new RelayCommand(() => + { + OpenDataLocationRequested?.Invoke(this, new DataLocationOpenEventArgs(IsDataLocationEnabled)); + }); + SetAllViewOnlyCommand = new RelayCommand(() => { IsSplashEnabled = false; IsErrorEnabled = false; IsUpdateEnabled = false; IsOobeEnabled = false; + IsDataLocationEnabled = false; UpdateStatus("全部页面已切换到查看模式"); }); @@ -214,6 +248,7 @@ public sealed class DevDebugWindowViewModel : INotifyPropertyChanged IsErrorEnabled = true; IsUpdateEnabled = true; IsOobeEnabled = true; + IsDataLocationEnabled = true; UpdateStatus("全部页面已切换到功能模式"); }); @@ -260,4 +295,10 @@ public class OobeOpenEventArgs : EventArgs public OobeOpenEventArgs(bool isFunctional) => IsFunctional = isFunctional; } +public class DataLocationOpenEventArgs : EventArgs +{ + public bool IsFunctional { get; } + public DataLocationOpenEventArgs(bool isFunctional) => IsFunctional = isFunctional; +} + #endregion diff --git a/LanMountainDesktop.Launcher/Views/DataLocationPromptWindow.axaml.cs b/LanMountainDesktop.Launcher/Views/DataLocationPromptWindow.axaml.cs index d2cdf2a..5a2e09d 100644 --- a/LanMountainDesktop.Launcher/Views/DataLocationPromptWindow.axaml.cs +++ b/LanMountainDesktop.Launcher/Views/DataLocationPromptWindow.axaml.cs @@ -221,6 +221,7 @@ internal partial class DataLocationPromptWindow : Window { Duration = TimeSpan.FromMilliseconds(500), Easing = new CubicEaseOut(), + FillMode = FillMode.Forward, Children = { new KeyFrame @@ -240,6 +241,7 @@ internal partial class DataLocationPromptWindow : Window { Duration = TimeSpan.FromMilliseconds(500), Easing = new CubicEaseOut(), + FillMode = FillMode.Forward, Children = { new KeyFrame @@ -280,6 +282,7 @@ internal partial class DataLocationPromptWindow : Window { Duration = TimeSpan.FromMilliseconds(200), Easing = new CubicEaseIn(), + FillMode = FillMode.Forward, Children = { new KeyFrame diff --git a/LanMountainDesktop.Launcher/Views/DevDebugWindow.axaml b/LanMountainDesktop.Launcher/Views/DevDebugWindow.axaml index 7bae0ad..e04ae05 100644 --- a/LanMountainDesktop.Launcher/Views/DevDebugWindow.axaml +++ b/LanMountainDesktop.Launcher/Views/DevDebugWindow.axaml @@ -141,6 +141,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LanMountainDesktop.Launcher/Views/OobeWindow.axaml.cs b/LanMountainDesktop.Launcher/Views/OobeWindow.axaml.cs index 0c1e17a..ceff7e7 100644 --- a/LanMountainDesktop.Launcher/Views/OobeWindow.axaml.cs +++ b/LanMountainDesktop.Launcher/Views/OobeWindow.axaml.cs @@ -1,182 +1,709 @@ using Avalonia; using Avalonia.Animation; -using Avalonia.Animation.Easings; using Avalonia.Controls; +using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; using Avalonia.Media; -using Avalonia.Styling; +using Avalonia.Threading; +using LanMountainDesktop.Launcher.Models; +using LanMountainDesktop.Launcher.Services; namespace LanMountainDesktop.Launcher.Views; public partial class OobeWindow : Window { + private const int AnimationDurationMs = 300; + private const int TypingDelayMs = 100; + private readonly TaskCompletionSource _completionSource = new(); + private readonly DataLocationResolver _resolver; private bool _isTransitioning; + private bool _isDebugMode; + private int _currentStep = 1; + + // 数据位置选择 + private DataLocationMode _selectedDataLocationMode = DataLocationMode.System; + private bool _migrateExistingData; + + // 主题选择 + private Services.ThemeMode _selectedThemeMode = Services.ThemeMode.Light; + private string _selectedAccentColor = "#0078D4"; + private MonetSource _selectedMonetSource = MonetSource.Wallpaper; public OobeWindow() { AvaloniaXamlLoader.Load(this); Loaded += OnWindowLoaded; Opened += OnWindowOpened; + + var appRoot = AppDomain.CurrentDomain.BaseDirectory; + _resolver = new DataLocationResolver(appRoot); } + public void SetDebugMode(bool isDebugMode) + { + _isDebugMode = isDebugMode; + } + + public Task WaitForEnterAsync() => _completionSource.Task; + private void OnWindowLoaded(object? sender, RoutedEventArgs e) { - Console.WriteLine("[OobeWindow] Window loaded, initializing components..."); + InitializeDataLocationStep(); + SetupEventHandlers(); + } - var enterButton = this.FindControl