Compare commits

..

1 Commits

Author SHA1 Message Date
doudou0720 ab7905696d refactor(PPT)!: 重构PPT链接管理器,引入基类并优化代码结构
将ROTPPTManager重命名为ROTPPTLinkManager
新增BasePPTLinkManager抽象基类,提取公共逻辑
重构ComPPTLinkManager继承自基类,减少重复代码

Signed-off-by: doudou0720 <98651603+doudou0720@users.noreply.github.com>
2026-04-12 13:40:54 +08:00
25 changed files with 368 additions and 610 deletions
+2 -2
View File
@@ -43,5 +43,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.7.18.10")] [assembly: AssemblyVersion("1.7.18.9")]
[assembly: AssemblyFileVersion("1.7.18.10")] [assembly: AssemblyFileVersion("1.7.18.9")]
+117
View File
@@ -0,0 +1,117 @@
using System;
namespace Ink_Canvas.Helpers
{
public abstract class BasePPTLinkManager : IPPTLinkManager
{
#region IPPTLinkManager
public event Action<object> SlideShowBegin;
public event Action<object> SlideShowNextSlide;
public event Action<object> SlideShowEnd;
public event Action<object> PresentationOpen;
public event Action<object> PresentationClose;
public event Action<bool> PPTConnectionChanged;
public event Action<bool> SlideShowStateChanged;
#endregion
#region IPPTLinkManager
public virtual bool IsConnected => PPTApplication != null;
public virtual bool IsInSlideShow { get; protected set; }
public virtual bool IsSupportWPS { get; set; }
public virtual bool SkipAnimationsWhenNavigating { get; set; }
public virtual int SlidesCount { get; protected set; }
public abstract object PPTApplication { get; protected set; }
#endregion
#region
protected BasePPTLinkManager()
{
}
#endregion
#region
public abstract void StartMonitoring();
public abstract void StopMonitoring();
public virtual void ReloadConnection()
{
LogHelper.WriteLogToFile($"{GetType().Name} 执行热重载:强制断开并重新连接", LogHelper.LogType.Event);
StopMonitoring();
}
#endregion
#region
public abstract bool TryStartSlideShow();
public abstract bool TryEndSlideShow();
#endregion
#region
public abstract bool TryNavigateToSlide(int slideNumber);
public abstract bool TryNavigateNext();
public abstract bool TryNavigatePrevious();
#endregion
#region
public abstract int GetCurrentSlideNumber();
public abstract string GetPresentationName();
public abstract bool TryShowSlideNavigation();
public abstract object GetCurrentActivePresentation();
#endregion
#region
protected virtual void OnSlideShowBegin(object slideShowWindow)
{
SlideShowBegin?.Invoke(slideShowWindow);
}
protected virtual void OnSlideShowNextSlide(object slideShowWindow)
{
SlideShowNextSlide?.Invoke(slideShowWindow);
}
protected virtual void OnSlideShowEnd(object presentation)
{
SlideShowEnd?.Invoke(presentation);
}
protected virtual void OnPresentationOpen(object presentation)
{
PresentationOpen?.Invoke(presentation);
}
protected virtual void OnPresentationClose(object presentation)
{
PresentationClose?.Invoke(presentation);
}
protected virtual void OnPPTConnectionChanged(bool isConnected)
{
PPTConnectionChanged?.Invoke(isConnected);
}
protected virtual void OnSlideShowStateChanged(bool isInSlideShow)
{
SlideShowStateChanged?.Invoke(isInSlideShow);
}
#endregion
#region IDisposable
public virtual void Dispose()
{
StopMonitoring();
}
#endregion
}
}
+32 -50
View File
@@ -2,7 +2,7 @@ using System;
namespace Ink_Canvas.Helpers namespace Ink_Canvas.Helpers
{ {
public class ComPPTLinkManager : IPPTLinkManager public class ComPPTLinkManager : BasePPTLinkManager
{ {
private readonly PPTManager _inner; private readonly PPTManager _inner;
@@ -10,65 +10,47 @@ namespace Ink_Canvas.Helpers
{ {
_inner = new PPTManager(); _inner = new PPTManager();
_inner.SlideShowBegin += wn => SlideShowBegin?.Invoke(wn); _inner.SlideShowBegin += wn => OnSlideShowBegin(wn);
_inner.SlideShowNextSlide += wn => SlideShowNextSlide?.Invoke(wn); _inner.SlideShowNextSlide += wn => OnSlideShowNextSlide(wn);
_inner.SlideShowEnd += pres => SlideShowEnd?.Invoke(pres); _inner.SlideShowEnd += pres => OnSlideShowEnd(pres);
_inner.PresentationOpen += pres => PresentationOpen?.Invoke(pres); _inner.PresentationOpen += pres => OnPresentationOpen(pres);
_inner.PresentationClose += pres => PresentationClose?.Invoke(pres); _inner.PresentationClose += pres => OnPresentationClose(pres);
_inner.PPTConnectionChanged += connected => PPTConnectionChanged?.Invoke(connected); _inner.PPTConnectionChanged += connected => OnPPTConnectionChanged(connected);
_inner.SlideShowStateChanged += inSlideShow => SlideShowStateChanged?.Invoke(inSlideShow); _inner.SlideShowStateChanged += inSlideShow => OnSlideShowStateChanged(inSlideShow);
} }
#region IPPTLinkManager #region BasePPTLinkManager
public event Action<object> SlideShowBegin; public override bool IsConnected => _inner.IsConnected;
public event Action<object> SlideShowNextSlide;
public event Action<object> SlideShowEnd;
public event Action<object> PresentationOpen;
public event Action<object> PresentationClose;
public event Action<bool> PPTConnectionChanged;
public event Action<bool> SlideShowStateChanged;
#endregion
#region IPPTLinkManager public override bool IsInSlideShow => _inner.IsInSlideShow;
public bool IsConnected => _inner.IsConnected;
public bool IsInSlideShow => _inner.IsInSlideShow; public override bool IsSupportWPS
public bool IsSupportWPS
{ {
get => _inner.IsSupportWPS; get => _inner.IsSupportWPS;
set => _inner.IsSupportWPS = value; set => _inner.IsSupportWPS = value;
} }
public bool SkipAnimationsWhenNavigating public override bool SkipAnimationsWhenNavigating
{ {
get => _inner.SkipAnimationsWhenNavigating; get => _inner.SkipAnimationsWhenNavigating;
set => _inner.SkipAnimationsWhenNavigating = value; set => _inner.SkipAnimationsWhenNavigating = value;
} }
public int SlidesCount => _inner.SlidesCount; public override int SlidesCount => _inner.SlidesCount;
public object PPTApplication => _inner.PPTApplication; public override object PPTApplication
{
get => _inner.PPTApplication;
protected set { }
}
#endregion #endregion
#region #region
/// <summary> public override void StartMonitoring() => _inner.StartMonitoring();
/// 开始监控本地 PowerPoint 的连接与运行状态,并在状态变化时触发相应事件。
/// </summary>
public void StartMonitoring() => _inner.StartMonitoring();
/// <summary> public override void StopMonitoring() => _inner.StopMonitoring();
/// 停止对 PowerPoint 的监控,断开当前连接并停止触发相关事件。
/// </summary>
public void StopMonitoring() => _inner.StopMonitoring();
/// <summary> public override void ReloadConnection()
/// 强制断开当前 COM PPT 连接并停止对其监控,同时写入事件日志。
/// </summary>
/// <remarks>
/// 会向日志记录一条事件信息并调用内部管理器停止监控;该方法不会重新启动监控或重新初始化内部管理器实例。
/// </remarks>
public void ReloadConnection()
{ {
LogHelper.WriteLogToFile("COM PPT 执行热重载:强制断开并重新连接", LogHelper.LogType.Event); LogHelper.WriteLogToFile("COM PPT 执行热重载:强制断开并重新连接", LogHelper.LogType.Event);
_inner.StopMonitoring(); _inner.StopMonitoring();
@@ -76,31 +58,31 @@ namespace Ink_Canvas.Helpers
#endregion #endregion
#region #region
public bool TryStartSlideShow() => _inner.TryStartSlideShow(); public override bool TryStartSlideShow() => _inner.TryStartSlideShow();
public bool TryEndSlideShow() => _inner.TryEndSlideShow(); public override bool TryEndSlideShow() => _inner.TryEndSlideShow();
#endregion #endregion
#region #region
public bool TryNavigateToSlide(int slideNumber) => _inner.TryNavigateToSlide(slideNumber); public override bool TryNavigateToSlide(int slideNumber) => _inner.TryNavigateToSlide(slideNumber);
public bool TryNavigateNext() => _inner.TryNavigateNext(); public override bool TryNavigateNext() => _inner.TryNavigateNext();
public bool TryNavigatePrevious() => _inner.TryNavigatePrevious(); public override bool TryNavigatePrevious() => _inner.TryNavigatePrevious();
#endregion #endregion
#region #region
public int GetCurrentSlideNumber() => _inner.GetCurrentSlideNumber(); public override int GetCurrentSlideNumber() => _inner.GetCurrentSlideNumber();
public string GetPresentationName() => _inner.GetPresentationName(); public override string GetPresentationName() => _inner.GetPresentationName();
public bool TryShowSlideNavigation() => _inner.TryShowSlideNavigation(); public override bool TryShowSlideNavigation() => _inner.TryShowSlideNavigation();
public object GetCurrentActivePresentation() => _inner.GetCurrentActivePresentation(); public override object GetCurrentActivePresentation() => _inner.GetCurrentActivePresentation();
#endregion #endregion
#region IDisposable #region IDisposable
public void Dispose() public override void Dispose()
{ {
_inner?.Dispose(); _inner?.Dispose();
} }
-6
View File
@@ -152,9 +152,6 @@ namespace Ink_Canvas.Helpers
var node = legacy.InkDrawingNode; var node = legacy.InkDrawingNode;
var shape = node.GetShape(); var shape = node.GetShape();
if (shape == null)
return InkShapeRecognitionResult.Empty;
var hot = ClonePointCollection(node.HotPoints); var hot = ClonePointCollection(node.HotPoints);
return new InkShapeRecognitionResult( return new InkShapeRecognitionResult(
node.GetShapeName(), node.GetShapeName(),
@@ -176,9 +173,6 @@ namespace Ink_Canvas.Helpers
public static bool IsContainShapeType(string name) public static bool IsContainShapeType(string name)
{ {
if (string.IsNullOrEmpty(name))
return false;
if (name.Contains("Triangle") || name.Contains("Circle") || if (name.Contains("Triangle") || name.Contains("Circle") ||
name.Contains("Rectangle") || name.Contains("Diamond") || name.Contains("Rectangle") || name.Contains("Diamond") ||
name.Contains("Parallelogram") || name.Contains("Square") name.Contains("Parallelogram") || name.Contains("Square")
@@ -11,25 +11,14 @@ using Timer = System.Timers.Timer;
namespace Ink_Canvas.Helpers namespace Ink_Canvas.Helpers
{ {
public class ROTPPTManager : IPPTLinkManager public class ROTPPTLinkManager : BasePPTLinkManager
{ {
#region Events public override dynamic PPTApplication { get; protected set; }
public event Action<object> SlideShowBegin;
public event Action<object> SlideShowNextSlide;
public event Action<object> SlideShowEnd;
public event Action<object> PresentationOpen;
public event Action<object> PresentationClose;
public event Action<bool> PPTConnectionChanged;
public event Action<bool> SlideShowStateChanged;
#endregion
#region Properties
public dynamic PPTApplication { get; private set; }
public dynamic CurrentPresentation { get; private set; } public dynamic CurrentPresentation { get; private set; }
public dynamic CurrentSlides { get; private set; } public dynamic CurrentSlides { get; private set; }
public dynamic CurrentSlide { get; private set; } public dynamic CurrentSlide { get; private set; }
public int SlidesCount { get; private set; } public override int SlidesCount { get; protected set; }
public bool IsConnected public override bool IsConnected
{ {
get get
{ {
@@ -38,14 +27,12 @@ namespace Ink_Canvas.Helpers
if (PPTApplication == null) return false; if (PPTApplication == null) return false;
if (!Marshal.IsComObject(PPTApplication)) return false; if (!Marshal.IsComObject(PPTApplication)) return false;
// 尝试访问一个简单的属性来验证连接是否有效
var _ = PPTApplication.Name; var _ = PPTApplication.Name;
return true; return true;
} }
catch (COMException comEx) catch (COMException comEx)
{ {
var hr = (uint)comEx.HResult; var hr = (uint)comEx.HResult;
// 如果COM对象已失效,返回false
if (hr == 0x8001010E || hr == 0x80004005 || hr == 0x800706B5) if (hr == 0x8001010E || hr == 0x80004005 || hr == 0x800706B5)
{ {
return false; return false;
@@ -58,7 +45,7 @@ namespace Ink_Canvas.Helpers
} }
} }
} }
public bool IsInSlideShow public override bool IsInSlideShow
{ {
get get
{ {
@@ -119,9 +106,8 @@ namespace Ink_Canvas.Helpers
} }
} }
} }
public bool IsSupportWPS { get; set; } = false; public override bool IsSupportWPS { get; set; } = false;
public bool SkipAnimationsWhenNavigating { get; set; } = false; public override bool SkipAnimationsWhenNavigating { get; set; } = false;
#endregion
#region Private Fields #region Private Fields
private Thread _monitoringThread; private Thread _monitoringThread;
@@ -149,11 +135,11 @@ namespace Ink_Canvas.Helpers
#endregion #endregion
#region Constructor & Initialization #region Constructor & Initialization
public ROTPPTManager() public ROTPPTLinkManager()
{ {
} }
public void StartMonitoring() public override void StartMonitoring()
{ {
if (_disposed) return; if (_disposed) return;
@@ -175,7 +161,7 @@ namespace Ink_Canvas.Helpers
} }
} }
public void StopMonitoring() public override void StopMonitoring()
{ {
lock (_monitoringLock) lock (_monitoringLock)
{ {
@@ -195,7 +181,7 @@ namespace Ink_Canvas.Helpers
} }
} }
public void ReloadConnection() public override void ReloadConnection()
{ {
if (_disposed) return; if (_disposed) return;
@@ -555,7 +541,7 @@ namespace Ink_Canvas.Helpers
{ {
try try
{ {
SlideShowNextSlide?.Invoke(_pptSlideShowWindow); OnSlideShowNextSlide(_pptSlideShowWindow);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -586,7 +572,7 @@ namespace Ink_Canvas.Helpers
{ {
try try
{ {
SlideShowNextSlide?.Invoke(_pptSlideShowWindow); OnSlideShowNextSlide(_pptSlideShowWindow);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -617,7 +603,7 @@ namespace Ink_Canvas.Helpers
SlidesCount = 0; SlidesCount = 0;
_lastSlideShowState = false; _lastSlideShowState = false;
SlideShowStateChanged?.Invoke(false); OnSlideShowStateChanged(false);
if (_pptActivePresentation != null) if (_pptActivePresentation != null)
{ {
@@ -964,7 +950,7 @@ namespace Ink_Canvas.Helpers
try try
{ {
LogHelper.WriteLogToFile($"轮询模式检测到页码变化: {_lastPolledSlideNumber} -> {currentPage},触发事件", LogHelper.LogType.Trace); LogHelper.WriteLogToFile($"轮询模式检测到页码变化: {_lastPolledSlideNumber} -> {currentPage},触发事件", LogHelper.LogType.Trace);
SlideShowNextSlide?.Invoke(_pptSlideShowWindow); OnSlideShowNextSlide(_pptSlideShowWindow);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -1008,7 +994,7 @@ namespace Ink_Canvas.Helpers
try try
{ {
LogHelper.WriteLogToFile($"轮询模式检测到页码变化: {_lastPolledSlideNumber} -> {currentPage},触发事件", LogHelper.LogType.Trace); LogHelper.WriteLogToFile($"轮询模式检测到页码变化: {_lastPolledSlideNumber} -> {currentPage},触发事件", LogHelper.LogType.Trace);
SlideShowNextSlide?.Invoke(_pptSlideShowWindow); OnSlideShowNextSlide(_pptSlideShowWindow);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -1038,7 +1024,7 @@ namespace Ink_Canvas.Helpers
SlidesCount = 0; SlidesCount = 0;
_lastSlideShowState = false; _lastSlideShowState = false;
SlideShowStateChanged?.Invoke(false); OnSlideShowStateChanged(false);
if (_pptActivePresentation != null) if (_pptActivePresentation != null)
{ {
@@ -1074,7 +1060,7 @@ namespace Ink_Canvas.Helpers
if (currentSlideShowState != _lastSlideShowState) if (currentSlideShowState != _lastSlideShowState)
{ {
_lastSlideShowState = currentSlideShowState; _lastSlideShowState = currentSlideShowState;
SlideShowStateChanged?.Invoke(currentSlideShowState); OnSlideShowStateChanged(currentSlideShowState);
if (!currentSlideShowState) if (!currentSlideShowState)
{ {
@@ -1252,7 +1238,7 @@ namespace Ink_Canvas.Helpers
UpdateCurrentPresentationInfo(); UpdateCurrentPresentationInfo();
} }
PPTConnectionChanged?.Invoke(true); OnPPTConnectionChanged(true);
try try
{ {
@@ -1301,7 +1287,7 @@ namespace Ink_Canvas.Helpers
if (slideShowWindow == null) if (slideShowWindow == null)
return; return;
SlideShowBegin?.Invoke(slideShowWindow); OnSlideShowBegin(slideShowWindow);
LogHelper.WriteLogToFile("检测到放映已在进行中,热重载", LogHelper.LogType.Trace); LogHelper.WriteLogToFile("检测到放映已在进行中,热重载", LogHelper.LogType.Trace);
} }
catch (COMException comEx) catch (COMException comEx)
@@ -1374,7 +1360,7 @@ namespace Ink_Canvas.Helpers
{ {
try try
{ {
PresentationClose?.Invoke(_pptActivePresentation); OnPresentationClose(_pptActivePresentation);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -1457,7 +1443,7 @@ namespace Ink_Canvas.Helpers
_forcePolling = true; _forcePolling = true;
_bindingEvents = false; _bindingEvents = false;
PPTConnectionChanged?.Invoke(false); OnPPTConnectionChanged(false);
LogHelper.WriteLogToFile("已断开PPT连接,并显式释放所有COM对象", LogHelper.LogType.Event); LogHelper.WriteLogToFile("已断开PPT连接,并显式释放所有COM对象", LogHelper.LogType.Event);
@@ -1776,7 +1762,7 @@ namespace Ink_Canvas.Helpers
} }
UpdateCurrentPresentationInfo(); UpdateCurrentPresentationInfo();
PresentationOpen?.Invoke(pres); OnPresentationOpen(pres);
LogHelper.WriteLogToFile($"演示文稿已打开: {pres?.Name}", LogHelper.LogType.Event); LogHelper.WriteLogToFile($"演示文稿已打开: {pres?.Name}", LogHelper.LogType.Event);
} }
catch (Exception ex) catch (Exception ex)
@@ -1821,7 +1807,7 @@ namespace Ink_Canvas.Helpers
} }
} }
private void OnSlideShowBegin(object wn) protected override void OnSlideShowBegin(object wn)
{ {
try try
{ {
@@ -1854,10 +1840,10 @@ namespace Ink_Canvas.Helpers
if (!_lastSlideShowState) if (!_lastSlideShowState)
{ {
_lastSlideShowState = true; _lastSlideShowState = true;
SlideShowStateChanged?.Invoke(true); OnSlideShowStateChanged(true);
} }
SlideShowBegin?.Invoke(wn); base.OnSlideShowBegin(wn);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -1890,7 +1876,7 @@ namespace Ink_Canvas.Helpers
} }
UpdateCurrentPresentationInfo(); UpdateCurrentPresentationInfo();
SlideShowNextSlide?.Invoke(wn); base.OnSlideShowNextSlide(wn);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -1898,7 +1884,7 @@ namespace Ink_Canvas.Helpers
} }
} }
private void OnSlideShowEnd(object pres) protected override void OnSlideShowEnd(object pres)
{ {
try try
{ {
@@ -1920,10 +1906,10 @@ namespace Ink_Canvas.Helpers
if (_lastSlideShowState) if (_lastSlideShowState)
{ {
_lastSlideShowState = false; _lastSlideShowState = false;
SlideShowStateChanged?.Invoke(false); OnSlideShowStateChanged(false);
} }
SlideShowEnd?.Invoke(pres); base.OnSlideShowEnd(pres);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -1938,7 +1924,7 @@ namespace Ink_Canvas.Helpers
#endregion #endregion
#region Public Methods #region Public Methods
public bool TryNavigateToSlide(int slideNumber) public override bool TryNavigateToSlide(int slideNumber)
{ {
object slideShowWindows = null; object slideShowWindows = null;
object slideShowWindow = null; object slideShowWindow = null;
@@ -2025,7 +2011,7 @@ namespace Ink_Canvas.Helpers
} }
} }
public bool TryNavigateNext() public override bool TryNavigateNext()
{ {
try try
{ {
@@ -2088,7 +2074,7 @@ namespace Ink_Canvas.Helpers
} }
} }
public bool TryNavigatePrevious() public override bool TryNavigatePrevious()
{ {
try try
{ {
@@ -2151,7 +2137,7 @@ namespace Ink_Canvas.Helpers
} }
} }
public bool TryEndSlideShow() public override bool TryEndSlideShow()
{ {
object slideShowWindows = null; object slideShowWindows = null;
object slideShowWindow = null; object slideShowWindow = null;
@@ -2203,7 +2189,7 @@ namespace Ink_Canvas.Helpers
} }
} }
public bool TryStartSlideShow() public override bool TryStartSlideShow()
{ {
try try
{ {
@@ -2234,7 +2220,7 @@ namespace Ink_Canvas.Helpers
/// <summary> /// <summary>
/// 获取当前活跃的演示文稿 /// 获取当前活跃的演示文稿
/// </summary> /// </summary>
public object GetCurrentActivePresentation() public override object GetCurrentActivePresentation()
{ {
object slideShowWindows = null; object slideShowWindows = null;
object slideShowWindow = null; object slideShowWindow = null;
@@ -2412,7 +2398,7 @@ namespace Ink_Canvas.Helpers
} }
} }
public int GetCurrentSlideNumber() public override int GetCurrentSlideNumber()
{ {
object activeWindow = null; object activeWindow = null;
object selection = null; object selection = null;
@@ -2483,7 +2469,7 @@ namespace Ink_Canvas.Helpers
} }
} }
public string GetPresentationName() public override string GetPresentationName()
{ {
try try
{ {
@@ -2535,7 +2521,7 @@ namespace Ink_Canvas.Helpers
} }
} }
public bool TryShowSlideNavigation() public override bool TryShowSlideNavigation()
{ {
object slideShowWindows = null; object slideShowWindows = null;
object slideShowWindow = null; object slideShowWindow = null;
@@ -3020,7 +3006,7 @@ namespace Ink_Canvas.Helpers
SlidesCount = 0; SlidesCount = 0;
StopWpsProcessCheckTimer(); StopWpsProcessCheckTimer();
PPTConnectionChanged?.Invoke(false); OnPPTConnectionChanged(false);
LogHelper.WriteLogToFile("WPS进程结束后已清理所有COM对象并重启连接检查", LogHelper.LogType.Event); LogHelper.WriteLogToFile("WPS进程结束后已清理所有COM对象并重启连接检查", LogHelper.LogType.Event);
} }
@@ -3542,7 +3528,7 @@ namespace Ink_Canvas.Helpers
#endregion #endregion
#region Dispose #region Dispose
public void Dispose() public override void Dispose()
{ {
if (!_disposed) if (!_disposed)
{ {
@@ -29,13 +29,31 @@ namespace Ink_Canvas.Helpers
public static bool IsApiAvailable => public static bool IsApiAvailable =>
OSVersion.GetOperatingSystem() >= OSVersionExtension.OperatingSystem.Windows10; OSVersion.GetOperatingSystem() >= OSVersionExtension.OperatingSystem.Windows10;
/// <summary>
/// 启动阶段不再预热线程内 WinRT 手写管线。历史上曾用 <see cref="WinRtInkShapeRecognizer.CreateMinimalWarmupStrokeCollection"/> 跑全链路,
/// 会显著拖慢启动;与更早的「空 <see cref="StrokeCollection"/>」一样,此处不再在 Idle 上做任何工作。
/// 首次真正需要手写识别时由 <see cref="RecognizeHandwritingAsync"/> 承担冷启动成本。
/// </summary>
public static void Warmup() public static void Warmup()
{ {
if (!IsApiAvailable || !Environment.Is64BitProcess) return;
try
{
var d = Application.Current?.Dispatcher;
if (d == null) return;
d.BeginInvoke(new Action(async () =>
{
try
{
await RecognizeHandwritingAsync(
WinRtInkShapeRecognizer.CreateMinimalWarmupStrokeCollection(),
verboseTrace: false).ConfigureAwait(true);
}
catch
{
// ignore
}
}));
}
catch
{
// ignore
}
} }
/// <summary> /// <summary>
@@ -124,9 +124,6 @@ namespace Ink_Canvas.Helpers
return null; return null;
var da = stroke.DrawingAttributes; var da = stroke.DrawingAttributes;
if (da == null)
return null;
var wda = new global::Windows.UI.Input.Inking.InkDrawingAttributes var wda = new global::Windows.UI.Input.Inking.InkDrawingAttributes
{ {
PenTip = global::Windows.UI.Input.Inking.PenTipShape.Circle, PenTip = global::Windows.UI.Input.Inking.PenTipShape.Circle,
-70
View File
@@ -624,67 +624,6 @@
</ikw:SimpleStackPanel> </ikw:SimpleStackPanel>
</ikw:SimpleStackPanel> </ikw:SimpleStackPanel>
</Border> </Border>
<Border x:Name="Net472CompatibilityWarningPanel"
Background="#FFFFF4C2"
BorderBrush="#FFF0C24B"
BorderThickness="1"
CornerRadius="10"
Padding="14,12"
Margin="0,14,0,0"
Visibility="Collapsed">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
TextWrapping="Wrap"
Foreground="#FF2A2A2A"
FontSize="14"
Text="此版本为最后一个基于.NET Framework 4.7.2的版本,您需要手动确认才能继续接受自动更新。" />
<Button Grid.Column="1"
Margin="14,0,0,0"
Padding="18,6"
MinWidth="92"
Click="ConfirmNetCompatibilityChangeButton_Click"
Background="#FFFFFFFF"
BorderBrush="#FFE5E7EB"
Foreground="#FF111827"
Content="确认">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="8"
SnapsToDevicePixels="True">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
RecognizesAccessKey="True"
Margin="{TemplateBinding Padding}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#FFF9FAFB" />
<Setter TargetName="Bd" Property="BorderBrush" Value="#FFD1D5DB" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#FFF3F4F6" />
<Setter TargetName="Bd" Property="BorderBrush" Value="#FF9CA3AF" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Bd" Property="Background" Value="#FFE7E7E7" />
<Setter Property="Foreground" Value="#FF7A7A7A" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Border>
<GroupBox Name="GroupBoxModeSettings"> <GroupBox Name="GroupBoxModeSettings">
<GroupBox.Header> <GroupBox.Header>
<TextBlock Margin="0,12,0,0" Text="{i18n:I18n Key=Settings_Mode}" FontWeight="Bold" Foreground="#fafafa" <TextBlock Margin="0,12,0,0" Text="{i18n:I18n Key=Settings_Mode}" FontWeight="Bold" Foreground="#fafafa"
@@ -864,15 +803,6 @@
FontSize="26" /> FontSize="26" />
</GroupBox.Header> </GroupBox.Header>
<ikw:SimpleStackPanel Spacing="6"> <ikw:SimpleStackPanel Spacing="6">
<ikw:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="{i18n:I18n Key=Canvas_DisableHardwareAcceleration}" VerticalAlignment="Center"
FontSize="14" Margin="0,0,16,0" />
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchDisableHardwareAcceleration"
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchDisableHardwareAcceleration_Toggled" />
</ikw:SimpleStackPanel>
<TextBlock Text="{i18n:I18n Key=Canvas_DisableHardwareAccelerationHint}" TextWrapping="Wrap" Foreground="#a1a1aa" />
<ikw:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left"> <ikw:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="{i18n:I18n Key=Canvas_ShowCursor}" VerticalAlignment="Center" <TextBlock Foreground="#fafafa" Text="{i18n:I18n Key=Canvas_ShowCursor}" VerticalAlignment="Center"
FontSize="14" Margin="0,0,16,0" /> FontSize="14" Margin="0,0,16,0" />
+1 -154
View File
@@ -1180,7 +1180,6 @@ namespace Ink_Canvas
loadPenCanvas(); loadPenCanvas();
//加载设置 //加载设置
LoadSettings(true); LoadSettings(true);
ScheduleNetCompatibilityChangePromptAfterStartup();
ApplyLanguageFromSettings(); ApplyLanguageFromSettings();
AutoBackupManager.Initialize(Settings); AutoBackupManager.Initialize(Settings);
CheckUpdateChannelAndTelemetryConsistency(); CheckUpdateChannelAndTelemetryConsistency();
@@ -1502,149 +1501,6 @@ namespace Ink_Canvas
AddTouchSupportToSliders(); AddTouchSupportToSliders();
} }
private void ShowNetCompatibilityChangePromptIfNeeded()
{
try
{
if (IsNetCompatibilityChangePromptAcknowledged() || IsNetCompatibilityChangeConfirmed())
{
return;
}
MessageBox.Show(
"下个版本将更换.NET6,请前往设置确认以便继续使用自动更新",
"兼容性变更",
MessageBoxButton.OK,
MessageBoxImage.Warning);
PersistNetCompatibilityChangePromptAcknowledgement();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"兼容性变更提示弹窗失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void ScheduleNetCompatibilityChangePromptAfterStartup()
{
Dispatcher.BeginInvoke(new Action(async () =>
{
try
{
// 等待主窗口启动流程与初始渲染基本完成后再提示,避免“刚启动就弹窗”。
await Task.Delay(1000);
ShowNetCompatibilityChangePromptIfNeeded();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"延迟显示兼容性变更提示失败: {ex.Message}", LogHelper.LogType.Error);
}
}), DispatcherPriority.ApplicationIdle);
}
private string GetNetCompatibilityConfirmationFlagPath()
{
return Path.Combine(App.RootPath, "Configs", "NetCompatibilityConfirmed.flag");
}
private string GetNetCompatibilityPromptAcknowledgedFlagPath()
{
return Path.Combine(App.RootPath, "Configs", "NetCompatibilityPromptAcknowledged.flag");
}
private bool IsNetCompatibilityChangePromptAcknowledged()
{
try
{
return File.Exists(GetNetCompatibilityPromptAcknowledgedFlagPath());
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"读取兼容性提示弹窗标记失败: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
private void PersistNetCompatibilityChangePromptAcknowledgement()
{
try
{
var flagPath = GetNetCompatibilityPromptAcknowledgedFlagPath();
var dir = Path.GetDirectoryName(flagPath);
if (!string.IsNullOrWhiteSpace(dir) && !Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
if (!File.Exists(flagPath))
{
File.WriteAllText(flagPath, "acknowledged=true");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"写入兼容性提示弹窗标记失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private bool IsNetCompatibilityChangeConfirmed()
{
if (Settings?.Startup?.HasConfirmedNetCompatibilityChange == true)
{
return true;
}
try
{
var flagPath = GetNetCompatibilityConfirmationFlagPath();
if (File.Exists(flagPath))
{
if (Settings?.Startup != null && !Settings.Startup.HasConfirmedNetCompatibilityChange)
{
Settings.Startup.HasConfirmedNetCompatibilityChange = true;
SaveSettingsToFile();
}
return true;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"读取兼容性确认标记失败: {ex.Message}", LogHelper.LogType.Error);
}
return false;
}
private void PersistNetCompatibilityChangeConfirmation()
{
if (Settings?.Startup == null)
{
Settings.Startup = new Startup();
}
Settings.Startup.HasConfirmedNetCompatibilityChange = true;
try
{
var flagPath = GetNetCompatibilityConfirmationFlagPath();
var dir = Path.GetDirectoryName(flagPath);
if (!string.IsNullOrWhiteSpace(dir) && !Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
if (!File.Exists(flagPath))
{
File.WriteAllText(flagPath, "confirmed=true");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"写入兼容性确认标记失败: {ex.Message}", LogHelper.LogType.Error);
}
SaveSettingsToFile();
}
private void ApplyLanguageFromSettings() private void ApplyLanguageFromSettings()
{ {
try try
@@ -2061,12 +1917,6 @@ namespace Ink_Canvas
private async void AutoUpdate() private async void AutoUpdate()
{ {
if (Settings?.Startup?.HasConfirmedNetCompatibilityChange != true)
{
LogHelper.WriteLogToFile("AutoUpdate | 自动更新已暂停:未确认兼容性变更(NET472 -> .NET6", LogHelper.LogType.Warning);
return;
}
if (!string.IsNullOrEmpty(Settings.Startup.AutoUpdatePauseUntilDate)) if (!string.IsNullOrEmpty(Settings.Startup.AutoUpdatePauseUntilDate))
{ {
if (DateTime.TryParse(Settings.Startup.AutoUpdatePauseUntilDate, out DateTime pauseUntilDate)) if (DateTime.TryParse(Settings.Startup.AutoUpdatePauseUntilDate, out DateTime pauseUntilDate))
@@ -2972,7 +2822,7 @@ namespace Ink_Canvas
settingsWindow.ShowDialog(); settingsWindow.ShowDialog();
} }
} }
#endregion #endregion
// 在MainWindow类中添加: // 在MainWindow类中添加:
@@ -4533,9 +4383,6 @@ namespace Ink_Canvas
{ {
try try
{ {
if (!IsLoaded)
return;
if (Settings.ModeSettings.IsPPTOnlyMode) if (Settings.ModeSettings.IsPPTOnlyMode)
{ {
if (TrayTemporaryShowUntilUtc.HasValue && DateTime.UtcNow < TrayTemporaryShowUntilUtc.Value) if (TrayTemporaryShowUntilUtc.HasValue && DateTime.UtcNow < TrayTemporaryShowUntilUtc.Value)
+2
View File
@@ -1,11 +1,13 @@
using Ink_Canvas.Helpers; using Ink_Canvas.Helpers;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Ink; using System.Windows.Ink;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
namespace Ink_Canvas namespace Ink_Canvas
+1 -1
View File
@@ -248,7 +248,7 @@ namespace Ink_Canvas
// 根据设置选择 COM / ROT 架构 // 根据设置选择 COM / ROT 架构
if (Settings.PowerPointSettings.UseRotPptLink) if (Settings.PowerPointSettings.UseRotPptLink)
{ {
_pptManager = new ROTPPTManager(); _pptManager = new ROTPPTLinkManager();
} }
else else
{ {
+45 -57
View File
@@ -617,18 +617,6 @@ namespace Ink_Canvas
private void ToggleSwitchIsAutoUpdate_Toggled(object sender, RoutedEventArgs e) private void ToggleSwitchIsAutoUpdate_Toggled(object sender, RoutedEventArgs e)
{ {
if (!isLoaded) return; if (!isLoaded) return;
if (ToggleSwitchIsAutoUpdate.IsOn && Settings?.Startup?.HasConfirmedNetCompatibilityChange != true)
{
ToggleSwitchIsAutoUpdate.IsOn = false;
MessageBox.Show(
"此版本为最后一个NET472版本,您需要手动确认才能继续接受自动更新。",
"兼容性变更",
MessageBoxButton.OK,
MessageBoxImage.Warning);
return;
}
Settings.Startup.IsAutoUpdate = ToggleSwitchIsAutoUpdate.IsOn; Settings.Startup.IsAutoUpdate = ToggleSwitchIsAutoUpdate.IsOn;
// 自动更新关闭时隐藏静默更新选项 // 自动更新关闭时隐藏静默更新选项
@@ -663,18 +651,6 @@ namespace Ink_Canvas
private void ToggleSwitchIsAutoUpdateWithSilence_Toggled(object sender, RoutedEventArgs e) private void ToggleSwitchIsAutoUpdateWithSilence_Toggled(object sender, RoutedEventArgs e)
{ {
if (!isLoaded) return; if (!isLoaded) return;
if (ToggleSwitchIsAutoUpdateWithSilence.IsOn && Settings?.Startup?.HasConfirmedNetCompatibilityChange != true)
{
ToggleSwitchIsAutoUpdateWithSilence.IsOn = false;
MessageBox.Show(
"此版本为最后一个NET472版本,您需要手动确认才能继续接受自动更新。",
"兼容性变更",
MessageBoxButton.OK,
MessageBoxImage.Warning);
return;
}
Settings.Startup.IsAutoUpdateWithSilence = ToggleSwitchIsAutoUpdateWithSilence.IsOn; Settings.Startup.IsAutoUpdateWithSilence = ToggleSwitchIsAutoUpdateWithSilence.IsOn;
// 静默更新的时间设置区域只在静默更新开启时显示 // 静默更新的时间设置区域只在静默更新开启时显示
@@ -3355,16 +3331,55 @@ namespace Ink_Canvas
SaveSettingsToFile(); SaveSettingsToFile();
} }
private void ToggleSwitchDisableHardwareAcceleration_Toggled(object sender, RoutedEventArgs e) // 注释掉这些方法,因为对应的UI控件还没有在XAML中定义
{ /*
private void ToggleSwitchAsyncInkSmoothing_Toggled(object sender, RoutedEventArgs e) {
if (!isLoaded) return; if (!isLoaded) return;
Settings.Canvas.UseAsyncInkSmoothing = ToggleSwitchAsyncInkSmoothing.IsOn;
Settings.Canvas.UseHardwareAcceleration = !ToggleSwitchDisableHardwareAcceleration.IsOn;
_inkSmoothingManager?.UpdateConfig(); _inkSmoothingManager?.UpdateConfig();
SaveSettingsToFile(); SaveSettingsToFile();
} }
private void ToggleSwitchHardwareAcceleration_Toggled(object sender, RoutedEventArgs e) {
if (!isLoaded) return;
Settings.Canvas.UseHardwareAcceleration = ToggleSwitchHardwareAcceleration.IsOn;
_inkSmoothingManager?.UpdateConfig();
SaveSettingsToFile();
}
private void ComboBoxInkSmoothingQuality_SelectionChanged(object sender, SelectionChangedEventArgs e) {
if (!isLoaded) return;
Settings.Canvas.InkSmoothingQuality = ComboBoxInkSmoothingQuality.SelectedIndex;
_inkSmoothingManager?.UpdateConfig();
SaveSettingsToFile();
}
private void SliderMaxConcurrentTasks_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) {
if (!isLoaded) return;
Settings.Canvas.MaxConcurrentSmoothingTasks = (int)SliderMaxConcurrentTasks.Value;
_inkSmoothingManager?.UpdateConfig();
SaveSettingsToFile();
}
private void ButtonApplyRecommendedSettings_Click(object sender, RoutedEventArgs e) {
// 应用推荐的性能设置
Helpers.InkSmoothingManager.ApplyRecommendedSettings();
LoadSettings(false);
_inkSmoothingManager?.UpdateConfig();
SaveSettingsToFile();
ShowNotification("已应用推荐的性能设置");
}
private void ButtonShowPerformanceStats_Click(object sender, RoutedEventArgs e) {
if (_inkSmoothingManager != null)
{
var stats = _inkSmoothingManager.GetPerformanceStats();
ShowNotification($"性能统计: {stats}");
}
}
*/
private void ToggleSwitchAutoSaveStrokesInPowerPoint_Toggled(object sender, RoutedEventArgs e) private void ToggleSwitchAutoSaveStrokesInPowerPoint_Toggled(object sender, RoutedEventArgs e)
{ {
if (!isLoaded) return; if (!isLoaded) return;
@@ -5327,7 +5342,7 @@ namespace Ink_Canvas
SaveSettingsToFile(); SaveSettingsToFile();
// 如果启用了自动更新,立即执行完整的检查更新操作 // 如果启用了自动更新,立即执行完整的检查更新操作
if (Settings.Startup.IsAutoUpdate && Settings.Startup.HasConfirmedNetCompatibilityChange) if (Settings.Startup.IsAutoUpdate)
{ {
LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, performing immediate update check"); LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, performing immediate update check");
ResetUpdateCheckRetry(); ResetUpdateCheckRetry();
@@ -5356,33 +5371,6 @@ namespace Ink_Canvas
} }
} }
private void ConfirmNetCompatibilityChangeButton_Click(object sender, RoutedEventArgs e)
{
try
{
PersistNetCompatibilityChangeConfirmation();
if (Net472CompatibilityWarningPanel != null)
{
Net472CompatibilityWarningPanel.Visibility = Visibility.Collapsed;
}
if (ToggleSwitchIsAutoUpdate != null)
{
ToggleSwitchIsAutoUpdate.IsEnabled = true;
}
if (ToggleSwitchIsAutoUpdateWithSilence != null)
{
ToggleSwitchIsAutoUpdateWithSilence.IsEnabled = true;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"确认兼容性变更失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private async void ManualUpdateButton_Click(object sender, RoutedEventArgs e) private async void ManualUpdateButton_Click(object sender, RoutedEventArgs e)
{ {
ManualUpdateButton.IsEnabled = false; ManualUpdateButton.IsEnabled = false;
+17 -24
View File
@@ -267,7 +267,7 @@ namespace Ink_Canvas
ToggleSwitchIsAutoUpdate.IsOn = Settings.Startup.IsAutoUpdate; ToggleSwitchIsAutoUpdate.IsOn = Settings.Startup.IsAutoUpdate;
// 只有在启用了自动更新功能时才检查更新 // 只有在启用了自动更新功能时才检查更新
if (Settings.Startup.IsAutoUpdate && Settings.Startup.HasConfirmedNetCompatibilityChange && !skipAutoUpdateCheck) if (Settings.Startup.IsAutoUpdate && !skipAutoUpdateCheck)
{ {
if (isStartup) if (isStartup)
{ {
@@ -288,8 +288,6 @@ namespace Ink_Canvas
ToggleSwitchIsAutoUpdateWithSilence.IsOn = true; ToggleSwitchIsAutoUpdateWithSilence.IsOn = true;
} }
ApplyNetCompatibilityConfirmationGateToUpdateSettingsUi();
// 初始化更新通道选择 // 初始化更新通道选择
foreach (var radioButton in UpdateChannelSelector.Items) foreach (var radioButton in UpdateChannelSelector.Items)
{ {
@@ -956,7 +954,22 @@ namespace Ink_Canvas
drawingAttributes.FitToCurve = false; drawingAttributes.FitToCurve = false;
} }
ToggleSwitchDisableHardwareAcceleration.IsOn = !Settings.Canvas.UseHardwareAcceleration; // 注释掉新的墨迹平滑性能设置,因为UI控件还没有定义
/*
// 初始化新的墨迹平滑性能设置
ToggleSwitchAsyncInkSmoothing.IsOn = Settings.Canvas.UseAsyncInkSmoothing;
ToggleSwitchHardwareAcceleration.IsOn = Settings.Canvas.UseHardwareAcceleration;
ComboBoxInkSmoothingQuality.SelectedIndex = Settings.Canvas.InkSmoothingQuality;
SliderMaxConcurrentTasks.Value = Settings.Canvas.MaxConcurrentSmoothingTasks > 0 ?
Settings.Canvas.MaxConcurrentSmoothingTasks : Environment.ProcessorCount;
// 检查硬件加速支持
if (!Helpers.InkSmoothingManager.IsHardwareAccelerationSupported())
{
ToggleSwitchHardwareAcceleration.IsEnabled = false;
// 可以添加提示文本说明硬件加速不可用
}
*/
// 初始化直线自动拉直相关设置 // 初始化直线自动拉直相关设置
ToggleSwitchAutoStraightenLine.IsOn = Settings.Canvas.AutoStraightenLine; ToggleSwitchAutoStraightenLine.IsOn = Settings.Canvas.AutoStraightenLine;
@@ -1319,26 +1332,6 @@ namespace Ink_Canvas
try { RefreshConfigProfileList(); } catch (Exception ex) { LogHelper.WriteLogToFile($"刷新配置文件列表失败: {ex.Message}", LogHelper.LogType.Warning); } try { RefreshConfigProfileList(); } catch (Exception ex) { LogHelper.WriteLogToFile($"刷新配置文件列表失败: {ex.Message}", LogHelper.LogType.Warning); }
} }
private void ApplyNetCompatibilityConfirmationGateToUpdateSettingsUi()
{
bool confirmed = Settings?.Startup?.HasConfirmedNetCompatibilityChange == true;
if (Net472CompatibilityWarningPanel != null)
{
Net472CompatibilityWarningPanel.Visibility = confirmed ? Visibility.Collapsed : Visibility.Visible;
}
if (ToggleSwitchIsAutoUpdate != null)
{
ToggleSwitchIsAutoUpdate.IsEnabled = confirmed;
}
if (ToggleSwitchIsAutoUpdateWithSilence != null)
{
ToggleSwitchIsAutoUpdateWithSilence.IsEnabled = confirmed;
}
}
/// <summary> /// <summary>
/// 将画笔自动恢复相关的设置应用到界面控件并在启用时初始化自动恢复定时器。 /// 将画笔自动恢复相关的设置应用到界面控件并在启用时初始化自动恢复定时器。
/// </summary> /// </summary>
@@ -718,18 +718,6 @@ namespace Ink_Canvas
var endP = new Point(result.Centroid.X + result.ShapeWidth / 2, var endP = new Point(result.Centroid.X + result.ShapeWidth / 2,
result.Centroid.Y + result.ShapeHeight / 2); result.Centroid.Y + result.ShapeHeight / 2);
// WinRT 返回的热点顺序/方向不稳定时,用点集反推 IACore 风格椭圆参数(中心/长短轴/方向/四个端点)
var hasEllipseParams = TryEstimateEllipseParamsFromStrokes(
result.StrokesToRemove,
out var ellipseCentroid,
out var ellipseA,
out var ellipseB,
out var ellipseThetaRad,
out var ellipseMajor0,
out var ellipseMajor1,
out var ellipseMinor0,
out var ellipseMinor1);
foreach (var circle in circles) foreach (var circle in circles)
//判断是否画同心椭圆 //判断是否画同心椭圆
if (Math.Abs(result.Centroid.X - circle.Centroid.X) / a < 0.2 && if (Math.Abs(result.Centroid.X - circle.Centroid.X) / a < 0.2 &&
@@ -817,17 +805,13 @@ namespace Ink_Canvas
} }
} }
// 用反推参数替换中心与长短轴(比 WinRT 的包围盒更接近 IACore,且不会竖横翻转) //纠正垂直与水平关系
if (hasEllipseParams) var newPoints = FixPointsDirection(p[0], p[2]);
{ p[0] = newPoints[0];
result.Centroid = ellipseCentroid; p[2] = newPoints[1];
a = ellipseA; newPoints = FixPointsDirection(p[1], p[3]);
b = ellipseB; p[1] = newPoints[0];
iniP = new Point(result.Centroid.X - a, result.Centroid.Y - b); p[3] = newPoints[1];
endP = new Point(result.Centroid.X + a, result.Centroid.Y + b);
// 用端点重写热点,保证后续回退分支也一致
p = new PointCollection { ellipseMajor0, ellipseMinor0, ellipseMajor1, ellipseMinor1 };
}
var pointList = GenerateEllipseGeometry(iniP, endP); var pointList = GenerateEllipseGeometry(iniP, endP);
var point = new StylusPointCollection(pointList); var point = new StylusPointCollection(pointList);
@@ -839,8 +823,9 @@ namespace Ink_Canvas
if (needRotation) if (needRotation)
{ {
var m = new Matrix(); var m = new Matrix();
// 优先使用反推参数角度;否则用端点向量角度(使用 Atan2 避免斜率无穷) var fe = e.Source as FrameworkElement;
var theta = hasEllipseParams ? ellipseThetaRad : Math.Atan2(p[2].Y - p[0].Y, p[2].X - p[0].X); var tanTheta = (p[2].Y - p[0].Y) / (p[2].X - p[0].X);
var theta = Math.Atan(tanTheta);
m.RotateAt(theta * 180.0 / Math.PI, result.Centroid.X, result.Centroid.Y); m.RotateAt(theta * 180.0 / Math.PI, result.Centroid.X, result.Centroid.Y);
stroke.Transform(m, false); stroke.Transform(m, false);
} }
@@ -2297,124 +2282,6 @@ namespace Ink_Canvas
return new Point[2] { p1, p2 }; return new Point[2] { p1, p2 };
} }
/// <summary>
/// 用点集拟合出 IACore 风格椭圆参数(中心/长短半轴/方向/四个端点)。
/// 解决 WinRT 返回热点顺序不稳定导致椭圆纠正角度翻转的问题。
/// </summary>
private static bool TryEstimateEllipseParamsFromStrokes(
StrokeCollection strokes,
out Point centroid,
out double a,
out double b,
out double thetaRad,
out Point major0,
out Point major1,
out Point minor0,
out Point minor1)
{
centroid = default;
a = b = 0;
thetaRad = 0;
major0 = major1 = minor0 = minor1 = default;
if (strokes == null || strokes.Count == 0) return false;
var pts = new List<Point>(256);
foreach (var s in strokes)
{
if (s?.StylusPoints == null) continue;
foreach (var sp in s.StylusPoints)
pts.Add(sp.ToPoint());
}
if (pts.Count < 12) return false;
double mx = 0, my = 0;
for (int i = 0; i < pts.Count; i++)
{
mx += pts[i].X;
my += pts[i].Y;
}
mx /= pts.Count;
my /= pts.Count;
centroid = new Point(mx, my);
double sxx = 0, syy = 0, sxy = 0;
for (int i = 0; i < pts.Count; i++)
{
var dx = pts[i].X - mx;
var dy = pts[i].Y - my;
sxx += dx * dx;
syy += dy * dy;
sxy += dx * dy;
}
if (sxx + syy < 1e-6) return false;
thetaRad = 0.5 * Math.Atan2(2.0 * sxy, sxx - syy);
if (double.IsNaN(thetaRad) || double.IsInfinity(thetaRad)) return false;
// 主轴单位向量 v1=(cos,sin),次轴 v2=(-sin,cos)
var cos = Math.Cos(thetaRad);
var sin = Math.Sin(thetaRad);
// 投影收集,用分位数抑制离群点
var us = new double[pts.Count];
var vs = new double[pts.Count];
double maxU = double.MinValue, minU = double.MaxValue;
double maxV = double.MinValue, minV = double.MaxValue;
for (int i = 0; i < pts.Count; i++)
{
var dx = pts[i].X - mx;
var dy = pts[i].Y - my;
var u = dx * cos + dy * sin;
var v = -dx * sin + dy * cos;
us[i] = u;
vs[i] = v;
if (u > maxU) maxU = u;
if (u < minU) minU = u;
if (v > maxV) maxV = v;
if (v < minV) minV = v;
}
Array.Sort(us);
Array.Sort(vs);
int hi = (int)Math.Round((pts.Count - 1) * 0.98);
int lo = (int)Math.Round((pts.Count - 1) * 0.02);
hi = Math.Max(0, Math.Min(pts.Count - 1, hi));
lo = Math.Max(0, Math.Min(pts.Count - 1, lo));
var uHi = us[hi];
var uLo = us[lo];
var vHi = vs[hi];
var vLo = vs[lo];
var aCandidate = Math.Max(Math.Abs(uHi), Math.Abs(uLo));
var bCandidate = Math.Max(Math.Abs(vHi), Math.Abs(vLo));
if (aCandidate < 1e-3) aCandidate = Math.Max(Math.Abs(maxU), Math.Abs(minU));
if (bCandidate < 1e-3) bCandidate = Math.Max(Math.Abs(maxV), Math.Abs(minV));
a = aCandidate;
b = bCandidate;
// 保证 a 为长半轴
if (b > a)
{
var t = a; a = b; b = t;
thetaRad += Math.PI / 2;
cos = Math.Cos(thetaRad);
sin = Math.Sin(thetaRad);
}
major0 = new Point(mx - a * cos, my - a * sin);
major1 = new Point(mx + a * cos, my + a * sin);
minor0 = new Point(mx + b * sin, my - b * cos);
minor1 = new Point(mx - b * sin, my + b * cos);
return a > 1e-2 && b > 1e-2;
}
public StylusPointCollection GenerateFakePressureTriangle(StylusPointCollection points) public StylusPointCollection GenerateFakePressureTriangle(StylusPointCollection points)
{ {
var newPoint = new StylusPointCollection(); var newPoint = new StylusPointCollection();
+2 -2
View File
@@ -43,5 +43,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.7.18.10")] [assembly: AssemblyVersion("1.7.18.9")]
[assembly: AssemblyFileVersion("1.7.18.10")] [assembly: AssemblyFileVersion("1.7.18.9")]
-12
View File
@@ -528,12 +528,6 @@
<data name="InkRecog_EnableInkRecognition" xml:space="preserve"> <data name="InkRecog_EnableInkRecognition" xml:space="preserve">
<value>Enable ink recognition</value> <value>Enable ink recognition</value>
</data> </data>
<data name="InkRecog_HandwritingBeautify" xml:space="preserve">
<value>Handwriting correction</value>
</data>
<data name="InkRecog_HandwritingBeautifyHint" xml:space="preserve">
<value># When enabled, strokes will be corrected to look smoother and neater (WinRT-based).</value>
</data>
<data name="InkRecog_BlockRectFakePressure" xml:space="preserve"> <data name="InkRecog_BlockRectFakePressure" xml:space="preserve">
<value>Block fake pressure on corrected rectangles</value> <value>Block fake pressure on corrected rectangles</value>
</data> </data>
@@ -963,12 +957,6 @@
<data name="Canvas_GroupTitle" xml:space="preserve"> <data name="Canvas_GroupTitle" xml:space="preserve">
<value>Canvas and ink</value> <value>Canvas and ink</value>
</data> </data>
<data name="Canvas_DisableHardwareAcceleration" xml:space="preserve">
<value>Disable hardware acceleration</value>
</data>
<data name="Canvas_DisableHardwareAccelerationHint" xml:space="preserve">
<value># Improves compatibility but may reduce performance; some effects may require an app restart to fully apply.</value>
</data>
<data name="Canvas_ShowCursor" xml:space="preserve"> <data name="Canvas_ShowCursor" xml:space="preserve">
<value>Show pen cursor</value> <value>Show pen cursor</value>
</data> </data>
-12
View File
@@ -549,12 +549,6 @@
<data name="InkRecog_ShapeEngineHint" xml:space="preserve"> <data name="InkRecog_ShapeEngineHint" xml:space="preserve">
<value> 自动:64 位进程使用 WinRTWindows 10+),32 位使用 IACore。可强制指定 IACore 或 WinRT。</value> <value> 自动:64 位进程使用 WinRTWindows 10+),32 位使用 IACore。可强制指定 IACore 或 WinRT。</value>
</data> </data>
<data name="InkRecog_HandwritingBeautify" xml:space="preserve">
<value>手写体纠正</value>
</data>
<data name="InkRecog_HandwritingBeautifyHint" xml:space="preserve">
<value># 开启后,将对手写笔画进行更平滑、更工整的纠正处理(基于 WinRT)。</value>
</data>
<data name="InkRecog_ShapeEngineAuto" xml:space="preserve"> <data name="InkRecog_ShapeEngineAuto" xml:space="preserve">
<value>自动</value> <value>自动</value>
</data> </data>
@@ -1005,12 +999,6 @@
<data name="Canvas_GroupTitle" xml:space="preserve"> <data name="Canvas_GroupTitle" xml:space="preserve">
<value>画板和墨迹</value> <value>画板和墨迹</value>
</data> </data>
<data name="Canvas_DisableHardwareAcceleration" xml:space="preserve">
<value>关闭硬件加速</value>
</data>
<data name="Canvas_DisableHardwareAccelerationHint" xml:space="preserve">
<value># 关闭后可提升兼容性,但可能降低性能;部分效果可能需要重启程序后完全生效。</value>
</data>
<data name="Canvas_ShowCursor" xml:space="preserve"> <data name="Canvas_ShowCursor" xml:space="preserve">
<value>显示画笔光标</value> <value>显示画笔光标</value>
</data> </data>
-2
View File
@@ -250,8 +250,6 @@ namespace Ink_Canvas
public bool HasAcceptedTelemetryPrivacy { get; set; } = false; public bool HasAcceptedTelemetryPrivacy { get; set; } = false;
[JsonProperty("hasShownOobe")] [JsonProperty("hasShownOobe")]
public bool HasShownOobe { get; set; } = false; public bool HasShownOobe { get; set; } = false;
[JsonProperty("hasConfirmedNetCompatibilityChange")]
public bool HasConfirmedNetCompatibilityChange { get; set; } = false;
} }
public class Appearance public class Appearance
@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Windows; using System.Windows;
@@ -1,5 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ink_Canvas.Windows.SettingsViews.Pages namespace Ink_Canvas.Windows.SettingsViews.Pages
{ {
@@ -1,5 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ink_Canvas.Windows.SettingsViews.Pages namespace Ink_Canvas.Windows.SettingsViews.Pages
{ {
@@ -1,5 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ink_Canvas.Windows.SettingsViews.Pages namespace Ink_Canvas.Windows.SettingsViews.Pages
{ {
@@ -1,5 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ink_Canvas.Windows.SettingsViews.Pages namespace Ink_Canvas.Windows.SettingsViews.Pages
{ {
@@ -1,4 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ink_Canvas.Windows.SettingsViews.Pages namespace Ink_Canvas.Windows.SettingsViews.Pages
{ {
@@ -2,10 +2,11 @@ using Ink_Canvas.Windows.SettingsViews.Pages;
using iNKORE.UI.WPF.Modern.Controls; using iNKORE.UI.WPF.Modern.Controls;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Interop;
using System.Windows.Navigation; using System.Windows.Navigation;
using System.Windows.Interop;
using System.Windows.Input;
using System.Linq;
using MessageBox = System.Windows.MessageBox; using MessageBox = System.Windows.MessageBox;
using Screen = System.Windows.Forms.Screen; using Screen = System.Windows.Forms.Screen;
@@ -15,13 +16,13 @@ namespace Ink_Canvas.Windows.SettingsViews
{ {
private readonly Dictionary<string, Type> _pageTypes; private readonly Dictionary<string, Type> _pageTypes;
private readonly Dictionary<string, object> _pages = new Dictionary<string, object>(); private readonly Dictionary<string, object> _pages = new Dictionary<string, object>();
// 保存窗口原始位置和大小 // 保存窗口原始位置和大小
private double _originalLeft; private double _originalLeft;
private double _originalTop; private double _originalTop;
private double _originalWidth; private double _originalWidth;
private double _originalHeight; private double _originalHeight;
// 标记窗口是否曾经最大化过 // 标记窗口是否曾经最大化过
private bool _wasMaximized = false; private bool _wasMaximized = false;
@@ -88,10 +89,10 @@ namespace Ink_Canvas.Windows.SettingsViews
_originalTop = this.Top; _originalTop = this.Top;
_originalWidth = this.Width; _originalWidth = this.Width;
_originalHeight = this.Height; _originalHeight = this.Height;
// 标记窗口曾经最大化过 // 标记窗口曾经最大化过
_wasMaximized = true; _wasMaximized = true;
// 最大化时清除最大尺寸限制 // 最大化时清除最大尺寸限制
this.MaxWidth = double.PositiveInfinity; this.MaxWidth = double.PositiveInfinity;
this.MaxHeight = double.PositiveInfinity; this.MaxHeight = double.PositiveInfinity;
@@ -103,10 +104,10 @@ namespace Ink_Canvas.Windows.SettingsViews
this.Top = _originalTop; this.Top = _originalTop;
this.Width = _originalWidth; this.Width = _originalWidth;
this.Height = _originalHeight; this.Height = _originalHeight;
// 重置标记 // 重置标记
_wasMaximized = false; _wasMaximized = false;
// 只设置最大尺寸,不改变窗口位置 // 只设置最大尺寸,不改变窗口位置
SetMaxSizeOnly(); SetMaxSizeOnly();
} }
@@ -115,11 +116,11 @@ namespace Ink_Canvas.Windows.SettingsViews
// 正常状态下只设置最大尺寸限制 // 正常状态下只设置最大尺寸限制
SetMaxSizeOnly(); SetMaxSizeOnly();
} }
// 窗口状态改变时更新标题栏显示 // 窗口状态改变时更新标题栏显示
UpdateAppTitleBarMargin(); UpdateAppTitleBarMargin();
}; };
// 窗口大小改变时更新标题栏显示 // 窗口大小改变时更新标题栏显示
this.SizeChanged += (sender, e) => this.SizeChanged += (sender, e) =>
{ {
@@ -157,7 +158,7 @@ namespace Ink_Canvas.Windows.SettingsViews
#endregion #endregion
#region DPI/ #region DPI/
/// <summary> /// <summary>
/// 获取当前窗口所在屏幕的工作区尺寸(DIP单位) /// 获取当前窗口所在屏幕的工作区尺寸(DIP单位)
/// </summary> /// </summary>
@@ -340,7 +341,7 @@ namespace Ink_Canvas.Windows.SettingsViews
if (sender.DisplayMode == NavigationViewDisplayMode.Minimal) if (sender.DisplayMode == NavigationViewDisplayMode.Minimal)
{ {
AppTitleBar.Margin = new Thickness((sender.CompactPaneLength * 2), currMargin.Top, currMargin.Right, currMargin.Bottom); AppTitleBar.Margin = new Thickness((sender.CompactPaneLength * 2), currMargin.Top, currMargin.Right, currMargin.Bottom);
// 当窗口宽度非常小时,隐藏图标和应用设置文字 // 当窗口宽度非常小时,隐藏图标和应用设置文字
if (this.ActualWidth < 400) if (this.ActualWidth < 400)
{ {