improve:OOBE

This commit is contained in:
2026-05-01 09:10:28 +08:00
parent 17c0ecc0f5
commit 17945a9298
2 changed files with 729 additions and 420 deletions
+494 -364
View File
@@ -9,10 +9,10 @@
xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" mc:Ignorable="d"
Title="欢迎使用 InkCanvasForClass" Title="欢迎使用 InkCanvasForClass"
Height="660" Height="700"
Width="980" Width="1080"
MinHeight="460" MinHeight="520"
MinWidth="760" MinWidth="880"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
ResizeMode="CanResize" ResizeMode="CanResize"
Topmost="True" Topmost="True"
@@ -21,7 +21,7 @@
ui:TitleBar.ExtendViewIntoTitleBar="True" ui:TitleBar.ExtendViewIntoTitleBar="True"
ui:WindowHelper.SystemBackdropType="Mica" ui:WindowHelper.SystemBackdropType="Mica"
ui:WindowHelper.UseModernWindowStyle="True" ui:WindowHelper.UseModernWindowStyle="True"
ui:TitleBar.Height="48"> ui:TitleBar.Height="40">
<Window.Resources> <Window.Resources>
<ResourceDictionary> <ResourceDictionary>
@@ -43,10 +43,8 @@
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- 自定义标题栏 --> <!-- 自定义标题栏 -->
@@ -67,7 +65,7 @@
Spacing="12" Spacing="12"
VerticalAlignment="Center" VerticalAlignment="Center"
Margin="16,0,0,0"> Margin="16,0,0,0">
<Image Source="\Resources\icc.ico" Width="20" <Image Source="\Resources\icc.ico" Width="18"
RenderOptions.BitmapScalingMode="HighQuality" /> RenderOptions.BitmapScalingMode="HighQuality" />
<TextBlock VerticalAlignment="Center" <TextBlock VerticalAlignment="Center"
Text="欢迎使用 InkCanvasForClass" Text="欢迎使用 InkCanvasForClass"
@@ -79,419 +77,551 @@
</Grid> </Grid>
</Border> </Border>
<!-- 顶部 Banner --> <!-- 主体: 左侧导航 + 内容 -->
<Border Grid.Row="1" <ui:NavigationView x:Name="NavView"
x:Name="HeaderBanner" Grid.Row="1"
Padding="56,16,56,18" PaneDisplayMode="Left"
BorderThickness="0,0,0,1" IsPaneOpen="True"
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"> IsBackButtonVisible="Collapsed"
<Border.Background> IsBackEnabled="False"
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1"> IsSettingsVisible="False"
<GradientStop Color="#1F3B82F6" Offset="0" /> OpenPaneLength="240"
<GradientStop Color="#0A8B5CF6" Offset="1" /> SelectionChanged="NavView_SelectionChanged">
</LinearGradientBrush>
</Border.Background> <ui:NavigationView.MenuItems>
<ui:NavigationViewItem x:Name="NavItemWelcome" Content="欢迎" Tag="welcome">
<ui:NavigationViewItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Home}" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItemSeparator />
<ui:NavigationViewItem x:Name="NavItemTelemetry" Content="启动与隐私" Tag="step0">
<ui:NavigationViewItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Shield}" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem x:Name="NavItemCanvas" Content="画板与墨迹" Tag="step1">
<ui:NavigationViewItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Edit}" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem x:Name="NavItemGestures" Content="手势操作" Tag="step2">
<ui:NavigationViewItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.TouchPointer}" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem x:Name="NavItemAppearance" Content="个性化" Tag="step3">
<ui:NavigationViewItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Personalize}" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem x:Name="NavItemPpt" Content="PowerPoint 联动" Tag="step4">
<ui:NavigationViewItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Slideshow}" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem x:Name="NavItemAutomation" Content="自动化与截图" Tag="step5">
<ui:NavigationViewItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Sync}" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem x:Name="NavItemLuckyRandom" Content="随机点名" Tag="step6">
<ui:NavigationViewItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.People}" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem x:Name="NavItemAdvanced" Content="高级选项" Tag="step7">
<ui:NavigationViewItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Settings}" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItemSeparator />
<ui:NavigationViewItem x:Name="NavItemFinish" Content="完成" Tag="finish">
<ui:NavigationViewItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Accept}" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
</ui:NavigationView.MenuItems>
<Grid> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Border Grid.Column="0" Grid.RowSpan="3" <!-- 内容区 -->
Width="56" Height="56" <Grid Grid.Row="0" ClipToBounds="True">
Margin="0,0,18,0"
VerticalAlignment="Top"
CornerRadius="12"
Background="{DynamicResource SystemControlBackgroundChromeMediumBrush}">
<ui:FontIcon x:Name="StepIcon"
FontSize="28"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Icon="{x:Static ui:SegoeFluentIcons.Shield}" />
</Border>
<TextBlock Grid.Column="1" Grid.Row="0" <!-- 欢迎页 (居中布局, 不进 ScrollViewer 的滚动条) -->
x:Name="StepIndicatorText" <Grid x:Name="WelcomePanel" Visibility="Visible">
Style="{DynamicResource CaptionTextBlockStyle}" <ikw:SimpleStackPanel HorizontalAlignment="Center"
Opacity="0.75" VerticalAlignment="Center"
Text="步骤 1 / 8" /> MaxWidth="640"
<TextBlock Grid.Column="1" Grid.Row="1" Spacing="16">
x:Name="StepTitleText" <Border Width="96" Height="96"
Style="{DynamicResource TitleTextBlockStyle}" HorizontalAlignment="Center"
Margin="0,2,0,0" CornerRadius="20"
Text="启动与隐私" /> Background="{DynamicResource SystemControlBackgroundChromeMediumBrush}">
<TextBlock Grid.Column="1" Grid.Row="2" <Image Source="\Resources\icc.ico"
x:Name="StepSubtitleText" Width="64"
Style="{DynamicResource BodyTextBlockStyle}" RenderOptions.BitmapScalingMode="HighQuality" />
Margin="0,4,0,0" </Border>
Opacity="0.85"
TextWrapping="Wrap"
Text="遥测、隐私协议、自动更新与崩溃处理。" />
</Grid>
</Border>
<!-- 步骤内容 --> <TextBlock Text="欢迎使用 InkCanvasForClass"
<Grid Grid.Row="2" ClipToBounds="True"> Style="{DynamicResource TitleLargeTextBlockStyle}"
<ScrollViewer x:Name="StepScrollViewer" HorizontalAlignment="Center"
PanningMode="VerticalFirst" TextAlignment="Center"
VerticalScrollBarVisibility="Auto" Margin="0,8,0,0" />
HorizontalScrollBarVisibility="Disabled">
<Grid Margin="56,18,56,0">
<ikw:SimpleStackPanel x:Name="StepHost"
MaxWidth="1000"
HorizontalAlignment="Stretch"
Spacing="{StaticResource SettingsCardSpacing}">
<ikw:SimpleStackPanel.RenderTransform> <TextBlock HorizontalAlignment="Center"
<TranslateTransform x:Name="StepHostTransform" X="0" Y="0" /> TextAlignment="Center"
</ikw:SimpleStackPanel.RenderTransform> TextWrapping="Wrap"
Opacity="0.85"
Style="{DynamicResource BodyTextBlockStyle}"
Text="一款面向课堂场景的批注与白板工具。下面的向导将帮助你快速完成首次配置,所有选项都可以稍后在「设置」中修改。" />
<!-- 步骤 1: 启动与隐私 --> <TextBlock HorizontalAlignment="Center"
<ikw:SimpleStackPanel x:Name="StepTelemetryPanel" TextAlignment="Center"
Spacing="{StaticResource SettingsCardSpacing}"> Opacity="0.6"
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Style="{DynamicResource CaptionTextBlockStyle}"
Text="匿名使用数据与隐私" /> Text="共 8 个步骤 · 大约 1 分钟" />
<ui:SettingsExpander Header="匿名使用数据与隐私" <Button x:Name="BtnStartWelcome"
Description="完全匿名的使用统计,仅用于改进软件稳定性与性能。" HorizontalAlignment="Center"
IsExpanded="True"> MinWidth="180"
<ui:SettingsExpander.HeaderIcon> Height="36"
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Shield}" /> Margin="0,12,0,0"
</ui:SettingsExpander.HeaderIcon> Style="{DynamicResource AccentButtonStyle}"
<ui:SettingsExpander.Items> Click="BtnStartWelcome_Click">
<ui:SettingsCard ContentAlignment="Left"> <ikw:SimpleStackPanel Orientation="Horizontal" Spacing="8">
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="4" VerticalAlignment="Center"> <TextBlock Text="开始" />
<CheckBox x:Name="CheckBoxPrivacyAccepted" <ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.ChevronRight}" FontSize="12" />
VerticalAlignment="Center" </ikw:SimpleStackPanel>
Checked="CheckBoxPrivacyAccepted_Changed" </Button>
Unchecked="CheckBoxPrivacyAccepted_Changed" </ikw:SimpleStackPanel>
Content="我已阅读并同意" /> </Grid>
<ui:HyperlinkButton Content="《隐私协议》"
Padding="4,0" <!-- 步骤页 (8 个) -->
VerticalAlignment="Center" <ScrollViewer x:Name="StepScrollViewer"
Click="HyperlinkPrivacy_Click" /> Visibility="Collapsed"
</ikw:SimpleStackPanel> PanningMode="VerticalFirst"
</ui:SettingsCard> VerticalScrollBarVisibility="Auto"
<ui:SettingsCard Header="遥测级别" HorizontalScrollBarVisibility="Disabled">
Description="本软件不会收集课堂内容、学生个人信息或可识别的原始墨迹。详细请见 privacy.txt。"> <Grid Margin="40,16,40,0">
<ComboBox x:Name="ComboBoxTelemetryUploadLevel" MinWidth="220"> <ikw:SimpleStackPanel x:Name="StepHost"
<ComboBoxItem Content="不上传任何匿名使用数据" /> MaxWidth="900"
<ComboBoxItem Content="基础(崩溃信息、版本与系统信息)" /> HorizontalAlignment="Stretch"
<ComboBoxItem Content="可选(功能使用频率等)" /> Spacing="{StaticResource SettingsCardSpacing}">
<ikw:SimpleStackPanel.RenderTransform>
<TranslateTransform x:Name="StepHostTransform" X="0" Y="0" />
</ikw:SimpleStackPanel.RenderTransform>
<!-- 步骤标题 -->
<ikw:SimpleStackPanel Margin="0,0,0,8" Spacing="2">
<TextBlock x:Name="StepIndicatorText"
Style="{DynamicResource CaptionTextBlockStyle}"
Opacity="0.7"
Text="步骤 1 / 8" />
<TextBlock x:Name="StepTitleText"
Style="{DynamicResource TitleTextBlockStyle}"
Text="启动与隐私" />
<TextBlock x:Name="StepSubtitleText"
Style="{DynamicResource BodyTextBlockStyle}"
Margin="0,2,0,0"
Opacity="0.85"
TextWrapping="Wrap"
Text="遥测、隐私协议、自动更新与崩溃处理。" />
</ikw:SimpleStackPanel>
<!-- 步骤 1: 启动与隐私 -->
<ikw:SimpleStackPanel x:Name="StepTelemetryPanel"
Spacing="{StaticResource SettingsCardSpacing}">
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="匿名使用数据与隐私" />
<ui:SettingsExpander Header="匿名使用数据与隐私"
Description="完全匿名的使用统计,仅用于改进软件稳定性与性能。"
IsExpanded="True">
<ui:SettingsExpander.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Shield}" />
</ui:SettingsExpander.HeaderIcon>
<ui:SettingsExpander.Items>
<ui:SettingsCard ContentAlignment="Left">
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="4" VerticalAlignment="Center">
<CheckBox x:Name="CheckBoxPrivacyAccepted"
VerticalAlignment="Center"
Checked="CheckBoxPrivacyAccepted_Changed"
Unchecked="CheckBoxPrivacyAccepted_Changed"
Content="我已阅读并同意" />
<ui:HyperlinkButton Content="《隐私协议》"
Padding="4,0"
VerticalAlignment="Center"
Click="HyperlinkPrivacy_Click" />
</ikw:SimpleStackPanel>
</ui:SettingsCard>
<ui:SettingsCard Header="遥测级别"
Description="本软件不会收集课堂内容、学生个人信息或可识别的原始墨迹。详细请见 privacy.txt。">
<ComboBox x:Name="ComboBoxTelemetryUploadLevel" MinWidth="220">
<ComboBoxItem Content="不上传任何匿名使用数据" />
<ComboBoxItem Content="基础(崩溃信息、版本与系统信息)" />
<ComboBoxItem Content="可选(功能使用频率等)" />
</ComboBox>
</ui:SettingsCard>
</ui:SettingsExpander.Items>
</ui:SettingsExpander>
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Margin="1,18,0,6"
Text="启动与崩溃处理" />
<controls:LabeledSettingsCard x:Name="CardFoldAtStartup"
Header="启动时收纳到屏幕边缘"
Description="启动后将浮动工具栏自动隐藏到屏幕边缘。"
Icon="{x:Static ui:SegoeFluentIcons.ChevronLeft}"
SwitchName="ToggleSwitchFoldAtStartup" />
<controls:LabeledSettingsCard x:Name="CardAutoUpdate"
Header="自动检查更新"
Description="允许后台检查更新并下载新版本。"
Icon="{x:Static ui:SegoeFluentIcons.Sync}"
SwitchName="ToggleSwitchAutoUpdate" />
<ui:SettingsCard Header="发生未处理异常时"
Description="可在 设置 → 启动 中修改。">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Warning}" />
</ui:SettingsCard.HeaderIcon>
<ComboBox x:Name="ComboBoxCrashAction" MinWidth="200">
<ComboBoxItem Content="静默重启软件(推荐)" />
<ComboBoxItem Content="无操作(仅记录日志)" />
</ComboBox> </ComboBox>
</ui:SettingsCard> </ui:SettingsCard>
</ui:SettingsExpander.Items> </ikw:SimpleStackPanel>
</ui:SettingsExpander>
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" <!-- 步骤 2: 画板与墨迹(含纠正) -->
Margin="1,18,0,6" <ikw:SimpleStackPanel x:Name="StepCanvasPanel"
Text="启动与崩溃处理" /> Spacing="{StaticResource SettingsCardSpacing}"
Visibility="Collapsed">
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="画板与墨迹" />
<controls:LabeledSettingsCard x:Name="CardFoldAtStartup" <controls:LabeledSettingsCard x:Name="CardShowCursor"
Header="启动时收纳到屏幕边缘" Header="显示画笔光标"
Description="启动后将浮动工具栏自动隐藏到屏幕边缘。" Description="绘制时显示光标位置。"
Icon="{x:Static ui:SegoeFluentIcons.ChevronLeft}" Icon="{x:Static ui:SegoeFluentIcons.TouchPointer}"
SwitchName="ToggleSwitchFoldAtStartup" /> SwitchName="ToggleSwitchShowCursor" />
<controls:LabeledSettingsCard x:Name="CardAutoUpdate" <controls:LabeledSettingsCard x:Name="CardDisablePressure"
Header="自动检查更新" Header="屏蔽压感"
Description="允许后台检查更新并下载新版本。" Description="所有笔画统一粗细,不再受压力影响。"
Icon="{x:Static ui:SegoeFluentIcons.Sync}" Icon="{x:Static ui:SegoeFluentIcons.Edit}"
SwitchName="ToggleSwitchAutoUpdate" /> SwitchName="ToggleSwitchDisablePressure" />
<ui:SettingsCard Header="发生未处理异常时" <controls:LabeledSettingsCard x:Name="CardHideStrokeWhenSelecting"
Description="可在 设置 → 启动 中修改。"> Header="退出画板模式时隐藏墨迹"
<ui:SettingsCard.HeaderIcon> Description="进入 PPT 非批注模式时不显示墨迹。"
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Warning}" /> Icon="{x:Static ui:SegoeFluentIcons.Hide}"
</ui:SettingsCard.HeaderIcon> SwitchName="ToggleSwitchHideStrokeWhenSelecting" />
<ComboBox x:Name="ComboBoxCrashAction" MinWidth="200">
<ComboBoxItem Content="静默重启软件(推荐)" />
<ComboBoxItem Content="无操作(仅记录日志)" />
</ComboBox>
</ui:SettingsCard>
</ikw:SimpleStackPanel>
<!-- 步骤 2: 画板与墨迹(含纠正) --> <TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
<ikw:SimpleStackPanel x:Name="StepCanvasPanel" Margin="1,18,0,6"
Spacing="{StaticResource SettingsCardSpacing}" Text="墨迹纠正" />
Visibility="Collapsed">
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="画板与墨迹" />
<controls:LabeledSettingsCard x:Name="CardShowCursor" <controls:LabeledSettingsCard x:Name="CardInkToShapeEnabled"
Header="显示画笔光标" Header="启用墨迹识别"
Description="绘制时显示光标位置。" Description="手绘图形自动转为标准形状。"
Icon="{x:Static ui:SegoeFluentIcons.TouchPointer}" Icon="{x:Static ui:SegoeFluentIcons.Draw}"
SwitchName="ToggleSwitchShowCursor" /> SwitchName="ToggleSwitchInkToShapeEnabled" />
</ikw:SimpleStackPanel>
<controls:LabeledSettingsCard x:Name="CardDisablePressure" <!-- 步骤 3: 手势 -->
Header="屏蔽压感" <ikw:SimpleStackPanel x:Name="StepGesturesPanel"
Description="所有笔画统一粗细,不再受压力影响。" Spacing="{StaticResource SettingsCardSpacing}"
Icon="{x:Static ui:SegoeFluentIcons.Edit}" Visibility="Collapsed">
SwitchName="ToggleSwitchDisablePressure" /> <TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="手势操作" />
<controls:LabeledSettingsCard x:Name="CardHideStrokeWhenSelecting" <controls:LabeledSettingsCard x:Name="CardTwoFingerZoom"
Header="退出画板模式时隐藏墨迹" Header="启用双指缩放"
Description="进入 PPT 非批注模式时不显示墨迹。" Icon="{x:Static ui:SegoeFluentIcons.View}"
Icon="{x:Static ui:SegoeFluentIcons.Hide}" SwitchName="ToggleSwitchTwoFingerZoom" />
SwitchName="ToggleSwitchHideStrokeWhenSelecting" />
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" <controls:LabeledSettingsCard x:Name="CardTwoFingerTranslate"
Margin="1,18,0,6" Header="启用双指平移"
Text="墨迹纠正" /> Icon="{x:Static ui:SegoeFluentIcons.Touch}"
SwitchName="ToggleSwitchTwoFingerTranslate" />
<controls:LabeledSettingsCard x:Name="CardInkToShapeEnabled" <controls:LabeledSettingsCard x:Name="CardAutoSwitchTwoFingerGesture"
Header="启用墨迹识别" Header="进出白板模式时自动切换双指手势"
Description="手绘图形自动转为标准形状。" Icon="{x:Static ui:SegoeFluentIcons.Switch}"
Icon="{x:Static ui:SegoeFluentIcons.Draw}" SwitchName="ToggleSwitchAutoSwitchTwoFingerGesture" />
SwitchName="ToggleSwitchInkToShapeEnabled" />
</ikw:SimpleStackPanel>
<!-- 步骤 3: 手势 --> <controls:LabeledSettingsCard x:Name="CardEnablePalmEraser"
<ikw:SimpleStackPanel x:Name="StepGesturesPanel" Header="启用手掌擦"
Spacing="{StaticResource SettingsCardSpacing}" Description="多触点且接触面积较大时临时切换为橡皮。"
Visibility="Collapsed"> Icon="{x:Static ui:SegoeFluentIcons.EraseTool}"
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" SwitchName="ToggleSwitchEnablePalmEraser" />
Text="手势操作" /> </ikw:SimpleStackPanel>
<controls:LabeledSettingsCard x:Name="CardTwoFingerZoom" <!-- 步骤 4: 个性化 -->
Header="启用双指缩放" <ikw:SimpleStackPanel x:Name="StepAppearancePanel"
Icon="{x:Static ui:SegoeFluentIcons.View}" Spacing="{StaticResource SettingsCardSpacing}"
SwitchName="ToggleSwitchTwoFingerZoom" /> Visibility="Collapsed">
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="主题" />
<controls:LabeledSettingsCard x:Name="CardTwoFingerTranslate" <ui:SettingsCard Header="应用主题"
Header="启用双指平移" Description="可在 设置 → 个性化 中随时修改。">
Icon="{x:Static ui:SegoeFluentIcons.Touch}" <ui:SettingsCard.HeaderIcon>
SwitchName="ToggleSwitchTwoFingerTranslate" /> <ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Personalize}" />
</ui:SettingsCard.HeaderIcon>
<ComboBox x:Name="ComboBoxTheme" MinWidth="160">
<ComboBoxItem Content="浅色" />
<ComboBoxItem Content="深色" />
<ComboBoxItem Content="跟随系统" />
</ComboBox>
</ui:SettingsCard>
<controls:LabeledSettingsCard x:Name="CardAutoSwitchTwoFingerGesture" <controls:LabeledSettingsCard x:Name="CardEnableSplashScreen"
Header="进出白板模式时自动切换双指手势" Header="启用启动动画"
Icon="{x:Static ui:SegoeFluentIcons.Switch}" Description="首次启动时显示全屏欢迎页。"
SwitchName="ToggleSwitchAutoSwitchTwoFingerGesture" /> Icon="{x:Static ui:SegoeFluentIcons.Photo}"
SwitchName="ToggleSwitchEnableSplashScreen" />
<controls:LabeledSettingsCard x:Name="CardEnablePalmEraser" <TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Header="启用手掌擦" Margin="1,18,0,6"
Description="多触点且接触面积较大时临时切换为橡皮。" Text="托盘与快速面板" />
Icon="{x:Static ui:SegoeFluentIcons.EraseTool}"
SwitchName="ToggleSwitchEnablePalmEraser" />
</ikw:SimpleStackPanel>
<!-- 步骤 4: 个性化(主题 + 启动动画 + 托盘 + 快速面板 + 快捷键) --> <controls:LabeledSettingsCard x:Name="CardEnableTrayIcon"
<ikw:SimpleStackPanel x:Name="StepAppearancePanel" Header="在系统托盘显示图标"
Spacing="{StaticResource SettingsCardSpacing}" Description="方便后台快速唤出 InkCanvasForClass。"
Visibility="Collapsed"> Icon="{x:Static ui:SegoeFluentIcons.Pin}"
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" SwitchName="ToggleSwitchEnableTrayIcon" />
Text="主题" />
<ui:SettingsCard Header="应用主题" <controls:LabeledSettingsCard x:Name="CardShowQuickPanel"
Description="可在 设置 → 个性化 中随时修改。"> Header="启用快速面板"
<ui:SettingsCard.HeaderIcon> Description="计时器、点名等常用工具的快速入口。"
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Personalize}" /> Icon="{x:Static ui:SegoeFluentIcons.ViewAll}"
</ui:SettingsCard.HeaderIcon> SwitchName="ToggleSwitchShowQuickPanel" />
<ComboBox x:Name="ComboBoxTheme" MinWidth="160">
<ComboBoxItem Content="浅色" />
<ComboBoxItem Content="深色" />
<ComboBoxItem Content="跟随系统" />
</ComboBox>
</ui:SettingsCard>
<controls:LabeledSettingsCard x:Name="CardEnableSplashScreen" <TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Header="启用启动动画" Margin="1,18,0,6"
Description="首次启动时显示全屏欢迎页。" Text="快捷键" />
Icon="{x:Static ui:SegoeFluentIcons.Photo}"
SwitchName="ToggleSwitchEnableSplashScreen" />
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" <controls:LabeledSettingsCard x:Name="CardEnableHotkeysInMouseMode"
Margin="1,18,0,6" Header="鼠标模式下启用全局快捷键"
Text="托盘与快速面板" /> Description="具体快捷键可在 设置 → 个性化 中调整。"
Icon="{x:Static ui:SegoeFluentIcons.PenWorkspace}"
SwitchName="ToggleSwitchEnableHotkeysInMouseMode" />
</ikw:SimpleStackPanel>
<controls:LabeledSettingsCard x:Name="CardEnableTrayIcon" <!-- 步骤 5: PowerPoint -->
Header="在系统托盘显示图标" <ikw:SimpleStackPanel x:Name="StepPptPanel"
Description="方便后台快速唤出 InkCanvasForClass。" Spacing="{StaticResource SettingsCardSpacing}"
Icon="{x:Static ui:SegoeFluentIcons.Pin}" Visibility="Collapsed">
SwitchName="ToggleSwitchEnableTrayIcon" /> <TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="PowerPoint 联动" />
<controls:LabeledSettingsCard x:Name="CardShowQuickPanel" <controls:LabeledSettingsCard x:Name="CardPptSupport"
Header="启用快速面板" Header="启用与 PowerPoint / WPS 的联动"
Description="计时器、点名等常用工具的快速入口。" Description="在幻灯片中一键呼出画板。"
Icon="{x:Static ui:SegoeFluentIcons.ViewAll}" Icon="{x:Static ui:SegoeFluentIcons.Slideshow}"
SwitchName="ToggleSwitchShowQuickPanel" /> SwitchName="ToggleSwitchPptSupport" />
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" <controls:LabeledSettingsCard x:Name="CardPptAutoSaveStrokes"
Margin="1,18,0,6" Header="放映中自动保存墨迹"
Text="快捷键" /> Description="避免误关丢失板书。"
Icon="{x:Static ui:SegoeFluentIcons.Save}"
SwitchName="ToggleSwitchPptAutoSaveStrokes" />
<controls:LabeledSettingsCard x:Name="CardEnableHotkeysInMouseMode" <controls:LabeledSettingsCard x:Name="CardPptAutoSaveScreenshots"
Header="鼠标模式下启用全局快捷键" Header="放映中自动保存截屏"
Description="具体快捷键可在 设置 → 个性化 中调整。" Description="用于事后回看课堂内容。"
Icon="{x:Static ui:SegoeFluentIcons.PenWorkspace}" Icon="{x:Static ui:SegoeFluentIcons.Camera}"
SwitchName="ToggleSwitchEnableHotkeysInMouseMode" /> SwitchName="ToggleSwitchPptAutoSaveScreenshots" />
</ikw:SimpleStackPanel>
<!-- 步骤 5: PowerPoint --> <controls:LabeledSettingsCard x:Name="CardPptTimeCapsule"
<ikw:SimpleStackPanel x:Name="StepPptPanel" Header="启用 PPT 时间胶囊"
Spacing="{StaticResource SettingsCardSpacing}" Description="放映时显示时间提醒。"
Visibility="Collapsed"> Icon="{x:Static ui:SegoeFluentIcons.QuietHours}"
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" SwitchName="ToggleSwitchPptTimeCapsule" />
Text="PowerPoint 联动" /> </ikw:SimpleStackPanel>
<controls:LabeledSettingsCard x:Name="CardPptSupport" <!-- 步骤 6: 自动化 + 截图 -->
Header="启用与 PowerPoint / WPS 的联动" <ikw:SimpleStackPanel x:Name="StepAutomationPanel"
Description="在幻灯片中一键呼出画板。" Spacing="{StaticResource SettingsCardSpacing}"
Icon="{x:Static ui:SegoeFluentIcons.Slideshow}" Visibility="Collapsed">
SwitchName="ToggleSwitchPptSupport" /> <TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="自动化行为" />
<controls:LabeledSettingsCard x:Name="CardPptAutoSaveStrokes" <controls:LabeledSettingsCard x:Name="CardAutoFoldInPPTSlideShow"
Header="放映自动保存墨迹" Header="进入 PPT 放映自动收纳"
Description="避免误关丢失板书。" Icon="{x:Static ui:SegoeFluentIcons.ChevronLeft}"
Icon="{x:Static ui:SegoeFluentIcons.Save}" SwitchName="ToggleSwitchAutoFoldInPPTSlideShow" />
SwitchName="ToggleSwitchPptAutoSaveStrokes" />
<controls:LabeledSettingsCard x:Name="CardPptAutoSaveScreenshots" <controls:LabeledSettingsCard x:Name="CardEnableAutoSaveStrokes"
Header="放映中自动保存截屏" Header="启用墨迹自动保存"
Description="用于事后回看课堂内容。" Description="定时将墨迹保存到本地。"
Icon="{x:Static ui:SegoeFluentIcons.Camera}" Icon="{x:Static ui:SegoeFluentIcons.Save}"
SwitchName="ToggleSwitchPptAutoSaveScreenshots" /> SwitchName="ToggleSwitchEnableAutoSaveStrokes" />
<controls:LabeledSettingsCard x:Name="CardPptTimeCapsule" <controls:LabeledSettingsCard x:Name="CardFloatingWindowInterceptor"
Header="启用 PPT 时间胶囊" Header="启用悬浮窗拦截"
Description="放映时显示时间提醒。" Description="拦截希沃、鸿合等课堂软件的悬浮窗,避免遮挡画板。"
Icon="{x:Static ui:SegoeFluentIcons.QuietHours}" Icon="{x:Static ui:SegoeFluentIcons.Cancel}"
SwitchName="ToggleSwitchPptTimeCapsule" /> SwitchName="ToggleSwitchFloatingWindowInterceptor" />
</ikw:SimpleStackPanel>
<!-- 步骤 6: 自动化 + 截图 --> <TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
<ikw:SimpleStackPanel x:Name="StepAutomationPanel" Margin="1,18,0,6"
Spacing="{StaticResource SettingsCardSpacing}" Text="截图与屏幕捕捉" />
Visibility="Collapsed">
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="自动化行为" />
<controls:LabeledSettingsCard x:Name="CardAutoFoldInPPTSlideShow" <controls:LabeledSettingsCard x:Name="CardAutoSaveStrokesAtClear"
Header="进入 PPT 放映时自动收纳" Header="清屏时自动保存截图"
Icon="{x:Static ui:SegoeFluentIcons.ChevronLeft}" Icon="{x:Static ui:SegoeFluentIcons.Camera}"
SwitchName="ToggleSwitchAutoFoldInPPTSlideShow" /> SwitchName="ToggleSwitchAutoSaveStrokesAtClear" />
<controls:LabeledSettingsCard x:Name="CardEnableAutoSaveStrokes" <controls:LabeledSettingsCard x:Name="CardSaveScreenshotsInDateFolders"
Header="启用墨迹自动保存" Header="按日期分文件夹保存截图"
Description="定时将墨迹保存到本地。" Icon="{x:Static ui:SegoeFluentIcons.Folder}"
Icon="{x:Static ui:SegoeFluentIcons.Save}" SwitchName="ToggleSwitchSaveScreenshotsInDateFolders" />
SwitchName="ToggleSwitchEnableAutoSaveStrokes" /> </ikw:SimpleStackPanel>
<controls:LabeledSettingsCard x:Name="CardFloatingWindowInterceptor" <!-- 步骤 7: 随机点名 -->
Header="启用悬浮窗拦截" <ikw:SimpleStackPanel x:Name="StepLuckyRandomPanel"
Description="拦截希沃、鸿合等课堂软件的悬浮窗,避免遮挡画板。" Spacing="{StaticResource SettingsCardSpacing}"
Icon="{x:Static ui:SegoeFluentIcons.Cancel}" Visibility="Collapsed">
SwitchName="ToggleSwitchFloatingWindowInterceptor" /> <TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="随机点名" />
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" <controls:LabeledSettingsCard x:Name="CardShowRandomAndSingleDraw"
Margin="1,18,0,6" Header="显示随机抽和单次抽按钮"
Text="截图与屏幕捕捉" /> Icon="{x:Static ui:SegoeFluentIcons.People}"
SwitchName="ToggleSwitchShowRandomAndSingleDraw" />
</ikw:SimpleStackPanel>
<controls:LabeledSettingsCard x:Name="CardAutoSaveStrokesAtClear" <!-- 步骤 8: 高级 -->
Header="清屏时自动保存截图" <ikw:SimpleStackPanel x:Name="StepAdvancedPanel"
Icon="{x:Static ui:SegoeFluentIcons.Camera}" Spacing="{StaticResource SettingsCardSpacing}"
SwitchName="ToggleSwitchAutoSaveStrokesAtClear" /> Visibility="Collapsed">
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="高级选项" />
<controls:LabeledSettingsCard x:Name="CardSaveScreenshotsInDateFolders" <controls:LabeledSettingsCard x:Name="CardIsLogEnabled"
Header="按日期分文件夹保存截图" Header="启用日志"
Icon="{x:Static ui:SegoeFluentIcons.Folder}" Description="便于问题排查与反馈。"
SwitchName="ToggleSwitchSaveScreenshotsInDateFolders" /> Icon="{x:Static ui:SegoeFluentIcons.Document}"
</ikw:SimpleStackPanel> SwitchName="ToggleSwitchIsLogEnabled" />
</ikw:SimpleStackPanel>
<!-- 步骤 7: 随机点名 --> <Rectangle Height="32" />
<ikw:SimpleStackPanel x:Name="StepLuckyRandomPanel" </ikw:SimpleStackPanel>
Spacing="{StaticResource SettingsCardSpacing}" </Grid>
Visibility="Collapsed"> </ScrollViewer>
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="随机点名" />
<controls:LabeledSettingsCard x:Name="CardShowRandomAndSingleDraw" <!-- 完成页 -->
Header="显示随机抽和单次抽按钮" <Grid x:Name="FinishPanel" Visibility="Collapsed">
Icon="{x:Static ui:SegoeFluentIcons.People}" <ScrollViewer VerticalScrollBarVisibility="Auto"
SwitchName="ToggleSwitchShowRandomAndSingleDraw" /> HorizontalScrollBarVisibility="Disabled">
</ikw:SimpleStackPanel> <ikw:SimpleStackPanel HorizontalAlignment="Center"
VerticalAlignment="Top"
MaxWidth="640"
Margin="40,32,40,16"
Spacing="14">
<Border Width="80" Height="80"
HorizontalAlignment="Center"
CornerRadius="20"
Background="{DynamicResource SystemControlBackgroundAccentBrush}">
<ui:FontIcon FontSize="40"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Icon="{x:Static ui:SegoeFluentIcons.Accept}" />
</Border>
<!-- 步骤 8: 高级 --> <TextBlock Text="一切就绪"
<ikw:SimpleStackPanel x:Name="StepAdvancedPanel" Style="{DynamicResource TitleLargeTextBlockStyle}"
Spacing="{StaticResource SettingsCardSpacing}" HorizontalAlignment="Center"
Visibility="Collapsed"> TextAlignment="Center"
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Margin="0,4,0,0" />
Text="高级选项" />
<controls:LabeledSettingsCard x:Name="CardIsLogEnabled" <TextBlock HorizontalAlignment="Center"
Header="启用日志" TextAlignment="Center"
Description="便于问题排查与反馈。" TextWrapping="Wrap"
Icon="{x:Static ui:SegoeFluentIcons.Document}" Opacity="0.85"
SwitchName="ToggleSwitchIsLogEnabled" /> Style="{DynamicResource BodyTextBlockStyle}"
</ikw:SimpleStackPanel> Text="确认下面的关键配置后即可开始使用。所有设置稍后都能在「设置」中修改。" />
<Rectangle Height="32" /> <Border CornerRadius="8"
</ikw:SimpleStackPanel> Padding="16"
</Grid> Margin="0,8,0,0"
</ScrollViewer> BorderThickness="1"
</Grid> BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">
<ikw:SimpleStackPanel x:Name="FinishSummaryHost" Spacing="6" />
</Border>
</ikw:SimpleStackPanel>
</ScrollViewer>
</Grid>
<!-- Footer -->
<Border Grid.Row="3"
Padding="56,12,56,16"
BorderThickness="0,1,0,0"
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" VerticalAlignment="Center" Margin="0,0,16,0" MaxWidth="420" HorizontalAlignment="Left">
<Border Height="4" CornerRadius="2"
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}" />
<Border x:Name="ProgressFill"
Height="4"
CornerRadius="2"
HorizontalAlignment="Left"
Background="{DynamicResource SystemControlForegroundAccentBrush}" />
</Grid> </Grid>
<TextBlock Grid.Column="1" <!-- Footer -->
x:Name="FooterStepText" <Border Grid.Row="1"
VerticalAlignment="Center" Padding="40,12,40,16"
Margin="0,0,16,0" BorderThickness="0,1,0,0"
Style="{DynamicResource CaptionTextBlockStyle}" BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}">
Opacity="0.7" <Grid>
Text="1 / 8" /> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button x:Name="BtnPreviousStep" <ui:ProgressBar x:Name="StepProgressBar"
Grid.Column="2" Grid.Column="0"
MinWidth="96" VerticalAlignment="Center"
Height="32" Margin="0,0,16,0"
Margin="0,0,8,0" MaxWidth="420"
Click="BtnPreviousStep_Click"> HorizontalAlignment="Stretch"
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6"> Minimum="0"
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Back}" FontSize="12" /> Maximum="100"
<TextBlock Text="上一步" /> Value="0" />
</ikw:SimpleStackPanel>
</Button>
<Button x:Name="BtnConfirm" <TextBlock Grid.Column="1"
Grid.Column="3" x:Name="FooterStepText"
MinWidth="140" VerticalAlignment="Center"
Height="32" Margin="0,0,16,0"
Style="{DynamicResource AccentButtonStyle}" Style="{DynamicResource CaptionTextBlockStyle}"
Click="BtnConfirm_Click"> Opacity="0.7"
<ikw:SimpleStackPanel x:Name="BtnConfirmContent" Orientation="Horizontal" Spacing="6"> Text="" />
<TextBlock x:Name="BtnConfirmText" Text="下一步" />
<ui:FontIcon x:Name="BtnConfirmIcon" Icon="{x:Static ui:SegoeFluentIcons.ChevronRight}" FontSize="12" /> <Button x:Name="BtnPreviousStep"
</ikw:SimpleStackPanel> Grid.Column="2"
</Button> MinWidth="96"
Height="32"
Margin="0,0,8,0"
Click="BtnPreviousStep_Click">
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Back}" FontSize="12" />
<TextBlock Text="上一步" />
</ikw:SimpleStackPanel>
</Button>
<Button x:Name="BtnConfirm"
Grid.Column="3"
MinWidth="140"
Height="32"
Style="{DynamicResource AccentButtonStyle}"
Click="BtnConfirm_Click">
<ikw:SimpleStackPanel x:Name="BtnConfirmContent" Orientation="Horizontal" Spacing="6">
<TextBlock x:Name="BtnConfirmText" Text="下一步" />
<ui:FontIcon x:Name="BtnConfirmIcon" Icon="{x:Static ui:SegoeFluentIcons.ChevronRight}" FontSize="12" />
</ikw:SimpleStackPanel>
</Button>
</Grid>
</Border>
</Grid> </Grid>
</Border> </ui:NavigationView>
</Grid> </Grid>
</Window> </Window>
+235 -56
View File
@@ -4,21 +4,33 @@ using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Animation; using System.Windows.Media.Animation;
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;
namespace Ink_Canvas.Windows namespace Ink_Canvas.Windows
{ {
/// <summary> /// <summary>
/// 首次启动体验(OOBE)窗口,使用与设置窗口一致的卡片化 UI 引导用户完成初始配置。 /// 首次启动体验(OOBE)窗口使用 iNKORE.UI.WPF.Modern 的 NavigationView 作为左侧导航,
/// 切换步骤时使用横向滑动 + 淡入,模仿 ClassIsland 的设置向导动画风格 /// 引导用户依次完成欢迎页、8 个配置步骤与完成摘要页
/// </summary> /// </summary>
public partial class OobeWindow : Window public partial class OobeWindow : Window
{ {
private readonly Settings _settings; private readonly Settings _settings;
private int _currentStep = 0;
private const int MaxStepIndex = 7; // 视图状态: -1 = 欢迎; 0..7 = 步骤; 8 = 完成
private const int WelcomeIndex = -1;
private const int FinishIndex = 8;
private const int StepCount = 8; private const int StepCount = 8;
private const int MaxStepIndex = StepCount - 1;
private int _currentStep = WelcomeIndex;
private bool _suppressNavSelection;
private FrameworkElement[] _stepPanels; private FrameworkElement[] _stepPanels;
private NavigationViewItem[] _navItems;
private static readonly TimeSpan SlideDuration = TimeSpan.FromMilliseconds(280); private static readonly TimeSpan SlideDuration = TimeSpan.FromMilliseconds(280);
private static readonly IEasingFunction SlideEase = new CubicEase { EasingMode = EasingMode.EaseOut }; private static readonly IEasingFunction SlideEase = new CubicEase { EasingMode = EasingMode.EaseOut };
@@ -43,8 +55,21 @@ namespace Ink_Canvas.Windows
StepAdvancedPanel, StepAdvancedPanel,
}; };
_navItems = new[]
{
NavItemTelemetry,
NavItemCanvas,
NavItemGestures,
NavItemAppearance,
NavItemPpt,
NavItemAutomation,
NavItemLuckyRandom,
NavItemAdvanced,
};
InitializeFromSettings(); InitializeFromSettings();
UpdateStepUI(animateDirection: 0, instant: true); UpdateView(animateDirection: 0, instant: true);
SyncNavSelection();
} }
#region Settings IO #region Settings IO
@@ -273,8 +298,14 @@ namespace Ink_Canvas.Windows
#region Navigation #region Navigation
private void BtnStartWelcome_Click(object sender, RoutedEventArgs e)
{
NavigateTo(0, direction: 1);
}
private void BtnConfirm_Click(object sender, RoutedEventArgs e) private void BtnConfirm_Click(object sender, RoutedEventArgs e)
{ {
// 离开"启动与隐私"页前必须勾选隐私协议
if (_currentStep == 0 && CheckBoxPrivacyAccepted.IsChecked != true) if (_currentStep == 0 && CheckBoxPrivacyAccepted.IsChecked != true)
{ {
MessageBox.Show(this, MessageBox.Show(this,
@@ -285,23 +316,83 @@ namespace Ink_Canvas.Windows
return; return;
} }
if (_currentStep < MaxStepIndex) if (_currentStep == FinishIndex)
{ {
_currentStep++; ApplySelection();
UpdateStepUI(animateDirection: 1); DialogResult = true;
Close();
return; return;
} }
ApplySelection(); NavigateTo(_currentStep + 1, direction: 1);
DialogResult = true;
Close();
} }
private void BtnPreviousStep_Click(object sender, RoutedEventArgs e) private void BtnPreviousStep_Click(object sender, RoutedEventArgs e)
{ {
if (_currentStep <= 0) return; if (_currentStep <= WelcomeIndex) return;
_currentStep--; NavigateTo(_currentStep - 1, direction: -1);
UpdateStepUI(animateDirection: -1); }
private void NavView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
if (_suppressNavSelection) return;
int target = ResolveTargetFromNavItem(args.SelectedItem as NavigationViewItem);
if (target == _currentStep) return;
// 强制隐私门禁: 如果当前在步骤 0 且未勾选, 仅允许返回欢迎页, 不允许跳到后续步骤
if (_currentStep == 0 && CheckBoxPrivacyAccepted.IsChecked != true && target > 0)
{
MessageBox.Show(this,
"请先勾选\"我已阅读并同意《隐私协议》\"后再继续。",
"需要同意隐私协议",
MessageBoxButton.OK,
MessageBoxImage.Information);
SyncNavSelection();
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;
}
} }
private void CheckBoxPrivacyAccepted_Changed(object sender, RoutedEventArgs e) private void CheckBoxPrivacyAccepted_Changed(object sender, RoutedEventArgs e)
@@ -328,11 +419,16 @@ namespace Ink_Canvas.Windows
{ {
System.Diagnostics.Debug.WriteLine(ex); System.Diagnostics.Debug.WriteLine(ex);
} }
finally
{
_privacyDialogShown = false;
}
} }
private void UpdateConfirmEnabled() private void UpdateConfirmEnabled()
{ {
if (BtnConfirm == null || CheckBoxPrivacyAccepted == null) return; if (BtnConfirm == null || CheckBoxPrivacyAccepted == null) return;
// 在步骤 0 时, 必须先勾选隐私协议才能继续
BtnConfirm.IsEnabled = _currentStep != 0 || CheckBoxPrivacyAccepted.IsChecked == true; BtnConfirm.IsEnabled = _currentStep != 0 || CheckBoxPrivacyAccepted.IsChecked == true;
} }
@@ -357,35 +453,70 @@ namespace Ink_Canvas.Windows
} }
} }
private void UpdateStepUI(int animateDirection, bool instant = false) private void UpdateView(int animateDirection, bool instant = false)
{ {
try try
{ {
for (int i = 0; i < _stepPanels.Length; i++) 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)
{ {
_stepPanels[i].Visibility = i == _currentStep ? Visibility.Visible : Visibility.Collapsed; 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();
} }
StepIndicatorText.Text = $"步骤 {_currentStep + 1} / {StepCount}"; if (isFinish)
FooterStepText.Text = $"{_currentStep + 1} / {StepCount}"; {
BtnPreviousStep.Visibility = _currentStep > 0 ? Visibility.Visible : Visibility.Collapsed; BuildFinishSummary();
}
ApplyStepMeta(_currentStep); // 底部进度: 欢迎=0, 各步骤按比例, 完成=100
double progress;
if (isWelcome) progress = 0;
else if (isFinish) progress = 100;
else progress = (_currentStep + 1) / (double)(StepCount + 1) * 100.0;
BtnConfirmText.Text = _currentStep == MaxStepIndex ? "保存并开始使用" : "下一步"; AnimateProgress(progress, instant);
BtnConfirmIcon.Icon = _currentStep == MaxStepIndex
? SegoeFluentIcons.Accept // Footer 步骤计数
: SegoeFluentIcons.ChevronRight; if (isWelcome) FooterStepText.Text = string.Empty;
else if (isFinish) FooterStepText.Text = $"{StepCount} / {StepCount} · 完成";
else FooterStepText.Text = $"{_currentStep + 1} / {StepCount}";
// 上一步按钮: 欢迎页隐藏
BtnPreviousStep.Visibility = isWelcome ? Visibility.Collapsed : Visibility.Visible;
// 主按钮: 欢迎页隐藏(由欢迎页自身的"开始"按钮负责)
BtnConfirm.Visibility = isWelcome ? Visibility.Collapsed : Visibility.Visible;
if (isFinish)
{
BtnConfirmText.Text = "保存并开始使用";
BtnConfirmIcon.Icon = SegoeFluentIcons.Accept;
}
else
{
BtnConfirmText.Text = "下一步";
BtnConfirmIcon.Icon = SegoeFluentIcons.ChevronRight;
}
UpdateConfirmEnabled(); UpdateConfirmEnabled();
if (StepScrollViewer != null) StepScrollViewer.ScrollToTop();
AnimateProgress(instant);
if (!instant && animateDirection != 0) if (!instant && animateDirection != 0)
{ {
AnimateStepSlide(animateDirection); AnimateContentSlide(animateDirection);
} }
else else
{ {
@@ -401,81 +532,135 @@ namespace Ink_Canvas.Windows
private void ApplyStepMeta(int step) private void ApplyStepMeta(int step)
{ {
string title; string subtitle; FontIconData icon; string title; string subtitle;
switch (step) switch (step)
{ {
case 0: case 0:
title = "启动与隐私"; title = "启动与隐私";
subtitle = "遥测、隐私协议、自动更新与崩溃处理。"; subtitle = "遥测、隐私协议、自动更新与崩溃处理。";
icon = SegoeFluentIcons.Shield;
break; break;
case 1: case 1:
title = "画板与墨迹"; title = "画板与墨迹";
subtitle = "光标、压感、墨迹显示与墨迹纠正。"; subtitle = "光标、压感、墨迹显示与墨迹纠正。";
icon = SegoeFluentIcons.Edit;
break; break;
case 2: case 2:
title = "手势操作"; title = "手势操作";
subtitle = "双指缩放/平移、手掌擦等。"; subtitle = "双指缩放/平移、手掌擦等。";
icon = SegoeFluentIcons.TouchPointer;
break; break;
case 3: case 3:
title = "个性化"; title = "个性化";
subtitle = "主题、启动动画、托盘、快速面板与快捷键。"; subtitle = "主题、启动动画、托盘、快速面板与快捷键。";
icon = SegoeFluentIcons.Personalize;
break; break;
case 4: case 4:
title = "PowerPoint 联动"; title = "PowerPoint 联动";
subtitle = "放映联动、墨迹与截屏自动保存、时间胶囊。"; subtitle = "放映联动、墨迹与截屏自动保存、时间胶囊。";
icon = SegoeFluentIcons.Slideshow;
break; break;
case 5: case 5:
title = "自动化与截图"; title = "自动化与截图";
subtitle = "自动收纳、墨迹自动保存、悬浮窗拦截与截图保存。"; subtitle = "自动收纳、墨迹自动保存、悬浮窗拦截与截图保存。";
icon = SegoeFluentIcons.Sync;
break; break;
case 6: case 6:
title = "随机点名"; title = "随机点名";
subtitle = "点名窗口选项。"; subtitle = "点名窗口选项。";
icon = SegoeFluentIcons.People;
break; break;
case 7: case 7:
title = "高级选项"; title = "高级选项";
subtitle = "日志等高级配置。"; subtitle = "日志等高级配置。";
icon = SegoeFluentIcons.Settings;
break; break;
default: default:
title = string.Empty; subtitle = string.Empty; icon = SegoeFluentIcons.Home; title = string.Empty; subtitle = string.Empty;
break; break;
} }
StepTitleText.Text = title; StepTitleText.Text = title;
StepSubtitleText.Text = subtitle; StepSubtitleText.Text = subtitle;
StepIcon.Icon = icon;
} }
private void AnimateProgress(bool instant) 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)
{ {
try try
{ {
double total = ActualWidth > 0 ? ActualWidth : Width; if (StepProgressBar == null) return;
double trackWidth = Math.Max(0, Math.Min(420, total - 56 * 2 - 320));
double progress = (_currentStep + 1) / (double)StepCount;
double targetWidth = trackWidth * progress;
if (instant) if (instant)
{ {
ProgressFill.Width = targetWidth; StepProgressBar.BeginAnimation(System.Windows.Controls.Primitives.RangeBase.ValueProperty, null);
StepProgressBar.Value = targetPercent;
return; return;
} }
var anim = new DoubleAnimation var anim = new DoubleAnimation
{ {
To = targetWidth, To = targetPercent,
Duration = TimeSpan.FromMilliseconds(320), Duration = TimeSpan.FromMilliseconds(320),
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut } EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
}; };
ProgressFill.BeginAnimation(WidthProperty, anim); StepProgressBar.BeginAnimation(System.Windows.Controls.Primitives.RangeBase.ValueProperty, anim);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -483,7 +668,7 @@ namespace Ink_Canvas.Windows
} }
} }
private void AnimateStepSlide(int direction) private void AnimateContentSlide(int direction)
{ {
// direction: 1 = 前进 (新内容从右滑入), -1 = 后退 (从左滑入) // direction: 1 = 前进 (新内容从右滑入), -1 = 后退 (从左滑入)
double from = direction > 0 ? 36 : -36; double from = direction > 0 ? 36 : -36;
@@ -509,11 +694,5 @@ namespace Ink_Canvas.Windows
StepHostTransform.BeginAnimation(TranslateTransform.XProperty, slide); StepHostTransform.BeginAnimation(TranslateTransform.XProperty, slide);
StepHost.BeginAnimation(OpacityProperty, fade); StepHost.BeginAnimation(OpacityProperty, fade);
} }
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
AnimateProgress(instant: true);
}
} }
} }