+1
-3
@@ -9,9 +9,7 @@
|
|||||||
>
|
>
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<Style TargetType="ui:ScrollViewerEx">
|
<FontFamily x:Key="HarmonyOSFont">./Resources/Fonts/#HarmonyOS Sans SC</FontFamily>
|
||||||
<EventSetter Event="PreviewMouseWheel" Handler="ScrollViewer_PreviewMouseWheel"/>
|
|
||||||
</Style>
|
|
||||||
<ContextMenu Opened="SysTrayMenu_Opened" Closed="SysTrayMenu_Closed" x:Shared="false" x:Key="SysTrayMenu" Padding="6" ui:ThemeManager.RequestedTheme="Light">
|
<ContextMenu Opened="SysTrayMenu_Opened" Closed="SysTrayMenu_Closed" x:Shared="false" x:Key="SysTrayMenu" Padding="6" ui:ThemeManager.RequestedTheme="Light">
|
||||||
<MenuItem IsCheckable="True" IsChecked="False" Checked="HideICCMainWindowTrayIconMenuItem_Checked" Unchecked="HideICCMainWindowTrayIconMenuItem_UnChecked" Name="HideICCMainWindowTrayIconMenuItem">
|
<MenuItem IsCheckable="True" IsChecked="False" Checked="HideICCMainWindowTrayIconMenuItem_Checked" Unchecked="HideICCMainWindowTrayIconMenuItem_UnChecked" Name="HideICCMainWindowTrayIconMenuItem">
|
||||||
<MenuItem.Header>
|
<MenuItem.Header>
|
||||||
|
|||||||
@@ -2,28 +2,24 @@ using System.Windows;
|
|||||||
|
|
||||||
namespace Ink_Canvas.Controls.Toolbar
|
namespace Ink_Canvas.Controls.Toolbar
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 一个工具栏按钮(或任意浮动栏/白板栏条目)的插件化契约。
|
|
||||||
/// 实现类必须有无参构造函数,启动时会被 ToolbarRegistry 反射实例化。
|
|
||||||
/// </summary>
|
|
||||||
public interface IToolbarItem
|
public interface IToolbarItem
|
||||||
{
|
{
|
||||||
/// <summary>稳定、唯一的 id,用于持久化用户配置。不要随便改。</summary>
|
|
||||||
string Id { get; }
|
string Id { get; }
|
||||||
|
|
||||||
ToolbarSlot DefaultSlot { get; }
|
ToolbarSlot DefaultSlot { get; }
|
||||||
|
|
||||||
/// <summary>同一 slot 内的默认顺序,小的在前。</summary>
|
|
||||||
int DefaultOrder { get; }
|
int DefaultOrder { get; }
|
||||||
|
|
||||||
bool DefaultVisible { get; }
|
bool DefaultVisible { get; }
|
||||||
|
|
||||||
ToolbarInsertPosition DefaultPosition { get; }
|
ToolbarInsertPosition DefaultPosition { get; }
|
||||||
|
|
||||||
/// <summary>仅当 Position 为 BeforeAnchor/AfterAnchor 时有意义,对应 XAML 里 x:Name。</summary>
|
|
||||||
string DefaultAnchorName { get; }
|
string DefaultAnchorName { get; }
|
||||||
|
|
||||||
/// <summary>构造 UI 元素并接线所有行为。</summary>
|
string DisplayName { get; }
|
||||||
|
|
||||||
|
string MenuPanelName { get; }
|
||||||
|
|
||||||
FrameworkElement BuildView(IToolbarHost host);
|
FrameworkElement BuildView(IToolbarHost host);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@ namespace Ink_Canvas.Controls.Toolbar.Items
|
|||||||
public override string LocalizationKey => "FloatingBar_AreaEraser";
|
public override string LocalizationKey => "FloatingBar_AreaEraser";
|
||||||
public override ToolbarSlot DefaultSlot => ToolbarSlot.FloatingBarCanvasControls;
|
public override ToolbarSlot DefaultSlot => ToolbarSlot.FloatingBarCanvasControls;
|
||||||
public override int DefaultOrder => 100;
|
public override int DefaultOrder => 100;
|
||||||
|
public override string MenuPanelName => "EraserSizePanel";
|
||||||
|
|
||||||
protected override void OnClick(IToolbarHost host, object sender, MouseButtonEventArgs e)
|
protected override void OnClick(IToolbarHost host, object sender, MouseButtonEventArgs e)
|
||||||
=> host.Window.EraserIcon_Click(sender, e);
|
=> host.Window.EraserIcon_Click(sender, e);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace Ink_Canvas.Controls.Toolbar.Items
|
|||||||
public override string LocalizationKey => "FloatingBar_Annotate";
|
public override string LocalizationKey => "FloatingBar_Annotate";
|
||||||
public override ToolbarSlot DefaultSlot => ToolbarSlot.FloatingBarMain;
|
public override ToolbarSlot DefaultSlot => ToolbarSlot.FloatingBarMain;
|
||||||
public override int DefaultOrder => 110;
|
public override int DefaultOrder => 110;
|
||||||
|
public override string MenuPanelName => "PenPalette";
|
||||||
|
|
||||||
protected override void OnClick(IToolbarHost host, object sender, MouseButtonEventArgs e)
|
protected override void OnClick(IToolbarHost host, object sender, MouseButtonEventArgs e)
|
||||||
=> host.Window.PenIcon_Click(sender, e);
|
=> host.Window.PenIcon_Click(sender, e);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace Ink_Canvas.Controls.Toolbar.Items
|
|||||||
public override string LocalizationKey => "FloatingBar_Geometry";
|
public override string LocalizationKey => "FloatingBar_Geometry";
|
||||||
public override ToolbarSlot DefaultSlot => ToolbarSlot.FloatingBarCanvasControls;
|
public override ToolbarSlot DefaultSlot => ToolbarSlot.FloatingBarCanvasControls;
|
||||||
public override int DefaultOrder => 130;
|
public override int DefaultOrder => 130;
|
||||||
|
public override string MenuPanelName => "BorderDrawShape";
|
||||||
|
|
||||||
protected override void OnClick(IToolbarHost host, object sender, MouseButtonEventArgs e)
|
protected override void OnClick(IToolbarHost host, object sender, MouseButtonEventArgs e)
|
||||||
=> host.Window.ImageDrawShape_MouseUp(sender, e);
|
=> host.Window.ImageDrawShape_MouseUp(sender, e);
|
||||||
|
|||||||
@@ -5,10 +5,6 @@ using System.Windows.Media;
|
|||||||
|
|
||||||
namespace Ink_Canvas.Controls.Toolbar.Items
|
namespace Ink_Canvas.Controls.Toolbar.Items
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 通用 ToolbarImageButton 工具栏条目基类——大幅减少每个按钮的样板代码。
|
|
||||||
/// 派生类通常只需给 Id / 本地化键 / Slot / Order / 点击处理 / Attach 回填。
|
|
||||||
/// </summary>
|
|
||||||
internal abstract class ToolbarImageButtonItemBase : IToolbarItem
|
internal abstract class ToolbarImageButtonItemBase : IToolbarItem
|
||||||
{
|
{
|
||||||
public abstract string Id { get; }
|
public abstract string Id { get; }
|
||||||
@@ -19,22 +15,22 @@ namespace Ink_Canvas.Controls.Toolbar.Items
|
|||||||
public virtual ToolbarInsertPosition DefaultPosition => ToolbarInsertPosition.Prepend;
|
public virtual ToolbarInsertPosition DefaultPosition => ToolbarInsertPosition.Prepend;
|
||||||
public virtual string DefaultAnchorName => null;
|
public virtual string DefaultAnchorName => null;
|
||||||
|
|
||||||
/// <summary>DynamicResource 名称,用于 IconBrush。默认为 null(使用控件自带前景色)。</summary>
|
public string DisplayName => Strings.GetString(LocalizationKey) ?? LocalizationKey;
|
||||||
protected virtual string IconBrushResourceKey => null;
|
public virtual string MenuPanelName => null;
|
||||||
|
|
||||||
/// <summary>DynamicResource 名称,用于 LabelBrush(文字颜色)。默认为 null(使用控件自带前景色)。</summary>
|
protected virtual string IconBrushResourceKey => null;
|
||||||
protected virtual string LabelBrushResourceKey => null;
|
protected virtual string LabelBrushResourceKey => null;
|
||||||
|
|
||||||
protected abstract void OnClick(IToolbarHost host, object sender, MouseButtonEventArgs e);
|
protected abstract void OnClick(IToolbarHost host, object sender, MouseButtonEventArgs e);
|
||||||
|
|
||||||
/// <summary>构建后调用,用于回填 MainWindow 的原命名属性(partial 扩展里的 Attach*)。可选。</summary>
|
|
||||||
protected virtual void AfterBuild(IToolbarHost host, ToolbarImageButton view) { }
|
protected virtual void AfterBuild(IToolbarHost host, ToolbarImageButton view) { }
|
||||||
|
|
||||||
public FrameworkElement BuildView(IToolbarHost host)
|
public FrameworkElement BuildView(IToolbarHost host)
|
||||||
{
|
{
|
||||||
var btn = new ToolbarImageButton
|
var btn = new ToolbarImageButton
|
||||||
{
|
{
|
||||||
Label = Strings.GetString(LocalizationKey) ?? LocalizationKey
|
Label = Strings.GetString(LocalizationKey) ?? LocalizationKey,
|
||||||
|
Tag = "ToolbarRegistryInjected"
|
||||||
};
|
};
|
||||||
if (!string.IsNullOrEmpty(IconBrushResourceKey))
|
if (!string.IsNullOrEmpty(IconBrushResourceKey))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ namespace Ink_Canvas.Controls.Toolbar.Items
|
|||||||
public override int DefaultOrder => 110;
|
public override int DefaultOrder => 110;
|
||||||
public override ToolbarInsertPosition DefaultPosition => ToolbarInsertPosition.AfterAnchor;
|
public override ToolbarInsertPosition DefaultPosition => ToolbarInsertPosition.AfterAnchor;
|
||||||
public override string DefaultAnchorName => "FloatingBarEndSeparator";
|
public override string DefaultAnchorName => "FloatingBarEndSeparator";
|
||||||
|
public override string MenuPanelName => "BorderTools";
|
||||||
|
|
||||||
protected override void OnClick(IToolbarHost host, object sender, MouseButtonEventArgs e)
|
protected override void OnClick(IToolbarHost host, object sender, MouseButtonEventArgs e)
|
||||||
=> host.Window.SymbolIconTools_MouseUp(sender, e);
|
=> host.Window.SymbolIconTools_MouseUp(sender, e);
|
||||||
|
|||||||
@@ -8,12 +8,10 @@ using System.Windows.Controls;
|
|||||||
|
|
||||||
namespace Ink_Canvas.Controls.Toolbar
|
namespace Ink_Canvas.Controls.Toolbar
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 扫描当前程序集里的 IToolbarItem 实现,按用户配置(Settings.Toolbar)排序/过滤后注入到目标容器。
|
|
||||||
/// </summary>
|
|
||||||
public static class ToolbarRegistry
|
public static class ToolbarRegistry
|
||||||
{
|
{
|
||||||
private static List<IToolbarItem> _items;
|
private static List<IToolbarItem> _items;
|
||||||
|
internal const string InjectedTag = "ToolbarRegistryInjected";
|
||||||
|
|
||||||
public static IReadOnlyList<IToolbarItem> Discover()
|
public static IReadOnlyList<IToolbarItem> Discover()
|
||||||
{
|
{
|
||||||
@@ -34,13 +32,25 @@ namespace Ink_Canvas.Controls.Toolbar
|
|||||||
})
|
})
|
||||||
.Where(i => i != null)
|
.Where(i => i != null)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
LogHelper.WriteLogToFile($"ToolbarRegistry: Discover 完成, 发现 {_items.Count} 个条目", LogHelper.LogType.Info);
|
||||||
return _items;
|
return _items;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>按 slot 分配工具栏条目到对应容器。调用者负责清空目标容器里要被接管的旧内容。</summary>
|
public static void ClearInjected(Panel container)
|
||||||
|
{
|
||||||
|
if (container == null) return;
|
||||||
|
var toRemove = container.Children.OfType<FrameworkElement>()
|
||||||
|
.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<ToolbarSlot, Panel> slots, ToolbarLayoutSettings layout)
|
public static void Populate(IToolbarHost host, IDictionary<ToolbarSlot, Panel> 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();
|
layout = layout ?? new ToolbarLayoutSettings();
|
||||||
|
|
||||||
var grouped = new Dictionary<ToolbarSlot, List<(IToolbarItem item, ToolbarItemConfig cfg)>>();
|
var grouped = new Dictionary<ToolbarSlot, List<(IToolbarItem item, ToolbarItemConfig cfg)>>();
|
||||||
@@ -65,18 +75,56 @@ namespace Ink_Canvas.Controls.Toolbar
|
|||||||
}
|
}
|
||||||
list.Add((item, cfg));
|
list.Add((item, cfg));
|
||||||
}
|
}
|
||||||
|
LogHelper.WriteLogToFile($"ToolbarRegistry: 分组完成, {grouped.Count} 个 slot 有可见条目", LogHelper.LogType.Info);
|
||||||
|
|
||||||
foreach (var kv in grouped)
|
foreach (var kv in grouped)
|
||||||
{
|
{
|
||||||
if (!slots.TryGetValue(kv.Key, out var container) || container == null) continue;
|
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);
|
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,
|
private static void InjectIntoContainer(IToolbarHost host, Panel container,
|
||||||
List<(IToolbarItem item, ToolbarItemConfig cfg)> entries)
|
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 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 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();
|
var before = entries.Where(e => e.cfg.Position == ToolbarInsertPosition.BeforeAnchor).ToList();
|
||||||
@@ -153,9 +201,9 @@ namespace Ink_Canvas.Controls.Toolbar
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Controls.Primitives;
|
||||||
|
using System.Windows.Interop;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Animation;
|
using System.Windows.Media.Animation;
|
||||||
|
|
||||||
@@ -7,6 +10,133 @@ namespace Ink_Canvas.Helpers
|
|||||||
{
|
{
|
||||||
internal class AnimationsHelper
|
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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 强制刷新 Popup 的实际窗口位置(终极方案)
|
||||||
|
/// 通过 Win32 API 直接操作窗口句柄
|
||||||
|
/// </summary>
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将 Popup 窗口提升到最顶层,确保不被其他控件遮挡
|
||||||
|
/// 采用多重策略确保置顶生效
|
||||||
|
/// </summary>
|
||||||
|
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)
|
private static UIElement ResolveAnimationTarget(UIElement element)
|
||||||
{
|
{
|
||||||
return element;
|
return element;
|
||||||
@@ -264,7 +394,6 @@ namespace Ink_Canvas.Helpers
|
|||||||
|
|
||||||
var sb = new Storyboard();
|
var sb = new Storyboard();
|
||||||
|
|
||||||
// 渐变动画
|
|
||||||
var fadeOutAnimation = new DoubleAnimation
|
var fadeOutAnimation = new DoubleAnimation
|
||||||
{
|
{
|
||||||
From = 1,
|
From = 1,
|
||||||
@@ -283,5 +412,110 @@ namespace Ink_Canvas.Helpers
|
|||||||
sb.Begin((FrameworkElement)element);
|
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); }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -614,6 +614,12 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Resource Include="Resources\Fonts\LXGWWenKaiTC-Regular.ttf" />
|
<Resource Include="Resources\Fonts\LXGWWenKaiTC-Regular.ttf" />
|
||||||
|
<Resource Include="Resources\Fonts\HarmonyOS_SansSC_Black.ttf" />
|
||||||
|
<Resource Include="Resources\Fonts\HarmonyOS_SansSC_Bold.ttf" />
|
||||||
|
<Resource Include="Resources\Fonts\HarmonyOS_SansSC_Light.ttf" />
|
||||||
|
<Resource Include="Resources\Fonts\HarmonyOS_SansSC_Medium.ttf" />
|
||||||
|
<Resource Include="Resources\Fonts\HarmonyOS_SansSC_Regular.ttf" />
|
||||||
|
<Resource Include="Resources\Fonts\HarmonyOS_SansSC_Thin.ttf" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Resource Include="Resources\Icons-png\HiteAnnotation.png" />
|
<Resource Include="Resources\Icons-png\HiteAnnotation.png" />
|
||||||
|
|||||||
+138
-139
@@ -666,10 +666,10 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- 图片选中框(边框 + 四角自由缩放 + 顶部旋转手柄) -->
|
<!-- 图片选择覆盖层(缩放/移动/旋转) -->
|
||||||
<localControls:ImageSelectionOverlay x:Name="ImageSelectionOverlay"
|
<localControls:ImageSelectionOverlay x:Name="ImageSelectionOverlay"
|
||||||
Visibility="Collapsed"
|
Visibility="Collapsed"
|
||||||
Panel.ZIndex="1000" />
|
Panel.ZIndex="1000" />
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
@@ -1489,18 +1489,20 @@
|
|||||||
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
|
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
|
||||||
CornerRadius="6,6,0,0"
|
CornerRadius="6,6,0,0"
|
||||||
Background="#2563eb" Margin="-1,-1,-1,1">
|
Background="#2563eb" Margin="-1,-1,-1,1">
|
||||||
<Canvas Height="24" ClipToBounds="True">
|
<Grid Margin="5">
|
||||||
<TextBlock Text="{i18n:I18n Key=Board_EraserOptions}" Canvas.Left="8" Foreground="White"
|
<Grid.ColumnDefinitions>
|
||||||
Padding="0,5"
|
<ColumnDefinition Width="*" />
|
||||||
FontSize="11" FontWeight="Bold"
|
<ColumnDefinition Width="Auto" />
|
||||||
TextAlignment="Center" />
|
</Grid.ColumnDefinitions>
|
||||||
<Image Margin="100,4,0,0"
|
<TextBlock Text="{i18n:I18n Key=Board_EraserOptions}" Foreground="White"
|
||||||
Source="/Resources/new-icons/close-white.png"
|
VerticalAlignment="Center" Grid.Column="0"
|
||||||
RenderOptions.BitmapScalingMode="HighQuality"
|
FontSize="11" FontWeight="Bold" />
|
||||||
Height="16" Width="16"
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.ChromeCloseContrast}"
|
||||||
MouseDown="Border_MouseDown"
|
Foreground="White" FontSize="12"
|
||||||
MouseUp="CloseBordertools_MouseUp" />
|
VerticalAlignment="Center" Grid.Column="1"
|
||||||
</Canvas>
|
MouseDown="Border_MouseDown"
|
||||||
|
MouseUp="CloseBordertools_MouseUp" />
|
||||||
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
<ikw:SimpleStackPanel Orientation="Horizontal" Margin="0,6,0,0"
|
<ikw:SimpleStackPanel Orientation="Horizontal" Margin="0,6,0,0"
|
||||||
Spacing="2"
|
Spacing="2"
|
||||||
@@ -1934,67 +1936,7 @@
|
|||||||
Label="{i18n:I18n Key=Board_Tools}"
|
Label="{i18n:I18n Key=Board_Tools}"
|
||||||
IconGeometry="F1 M24,24z M0,0z M3.336,1.17001C2.13975,1.17001,1.17,2.13976,1.17,3.33601L1.17,8.75101C1.17,9.94726,2.13975,10.917,3.336,10.917L8.751,10.917C9.94725,10.917,10.917,9.94726,10.917,8.75101L10.917,3.33601C10.917,2.13976,9.94725,1.17001,8.751,1.17001L3.336,1.17001z M15.249,1.17001C14.0527,1.17001,13.083,2.13976,13.083,3.33601L13.083,8.75101C13.083,9.94726,14.0527,10.917,15.249,10.917L20.664,10.917C21.8602,10.917,22.83,9.94726,22.83,8.75101L22.83,3.33601C22.83,2.13976,21.8602,1.17001,20.664,1.17001L15.249,1.17001z M3.336,13.083C2.13975,13.083,1.17,14.0528,1.17,15.249L1.17,20.664C1.17,21.8603,2.13975,22.83,3.336,22.83L8.751,22.83C9.94725,22.83,10.917,21.8603,10.917,20.664L10.917,15.249C10.917,14.0528,9.94725,13.083,8.751,13.083L3.336,13.083z M15.249,13.083C14.0527,13.083,13.083,14.0528,13.083,15.249L13.083,20.664C13.083,21.8603,14.0527,22.83,15.249,22.83L20.664,22.83C21.8602,22.83,22.83,21.8603,22.83,20.664L22.83,15.249C22.83,14.0528,21.8602,13.083,20.664,13.083L15.249,13.083z"
|
IconGeometry="F1 M24,24z M0,0z M3.336,1.17001C2.13975,1.17001,1.17,2.13976,1.17,3.33601L1.17,8.75101C1.17,9.94726,2.13975,10.917,3.336,10.917L8.751,10.917C9.94725,10.917,10.917,9.94726,10.917,8.75101L10.917,3.33601C10.917,2.13976,9.94725,1.17001,8.751,1.17001L3.336,1.17001z M15.249,1.17001C14.0527,1.17001,13.083,2.13976,13.083,3.33601L13.083,8.75101C13.083,9.94726,14.0527,10.917,15.249,10.917L20.664,10.917C21.8602,10.917,22.83,9.94726,22.83,8.75101L22.83,3.33601C22.83,2.13976,21.8602,1.17001,20.664,1.17001L15.249,1.17001z M3.336,13.083C2.13975,13.083,1.17,14.0528,1.17,15.249L1.17,20.664C1.17,21.8603,2.13975,22.83,3.336,22.83L8.751,22.83C9.94725,22.83,10.917,21.8603,10.917,20.664L10.917,15.249C10.917,14.0528,9.94725,13.083,8.751,13.083L3.336,13.083z M15.249,13.083C14.0527,13.083,13.083,14.0528,13.083,15.249L13.083,20.664C13.083,21.8603,14.0527,22.83,15.249,22.83L20.664,22.83C21.8602,22.83,22.83,21.8603,22.83,20.664L22.83,15.249C22.83,14.0528,21.8602,13.083,20.664,13.083L15.249,13.083z"
|
||||||
ButtonMouseUp="SymbolIconTools_MouseUp" />
|
ButtonMouseUp="SymbolIconTools_MouseUp" />
|
||||||
<!--工具菜单-->
|
|
||||||
<Border>
|
|
||||||
<Grid Width="0" Margin="0,0,0,5" RenderTransformOrigin="0,1">
|
|
||||||
<Grid.RenderTransform>
|
|
||||||
<TransformGroup>
|
|
||||||
<ScaleTransform ScaleX="1.5" ScaleY="1.5" />
|
|
||||||
<SkewTransform />
|
|
||||||
<RotateTransform />
|
|
||||||
<TranslateTransform />
|
|
||||||
</TransformGroup>
|
|
||||||
</Grid.RenderTransform>
|
|
||||||
<Border ClipToBounds="True" x:Name="BoardBorderTools"
|
|
||||||
Visibility="Collapsed"
|
|
||||||
Margin="-80,-160,-39,50"
|
|
||||||
CornerRadius="5" Background="{DynamicResource SettingsPageBackground}" Opacity="1"
|
|
||||||
BorderThickness="1"
|
|
||||||
BorderBrush="#2563eb">
|
|
||||||
<ikw:SimpleStackPanel Margin="-1,0,0,0">
|
|
||||||
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
|
|
||||||
CornerRadius="6,6,0,0"
|
|
||||||
Background="#2563eb" Margin="-1,-1,-1,1">
|
|
||||||
<Grid Height="24">
|
|
||||||
<TextBlock Text="{i18n:I18n Key=Tools_MoreFeaturesTitle}" Margin="8,0,0,0" Foreground="White"
|
|
||||||
VerticalAlignment="Center" HorizontalAlignment="Left"
|
|
||||||
FontSize="11" FontWeight="Bold" />
|
|
||||||
<Image Margin="0,0,8,0"
|
|
||||||
Source="/Resources/new-icons/close-white.png"
|
|
||||||
RenderOptions.BitmapScalingMode="HighQuality"
|
|
||||||
Height="16" Width="16"
|
|
||||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
|
||||||
MouseDown="Border_MouseDown"
|
|
||||||
MouseUp="CloseBordertools_MouseUp" />
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
<ikw:SimpleStackPanel Margin="10,3,10,2" Spacing="2">
|
|
||||||
<ikw:SimpleStackPanel.Resources>
|
|
||||||
<Style TargetType="Label" BasedOn="{StaticResource AutoFitToolPopupLabel8}" />
|
|
||||||
</ikw:SimpleStackPanel.Resources>
|
|
||||||
<ikw:SimpleStackPanel Margin="0,0,0,0" Height="40"
|
|
||||||
Orientation="Horizontal">
|
|
||||||
<controls:ToolMenuButton x:Name="BoardTimerToolBtn" ButtonMouseUp="ImageCountdownTimer_MouseUp" Label="{i18n:I18n Key=Tools_Timer}" />
|
|
||||||
<controls:ToolMenuButton x:Name="BoardRandomDrawToolBtn" Visibility="Collapsed" ButtonMouseUp="SymbolIconRand_MouseUp" Label="{i18n:I18n Key=Tools_RandomDraw}" />
|
|
||||||
<controls:ToolMenuButton x:Name="BoardSingleDrawToolBtn" Visibility="Collapsed" ButtonMouseUp="SymbolIconRandOne_MouseUp" Label="{i18n:I18n Key=Tools_SingleDraw}" />
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
<ikw:SimpleStackPanel Margin="0,0,0,0" Height="40"
|
|
||||||
Orientation="Horizontal">
|
|
||||||
<controls:ToolMenuButton x:Name="BoardSaveToolBtn" ButtonMouseDown="Border_MouseDown" ButtonMouseUp="SymbolIconSaveStrokes_MouseUp" Label="{i18n:I18n Key=Tools_Save}" />
|
|
||||||
<controls:ToolMenuButton x:Name="BoardOpenToolBtn" ButtonMouseDown="Border_MouseDown" ButtonMouseUp="SymbolIconOpenStrokes_MouseUp" Label="{i18n:I18n Key=Tools_Open}" />
|
|
||||||
<controls:ToolMenuButton x:Name="BoardReplayToolBtn" ButtonMouseUp="GridInkReplayButton_MouseUp" Label="{i18n:I18n Key=Tools_Replay}" />
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
<ikw:SimpleStackPanel Margin="0,0,0,0" Height="40"
|
|
||||||
Orientation="Horizontal">
|
|
||||||
<controls:ToolMenuButton x:Name="BoardScreenshotToolBtn" ButtonMouseUp="SymbolIconScreenshot_MouseUp" Label="{i18n:I18n Key=Tools_Screenshot}" />
|
|
||||||
<controls:ToolMenuButton x:Name="BoardManualToolBtn" ButtonMouseUp="OperatingGuideWindowIcon_MouseUp" Label="{i18n:I18n Key=Tools_Manual}" />
|
|
||||||
<controls:ToolMenuButton x:Name="BoardSettingsToolBtn" ButtonMouseUp="SymbolIconSettings_Click" Label="{i18n:I18n Key=Tools_Settings}" />
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
<controls:BoardToolbarButton x:Name="BoardExit"
|
<controls:BoardToolbarButton x:Name="BoardExit"
|
||||||
Position="Last"
|
Position="Last"
|
||||||
Label="{i18n:I18n Key=Board_Exit}"
|
Label="{i18n:I18n Key=Board_Exit}"
|
||||||
@@ -2006,6 +1948,59 @@
|
|||||||
</ikw:SimpleStackPanel>
|
</ikw:SimpleStackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Viewbox>
|
</Viewbox>
|
||||||
|
<Popup x:Name="BoardBorderToolsPopup"
|
||||||
|
PlacementTarget="{Binding ElementName=BoardTools}"
|
||||||
|
Placement="Custom"
|
||||||
|
AllowsTransparency="True"
|
||||||
|
StaysOpen="True"
|
||||||
|
IsOpen="False">
|
||||||
|
<Border x:Name="BoardBorderTools"
|
||||||
|
CornerRadius="6" Background="{DynamicResource SettingsPageBackground}" Opacity="1"
|
||||||
|
BorderThickness="1"
|
||||||
|
BorderBrush="#2563eb">
|
||||||
|
<ikw:SimpleStackPanel Margin="0,0,0,0">
|
||||||
|
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
|
||||||
|
CornerRadius="4,4,0,0" ClipToBounds="True"
|
||||||
|
Background="#2563eb">
|
||||||
|
<Grid Margin="5">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Text="{i18n:I18n Key=Tools_MoreFeaturesTitle}" Foreground="White"
|
||||||
|
VerticalAlignment="Center" Grid.Column="0"
|
||||||
|
FontSize="11" FontWeight="Bold" />
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.ChromeCloseContrast}"
|
||||||
|
Foreground="White" FontSize="12"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Grid.Column="1"
|
||||||
|
MouseDown="Border_MouseDown"
|
||||||
|
MouseUp="CloseBordertools_MouseUp" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
<ikw:SimpleStackPanel Margin="10,3,10,2" Spacing="2">
|
||||||
|
<ikw:SimpleStackPanel.Resources>
|
||||||
|
<Style TargetType="Label" BasedOn="{StaticResource AutoFitToolPopupLabel8}" />
|
||||||
|
</ikw:SimpleStackPanel.Resources>
|
||||||
|
<ikw:SimpleStackPanel Height="40" Orientation="Horizontal">
|
||||||
|
<controls:ToolMenuButton x:Name="BoardTimerToolBtn" ButtonMouseUp="ImageCountdownTimer_MouseUp" Label="{i18n:I18n Key=Tools_Timer}" />
|
||||||
|
<controls:ToolMenuButton x:Name="BoardRandomDrawToolBtn" Visibility="Collapsed" ButtonMouseUp="SymbolIconRand_MouseUp" Label="{i18n:I18n Key=Tools_RandomDraw}" />
|
||||||
|
<controls:ToolMenuButton x:Name="BoardSingleDrawToolBtn" Visibility="Collapsed" ButtonMouseUp="SymbolIconRandOne_MouseUp" Label="{i18n:I18n Key=Tools_SingleDraw}" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
<ikw:SimpleStackPanel Height="40" Orientation="Horizontal">
|
||||||
|
<controls:ToolMenuButton x:Name="BoardSaveToolBtn" ButtonMouseDown="Border_MouseDown" ButtonMouseUp="SymbolIconSaveStrokes_MouseUp" Label="{i18n:I18n Key=Tools_Save}" />
|
||||||
|
<controls:ToolMenuButton x:Name="BoardOpenToolBtn" ButtonMouseDown="Border_MouseDown" ButtonMouseUp="SymbolIconOpenStrokes_MouseUp" Label="{i18n:I18n Key=Tools_Open}" />
|
||||||
|
<controls:ToolMenuButton x:Name="BoardReplayToolBtn" ButtonMouseUp="GridInkReplayButton_MouseUp" Label="{i18n:I18n Key=Tools_Replay}" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
<ikw:SimpleStackPanel Height="40" Orientation="Horizontal">
|
||||||
|
<controls:ToolMenuButton x:Name="BoardScreenshotToolBtn" ButtonMouseUp="SymbolIconScreenshot_MouseUp" Label="{i18n:I18n Key=Tools_Screenshot}" />
|
||||||
|
<controls:ToolMenuButton x:Name="BoardManualToolBtn" ButtonMouseUp="OperatingGuideWindowIcon_MouseUp" Label="{i18n:I18n Key=Tools_Manual}" />
|
||||||
|
<controls:ToolMenuButton x:Name="BoardSettingsToolBtn" ButtonMouseUp="SymbolIconSettings_Click" Label="{i18n:I18n Key=Tools_Settings}" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</Border>
|
||||||
|
</Popup>
|
||||||
<!-- 右下角 -->
|
<!-- 右下角 -->
|
||||||
<Viewbox x:Name="ViewboxBlackboardRightSide" Margin="3,0,0,0" Height="55"
|
<Viewbox x:Name="ViewboxBlackboardRightSide" Margin="3,0,0,0" Height="55"
|
||||||
HorizontalAlignment="Right" VerticalAlignment="Bottom">
|
HorizontalAlignment="Right" VerticalAlignment="Bottom">
|
||||||
@@ -2368,8 +2363,8 @@
|
|||||||
HorizontalAlignment="Right" VerticalAlignment="Center"/>
|
HorizontalAlignment="Right" VerticalAlignment="Center"/>
|
||||||
|
|
||||||
<Grid Name="FloatingbarUIForInkReplay">
|
<Grid Name="FloatingbarUIForInkReplay">
|
||||||
<Viewbox Name="ViewboxFloatingBar" Margin="100,5,0,0" Cursor="Arrow"
|
<Viewbox Name="ViewboxFloatingBar" Margin="150,250,0,0" Cursor="Arrow"
|
||||||
HorizontalAlignment="Left" Height="58" VerticalAlignment="Top" Width="1200"
|
HorizontalAlignment="Left" Height="58" VerticalAlignment="Top"
|
||||||
UseLayoutRounding="True" SnapsToDevicePixels="True"
|
UseLayoutRounding="True" SnapsToDevicePixels="True"
|
||||||
RenderTransformOrigin="0.5,0.5">
|
RenderTransformOrigin="0.5,0.5">
|
||||||
<Viewbox.LayoutTransform>
|
<Viewbox.LayoutTransform>
|
||||||
@@ -2395,7 +2390,7 @@
|
|||||||
HorizontalAlignment="Left">
|
HorizontalAlignment="Left">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Canvas Name="FloatingbarSelectionBGCanvas" Margin="2,0,2,0">
|
<Canvas Name="FloatingbarSelectionBGCanvas" Margin="2,0,2,0">
|
||||||
<Border Name="FloatingbarSelectionBG" Visibility="Hidden" Width="28" Height="34"
|
<Border Name="FloatingbarSelectionBG" Visibility="Hidden" d:Visibility="Visible" Width="28" Height="34"
|
||||||
Canvas.Left="28" Margin="0,-2,0,-2" Background="#153b82f6">
|
Canvas.Left="28" Margin="0,-2,0,-2" Background="#153b82f6">
|
||||||
<Canvas>
|
<Canvas>
|
||||||
<Border Canvas.Bottom="0" Canvas.Left="8" Width="12" Height="2"
|
<Border Canvas.Bottom="0" Canvas.Left="8" Width="12" Height="2"
|
||||||
@@ -2803,22 +2798,21 @@
|
|||||||
</TextBlock>
|
</TextBlock>
|
||||||
<ui:SymbolIcon Margin="0,-20,8,15" Symbol="Pin" MouseDown="Border_MouseDown" MouseUp="SymbolIconPinBorderDrawShape_MouseUp" Foreground="{DynamicResource FloatBarForeground}" HorizontalAlignment="Right"/>-->
|
<ui:SymbolIcon Margin="0,-20,8,15" Symbol="Pin" MouseDown="Border_MouseDown" MouseUp="SymbolIconPinBorderDrawShape_MouseUp" Foreground="{DynamicResource FloatBarForeground}" HorizontalAlignment="Right"/>-->
|
||||||
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
|
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
|
||||||
CornerRadius="8,8,0,0" Background="#2563eb" Margin="-1,-1,-1,0"
|
CornerRadius="6,6,0,0" Background="#2563eb" Margin="-1,-1,-1,0">
|
||||||
Padding="1,1,1,0">
|
<Grid Margin="5">
|
||||||
<ikw:SimpleStackPanel Orientation="Horizontal"
|
<Grid.ColumnDefinitions>
|
||||||
HorizontalAlignment="Stretch">
|
<ColumnDefinition Width="*" />
|
||||||
<Canvas Height="24" Width="315" ClipToBounds="True">
|
<ColumnDefinition Width="Auto" />
|
||||||
<TextBlock Text="{i18n:I18n Key=Board_Shape}" Canvas.Left="8" Foreground="White"
|
</Grid.ColumnDefinitions>
|
||||||
Padding="0,5" FontSize="11" FontWeight="Bold"
|
<TextBlock Text="{i18n:I18n Key=Board_Shape}" Foreground="White"
|
||||||
TextAlignment="Center" />
|
VerticalAlignment="Center" Grid.Column="0"
|
||||||
<Image Margin="295,3.5,0,0"
|
FontSize="11" FontWeight="Bold" />
|
||||||
Source="/Resources/new-icons/close-white.png"
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.ChromeCloseContrast}"
|
||||||
RenderOptions.BitmapScalingMode="HighQuality"
|
Foreground="White" FontSize="12"
|
||||||
Height="16"
|
VerticalAlignment="Center" Grid.Column="1"
|
||||||
Width="16" MouseDown="Border_MouseDown"
|
MouseDown="Border_MouseDown"
|
||||||
MouseUp="CloseBordertools_MouseUp" />
|
MouseUp="CloseBordertools_MouseUp" />
|
||||||
</Canvas>
|
</Grid>
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<ikw:SimpleStackPanel Orientation="Horizontal" Height="38" Spacing="2"
|
<ikw:SimpleStackPanel Orientation="Horizontal" Height="38" Spacing="2"
|
||||||
@@ -2856,17 +2850,20 @@
|
|||||||
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
|
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
|
||||||
CornerRadius="6,6,0,0"
|
CornerRadius="6,6,0,0"
|
||||||
Background="#2563eb" Margin="-1,-1,-1,1">
|
Background="#2563eb" Margin="-1,-1,-1,1">
|
||||||
<Canvas Height="24" ClipToBounds="True">
|
<Grid Margin="5">
|
||||||
<TextBlock Text="{i18n:I18n Key=Board_EraserOptions}" Canvas.Left="8" Foreground="White"
|
<Grid.ColumnDefinitions>
|
||||||
Padding="0,5"
|
<ColumnDefinition Width="*" />
|
||||||
FontSize="11" FontWeight="Bold" TextAlignment="Center" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<Image Margin="100,4,0,0"
|
</Grid.ColumnDefinitions>
|
||||||
Source="/Resources/new-icons/close-white.png"
|
<TextBlock Text="{i18n:I18n Key=Board_EraserOptions}" Foreground="White"
|
||||||
RenderOptions.BitmapScalingMode="HighQuality"
|
VerticalAlignment="Center" Grid.Column="0"
|
||||||
Height="16"
|
FontSize="11" FontWeight="Bold" />
|
||||||
Width="16" MouseDown="Border_MouseDown"
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.ChromeCloseContrast}"
|
||||||
MouseUp="CloseBordertools_MouseUp" />
|
Foreground="White" FontSize="12"
|
||||||
</Canvas>
|
VerticalAlignment="Center" Grid.Column="1"
|
||||||
|
MouseDown="Border_MouseDown"
|
||||||
|
MouseUp="CloseBordertools_MouseUp" />
|
||||||
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
<ikw:SimpleStackPanel Orientation="Horizontal" Margin="0,8,0,0" Spacing="2"
|
<ikw:SimpleStackPanel Orientation="Horizontal" Margin="0,8,0,0" Spacing="2"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@@ -2982,57 +2979,59 @@
|
|||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
<ikw:SimpleStackPanel x:Name="StackPanelFloatingBarEnd"
|
<ikw:SimpleStackPanel x:Name="StackPanelFloatingBarEnd"
|
||||||
Orientation="{Binding ElementName=StackPanelFloatingBar, Path=Orientation}">
|
Orientation="{Binding ElementName=StackPanelFloatingBar, Path=Orientation}">
|
||||||
<Border x:Name="FloatingBarEndSeparator" Margin="2,0,2,0" BorderBrush="#71717a" BorderThickness="1,0,0,0" />
|
<Border x:Name="FloatingBarEndSeparator" Margin="2,0,2,0" BorderBrush="#71717a" BorderThickness="1,0,0,0" />
|
||||||
<!-- WhiteboardFloatingBarBtn / ToolsFloatingBarBtn / Fold_Icon 由 ToolbarRegistry 注入(AfterAnchor=FloatingBarEndSeparator) -->
|
<!-- WhiteboardFloatingBarBtn / ToolsFloatingBarBtn / Fold_Icon 由 ToolbarRegistry 注入(AfterAnchor=FloatingBarEndSeparator) -->
|
||||||
<Grid Width="0">
|
<Grid Width="0" d:Visibility="Visible">
|
||||||
<Border ClipToBounds="True" Name="BorderTools" Margin="-103,-156,-16,37"
|
</Grid>
|
||||||
CornerRadius="5" Background="{DynamicResource FloatBarBackground}" Opacity="1" BorderThickness="1"
|
</ikw:SimpleStackPanel>
|
||||||
BorderBrush="#2563eb">
|
<Popup x:Name="BorderTools"
|
||||||
<ikw:SimpleStackPanel Margin="-1,0,0,0">
|
Placement="Custom"
|
||||||
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1" Background="#2563eb" Margin="-1,-1,-1,0"
|
AllowsTransparency="True"
|
||||||
Padding="1,1,1,0">
|
StaysOpen="True"
|
||||||
<Grid Margin="6">
|
IsOpen="False">
|
||||||
<Grid.ColumnDefinitions>
|
<Border CornerRadius="8" Background="{DynamicResource ToolsPopupBackground}"
|
||||||
<ColumnDefinition Width="*" />
|
BorderBrush="#3b82f6" BorderThickness="2">
|
||||||
<ColumnDefinition Width="Auto" />
|
<ikw:SimpleStackPanel Margin="-1">
|
||||||
</Grid.ColumnDefinitions>
|
<Grid Margin="8,7,10,5">
|
||||||
<TextBlock Grid.Column="0" Text="{i18n:I18n Key=Tools_MoreFeaturesTitle}"
|
<Grid.ColumnDefinitions>
|
||||||
Foreground="White" FontWeight="Bold"/>
|
<ColumnDefinition Width="*" />
|
||||||
<ui:FontIcon Grid.Column="1" Icon="{x:Static ui:SegoeFluentIcons.ChromeCloseContrast}"
|
<ColumnDefinition Width="Auto" />
|
||||||
Foreground="White" FontSize="12"
|
</Grid.ColumnDefinitions>
|
||||||
MouseDown="Border_MouseDown"
|
<TextBlock Grid.Column="0" VerticalAlignment="Center" FontSize="10"
|
||||||
MouseUp="CloseBordertools_MouseUp" />
|
Text="{i18n:I18n Key=Tools_MoreFeaturesTitle}"
|
||||||
</Grid>
|
Foreground="{DynamicResource ToolsPopupTitleForeground}" FontWeight="Bold"/>
|
||||||
</Border>
|
<ui:FontIcon Grid.Column="1" Icon="{x:Static ui:SegoeFluentIcons.ChromeCloseContrast}"
|
||||||
<!---->
|
Foreground="#DC2626" FontSize="8" VerticalAlignment="Center"
|
||||||
<ikw:SimpleStackPanel Margin="10,3,10,2" Spacing="2">
|
MouseDown="Border_MouseDown"
|
||||||
|
MouseUp="CloseBordertools_MouseUp" />
|
||||||
|
</Grid>
|
||||||
|
<Border Margin="6,0,6,6" BorderBrush="{DynamicResource ToolsPopupInnerBorderBrush}" Background="{DynamicResource ToolsPopupInnerBackground}" BorderThickness="1"
|
||||||
|
CornerRadius="4">
|
||||||
|
<ikw:SimpleStackPanel Margin="2" Spacing="1">
|
||||||
<ikw:SimpleStackPanel.Resources>
|
<ikw:SimpleStackPanel.Resources>
|
||||||
<Style TargetType="Label" BasedOn="{StaticResource AutoFitToolPopupLabel8}" />
|
<Style TargetType="Label" BasedOn="{StaticResource AutoFitToolPopupLabel8}" />
|
||||||
</ikw:SimpleStackPanel.Resources>
|
</ikw:SimpleStackPanel.Resources>
|
||||||
<ikw:SimpleStackPanel Margin="0,0,0,0" Height="40"
|
<ikw:SimpleStackPanel Margin="0,0,0,0" Height="40" Orientation="Horizontal">
|
||||||
Orientation="Horizontal">
|
|
||||||
<controls:ToolMenuButton x:Name="TimerToolBtn" ButtonMouseUp="ImageCountdownTimer_MouseUp" Label="{i18n:I18n Key=Tools_Timer}" />
|
<controls:ToolMenuButton x:Name="TimerToolBtn" ButtonMouseUp="ImageCountdownTimer_MouseUp" Label="{i18n:I18n Key=Tools_Timer}" />
|
||||||
<controls:ToolMenuButton x:Name="RandomDrawToolBtn" ButtonMouseUp="SymbolIconRand_MouseUp" Label="{i18n:I18n Key=Tools_RandomDraw}" />
|
<controls:ToolMenuButton x:Name="RandomDrawToolBtn" ButtonMouseUp="SymbolIconRand_MouseUp" Label="{i18n:I18n Key=Tools_RandomDraw}" />
|
||||||
<controls:ToolMenuButton x:Name="SingleDrawToolBtn" ButtonMouseUp="SymbolIconRandOne_MouseUp" Label="{i18n:I18n Key=Tools_SingleDraw}" />
|
<controls:ToolMenuButton x:Name="SingleDrawToolBtn" ButtonMouseUp="SymbolIconRandOne_MouseUp" Label="{i18n:I18n Key=Tools_SingleDraw}" />
|
||||||
</ikw:SimpleStackPanel>
|
</ikw:SimpleStackPanel>
|
||||||
<ikw:SimpleStackPanel Margin="0,0,0,0" Height="40"
|
<ikw:SimpleStackPanel Margin="0,0,0,0" Height="40" Orientation="Horizontal">
|
||||||
Orientation="Horizontal">
|
|
||||||
<controls:ToolMenuButton x:Name="SaveToolBtn" ButtonMouseDown="Border_MouseDown" ButtonMouseUp="SymbolIconSaveStrokes_MouseUp" Label="{i18n:I18n Key=Tools_Save}" />
|
<controls:ToolMenuButton x:Name="SaveToolBtn" ButtonMouseDown="Border_MouseDown" ButtonMouseUp="SymbolIconSaveStrokes_MouseUp" Label="{i18n:I18n Key=Tools_Save}" />
|
||||||
<controls:ToolMenuButton x:Name="OpenToolBtn" ButtonMouseDown="Border_MouseDown" ButtonMouseUp="SymbolIconOpenStrokes_MouseUp" Label="{i18n:I18n Key=Tools_Open}" />
|
<controls:ToolMenuButton x:Name="OpenToolBtn" ButtonMouseDown="Border_MouseDown" ButtonMouseUp="SymbolIconOpenStrokes_MouseUp" Label="{i18n:I18n Key=Tools_Open}" />
|
||||||
<controls:ToolMenuButton x:Name="ReplayToolBtn" ButtonMouseUp="GridInkReplayButton_MouseUp" Label="{i18n:I18n Key=Tools_Replay}" />
|
<controls:ToolMenuButton x:Name="ReplayToolBtn" ButtonMouseUp="GridInkReplayButton_MouseUp" Label="{i18n:I18n Key=Tools_Replay}" />
|
||||||
</ikw:SimpleStackPanel>
|
</ikw:SimpleStackPanel>
|
||||||
<ikw:SimpleStackPanel Margin="0,0,0,0" Height="40"
|
<ikw:SimpleStackPanel Margin="0,0,0,0" Height="40" Orientation="Horizontal">
|
||||||
Orientation="Horizontal">
|
|
||||||
<controls:ToolMenuButton x:Name="ScreenshotToolBtn" ButtonMouseUp="SymbolIconScreenshot_MouseUp" Label="{i18n:I18n Key=Tools_Screenshot}" />
|
<controls:ToolMenuButton x:Name="ScreenshotToolBtn" ButtonMouseUp="SymbolIconScreenshot_MouseUp" Label="{i18n:I18n Key=Tools_Screenshot}" />
|
||||||
<controls:ToolMenuButton x:Name="ManualToolBtn" ButtonMouseUp="OperatingGuideWindowIcon_MouseUp" Label="{i18n:I18n Key=Tools_Manual}" />
|
<controls:ToolMenuButton x:Name="ManualToolBtn" ButtonMouseUp="OperatingGuideWindowIcon_MouseUp" Label="{i18n:I18n Key=Tools_Manual}" />
|
||||||
<controls:ToolMenuButton x:Name="SettingsToolBtn" ButtonMouseUp="SymbolIconSettings_Click" Label="{i18n:I18n Key=Tools_Settings}" />
|
<controls:ToolMenuButton x:Name="SettingsToolBtn" ButtonMouseUp="SymbolIconSettings_Click" Label="{i18n:I18n Key=Tools_Settings}" />
|
||||||
</ikw:SimpleStackPanel>
|
</ikw:SimpleStackPanel>
|
||||||
</ikw:SimpleStackPanel>
|
</ikw:SimpleStackPanel>
|
||||||
</ikw:SimpleStackPanel>
|
</Border>
|
||||||
</Border>
|
</ikw:SimpleStackPanel>
|
||||||
</Grid>
|
</Border>
|
||||||
</ikw:SimpleStackPanel>
|
</Popup>
|
||||||
</ikw:SimpleStackPanel>
|
</ikw:SimpleStackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|||||||
@@ -100,10 +100,26 @@ namespace Ink_Canvas
|
|||||||
*/
|
*/
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
BoardBorderToolsPopup.CustomPopupPlacementCallback =
|
||||||
|
(popupSize, targetSize, offset) => new[]
|
||||||
|
{
|
||||||
|
new CustomPopupPlacement(
|
||||||
|
new Point((targetSize.Width - popupSize.Width) / 2, -popupSize.Height - 5),
|
||||||
|
PopupPrimaryAxis.Vertical)
|
||||||
|
};
|
||||||
|
|
||||||
|
BorderTools.CustomPopupPlacementCallback =
|
||||||
|
(popupSize, targetSize, offset) => new[]
|
||||||
|
{
|
||||||
|
new CustomPopupPlacement(
|
||||||
|
new Point(targetSize.Width / 2 - popupSize.Width / 2, -popupSize.Height - 8),
|
||||||
|
PopupPrimaryAxis.Vertical)
|
||||||
|
};
|
||||||
|
|
||||||
BlackboardLeftSide.Visibility = Visibility.Collapsed;
|
BlackboardLeftSide.Visibility = Visibility.Collapsed;
|
||||||
BlackboardCenterSide.Visibility = Visibility.Collapsed;
|
BlackboardCenterSide.Visibility = Visibility.Collapsed;
|
||||||
BlackboardRightSide.Visibility = Visibility.Collapsed;
|
BlackboardRightSide.Visibility = Visibility.Collapsed;
|
||||||
BorderTools.Visibility = Visibility.Collapsed;
|
BorderTools.IsOpen = false;
|
||||||
LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
|
LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
|
||||||
RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
|
RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
|
||||||
TwoFingerGestureBorder.Visibility = Visibility.Collapsed;
|
TwoFingerGestureBorder.Visibility = Visibility.Collapsed;
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ namespace Ink_Canvas
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
|
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BorderTools);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BoardBorderToolsPopup);
|
||||||
AnimationsHelper.HideWithSlideAndFade(PenPalette);
|
AnimationsHelper.HideWithSlideAndFade(PenPalette);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
|
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
|
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Controls.Primitives;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using System.Windows.Ink;
|
using System.Windows.Ink;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
@@ -205,6 +206,7 @@ namespace Ink_Canvas
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private Point pointPPT = new Point(-1, -1);
|
private Point pointPPT = new Point(-1, -1);
|
||||||
private DispatcherTimer _floatingBarScreenFollowTimer;
|
private DispatcherTimer _floatingBarScreenFollowTimer;
|
||||||
|
private DispatcherTimer _popupRefreshTimer; // Popup 位置刷新节流定时器
|
||||||
private string _lastFloatingBarScreenDeviceName;
|
private string _lastFloatingBarScreenDeviceName;
|
||||||
private string _lastCanvasScreenDeviceName;
|
private string _lastCanvasScreenDeviceName;
|
||||||
private bool _isRebuildingCanvasForScreen;
|
private bool _isRebuildingCanvasForScreen;
|
||||||
@@ -227,6 +229,95 @@ namespace Ink_Canvas
|
|||||||
pointPPT = new Point(xPos, yPos);
|
pointPPT = new Point(xPos, yPos);
|
||||||
else
|
else
|
||||||
pointDesktop = new Point(xPos, yPos);
|
pointDesktop = new Point(xPos, yPos);
|
||||||
|
|
||||||
|
// 刷新 Popup 菜单位置(带节流)
|
||||||
|
RefreshPopupPositionThrottled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 刷新 Popup 菜单位置(带节流,避免频繁调用)
|
||||||
|
/// </summary>
|
||||||
|
private void RefreshPopupPositionThrottled()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 如果没有打开的 Popup,直接返回
|
||||||
|
if (!BorderTools.IsOpen && !BoardBorderToolsPopup.IsOpen) return;
|
||||||
|
|
||||||
|
// 如果定时器不存在,创建一个(16ms ≈ 60fps)
|
||||||
|
if (_popupRefreshTimer == null)
|
||||||
|
{
|
||||||
|
_popupRefreshTimer = new DispatcherTimer();
|
||||||
|
_popupRefreshTimer.Interval = TimeSpan.FromMilliseconds(16);
|
||||||
|
_popupRefreshTimer.Tick += (s, e) =>
|
||||||
|
{
|
||||||
|
_popupRefreshTimer?.Stop();
|
||||||
|
RefreshPopupPositionInternal();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重启定时器(如果已经在运行,会重新计时)
|
||||||
|
_popupRefreshTimer.Stop();
|
||||||
|
_popupRefreshTimer.Start();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"RefreshPopupPositionThrottled error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实际执行 Popup 位置刷新(强力版本)
|
||||||
|
/// </summary>
|
||||||
|
private void RefreshPopupPositionInternal()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RefreshPopupForcefully(BorderTools);
|
||||||
|
RefreshPopupForcefully(BoardBorderToolsPopup);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"RefreshPopupPositionInternal error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 强制刷新单个 Popup 的位置
|
||||||
|
/// 使用多种策略确保位置更新生效
|
||||||
|
/// </summary>
|
||||||
|
private void RefreshPopupForcefully(Popup popup)
|
||||||
|
{
|
||||||
|
if (popup == null || !popup.IsOpen || popup.PlacementTarget == null) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 策略1:强制失效测量和布局
|
||||||
|
popup.InvalidateMeasure();
|
||||||
|
popup.InvalidateArrange();
|
||||||
|
|
||||||
|
// 策略2:修改 Offset 触发重新定位
|
||||||
|
var originalHorizontalOffset = popup.HorizontalOffset;
|
||||||
|
var originalVerticalOffset = popup.VerticalOffset;
|
||||||
|
|
||||||
|
popup.HorizontalOffset = originalHorizontalOffset + 0.001;
|
||||||
|
popup.VerticalOffset = originalVerticalOffset + 0.001;
|
||||||
|
|
||||||
|
popup.HorizontalOffset = originalHorizontalOffset;
|
||||||
|
popup.VerticalOffset = originalVerticalOffset;
|
||||||
|
|
||||||
|
// 策略3:强制更新布局
|
||||||
|
popup.UpdateLayout();
|
||||||
|
|
||||||
|
// 策略4:使用 Win32 API 强制刷新窗口位置(终极方案)
|
||||||
|
AnimationsHelper.ForceRefreshPopupPosition(popup);
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[PopupRefresh] Forcefully refreshed popup position");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[PopupRefresh] Error: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,8 +385,8 @@ namespace Ink_Canvas
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void HideSubPanelsImmediately()
|
private void HideSubPanelsImmediately()
|
||||||
{
|
{
|
||||||
BorderTools.Visibility = Visibility.Collapsed;
|
BorderTools.IsOpen = false;
|
||||||
BoardBorderTools.Visibility = Visibility.Collapsed;
|
BoardBorderToolsPopup.IsOpen = false;
|
||||||
PenPalette.Visibility = Visibility.Collapsed;
|
PenPalette.Visibility = Visibility.Collapsed;
|
||||||
BoardPenPalette.Visibility = Visibility.Collapsed;
|
BoardPenPalette.Visibility = Visibility.Collapsed;
|
||||||
BoardEraserSizePanel.Visibility = Visibility.Collapsed;
|
BoardEraserSizePanel.Visibility = Visibility.Collapsed;
|
||||||
@@ -376,8 +467,8 @@ namespace Ink_Canvas
|
|||||||
/// </param>
|
/// </param>
|
||||||
internal async void HideSubPanels(string mode = null, bool autoAlignCenter = false)
|
internal async void HideSubPanels(string mode = null, bool autoAlignCenter = false)
|
||||||
{
|
{
|
||||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BorderTools);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BoardBorderToolsPopup);
|
||||||
AnimationsHelper.HideWithSlideAndFade(PenPalette);
|
AnimationsHelper.HideWithSlideAndFade(PenPalette);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
|
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
||||||
@@ -1099,8 +1190,8 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
LeftUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
|
LeftUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
|
||||||
RightUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
|
RightUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
|
||||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BorderTools);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BoardBorderToolsPopup);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
||||||
|
|
||||||
if (Settings.RandSettings?.UseNewStyleUI == true)
|
if (Settings.RandSettings?.UseNewStyleUI == true)
|
||||||
@@ -1151,8 +1242,8 @@ namespace Ink_Canvas
|
|||||||
/// <param name="e">路由事件参数</param>
|
/// <param name="e">路由事件参数</param>
|
||||||
private void OperatingGuideWindowIcon_MouseUp(object sender, MouseButtonEventArgs e)
|
private void OperatingGuideWindowIcon_MouseUp(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BorderTools);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BoardBorderToolsPopup);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
||||||
|
|
||||||
new OperatingGuideWindow().Show();
|
new OperatingGuideWindow().Show();
|
||||||
@@ -1171,8 +1262,8 @@ namespace Ink_Canvas
|
|||||||
LeftUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
|
LeftUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
|
||||||
RightUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
|
RightUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BorderTools);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BoardBorderToolsPopup);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
||||||
|
|
||||||
// 根据设置决定使用哪个点名窗口
|
// 根据设置决定使用哪个点名窗口
|
||||||
@@ -1306,8 +1397,8 @@ namespace Ink_Canvas
|
|||||||
LeftUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
|
LeftUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
|
||||||
RightUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
|
RightUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BorderTools);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BoardBorderToolsPopup);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
||||||
|
|
||||||
// 检查是否启用了外部点名功能
|
// 检查是否启用了外部点名功能
|
||||||
@@ -1385,8 +1476,8 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
//if (lastBorderMouseDownObject != sender) return;
|
//if (lastBorderMouseDownObject != sender) return;
|
||||||
|
|
||||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BorderTools);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BoardBorderToolsPopup);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
||||||
|
|
||||||
CollapseBorderDrawShape();
|
CollapseBorderDrawShape();
|
||||||
@@ -1687,22 +1778,21 @@ namespace Ink_Canvas
|
|||||||
/// <param name="e">鼠标按钮事件参数</param>
|
/// <param name="e">鼠标按钮事件参数</param>
|
||||||
internal void SymbolIconTools_MouseUp(object sender, MouseButtonEventArgs e)
|
internal void SymbolIconTools_MouseUp(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
if (BorderTools.Visibility == Visibility.Visible || BoardBorderTools.Visibility == Visibility.Visible)
|
if (BorderTools.IsOpen || BoardBorderToolsPopup.IsOpen)
|
||||||
{
|
{
|
||||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BorderTools);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BoardBorderToolsPopup);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
HideSubPanels();
|
HideSubPanels();
|
||||||
if (currentMode == 0)
|
if (currentMode == 0)
|
||||||
{
|
{
|
||||||
UpdateBorderToolsPosition();
|
AnimationsHelper.ShowPopupWithSlideAndFade(BorderTools);
|
||||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BorderTools);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardBorderTools);
|
AnimationsHelper.ShowPopupWithSlideAndFade(BoardBorderToolsPopup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,8 +98,8 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
if (lastBorderMouseDownObject != sender || inkCanvas.Visibility != Visibility.Visible) return;
|
if (lastBorderMouseDownObject != sender || inkCanvas.Visibility != Visibility.Visible) return;
|
||||||
|
|
||||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BorderTools);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BoardBorderToolsPopup);
|
||||||
|
|
||||||
GridNotifications.Visibility = Visibility.Collapsed;
|
GridNotifications.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
@@ -916,8 +916,8 @@ namespace Ink_Canvas
|
|||||||
private void SymbolIconOpenStrokes_MouseUp(object sender, MouseButtonEventArgs e)
|
private void SymbolIconOpenStrokes_MouseUp(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
if (lastBorderMouseDownObject != sender) return;
|
if (lastBorderMouseDownObject != sender) return;
|
||||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BorderTools);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
AnimationsHelper.HidePopupWithSlideAndFade(BoardBorderToolsPopup);
|
||||||
|
|
||||||
var openFileDialog = new OpenFileDialog();
|
var openFileDialog = new OpenFileDialog();
|
||||||
openFileDialog.InitialDirectory = Settings.Automation.AutoSavedStrokesLocation;
|
openFileDialog.InitialDirectory = Settings.Automation.AutoSavedStrokesLocation;
|
||||||
|
|||||||
@@ -915,6 +915,8 @@ namespace Ink_Canvas
|
|||||||
Settings.Canvas.DisablePressure = false;
|
Settings.Canvas.DisablePressure = false;
|
||||||
Settings.Canvas.AutoStraightenLine = true;
|
Settings.Canvas.AutoStraightenLine = true;
|
||||||
Settings.Canvas.AutoStraightenLineThreshold = 80;
|
Settings.Canvas.AutoStraightenLineThreshold = 80;
|
||||||
|
Settings.Canvas.PauseStraightenLine = false;
|
||||||
|
Settings.Canvas.PauseStraightenDelay = 300;
|
||||||
Settings.Canvas.LineEndpointSnapping = true;
|
Settings.Canvas.LineEndpointSnapping = true;
|
||||||
Settings.Canvas.LineEndpointSnappingThreshold = 15;
|
Settings.Canvas.LineEndpointSnappingThreshold = 15;
|
||||||
Settings.Canvas.UsingWhiteboard = false;
|
Settings.Canvas.UsingWhiteboard = false;
|
||||||
|
|||||||
@@ -2517,6 +2517,7 @@ namespace Ink_Canvas
|
|||||||
_isMouseRealtimeInking = true;
|
_isMouseRealtimeInking = true;
|
||||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||||
var p = e.GetPosition(inkCanvas);
|
var p = e.GetPosition(inkCanvas);
|
||||||
|
CancelPauseStraightenTimer(MouseRealtimeStrokeId);
|
||||||
InitializeRealtimeBrushTipStateFromPoint(MouseRealtimeStrokeId, p);
|
InitializeRealtimeBrushTipStateFromPoint(MouseRealtimeStrokeId, p);
|
||||||
var sv = GetStrokeVisual(MouseRealtimeStrokeId);
|
var sv = GetStrokeVisual(MouseRealtimeStrokeId);
|
||||||
TryAppendRealtimeVelocityBrushTipPoint(sv, MouseRealtimeStrokeId, p);
|
TryAppendRealtimeVelocityBrushTipPoint(sv, MouseRealtimeStrokeId, p);
|
||||||
@@ -2547,7 +2548,10 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
var sv = GetStrokeVisual(MouseRealtimeStrokeId);
|
var sv = GetStrokeVisual(MouseRealtimeStrokeId);
|
||||||
if (TryAppendRealtimeVelocityBrushTipPoint(sv, MouseRealtimeStrokeId, e.GetPosition(inkCanvas)))
|
if (TryAppendRealtimeVelocityBrushTipPoint(sv, MouseRealtimeStrokeId, e.GetPosition(inkCanvas)))
|
||||||
|
{
|
||||||
sv.ForceRedraw();
|
sv.ForceRedraw();
|
||||||
|
ResetPauseStraightenTimer(MouseRealtimeStrokeId);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_isMouseRealtimeInking = false;
|
_isMouseRealtimeInking = false;
|
||||||
@@ -2612,6 +2616,7 @@ namespace Ink_Canvas
|
|||||||
VisualCanvasList.Remove(MouseRealtimeStrokeId);
|
VisualCanvasList.Remove(MouseRealtimeStrokeId);
|
||||||
TouchDownPointsList.Remove(MouseRealtimeStrokeId);
|
TouchDownPointsList.Remove(MouseRealtimeStrokeId);
|
||||||
CleanupRealtimeBrushTipState(MouseRealtimeStrokeId);
|
CleanupRealtimeBrushTipState(MouseRealtimeStrokeId);
|
||||||
|
CancelPauseStraightenTimer(MouseRealtimeStrokeId);
|
||||||
_isMouseRealtimeInking = false;
|
_isMouseRealtimeInking = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
using System;
|
||||||
using Ink_Canvas.Controls;
|
using Ink_Canvas.Controls;
|
||||||
using Ink_Canvas.Controls.Toolbar;
|
using Ink_Canvas.Controls.Toolbar;
|
||||||
|
using Ink_Canvas.Helpers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
|
||||||
@@ -7,8 +9,6 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
public partial class MainWindow
|
public partial class MainWindow
|
||||||
{
|
{
|
||||||
// 这批属性替代了 XAML 中原有的 x:Name 自动生成字段;外部代码继续按原名访问。
|
|
||||||
// 由对应 Toolbar Item 的 AfterBuild 回填,Populate 发生在 Window_Loaded 早期。
|
|
||||||
internal ToolbarImageButton SymbolIconDelete { get; private set; }
|
internal ToolbarImageButton SymbolIconDelete { get; private set; }
|
||||||
internal ToolbarImageButton Eraser_Icon { get; private set; }
|
internal ToolbarImageButton Eraser_Icon { get; private set; }
|
||||||
internal ToolbarImageButton EraserByStrokes_Icon { get; private set; }
|
internal ToolbarImageButton EraserByStrokes_Icon { get; private set; }
|
||||||
@@ -32,25 +32,53 @@ namespace Ink_Canvas
|
|||||||
internal void AttachSymbolIconRedo(ToolbarImageButton btn) => SymbolIconRedo = btn;
|
internal void AttachSymbolIconRedo(ToolbarImageButton btn) => SymbolIconRedo = btn;
|
||||||
internal void AttachCursorWithDelBtn(ToolbarImageButton btn) => CursorWithDelFloatingBarBtn = btn;
|
internal void AttachCursorWithDelBtn(ToolbarImageButton btn) => CursorWithDelFloatingBarBtn = btn;
|
||||||
internal void AttachWhiteboardBtn(ToolbarImageButton btn) => WhiteboardFloatingBarBtn = btn;
|
internal void AttachWhiteboardBtn(ToolbarImageButton btn) => WhiteboardFloatingBarBtn = btn;
|
||||||
internal void AttachToolsBtn(ToolbarImageButton btn) => ToolsFloatingBarBtn = btn;
|
internal void AttachToolsBtn(ToolbarImageButton btn)
|
||||||
|
{
|
||||||
|
ToolsFloatingBarBtn = btn;
|
||||||
|
BorderTools.PlacementTarget = btn;
|
||||||
|
}
|
||||||
internal void AttachFoldIcon(ToolbarImageButton btn) => Fold_Icon = btn;
|
internal void AttachFoldIcon(ToolbarImageButton btn) => Fold_Icon = btn;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 在 Window_Loaded 早期调用:按 Settings.Toolbar 配置把插件化按钮填充到对应容器。
|
|
||||||
/// 必须在 LoadSettings 之前,因为 LoadSettings 会访问 Cursor_Icon/Pen_Icon/Eraser_Icon 等。
|
|
||||||
/// </summary>
|
|
||||||
internal void InitializeToolbarPlugins()
|
internal void InitializeToolbarPlugins()
|
||||||
{
|
{
|
||||||
ToolbarHost = new ToolbarHost(this);
|
LogHelper.WriteLogToFile("MW_Toolbar: InitializeToolbarPlugins 开始", LogHelper.LogType.Info);
|
||||||
var slots = new Dictionary<ToolbarSlot, Panel>
|
try
|
||||||
{
|
{
|
||||||
{ ToolbarSlot.FloatingBarMain, StackPanelFloatingBar },
|
ToolbarHost = new ToolbarHost(this);
|
||||||
{ ToolbarSlot.FloatingBarCanvasControls, StackPanelCanvasControls },
|
var slots = new Dictionary<ToolbarSlot, Panel>
|
||||||
{ ToolbarSlot.FloatingBarEnd, StackPanelFloatingBarEnd },
|
{
|
||||||
{ ToolbarSlot.BlackboardLeft, BlackboardLeftSide },
|
{ ToolbarSlot.FloatingBarMain, StackPanelFloatingBar },
|
||||||
{ ToolbarSlot.BlackboardRight, BlackboardRightSide }
|
{ ToolbarSlot.FloatingBarCanvasControls, StackPanelCanvasControls },
|
||||||
};
|
{ ToolbarSlot.FloatingBarEnd, StackPanelFloatingBarEnd },
|
||||||
ToolbarRegistry.Populate(ToolbarHost, slots, Settings?.Toolbar);
|
{ ToolbarSlot.BlackboardLeft, BlackboardLeftSide },
|
||||||
|
{ ToolbarSlot.BlackboardRight, BlackboardRightSide }
|
||||||
|
};
|
||||||
|
ToolbarRegistry.Populate(ToolbarHost, slots, Settings?.Toolbar);
|
||||||
|
LogHelper.WriteLogToFile("MW_Toolbar: InitializeToolbarPlugins 完成", LogHelper.LogType.Info);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile($"MW_Toolbar: InitializeToolbarPlugins 异常: {ex.GetType().Name}: {ex.Message}\n{ex.StackTrace}", LogHelper.LogType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void RebuildToolbar()
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile("MW_Toolbar: RebuildToolbar 开始", LogHelper.LogType.Info);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ToolbarRegistry.ClearInjected(StackPanelFloatingBar);
|
||||||
|
ToolbarRegistry.ClearInjected(StackPanelCanvasControls);
|
||||||
|
ToolbarRegistry.ClearInjected(StackPanelFloatingBarEnd);
|
||||||
|
ToolbarRegistry.ClearInjected(BlackboardLeftSide);
|
||||||
|
ToolbarRegistry.ClearInjected(BlackboardRightSide);
|
||||||
|
InitializeToolbarPlugins();
|
||||||
|
LogHelper.WriteLogToFile("MW_Toolbar: RebuildToolbar 完成", LogHelper.LogType.Info);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile($"MW_Toolbar: RebuildToolbar 异常: {ex.GetType().Name}: {ex.Message}\n{ex.StackTrace}", LogHelper.LogType.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Ink_Canvas.Helpers;
|
using Ink_Canvas.Helpers;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
@@ -8,6 +9,7 @@ using System.Windows.Controls;
|
|||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Threading;
|
||||||
using Point = System.Windows.Point;
|
using Point = System.Windows.Point;
|
||||||
|
|
||||||
namespace Ink_Canvas
|
namespace Ink_Canvas
|
||||||
@@ -54,6 +56,9 @@ namespace Ink_Canvas
|
|||||||
private readonly HashSet<int> _activeRealtimeTouchStrokeIds = new HashSet<int>();
|
private readonly HashSet<int> _activeRealtimeTouchStrokeIds = new HashSet<int>();
|
||||||
private readonly HashSet<int> _activeTouchStrokeIds = new HashSet<int>();
|
private readonly HashSet<int> _activeTouchStrokeIds = new HashSet<int>();
|
||||||
|
|
||||||
|
private readonly Dictionary<int, DispatcherTimer> _pauseStraightenTimers = new Dictionary<int, DispatcherTimer>();
|
||||||
|
private const int PauseStraightenDelayMs = 300;
|
||||||
|
|
||||||
private sealed class OneEuroFilter
|
private sealed class OneEuroFilter
|
||||||
{
|
{
|
||||||
private readonly float _minCutoff;
|
private readonly float _minCutoff;
|
||||||
@@ -762,6 +767,9 @@ namespace Ink_Canvas
|
|||||||
|| inkCanvas.EditingMode == InkCanvasEditingMode.Select) return;
|
|| inkCanvas.EditingMode == InkCanvasEditingMode.Select) return;
|
||||||
|
|
||||||
InitializeRealtimeBrushTipState(e.StylusDevice.Id, e);
|
InitializeRealtimeBrushTipState(e.StylusDevice.Id, e);
|
||||||
|
CancelPauseStraightenTimer(e.StylusDevice.Id);
|
||||||
|
_pauseStraightenInkModeStartPos = e.GetPosition(inkCanvas);
|
||||||
|
_pauseStraightenInkModeTracking = true;
|
||||||
TouchDownPointsList[e.StylusDevice.Id] = InkCanvasEditingMode.None;
|
TouchDownPointsList[e.StylusDevice.Id] = InkCanvasEditingMode.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -865,6 +873,9 @@ namespace Ink_Canvas
|
|||||||
VisualCanvasList.Remove(e.StylusDevice.Id);
|
VisualCanvasList.Remove(e.StylusDevice.Id);
|
||||||
TouchDownPointsList.Remove(e.StylusDevice.Id);
|
TouchDownPointsList.Remove(e.StylusDevice.Id);
|
||||||
CleanupRealtimeBrushTipState(e.StylusDevice.Id);
|
CleanupRealtimeBrushTipState(e.StylusDevice.Id);
|
||||||
|
CancelPauseStraightenTimer(e.StylusDevice.Id);
|
||||||
|
CancelPauseStraightenTimer(-200001);
|
||||||
|
_pauseStraightenInkModeTracking = false;
|
||||||
if (StrokeVisualList.Count == 0 || VisualCanvasList.Count == 0 || TouchDownPointsList.Count == 0)
|
if (StrokeVisualList.Count == 0 || VisualCanvasList.Count == 0 || TouchDownPointsList.Count == 0)
|
||||||
{
|
{
|
||||||
// 只清除手写笔预览相关的Canvas,不清除所有子元素
|
// 只清除手写笔预览相关的Canvas,不清除所有子元素
|
||||||
@@ -921,7 +932,14 @@ namespace Ink_Canvas
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetTouchDownPointsList(e.StylusDevice.Id) != InkCanvasEditingMode.None) return;
|
if (GetTouchDownPointsList(e.StylusDevice.Id) != InkCanvasEditingMode.None)
|
||||||
|
{
|
||||||
|
// Regular Ink mode — InkCanvas builds the stroke internally.
|
||||||
|
// Track position for pause-straighten.
|
||||||
|
if (inkCanvas.EditingMode == InkCanvasEditingMode.Ink && drawingShapeMode == 0)
|
||||||
|
ResetPauseStraightenTimerInkMode(e.GetPosition(inkCanvas));
|
||||||
|
return;
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (e.StylusDevice.StylusButtons[1].StylusButtonState == StylusButtonState.Down) return;
|
if (e.StylusDevice.StylusButtons[1].StylusButtonState == StylusButtonState.Down) return;
|
||||||
@@ -938,6 +956,8 @@ namespace Ink_Canvas
|
|||||||
strokeVisual.Add(new StylusPoint(stylusPoint.X, stylusPoint.Y, stylusPoint.PressureFactor));
|
strokeVisual.Add(new StylusPoint(stylusPoint.X, stylusPoint.Y, stylusPoint.PressureFactor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResetPauseStraightenTimer(e.StylusDevice.Id);
|
||||||
|
|
||||||
if (isHandledByRealtime)
|
if (isHandledByRealtime)
|
||||||
strokeVisual.ForceRedraw();
|
strokeVisual.ForceRedraw();
|
||||||
else
|
else
|
||||||
@@ -987,6 +1007,126 @@ namespace Ink_Canvas
|
|||||||
return VisualCanvasList.TryGetValue(id, out var visualCanvas) ? visualCanvas : null;
|
return VisualCanvasList.TryGetValue(id, out var visualCanvas) ? visualCanvas : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ResetPauseStraightenTimer(int stylusId)
|
||||||
|
{
|
||||||
|
if (!Settings.Canvas.PauseStraightenLine) return;
|
||||||
|
Debug.WriteLine($"ResetPauseStraightenTimer: id={stylusId}");
|
||||||
|
if (_pauseStraightenTimers.TryGetValue(stylusId, out var existing))
|
||||||
|
{
|
||||||
|
existing.Stop();
|
||||||
|
existing.Interval = TimeSpan.FromMilliseconds(Settings.Canvas.PauseStraightenDelay);
|
||||||
|
existing.Start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(Settings.Canvas.PauseStraightenDelay) };
|
||||||
|
var capturedId = stylusId;
|
||||||
|
timer.Tick += (s, e) =>
|
||||||
|
{
|
||||||
|
timer.Stop();
|
||||||
|
_pauseStraightenTimers.Remove(capturedId);
|
||||||
|
Debug.WriteLine($"PauseStraightenTimer fired: id={capturedId}");
|
||||||
|
TryPauseStraighten(capturedId);
|
||||||
|
};
|
||||||
|
_pauseStraightenTimers[stylusId] = timer;
|
||||||
|
timer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetPauseStraightenTimerInkMode(Point currentPos)
|
||||||
|
{
|
||||||
|
if (!Settings.Canvas.PauseStraightenLine) return;
|
||||||
|
const int inkModeId = -200001;
|
||||||
|
_pauseStraightenInkModeLastPos = currentPos;
|
||||||
|
if (_pauseStraightenTimers.TryGetValue(inkModeId, out var existing))
|
||||||
|
{
|
||||||
|
existing.Stop();
|
||||||
|
existing.Interval = TimeSpan.FromMilliseconds(Settings.Canvas.PauseStraightenDelay);
|
||||||
|
existing.Start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(Settings.Canvas.PauseStraightenDelay) };
|
||||||
|
timer.Tick += (s, e) =>
|
||||||
|
{
|
||||||
|
timer.Stop();
|
||||||
|
_pauseStraightenTimers.Remove(inkModeId);
|
||||||
|
TryPauseStraightenInkMode();
|
||||||
|
};
|
||||||
|
_pauseStraightenTimers[inkModeId] = timer;
|
||||||
|
timer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Point _pauseStraightenInkModeLastPos;
|
||||||
|
private Point _pauseStraightenInkModeStartPos;
|
||||||
|
private bool _pauseStraightenInkModeTracking;
|
||||||
|
|
||||||
|
private void TryPauseStraightenInkMode()
|
||||||
|
{
|
||||||
|
if (!Settings.Canvas.PauseStraightenLine) return;
|
||||||
|
if (!_pauseStraightenInkModeTracking) return;
|
||||||
|
if (inkCanvas.EditingMode != InkCanvasEditingMode.Ink) return;
|
||||||
|
if (drawingShapeMode != 0) return;
|
||||||
|
|
||||||
|
var start = _pauseStraightenInkModeStartPos;
|
||||||
|
var end = _pauseStraightenInkModeLastPos;
|
||||||
|
double lineLength = GetDistance(start, end);
|
||||||
|
if (lineLength < 2) return;
|
||||||
|
|
||||||
|
// Commit current stroke by briefly switching mode
|
||||||
|
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||||
|
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||||
|
|
||||||
|
// The just-committed stroke should now be last in inkCanvas.Strokes
|
||||||
|
if (inkCanvas.Strokes.Count == 0) return;
|
||||||
|
var stroke = inkCanvas.Strokes[inkCanvas.Strokes.Count - 1];
|
||||||
|
if (stroke.StylusPoints.Count < 2) return;
|
||||||
|
|
||||||
|
var newPoints = new StylusPointCollection();
|
||||||
|
newPoints.Add(new StylusPoint(start.X, start.Y, 0.5f));
|
||||||
|
if (lineLength > 100)
|
||||||
|
{
|
||||||
|
newPoints.Add(new StylusPoint(start.X + (end.X - start.X) / 3.0, start.Y + (end.Y - start.Y) / 3.0, 0.5f));
|
||||||
|
newPoints.Add(new StylusPoint(start.X + (end.X - start.X) * 2.0 / 3.0, start.Y + (end.Y - start.Y) * 2.0 / 3.0, 0.5f));
|
||||||
|
}
|
||||||
|
newPoints.Add(new StylusPoint(end.X, end.Y, 0.5f));
|
||||||
|
stroke.StylusPoints = newPoints;
|
||||||
|
|
||||||
|
_pauseStraightenInkModeTracking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CancelPauseStraightenTimer(int stylusId)
|
||||||
|
{
|
||||||
|
if (_pauseStraightenTimers.TryGetValue(stylusId, out var timer))
|
||||||
|
{
|
||||||
|
timer.Stop();
|
||||||
|
_pauseStraightenTimers.Remove(stylusId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryPauseStraighten(int stylusId)
|
||||||
|
{
|
||||||
|
if (!Settings.Canvas.PauseStraightenLine) { Debug.WriteLine("PauseStraighten: disabled"); return; }
|
||||||
|
var strokeVisual = StrokeVisualList.TryGetValue(stylusId, out var sv) ? sv : null;
|
||||||
|
if (strokeVisual?.Stroke == null) { Debug.WriteLine($"PauseStraighten: no stroke for id={stylusId}"); return; }
|
||||||
|
var stroke = strokeVisual.Stroke;
|
||||||
|
Debug.WriteLine($"PauseStraighten: points={stroke.StylusPoints.Count}");
|
||||||
|
if (stroke.StylusPoints.Count < 2) return;
|
||||||
|
|
||||||
|
var start = stroke.StylusPoints[0].ToPoint();
|
||||||
|
var end = stroke.StylusPoints[stroke.StylusPoints.Count - 1].ToPoint();
|
||||||
|
double lineLength = GetDistance(start, end);
|
||||||
|
Debug.WriteLine($"PauseStraighten: length={lineLength:F1}, STRAIGHTENING!");
|
||||||
|
|
||||||
|
var newPoints = new StylusPointCollection();
|
||||||
|
newPoints.Add(new StylusPoint(start.X, start.Y, 0.5f));
|
||||||
|
if (lineLength > 100)
|
||||||
|
{
|
||||||
|
newPoints.Add(new StylusPoint(start.X + (end.X - start.X) / 3.0, start.Y + (end.Y - start.Y) / 3.0, 0.5f));
|
||||||
|
newPoints.Add(new StylusPoint(start.X + (end.X - start.X) * 2.0 / 3.0, start.Y + (end.Y - start.Y) * 2.0 / 3.0, 0.5f));
|
||||||
|
}
|
||||||
|
newPoints.Add(new StylusPoint(end.X, end.Y, 0.5f));
|
||||||
|
stroke.StylusPoints = newPoints;
|
||||||
|
strokeVisual.ForceRedraw();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取触摸按下点的编辑模式方法
|
/// 获取触摸按下点的编辑模式方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1191,6 +1331,7 @@ namespace Ink_Canvas
|
|||||||
var touchId = e.TouchDevice.Id;
|
var touchId = e.TouchDevice.Id;
|
||||||
var p = e.GetTouchPoint(inkCanvas).Position;
|
var p = e.GetTouchPoint(inkCanvas).Position;
|
||||||
_activeRealtimeTouchStrokeIds.Add(touchId);
|
_activeRealtimeTouchStrokeIds.Add(touchId);
|
||||||
|
CancelPauseStraightenTimer(touchId);
|
||||||
InitializeRealtimeBrushTipStateFromPoint(touchId, p);
|
InitializeRealtimeBrushTipStateFromPoint(touchId, p);
|
||||||
var sv = GetStrokeVisual(touchId);
|
var sv = GetStrokeVisual(touchId);
|
||||||
TryAppendRealtimeVelocityBrushTipPoint(sv, touchId, p);
|
TryAppendRealtimeVelocityBrushTipPoint(sv, touchId, p);
|
||||||
@@ -1214,6 +1355,7 @@ namespace Ink_Canvas
|
|||||||
var touchId = e.TouchDevice.Id;
|
var touchId = e.TouchDevice.Id;
|
||||||
var p = e.GetTouchPoint(inkCanvas).Position;
|
var p = e.GetTouchPoint(inkCanvas).Position;
|
||||||
_activeTouchStrokeIds.Add(touchId);
|
_activeTouchStrokeIds.Add(touchId);
|
||||||
|
CancelPauseStraightenTimer(touchId);
|
||||||
var sv = GetStrokeVisual(touchId);
|
var sv = GetStrokeVisual(touchId);
|
||||||
sv.Add(new StylusPoint(p.X, p.Y, 0.5f));
|
sv.Add(new StylusPoint(p.X, p.Y, 0.5f));
|
||||||
sv.Redraw();
|
sv.Redraw();
|
||||||
@@ -1348,6 +1490,7 @@ namespace Ink_Canvas
|
|||||||
var sv = GetStrokeVisual(touchId);
|
var sv = GetStrokeVisual(touchId);
|
||||||
if (TryAppendRealtimeVelocityBrushTipPoint(sv, touchId, p))
|
if (TryAppendRealtimeVelocityBrushTipPoint(sv, touchId, p))
|
||||||
sv.ForceRedraw();
|
sv.ForceRedraw();
|
||||||
|
ResetPauseStraightenTimer(touchId);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -1364,6 +1507,7 @@ namespace Ink_Canvas
|
|||||||
var sv = GetStrokeVisual(touchId);
|
var sv = GetStrokeVisual(touchId);
|
||||||
sv.Add(new StylusPoint(p.X, p.Y, 0.5f));
|
sv.Add(new StylusPoint(p.X, p.Y, 0.5f));
|
||||||
sv.Redraw();
|
sv.Redraw();
|
||||||
|
ResetPauseStraightenTimer(touchId);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -1424,6 +1568,7 @@ namespace Ink_Canvas
|
|||||||
VisualCanvasList.Remove(touchId);
|
VisualCanvasList.Remove(touchId);
|
||||||
TouchDownPointsList.Remove(touchId);
|
TouchDownPointsList.Remove(touchId);
|
||||||
CleanupRealtimeBrushTipState(touchId);
|
CleanupRealtimeBrushTipState(touchId);
|
||||||
|
CancelPauseStraightenTimer(touchId);
|
||||||
_activeRealtimeTouchStrokeIds.Remove(touchId);
|
_activeRealtimeTouchStrokeIds.Remove(touchId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1452,6 +1597,7 @@ namespace Ink_Canvas
|
|||||||
VisualCanvasList.Remove(touchId);
|
VisualCanvasList.Remove(touchId);
|
||||||
TouchDownPointsList.Remove(touchId);
|
TouchDownPointsList.Remove(touchId);
|
||||||
CleanupRealtimeBrushTipState(touchId);
|
CleanupRealtimeBrushTipState(touchId);
|
||||||
|
CancelPauseStraightenTimer(touchId);
|
||||||
_activeTouchStrokeIds.Remove(touchId);
|
_activeTouchStrokeIds.Remove(touchId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -630,6 +630,15 @@
|
|||||||
<data name="InkRecog_HighPrecisionHint" xml:space="preserve">
|
<data name="InkRecog_HighPrecisionHint" xml:space="preserve">
|
||||||
<value>开启后,当绘制的直线超过设定长度阈值时,将自动调整为完美直线。灵敏度范围0.05-2.0,越小要求越严格,越弯曲的线条越不容易被拉直;值越大越容易识别为直线。高精度模式下,每隔10像素取一个计数点,获取更准确的平均值用于判断。</value>
|
<value>开启后,当绘制的直线超过设定长度阈值时,将自动调整为完美直线。灵敏度范围0.05-2.0,越小要求越严格,越弯曲的线条越不容易被拉直;值越大越容易识别为直线。高精度模式下,每隔10像素取一个计数点,获取更准确的平均值用于判断。</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="InkRecog_PauseStraightenLine" xml:space="preserve">
|
||||||
|
<value>停顿拉直</value>
|
||||||
|
</data>
|
||||||
|
<data name="InkRecog_PauseStraightenHint" xml:space="preserve">
|
||||||
|
<value>书写中停顿时,自动将当前笔画拉直为直线(需同时开启直线自动拉直)。</value>
|
||||||
|
</data>
|
||||||
|
<data name="InkRecog_PauseStraightenDelay" xml:space="preserve">
|
||||||
|
<value>停顿触发延迟</value>
|
||||||
|
</data>
|
||||||
<data name="InkRecog_LineEndpointSnapping" xml:space="preserve">
|
<data name="InkRecog_LineEndpointSnapping" xml:space="preserve">
|
||||||
<value>直线端点吸附</value>
|
<value>直线端点吸附</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -108,6 +108,10 @@ namespace Ink_Canvas
|
|||||||
public int AutoStraightenLineThreshold { get; set; } = 80; // 直线自动拉直的长度阈值(像素)
|
public int AutoStraightenLineThreshold { get; set; } = 80; // 直线自动拉直的长度阈值(像素)
|
||||||
[JsonProperty("highPrecisionLineStraighten")]
|
[JsonProperty("highPrecisionLineStraighten")]
|
||||||
public bool HighPrecisionLineStraighten { get; set; } = true; // 是否启用高精度直线拉直
|
public bool HighPrecisionLineStraighten { get; set; } = true; // 是否启用高精度直线拉直
|
||||||
|
[JsonProperty("pauseStraightenLine")]
|
||||||
|
public bool PauseStraightenLine { get; set; } = false; // 是否启用停顿拉直(书写中停顿时自动拉直笔画)
|
||||||
|
[JsonProperty("pauseStraightenDelay")]
|
||||||
|
public int PauseStraightenDelay { get; set; } = 300; // 停顿拉直触发延迟(毫秒)
|
||||||
[JsonProperty("lineEndpointSnapping")]
|
[JsonProperty("lineEndpointSnapping")]
|
||||||
public bool LineEndpointSnapping { get; set; } = true; // 是否启用直线端点吸附
|
public bool LineEndpointSnapping { get; set; } = true; // 是否启用直线端点吸附
|
||||||
[JsonProperty("lineEndpointSnappingThreshold")]
|
[JsonProperty("lineEndpointSnappingThreshold")]
|
||||||
|
|||||||
@@ -28,6 +28,12 @@
|
|||||||
<!-- 白板模式浮动栏颜色 -->
|
<!-- 白板模式浮动栏颜色 -->
|
||||||
<SolidColorBrush x:Key="BoardFloatBarBackground" Color="#2a2a2a" />
|
<SolidColorBrush x:Key="BoardFloatBarBackground" Color="#2a2a2a" />
|
||||||
<SolidColorBrush x:Key="BoardFloatBarBorderBrush" Color="#555555" />
|
<SolidColorBrush x:Key="BoardFloatBarBorderBrush" Color="#555555" />
|
||||||
|
|
||||||
|
<!-- 更多功能弹窗颜色 -->
|
||||||
|
<SolidColorBrush x:Key="ToolsPopupBackground" Color="#1f1f1f" />
|
||||||
|
<SolidColorBrush x:Key="ToolsPopupInnerBackground" Color="#2a2a2a" />
|
||||||
|
<SolidColorBrush x:Key="ToolsPopupInnerBorderBrush" Color="#404040" />
|
||||||
|
<SolidColorBrush x:Key="ToolsPopupTitleForeground" Color="#60a5fa" />
|
||||||
|
|
||||||
<!-- 白板模式按钮选中状态颜色 -->
|
<!-- 白板模式按钮选中状态颜色 -->
|
||||||
<SolidColorBrush x:Key="BoardFloatBarSelectedBackground" Color="#2563eb" />
|
<SolidColorBrush x:Key="BoardFloatBarSelectedBackground" Color="#2563eb" />
|
||||||
|
|||||||
@@ -28,6 +28,12 @@
|
|||||||
<!-- 白板模式浮动栏颜色 -->
|
<!-- 白板模式浮动栏颜色 -->
|
||||||
<SolidColorBrush x:Key="BoardFloatBarBackground" Color="#f4f4f5" />
|
<SolidColorBrush x:Key="BoardFloatBarBackground" Color="#f4f4f5" />
|
||||||
<SolidColorBrush x:Key="BoardFloatBarBorderBrush" Color="#a1a1aa" />
|
<SolidColorBrush x:Key="BoardFloatBarBorderBrush" Color="#a1a1aa" />
|
||||||
|
|
||||||
|
<!-- 更多功能弹窗颜色 -->
|
||||||
|
<SolidColorBrush x:Key="ToolsPopupBackground" Color="#F4F4F5" />
|
||||||
|
<SolidColorBrush x:Key="ToolsPopupInnerBackground" Color="#fafafa" />
|
||||||
|
<SolidColorBrush x:Key="ToolsPopupInnerBorderBrush" Color="#D4D4D8" />
|
||||||
|
<SolidColorBrush x:Key="ToolsPopupTitleForeground" Color="#2563eb" />
|
||||||
|
|
||||||
<!-- 白板模式按钮选中状态颜色 -->
|
<!-- 白板模式按钮选中状态颜色 -->
|
||||||
<SolidColorBrush x:Key="BoardFloatBarSelectedBackground" Color="#2563eb" />
|
<SolidColorBrush x:Key="BoardFloatBarSelectedBackground" Color="#2563eb" />
|
||||||
|
|||||||
@@ -0,0 +1,293 @@
|
|||||||
|
<Window x:Class="Ink_Canvas.Windows.OobePresetWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
|
||||||
|
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="选择预设配置 - InkCanvasForClass CE"
|
||||||
|
Height="560" Width="680"
|
||||||
|
MinHeight="420" MinWidth="520"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
Topmost="True"
|
||||||
|
ui:ThemeManager.IsThemeAware="True"
|
||||||
|
ui:TitleBar.ExtendViewIntoTitleBar="True"
|
||||||
|
ui:WindowHelper.SystemBackdropType="Mica"
|
||||||
|
ui:WindowHelper.UseModernWindowStyle="True"
|
||||||
|
ui:TitleBar.Height="40">
|
||||||
|
|
||||||
|
<Window.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ui:ThemeResources CanBeAccessedAcrossThreads="True">
|
||||||
|
<ui:ThemeResources.ThemeDictionaries />
|
||||||
|
</ui:ThemeResources>
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Window.Resources>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- 标题栏 -->
|
||||||
|
<Border Grid.Row="0"
|
||||||
|
x:Name="AppTitleBar"
|
||||||
|
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=(ui:TitleBar.Height)}"
|
||||||
|
Background="Transparent"
|
||||||
|
IsHitTestVisible="True"
|
||||||
|
Canvas.ZIndex="10">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition Width="137" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<ikw:SimpleStackPanel Grid.Column="0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Spacing="12"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="16,0,0,0">
|
||||||
|
<Image Source="\Resources\icc.ico" Width="18"
|
||||||
|
RenderOptions.BitmapScalingMode="HighQuality" />
|
||||||
|
<TextBlock VerticalAlignment="Center"
|
||||||
|
Text="选择预设配置"
|
||||||
|
TextWrapping="NoWrap" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
<Rectangle Grid.Column="2"
|
||||||
|
Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=(ui:TitleBar.SystemOverlayRightInset)}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Banner Header -->
|
||||||
|
<Border Grid.Row="1"
|
||||||
|
Padding="36,16,36,16"
|
||||||
|
BorderThickness="0,0,0,1"
|
||||||
|
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}">
|
||||||
|
<Border.Background>
|
||||||
|
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
|
||||||
|
<GradientStop Color="#1F3B82F6" Offset="0" />
|
||||||
|
<GradientStop Color="#0A10B981" Offset="1" />
|
||||||
|
</LinearGradientBrush>
|
||||||
|
</Border.Background>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Border Grid.Column="0"
|
||||||
|
Width="44" Height="44"
|
||||||
|
Margin="0,0,14,0"
|
||||||
|
CornerRadius="10"
|
||||||
|
Background="{DynamicResource SystemControlBackgroundChromeMediumBrush}">
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Repair}"
|
||||||
|
FontSize="22"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Border>
|
||||||
|
<ikw:SimpleStackPanel Grid.Column="1" Spacing="2" VerticalAlignment="Center">
|
||||||
|
<TextBlock Style="{DynamicResource TitleTextBlockStyle}"
|
||||||
|
Text="选择预设配置" />
|
||||||
|
<TextBlock Style="{DynamicResource BodyTextBlockStyle}"
|
||||||
|
Opacity="0.85"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="选择一套由开发者调校的推荐配置,一键完成设置。所有选项可在「设置」中随时修改。" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- 预设列表 -->
|
||||||
|
<ScrollViewer Grid.Row="2"
|
||||||
|
VerticalScrollBarVisibility="Auto"
|
||||||
|
HorizontalScrollBarVisibility="Disabled"
|
||||||
|
PanningMode="VerticalFirst"
|
||||||
|
Padding="36,16,36,8">
|
||||||
|
<ikw:SimpleStackPanel Spacing="10">
|
||||||
|
|
||||||
|
<!-- 预设卡片: 课堂标准配置 -->
|
||||||
|
<Border x:Name="CardStandard"
|
||||||
|
CornerRadius="8"
|
||||||
|
Padding="20,16"
|
||||||
|
BorderThickness="2"
|
||||||
|
Cursor="Hand"
|
||||||
|
MouseLeftButtonDown="CardStandard_Click">
|
||||||
|
<Border.Style>
|
||||||
|
<Style TargetType="Border">
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlForegroundBaseLowBrush}" />
|
||||||
|
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}" />
|
||||||
|
</Style>
|
||||||
|
</Border.Style>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Border Grid.Column="0"
|
||||||
|
Width="40" Height="40"
|
||||||
|
CornerRadius="8"
|
||||||
|
Margin="0,0,14,0"
|
||||||
|
Background="{DynamicResource SystemControlBackgroundAccentBrush}">
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Slideshow}"
|
||||||
|
FontSize="20"
|
||||||
|
Foreground="White"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Border>
|
||||||
|
<ikw:SimpleStackPanel Grid.Column="1" Spacing="3" VerticalAlignment="Center">
|
||||||
|
<TextBlock Style="{DynamicResource BodyStrongTextBlockStyle}"
|
||||||
|
Text="课堂标准配置" />
|
||||||
|
<TextBlock Style="{DynamicResource CaptionTextBlockStyle}"
|
||||||
|
Opacity="0.75"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="启用 PPT 联动、自动保存墨迹、托盘图标、双指手势,适合大多数课堂场景。" />
|
||||||
|
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6" Margin="0,4,0,0">
|
||||||
|
<Border CornerRadius="4" Padding="6,2"
|
||||||
|
Background="{DynamicResource SystemControlBackgroundAccentBrush}">
|
||||||
|
<TextBlock Style="{DynamicResource CaptionTextBlockStyle}"
|
||||||
|
Foreground="White" Text="PPT 联动" />
|
||||||
|
</Border>
|
||||||
|
<Border CornerRadius="4" Padding="6,2"
|
||||||
|
Background="{DynamicResource SystemControlBackgroundAccentBrush}">
|
||||||
|
<TextBlock Style="{DynamicResource CaptionTextBlockStyle}"
|
||||||
|
Foreground="White" Text="自动保存" />
|
||||||
|
</Border>
|
||||||
|
<Border CornerRadius="4" Padding="6,2"
|
||||||
|
Background="{DynamicResource SystemControlBackgroundAccentBrush}">
|
||||||
|
<TextBlock Style="{DynamicResource CaptionTextBlockStyle}"
|
||||||
|
Foreground="White" Text="双指手势" />
|
||||||
|
</Border>
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
<ui:FontIcon x:Name="IconStandard"
|
||||||
|
Grid.Column="2"
|
||||||
|
Icon="{x:Static ui:SegoeFluentIcons.RadioBullet}"
|
||||||
|
FontSize="20"
|
||||||
|
Margin="12,0,0,0"
|
||||||
|
Opacity="0"
|
||||||
|
Foreground="{DynamicResource SystemControlForegroundAccentBrush}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- 预设卡片: 简洁轻量配置 -->
|
||||||
|
<Border x:Name="CardLite"
|
||||||
|
CornerRadius="8"
|
||||||
|
Padding="20,16"
|
||||||
|
BorderThickness="2"
|
||||||
|
Cursor="Hand"
|
||||||
|
MouseLeftButtonDown="CardLite_Click">
|
||||||
|
<Border.Style>
|
||||||
|
<Style TargetType="Border">
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlForegroundBaseLowBrush}" />
|
||||||
|
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}" />
|
||||||
|
</Style>
|
||||||
|
</Border.Style>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Border Grid.Column="0"
|
||||||
|
Width="40" Height="40"
|
||||||
|
CornerRadius="8"
|
||||||
|
Margin="0,0,14,0"
|
||||||
|
Background="#FF6366F1">
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Edit}"
|
||||||
|
FontSize="20"
|
||||||
|
Foreground="White"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Border>
|
||||||
|
<ikw:SimpleStackPanel Grid.Column="1" Spacing="3" VerticalAlignment="Center">
|
||||||
|
<TextBlock Style="{DynamicResource BodyStrongTextBlockStyle}"
|
||||||
|
Text="简洁轻量配置" />
|
||||||
|
<TextBlock Style="{DynamicResource CaptionTextBlockStyle}"
|
||||||
|
Opacity="0.75"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="关闭大部分自动化与联动功能,保持最小后台行为,适合简单批注场景。" />
|
||||||
|
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6" Margin="0,4,0,0">
|
||||||
|
<Border CornerRadius="4" Padding="6,2"
|
||||||
|
Background="#FF6366F1">
|
||||||
|
<TextBlock Style="{DynamicResource CaptionTextBlockStyle}"
|
||||||
|
Foreground="White" Text="PPT 联动" />
|
||||||
|
</Border>
|
||||||
|
<Border CornerRadius="4" Padding="6,2"
|
||||||
|
Background="#FF6366F1">
|
||||||
|
<TextBlock Style="{DynamicResource CaptionTextBlockStyle}"
|
||||||
|
Foreground="White" Text="托盘图标" />
|
||||||
|
</Border>
|
||||||
|
<Border CornerRadius="4" Padding="6,2"
|
||||||
|
Background="#FF6366F1">
|
||||||
|
<TextBlock Style="{DynamicResource CaptionTextBlockStyle}"
|
||||||
|
Foreground="White" Text="自动保存" />
|
||||||
|
</Border>
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
<ui:FontIcon x:Name="IconLite"
|
||||||
|
Grid.Column="2"
|
||||||
|
Icon="{x:Static ui:SegoeFluentIcons.RadioBullet}"
|
||||||
|
FontSize="20"
|
||||||
|
Margin="12,0,0,0"
|
||||||
|
Opacity="0"
|
||||||
|
Foreground="{DynamicResource SystemControlForegroundAccentBrush}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<Border Grid.Row="3"
|
||||||
|
Padding="36,12,36,16"
|
||||||
|
BorderThickness="0,1,0,0"
|
||||||
|
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{DynamicResource CaptionTextBlockStyle}"
|
||||||
|
Opacity="0.7"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="选择预设后将直接完成初始设置,无需逐步配置。" />
|
||||||
|
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
x:Name="BtnCancel"
|
||||||
|
MinWidth="96"
|
||||||
|
Height="32"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
Click="BtnCancel_Click">
|
||||||
|
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Back}" FontSize="12" />
|
||||||
|
<TextBlock Text="返回" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button Grid.Column="2"
|
||||||
|
x:Name="BtnApply"
|
||||||
|
MinWidth="140"
|
||||||
|
Height="32"
|
||||||
|
IsEnabled="False"
|
||||||
|
Style="{DynamicResource AccentButtonStyle}"
|
||||||
|
Click="BtnApply_Click">
|
||||||
|
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Accept}" FontSize="12" />
|
||||||
|
<TextBlock Text="应用并开始使用" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 预设定义 ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 课堂标准配置:适合大多数教学场景,启用 PPT 联动、自动保存、手势等。
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 简洁轻量配置:最小化后台行为,适合简单批注场景。
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -194,10 +194,21 @@
|
|||||||
Style="{DynamicResource AccentButtonStyle}"
|
Style="{DynamicResource AccentButtonStyle}"
|
||||||
Click="BtnStartWelcome_Click">
|
Click="BtnStartWelcome_Click">
|
||||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="8">
|
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="8">
|
||||||
<TextBlock Text="开始" />
|
<TextBlock Text="开始逐步配置" />
|
||||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.ChevronRight}" FontSize="12" />
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.ChevronRight}" FontSize="12" />
|
||||||
</ikw:SimpleStackPanel>
|
</ikw:SimpleStackPanel>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<Button x:Name="BtnUsePreset"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
MinWidth="180"
|
||||||
|
Height="36"
|
||||||
|
Click="BtnUsePreset_Click">
|
||||||
|
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="8">
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Repair}" FontSize="12" />
|
||||||
|
<TextBlock Text="使用预设配置" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</Button>
|
||||||
</ikw:SimpleStackPanel>
|
</ikw:SimpleStackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|||||||
@@ -303,6 +303,28 @@ namespace Ink_Canvas.Windows
|
|||||||
NavigateTo(0, direction: 1);
|
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)
|
private void BtnConfirm_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (_currentStep == FinishIndex)
|
if (_currentStep == FinishIndex)
|
||||||
|
|||||||
@@ -157,6 +157,22 @@
|
|||||||
OffContent="{DynamicResource Common_Off}"
|
OffContent="{DynamicResource Common_Off}"
|
||||||
Toggled="ToggleSwitchHighPrecisionLineStraighten_Toggled" />
|
Toggled="ToggleSwitchHighPrecisionLineStraighten_Toggled" />
|
||||||
</ui:SettingsCard>
|
</ui:SettingsCard>
|
||||||
|
<ui:SettingsCard Header="{i18n:I18n Key=InkRecog_PauseStraightenLine}"
|
||||||
|
Description="{i18n:I18n Key=InkRecog_PauseStraightenHint}">
|
||||||
|
<ui:ToggleSwitch x:Name="ToggleSwitchPauseStraightenLine"
|
||||||
|
OnContent="{DynamicResource Common_On}"
|
||||||
|
OffContent="{DynamicResource Common_Off}"
|
||||||
|
Toggled="ToggleSwitchPauseStraightenLine_Toggled" />
|
||||||
|
</ui:SettingsCard>
|
||||||
|
<ui:SettingsCard Header="{i18n:I18n Key=InkRecog_PauseStraightenDelay}">
|
||||||
|
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="8">
|
||||||
|
<TextBlock Text="{Binding Value, ElementName=PauseStraightenDelaySlider, StringFormat={}{0:0} ms}"
|
||||||
|
VerticalAlignment="Center" FontFamily="Consolas"/>
|
||||||
|
<Slider x:Name="PauseStraightenDelaySlider" Width="200" Minimum="100" Maximum="1000"
|
||||||
|
Value="300" TickFrequency="50" IsSnapToTickEnabled="True"
|
||||||
|
ValueChanged="PauseStraightenDelaySlider_ValueChanged" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</ui:SettingsCard>
|
||||||
</ui:SettingsExpander.Items>
|
</ui:SettingsExpander.Items>
|
||||||
</ui:SettingsExpander>
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
|||||||
ToggleSwitchAutoStraightenLine.IsOn = settings.Canvas.AutoStraightenLine;
|
ToggleSwitchAutoStraightenLine.IsOn = settings.Canvas.AutoStraightenLine;
|
||||||
AutoStraightenLineThresholdSlider.Value = settings.Canvas.AutoStraightenLineThreshold;
|
AutoStraightenLineThresholdSlider.Value = settings.Canvas.AutoStraightenLineThreshold;
|
||||||
ToggleSwitchHighPrecisionLineStraighten.IsOn = settings.Canvas.HighPrecisionLineStraighten;
|
ToggleSwitchHighPrecisionLineStraighten.IsOn = settings.Canvas.HighPrecisionLineStraighten;
|
||||||
|
ToggleSwitchPauseStraightenLine.IsOn = settings.Canvas.PauseStraightenLine;
|
||||||
|
PauseStraightenDelaySlider.Value = settings.Canvas.PauseStraightenDelay;
|
||||||
ToggleSwitchLineEndpointSnapping.IsOn = settings.Canvas.LineEndpointSnapping;
|
ToggleSwitchLineEndpointSnapping.IsOn = settings.Canvas.LineEndpointSnapping;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,6 +194,20 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
|||||||
SettingsManager.SaveSettingsToFile();
|
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<double> e)
|
||||||
|
{
|
||||||
|
if (!_isLoaded) return;
|
||||||
|
SettingsManager.Settings.Canvas.PauseStraightenDelay = (int)PauseStraightenDelaySlider.Value;
|
||||||
|
SettingsManager.SaveSettingsToFile();
|
||||||
|
}
|
||||||
|
|
||||||
private void ToggleSwitchLineEndpointSnapping_Toggled(object sender, RoutedEventArgs e)
|
private void ToggleSwitchLineEndpointSnapping_Toggled(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (!_isLoaded) return;
|
if (!_isLoaded) return;
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
<ui:Page x:Class="Ink_Canvas.Windows.SettingsViews.Pages.ToolbarPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:Ink_Canvas.Windows.SettingsViews.Pages"
|
||||||
|
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
|
||||||
|
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
|
||||||
|
xmlns:dd="urn:gong-wpf-dragdrop"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="工具栏">
|
||||||
|
|
||||||
|
<Page.Resources>
|
||||||
|
<Style x:Key="ToolbarItemStyle" TargetType="ListBox">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||||
|
<Setter Property="dd:DragDrop.IsDragSource" Value="True"/>
|
||||||
|
<Setter Property="dd:DragDrop.IsDropTarget" Value="True"/>
|
||||||
|
<Setter Property="dd:DragDrop.UseDefaultEffectDataTemplate" Value="False"/>
|
||||||
|
<Setter Property="dd:DragDrop.SelectDroppedItems" Value="True"/>
|
||||||
|
<Setter Property="dd:DragDrop.DropTargetAdornerBrush" Value="#2563eb"/>
|
||||||
|
<Setter Property="dd:DragDrop.DropHandler" Value="{Binding}"/>
|
||||||
|
<Setter Property="ScrollViewer.PanningMode" Value="VerticalOnly"/>
|
||||||
|
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
|
||||||
|
<Setter Property="dd:DragDrop.EffectMoveAdornerTemplate">
|
||||||
|
<Setter.Value>
|
||||||
|
<DataTemplate>
|
||||||
|
<Border Background="#2563eb" CornerRadius="12" Padding="16,8"
|
||||||
|
Opacity="0.9">
|
||||||
|
<TextBlock Text="移动" Foreground="White"
|
||||||
|
FontSize="13" FontWeight="Medium"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<DataTemplate x:Key="ToolbarItemTemplate">
|
||||||
|
<Grid Height="44" Margin="0,1">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Column="0" Text=""
|
||||||
|
FontFamily="Segoe MDL2 Assets" FontSize="14"
|
||||||
|
VerticalAlignment="Center" Margin="0,0,10,0" Opacity="0.45"/>
|
||||||
|
|
||||||
|
<TextBlock Grid.Column="1" Text="{Binding DisplayName}"
|
||||||
|
FontSize="14" VerticalAlignment="Center"/>
|
||||||
|
|
||||||
|
<CheckBox Grid.Column="2" IsChecked="{Binding IsVisible, Mode=TwoWay}"
|
||||||
|
VerticalAlignment="Center" Margin="12,0,0,0"
|
||||||
|
ToolTip="显示或隐藏此按钮"/>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</Page.Resources>
|
||||||
|
|
||||||
|
<ScrollViewer PanningMode="VerticalFirst">
|
||||||
|
<StackPanel Margin="59,0,59,0" MaxWidth="1000">
|
||||||
|
|
||||||
|
<!-- 主工具栏 -->
|
||||||
|
<TextBlock Text="主工具栏" FontSize="16" FontWeight="Bold" Margin="0,20,0,4"/>
|
||||||
|
<TextBlock Text="拖动调整顺序,开关控制显示。隐藏的按钮不会出现在浮动工具栏中。"
|
||||||
|
Opacity="0.65" FontSize="12" Margin="0,0,0,8"/>
|
||||||
|
<ListBox Name="MainItemsList" Style="{StaticResource ToolbarItemStyle}"
|
||||||
|
ItemTemplate="{StaticResource ToolbarItemTemplate}"
|
||||||
|
ItemsSource="{Binding MainItems}" />
|
||||||
|
|
||||||
|
<!-- 画布控制 -->
|
||||||
|
<TextBlock Text="画布控制" FontSize="16" FontWeight="Bold" Margin="0,24,0,4"/>
|
||||||
|
<TextBlock Text="批注模式下可见的工具按钮排序。"
|
||||||
|
Opacity="0.65" FontSize="12" Margin="0,0,0,8"/>
|
||||||
|
<ListBox Name="CanvasItemsList" Style="{StaticResource ToolbarItemStyle}"
|
||||||
|
ItemTemplate="{StaticResource ToolbarItemTemplate}"
|
||||||
|
ItemsSource="{Binding CanvasItems}" />
|
||||||
|
|
||||||
|
<!-- 尾部按钮 -->
|
||||||
|
<TextBlock Text="尾部按钮" FontSize="16" FontWeight="Bold" Margin="0,24,0,4"/>
|
||||||
|
<TextBlock Text="白板、工具、折叠按钮排序。"
|
||||||
|
Opacity="0.65" FontSize="12" Margin="0,0,0,8"/>
|
||||||
|
<ListBox Name="EndItemsList" Style="{StaticResource ToolbarItemStyle}"
|
||||||
|
ItemTemplate="{StaticResource ToolbarItemTemplate}"
|
||||||
|
ItemsSource="{Binding EndItems}" />
|
||||||
|
|
||||||
|
<Button Content="恢复默认布局" Click="ButtonReset_Click"
|
||||||
|
Padding="20,10" HorizontalAlignment="Left" Margin="0,16,0,0"/>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</ui:Page>
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
using GongSolutions.Wpf.DragDrop;
|
||||||
|
using Ink_Canvas.Controls.Toolbar;
|
||||||
|
using Ink_Canvas.Helpers;
|
||||||
|
using Ink_Canvas.Windows.SettingsViews.Helpers;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using Page = iNKORE.UI.WPF.Modern.Controls.Page;
|
||||||
|
|
||||||
|
namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||||
|
{
|
||||||
|
public partial class ToolbarPage : Page, IDropTarget
|
||||||
|
{
|
||||||
|
public class ToolbarItemViewModel : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
public string Id { get; }
|
||||||
|
public string DisplayName { get; }
|
||||||
|
|
||||||
|
private int _order;
|
||||||
|
public int Order { get => _order; set { _order = value; OnPropertyChanged(nameof(Order)); } }
|
||||||
|
|
||||||
|
private bool _isVisible = true;
|
||||||
|
public bool IsVisible { get => _isVisible; set { _isVisible = value; OnPropertyChanged(nameof(IsVisible)); } }
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
protected void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
||||||
|
|
||||||
|
public ToolbarItemViewModel(string id, string displayName, int order, bool isVisible)
|
||||||
|
{
|
||||||
|
Id = id; DisplayName = displayName; _order = order; _isVisible = isVisible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly string LogTag = "ToolbarPage";
|
||||||
|
private bool _isLoaded;
|
||||||
|
|
||||||
|
public ObservableCollection<ToolbarItemViewModel> MainItems { get; } = new();
|
||||||
|
public ObservableCollection<ToolbarItemViewModel> CanvasItems { get; } = new();
|
||||||
|
public ObservableCollection<ToolbarItemViewModel> EndItems { get; } = new();
|
||||||
|
|
||||||
|
public ToolbarPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = this;
|
||||||
|
Loaded += OnPageLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPageLoaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
try { LoadSettings(); }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile($"{LogTag}: LoadSettings 异常: {ex.GetType().Name}: {ex.Message}\n{ex.StackTrace}", LogHelper.LogType.Error);
|
||||||
|
}
|
||||||
|
_isLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadSettings()
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile($"{LogTag}: LoadSettings 开始", LogHelper.LogType.Info);
|
||||||
|
MainItems.Clear(); CanvasItems.Clear(); EndItems.Clear();
|
||||||
|
|
||||||
|
var layout = SettingsManager.Settings?.Toolbar ?? new ToolbarLayoutSettings();
|
||||||
|
IReadOnlyList<IToolbarItem> discoveredItems;
|
||||||
|
try { discoveredItems = ToolbarRegistry.Discover(); }
|
||||||
|
catch (Exception ex) { LogHelper.WriteLogToFile($"{LogTag}: Discover 失败: {ex.Message}", LogHelper.LogType.Error); return; }
|
||||||
|
|
||||||
|
foreach (var item in discoveredItems)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!layout.Items.TryGetValue(item.Id, out var cfg))
|
||||||
|
{
|
||||||
|
cfg = new ToolbarItemConfig
|
||||||
|
{
|
||||||
|
Visible = item.DefaultVisible,
|
||||||
|
Order = item.DefaultOrder,
|
||||||
|
Slot = item.DefaultSlot,
|
||||||
|
Position = item.DefaultPosition,
|
||||||
|
AnchorName = item.DefaultAnchorName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
string displayName;
|
||||||
|
try { displayName = item.DisplayName ?? item.Id; }
|
||||||
|
catch { displayName = item.Id; }
|
||||||
|
|
||||||
|
var vm = new ToolbarItemViewModel(item.Id, displayName, cfg.Order, cfg.Visible);
|
||||||
|
switch (cfg.Slot)
|
||||||
|
{
|
||||||
|
case ToolbarSlot.FloatingBarMain: MainItems.Add(vm); break;
|
||||||
|
case ToolbarSlot.FloatingBarCanvasControls: CanvasItems.Add(vm); break;
|
||||||
|
case ToolbarSlot.FloatingBarEnd: EndItems.Add(vm); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile($"{LogTag}: 处理条目失败 [{item?.Id}]: {ex.Message}", LogHelper.LogType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReorderCollections();
|
||||||
|
LogHelper.WriteLogToFile($"{LogTag}: LoadSettings 完成 Main={MainItems.Count} Canvas={CanvasItems.Count} End={EndItems.Count}", LogHelper.LogType.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReorderCollections()
|
||||||
|
{
|
||||||
|
SortCollection(MainItems);
|
||||||
|
SortCollection(CanvasItems);
|
||||||
|
SortCollection(EndItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SortCollection(ObservableCollection<ToolbarItemViewModel> collection)
|
||||||
|
{
|
||||||
|
if (collection == null) return;
|
||||||
|
var sorted = collection.OrderBy(x => x.Order).ToList();
|
||||||
|
for (int i = 0; i < sorted.Count; i++)
|
||||||
|
{
|
||||||
|
var oldIndex = collection.IndexOf(sorted[i]);
|
||||||
|
if (oldIndex != -1 && oldIndex != i)
|
||||||
|
collection.Move(oldIndex, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void DragOver(IDropInfo dropInfo)
|
||||||
|
{
|
||||||
|
if (dropInfo.Data is not ToolbarItemViewModel) return;
|
||||||
|
dropInfo.DropTargetAdorner = DropTargetAdorners.Insert;
|
||||||
|
dropInfo.Effects = DragDropEffects.Move;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void Drop(IDropInfo dropInfo)
|
||||||
|
{
|
||||||
|
if (dropInfo.Data is not ToolbarItemViewModel vm) return;
|
||||||
|
if (dropInfo.TargetCollection is not ObservableCollection<ToolbarItemViewModel> target) return;
|
||||||
|
|
||||||
|
var oldIndex = target.IndexOf(vm);
|
||||||
|
var newIndex = oldIndex < dropInfo.UnfilteredInsertIndex ? dropInfo.UnfilteredInsertIndex - 1 : dropInfo.UnfilteredInsertIndex;
|
||||||
|
var finalIndex = Math.Min(newIndex >= target.Count ? target.Count - 1 : newIndex, target.Count);
|
||||||
|
|
||||||
|
if (!target.Contains(vm))
|
||||||
|
{
|
||||||
|
if (dropInfo.DragInfo.SourceCollection is ObservableCollection<ToolbarItemViewModel> source)
|
||||||
|
source.Remove(vm);
|
||||||
|
target.Insert(dropInfo.UnfilteredInsertIndex, vm);
|
||||||
|
}
|
||||||
|
else if (oldIndex != -1 && oldIndex != finalIndex)
|
||||||
|
{
|
||||||
|
target.Move(oldIndex, finalIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateOrdersFromCollection(target);
|
||||||
|
SaveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateOrdersFromCollection(ObservableCollection<ToolbarItemViewModel> collection)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < collection.Count; i++)
|
||||||
|
collection[i].Order = (i + 1) * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveSettings()
|
||||||
|
{
|
||||||
|
if (!_isLoaded) return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var settings = SettingsManager.Settings;
|
||||||
|
if (settings == null) return;
|
||||||
|
if (settings.Toolbar == null) settings.Toolbar = new ToolbarLayoutSettings();
|
||||||
|
var layout = settings.Toolbar;
|
||||||
|
|
||||||
|
foreach (var vm in MainItems.Concat(CanvasItems).Concat(EndItems))
|
||||||
|
{
|
||||||
|
if (!layout.Items.TryGetValue(vm.Id, out var cfg))
|
||||||
|
{
|
||||||
|
var item = ToolbarRegistry.Discover().FirstOrDefault(i => i.Id == vm.Id);
|
||||||
|
cfg = new ToolbarItemConfig
|
||||||
|
{
|
||||||
|
Visible = item?.DefaultVisible ?? true,
|
||||||
|
Order = item?.DefaultOrder ?? 0,
|
||||||
|
Slot = item?.DefaultSlot ?? ToolbarSlot.FloatingBarMain,
|
||||||
|
Position = item?.DefaultPosition ?? ToolbarInsertPosition.Prepend,
|
||||||
|
AnchorName = item?.DefaultAnchorName
|
||||||
|
};
|
||||||
|
layout.Items[vm.Id] = cfg;
|
||||||
|
}
|
||||||
|
cfg.Visible = vm.IsVisible;
|
||||||
|
cfg.Order = vm.Order;
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsManager.SaveSettingsToFile();
|
||||||
|
LogHelper.WriteLogToFile($"{LogTag}: 设置已保存", LogHelper.LogType.Info);
|
||||||
|
|
||||||
|
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
|
||||||
|
mainWindow?.RebuildToolbar();
|
||||||
|
}
|
||||||
|
catch (Exception ex) { LogHelper.WriteLogToFile($"{LogTag}: RebuildToolbar 异常: {ex.Message}", LogHelper.LogType.Error); }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile($"{LogTag}: SaveSettings 异常: {ex.GetType().Name}: {ex.Message}\n{ex.StackTrace}", LogHelper.LogType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonReset_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SettingsManager.Settings?.Toolbar?.Items.Clear();
|
||||||
|
SettingsManager.SaveSettingsToFile();
|
||||||
|
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||||||
|
{
|
||||||
|
try { Application.Current.Windows.OfType<MainWindow>().FirstOrDefault()?.RebuildToolbar(); }
|
||||||
|
catch (Exception ex) { LogHelper.WriteLogToFile($"{LogTag}: Reset Rebuild 异常: {ex.Message}", LogHelper.LogType.Error); }
|
||||||
|
}));
|
||||||
|
LoadSettings();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile($"{LogTag}: ButtonReset 异常: {ex.GetType().Name}: {ex.Message}\n{ex.StackTrace}", LogHelper.LogType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -202,6 +202,15 @@
|
|||||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.KeyboardStandard}"/>
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.KeyboardStandard}"/>
|
||||||
</ui:NavigationViewItem.Icon>
|
</ui:NavigationViewItem.Icon>
|
||||||
</ui:NavigationViewItem>
|
</ui:NavigationViewItem>
|
||||||
|
<ui:NavigationViewItem
|
||||||
|
x:Name="ToolbarPageItem"
|
||||||
|
Content="工具栏"
|
||||||
|
Tag="ToolbarPage"
|
||||||
|
ToolTipService.ToolTip="工具栏按钮排序与可见性">
|
||||||
|
<ui:NavigationViewItem.Icon>
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Rename}"/>
|
||||||
|
</ui:NavigationViewItem.Icon>
|
||||||
|
</ui:NavigationViewItem>
|
||||||
</ui:NavigationViewItem.MenuItems>
|
</ui:NavigationViewItem.MenuItems>
|
||||||
</ui:NavigationViewItem>
|
</ui:NavigationViewItem>
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ namespace Ink_Canvas.Windows.SettingsViews
|
|||||||
{ "WindowPage", typeof(WindowPage) },
|
{ "WindowPage", typeof(WindowPage) },
|
||||||
{ "AppearancePage", typeof(AppearancePage) },
|
{ "AppearancePage", typeof(AppearancePage) },
|
||||||
{ "HotkeyPage", typeof(HotkeyPage) },
|
{ "HotkeyPage", typeof(HotkeyPage) },
|
||||||
|
{ "ToolbarPage", typeof(ToolbarPage) },
|
||||||
{ "UpdatePage", typeof(UpdatePage) },
|
{ "UpdatePage", typeof(UpdatePage) },
|
||||||
{ "ExperimentalPage", typeof(ExperimentalPage) },
|
{ "ExperimentalPage", typeof(ExperimentalPage) },
|
||||||
{ "AdvancedPage", typeof(AdvancedPage) },
|
{ "AdvancedPage", typeof(AdvancedPage) },
|
||||||
@@ -313,8 +314,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
|||||||
|
|
||||||
if (!_pages.TryGetValue(pageTag, out var cachedPage))
|
if (!_pages.TryGetValue(pageTag, out var cachedPage))
|
||||||
{
|
{
|
||||||
|
Ink_Canvas.Helpers.LogHelper.WriteLogToFile($"SettingsWindow: 创建页面实例 {pageTag} ({pageType.Name})", Ink_Canvas.Helpers.LogHelper.LogType.Info);
|
||||||
cachedPage = Activator.CreateInstance(pageType);
|
cachedPage = Activator.CreateInstance(pageType);
|
||||||
_pages.Add(pageTag, cachedPage);
|
_pages.Add(pageTag, cachedPage);
|
||||||
|
Ink_Canvas.Helpers.LogHelper.WriteLogToFile($"SettingsWindow: 页面实例 {pageTag} 创建成功", Ink_Canvas.Helpers.LogHelper.LogType.Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cachedPage is PluginSettingsPage pluginSettingsPage && pluginInfo != null)
|
if (cachedPage is PluginSettingsPage pluginSettingsPage && pluginInfo != null)
|
||||||
@@ -326,6 +329,7 @@ namespace Ink_Canvas.Windows.SettingsViews
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Ink_Canvas.Helpers.LogHelper.WriteLogToFile($"SettingsWindow: 导航到 {pageTag} 异常: {ex.GetType().Name}: {ex.Message}\n{ex.StackTrace}", Ink_Canvas.Helpers.LogHelper.LogType.Error);
|
||||||
MessageBox.Show($"导航到页面时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show($"导航到页面时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|||||||
@@ -6,33 +6,37 @@
|
|||||||
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
|
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="38" d:DesignWidth="32">
|
d:DesignHeight="38" d:DesignWidth="32">
|
||||||
<ikw:SimpleStackPanel x:Name="ButtonPanel"
|
<Border x:Name="ButtonBorder"
|
||||||
MouseDown="ButtonPanel_MouseDown"
|
CornerRadius="5"
|
||||||
MouseLeave="ButtonPanel_MouseLeave"
|
Background="Transparent">
|
||||||
MouseUp="ButtonPanel_MouseUp"
|
<ikw:SimpleStackPanel x:Name="ButtonPanel"
|
||||||
Background="Transparent"
|
MouseDown="ButtonPanel_MouseDown"
|
||||||
Orientation="Vertical"
|
MouseLeave="ButtonPanel_MouseLeave"
|
||||||
HorizontalAlignment="Center"
|
MouseUp="ButtonPanel_MouseUp"
|
||||||
Height="38" Width="32"
|
Background="Transparent"
|
||||||
Margin="0">
|
Orientation="Vertical"
|
||||||
<Image x:Name="ButtonImage"
|
HorizontalAlignment="Center"
|
||||||
Margin="0,4,0,2"
|
Height="38" Width="32"
|
||||||
Height="19" Width="19"
|
Margin="0">
|
||||||
RenderOptions.BitmapScalingMode="HighQuality">
|
<Image x:Name="ButtonImage"
|
||||||
<Image.Source>
|
Margin="0,4,0,2"
|
||||||
<DrawingImage>
|
Height="19" Width="19"
|
||||||
<DrawingImage.Drawing>
|
RenderOptions.BitmapScalingMode="HighQuality">
|
||||||
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
|
<Image.Source>
|
||||||
<GeometryDrawing x:Name="IconGeometryInternal"
|
<DrawingImage>
|
||||||
Brush="{DynamicResource IconForeground}" />
|
<DrawingImage.Drawing>
|
||||||
</DrawingGroup>
|
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
|
||||||
</DrawingImage.Drawing>
|
<GeometryDrawing x:Name="IconGeometryInternal"
|
||||||
</DrawingImage>
|
Brush="{DynamicResource IconForeground}" />
|
||||||
</Image.Source>
|
</DrawingGroup>
|
||||||
</Image>
|
</DrawingImage.Drawing>
|
||||||
<Label x:Name="LabelControl"
|
</DrawingImage>
|
||||||
FontSize="8"
|
</Image.Source>
|
||||||
HorizontalAlignment="Center"
|
</Image>
|
||||||
Foreground="{DynamicResource FloatBarForeground}" />
|
<Label x:Name="LabelControl"
|
||||||
</ikw:SimpleStackPanel>
|
FontSize="8"
|
||||||
</UserControl>
|
HorizontalAlignment="Center"
|
||||||
|
Foreground="{DynamicResource FloatBarForeground}" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
@@ -9,6 +9,7 @@ namespace Ink_Canvas.Controls
|
|||||||
{
|
{
|
||||||
private Geometry _pendingGeometry;
|
private Geometry _pendingGeometry;
|
||||||
private Brush _pendingBrush;
|
private Brush _pendingBrush;
|
||||||
|
private static ToolMenuButton _lastPressedButton;
|
||||||
|
|
||||||
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
|
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
|
||||||
nameof(Label), typeof(string), typeof(ToolMenuButton),
|
nameof(Label), typeof(string), typeof(ToolMenuButton),
|
||||||
@@ -68,8 +69,8 @@ namespace Ink_Canvas.Controls
|
|||||||
|
|
||||||
public new Brush Background
|
public new Brush Background
|
||||||
{
|
{
|
||||||
get => ButtonPanel.Background;
|
get => ButtonBorder.Background;
|
||||||
set => ButtonPanel.Background = value;
|
set => ButtonBorder.Background = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GeometryDrawing Icon
|
public GeometryDrawing Icon
|
||||||
@@ -90,6 +91,7 @@ namespace Ink_Canvas.Controls
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
Loaded += ToolMenuButton_Loaded;
|
Loaded += ToolMenuButton_Loaded;
|
||||||
|
ButtonBorder.Background = Brushes.Transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToolMenuButton_Loaded(object sender, RoutedEventArgs e)
|
private void ToolMenuButton_Loaded(object sender, RoutedEventArgs e)
|
||||||
@@ -105,17 +107,36 @@ namespace Ink_Canvas.Controls
|
|||||||
|
|
||||||
private void ButtonPanel_MouseDown(object sender, MouseButtonEventArgs e)
|
private void ButtonPanel_MouseDown(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (!IsEnabled) return;
|
||||||
|
if (_lastPressedButton != null && _lastPressedButton != this)
|
||||||
|
{
|
||||||
|
_lastPressedButton.ButtonBorder.Background = Brushes.Transparent;
|
||||||
|
}
|
||||||
|
_lastPressedButton = this;
|
||||||
|
ButtonBorder.Background = new SolidColorBrush(Color.FromArgb(80, 24, 24, 27));
|
||||||
ButtonMouseDown?.Invoke(this, e);
|
ButtonMouseDown?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ButtonPanel_MouseLeave(object sender, MouseEventArgs e)
|
private void ButtonPanel_MouseLeave(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (!IsEnabled) return;
|
||||||
|
if (_lastPressedButton == this)
|
||||||
|
{
|
||||||
|
ButtonBorder.Background = Brushes.Transparent;
|
||||||
|
_lastPressedButton = null;
|
||||||
|
}
|
||||||
ButtonMouseLeave?.Invoke(this, e);
|
ButtonMouseLeave?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ButtonPanel_MouseUp(object sender, MouseButtonEventArgs e)
|
private void ButtonPanel_MouseUp(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (!IsEnabled) return;
|
||||||
|
if (_lastPressedButton == this)
|
||||||
|
{
|
||||||
|
ButtonBorder.Background = Brushes.Transparent;
|
||||||
|
_lastPressedButton = null;
|
||||||
|
}
|
||||||
ButtonMouseUp?.Invoke(this, e);
|
ButtonMouseUp?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user