Files
community/Ink Canvas/Windows/OobeWindow.xaml.cs
T

543 lines
21 KiB
C#
Raw Normal View History

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
}