improve:墨迹渐隐
This commit is contained in:
@@ -833,12 +833,8 @@ namespace Ink_Canvas.Helpers
|
|||||||
// 归一化方向向量
|
// 归一化方向向量
|
||||||
direction.Normalize();
|
direction.Normalize();
|
||||||
|
|
||||||
// 使用OpacityMask创建更自然的擦除效果
|
// 创建精确的擦除动画 - 使用PathGeometry来避免变形
|
||||||
var maskBrush = CreateEraseMaskBrush(stroke, strokeStart, strokeEnd, 1.0);
|
CreatePreciseEraseAnimation(visual, stroke, bounds, strokeStart, strokeEnd, direction, length, duration);
|
||||||
visual.OpacityMask = maskBrush;
|
|
||||||
|
|
||||||
// 创建擦除动画 - 使用定时器来更新遮罩
|
|
||||||
StartEraseMaskAnimation(visual, stroke, strokeStart, strokeEnd, duration);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -848,6 +844,58 @@ namespace Ink_Canvas.Helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建精确的擦除动画 - 使用动态裁剪区域避免墨迹移动
|
||||||
|
/// </summary>
|
||||||
|
private void CreatePreciseEraseAnimation(UIElement visual, Stroke stroke, Rect bounds, Point strokeStart, Point strokeEnd, Vector direction, double length, int duration)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 计算擦除方向上的移动距离
|
||||||
|
var totalDistance = Math.Sqrt(Math.Pow(strokeEnd.X - strokeStart.X, 2) + Math.Pow(strokeEnd.Y - strokeStart.Y, 2));
|
||||||
|
|
||||||
|
if (totalDistance == 0)
|
||||||
|
{
|
||||||
|
// 如果墨迹没有长度,使用简单渐隐
|
||||||
|
StartSimpleFadeAnimation(visual, stroke, visual.Opacity, duration);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建动态裁剪区域 - 从完整墨迹开始,逐渐缩小
|
||||||
|
var clipGeometry = new RectangleGeometry
|
||||||
|
{
|
||||||
|
Rect = bounds
|
||||||
|
};
|
||||||
|
|
||||||
|
visual.Clip = clipGeometry;
|
||||||
|
|
||||||
|
// 创建擦除动画 - 裁剪区域从起点向终点移动并缩小
|
||||||
|
// 使用更自然的擦除效果:从起点开始,逐渐向终点移动
|
||||||
|
var eraseAnimation = new RectAnimation
|
||||||
|
{
|
||||||
|
From = bounds, // 从完整边界开始
|
||||||
|
To = new Rect(strokeEnd.X, strokeEnd.Y, 0, bounds.Height), // 到终点位置,宽度为0
|
||||||
|
Duration = TimeSpan.FromMilliseconds(duration),
|
||||||
|
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut }
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加动画完成事件
|
||||||
|
eraseAnimation.Completed += (sender, e) =>
|
||||||
|
{
|
||||||
|
OnAnimationCompleted(visual, stroke);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 开始动画
|
||||||
|
clipGeometry.BeginAnimation(RectangleGeometry.RectProperty, eraseAnimation);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile($"精确擦除动画失败: {ex}", LogHelper.LogType.Error);
|
||||||
|
// 失败时回退到简单动画
|
||||||
|
StartSimpleFadeAnimation(visual, stroke, visual.Opacity, duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 动画完成后的统一处理
|
/// 动画完成后的统一处理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -155,6 +155,22 @@ namespace Ink_Canvas.Helpers
|
|||||||
{
|
{
|
||||||
// 更新创建时间,使其成为最新的窗口
|
// 更新创建时间,使其成为最新的窗口
|
||||||
windowInfo.CreatedTime = DateTime.Now;
|
windowInfo.CreatedTime = DateTime.Now;
|
||||||
|
|
||||||
|
// 立即将窗口置顶
|
||||||
|
var hwnd = new WindowInteropHelper(window).Handle;
|
||||||
|
if (hwnd != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
|
||||||
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
|
||||||
|
|
||||||
|
// 确保窗口样式正确
|
||||||
|
int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||||||
|
if ((exStyle & WS_EX_TOPMOST) == 0)
|
||||||
|
{
|
||||||
|
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ApplyZOrder();
|
ApplyZOrder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,64 +181,12 @@ namespace Ink_Canvas.Helpers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private static void ApplyZOrder()
|
private static void ApplyZOrder()
|
||||||
{
|
{
|
||||||
// 按创建时间排序,最新的窗口在最后
|
// 简化逻辑:直接设置所有窗口为置顶,让Windows系统自然处理层级
|
||||||
var sortedWindows = _windowStack
|
foreach (var windowInfo in _windowStack.ToList())
|
||||||
.Where(w => IsWindow(w.Handle) && IsWindowVisible(w.Handle) && !IsIconic(w.Handle))
|
|
||||||
.OrderBy(w => w.CreatedTime)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (sortedWindows.Count == 0) return;
|
|
||||||
|
|
||||||
// 获取主窗口(第一个注册的窗口)
|
|
||||||
var mainWindow = sortedWindows.FirstOrDefault();
|
|
||||||
if (mainWindow == null) return;
|
|
||||||
|
|
||||||
// 如果主窗口需要置顶且启用了无焦点模式
|
|
||||||
if (mainWindow.IsTopmost && mainWindow.IsNoFocusMode)
|
|
||||||
{
|
{
|
||||||
// 检查是否有子窗口在前景
|
if (windowInfo.IsTopmost && IsWindow(windowInfo.Handle) && IsWindowVisible(windowInfo.Handle) && !IsIconic(windowInfo.Handle))
|
||||||
var foregroundWindow = GetForegroundWindow();
|
|
||||||
var hasChildWindowInForeground = false;
|
|
||||||
|
|
||||||
if (foregroundWindow != mainWindow.Handle)
|
|
||||||
{
|
|
||||||
var foregroundWindowProcessId = GetWindowThreadProcessId(foregroundWindow, out uint processId);
|
|
||||||
var currentProcessId = GetCurrentProcessId();
|
|
||||||
|
|
||||||
if (processId == currentProcessId)
|
|
||||||
{
|
|
||||||
// 检查前景窗口是否在我们的窗口列表中
|
|
||||||
var foregroundWindowInfo = sortedWindows.FirstOrDefault(w => w.Handle == foregroundWindow);
|
|
||||||
if (foregroundWindowInfo != null)
|
|
||||||
{
|
|
||||||
hasChildWindowInForeground = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasChildWindowInForeground)
|
|
||||||
{
|
|
||||||
// 没有子窗口在前景,主窗口置顶
|
|
||||||
SetWindowPos(mainWindow.Handle, HWND_TOPMOST, 0, 0, 0, 0,
|
|
||||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
|
|
||||||
|
|
||||||
// 确保主窗口样式正确
|
|
||||||
int exStyle = GetWindowLong(mainWindow.Handle, GWL_EXSTYLE);
|
|
||||||
if ((exStyle & WS_EX_TOPMOST) == 0)
|
|
||||||
{
|
|
||||||
SetWindowLong(mainWindow.Handle, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理其他窗口的层级
|
|
||||||
for (int i = 1; i < sortedWindows.Count; i++)
|
|
||||||
{
|
|
||||||
var windowInfo = sortedWindows[i];
|
|
||||||
|
|
||||||
// 子窗口应该置顶于主窗口
|
|
||||||
if (windowInfo.IsTopmost)
|
|
||||||
{
|
{
|
||||||
|
// 设置窗口为置顶
|
||||||
SetWindowPos(windowInfo.Handle, HWND_TOPMOST, 0, 0, 0, 0,
|
SetWindowPos(windowInfo.Handle, HWND_TOPMOST, 0, 0, 0, 0,
|
||||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
|
||||||
|
|
||||||
@@ -273,5 +237,28 @@ namespace Ink_Canvas.Helpers
|
|||||||
return _windowStack.Count;
|
return _windowStack.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 强制刷新所有窗口的置顶状态
|
||||||
|
/// </summary>
|
||||||
|
public static void ForceRefreshAllWindows()
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
foreach (var windowInfo in _windowStack.ToList())
|
||||||
|
{
|
||||||
|
if (windowInfo.IsTopmost && IsWindow(windowInfo.Handle))
|
||||||
|
{
|
||||||
|
// 强制设置窗口为置顶
|
||||||
|
SetWindowPos(windowInfo.Handle, HWND_TOPMOST, 0, 0, 0, 0,
|
||||||
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
|
||||||
|
|
||||||
|
// 确保窗口样式正确
|
||||||
|
int exStyle = GetWindowLong(windowInfo.Handle, GWL_EXSTYLE);
|
||||||
|
SetWindowLong(windowInfo.Handle, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,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;
|
||||||
@@ -246,6 +247,9 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
// 为浮动栏按钮添加触摸事件支持
|
// 为浮动栏按钮添加触摸事件支持
|
||||||
AddTouchSupportToFloatingBarButtons();
|
AddTouchSupportToFloatingBarButtons();
|
||||||
|
|
||||||
|
// 为滑块控件添加触摸事件支持
|
||||||
|
AddTouchSupportToSliders();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1755,6 +1759,9 @@ namespace Ink_Canvas
|
|||||||
// 先设置WPF的Topmost属性
|
// 先设置WPF的Topmost属性
|
||||||
Topmost = true;
|
Topmost = true;
|
||||||
|
|
||||||
|
// 立即应用置顶
|
||||||
|
WindowZOrderManager.BringToTop(this);
|
||||||
|
|
||||||
// 如果启用了无焦点模式,需要特殊处理
|
// 如果启用了无焦点模式,需要特殊处理
|
||||||
if (Settings.Advanced.IsNoFocusMode)
|
if (Settings.Advanced.IsNoFocusMode)
|
||||||
{
|
{
|
||||||
@@ -1847,8 +1854,8 @@ namespace Ink_Canvas
|
|||||||
// 检查是否有子窗口在前景
|
// 检查是否有子窗口在前景
|
||||||
if (!WindowZOrderManager.HasChildWindowInForeground())
|
if (!WindowZOrderManager.HasChildWindowInForeground())
|
||||||
{
|
{
|
||||||
// 没有子窗口在前景,重新应用Z-Order
|
// 没有子窗口在前景,强制刷新所有窗口的置顶状态
|
||||||
WindowZOrderManager.SetWindowTopmost(this, true);
|
WindowZOrderManager.ForceRefreshAllWindows();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -2227,5 +2234,288 @@ namespace Ink_Canvas
|
|||||||
LogHelper.WriteLogToFile($"设置工具模式时出错: {ex.Message}", LogHelper.LogType.Error);
|
LogHelper.WriteLogToFile($"设置工具模式时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region 滑块触摸支持
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 为所有滑块控件添加触摸和手写笔事件支持
|
||||||
|
/// </summary>
|
||||||
|
private void AddTouchSupportToSliders()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 获取所有滑块控件并添加触摸支持
|
||||||
|
var sliders = new List<Slider>
|
||||||
|
{
|
||||||
|
InkFadeTimeSlider,
|
||||||
|
AutoStraightenLineThresholdSlider,
|
||||||
|
LineStraightenSensitivitySlider,
|
||||||
|
LineEndpointSnappingThresholdSlider,
|
||||||
|
ViewboxFloatingBarScaleTransformValueSlider,
|
||||||
|
ViewboxFloatingBarOpacityValueSlider,
|
||||||
|
ViewboxFloatingBarOpacityInPPTValueSlider,
|
||||||
|
PPTButtonLeftPositionValueSlider,
|
||||||
|
PPTButtonRightPositionValueSlider,
|
||||||
|
PPTButtonLBPositionValueSlider,
|
||||||
|
PPTButtonRBPositionValueSlider,
|
||||||
|
TouchMultiplierSlider,
|
||||||
|
NibModeBoundsWidthSlider,
|
||||||
|
FingerModeBoundsWidthSlider,
|
||||||
|
SideControlMinimumAutomationSlider,
|
||||||
|
RandWindowOnceCloseLatencySlider,
|
||||||
|
RandWindowOnceMaxStudentsSlider,
|
||||||
|
BoardInkWidthSlider,
|
||||||
|
BoardInkAlphaSlider,
|
||||||
|
BoardHighlighterWidthSlider,
|
||||||
|
InkWidthSlider,
|
||||||
|
InkAlphaSlider,
|
||||||
|
HighlighterWidthSlider
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var slider in sliders)
|
||||||
|
{
|
||||||
|
if (slider != null)
|
||||||
|
{
|
||||||
|
AddTouchSupportToSlider(slider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogHelper.WriteLogToFile("已为所有滑块控件添加触摸支持", LogHelper.LogType.Trace);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile($"添加滑块触摸支持时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 为单个滑块控件添加触摸和手写笔事件支持
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slider">要添加触摸支持的滑块控件</param>
|
||||||
|
private void AddTouchSupportToSlider(Slider slider)
|
||||||
|
{
|
||||||
|
if (slider == null) return;
|
||||||
|
|
||||||
|
// 启用触摸和手写笔支持
|
||||||
|
slider.IsManipulationEnabled = true;
|
||||||
|
|
||||||
|
// 添加触摸事件
|
||||||
|
slider.TouchDown += Slider_TouchDown;
|
||||||
|
slider.TouchMove += Slider_TouchMove;
|
||||||
|
slider.TouchUp += Slider_TouchUp;
|
||||||
|
|
||||||
|
// 添加手写笔事件
|
||||||
|
slider.StylusDown += Slider_StylusDown;
|
||||||
|
slider.StylusMove += Slider_StylusMove;
|
||||||
|
slider.StylusUp += Slider_StylusUp;
|
||||||
|
|
||||||
|
// 添加操作事件(用于更精确的触摸控制)
|
||||||
|
slider.ManipulationStarted += Slider_ManipulationStarted;
|
||||||
|
slider.ManipulationDelta += Slider_ManipulationDelta;
|
||||||
|
slider.ManipulationCompleted += Slider_ManipulationCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 滑块触摸按下事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void Slider_TouchDown(object sender, TouchEventArgs e)
|
||||||
|
{
|
||||||
|
var slider = sender as Slider;
|
||||||
|
if (slider == null) return;
|
||||||
|
|
||||||
|
// 捕获触摸设备
|
||||||
|
slider.CaptureTouch(e.TouchDevice);
|
||||||
|
|
||||||
|
// 计算触摸位置对应的滑块值
|
||||||
|
var touchPoint = e.GetTouchPoint(slider);
|
||||||
|
UpdateSliderValueFromPosition(slider, touchPoint.Position);
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 滑块触摸移动事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void Slider_TouchMove(object sender, TouchEventArgs e)
|
||||||
|
{
|
||||||
|
var slider = sender as Slider;
|
||||||
|
if (slider == null) return;
|
||||||
|
|
||||||
|
// 计算触摸位置对应的滑块值
|
||||||
|
var touchPoint = e.GetTouchPoint(slider);
|
||||||
|
UpdateSliderValueFromPosition(slider, touchPoint.Position);
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 滑块触摸释放事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void Slider_TouchUp(object sender, TouchEventArgs e)
|
||||||
|
{
|
||||||
|
var slider = sender as Slider;
|
||||||
|
if (slider == null) return;
|
||||||
|
|
||||||
|
// 释放触摸捕获
|
||||||
|
slider.ReleaseTouchCapture(e.TouchDevice);
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 滑块手写笔按下事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void Slider_StylusDown(object sender, StylusDownEventArgs e)
|
||||||
|
{
|
||||||
|
var slider = sender as Slider;
|
||||||
|
if (slider == null) return;
|
||||||
|
|
||||||
|
// 捕获手写笔设备
|
||||||
|
slider.CaptureStylus();
|
||||||
|
|
||||||
|
// 计算手写笔位置对应的滑块值
|
||||||
|
var stylusPoint = e.GetStylusPoints(slider);
|
||||||
|
if (stylusPoint.Count > 0)
|
||||||
|
{
|
||||||
|
UpdateSliderValueFromPosition(slider, stylusPoint[0].ToPoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 滑块手写笔移动事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void Slider_StylusMove(object sender, StylusEventArgs e)
|
||||||
|
{
|
||||||
|
var slider = sender as Slider;
|
||||||
|
if (slider == null || !slider.IsStylusCaptured) return;
|
||||||
|
|
||||||
|
// 计算手写笔位置对应的滑块值
|
||||||
|
var stylusPoint = e.GetStylusPoints(slider);
|
||||||
|
if (stylusPoint.Count > 0)
|
||||||
|
{
|
||||||
|
UpdateSliderValueFromPosition(slider, stylusPoint[0].ToPoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 滑块手写笔释放事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void Slider_StylusUp(object sender, StylusEventArgs e)
|
||||||
|
{
|
||||||
|
var slider = sender as Slider;
|
||||||
|
if (slider == null) return;
|
||||||
|
|
||||||
|
// 释放手写笔捕获
|
||||||
|
slider.ReleaseStylusCapture();
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 滑块操作开始事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void Slider_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
|
||||||
|
{
|
||||||
|
var slider = sender as Slider;
|
||||||
|
if (slider == null) return;
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 滑块操作变化事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void Slider_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
|
||||||
|
{
|
||||||
|
var slider = sender as Slider;
|
||||||
|
if (slider == null) return;
|
||||||
|
|
||||||
|
// 计算操作位置对应的滑块值
|
||||||
|
var manipulationOrigin = e.ManipulationOrigin;
|
||||||
|
UpdateSliderValueFromPosition(slider, manipulationOrigin);
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 滑块操作完成事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void Slider_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
|
||||||
|
{
|
||||||
|
var slider = sender as Slider;
|
||||||
|
if (slider == null) return;
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据触摸/手写笔位置更新滑块值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slider">滑块控件</param>
|
||||||
|
/// <param name="position">触摸/手写笔位置</param>
|
||||||
|
private void UpdateSliderValueFromPosition(Slider slider, Point position)
|
||||||
|
{
|
||||||
|
if (slider == null) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 计算滑块轨道的位置和长度
|
||||||
|
var track = slider.Template.FindName("PART_Track", slider) as Track;
|
||||||
|
if (track == null) return;
|
||||||
|
|
||||||
|
var thumb = track.Thumb;
|
||||||
|
if (thumb == null) return;
|
||||||
|
|
||||||
|
// 获取滑块轨道的实际渲染位置
|
||||||
|
track.Arrange(new Rect(track.DesiredSize));
|
||||||
|
thumb.Arrange(new Rect(thumb.DesiredSize));
|
||||||
|
|
||||||
|
// 计算相对位置(0-1之间)
|
||||||
|
double relativePosition = 0;
|
||||||
|
|
||||||
|
if (slider.Orientation == System.Windows.Controls.Orientation.Horizontal)
|
||||||
|
{
|
||||||
|
// 水平滑块
|
||||||
|
var trackWidth = track.ActualWidth;
|
||||||
|
if (trackWidth > 0)
|
||||||
|
{
|
||||||
|
relativePosition = Math.Max(0, Math.Min(1, position.X / trackWidth));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 垂直滑块
|
||||||
|
var trackHeight = track.ActualHeight;
|
||||||
|
if (trackHeight > 0)
|
||||||
|
{
|
||||||
|
relativePosition = Math.Max(0, Math.Min(1, position.Y / trackHeight));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算新的滑块值
|
||||||
|
var newValue = slider.Minimum + relativePosition * (slider.Maximum - slider.Minimum);
|
||||||
|
|
||||||
|
// 如果启用了吸附到刻度,则调整到最近的刻度
|
||||||
|
if (slider.IsSnapToTickEnabled && slider.TickFrequency > 0)
|
||||||
|
{
|
||||||
|
var tickCount = (int)((slider.Maximum - slider.Minimum) / slider.TickFrequency);
|
||||||
|
var tickIndex = (int)Math.Round(relativePosition * tickCount);
|
||||||
|
newValue = slider.Minimum + tickIndex * slider.TickFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新滑块值
|
||||||
|
slider.Value = Math.Max(slider.Minimum, Math.Min(slider.Maximum, newValue));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile($"更新滑块值时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -944,7 +944,11 @@ namespace Ink_Canvas
|
|||||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||||
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
||||||
|
|
||||||
new RandWindow(Settings).Show();
|
var randWindow = new RandWindow(Settings);
|
||||||
|
randWindow.Show();
|
||||||
|
// 确保窗口显示后立即置顶
|
||||||
|
randWindow.Activate();
|
||||||
|
WindowZOrderManager.BringToTop(randWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckEraserTypeTab()
|
public void CheckEraserTypeTab()
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
// 添加窗口关闭事件处理
|
// 添加窗口关闭事件处理
|
||||||
Closed += RandWindow_Closed;
|
Closed += RandWindow_Closed;
|
||||||
|
|
||||||
|
// 添加窗口显示事件处理
|
||||||
|
Loaded += RandWindow_Loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadBackground(Settings settings)
|
private void LoadBackground(Settings settings)
|
||||||
@@ -86,6 +89,9 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
// 添加窗口关闭事件处理
|
// 添加窗口关闭事件处理
|
||||||
Closed += RandWindow_Closed;
|
Closed += RandWindow_Closed;
|
||||||
|
|
||||||
|
// 添加窗口显示事件处理
|
||||||
|
Loaded += RandWindow_Loaded;
|
||||||
|
|
||||||
new Thread(() =>
|
new Thread(() =>
|
||||||
{
|
{
|
||||||
@@ -349,6 +355,15 @@ namespace Ink_Canvas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 窗口加载事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void RandWindow_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// 窗口加载完成后,立即将其置顶
|
||||||
|
WindowZOrderManager.BringToTop(this);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 窗口关闭事件处理
|
/// 窗口关闭事件处理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -218,6 +218,9 @@ namespace Ink_Canvas.Windows {
|
|||||||
|
|
||||||
_selectedSidebarItemName = "CanvasAndInkItem";
|
_selectedSidebarItemName = "CanvasAndInkItem";
|
||||||
UpdateSidebarItemsSelection();
|
UpdateSidebarItemsSelection();
|
||||||
|
|
||||||
|
// 为自定义滑块控件添加触摸支持
|
||||||
|
AddTouchSupportToCustomSliders();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -594,5 +597,350 @@ namespace Ink_Canvas.Windows {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region 自定义滑块触摸支持
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 为自定义滑块控件添加触摸和手写笔事件支持
|
||||||
|
/// </summary>
|
||||||
|
private void AddTouchSupportToCustomSliders()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 延迟执行,确保UI元素已加载
|
||||||
|
Dispatcher.BeginInvoke(new Action(() =>
|
||||||
|
{
|
||||||
|
// 查找所有自定义滑块控件并添加触摸支持
|
||||||
|
AddTouchSupportToCustomSliderInPane(CanvasAndInkPane);
|
||||||
|
AddTouchSupportToCustomSliderInPane(ThemePane);
|
||||||
|
AddTouchSupportToCustomSliderInPane(PowerPointPane);
|
||||||
|
AddTouchSupportToCustomSliderInPane(AutomationPane);
|
||||||
|
AddTouchSupportToCustomSliderInPane(LuckyRandomPane);
|
||||||
|
AddTouchSupportToCustomSliderInPane(AdvancedPane);
|
||||||
|
}), System.Windows.Threading.DispatcherPriority.Loaded);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// 记录错误但不影响程序运行
|
||||||
|
System.Diagnostics.Debug.WriteLine($"添加自定义滑块触摸支持时出错: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 为指定面板中的自定义滑块控件添加触摸支持
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pane">面板控件</param>
|
||||||
|
private void AddTouchSupportToCustomSliderInPane(Grid pane)
|
||||||
|
{
|
||||||
|
if (pane == null) return;
|
||||||
|
|
||||||
|
// 查找面板中的所有自定义滑块控件
|
||||||
|
var customSliders = FindCustomSlidersInPanel(pane);
|
||||||
|
|
||||||
|
foreach (var slider in customSliders)
|
||||||
|
{
|
||||||
|
AddTouchSupportToCustomSlider(slider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在面板中查找自定义滑块控件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="panel">面板控件</param>
|
||||||
|
/// <returns>自定义滑块控件列表</returns>
|
||||||
|
private List<CustomSliderInfo> FindCustomSlidersInPanel(DependencyObject panel)
|
||||||
|
{
|
||||||
|
var customSliders = new List<CustomSliderInfo>();
|
||||||
|
|
||||||
|
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(panel); i++)
|
||||||
|
{
|
||||||
|
var child = VisualTreeHelper.GetChild(panel, i);
|
||||||
|
|
||||||
|
// 检查是否是自定义滑块控件(包含GnomeSliderThumb图片的Grid)
|
||||||
|
if (child is Grid grid)
|
||||||
|
{
|
||||||
|
var customSlider = FindCustomSliderInGrid(grid);
|
||||||
|
if (customSlider != null)
|
||||||
|
{
|
||||||
|
customSliders.Add(customSlider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归查找子元素
|
||||||
|
customSliders.AddRange(FindCustomSlidersInPanel(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
return customSliders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在Grid中查找自定义滑块控件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="grid">Grid控件</param>
|
||||||
|
/// <returns>自定义滑块信息</returns>
|
||||||
|
private CustomSliderInfo FindCustomSliderInGrid(Grid grid)
|
||||||
|
{
|
||||||
|
// 查找包含GnomeSliderThumb图片的Grid
|
||||||
|
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(grid); i++)
|
||||||
|
{
|
||||||
|
var child = VisualTreeHelper.GetChild(grid, i);
|
||||||
|
|
||||||
|
if (child is Image image && image.Source != null)
|
||||||
|
{
|
||||||
|
var sourceName = image.Source.ToString();
|
||||||
|
if (sourceName.Contains("GnomeSliderThumb"))
|
||||||
|
{
|
||||||
|
// 找到滑块控件,创建自定义滑块信息
|
||||||
|
var customSlider = new CustomSliderInfo
|
||||||
|
{
|
||||||
|
Container = grid,
|
||||||
|
ThumbImage = image,
|
||||||
|
TrackBorder = FindTrackBorderInGrid(grid),
|
||||||
|
ValueBorder = FindValueBorderInGrid(grid)
|
||||||
|
};
|
||||||
|
|
||||||
|
return customSlider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在Grid中查找轨道Border
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="grid">Grid控件</param>
|
||||||
|
/// <returns>轨道Border</returns>
|
||||||
|
private Border FindTrackBorderInGrid(Grid grid)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(grid); i++)
|
||||||
|
{
|
||||||
|
var child = VisualTreeHelper.GetChild(grid, i);
|
||||||
|
|
||||||
|
if (child is Border border && border.Background != null)
|
||||||
|
{
|
||||||
|
var brush = border.Background as SolidColorBrush;
|
||||||
|
if (brush != null && brush.Color.ToString() == "#FFDEDEDE")
|
||||||
|
{
|
||||||
|
return border;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在Grid中查找值显示Border
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="grid">Grid控件</param>
|
||||||
|
/// <returns>值显示Border</returns>
|
||||||
|
private Border FindValueBorderInGrid(Grid grid)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(grid); i++)
|
||||||
|
{
|
||||||
|
var child = VisualTreeHelper.GetChild(grid, i);
|
||||||
|
|
||||||
|
if (child is Border border && border.Background != null)
|
||||||
|
{
|
||||||
|
var brush = border.Background as SolidColorBrush;
|
||||||
|
if (brush != null && brush.Color.ToString() == "#FF3584E4")
|
||||||
|
{
|
||||||
|
return border;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 为自定义滑块控件添加触摸支持
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="customSlider">自定义滑块信息</param>
|
||||||
|
private void AddTouchSupportToCustomSlider(CustomSliderInfo customSlider)
|
||||||
|
{
|
||||||
|
if (customSlider?.Container == null) return;
|
||||||
|
|
||||||
|
// 启用触摸和手写笔支持
|
||||||
|
customSlider.Container.IsManipulationEnabled = true;
|
||||||
|
|
||||||
|
// 添加触摸事件
|
||||||
|
customSlider.Container.TouchDown += (s, e) => CustomSlider_TouchDown(s, e, customSlider);
|
||||||
|
customSlider.Container.TouchMove += (s, e) => CustomSlider_TouchMove(s, e, customSlider);
|
||||||
|
customSlider.Container.TouchUp += (s, e) => CustomSlider_TouchUp(s, e, customSlider);
|
||||||
|
|
||||||
|
// 添加手写笔事件
|
||||||
|
customSlider.Container.StylusDown += (s, e) => CustomSlider_StylusDown(s, e, customSlider);
|
||||||
|
customSlider.Container.StylusMove += (s, e) => CustomSlider_StylusMove(s, e, customSlider);
|
||||||
|
customSlider.Container.StylusUp += (s, e) => CustomSlider_StylusUp(s, e, customSlider);
|
||||||
|
|
||||||
|
// 添加操作事件
|
||||||
|
customSlider.Container.ManipulationStarted += (s, e) => CustomSlider_ManipulationStarted(s, e, customSlider);
|
||||||
|
customSlider.Container.ManipulationDelta += (s, e) => CustomSlider_ManipulationDelta(s, e, customSlider);
|
||||||
|
customSlider.Container.ManipulationCompleted += (s, e) => CustomSlider_ManipulationCompleted(s, e, customSlider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义滑块触摸按下事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void CustomSlider_TouchDown(object sender, TouchEventArgs e, CustomSliderInfo customSlider)
|
||||||
|
{
|
||||||
|
customSlider.Container.CaptureTouch(e.TouchDevice);
|
||||||
|
customSlider.IsTouchCaptured = true;
|
||||||
|
var touchPoint = e.GetTouchPoint(customSlider.Container);
|
||||||
|
UpdateCustomSliderValueFromPosition(customSlider, touchPoint.Position);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义滑块触摸移动事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void CustomSlider_TouchMove(object sender, TouchEventArgs e, CustomSliderInfo customSlider)
|
||||||
|
{
|
||||||
|
// 检查是否有触摸捕获
|
||||||
|
if (!customSlider.IsTouchCaptured) return;
|
||||||
|
var touchPoint = e.GetTouchPoint(customSlider.Container);
|
||||||
|
UpdateCustomSliderValueFromPosition(customSlider, touchPoint.Position);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义滑块触摸释放事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void CustomSlider_TouchUp(object sender, TouchEventArgs e, CustomSliderInfo customSlider)
|
||||||
|
{
|
||||||
|
customSlider.Container.ReleaseTouchCapture(e.TouchDevice);
|
||||||
|
customSlider.IsTouchCaptured = false;
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义滑块手写笔按下事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void CustomSlider_StylusDown(object sender, StylusDownEventArgs e, CustomSliderInfo customSlider)
|
||||||
|
{
|
||||||
|
customSlider.Container.CaptureStylus();
|
||||||
|
var stylusPoint = e.GetStylusPoints(customSlider.Container);
|
||||||
|
if (stylusPoint.Count > 0)
|
||||||
|
{
|
||||||
|
UpdateCustomSliderValueFromPosition(customSlider, stylusPoint[0].ToPoint());
|
||||||
|
}
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义滑块手写笔移动事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void CustomSlider_StylusMove(object sender, StylusEventArgs e, CustomSliderInfo customSlider)
|
||||||
|
{
|
||||||
|
if (!customSlider.Container.IsStylusCaptured) return;
|
||||||
|
var stylusPoint = e.GetStylusPoints(customSlider.Container);
|
||||||
|
if (stylusPoint.Count > 0)
|
||||||
|
{
|
||||||
|
UpdateCustomSliderValueFromPosition(customSlider, stylusPoint[0].ToPoint());
|
||||||
|
}
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义滑块手写笔释放事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void CustomSlider_StylusUp(object sender, StylusEventArgs e, CustomSliderInfo customSlider)
|
||||||
|
{
|
||||||
|
customSlider.Container.ReleaseStylusCapture();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义滑块操作开始事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void CustomSlider_ManipulationStarted(object sender, ManipulationStartedEventArgs e, CustomSliderInfo customSlider)
|
||||||
|
{
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义滑块操作变化事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void CustomSlider_ManipulationDelta(object sender, ManipulationDeltaEventArgs e, CustomSliderInfo customSlider)
|
||||||
|
{
|
||||||
|
var manipulationOrigin = e.ManipulationOrigin;
|
||||||
|
UpdateCustomSliderValueFromPosition(customSlider, manipulationOrigin);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义滑块操作完成事件处理
|
||||||
|
/// </summary>
|
||||||
|
private void CustomSlider_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e, CustomSliderInfo customSlider)
|
||||||
|
{
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据触摸/手写笔位置更新自定义滑块值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="customSlider">自定义滑块信息</param>
|
||||||
|
/// <param name="position">触摸/手写笔位置</param>
|
||||||
|
private void UpdateCustomSliderValueFromPosition(CustomSliderInfo customSlider, Point position)
|
||||||
|
{
|
||||||
|
if (customSlider?.TrackBorder == null || customSlider.ThumbImage == null) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 计算滑块轨道的实际位置和长度
|
||||||
|
var trackWidth = customSlider.TrackBorder.ActualWidth;
|
||||||
|
if (trackWidth <= 0) return;
|
||||||
|
|
||||||
|
// 计算相对位置(0-1之间)
|
||||||
|
var relativePosition = Math.Max(0, Math.Min(1, position.X / trackWidth));
|
||||||
|
|
||||||
|
// 更新滑块位置
|
||||||
|
var thumbTransform = customSlider.ThumbImage.RenderTransform as TranslateTransform;
|
||||||
|
if (thumbTransform == null)
|
||||||
|
{
|
||||||
|
thumbTransform = new TranslateTransform();
|
||||||
|
customSlider.ThumbImage.RenderTransform = thumbTransform;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算新的滑块位置
|
||||||
|
var newX = relativePosition * trackWidth;
|
||||||
|
thumbTransform.X = newX;
|
||||||
|
|
||||||
|
// 更新值显示Border的宽度
|
||||||
|
if (customSlider.ValueBorder != null)
|
||||||
|
{
|
||||||
|
var valueWidth = relativePosition * trackWidth;
|
||||||
|
customSlider.ValueBorder.Width = Math.Max(0, valueWidth);
|
||||||
|
|
||||||
|
// 调整值显示Border的位置
|
||||||
|
var valueMargin = customSlider.ValueBorder.Margin;
|
||||||
|
customSlider.ValueBorder.Margin = new Thickness(0, valueMargin.Top, trackWidth - valueWidth, valueMargin.Bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这里可以根据需要添加值变化事件处理
|
||||||
|
// 例如:OnCustomSliderValueChanged(customSlider, relativePosition);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"更新自定义滑块值时出错: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义滑块信息类
|
||||||
|
/// </summary>
|
||||||
|
private class CustomSliderInfo
|
||||||
|
{
|
||||||
|
public Grid Container { get; set; }
|
||||||
|
public Image ThumbImage { get; set; }
|
||||||
|
public Border TrackBorder { get; set; }
|
||||||
|
public Border ValueBorder { get; set; }
|
||||||
|
public bool IsTouchCaptured { get; set; } = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user