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-05-01 09:10:28 +08:00
|
|
|
using FontIcon = iNKORE.UI.WPF.Modern.Controls.FontIcon;
|
|
|
|
|
using NavigationView = iNKORE.UI.WPF.Modern.Controls.NavigationView;
|
|
|
|
|
using NavigationViewItem = iNKORE.UI.WPF.Modern.Controls.NavigationViewItem;
|
|
|
|
|
using NavigationViewSelectionChangedEventArgs = iNKORE.UI.WPF.Modern.Controls.NavigationViewSelectionChangedEventArgs;
|
2026-02-14 15:59:10 +08:00
|
|
|
|
|
|
|
|
namespace Ink_Canvas.Windows
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
2026-05-01 09:10:28 +08:00
|
|
|
/// 首次启动体验(OOBE)窗口。使用 iNKORE.UI.WPF.Modern 的 NavigationView 作为左侧导航,
|
|
|
|
|
/// 引导用户依次完成欢迎页、8 个配置步骤与完成摘要页。
|
2026-02-14 15:59:10 +08:00
|
|
|
/// </summary>
|
|
|
|
|
public partial class OobeWindow : Window
|
|
|
|
|
{
|
|
|
|
|
private readonly Settings _settings;
|
2026-05-01 09:10:28 +08:00
|
|
|
|
|
|
|
|
// 视图状态: -1 = 欢迎; 0..7 = 步骤; 8 = 完成
|
|
|
|
|
private const int WelcomeIndex = -1;
|
|
|
|
|
private const int FinishIndex = 8;
|
2026-05-01 08:39:36 +08:00
|
|
|
private const int StepCount = 8;
|
2026-05-01 09:10:28 +08:00
|
|
|
private const int MaxStepIndex = StepCount - 1;
|
|
|
|
|
|
|
|
|
|
private int _currentStep = WelcomeIndex;
|
|
|
|
|
private bool _suppressNavSelection;
|
2026-05-01 01:49:56 +08:00
|
|
|
|
|
|
|
|
private FrameworkElement[] _stepPanels;
|
2026-05-01 09:10:28 +08:00
|
|
|
private NavigationViewItem[] _navItems;
|
|
|
|
|
|
2026-05-01 01:49:56 +08:00
|
|
|
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,
|
|
|
|
|
StepAppearancePanel,
|
|
|
|
|
StepPptPanel,
|
|
|
|
|
StepAutomationPanel,
|
|
|
|
|
StepLuckyRandomPanel,
|
|
|
|
|
StepAdvancedPanel,
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-01 09:10:28 +08:00
|
|
|
_navItems = new[]
|
|
|
|
|
{
|
|
|
|
|
NavItemTelemetry,
|
|
|
|
|
NavItemCanvas,
|
|
|
|
|
NavItemGestures,
|
|
|
|
|
NavItemAppearance,
|
|
|
|
|
NavItemPpt,
|
|
|
|
|
NavItemAutomation,
|
|
|
|
|
NavItemLuckyRandom,
|
|
|
|
|
NavItemAdvanced,
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-14 15:59:10 +08:00
|
|
|
InitializeFromSettings();
|
2026-05-01 09:10:28 +08:00
|
|
|
UpdateView(animateDirection: 0, instant: true);
|
|
|
|
|
SyncNavSelection();
|
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-05-01 09:10:28 +08:00
|
|
|
private void BtnStartWelcome_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
NavigateTo(0, direction: 1);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-02 17:26:40 +08:00
|
|
|
private void BtnUsePreset_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
var presetWindow = new OobePresetWindow { Owner = this };
|
|
|
|
|
bool? result = presetWindow.ShowDialog();
|
|
|
|
|
if (result != true) return;
|
|
|
|
|
|
|
|
|
|
switch (presetWindow.SelectedPreset)
|
|
|
|
|
{
|
|
|
|
|
case OobePresetWindow.PresetKind.Standard:
|
|
|
|
|
OobePresetWindow.ApplyStandard(_settings);
|
|
|
|
|
break;
|
|
|
|
|
case OobePresetWindow.PresetKind.Lite:
|
|
|
|
|
OobePresetWindow.ApplyLite(_settings);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DialogResult = true;
|
|
|
|
|
Close();
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 15:59:10 +08:00
|
|
|
private void BtnConfirm_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
2026-05-01 09:10:28 +08:00
|
|
|
if (_currentStep == FinishIndex)
|
2026-02-14 15:59:10 +08:00
|
|
|
{
|
2026-05-01 09:10:28 +08:00
|
|
|
ApplySelection();
|
|
|
|
|
DialogResult = true;
|
|
|
|
|
Close();
|
2026-02-14 15:59:10 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-01 09:10:28 +08:00
|
|
|
NavigateTo(_currentStep + 1, direction: 1);
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void BtnPreviousStep_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
2026-05-01 09:10:28 +08:00
|
|
|
if (_currentStep <= WelcomeIndex) return;
|
|
|
|
|
NavigateTo(_currentStep - 1, direction: -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void NavView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
|
|
|
|
|
{
|
|
|
|
|
if (_suppressNavSelection) return;
|
|
|
|
|
|
|
|
|
|
int target = ResolveTargetFromNavItem(args.SelectedItem as NavigationViewItem);
|
|
|
|
|
if (target == _currentStep) return;
|
|
|
|
|
|
|
|
|
|
int direction = target > _currentStep ? 1 : -1;
|
|
|
|
|
NavigateTo(target, direction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int ResolveTargetFromNavItem(NavigationViewItem item)
|
|
|
|
|
{
|
|
|
|
|
if (item == null) return _currentStep;
|
|
|
|
|
if (item == NavItemWelcome) return WelcomeIndex;
|
|
|
|
|
if (item == NavItemFinish) return FinishIndex;
|
|
|
|
|
for (int i = 0; i < _navItems.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
if (_navItems[i] == item) return i;
|
|
|
|
|
}
|
|
|
|
|
return _currentStep;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void NavigateTo(int target, int direction)
|
|
|
|
|
{
|
|
|
|
|
if (target < WelcomeIndex) target = WelcomeIndex;
|
|
|
|
|
if (target > FinishIndex) target = FinishIndex;
|
|
|
|
|
_currentStep = target;
|
|
|
|
|
UpdateView(direction);
|
|
|
|
|
SyncNavSelection();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SyncNavSelection()
|
|
|
|
|
{
|
|
|
|
|
_suppressNavSelection = true;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_currentStep == WelcomeIndex)
|
|
|
|
|
NavView.SelectedItem = NavItemWelcome;
|
|
|
|
|
else if (_currentStep == FinishIndex)
|
|
|
|
|
NavView.SelectedItem = NavItemFinish;
|
|
|
|
|
else
|
|
|
|
|
NavView.SelectedItem = _navItems[_currentStep];
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
_suppressNavSelection = false;
|
|
|
|
|
}
|
2026-05-01 01:49:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2026-05-01 21:28:54 +08:00
|
|
|
e.Handled = true;
|
2026-05-01 01:49:56 +08:00
|
|
|
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);
|
|
|
|
|
}
|
2026-05-01 09:10:28 +08:00
|
|
|
finally
|
|
|
|
|
{
|
2026-05-01 21:28:54 +08:00
|
|
|
Dispatcher.BeginInvoke(new Action(() => _privacyDialogShown = false),
|
|
|
|
|
System.Windows.Threading.DispatcherPriority.Background);
|
2026-05-01 09:10:28 +08:00
|
|
|
}
|
2026-05-01 01:49:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateConfirmEnabled()
|
|
|
|
|
{
|
2026-05-01 21:28:54 +08:00
|
|
|
if (BtnConfirm == null) return;
|
|
|
|
|
BtnConfirm.IsEnabled = true;
|
2026-05-01 01:49:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#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 09:10:28 +08:00
|
|
|
private void UpdateView(int animateDirection, bool instant = false)
|
2026-02-14 15:59:10 +08:00
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2026-05-01 09:10:28 +08:00
|
|
|
bool isWelcome = _currentStep == WelcomeIndex;
|
|
|
|
|
bool isFinish = _currentStep == FinishIndex;
|
|
|
|
|
bool isStep = !isWelcome && !isFinish;
|
|
|
|
|
|
|
|
|
|
WelcomePanel.Visibility = isWelcome ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
|
StepScrollViewer.Visibility = isStep ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
|
FinishPanel.Visibility = isFinish ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
|
|
|
|
|
|
if (isStep)
|
2026-02-14 15:59:10 +08:00
|
|
|
{
|
2026-05-01 09:10:28 +08:00
|
|
|
for (int i = 0; i < _stepPanels.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
_stepPanels[i].Visibility = i == _currentStep ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StepIndicatorText.Text = $"步骤 {_currentStep + 1} / {StepCount}";
|
|
|
|
|
ApplyStepMeta(_currentStep);
|
|
|
|
|
|
|
|
|
|
if (StepScrollViewer != null) StepScrollViewer.ScrollToTop();
|
2026-05-01 01:49:56 +08:00
|
|
|
}
|
2026-02-14 15:59:10 +08:00
|
|
|
|
2026-05-01 09:10:28 +08:00
|
|
|
if (isFinish)
|
|
|
|
|
{
|
|
|
|
|
BuildFinishSummary();
|
|
|
|
|
}
|
2026-02-14 15:59:10 +08:00
|
|
|
|
2026-05-01 09:10:28 +08:00
|
|
|
// 底部进度: 欢迎=0, 各步骤按比例, 完成=100
|
|
|
|
|
double progress;
|
|
|
|
|
if (isWelcome) progress = 0;
|
|
|
|
|
else if (isFinish) progress = 100;
|
|
|
|
|
else progress = (_currentStep + 1) / (double)(StepCount + 1) * 100.0;
|
2026-05-01 01:49:56 +08:00
|
|
|
|
2026-05-01 09:10:28 +08:00
|
|
|
AnimateProgress(progress, instant);
|
2026-05-01 01:49:56 +08:00
|
|
|
|
2026-05-01 09:10:28 +08:00
|
|
|
// Footer 步骤计数
|
|
|
|
|
if (isWelcome) FooterStepText.Text = string.Empty;
|
|
|
|
|
else if (isFinish) FooterStepText.Text = $"{StepCount} / {StepCount} · 完成";
|
|
|
|
|
else FooterStepText.Text = $"{_currentStep + 1} / {StepCount}";
|
2026-05-01 01:49:56 +08:00
|
|
|
|
2026-05-01 09:10:28 +08:00
|
|
|
// 上一步按钮: 欢迎页隐藏
|
|
|
|
|
BtnPreviousStep.Visibility = isWelcome ? Visibility.Collapsed : Visibility.Visible;
|
2026-05-01 01:49:56 +08:00
|
|
|
|
2026-05-01 09:10:28 +08:00
|
|
|
// 主按钮: 欢迎页隐藏(由欢迎页自身的"开始"按钮负责)
|
|
|
|
|
BtnConfirm.Visibility = isWelcome ? Visibility.Collapsed : Visibility.Visible;
|
|
|
|
|
if (isFinish)
|
|
|
|
|
{
|
|
|
|
|
BtnConfirmText.Text = "保存并开始使用";
|
|
|
|
|
BtnConfirmIcon.Icon = SegoeFluentIcons.Accept;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
BtnConfirmText.Text = "下一步";
|
|
|
|
|
BtnConfirmIcon.Icon = SegoeFluentIcons.ChevronRight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateConfirmEnabled();
|
2026-05-01 01:49:56 +08:00
|
|
|
|
|
|
|
|
if (!instant && animateDirection != 0)
|
|
|
|
|
{
|
2026-05-01 09:10:28 +08:00
|
|
|
AnimateContentSlide(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)
|
|
|
|
|
{
|
2026-05-01 09:10:28 +08:00
|
|
|
string title; string subtitle;
|
2026-05-01 01:49:56 +08:00
|
|
|
switch (step)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
2026-05-01 08:39:36 +08:00
|
|
|
title = "启动与隐私";
|
|
|
|
|
subtitle = "遥测、隐私协议、自动更新与崩溃处理。";
|
2026-05-01 01:49:56 +08:00
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
title = "画板与墨迹";
|
2026-05-01 08:39:36 +08:00
|
|
|
subtitle = "光标、压感、墨迹显示与墨迹纠正。";
|
2026-05-01 01:49:56 +08:00
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
title = "手势操作";
|
2026-05-01 08:39:36 +08:00
|
|
|
subtitle = "双指缩放/平移、手掌擦等。";
|
2026-05-01 01:49:56 +08:00
|
|
|
break;
|
|
|
|
|
case 3:
|
2026-05-01 08:39:36 +08:00
|
|
|
title = "个性化";
|
|
|
|
|
subtitle = "主题、启动动画、托盘、快速面板与快捷键。";
|
2026-05-01 01:49:56 +08:00
|
|
|
break;
|
2026-05-01 08:39:36 +08:00
|
|
|
case 4:
|
2026-05-01 01:49:56 +08:00
|
|
|
title = "PowerPoint 联动";
|
2026-05-01 08:39:36 +08:00
|
|
|
subtitle = "放映联动、墨迹与截屏自动保存、时间胶囊。";
|
2026-05-01 01:49:56 +08:00
|
|
|
break;
|
2026-05-01 08:39:36 +08:00
|
|
|
case 5:
|
|
|
|
|
title = "自动化与截图";
|
|
|
|
|
subtitle = "自动收纳、墨迹自动保存、悬浮窗拦截与截图保存。";
|
2026-05-01 01:49:56 +08:00
|
|
|
break;
|
2026-05-01 08:39:36 +08:00
|
|
|
case 6:
|
2026-05-01 01:49:56 +08:00
|
|
|
title = "随机点名";
|
2026-05-01 08:39:36 +08:00
|
|
|
subtitle = "点名窗口选项。";
|
2026-05-01 01:49:56 +08:00
|
|
|
break;
|
2026-05-01 08:39:36 +08:00
|
|
|
case 7:
|
2026-05-01 01:49:56 +08:00
|
|
|
title = "高级选项";
|
2026-05-01 08:39:36 +08:00
|
|
|
subtitle = "日志等高级配置。";
|
2026-05-01 01:49:56 +08:00
|
|
|
break;
|
|
|
|
|
default:
|
2026-05-01 09:10:28 +08:00
|
|
|
title = string.Empty; subtitle = string.Empty;
|
2026-05-01 01:49:56 +08:00
|
|
|
break;
|
|
|
|
|
}
|
2026-02-14 15:59:10 +08:00
|
|
|
|
2026-05-01 01:49:56 +08:00
|
|
|
StepTitleText.Text = title;
|
|
|
|
|
StepSubtitleText.Text = subtitle;
|
|
|
|
|
}
|
2026-02-14 15:59:10 +08:00
|
|
|
|
2026-05-01 09:10:28 +08:00
|
|
|
private void BuildFinishSummary()
|
|
|
|
|
{
|
|
|
|
|
FinishSummaryHost.Children.Clear();
|
|
|
|
|
|
|
|
|
|
string telemetryText;
|
|
|
|
|
switch (ComboBoxTelemetryUploadLevel.SelectedIndex)
|
|
|
|
|
{
|
|
|
|
|
case 0: telemetryText = "不上传任何匿名使用数据"; break;
|
|
|
|
|
case 1: telemetryText = "基础(崩溃信息、版本与系统信息)"; break;
|
|
|
|
|
default: telemetryText = "可选(功能使用频率等)"; break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string themeText;
|
|
|
|
|
switch (ComboBoxTheme.SelectedIndex)
|
|
|
|
|
{
|
|
|
|
|
case 0: themeText = "浅色"; break;
|
|
|
|
|
case 1: themeText = "深色"; break;
|
|
|
|
|
default: themeText = "跟随系统"; break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AddSummaryRow(SegoeFluentIcons.Shield, "遥测级别", telemetryText);
|
|
|
|
|
AddSummaryRow(SegoeFluentIcons.Sync, "自动检查更新", BoolText(CardAutoUpdate.IsOn));
|
|
|
|
|
AddSummaryRow(SegoeFluentIcons.Personalize, "应用主题", themeText);
|
|
|
|
|
AddSummaryRow(SegoeFluentIcons.Slideshow, "PowerPoint / WPS 联动", BoolText(CardPptSupport.IsOn));
|
|
|
|
|
AddSummaryRow(SegoeFluentIcons.TouchPointer, "双指缩放 / 平移",
|
|
|
|
|
$"{BoolText(CardTwoFingerZoom.IsOn)} / {BoolText(CardTwoFingerTranslate.IsOn)}");
|
|
|
|
|
AddSummaryRow(SegoeFluentIcons.Pin, "系统托盘图标", BoolText(CardEnableTrayIcon.IsOn));
|
|
|
|
|
AddSummaryRow(SegoeFluentIcons.Document, "启用日志", BoolText(CardIsLogEnabled.IsOn));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string BoolText(bool value) => value ? "已启用" : "已关闭";
|
|
|
|
|
|
|
|
|
|
private void AddSummaryRow(FontIconData icon, string label, string value)
|
|
|
|
|
{
|
|
|
|
|
var grid = new Grid { Margin = new Thickness(0, 2, 0, 2) };
|
|
|
|
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(28) });
|
|
|
|
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(180) });
|
|
|
|
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
|
|
|
|
|
|
|
|
|
var fontIcon = new FontIcon { Icon = icon, FontSize = 16, Opacity = 0.85 };
|
|
|
|
|
Grid.SetColumn(fontIcon, 0);
|
|
|
|
|
grid.Children.Add(fontIcon);
|
|
|
|
|
|
|
|
|
|
var labelBlock = new TextBlock
|
|
|
|
|
{
|
|
|
|
|
Text = label,
|
|
|
|
|
Opacity = 0.85,
|
|
|
|
|
VerticalAlignment = VerticalAlignment.Center
|
|
|
|
|
};
|
|
|
|
|
Grid.SetColumn(labelBlock, 1);
|
|
|
|
|
grid.Children.Add(labelBlock);
|
|
|
|
|
|
|
|
|
|
var valueBlock = new TextBlock
|
|
|
|
|
{
|
|
|
|
|
Text = value,
|
|
|
|
|
FontWeight = FontWeights.SemiBold,
|
|
|
|
|
TextWrapping = TextWrapping.Wrap,
|
|
|
|
|
VerticalAlignment = VerticalAlignment.Center
|
|
|
|
|
};
|
|
|
|
|
Grid.SetColumn(valueBlock, 2);
|
|
|
|
|
grid.Children.Add(valueBlock);
|
|
|
|
|
|
|
|
|
|
FinishSummaryHost.Children.Add(grid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void AnimateProgress(double targetPercent, bool instant)
|
2026-05-01 01:49:56 +08:00
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2026-05-01 09:10:28 +08:00
|
|
|
if (StepProgressBar == null) return;
|
2026-05-01 01:49:56 +08:00
|
|
|
|
|
|
|
|
if (instant)
|
2026-02-14 15:59:10 +08:00
|
|
|
{
|
2026-05-01 09:10:28 +08:00
|
|
|
StepProgressBar.BeginAnimation(System.Windows.Controls.Primitives.RangeBase.ValueProperty, null);
|
|
|
|
|
StepProgressBar.Value = targetPercent;
|
2026-05-01 01:49:56 +08:00
|
|
|
return;
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 01:49:56 +08:00
|
|
|
var anim = new DoubleAnimation
|
|
|
|
|
{
|
2026-05-01 09:10:28 +08:00
|
|
|
To = targetPercent,
|
2026-05-01 01:49:56 +08:00
|
|
|
Duration = TimeSpan.FromMilliseconds(320),
|
|
|
|
|
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
|
|
|
|
|
};
|
2026-05-01 09:10:28 +08:00
|
|
|
StepProgressBar.BeginAnimation(System.Windows.Controls.Primitives.RangeBase.ValueProperty, 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
|
|
|
|
2026-05-01 09:10:28 +08:00
|
|
|
private void AnimateContentSlide(int direction)
|
2026-05-01 01:49:56 +08:00
|
|
|
{
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
2026-02-14 15:59:10 +08:00
|
|
|
}
|
2026-05-01 01:49:56 +08:00
|
|
|
}
|