2026-05-01 01:49:56 +08:00
|
|
|
using iNKORE.UI.WPF.Modern.Common.IconKeys;
|
2026-02-14 15:59:10 +08:00
|
|
|
using System;
|
|
|
|
|
using System.Windows;
|
2026-05-01 01:49:56 +08:00
|
|
|
using System.Windows.Controls;
|
|
|
|
|
using System.Windows.Media;
|
|
|
|
|
using System.Windows.Media.Animation;
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
namespace Ink_Canvas.Windows
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
2026-05-01 01:49:56 +08:00
|
|
|
/// 首次启动体验(OOBE)窗口,使用与设置窗口一致的卡片化 UI 引导用户完成初始配置。
|
|
|
|
|
/// 切换步骤时使用横向滑动 + 淡入,模仿 ClassIsland 的设置向导动画风格。
|
2026-02-14 15:59:10 +08:00
|
|
|
/// </summary>
|
|
|
|
|
public partial class OobeWindow : Window
|
|
|
|
|
{
|
|
|
|
|
private readonly Settings _settings;
|
|
|
|
|
private int _currentStep = 0;
|
2026-03-03 16:04:20 +08:00
|
|
|
private const int MaxStepIndex = 11;
|
2026-05-01 01:49:56 +08:00
|
|
|
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 };
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
public OobeWindow(Settings settings)
|
|
|
|
|
{
|
|
|
|
|
if (settings == null) throw new ArgumentNullException(nameof(settings));
|
|
|
|
|
|
|
|
|
|
_settings = settings;
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
|
|
|
|
|
Opacity = 0;
|
|
|
|
|
|
2026-05-01 01:49:56 +08:00
|
|
|
_stepPanels = new FrameworkElement[]
|
|
|
|
|
{
|
|
|
|
|
StepTelemetryPanel,
|
|
|
|
|
StepCanvasPanel,
|
|
|
|
|
StepGesturesPanel,
|
|
|
|
|
StepInkRecognitionPanel,
|
|
|
|
|
StepAppearancePanel,
|
|
|
|
|
StepShortcutsPanel,
|
|
|
|
|
StepCrashActionPanel,
|
|
|
|
|
StepPptPanel,
|
|
|
|
|
StepAutomationPanel,
|
|
|
|
|
StepLuckyRandomPanel,
|
|
|
|
|
StepAdvancedPanel,
|
|
|
|
|
StepSnapshotPanel,
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-14 15:59:10 +08:00
|
|
|
InitializeFromSettings();
|
2026-05-01 01:49:56 +08:00
|
|
|
UpdateStepUI(animateDirection: 0, instant: true);
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 01:49:56 +08:00
|
|
|
#region Settings IO
|
|
|
|
|
|
2026-02-14 15:59:10 +08:00
|
|
|
private void InitializeFromSettings()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.Startup != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
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;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-05-01 01:49:56 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.Canvas != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
CardShowCursor.IsOn = _settings.Canvas.IsShowCursor;
|
|
|
|
|
CardDisablePressure.IsOn = _settings.Canvas.DisablePressure;
|
|
|
|
|
CardHideStrokeWhenSelecting.IsOn = _settings.Canvas.HideStrokeWhenSelecting;
|
|
|
|
|
CardEnablePalmEraser.IsOn = _settings.Canvas.EnablePalmEraser;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.Gesture != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
CardTwoFingerZoom.IsOn = _settings.Gesture.IsEnableTwoFingerZoom;
|
|
|
|
|
CardTwoFingerTranslate.IsOn = _settings.Gesture.IsEnableTwoFingerTranslate;
|
|
|
|
|
CardAutoSwitchTwoFingerGesture.IsOn = _settings.Gesture.AutoSwitchTwoFingerGesture;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.InkToShape != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
CardInkToShapeEnabled.IsOn = _settings.InkToShape.IsInkToShapeEnabled;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.Appearance != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
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;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
if (_settings.PowerPointSettings != null)
|
|
|
|
|
{
|
|
|
|
|
CardPptSupport.IsOn = _settings.PowerPointSettings.PowerPointSupport;
|
|
|
|
|
CardPptAutoSaveStrokes.IsOn = _settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint;
|
|
|
|
|
CardPptAutoSaveScreenshots.IsOn = _settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint;
|
|
|
|
|
CardPptTimeCapsule.IsOn = _settings.PowerPointSettings.EnablePPTTimeCapsule;
|
|
|
|
|
}
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.Automation != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
CardAutoFoldInPPTSlideShow.IsOn = _settings.Automation.IsAutoFoldInPPTSlideShow;
|
|
|
|
|
CardEnableAutoSaveStrokes.IsOn = _settings.Automation.IsEnableAutoSaveStrokes;
|
2026-02-14 15:59:10 +08:00
|
|
|
if (_settings.Automation.FloatingWindowInterceptor != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
CardFloatingWindowInterceptor.IsOn = _settings.Automation.FloatingWindowInterceptor.IsEnabled;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
2026-05-01 01:49:56 +08:00
|
|
|
CardAutoSaveStrokesAtClear.IsOn = _settings.Automation.IsAutoSaveStrokesAtClear;
|
|
|
|
|
CardSaveScreenshotsInDateFolders.IsOn = _settings.Automation.IsSaveScreenshotsInDateFolders;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.RandSettings != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
CardShowRandomAndSingleDraw.IsOn = _settings.RandSettings.ShowRandomAndSingleDraw;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.Advanced != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
CardIsLogEnabled.IsOn = _settings.Advanced.IsLogEnabled;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ApplySelection()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.Startup != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
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;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-05-01 01:49:56 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.Canvas != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
_settings.Canvas.IsShowCursor = CardShowCursor.IsOn;
|
|
|
|
|
_settings.Canvas.DisablePressure = CardDisablePressure.IsOn;
|
|
|
|
|
_settings.Canvas.HideStrokeWhenSelecting = CardHideStrokeWhenSelecting.IsOn;
|
|
|
|
|
_settings.Canvas.EnablePalmEraser = CardEnablePalmEraser.IsOn;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.Gesture != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
_settings.Gesture.IsEnableTwoFingerZoom = CardTwoFingerZoom.IsOn;
|
|
|
|
|
_settings.Gesture.IsEnableTwoFingerTranslate = CardTwoFingerTranslate.IsOn;
|
|
|
|
|
_settings.Gesture.AutoSwitchTwoFingerGesture = CardAutoSwitchTwoFingerGesture.IsOn;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.InkToShape != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
_settings.InkToShape.IsInkToShapeEnabled = CardInkToShapeEnabled.IsOn;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.Appearance != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
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;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
if (_settings.PowerPointSettings != null)
|
|
|
|
|
{
|
|
|
|
|
_settings.PowerPointSettings.PowerPointSupport = CardPptSupport.IsOn;
|
|
|
|
|
_settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint = CardPptAutoSaveStrokes.IsOn;
|
|
|
|
|
_settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint = CardPptAutoSaveScreenshots.IsOn;
|
|
|
|
|
_settings.PowerPointSettings.EnablePPTTimeCapsule = CardPptTimeCapsule.IsOn;
|
|
|
|
|
}
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.Automation != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
_settings.Automation.IsAutoFoldInPPTSlideShow = CardAutoFoldInPPTSlideShow.IsOn;
|
|
|
|
|
_settings.Automation.IsEnableAutoSaveStrokes = CardEnableAutoSaveStrokes.IsOn;
|
|
|
|
|
_settings.Automation.IsAutoSaveStrokesAtClear = CardAutoSaveStrokesAtClear.IsOn;
|
|
|
|
|
_settings.Automation.IsSaveScreenshotsInDateFolders = CardSaveScreenshotsInDateFolders.IsOn;
|
2026-02-14 15:59:10 +08:00
|
|
|
if (_settings.Automation.FloatingWindowInterceptor != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
_settings.Automation.FloatingWindowInterceptor.IsEnabled = CardFloatingWindowInterceptor.IsOn;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.RandSettings != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
_settings.RandSettings.ShowRandomAndSingleDraw = CardShowRandomAndSingleDraw.IsOn;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_settings.Advanced != null)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
_settings.Advanced.IsLogEnabled = CardIsLogEnabled.IsOn;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-21 16:51:34 +08:00
|
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 01:49:56 +08:00
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Navigation
|
|
|
|
|
|
2026-02-14 15:59:10 +08:00
|
|
|
private void BtnConfirm_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
if (_currentStep == 0 && CheckBoxPrivacyAccepted.IsChecked != true)
|
|
|
|
|
{
|
|
|
|
|
MessageBox.Show(this,
|
|
|
|
|
"请先勾选\"我已阅读并同意《隐私协议》\"后再继续。",
|
|
|
|
|
"需要同意隐私协议",
|
|
|
|
|
MessageBoxButton.OK,
|
|
|
|
|
MessageBoxImage.Information);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 15:59:10 +08:00
|
|
|
if (_currentStep < MaxStepIndex)
|
|
|
|
|
{
|
|
|
|
|
_currentStep++;
|
2026-05-01 01:49:56 +08:00
|
|
|
UpdateStepUI(animateDirection: 1);
|
2026-02-14 15:59:10 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ApplySelection();
|
|
|
|
|
DialogResult = true;
|
|
|
|
|
Close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void BtnPreviousStep_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (_currentStep <= 0) return;
|
|
|
|
|
_currentStep--;
|
2026-05-01 01:49:56 +08:00
|
|
|
UpdateStepUI(animateDirection: -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CheckBoxPrivacyAccepted_Changed(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
UpdateConfirmEnabled();
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 01:49:56 +08:00
|
|
|
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
|
|
|
|
|
|
2026-02-14 15:59:10 +08:00
|
|
|
private void OobeWindow_OnLoaded(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
var animation = new DoubleAnimation
|
2026-02-14 15:59:10 +08:00
|
|
|
{
|
|
|
|
|
From = 0,
|
|
|
|
|
To = 1,
|
2026-05-01 01:49:56 +08:00
|
|
|
Duration = TimeSpan.FromMilliseconds(260),
|
|
|
|
|
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
|
2026-02-14 15:59:10 +08:00
|
|
|
};
|
|
|
|
|
BeginAnimation(OpacityProperty, animation);
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
Opacity = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-01 01:49:56 +08:00
|
|
|
private void UpdateStepUI(int animateDirection, bool instant = false)
|
2026-02-14 15:59:10 +08:00
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
for (int i = 0; i < _stepPanels.Length; i++)
|
2026-02-14 15:59:10 +08:00
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
_stepPanels[i].Visibility = i == _currentStep ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
|
}
|
2026-02-14 15:59:10 +08:00
|
|
|
|
2026-05-01 01:49:56 +08:00
|
|
|
StepIndicatorText.Text = $"步骤 {_currentStep + 1} / {StepCount}";
|
|
|
|
|
FooterStepText.Text = $"{_currentStep + 1} / {StepCount}";
|
|
|
|
|
BtnPreviousStep.Visibility = _currentStep > 0 ? Visibility.Visible : Visibility.Collapsed;
|
2026-02-14 15:59:10 +08:00
|
|
|
|
2026-05-01 01:49:56 +08:00
|
|
|
ApplyStepMeta(_currentStep);
|
|
|
|
|
|
|
|
|
|
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);
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
2026-05-01 01:49:56 +08:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
StepHostTransform.X = 0;
|
|
|
|
|
StepHost.Opacity = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
System.Diagnostics.Debug.WriteLine(ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-14 15:59:10 +08:00
|
|
|
|
2026-05-01 01:49:56 +08:00
|
|
|
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;
|
|
|
|
|
}
|
2026-02-14 15:59:10 +08:00
|
|
|
|
2026-05-01 01:49:56 +08:00
|
|
|
StepTitleText.Text = title;
|
|
|
|
|
StepSubtitleText.Text = subtitle;
|
|
|
|
|
StepIcon.Icon = icon;
|
|
|
|
|
}
|
2026-02-14 15:59:10 +08:00
|
|
|
|
2026-05-01 01:49:56 +08:00
|
|
|
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)
|
2026-02-14 15:59:10 +08:00
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
ProgressFill.Width = targetWidth;
|
|
|
|
|
return;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 01:49:56 +08:00
|
|
|
var anim = new DoubleAnimation
|
|
|
|
|
{
|
|
|
|
|
To = targetWidth,
|
|
|
|
|
Duration = TimeSpan.FromMilliseconds(320),
|
|
|
|
|
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
|
|
|
|
|
};
|
|
|
|
|
ProgressFill.BeginAnimation(WidthProperty, anim);
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
2026-05-01 01:49:56 +08:00
|
|
|
catch (Exception ex)
|
2026-02-14 15:59:10 +08:00
|
|
|
{
|
2026-05-01 01:49:56 +08:00
|
|
|
System.Diagnostics.Debug.WriteLine(ex);
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-05-01 01:49:56 +08:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
2026-05-01 01:49:56 +08:00
|
|
|
}
|