diff --git a/Ink Canvas/App.xaml b/Ink Canvas/App.xaml index 9ed51200..9ac047f0 100644 --- a/Ink Canvas/App.xaml +++ b/Ink Canvas/App.xaml @@ -9,9 +9,7 @@ > - + ./Resources/Fonts/#HarmonyOS Sans SC diff --git a/Ink Canvas/Controls/Toolbar/IToolbarItem.cs b/Ink Canvas/Controls/Toolbar/IToolbarItem.cs index 0c7498c7..2f008826 100644 --- a/Ink Canvas/Controls/Toolbar/IToolbarItem.cs +++ b/Ink Canvas/Controls/Toolbar/IToolbarItem.cs @@ -2,28 +2,24 @@ using System.Windows; namespace Ink_Canvas.Controls.Toolbar { - /// - /// 一个工具栏按钮(或任意浮动栏/白板栏条目)的插件化契约。 - /// 实现类必须有无参构造函数,启动时会被 ToolbarRegistry 反射实例化。 - /// public interface IToolbarItem { - /// 稳定、唯一的 id,用于持久化用户配置。不要随便改。 string Id { get; } ToolbarSlot DefaultSlot { get; } - /// 同一 slot 内的默认顺序,小的在前。 int DefaultOrder { get; } bool DefaultVisible { get; } ToolbarInsertPosition DefaultPosition { get; } - /// 仅当 Position 为 BeforeAnchor/AfterAnchor 时有意义,对应 XAML 里 x:Name。 string DefaultAnchorName { get; } - /// 构造 UI 元素并接线所有行为。 + string DisplayName { get; } + + string MenuPanelName { get; } + FrameworkElement BuildView(IToolbarHost host); } } \ No newline at end of file diff --git a/Ink Canvas/Controls/Toolbar/Items/EraserToolItem.cs b/Ink Canvas/Controls/Toolbar/Items/EraserToolItem.cs index fb44594a..6cb37806 100644 --- a/Ink Canvas/Controls/Toolbar/Items/EraserToolItem.cs +++ b/Ink Canvas/Controls/Toolbar/Items/EraserToolItem.cs @@ -8,6 +8,7 @@ namespace Ink_Canvas.Controls.Toolbar.Items public override string LocalizationKey => "FloatingBar_AreaEraser"; public override ToolbarSlot DefaultSlot => ToolbarSlot.FloatingBarCanvasControls; public override int DefaultOrder => 100; + public override string MenuPanelName => "EraserSizePanel"; protected override void OnClick(IToolbarHost host, object sender, MouseButtonEventArgs e) => host.Window.EraserIcon_Click(sender, e); diff --git a/Ink Canvas/Controls/Toolbar/Items/PenToolItem.cs b/Ink Canvas/Controls/Toolbar/Items/PenToolItem.cs index 8f339eb0..1bd2ac36 100644 --- a/Ink Canvas/Controls/Toolbar/Items/PenToolItem.cs +++ b/Ink Canvas/Controls/Toolbar/Items/PenToolItem.cs @@ -8,6 +8,7 @@ namespace Ink_Canvas.Controls.Toolbar.Items public override string LocalizationKey => "FloatingBar_Annotate"; public override ToolbarSlot DefaultSlot => ToolbarSlot.FloatingBarMain; public override int DefaultOrder => 110; + public override string MenuPanelName => "PenPalette"; protected override void OnClick(IToolbarHost host, object sender, MouseButtonEventArgs e) => host.Window.PenIcon_Click(sender, e); diff --git a/Ink Canvas/Controls/Toolbar/Items/ShapeDrawToolItem.cs b/Ink Canvas/Controls/Toolbar/Items/ShapeDrawToolItem.cs index 675cc43a..48133f54 100644 --- a/Ink Canvas/Controls/Toolbar/Items/ShapeDrawToolItem.cs +++ b/Ink Canvas/Controls/Toolbar/Items/ShapeDrawToolItem.cs @@ -8,6 +8,7 @@ namespace Ink_Canvas.Controls.Toolbar.Items public override string LocalizationKey => "FloatingBar_Geometry"; public override ToolbarSlot DefaultSlot => ToolbarSlot.FloatingBarCanvasControls; public override int DefaultOrder => 130; + public override string MenuPanelName => "BorderDrawShape"; protected override void OnClick(IToolbarHost host, object sender, MouseButtonEventArgs e) => host.Window.ImageDrawShape_MouseUp(sender, e); diff --git a/Ink Canvas/Controls/Toolbar/Items/ToolbarImageButtonItemBase.cs b/Ink Canvas/Controls/Toolbar/Items/ToolbarImageButtonItemBase.cs index 330322e5..e49df7a7 100644 --- a/Ink Canvas/Controls/Toolbar/Items/ToolbarImageButtonItemBase.cs +++ b/Ink Canvas/Controls/Toolbar/Items/ToolbarImageButtonItemBase.cs @@ -5,10 +5,6 @@ using System.Windows.Media; namespace Ink_Canvas.Controls.Toolbar.Items { - /// - /// 通用 ToolbarImageButton 工具栏条目基类——大幅减少每个按钮的样板代码。 - /// 派生类通常只需给 Id / 本地化键 / Slot / Order / 点击处理 / Attach 回填。 - /// internal abstract class ToolbarImageButtonItemBase : IToolbarItem { public abstract string Id { get; } @@ -19,22 +15,22 @@ namespace Ink_Canvas.Controls.Toolbar.Items public virtual ToolbarInsertPosition DefaultPosition => ToolbarInsertPosition.Prepend; public virtual string DefaultAnchorName => null; - /// DynamicResource 名称,用于 IconBrush。默认为 null(使用控件自带前景色)。 - protected virtual string IconBrushResourceKey => null; + public string DisplayName => Strings.GetString(LocalizationKey) ?? LocalizationKey; + public virtual string MenuPanelName => null; - /// DynamicResource 名称,用于 LabelBrush(文字颜色)。默认为 null(使用控件自带前景色)。 + protected virtual string IconBrushResourceKey => null; protected virtual string LabelBrushResourceKey => null; protected abstract void OnClick(IToolbarHost host, object sender, MouseButtonEventArgs e); - /// 构建后调用,用于回填 MainWindow 的原命名属性(partial 扩展里的 Attach*)。可选。 protected virtual void AfterBuild(IToolbarHost host, ToolbarImageButton view) { } public FrameworkElement BuildView(IToolbarHost host) { var btn = new ToolbarImageButton { - Label = Strings.GetString(LocalizationKey) ?? LocalizationKey + Label = Strings.GetString(LocalizationKey) ?? LocalizationKey, + Tag = "ToolbarRegistryInjected" }; if (!string.IsNullOrEmpty(IconBrushResourceKey)) { diff --git a/Ink Canvas/Controls/Toolbar/Items/ToolsToolItem.cs b/Ink Canvas/Controls/Toolbar/Items/ToolsToolItem.cs index cb80b8f8..506a286f 100644 --- a/Ink Canvas/Controls/Toolbar/Items/ToolsToolItem.cs +++ b/Ink Canvas/Controls/Toolbar/Items/ToolsToolItem.cs @@ -10,6 +10,7 @@ namespace Ink_Canvas.Controls.Toolbar.Items public override int DefaultOrder => 110; public override ToolbarInsertPosition DefaultPosition => ToolbarInsertPosition.AfterAnchor; public override string DefaultAnchorName => "FloatingBarEndSeparator"; + public override string MenuPanelName => "BorderTools"; protected override void OnClick(IToolbarHost host, object sender, MouseButtonEventArgs e) => host.Window.SymbolIconTools_MouseUp(sender, e); diff --git a/Ink Canvas/Controls/Toolbar/ToolbarRegistry.cs b/Ink Canvas/Controls/Toolbar/ToolbarRegistry.cs index 3701c296..e0deb13d 100644 --- a/Ink Canvas/Controls/Toolbar/ToolbarRegistry.cs +++ b/Ink Canvas/Controls/Toolbar/ToolbarRegistry.cs @@ -8,12 +8,10 @@ using System.Windows.Controls; namespace Ink_Canvas.Controls.Toolbar { - /// - /// 扫描当前程序集里的 IToolbarItem 实现,按用户配置(Settings.Toolbar)排序/过滤后注入到目标容器。 - /// public static class ToolbarRegistry { private static List _items; + internal const string InjectedTag = "ToolbarRegistryInjected"; public static IReadOnlyList Discover() { @@ -34,13 +32,25 @@ namespace Ink_Canvas.Controls.Toolbar }) .Where(i => i != null) .ToList(); + LogHelper.WriteLogToFile($"ToolbarRegistry: Discover 完成, 发现 {_items.Count} 个条目", LogHelper.LogType.Info); return _items; } - /// 按 slot 分配工具栏条目到对应容器。调用者负责清空目标容器里要被接管的旧内容。 + public static void ClearInjected(Panel container) + { + if (container == null) return; + var toRemove = container.Children.OfType() + .Where(e => e.Tag as string == InjectedTag) + .ToList(); + foreach (var element in toRemove) + container.Children.Remove(element); + LogHelper.WriteLogToFile($"ToolbarRegistry: ClearInjected 清除 {toRemove.Count} 个元素 [{container.Name}]", LogHelper.LogType.Info); + } + public static void Populate(IToolbarHost host, IDictionary slots, ToolbarLayoutSettings layout) { - if (host == null || slots == null) return; + LogHelper.WriteLogToFile($"ToolbarRegistry: Populate 开始", LogHelper.LogType.Info); + if (host == null || slots == null) { LogHelper.WriteLogToFile("ToolbarRegistry: Populate host 或 slots 为空", LogHelper.LogType.Warning); return; } layout = layout ?? new ToolbarLayoutSettings(); var grouped = new Dictionary>(); @@ -65,18 +75,56 @@ namespace Ink_Canvas.Controls.Toolbar } list.Add((item, cfg)); } + LogHelper.WriteLogToFile($"ToolbarRegistry: 分组完成, {grouped.Count} 个 slot 有可见条目", LogHelper.LogType.Info); foreach (var kv in grouped) { if (!slots.TryGetValue(kv.Key, out var container) || container == null) continue; + LogHelper.WriteLogToFile($"ToolbarRegistry: 注入到 {kv.Key}, 条目数={kv.Value.Count}", LogHelper.LogType.Info); InjectIntoContainer(host, container, kv.Value); } + + ApplyMenuVisibility(host, layout); + LogHelper.WriteLogToFile($"ToolbarRegistry: Populate 完成", LogHelper.LogType.Info); + } + + public static void ApplyMenuVisibility(IToolbarHost host, ToolbarLayoutSettings layout) + { + if (host == null || layout == null) return; + foreach (var item in Discover()) + { + if (string.IsNullOrEmpty(item.MenuPanelName)) continue; + bool visible = true; + if (layout.Items.TryGetValue(item.Id, out var cfg)) + visible = cfg.Visible; + try + { + var menuElement = host.Window.FindName(item.MenuPanelName); + if (menuElement is System.Windows.Controls.Primitives.Popup popup) + { + popup.IsOpen = visible; + LogHelper.WriteLogToFile($"ToolbarRegistry: 菜单 Popup [{item.MenuPanelName}] -> {(visible ? "Open" : "Closed")}", LogHelper.LogType.Info); + } + else if (menuElement is FrameworkElement fe) + { + fe.Visibility = visible ? Visibility.Visible : Visibility.Collapsed; + LogHelper.WriteLogToFile($"ToolbarRegistry: 菜单 [{item.MenuPanelName}] -> {(visible ? "Visible" : "Collapsed")}", LogHelper.LogType.Info); + } + else + { + LogHelper.WriteLogToFile($"ToolbarRegistry: 找不到菜单面板 [{item.MenuPanelName}]", LogHelper.LogType.Warning); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ToolbarRegistry: 设置菜单可见性异常 [{item.MenuPanelName}]: {ex.Message}", LogHelper.LogType.Warning); + } + } } private static void InjectIntoContainer(IToolbarHost host, Panel container, List<(IToolbarItem item, ToolbarItemConfig cfg)> entries) { - // 按 Position 分桶,每桶内按 Order 升序。 var prepend = entries.Where(e => e.cfg.Position == ToolbarInsertPosition.Prepend).OrderBy(e => e.cfg.Order).ToList(); var append = entries.Where(e => e.cfg.Position == ToolbarInsertPosition.Append).OrderBy(e => e.cfg.Order).ToList(); var before = entries.Where(e => e.cfg.Position == ToolbarInsertPosition.BeforeAnchor).ToList(); @@ -153,9 +201,9 @@ namespace Ink_Canvas.Controls.Toolbar } catch (Exception ex) { - LogHelper.WriteLogToFile($"ToolbarRegistry: 构建 {item.Id} 失败: {ex.Message}", LogHelper.LogType.Warning); + LogHelper.WriteLogToFile($"ToolbarRegistry: 构建 {item.Id} 失败: {ex.GetType().Name}: {ex.Message}\n{ex.StackTrace}", LogHelper.LogType.Error); return null; } } } -} \ No newline at end of file +} diff --git a/Ink Canvas/Helpers/AnimationsHelper.cs b/Ink Canvas/Helpers/AnimationsHelper.cs index 00e8ba46..bac2f293 100644 --- a/Ink Canvas/Helpers/AnimationsHelper.cs +++ b/Ink Canvas/Helpers/AnimationsHelper.cs @@ -1,5 +1,8 @@ using System; +using System.Runtime.InteropServices; using System.Windows; +using System.Windows.Controls.Primitives; +using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Animation; @@ -7,6 +10,133 @@ namespace Ink_Canvas.Helpers { internal class AnimationsHelper { + #region Win32 API - 用于提升 Popup 层级和刷新位置 + + [DllImport("user32.dll")] + private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + + [DllImport("user32.dll")] + private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); + + [DllImport("user32.dll")] + private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd); + + [StructLayout(LayoutKind.Sequential)] + private struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + + private const uint GW_HWNDPREV = 3; + private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); + private static readonly IntPtr HWND_TOP = new IntPtr(0); + private const uint SWP_NOMOVE = 0x0002; + private const uint SWP_NOSIZE = 0x0001; + private const uint SWP_NOACTIVATE = 0x0010; + private const uint SWP_SHOWWINDOW = 0x0040; + + /// + /// 强制刷新 Popup 的实际窗口位置(终极方案) + /// 通过 Win32 API 直接操作窗口句柄 + /// + public static void ForceRefreshPopupPosition(Popup popup) + { + if (popup?.Child == null || !popup.IsOpen) return; + + try + { + Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + try + { + var source = PresentationSource.FromVisual(popup.Child) as HwndSource; + if (source?.Handle == null) return; + + var hwnd = source.Handle; + + // 获取当前窗口位置 + if (GetWindowRect(hwnd, out RECT rect)) + { + // 使用相同的参数调用 SetWindowPos,但加上 SWP_SHOWWINDOW + // 这会强制窗口管理器重新评估并更新窗口位置 + SetWindowPos( + hwnd, + HWND_TOP, + rect.Left, rect.Top, + rect.Right - rect.Left, + rect.Bottom - rect.Top, + SWP_NOACTIVATE | SWP_SHOWWINDOW); + + System.Diagnostics.Debug.WriteLine($"[PopupZOrder] Force refreshed position: ({rect.Left}, {rect.Top})"); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[PopupZOrder] ForceRefreshPopupPosition failed: {ex.Message}"); + } + }), System.Windows.Threading.DispatcherPriority.Render); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[PopupZOrder] ForceRefreshPopupPosition error: {ex.Message}"); + } + } + + /// + /// 将 Popup 窗口提升到最顶层,确保不被其他控件遮挡 + /// 采用多重策略确保置顶生效 + /// + private static void BringPopupToFront(Popup popup) + { + try + { + if (popup?.Child == null) return; + + Action bringToTopAction = () => + { + try + { + var source = PresentationSource.FromVisual(popup.Child) as HwndSource; + if (source?.Handle == null) return; + + var hwnd = source.Handle; + + // 策略1:直接设置为 TOPMOST(最高优先级) + SetWindowPos(hwnd, HWND_TOPMOST, + 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW); + + System.Diagnostics.Debug.WriteLine($"[PopupZOrder] Set TOPMOST for popup"); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[PopupZOrder] BringPopupToFront failed: {ex.Message}"); + } + }; + + // 立即执行第一次 + Application.Current.Dispatcher.BeginInvoke(bringToTopAction, + System.Windows.Threading.DispatcherPriority.Render); + + // 延迟 50ms 后再次执行(确保在其他窗口操作之后) + Application.Current.Dispatcher.BeginInvoke(bringToTopAction, + System.Windows.Threading.DispatcherPriority.Normal); + + // 延迟 100ms 后第三次执行(最终确认) + Application.Current.Dispatcher.BeginInvoke(bringToTopAction, + System.Windows.Threading.DispatcherPriority.Background); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[PopupZOrder] BringPopupToFront error: {ex.Message}"); + } + } + + #endregion + private static UIElement ResolveAnimationTarget(UIElement element) { return element; @@ -264,7 +394,6 @@ namespace Ink_Canvas.Helpers var sb = new Storyboard(); - // 渐变动画 var fadeOutAnimation = new DoubleAnimation { From = 1, @@ -283,5 +412,110 @@ namespace Ink_Canvas.Helpers sb.Begin((FrameworkElement)element); } + public static void ShowPopupWithSlideAndFade(Popup popup, double duration = 0.15) + { + try + { + if (popup == null) + throw new ArgumentNullException(nameof(popup)); + + if (popup.IsOpen) return; + + var child = popup.Child as FrameworkElement; + if (child == null) + { + popup.IsOpen = true; + BringPopupToFront(popup); + return; + } + + child.Opacity = 0.5; + child.RenderTransform = new TranslateTransform(0, 10); + + popup.IsOpen = true; + + // 提升 Popup 到最顶层 + BringPopupToFront(popup); + + var sb = new Storyboard(); + + var fadeInAnimation = new DoubleAnimation + { + From = 0.5, + To = 1, + Duration = TimeSpan.FromSeconds(duration) + }; + fadeInAnimation.EasingFunction = new CubicEase(); + Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(UIElement.OpacityProperty)); + + var slideAnimation = new DoubleAnimation + { + From = 10, + To = 0, + Duration = TimeSpan.FromSeconds(duration) + }; + slideAnimation.EasingFunction = new CubicEase(); + Storyboard.SetTargetProperty(slideAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.Y)")); + + sb.Children.Add(fadeInAnimation); + sb.Children.Add(slideAnimation); + + sb.Begin(child); + } + catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } + } + + public static void HidePopupWithSlideAndFade(Popup popup, double duration = 0.15) + { + try + { + if (popup == null) + throw new ArgumentNullException(nameof(popup)); + + if (!popup.IsOpen) return; + + var child = popup.Child as FrameworkElement; + if (child == null) + { + popup.IsOpen = false; + return; + } + + var sb = new Storyboard(); + + var fadeOutAnimation = new DoubleAnimation + { + From = 1, + To = 0, + Duration = TimeSpan.FromSeconds(duration) + }; + fadeOutAnimation.EasingFunction = new CubicEase(); + Storyboard.SetTargetProperty(fadeOutAnimation, new PropertyPath(UIElement.OpacityProperty)); + + var slideAnimation = new DoubleAnimation + { + From = 0, + To = 10, + Duration = TimeSpan.FromSeconds(duration) + }; + slideAnimation.EasingFunction = new CubicEase(); + Storyboard.SetTargetProperty(slideAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.Y)")); + + sb.Children.Add(fadeOutAnimation); + sb.Children.Add(slideAnimation); + + sb.Completed += (s, e) => + { + popup.IsOpen = false; + child.Opacity = 1; + child.RenderTransform = new TranslateTransform(); + }; + + child.RenderTransform = new TranslateTransform(); + sb.Begin(child); + } + catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } + } + } } diff --git a/Ink Canvas/InkCanvasForClass.csproj b/Ink Canvas/InkCanvasForClass.csproj index cb702a21..f77658da 100644 --- a/Ink Canvas/InkCanvasForClass.csproj +++ b/Ink Canvas/InkCanvasForClass.csproj @@ -614,6 +614,12 @@ + + + + + + diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 0e2a73bc..d28822a4 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -666,10 +666,10 @@ - + + Visibility="Collapsed" + Panel.ZIndex="1000" /> @@ -1489,18 +1489,20 @@ - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ink Canvas/Windows/OobePresetWindow.xaml.cs b/Ink Canvas/Windows/OobePresetWindow.xaml.cs new file mode 100644 index 00000000..c86f7237 --- /dev/null +++ b/Ink Canvas/Windows/OobePresetWindow.xaml.cs @@ -0,0 +1,176 @@ +using System.Windows; +using System.Windows.Input; +using System.Windows.Media; + +namespace Ink_Canvas.Windows +{ + public partial class OobePresetWindow : Window + { + public enum PresetKind { None, Standard, Lite } + + public PresetKind SelectedPreset { get; private set; } = PresetKind.None; + + public OobePresetWindow() + { + InitializeComponent(); + } + + private void SelectPreset(PresetKind kind) + { + SelectedPreset = kind; + + // 重置所有卡片边框 + var defaultBrush = (Brush)FindResource("SystemControlForegroundBaseLowBrush"); + CardStandard.BorderBrush = defaultBrush; + CardLite.BorderBrush = defaultBrush; + IconStandard.Opacity = 0; + IconLite.Opacity = 0; + + var accentBrush = (Brush)FindResource("SystemControlForegroundAccentBrush"); + switch (kind) + { + case PresetKind.Standard: + CardStandard.BorderBrush = accentBrush; + IconStandard.Opacity = 1; + break; + case PresetKind.Lite: + CardLite.BorderBrush = accentBrush; + IconLite.Opacity = 1; + break; + } + + BtnApply.IsEnabled = kind != PresetKind.None; + } + + private void CardStandard_Click(object sender, MouseButtonEventArgs e) => SelectPreset(PresetKind.Standard); + + private void CardLite_Click(object sender, MouseButtonEventArgs e) => SelectPreset(PresetKind.Lite); + + private void BtnApply_Click(object sender, RoutedEventArgs e) + { + if (SelectedPreset == PresetKind.None) return; + DialogResult = true; + Close(); + } + + private void BtnCancel_Click(object sender, RoutedEventArgs e) + { + DialogResult = false; + Close(); + } + + // ── 预设定义 ──────────────────────────────────────────────────────── + + /// + /// 课堂标准配置:适合大多数教学场景,启用 PPT 联动、自动保存、手势等。 + /// + public static void ApplyStandard(Settings settings) + { + if (settings == null) return; + + // 启动与隐私 + settings.Startup.IsFoldAtStartup = true; + settings.Startup.IsAutoUpdate = true; + settings.Startup.CrashAction = 0; // 静默重启 + settings.Startup.TelemetryUploadLevel = TelemetryUploadLevel.Basic; + settings.Startup.HasAcceptedTelemetryPrivacy = true; + + // 画板与墨迹 + settings.Canvas.IsShowCursor = false; + settings.Canvas.DisablePressure = false; + settings.Canvas.HideStrokeWhenSelecting = true; + settings.Canvas.EnablePalmEraser = true; + + // 墨迹纠正 + settings.InkToShape.IsInkToShapeEnabled = true; + + // 手势 + settings.Gesture.IsEnableTwoFingerZoom = true; + settings.Gesture.IsEnableTwoFingerTranslate = true; + settings.Gesture.AutoSwitchTwoFingerGesture = true; + + // 个性化 + settings.Appearance.Theme = 2; // 跟随系统 + settings.Appearance.EnableSplashScreen = false; + settings.Appearance.EnableTrayIcon = true; + settings.Appearance.IsShowQuickPanel = true; + settings.Appearance.EnableHotkeysInMouseMode = false; + + // PPT 联动 + settings.PowerPointSettings.PowerPointSupport = true; + settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint = true; + settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint = true; + settings.PowerPointSettings.EnablePPTTimeCapsule = true; + + // 自动化 + settings.Automation.IsAutoFoldInPPTSlideShow = false; + settings.Automation.IsEnableAutoSaveStrokes = true; + settings.Automation.IsAutoSaveStrokesAtClear = true; + settings.Automation.IsSaveScreenshotsInDateFolders = true; + if (settings.Automation.FloatingWindowInterceptor != null) + settings.Automation.FloatingWindowInterceptor.IsEnabled = true; + + // 随机点名 + settings.RandSettings.ShowRandomAndSingleDraw = true; + + // 高级 + settings.Advanced.IsLogEnabled = true; + } + + /// + /// 简洁轻量配置:最小化后台行为,适合简单批注场景。 + /// + public static void ApplyLite(Settings settings) + { + if (settings == null) return; + + // 启动与隐私 + settings.Startup.IsFoldAtStartup = true; + settings.Startup.IsAutoUpdate = true; + settings.Startup.CrashAction = 0; + settings.Startup.TelemetryUploadLevel = TelemetryUploadLevel.None; + settings.Startup.HasAcceptedTelemetryPrivacy = true; + + // 画板与墨迹 + settings.Canvas.IsShowCursor = false; + settings.Canvas.DisablePressure = false; + settings.Canvas.HideStrokeWhenSelecting = true; + settings.Canvas.EnablePalmEraser = false; + + // 墨迹纠正 + settings.InkToShape.IsInkToShapeEnabled = false; + + // 手势 + settings.Gesture.IsEnableTwoFingerZoom = false; + settings.Gesture.IsEnableTwoFingerTranslate = false; + settings.Gesture.AutoSwitchTwoFingerGesture = false; + + // 个性化 + settings.Appearance.Theme = 2; // 跟随系统 + settings.Appearance.EnableSplashScreen = false; + settings.Appearance.EnableTrayIcon = true; + settings.Appearance.IsShowQuickPanel = false; + settings.Appearance.EnableHotkeysInMouseMode = false; + + // PPT 联动 + settings.PowerPointSettings.PowerPointSupport = true; + settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint = true; + settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint = true; + settings.PowerPointSettings.EnablePPTTimeCapsule = false; + + // 自动化 + settings.Automation.IsAutoFoldInPPTSlideShow = false; + settings.Automation.IsEnableAutoSaveStrokes = true; + settings.Automation.IsAutoSaveStrokesAtClear = true; + settings.Automation.IsSaveScreenshotsInDateFolders = false; + if (settings.Automation.FloatingWindowInterceptor != null) + settings.Automation.FloatingWindowInterceptor.IsEnabled = false; + + // 随机点名 + settings.RandSettings.ShowRandomAndSingleDraw = true; + + // 高级 + settings.Advanced.IsLogEnabled = true; + } + } +} \ No newline at end of file diff --git a/Ink Canvas/Windows/OobeWindow.xaml b/Ink Canvas/Windows/OobeWindow.xaml index 1ea47654..1f17d12c 100644 --- a/Ink Canvas/Windows/OobeWindow.xaml +++ b/Ink Canvas/Windows/OobeWindow.xaml @@ -194,10 +194,21 @@ Style="{DynamicResource AccentButtonStyle}" Click="BtnStartWelcome_Click"> - + + + diff --git a/Ink Canvas/Windows/OobeWindow.xaml.cs b/Ink Canvas/Windows/OobeWindow.xaml.cs index 23f63c08..cda26854 100644 --- a/Ink Canvas/Windows/OobeWindow.xaml.cs +++ b/Ink Canvas/Windows/OobeWindow.xaml.cs @@ -303,6 +303,28 @@ namespace Ink_Canvas.Windows NavigateTo(0, direction: 1); } + 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(); + } + private void BtnConfirm_Click(object sender, RoutedEventArgs e) { if (_currentStep == FinishIndex) diff --git a/Ink Canvas/Windows/SettingsViews/Pages/InkRecognitionPage.xaml b/Ink Canvas/Windows/SettingsViews/Pages/InkRecognitionPage.xaml index ac652f08..d4c43f18 100644 --- a/Ink Canvas/Windows/SettingsViews/Pages/InkRecognitionPage.xaml +++ b/Ink Canvas/Windows/SettingsViews/Pages/InkRecognitionPage.xaml @@ -157,6 +157,22 @@ OffContent="{DynamicResource Common_Off}" Toggled="ToggleSwitchHighPrecisionLineStraighten_Toggled" /> + + + + + + + + + diff --git a/Ink Canvas/Windows/SettingsViews/Pages/InkRecognitionPage.xaml.cs b/Ink Canvas/Windows/SettingsViews/Pages/InkRecognitionPage.xaml.cs index 9bfb8df3..eeeff9af 100644 --- a/Ink Canvas/Windows/SettingsViews/Pages/InkRecognitionPage.xaml.cs +++ b/Ink Canvas/Windows/SettingsViews/Pages/InkRecognitionPage.xaml.cs @@ -52,6 +52,8 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages ToggleSwitchAutoStraightenLine.IsOn = settings.Canvas.AutoStraightenLine; AutoStraightenLineThresholdSlider.Value = settings.Canvas.AutoStraightenLineThreshold; ToggleSwitchHighPrecisionLineStraighten.IsOn = settings.Canvas.HighPrecisionLineStraighten; + ToggleSwitchPauseStraightenLine.IsOn = settings.Canvas.PauseStraightenLine; + PauseStraightenDelaySlider.Value = settings.Canvas.PauseStraightenDelay; ToggleSwitchLineEndpointSnapping.IsOn = settings.Canvas.LineEndpointSnapping; } } @@ -192,6 +194,20 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages SettingsManager.SaveSettingsToFile(); } + private void ToggleSwitchPauseStraightenLine_Toggled(object sender, RoutedEventArgs e) + { + if (!_isLoaded) return; + SettingsManager.Settings.Canvas.PauseStraightenLine = ToggleSwitchPauseStraightenLine.IsOn; + SettingsManager.SaveSettingsToFile(); + } + + private void PauseStraightenDelaySlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) + { + if (!_isLoaded) return; + SettingsManager.Settings.Canvas.PauseStraightenDelay = (int)PauseStraightenDelaySlider.Value; + SettingsManager.SaveSettingsToFile(); + } + private void ToggleSwitchLineEndpointSnapping_Toggled(object sender, RoutedEventArgs e) { if (!_isLoaded) return; diff --git a/Ink Canvas/Windows/SettingsViews/Pages/ToolbarPage.xaml b/Ink Canvas/Windows/SettingsViews/Pages/ToolbarPage.xaml new file mode 100644 index 00000000..821ab6c4 --- /dev/null +++ b/Ink Canvas/Windows/SettingsViews/Pages/ToolbarPage.xaml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +