diff --git a/Ink Canvas/Windows/OobeWindow.xaml b/Ink Canvas/Windows/OobeWindow.xaml index eb2ad467..7161dc56 100644 --- a/Ink Canvas/Windows/OobeWindow.xaml +++ b/Ink Canvas/Windows/OobeWindow.xaml @@ -5,16 +5,17 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf" + xmlns:controls="clr-namespace:Ink_Canvas.Controls;assembly=InkCanvas.Controls" + xmlns:sys="clr-namespace:System;assembly=mscorlib" mc:Ignorable="d" Title="欢迎使用 InkCanvasForClass" - Height="480" - Width="800" - MinHeight="300" - MinWidth="400" + Height="660" + Width="980" + MinHeight="460" + MinWidth="760" WindowStartupLocation="CenterScreen" ResizeMode="CanResize" Topmost="True" - Background="{DynamicResource SettingsPageBackground}" Loaded="OobeWindow_OnLoaded" ui:ThemeManager.IsThemeAware="True" ui:TitleBar.ExtendViewIntoTitleBar="True" @@ -23,607 +24,499 @@ ui:TitleBar.Height="48"> - + + + + + + - + 4 + + + + + - - + + + + - + - + - - - + + + - - - - - - - - + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 隐私说明:本软件不会收集课堂内容、学生个人信息或可识别您的原始墨迹内容,详情可查看安装目录中的 privacy.txt。 - - - - 启动与更新选项(对应 设置 → 启动) - - - - - - - - - - - 画笔光标、压感、墨迹显示等可在 设置 → 画板和墨迹 中修改。 - - - - - - - - - - - - 双指缩放、平移、旋转及手掌擦等可在 设置 → 手势操作 中修改。 - - - - - - - - - - - - - 手绘图形识别为标准形状等可在 设置 → 墨迹纠正 中修改。 - - - - - - - - - - 主题、启动动画、托盘图标等均可在 设置 → 外观 中修改。 - - - - - - - - - - - - - - - - - - - - - - - - - - 具体快捷键可在 设置 → 快捷键设置 中配置。 - - - - - - - - - - 发生未处理异常时的行为可在 设置 → 崩溃处理 中修改。 - - - - - - - - - - - - - 以下选项均可在 设置 → PPT 中修改,用于与 PowerPoint/WPS 放映联动。 - - - - - - - - - - - - - - - - - 进入/退出特定软件时自动收纳、墨迹自动保存、悬浮窗拦截等可在 设置 → 自动化行为 中修改。 - - - - - - - - - - - - 点名窗口样式与行为可在 设置 → 随机点名 中修改。 - - - - - - - - - - 日志、特殊屏幕、窗口模式等可在 设置 → 高级选项 中修改。 - - - - - - - - - - 清屏截图、按日期保存等可在 设置 → 截图和屏幕捕捉 中修改。 - - - - - - + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + - 此向导仅在首次启动时出现;之后可在 设置 中按侧边栏分区修改对应选项。 - + Margin="0,0,16,0" + Style="{DynamicResource CaptionTextBlockStyle}" + Opacity="0.7" + Text="1 / 12" /> - + - - + \ No newline at end of file diff --git a/Ink Canvas/Windows/OobeWindow.xaml.cs b/Ink Canvas/Windows/OobeWindow.xaml.cs index 42eca683..d3de118f 100644 --- a/Ink Canvas/Windows/OobeWindow.xaml.cs +++ b/Ink Canvas/Windows/OobeWindow.xaml.cs @@ -1,22 +1,27 @@ +using iNKORE.UI.WPF.Modern.Common.IconKeys; using System; using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Animation; namespace Ink_Canvas.Windows { /// - /// 首次启动体验(OOBE)窗口,用于引导用户选择遥测与隐私设置。 + /// 首次启动体验(OOBE)窗口,使用与设置窗口一致的卡片化 UI 引导用户完成初始配置。 + /// 切换步骤时使用横向滑动 + 淡入,模仿 ClassIsland 的设置向导动画风格。 /// public partial class OobeWindow : Window { private readonly Settings _settings; private int _currentStep = 0; private const int MaxStepIndex = 11; + private const int StepCount = 12; + + private FrameworkElement[] _stepPanels; + private static readonly TimeSpan SlideDuration = TimeSpan.FromMilliseconds(280); + private static readonly IEasingFunction SlideEase = new CubicEase { EasingMode = EasingMode.EaseOut }; - /// - /// 初始化 OobeWindow,并使用指定的 Settings 填充初始状态与界面。 - /// - /// 用于读取和写入用户首选项的 Settings 实例。 - /// 为 null 时抛出。 public OobeWindow(Settings settings) { if (settings == null) throw new ArgumentNullException(nameof(settings)); @@ -24,598 +29,515 @@ namespace Ink_Canvas.Windows _settings = settings; InitializeComponent(); - // 初始时设置为透明,等待加载完成后淡入 Opacity = 0; + _stepPanels = new FrameworkElement[] + { + StepTelemetryPanel, + StepCanvasPanel, + StepGesturesPanel, + StepInkRecognitionPanel, + StepAppearancePanel, + StepShortcutsPanel, + StepCrashActionPanel, + StepPptPanel, + StepAutomationPanel, + StepLuckyRandomPanel, + StepAdvancedPanel, + StepSnapshotPanel, + }; + InitializeFromSettings(); - UpdateStepUI(); + UpdateStepUI(animateDirection: 0, instant: true); } - /// - /// 从当前 Settings 对象将相关首选项映射并回显到各个 OOBE 界面控件中,以反映用户已保存的配置状态。 - /// - /// - /// 对各个配置分组(外观、启动、托盘、PPT、画板、手势、墨迹纠正、快捷键、崩溃处理、自动化、随机点名、高级选项、截图等)分别进行读取并更新对应控件的选中/选项状态;在初始化每个分组时会捕获并忽略异常,避免单个分组的错误影响窗口启动流程。 - /// + #region Settings IO + private void InitializeFromSettings() { - // 根据当前设置回显遥测选项 - switch (_settings.Startup.TelemetryUploadLevel) - { - case TelemetryUploadLevel.Basic: - RadioTelemetryBasic.IsChecked = true; - break; - case TelemetryUploadLevel.Extended: - RadioTelemetryExtended.IsChecked = true; - break; - case TelemetryUploadLevel.None: - default: - RadioTelemetryNone.IsChecked = true; - break; - } - - // 主题与外观设置 - try - { - if (_settings.Appearance != null) - { - switch (_settings.Appearance.Theme) - { - case 0: // 浅色 - RadioThemeLight.IsChecked = true; - break; - case 1: // 深色 - RadioThemeDark.IsChecked = true; - break; - case 2: // 跟随系统 - default: - RadioThemeFollowSystem.IsChecked = true; - break; - } - - CheckBoxEnableSplashScreen.IsChecked = _settings.Appearance.EnableSplashScreen; - } - } - catch - { - // 忽略外观初始化异常,避免影响启动 - } - - // 启动行为设置 try { if (_settings.Startup != null) { - CheckBoxFoldAtStartup.IsChecked = _settings.Startup.IsFoldAtStartup; - CheckBoxAutoUpdate.IsChecked = _settings.Startup.IsAutoUpdate; + ComboBoxTelemetryUploadLevel.SelectedIndex = (int)_settings.Startup.TelemetryUploadLevel; + CardFoldAtStartup.IsOn = _settings.Startup.IsFoldAtStartup; + CardAutoUpdate.IsOn = _settings.Startup.IsAutoUpdate; + ComboBoxCrashAction.SelectedIndex = _settings.Startup.CrashAction == 0 ? 0 : 1; + CheckBoxPrivacyAccepted.IsChecked = _settings.Startup.HasAcceptedTelemetryPrivacy; } } - catch - { - // 忽略启动行为初始化异常 - } + catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 托盘与快速面板 - try - { - if (_settings.Appearance != null) - { - CheckBoxEnableTrayIcon.IsChecked = _settings.Appearance.EnableTrayIcon; - CheckBoxShowQuickPanel.IsChecked = _settings.Appearance.IsShowQuickPanel; - } - } - catch - { - // 忽略托盘/快速面板初始化异常 - } - - // PPT 联动 - try - { - if (_settings.PowerPointSettings != null) - { - CheckBoxPptSupport.IsChecked = _settings.PowerPointSettings.PowerPointSupport; - CheckBoxPptAutoSaveStrokes.IsChecked = _settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint; - CheckBoxPptAutoSaveScreenshots.IsChecked = _settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint; - CheckBoxPptTimeCapsule.IsChecked = _settings.PowerPointSettings.EnablePPTTimeCapsule; - } - } - catch - { - // 忽略 PPT 联动初始化异常 - } - - // 画板和墨迹 try { if (_settings.Canvas != null) { - CheckBoxShowCursor.IsChecked = _settings.Canvas.IsShowCursor; - CheckBoxDisablePressure.IsChecked = _settings.Canvas.DisablePressure; - CheckBoxHideStrokeWhenSelecting.IsChecked = _settings.Canvas.HideStrokeWhenSelecting; + CardShowCursor.IsOn = _settings.Canvas.IsShowCursor; + CardDisablePressure.IsOn = _settings.Canvas.DisablePressure; + CardHideStrokeWhenSelecting.IsOn = _settings.Canvas.HideStrokeWhenSelecting; + CardEnablePalmEraser.IsOn = _settings.Canvas.EnablePalmEraser; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 手势操作 try { if (_settings.Gesture != null) { - CheckBoxTwoFingerZoom.IsChecked = _settings.Gesture.IsEnableTwoFingerZoom; - CheckBoxTwoFingerTranslate.IsChecked = _settings.Gesture.IsEnableTwoFingerTranslate; - CheckBoxAutoSwitchTwoFingerGesture.IsChecked = _settings.Gesture.AutoSwitchTwoFingerGesture; - CheckBoxEnablePalmEraser.IsChecked = _settings.Canvas != null && _settings.Canvas.EnablePalmEraser; + CardTwoFingerZoom.IsOn = _settings.Gesture.IsEnableTwoFingerZoom; + CardTwoFingerTranslate.IsOn = _settings.Gesture.IsEnableTwoFingerTranslate; + CardAutoSwitchTwoFingerGesture.IsOn = _settings.Gesture.AutoSwitchTwoFingerGesture; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 墨迹纠正 try { if (_settings.InkToShape != null) { - CheckBoxInkToShapeEnabled.IsChecked = _settings.InkToShape.IsInkToShapeEnabled; + CardInkToShapeEnabled.IsOn = _settings.InkToShape.IsInkToShapeEnabled; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 快捷键(外观) try { if (_settings.Appearance != null) { - CheckBoxEnableHotkeysInMouseMode.IsChecked = _settings.Appearance.EnableHotkeysInMouseMode; + int themeIndex = _settings.Appearance.Theme; + if (themeIndex < 0 || themeIndex > 2) themeIndex = 2; + ComboBoxTheme.SelectedIndex = themeIndex; + CardEnableSplashScreen.IsOn = _settings.Appearance.EnableSplashScreen; + CardEnableTrayIcon.IsOn = _settings.Appearance.EnableTrayIcon; + CardShowQuickPanel.IsOn = _settings.Appearance.IsShowQuickPanel; + CardEnableHotkeysInMouseMode.IsOn = _settings.Appearance.EnableHotkeysInMouseMode; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 崩溃处理 try { - RadioCrashSilentRestart.IsChecked = _settings.Startup.CrashAction == 0; - RadioCrashNoAction.IsChecked = _settings.Startup.CrashAction != 0; + if (_settings.PowerPointSettings != null) + { + CardPptSupport.IsOn = _settings.PowerPointSettings.PowerPointSupport; + CardPptAutoSaveStrokes.IsOn = _settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint; + CardPptAutoSaveScreenshots.IsOn = _settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint; + CardPptTimeCapsule.IsOn = _settings.PowerPointSettings.EnablePPTTimeCapsule; + } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 自动化行为 try { if (_settings.Automation != null) { - CheckBoxAutoFoldInPPTSlideShow.IsChecked = _settings.Automation.IsAutoFoldInPPTSlideShow; - CheckBoxEnableAutoSaveStrokes.IsChecked = _settings.Automation.IsEnableAutoSaveStrokes; + CardAutoFoldInPPTSlideShow.IsOn = _settings.Automation.IsAutoFoldInPPTSlideShow; + CardEnableAutoSaveStrokes.IsOn = _settings.Automation.IsEnableAutoSaveStrokes; if (_settings.Automation.FloatingWindowInterceptor != null) { - CheckBoxFloatingWindowInterceptorEnabled.IsChecked = _settings.Automation.FloatingWindowInterceptor.IsEnabled; + CardFloatingWindowInterceptor.IsOn = _settings.Automation.FloatingWindowInterceptor.IsEnabled; } + CardAutoSaveStrokesAtClear.IsOn = _settings.Automation.IsAutoSaveStrokesAtClear; + CardSaveScreenshotsInDateFolders.IsOn = _settings.Automation.IsSaveScreenshotsInDateFolders; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 随机点名 try { if (_settings.RandSettings != null) { - CheckBoxShowRandomAndSingleDraw.IsChecked = _settings.RandSettings.ShowRandomAndSingleDraw; + CardShowRandomAndSingleDraw.IsOn = _settings.RandSettings.ShowRandomAndSingleDraw; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 高级选项 try { if (_settings.Advanced != null) { - CheckBoxIsLogEnabled.IsChecked = _settings.Advanced.IsLogEnabled; - } - } - catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - - // 截图(自动化中的截图相关) - try - { - if (_settings.Automation != null) - { - CheckBoxAutoSaveStrokesAtClear.IsChecked = _settings.Automation.IsAutoSaveStrokesAtClear; - CheckBoxSaveScreenshotsInDateFolders.IsChecked = _settings.Automation.IsSaveScreenshotsInDateFolders; + CardIsLogEnabled.IsOn = _settings.Advanced.IsLogEnabled; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } - /// - /// 将当前界面上用户的选择写回到 Settings 对象并标记已接受遥测隐私说明。 - /// - /// - /// 更新的设置包括:启动时的遥测级别、主题与启动外观、启动行为、托盘/快速面板、PowerPoint 联动、画板与墨迹选项、手势设置、墨迹纠正、鼠标模式快捷键、崩溃处理策略、自动化相关设置、随机点名设置以及高级日志选项;操作中对各子配置块采用防护性写回(局部异常被忽略)。方法结束时会将 HasAcceptedTelemetryPrivacy 置为 true。 - /// private void ApplySelection() { - // 将当前遥测选项写回到设置 - if (RadioTelemetryBasic.IsChecked == true) - { - _settings.Startup.TelemetryUploadLevel = TelemetryUploadLevel.Basic; - } - else if (RadioTelemetryExtended.IsChecked == true) - { - _settings.Startup.TelemetryUploadLevel = TelemetryUploadLevel.Extended; - } - else - { - _settings.Startup.TelemetryUploadLevel = TelemetryUploadLevel.None; - } - - // 写回主题与外观设置 - try - { - if (_settings.Appearance != null) - { - if (RadioThemeLight.IsChecked == true) - { - _settings.Appearance.Theme = 0; - } - else if (RadioThemeDark.IsChecked == true) - { - _settings.Appearance.Theme = 1; - } - else - { - // 默认视为跟随系统 - _settings.Appearance.Theme = 2; - } - - _settings.Appearance.EnableSplashScreen = CheckBoxEnableSplashScreen.IsChecked == true; - } - } - catch - { - // 忽略外观写回异常 - } - - // 写回启动行为设置 try { if (_settings.Startup != null) { - _settings.Startup.IsFoldAtStartup = CheckBoxFoldAtStartup.IsChecked == true; - _settings.Startup.IsAutoUpdate = CheckBoxAutoUpdate.IsChecked == true; + int level = ComboBoxTelemetryUploadLevel.SelectedIndex; + if (level < 0) level = 0; + _settings.Startup.TelemetryUploadLevel = (TelemetryUploadLevel)level; + _settings.Startup.IsFoldAtStartup = CardFoldAtStartup.IsOn; + _settings.Startup.IsAutoUpdate = CardAutoUpdate.IsOn; + _settings.Startup.CrashAction = ComboBoxCrashAction.SelectedIndex == 1 ? 1 : 0; + _settings.Startup.HasAcceptedTelemetryPrivacy = CheckBoxPrivacyAccepted.IsChecked == true; } } - catch - { - // 忽略启动行为写回异常 - } + catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 写回托盘与快速面板设置 - try - { - if (_settings.Appearance != null) - { - _settings.Appearance.EnableTrayIcon = CheckBoxEnableTrayIcon.IsChecked == true; - _settings.Appearance.IsShowQuickPanel = CheckBoxShowQuickPanel.IsChecked == true; - } - } - catch - { - // 忽略托盘/快速面板写回异常 - } - - // 写回 PPT 联动设置 - try - { - if (_settings.PowerPointSettings != null) - { - _settings.PowerPointSettings.PowerPointSupport = CheckBoxPptSupport.IsChecked == true; - _settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint = CheckBoxPptAutoSaveStrokes.IsChecked == true; - _settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint = CheckBoxPptAutoSaveScreenshots.IsChecked == true; - _settings.PowerPointSettings.EnablePPTTimeCapsule = CheckBoxPptTimeCapsule.IsChecked == true; - } - } - catch - { - // 忽略 PPT 联动写回异常 - } - - // 写回画板和墨迹 try { if (_settings.Canvas != null) { - _settings.Canvas.IsShowCursor = CheckBoxShowCursor.IsChecked == true; - _settings.Canvas.DisablePressure = CheckBoxDisablePressure.IsChecked == true; - _settings.Canvas.HideStrokeWhenSelecting = CheckBoxHideStrokeWhenSelecting.IsChecked == true; - _settings.Canvas.EnablePalmEraser = CheckBoxEnablePalmEraser.IsChecked == true; + _settings.Canvas.IsShowCursor = CardShowCursor.IsOn; + _settings.Canvas.DisablePressure = CardDisablePressure.IsOn; + _settings.Canvas.HideStrokeWhenSelecting = CardHideStrokeWhenSelecting.IsOn; + _settings.Canvas.EnablePalmEraser = CardEnablePalmEraser.IsOn; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 写回手势操作 try { if (_settings.Gesture != null) { - _settings.Gesture.IsEnableTwoFingerZoom = CheckBoxTwoFingerZoom.IsChecked == true; - _settings.Gesture.IsEnableTwoFingerTranslate = CheckBoxTwoFingerTranslate.IsChecked == true; - _settings.Gesture.AutoSwitchTwoFingerGesture = CheckBoxAutoSwitchTwoFingerGesture.IsChecked == true; + _settings.Gesture.IsEnableTwoFingerZoom = CardTwoFingerZoom.IsOn; + _settings.Gesture.IsEnableTwoFingerTranslate = CardTwoFingerTranslate.IsOn; + _settings.Gesture.AutoSwitchTwoFingerGesture = CardAutoSwitchTwoFingerGesture.IsOn; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 写回墨迹纠正 try { if (_settings.InkToShape != null) { - _settings.InkToShape.IsInkToShapeEnabled = CheckBoxInkToShapeEnabled.IsChecked == true; + _settings.InkToShape.IsInkToShapeEnabled = CardInkToShapeEnabled.IsOn; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 写回快捷键(外观) try { if (_settings.Appearance != null) { - _settings.Appearance.EnableHotkeysInMouseMode = CheckBoxEnableHotkeysInMouseMode.IsChecked == true; + int themeIndex = ComboBoxTheme.SelectedIndex; + if (themeIndex < 0) themeIndex = 2; + _settings.Appearance.Theme = themeIndex; + _settings.Appearance.EnableSplashScreen = CardEnableSplashScreen.IsOn; + _settings.Appearance.EnableTrayIcon = CardEnableTrayIcon.IsOn; + _settings.Appearance.IsShowQuickPanel = CardShowQuickPanel.IsOn; + _settings.Appearance.EnableHotkeysInMouseMode = CardEnableHotkeysInMouseMode.IsOn; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 写回崩溃处理(0=静默重启,1=无操作) try { - _settings.Startup.CrashAction = RadioCrashNoAction.IsChecked == true ? 1 : 0; + if (_settings.PowerPointSettings != null) + { + _settings.PowerPointSettings.PowerPointSupport = CardPptSupport.IsOn; + _settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint = CardPptAutoSaveStrokes.IsOn; + _settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint = CardPptAutoSaveScreenshots.IsOn; + _settings.PowerPointSettings.EnablePPTTimeCapsule = CardPptTimeCapsule.IsOn; + } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 写回自动化行为 try { if (_settings.Automation != null) { - _settings.Automation.IsAutoFoldInPPTSlideShow = CheckBoxAutoFoldInPPTSlideShow.IsChecked == true; - _settings.Automation.IsEnableAutoSaveStrokes = CheckBoxEnableAutoSaveStrokes.IsChecked == true; - _settings.Automation.IsAutoSaveStrokesAtClear = CheckBoxAutoSaveStrokesAtClear.IsChecked == true; - _settings.Automation.IsSaveScreenshotsInDateFolders = CheckBoxSaveScreenshotsInDateFolders.IsChecked == true; + _settings.Automation.IsAutoFoldInPPTSlideShow = CardAutoFoldInPPTSlideShow.IsOn; + _settings.Automation.IsEnableAutoSaveStrokes = CardEnableAutoSaveStrokes.IsOn; + _settings.Automation.IsAutoSaveStrokesAtClear = CardAutoSaveStrokesAtClear.IsOn; + _settings.Automation.IsSaveScreenshotsInDateFolders = CardSaveScreenshotsInDateFolders.IsOn; if (_settings.Automation.FloatingWindowInterceptor != null) { - _settings.Automation.FloatingWindowInterceptor.IsEnabled = CheckBoxFloatingWindowInterceptorEnabled.IsChecked == true; + _settings.Automation.FloatingWindowInterceptor.IsEnabled = CardFloatingWindowInterceptor.IsOn; } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 写回随机点名 try { if (_settings.RandSettings != null) { - _settings.RandSettings.ShowRandomAndSingleDraw = CheckBoxShowRandomAndSingleDraw.IsChecked == true; + _settings.RandSettings.ShowRandomAndSingleDraw = CardShowRandomAndSingleDraw.IsOn; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 写回高级选项 try { if (_settings.Advanced != null) { - _settings.Advanced.IsLogEnabled = CheckBoxIsLogEnabled.IsChecked == true; + _settings.Advanced.IsLogEnabled = CardIsLogEnabled.IsOn; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - - // 标记用户已经阅读并确认过隐私说明 - _settings.Startup.HasAcceptedTelemetryPrivacy = true; } - /// - /// 处理“确认/下一步”按钮的点击:在未到最后一步时前进到下一步,若已是最后一步则应用当前选择并关闭窗口。 - /// - /// 触发事件的源对象。 - /// 路由事件参数。 + #endregion + + #region Navigation + private void BtnConfirm_Click(object sender, RoutedEventArgs e) { - // 如果还没到最后一步,则进入下一步 - if (_currentStep < MaxStepIndex) + if (_currentStep == 0 && CheckBoxPrivacyAccepted.IsChecked != true) { - _currentStep++; - UpdateStepUI(); + MessageBox.Show(this, + "请先勾选\"我已阅读并同意《隐私协议》\"后再继续。", + "需要同意隐私协议", + MessageBoxButton.OK, + MessageBoxImage.Information); + return; + } + + if (_currentStep < MaxStepIndex) + { + _currentStep++; + UpdateStepUI(animateDirection: 1); return; } - // 最后一步:应用选择并关闭窗口 ApplySelection(); DialogResult = true; Close(); } - /// - /// 导航到上一步骤;若已处于第一步(索引为 0)则不做任何操作。 - /// - /// 触发此事件的源对象。 - /// 事件的路由参数。 private void BtnPreviousStep_Click(object sender, RoutedEventArgs e) { if (_currentStep <= 0) return; _currentStep--; - UpdateStepUI(); + UpdateStepUI(animateDirection: -1); } - /// - /// 在窗口加载时对窗口不透明度执行淡入动画以显示窗口。 - /// - /// - /// 使用约 220 毫秒的缓出三次方缓动实现淡入;如果动画失败,方法会立即将窗口不透明度设为 1 作为回退。 - /// + private void CheckBoxPrivacyAccepted_Changed(object sender, RoutedEventArgs e) + { + UpdateConfirmEnabled(); + } + + private bool _privacyDialogShown; + + private void HyperlinkPrivacy_Click(object sender, RoutedEventArgs e) + { + if (_privacyDialogShown) return; + _privacyDialogShown = true; + try + { + var dialog = new PrivacyAgreementWindow { Owner = this }; + bool? result = dialog.ShowDialog(); + if (result == true && dialog.UserAccepted) + { + CheckBoxPrivacyAccepted.IsChecked = true; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex); + } + } + + private void UpdateConfirmEnabled() + { + if (BtnConfirm == null || CheckBoxPrivacyAccepted == null) return; + BtnConfirm.IsEnabled = _currentStep != 0 || CheckBoxPrivacyAccepted.IsChecked == true; + } + + #endregion + private void OobeWindow_OnLoaded(object sender, RoutedEventArgs e) { try { - var animation = new System.Windows.Media.Animation.DoubleAnimation + var animation = new DoubleAnimation { From = 0, To = 1, - Duration = TimeSpan.FromMilliseconds(220), - EasingFunction = new System.Windows.Media.Animation.CubicEase - { - EasingMode = System.Windows.Media.Animation.EasingMode.EaseOut - } + Duration = TimeSpan.FromMilliseconds(260), + EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut } }; - BeginAnimation(OpacityProperty, animation); } catch { - // 动画失败时直接显示 Opacity = 1; } } - - - /// - /// 根据当前步骤索引更新向导界面:显示对应步骤面板,播放切换动画,并刷新步骤指示、标题、子标题和按钮文本/可见性。 - /// - /// - /// 若更新过程中出现异常会被捕获并忽略以避免中断主流程。 - /// - private void UpdateStepUI() + private void UpdateStepUI(int animateDirection, bool instant = false) { try { - StepTelemetryPanel.Visibility = _currentStep == 0 ? Visibility.Visible : Visibility.Collapsed; - StepCanvasPanel.Visibility = _currentStep == 1 ? Visibility.Visible : Visibility.Collapsed; - StepGesturesPanel.Visibility = _currentStep == 2 ? Visibility.Visible : Visibility.Collapsed; - StepInkRecognitionPanel.Visibility = _currentStep == 3 ? Visibility.Visible : Visibility.Collapsed; - StepAppearancePanel.Visibility = _currentStep == 4 ? Visibility.Visible : Visibility.Collapsed; - StepShortcutsPanel.Visibility = _currentStep == 5 ? Visibility.Visible : Visibility.Collapsed; - StepCrashActionPanel.Visibility = _currentStep == 6 ? Visibility.Visible : Visibility.Collapsed; - StepPptPanel.Visibility = _currentStep == 7 ? Visibility.Visible : Visibility.Collapsed; - StepAutomationPanel.Visibility = _currentStep == 8 ? Visibility.Visible : Visibility.Collapsed; - StepLuckyRandomPanel.Visibility = _currentStep == 9 ? Visibility.Visible : Visibility.Collapsed; - StepAdvancedPanel.Visibility = _currentStep == 10 ? Visibility.Visible : Visibility.Collapsed; - StepSnapshotPanel.Visibility = _currentStep == 11 ? Visibility.Visible : Visibility.Collapsed; - - FrameworkElement activePanel = null; - if (_currentStep == 0) activePanel = StepTelemetryPanel; - else if (_currentStep == 1) activePanel = StepCanvasPanel; - else if (_currentStep == 2) activePanel = StepGesturesPanel; - else if (_currentStep == 3) activePanel = StepInkRecognitionPanel; - else if (_currentStep == 4) activePanel = StepAppearancePanel; - else if (_currentStep == 5) activePanel = StepShortcutsPanel; - else if (_currentStep == 6) activePanel = StepCrashActionPanel; - else if (_currentStep == 7) activePanel = StepPptPanel; - else if (_currentStep == 8) activePanel = StepAutomationPanel; - else if (_currentStep == 9) activePanel = StepLuckyRandomPanel; - else if (_currentStep == 10) activePanel = StepAdvancedPanel; - else if (_currentStep == 11) activePanel = StepSnapshotPanel; - - if (activePanel != null) + for (int i = 0; i < _stepPanels.Length; i++) { - activePanel.Opacity = 0; - - var transform = activePanel.RenderTransform as System.Windows.Media.TranslateTransform; - if (transform == null) - { - transform = new System.Windows.Media.TranslateTransform(0, 12); - activePanel.RenderTransform = transform; - } - else - { - transform.Y = 12; - } - - var fade = new System.Windows.Media.Animation.DoubleAnimation - { - From = 0, - To = 1, - Duration = TimeSpan.FromMilliseconds(200), - EasingFunction = new System.Windows.Media.Animation.CubicEase - { - EasingMode = System.Windows.Media.Animation.EasingMode.EaseOut - } - }; - - var slide = new System.Windows.Media.Animation.DoubleAnimation - { - From = 12, - To = 0, - Duration = TimeSpan.FromMilliseconds(200), - EasingFunction = new System.Windows.Media.Animation.CubicEase - { - EasingMode = System.Windows.Media.Animation.EasingMode.EaseOut - } - }; - - activePanel.BeginAnimation(OpacityProperty, fade); - transform.BeginAnimation(System.Windows.Media.TranslateTransform.YProperty, slide); + _stepPanels[i].Visibility = i == _currentStep ? Visibility.Visible : Visibility.Collapsed; } - StepIndicatorText.Text = $"步骤 {_currentStep + 1} / 12"; - + StepIndicatorText.Text = $"步骤 {_currentStep + 1} / {StepCount}"; + FooterStepText.Text = $"{_currentStep + 1} / {StepCount}"; BtnPreviousStep.Visibility = _currentStep > 0 ? Visibility.Visible : Visibility.Collapsed; - switch (_currentStep) - { - case 0: - StepTitleText.Text = "启动时行为"; - StepSubtitleText.Text = "遥测、自动更新与启动行为,对应 设置 → 启动时行为、高级 → 遥测。"; - break; - case 1: - StepTitleText.Text = "画板和墨迹"; - StepSubtitleText.Text = "画笔光标、压感、墨迹显示,对应 设置 → 画板和墨迹。"; - break; - case 2: - StepTitleText.Text = "手势操作"; - StepSubtitleText.Text = "双指缩放/平移、手掌擦等,对应 设置 → 手势操作。"; - break; - case 3: - StepTitleText.Text = "墨迹纠正"; - StepSubtitleText.Text = "手绘图形识别为标准形状,对应 设置 → 墨迹纠正。"; - break; - case 4: - StepTitleText.Text = "个性化设置"; - StepSubtitleText.Text = "主题、启动动画、托盘与快速工具栏,对应 设置 → 个性化设置。"; - break; - case 5: - StepTitleText.Text = "快捷键设置"; - StepSubtitleText.Text = "鼠标模式下全局快捷键,对应 设置 → 快捷键设置。"; - break; - case 6: - StepTitleText.Text = "崩溃处理"; - StepSubtitleText.Text = "未处理异常时的行为,对应 设置 → 崩溃处理。"; - break; - case 7: - StepTitleText.Text = "PowerPoint 支持"; - StepSubtitleText.Text = "放映联动与墨迹保存等,对应 设置 → PowerPoint 支持。"; - break; - case 8: - StepTitleText.Text = "自动化行为"; - StepSubtitleText.Text = "自动收纳、墨迹自动保存等,对应 设置 → 自动化行为。"; - break; - case 9: - StepTitleText.Text = "随机点名"; - StepSubtitleText.Text = "点名窗口选项,对应 设置 → 随机点名。"; - break; - case 10: - StepTitleText.Text = "高级选项"; - StepSubtitleText.Text = "日志、特殊屏幕等,对应 设置 → 高级选项。"; - break; - case 11: - StepTitleText.Text = "截图和屏幕捕捉"; - StepSubtitleText.Text = "清屏截图、按日期保存等,对应 设置 → 截图和屏幕捕捉。"; - break; - } + ApplyStepMeta(_currentStep); - BtnConfirm.Content = _currentStep == MaxStepIndex ? "保存并开始使用" : "下一步"; + BtnConfirmText.Text = _currentStep == MaxStepIndex ? "保存并开始使用" : "下一步"; + BtnConfirmIcon.Icon = _currentStep == MaxStepIndex + ? SegoeFluentIcons.Accept + : SegoeFluentIcons.ChevronRight; + + UpdateConfirmEnabled(); + + if (StepScrollViewer != null) StepScrollViewer.ScrollToTop(); + + AnimateProgress(instant); + + if (!instant && animateDirection != 0) + { + AnimateStepSlide(animateDirection); + } + else + { + StepHostTransform.X = 0; + StepHost.Opacity = 1; + } } - catch + catch (Exception ex) { - // 忽略 UI 更新异常,避免影响主流程 + System.Diagnostics.Debug.WriteLine(ex); } } + + private void ApplyStepMeta(int step) + { + string title; string subtitle; FontIconData icon; + switch (step) + { + case 0: + title = "启动时行为"; + subtitle = "遥测、自动更新与启动行为,对应 设置 → 启动、隐私。"; + icon = SegoeFluentIcons.Shield; + break; + case 1: + title = "画板与墨迹"; + subtitle = "画笔光标、压感、墨迹显示,对应 设置 → 画板。"; + icon = SegoeFluentIcons.Edit; + break; + case 2: + title = "手势操作"; + subtitle = "双指缩放/平移、手掌擦等,对应 设置 → 画板。"; + icon = SegoeFluentIcons.TouchPointer; + break; + case 3: + title = "墨迹纠正"; + subtitle = "手绘图形识别为标准形状,对应 设置 → 墨迹纠正。"; + icon = SegoeFluentIcons.Draw; + break; + case 4: + title = "个性化设置"; + subtitle = "主题、启动动画、托盘与快速面板,对应 设置 → 个性化。"; + icon = SegoeFluentIcons.Personalize; + break; + case 5: + title = "快捷键"; + subtitle = "鼠标模式下的全局快捷键,对应 设置 → 个性化。"; + icon = SegoeFluentIcons.PenWorkspace; + break; + case 6: + title = "崩溃处理"; + subtitle = "未处理异常时的行为,对应 设置 → 启动。"; + icon = SegoeFluentIcons.Warning; + break; + case 7: + title = "PowerPoint 联动"; + subtitle = "放映联动与墨迹保存,对应 设置 → PowerPoint。"; + icon = SegoeFluentIcons.Slideshow; + break; + case 8: + title = "自动化行为"; + subtitle = "自动收纳、墨迹自动保存等,对应 设置 → 自动化。"; + icon = SegoeFluentIcons.Sync; + break; + case 9: + title = "随机点名"; + subtitle = "点名窗口选项,对应 设置 → 随机点名。"; + icon = SegoeFluentIcons.People; + break; + case 10: + title = "高级选项"; + subtitle = "日志等,对应 设置 → 高级。"; + icon = SegoeFluentIcons.Settings; + break; + case 11: + title = "截图和屏幕捕捉"; + subtitle = "清屏截图、按日期保存等,对应 设置 → 自动化。"; + icon = SegoeFluentIcons.Camera; + break; + default: + title = string.Empty; subtitle = string.Empty; icon = SegoeFluentIcons.Home; + break; + } + + StepTitleText.Text = title; + StepSubtitleText.Text = subtitle; + StepIcon.Icon = icon; + } + + private void AnimateProgress(bool instant) + { + try + { + double total = ActualWidth > 0 ? ActualWidth : Width; + double trackWidth = Math.Max(0, Math.Min(420, total - 56 * 2 - 320)); + double progress = (_currentStep + 1) / (double)StepCount; + double targetWidth = trackWidth * progress; + + if (instant) + { + ProgressFill.Width = targetWidth; + return; + } + + var anim = new DoubleAnimation + { + To = targetWidth, + Duration = TimeSpan.FromMilliseconds(320), + EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut } + }; + ProgressFill.BeginAnimation(WidthProperty, anim); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex); + } + } + + private void AnimateStepSlide(int direction) + { + // direction: 1 = 前进 (新内容从右滑入), -1 = 后退 (从左滑入) + double from = direction > 0 ? 36 : -36; + + StepHostTransform.X = from; + StepHost.Opacity = 0; + + var slide = new DoubleAnimation + { + From = from, + To = 0, + Duration = SlideDuration, + EasingFunction = SlideEase + }; + var fade = new DoubleAnimation + { + From = 0, + To = 1, + Duration = SlideDuration, + EasingFunction = SlideEase + }; + + StepHostTransform.BeginAnimation(TranslateTransform.XProperty, slide); + StepHost.BeginAnimation(OpacityProperty, fade); + } + + protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) + { + base.OnRenderSizeChanged(sizeInfo); + AnimateProgress(instant: true); + } } -} +} \ No newline at end of file