Merge branch 'net6' into net462

This commit is contained in:
doudou0720
2026-05-02 10:16:15 +08:00
37 changed files with 1933 additions and 603 deletions
+2 -1
View File
@@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
xmlns:i18n="clr-namespace:Ink_Canvas.MarkupExtensions"
mc:Ignorable="d"
d:DesignHeight="68"
d:DesignWidth="600">
@@ -20,7 +21,7 @@
FontFamily="Consolas"
FontWeight="SemiBold"
Click="BtnSetHotkey_Click">
<TextBlock x:Name="CurrentHotkeyTextBlock" Text="未设置" />
<TextBlock x:Name="CurrentHotkeyTextBlock" Text="{i18n:I18n Key=Hotkey_NotSet}" />
</Button>
</ui:SettingsCard>
</UserControl>
+2 -1
View File
@@ -1,3 +1,4 @@
using Ink_Canvas.Helpers;
using System;
using System.Windows;
using System.Windows.Controls;
@@ -68,7 +69,7 @@ namespace Ink_Canvas.Windows
{
if (_currentKey == Key.None)
{
CurrentHotkeyTextBlock.Text = "未设置";
CurrentHotkeyTextBlock.Text = LocalizationHelper.GetString("Hotkey_NotSet");
}
else
{
+513
View File
@@ -0,0 +1,513 @@
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace Ink_Canvas.Windows
{
/// <summary>
/// 使用 Win32 Magnification API (Magnification.dll)。
/// </summary>
internal class MagnifierWindow : Window
{
#region P/Invoke
private const string MagnifierClassName = "Magnifier";
private const int WS_CHILD = 0x40000000;
private const int WS_VISIBLE = 0x10000000;
[StructLayout(LayoutKind.Sequential)]
private struct RECT { public int left, top, right, bottom; public RECT(int l, int t, int r, int b) { left = l; top = t; right = r; bottom = b; } }
[StructLayout(LayoutKind.Sequential)]
private struct MAGTRANSFORM
{
public float m00, m01, m02;
public float m10, m11, m12;
public float m20, m21, m22;
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr CreateWindowEx(int dwExStyle, string lpClassName, string lpWindowName, int dwStyle,
int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
[DllImport("user32.dll")] private static extern bool DestroyWindow(IntPtr hWnd);
[DllImport("user32.dll")] private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int w, int h, bool repaint);
[DllImport("user32.dll")] private static extern bool InvalidateRect(IntPtr hWnd, IntPtr lpRect, bool bErase);
[DllImport("Magnification.dll")] private static extern bool MagInitialize();
[DllImport("Magnification.dll")] private static extern bool MagUninitialize();
[DllImport("Magnification.dll")] private static extern bool MagSetWindowSource(IntPtr hwnd, RECT rect);
[DllImport("Magnification.dll")] private static extern bool MagSetWindowTransform(IntPtr hwnd, ref MAGTRANSFORM pTransform);
[DllImport("Magnification.dll")] private static extern bool MagSetWindowFilterList(IntPtr hwnd, int dwFilterMode, int count, IntPtr[] pHWND);
private const int MW_FILTERMODE_EXCLUDE = 0;
#endregion
#region
private static MagnifierWindow _instance;
public static bool HasInstance => _instance != null;
public static event EventHandler Closed2;
public static void Show(float zoom)
{
if (_instance != null) { _instance.Activate(); _instance.Zoom = zoom; return; }
_instance = new MagnifierWindow { Zoom = zoom };
_instance.Closed += (s, e) =>
{
_instance = null;
Closed2?.Invoke(null, EventArgs.Empty);
};
_instance.Show();
}
public static void HideInstance() => _instance?.Close();
public static void SetZoom(float zoom) { if (_instance != null) _instance.Zoom = zoom; }
#endregion
// 选择框(放大区域)在 Canvas 中的几何属性 (DIP)
private double _boxLeft = 200;
private double _boxTop = 150;
private double _boxWidth = 520;
private double _boxHeight = 360;
private const double MinBoxW = 200;
private const double MinBoxH = 140;
private System.Windows.Controls.Canvas _canvas;
private Rectangle _overlay; // 半透明遮罩
private Border _selectionBorder; // 白色边框选择框
private MagnifierHost _magHost;
private System.Windows.Controls.Border _toolbar;
private Ellipse[] _handles; // 8 个控点
private ToggleButton _blackoutButton;
private bool _blackoutOn;
private IntPtr _magHwnd;
private HwndSource _hwndSource;
private DispatcherTimer _timer;
private bool _magInitialized;
private float _zoom = 2.0f;
public float Zoom
{
get => _zoom;
set
{
_zoom = Math.Max(1.1f, Math.Min(8.0f, value));
if (_magHwnd != IntPtr.Zero) ApplyTransform();
}
}
private MagnifierWindow()
{
Title = "聚焦放大镜";
WindowStyle = WindowStyle.None;
ResizeMode = ResizeMode.NoResize;
AllowsTransparency = true;
Background = Brushes.Transparent;
Topmost = true;
ShowInTaskbar = false;
WindowState = WindowState.Maximized;
_canvas = new System.Windows.Controls.Canvas { Background = Brushes.Transparent };
// 半透明遮罩:固定浅色,挖空选择框
_overlay = new Rectangle
{
Fill = new SolidColorBrush(Color.FromArgb(102, 0, 0, 0)),
IsHitTestVisible = false
};
_canvas.Children.Add(_overlay);
// 选择框
_selectionBorder = new Border
{
BorderBrush = Brushes.White,
BorderThickness = new Thickness(1.5),
Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0)), // 几乎透明,可命中
Cursor = Cursors.SizeAll,
SnapsToDevicePixels = true
};
_selectionBorder.MouseLeftButtonDown += SelectionBorder_MouseDown;
_selectionBorder.MouseMove += SelectionBorder_MouseMove;
_selectionBorder.MouseLeftButtonUp += SelectionBorder_MouseUp;
_magHost = new MagnifierHost(this);
_selectionBorder.Child = _magHost;
_canvas.Children.Add(_selectionBorder);
// 8 个控点
_handles = new Ellipse[8];
string[] tags = { "NW", "N", "NE", "E", "SE", "S", "SW", "W" };
Cursor[] cursors = { Cursors.SizeNWSE, Cursors.SizeNS, Cursors.SizeNESW, Cursors.SizeWE,
Cursors.SizeNWSE, Cursors.SizeNS, Cursors.SizeNESW, Cursors.SizeWE };
for (int i = 0; i < 8; i++)
{
var h = new Ellipse
{
Width = 12,
Height = 12,
Fill = Brushes.White,
Stroke = new SolidColorBrush(Color.FromArgb(220, 0, 0, 0)),
StrokeThickness = 1,
Cursor = cursors[i],
Tag = tags[i]
};
h.MouseLeftButtonDown += Handle_MouseDown;
h.MouseMove += Handle_MouseMove;
h.MouseLeftButtonUp += Handle_MouseUp;
_handles[i] = h;
_canvas.Children.Add(h);
}
_toolbar = BuildToolbar();
_canvas.Children.Add(_toolbar);
Content = _canvas;
Loaded += (s, e) =>
{
LayoutAll();
};
SizeChanged += (s, e) => LayoutAll();
KeyDown += (s, e) => { if (e.Key == Key.Escape) Close(); };
}
private System.Windows.Controls.Border BuildToolbar()
{
var bar = new System.Windows.Controls.Border
{
Background = new SolidColorBrush(Color.FromArgb(245, 26, 26, 26)),
CornerRadius = new CornerRadius(8),
Padding = new Thickness(8, 4, 8, 4),
SnapsToDevicePixels = true
};
var sp = new StackPanel { Orientation = Orientation.Horizontal };
_blackoutButton = new ToggleButton { Content = MakeBtnContent("💡", "关灯") };
StyleToolButton(_blackoutButton);
_blackoutButton.Checked += (s, e) => SetBlackout(true);
_blackoutButton.Unchecked += (s, e) => SetBlackout(false);
var closeBtn = new System.Windows.Controls.Button { Content = MakeBtnContent("✕", "关闭") };
StyleToolButton(closeBtn);
closeBtn.Click += (s, e) => Close();
sp.Children.Add(_blackoutButton);
sp.Children.Add(closeBtn);
bar.Child = sp;
return bar;
}
private static FrameworkElement MakeBtnContent(string icon, string text)
{
var sp = new StackPanel { Orientation = Orientation.Vertical, HorizontalAlignment = HorizontalAlignment.Center };
sp.Children.Add(new TextBlock { Text = icon, FontSize = 16, Foreground = Brushes.White, HorizontalAlignment = HorizontalAlignment.Center });
sp.Children.Add(new TextBlock { Text = text, FontSize = 11, Foreground = Brushes.White, HorizontalAlignment = HorizontalAlignment.Center, Margin = new Thickness(0, 1, 0, 0) });
return sp;
}
private static void StyleToolButton(ButtonBase b)
{
b.Width = 56;
b.Height = 46;
b.Margin = new Thickness(3, 0, 3, 0);
b.Background = Brushes.Transparent;
b.BorderThickness = new Thickness(0);
b.Foreground = Brushes.White;
b.Cursor = Cursors.Hand;
b.Padding = new Thickness(0);
b.Template = BuildToolButtonTemplate();
}
private static ControlTemplate BuildToolButtonTemplate()
{
var t = new ControlTemplate(typeof(ButtonBase));
var border = new FrameworkElementFactory(typeof(System.Windows.Controls.Border));
border.Name = "Bd";
border.SetValue(System.Windows.Controls.Border.BackgroundProperty, new TemplateBindingExtension(System.Windows.Controls.Control.BackgroundProperty));
border.SetValue(System.Windows.Controls.Border.CornerRadiusProperty, new CornerRadius(6));
var cp = new FrameworkElementFactory(typeof(ContentPresenter));
cp.SetValue(ContentPresenter.HorizontalAlignmentProperty, HorizontalAlignment.Center);
cp.SetValue(ContentPresenter.VerticalAlignmentProperty, VerticalAlignment.Center);
border.AppendChild(cp);
t.VisualTree = border;
var hover = new Trigger { Property = UIElement.IsMouseOverProperty, Value = true };
hover.Setters.Add(new Setter(System.Windows.Controls.Border.BackgroundProperty, new SolidColorBrush(Color.FromArgb(60, 255, 255, 255)), "Bd"));
t.Triggers.Add(hover);
var checkedT = new Trigger { Property = ToggleButton.IsCheckedProperty, Value = true };
checkedT.Setters.Add(new Setter(System.Windows.Controls.Border.BackgroundProperty, new SolidColorBrush(Color.FromRgb(0x05, 0x96, 0x69)), "Bd"));
t.Triggers.Add(checkedT);
return t;
}
#region
private void LayoutAll()
{
double w = _canvas.ActualWidth;
double h = _canvas.ActualHeight;
if (w <= 0 || h <= 0) return;
// 首次居中
if (_boxLeft + _boxWidth > w) _boxLeft = Math.Max(0, w - _boxWidth);
if (_boxTop + _boxHeight > h) _boxTop = Math.Max(0, h - _boxHeight);
// 遮罩占满
_overlay.Width = w;
_overlay.Height = h;
System.Windows.Controls.Canvas.SetLeft(_overlay, 0);
System.Windows.Controls.Canvas.SetTop(_overlay, 0);
UpdateOverlayClip();
// 选择框
_selectionBorder.Width = _boxWidth;
_selectionBorder.Height = _boxHeight;
System.Windows.Controls.Canvas.SetLeft(_selectionBorder, _boxLeft);
System.Windows.Controls.Canvas.SetTop(_selectionBorder, _boxTop);
// 8 个控点(中心定位)
PositionHandle(0, _boxLeft, _boxTop); // NW
PositionHandle(1, _boxLeft + _boxWidth / 2, _boxTop); // N
PositionHandle(2, _boxLeft + _boxWidth, _boxTop); // NE
PositionHandle(3, _boxLeft + _boxWidth, _boxTop + _boxHeight / 2); // E
PositionHandle(4, _boxLeft + _boxWidth, _boxTop + _boxHeight); // SE
PositionHandle(5, _boxLeft + _boxWidth / 2, _boxTop + _boxHeight); // S
PositionHandle(6, _boxLeft, _boxTop + _boxHeight); // SW
PositionHandle(7, _boxLeft, _boxTop + _boxHeight / 2); // W
// 工具栏:选择框正下方居中;若超出则放上方
_toolbar.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
double tw = _toolbar.DesiredSize.Width, th = _toolbar.DesiredSize.Height;
double tx = _boxLeft + _boxWidth / 2 - tw / 2;
double ty = _boxTop + _boxHeight + 12;
if (ty + th > h) ty = _boxTop - th - 12;
tx = Math.Max(8, Math.Min(w - tw - 8, tx));
System.Windows.Controls.Canvas.SetLeft(_toolbar, tx);
System.Windows.Controls.Canvas.SetTop(_toolbar, ty);
}
private void PositionHandle(int i, double cx, double cy)
{
var dot = _handles[i];
System.Windows.Controls.Canvas.SetLeft(dot, cx - dot.Width / 2);
System.Windows.Controls.Canvas.SetTop(dot, cy - dot.Height / 2);
}
private void UpdateOverlayClip()
{
// 用 EvenOdd Geometry 在 overlay 上挖空选择框区域
double w = _canvas.ActualWidth;
double h = _canvas.ActualHeight;
if (w <= 0 || h <= 0) { _overlay.Clip = null; return; }
var outer = new RectangleGeometry(new Rect(0, 0, w, h));
var inner = new RectangleGeometry(new Rect(_boxLeft, _boxTop, _boxWidth, _boxHeight));
var combined = new CombinedGeometry(GeometryCombineMode.Exclude, outer, inner);
_overlay.Clip = combined;
}
#endregion
#region
private bool _draggingBox;
private Point _dragStart;
private double _dragBoxLeft, _dragBoxTop;
private void SelectionBorder_MouseDown(object sender, MouseButtonEventArgs e)
{
_draggingBox = true;
_dragStart = e.GetPosition(_canvas);
_dragBoxLeft = _boxLeft;
_dragBoxTop = _boxTop;
_selectionBorder.CaptureMouse();
e.Handled = true;
}
private void SelectionBorder_MouseMove(object sender, MouseEventArgs e)
{
if (!_draggingBox) return;
var p = e.GetPosition(_canvas);
_boxLeft = Math.Max(0, Math.Min(_canvas.ActualWidth - _boxWidth, _dragBoxLeft + p.X - _dragStart.X));
_boxTop = Math.Max(0, Math.Min(_canvas.ActualHeight - _boxHeight, _dragBoxTop + p.Y - _dragStart.Y));
LayoutAll();
}
private void SelectionBorder_MouseUp(object sender, MouseButtonEventArgs e)
{
if (!_draggingBox) return;
_draggingBox = false;
_selectionBorder.ReleaseMouseCapture();
}
#endregion
#region
private bool _resizing;
private string _resizeTag;
private Point _resizeStart;
private double _rL, _rT, _rW, _rH;
private void Handle_MouseDown(object sender, MouseButtonEventArgs e)
{
var el = (FrameworkElement)sender;
_resizing = true;
_resizeTag = (string)el.Tag;
_resizeStart = e.GetPosition(_canvas);
_rL = _boxLeft; _rT = _boxTop; _rW = _boxWidth; _rH = _boxHeight;
el.CaptureMouse();
e.Handled = true;
}
private void Handle_MouseMove(object sender, MouseEventArgs e)
{
if (!_resizing) return;
var p = e.GetPosition(_canvas);
double dx = p.X - _resizeStart.X;
double dy = p.Y - _resizeStart.Y;
double nL = _rL, nT = _rT, nW = _rW, nH = _rH;
switch (_resizeTag)
{
case "NW": nL = _rL + dx; nT = _rT + dy; nW = _rW - dx; nH = _rH - dy; break;
case "N": nT = _rT + dy; nH = _rH - dy; break;
case "NE": nT = _rT + dy; nW = _rW + dx; nH = _rH - dy; break;
case "E": nW = _rW + dx; break;
case "SE": nW = _rW + dx; nH = _rH + dy; break;
case "S": nH = _rH + dy; break;
case "SW": nL = _rL + dx; nW = _rW - dx; nH = _rH + dy; break;
case "W": nL = _rL + dx; nW = _rW - dx; break;
}
if (nW < MinBoxW) { if (_resizeTag.Contains("W")) nL -= MinBoxW - nW; nW = MinBoxW; }
if (nH < MinBoxH) { if (_resizeTag.Contains("N")) nT -= MinBoxH - nH; nH = MinBoxH; }
// 限制在窗口内
nL = Math.Max(0, nL);
nT = Math.Max(0, nT);
if (nL + nW > _canvas.ActualWidth) nW = _canvas.ActualWidth - nL;
if (nT + nH > _canvas.ActualHeight) nH = _canvas.ActualHeight - nT;
_boxLeft = nL; _boxTop = nT; _boxWidth = nW; _boxHeight = nH;
LayoutAll();
}
private void Handle_MouseUp(object sender, MouseButtonEventArgs e)
{
if (!_resizing) return;
_resizing = false;
((FrameworkElement)sender).ReleaseMouseCapture();
}
#endregion
#region
private void SetBlackout(bool on)
{
_blackoutOn = on;
_overlay.Fill = new SolidColorBrush(on
? Color.FromArgb(245, 0, 0, 0)
: Color.FromArgb(102, 0, 0, 0));
}
#endregion
#region Magnifier
private class MagnifierHost : HwndHost
{
private readonly MagnifierWindow _owner;
public IntPtr MagHwnd { get; private set; }
public MagnifierHost(MagnifierWindow owner) { _owner = owner; }
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
if (!MagInitialize()) throw new InvalidOperationException("MagInitialize 失败");
_owner._magInitialized = true;
MagHwnd = CreateWindowEx(
0, MagnifierClassName, "ICCMagnifier",
WS_CHILD | WS_VISIBLE,
0, 0, 100, 100,
hwndParent.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
_owner._magHwnd = MagHwnd;
return new HandleRef(this, MagHwnd);
}
protected override void DestroyWindowCore(HandleRef hwnd)
{
if (hwnd.Handle != IntPtr.Zero) DestroyWindow(hwnd.Handle);
MagHwnd = IntPtr.Zero;
}
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
_hwndSource = (HwndSource)PresentationSource.FromVisual(this);
ApplyTransform();
if (_hwndSource != null && _magHwnd != IntPtr.Zero)
MagSetWindowFilterList(_magHwnd, MW_FILTERMODE_EXCLUDE, 1, new[] { _hwndSource.Handle });
_timer = new DispatcherTimer(DispatcherPriority.Render) { Interval = TimeSpan.FromMilliseconds(33) };
_timer.Tick += OnTick;
_timer.Start();
}
private void ApplyTransform()
{
if (_magHwnd == IntPtr.Zero) return;
var m = new MAGTRANSFORM
{
m00 = _zoom, m01 = 0, m02 = 0,
m10 = 0, m11 = _zoom, m12 = 0,
m20 = 0, m21 = 0, m22 = 1.0f
};
MagSetWindowTransform(_magHwnd, ref m);
}
private void OnTick(object sender, EventArgs e)
{
if (_magHwnd == IntPtr.Zero || _magHost == null) return;
if (_magHost.RenderSize.Width <= 0) return;
// 选择框在屏幕物理像素坐标
var hostOffset = _magHost.TransformToAncestor(this).Transform(new Point(0, 0));
var tl = PointToScreen(hostOffset);
var br = PointToScreen(new Point(hostOffset.X + _magHost.RenderSize.Width,
hostOffset.Y + _magHost.RenderSize.Height));
int viewW = (int)(br.X - tl.X);
int viewH = (int)(br.Y - tl.Y);
if (viewW <= 0 || viewH <= 0) return;
int srcW = Math.Max(1, (int)(viewW / _zoom));
int srcH = Math.Max(1, (int)(viewH / _zoom));
int cx = (int)((tl.X + br.X) / 2);
int cy = (int)((tl.Y + br.Y) / 2);
var src = new RECT(cx - srcW / 2, cy - srcH / 2,
cx - srcW / 2 + srcW, cy - srcH / 2 + srcH);
MagSetWindowSource(_magHwnd, src);
InvalidateRect(_magHwnd, IntPtr.Zero, false);
}
#endregion
protected override void OnClosed(EventArgs e)
{
if (_timer != null) { _timer.Stop(); _timer.Tick -= OnTick; _timer = null; }
if (_magInitialized) { MagUninitialize(); _magInitialized = false; }
base.OnClosed(e);
}
}
}
+258 -163
View File
@@ -3,69 +3,127 @@
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"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="300">
d:DesignHeight="420" d:DesignWidth="260">
<UserControl.Resources>
<ResourceDictionary>
<!-- 展开动画 -->
<!-- 展开 / 折叠动画 -->
<Storyboard x:Key="ExpandAnimation">
<DoubleAnimation Storyboard.TargetName="PanelTransform"
Storyboard.TargetProperty="X"
Duration="0:0:0.3">
Storyboard.TargetProperty="X"
Duration="0:0:0.28">
<DoubleAnimation.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
<!-- 折叠动画 -->
<Storyboard x:Key="CollapseAnimation">
<DoubleAnimation Storyboard.TargetName="PanelTransform"
Storyboard.TargetProperty="X"
Duration="0:0:0.3">
Storyboard.TargetProperty="X"
Duration="0:0:0.28">
<DoubleAnimation.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
<CubicEase EasingMode="EaseIn"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
<!-- Game Bar 风格扁平按钮 -->
<Style x:Key="GameBarFlatButton" TargetType="Button">
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="10,0"/>
<Setter Property="Height" Value="34"/>
<Setter Property="Background" Value="#28FFFFFF"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Bd"
Background="{TemplateBinding Background}"
CornerRadius="6"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#40FFFFFF"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#55FFFFFF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- 区段标题 -->
<Style x:Key="GameBarSectionTitle" TargetType="TextBlock">
<Setter Property="FontSize" Value="10"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Opacity" Value="0.7"/>
<Setter Property="Margin" Value="2,0,0,4"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Canvas x:Name="MainCanvas" HorizontalAlignment="Right" VerticalAlignment="Top">
<!-- 主面板容器 -->
<Grid x:Name="MainPanel" Canvas.Right="0" Canvas.Top="50">
<Grid.RenderTransform>
<TranslateTransform x:Name="PanelTransform" X="0"/>
</Grid.RenderTransform>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24"/>
<ColumnDefinition Width="20"/>
<!-- 内容区域:宽度必须为 200 (与 _collapsedOffset 对应) -->
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<!-- 左侧圆角矩形 - 箭头按钮区域 -->
<Border x:Name="ArrowButtonBorder"
<!-- 内容区域外壳:圆角 + 背景 + 阴影 (只覆盖内容列,折叠后完全滑出视区) -->
<Border Grid.Column="1"
CornerRadius="12">
<Border.Background>
<SolidColorBrush x:Name="ContentBackgroundBrush" Color="#CC1F1F1F"/>
</Border.Background>
<Border.Effect>
<DropShadowEffect BlurRadius="18" ShadowDepth="0" Opacity="0.35" Color="Black"/>
</Border.Effect>
</Border>
<!-- 左侧箭头交互区(独立小胶囊,折叠后仍可见) -->
<Border x:Name="ArrowButtonBorder"
Grid.Column="0"
CornerRadius="6,0,0,6"
Width="14"
Height="44"
HorizontalAlignment="Center"
VerticalAlignment="Center"
CornerRadius="7"
Cursor="Hand"
MouseLeftButtonDown="ArrowButton_MouseLeftButtonDown"
MouseEnter="ArrowButton_MouseEnter"
MouseLeave="ArrowButton_MouseLeave">
<Border.Background>
<SolidColorBrush x:Name="ArrowButtonBackgroundBrush" Color="#CCFFFFFF"/>
<SolidColorBrush x:Name="ArrowButtonBackgroundBrush" Color="#CC1F1F1F"/>
</Border.Background>
<Border.Effect>
<DropShadowEffect BlurRadius="6" ShadowDepth="0" Opacity="0.2" Color="Black"/>
<DropShadowEffect BlurRadius="8" ShadowDepth="0" Opacity="0.3" Color="Black"/>
</Border.Effect>
<Viewbox Width="12" Height="12" VerticalAlignment="Center" HorizontalAlignment="Center">
<Path x:Name="ArrowPath"
Data="M 0,0 L 12,6 L 0,12 Z"
<Viewbox Width="7" Height="7"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<Path x:Name="ArrowPath"
Data="M 0,0 L 12,6 L 0,12 Z"
Stretch="Uniform"
RenderTransformOrigin="0.5,0.5">
<Path.Fill>
<SolidColorBrush x:Name="ArrowPathFillBrush" Color="#FF000000"/>
<SolidColorBrush x:Name="ArrowPathFillBrush" Color="White"/>
</Path.Fill>
<Path.RenderTransform>
<RotateTransform x:Name="ArrowRotateTransform" Angle="180"/>
@@ -73,72 +131,93 @@
</Path>
</Viewbox>
</Border>
<!-- 右侧矩形 - 面板内容区域 -->
<Border x:Name="ContentBorder"
<!-- 内容区域(透明,仅承担事件与裁剪) -->
<Border x:Name="ContentBorder"
Grid.Column="1"
CornerRadius="0"
Background="Transparent"
CornerRadius="0,12,12,0"
MouseLeftButtonDown="ContentBorder_MouseLeftButtonDown"
MouseMove="ContentBorder_MouseMove"
MouseLeftButtonUp="ContentBorder_MouseLeftButtonUp"
TouchDown="ContentBorder_TouchDown"
TouchMove="ContentBorder_TouchMove"
TouchUp="ContentBorder_TouchUp">
<Border.Background>
<SolidColorBrush x:Name="ContentBackgroundBrush" Color="#CCFFFFFF"/>
</Border.Background>
<Border.Effect>
<DropShadowEffect BlurRadius="8" ShadowDepth="0" Opacity="0.2" Color="Black"/>
</Border.Effect>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel x:Name="ContentPanel" Margin="10,8" Orientation="Vertical">
<!-- 系统音量控制区域 -->
<Grid Margin="0,0,0,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- 左侧音量按钮 -->
<Button x:Name="VolumeMuteButton"
Grid.Column="0"
Width="32" Height="32"
Background="Transparent"
BorderThickness="0"
Cursor="Hand"
Click="VolumeMuteButton_Click"
Margin="0,0,8,0"
Padding="0"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<Viewbox Width="20" Height="20" Stretch="Uniform">
<Grid>
<Path x:Name="VolumeIconPath"
Data="M 3,9 v 6 h 4 l 5,5 v -16 l -5,5 h -4 z"
Stretch="Uniform">
<Path.Fill>
<SolidColorBrush x:Name="VolumeIconFillBrush" Color="#FF000000"/>
</Path.Fill>
</Path>
<Path x:Name="VolumeIconPath2"
Data=""
Stretch="Uniform"
Opacity="0.5"
Visibility="Collapsed">
<Path.Fill>
<SolidColorBrush x:Name="VolumeIconFillBrush2" Color="#FF000000"/>
</Path.Fill>
</Path>
</Grid>
</Viewbox>
</Button>
<!-- 中间音量滑块 -->
<Grid Grid.Column="1" VerticalAlignment="Center" Margin="0,0,8,0">
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
Padding="0">
<StackPanel x:Name="ContentPanel" Margin="12,12,12,12" Orientation="Vertical">
<!-- 区段:声音 -->
<TextBlock Text="声音" Style="{StaticResource GameBarSectionTitle}">
<TextBlock.Foreground>
<SolidColorBrush x:Name="MagnifierTitleForegroundBrush" Color="White"/>
</TextBlock.Foreground>
</TextBlock>
<Border CornerRadius="8" Background="#1AFFFFFF" Padding="6" Margin="0,0,0,12">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- 静音按钮 -->
<Button x:Name="VolumeMuteButton"
Grid.Column="0"
Width="28" Height="28"
Background="Transparent"
BorderThickness="0"
Cursor="Hand"
Padding="0"
Click="VolumeMuteButton_Click"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="Bd"
Background="{TemplateBinding Background}"
CornerRadius="6">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#33FFFFFF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
<Viewbox Width="16" Height="16" Stretch="Uniform">
<Grid>
<Path x:Name="VolumeIconPath"
Data="M 3,9 v 6 h 4 l 5,5 v -16 l -5,5 h -4 z"
Stretch="Uniform">
<Path.Fill>
<SolidColorBrush x:Name="VolumeIconFillBrush" Color="White"/>
</Path.Fill>
</Path>
<Path x:Name="VolumeIconPath2"
Data=""
Stretch="Uniform"
Opacity="0.5"
Visibility="Collapsed">
<Path.Fill>
<SolidColorBrush x:Name="VolumeIconFillBrush2" Color="White"/>
</Path.Fill>
</Path>
</Grid>
</Viewbox>
</Button>
<!-- 音量滑块 -->
<Slider x:Name="VolumeSlider"
Grid.Column="1"
Margin="8,0,8,0"
VerticalAlignment="Center"
Stylus.IsPressAndHoldEnabled="False"
Minimum="0"
Maximum="100"
Value="50"
@@ -147,98 +226,114 @@
ValueChanged="VolumeSlider_ValueChanged"
PreviewMouseLeftButtonUp="VolumeSlider_PreviewMouseLeftButtonUp"
ManipulationCompleted="VolumeSlider_ManipulationCompleted"/>
</Grid>
<!-- 右侧音量值 -->
<TextBlock x:Name="VolumeValueText"
Grid.Column="2"
Text="50%"
FontSize="10"
VerticalAlignment="Center"
MinWidth="32"
TextAlignment="Center">
<TextBlock.Foreground>
<SolidColorBrush x:Name="VolumeValueForegroundBrush" Color="#80000000"/>
</TextBlock.Foreground>
</TextBlock>
</Grid>
<!-- 分隔线 -->
<Separator x:Name="Separator1" Margin="0,0,0,8" Height="1">
<Separator.Background>
<SolidColorBrush x:Name="Separator1BackgroundBrush" Color="#E0E0E0"/>
</Separator.Background>
</Separator>
<!-- 图片插入按钮区域 -->
<StackPanel Orientation="Vertical" Margin="0,0,0,8">
<!-- 选择文件按钮 -->
<Button x:Name="InsertImageSelectFileButton"
Content="选择文件"
FontSize="11"
Height="30"
Background="#3b82f6"
Foreground="White"
BorderThickness="0"
Cursor="Hand"
Click="InsertImageSelectFileButton_Click"
Margin="0,0,0,4">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
<!-- 截图插入按钮 -->
<Button x:Name="InsertImageScreenshotButton"
Content="截图插入"
FontSize="11"
Height="30"
Background="#3b82f6"
Foreground="White"
BorderThickness="0"
Cursor="Hand"
Click="InsertImageScreenshotButton_Click"
Margin="0,0,0,0">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
<!-- 聚焦放大镜(暂未设计)- 暂时隐藏 -->
<Border x:Name="MagnifierSection" Margin="0,0,0,8" Visibility="Collapsed">
<StackPanel Orientation="Vertical">
<TextBlock x:Name="MagnifierTitleText" Text="聚焦放大镜" FontSize="11" FontWeight="Bold" Margin="0,0,0,6">
<!-- 数值 -->
<TextBlock x:Name="VolumeValueText"
Grid.Column="2"
Text="50%"
FontSize="11"
FontWeight="SemiBold"
VerticalAlignment="Center"
MinWidth="32"
TextAlignment="Right">
<TextBlock.Foreground>
<SolidColorBrush x:Name="MagnifierTitleForegroundBrush" Color="#FF000000"/>
<SolidColorBrush x:Name="VolumeValueForegroundBrush" Color="#E6FFFFFF"/>
</TextBlock.Foreground>
</TextBlock>
<TextBlock x:Name="MagnifierDescText" Text="功能暂未设计" FontSize="9" FontStyle="Italic">
</Grid>
</Border>
<!-- 隐藏的占位分隔线(保留命名元素以满足 .cs 引用) -->
<Separator x:Name="Separator1" Visibility="Collapsed" Height="0">
<Separator.Background>
<SolidColorBrush x:Name="Separator1BackgroundBrush" Color="Transparent"/>
</Separator.Background>
</Separator>
<!-- 区段:插入图片 -->
<TextBlock Text="插入" Style="{StaticResource GameBarSectionTitle}"/>
<StackPanel Orientation="Vertical" Margin="0,0,0,4">
<Button x:Name="InsertImageSelectFileButton"
Content="选择文件"
Style="{StaticResource GameBarFlatButton}"
Margin="0,0,0,6"
Click="InsertImageSelectFileButton_Click"/>
<Button x:Name="InsertImageScreenshotButton"
Content="截图插入"
Style="{StaticResource GameBarFlatButton}"
Click="InsertImageScreenshotButton_Click"/>
</StackPanel>
<!-- 区段:聚焦放大镜 -->
<Border x:Name="MagnifierSection">
<StackPanel>
<TextBlock x:Name="MagnifierTitleText"
Text="聚焦放大镜"
Style="{StaticResource GameBarSectionTitle}">
<TextBlock.Foreground>
<SolidColorBrush x:Name="MagnifierDescForegroundBrush" Color="#80000000"/>
<SolidColorBrush x:Name="MagnifierTitleForeground2Brush" Color="White"/>
</TextBlock.Foreground>
</TextBlock>
<Button x:Name="MagnifierToggleButton"
Content="开启放大镜"
Style="{StaticResource GameBarFlatButton}"
Margin="0,0,0,6"
Click="MagnifierToggleButton_Click"/>
<Border CornerRadius="8" Background="#1AFFFFFF" Padding="6" Margin="0,0,0,4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="缩放"
FontSize="11"
VerticalAlignment="Center"
Margin="4,0,8,0"
Foreground="White"/>
<Slider x:Name="MagnifierZoomSlider"
Grid.Column="1"
VerticalAlignment="Center"
Stylus.IsPressAndHoldEnabled="False"
Minimum="1.5"
Maximum="6"
Value="2"
TickFrequency="0.5"
IsSnapToTickEnabled="True"
ValueChanged="MagnifierZoomSlider_ValueChanged"/>
<TextBlock x:Name="MagnifierZoomValueText"
Grid.Column="2"
Text="2.0x"
FontSize="11"
FontWeight="SemiBold"
VerticalAlignment="Center"
MinWidth="36"
TextAlignment="Right"
Foreground="#E6FFFFFF"/>
</Grid>
</Border>
<TextBlock x:Name="MagnifierDescText"
Text="拖动选择框移动,拖动控制点缩放,按 ESC 关闭"
FontSize="11"
Margin="2,0,0,0"
TextWrapping="Wrap">
<TextBlock.Foreground>
<SolidColorBrush x:Name="MagnifierDescForegroundBrush" Color="#80FFFFFF"/>
</TextBlock.Foreground>
</TextBlock>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</Border>
</Grid>
</Canvas>
</Grid>
</UserControl>
</UserControl>
+87 -43
View File
@@ -118,6 +118,19 @@ namespace Ink_Canvas.Windows
Loaded += PPTQuickPanel_Loaded;
Unloaded += PPTQuickPanel_Unloaded;
IsVisibleChanged += PPTQuickPanel_IsVisibleChanged;
MagnifierWindow.Closed2 += OnMagnifierClosed;
}
private void OnMagnifierClosed(object sender, EventArgs e)
{
Dispatcher.BeginInvoke(new Action(SyncMagnifierButtonState));
}
private void SyncMagnifierButtonState()
{
if (MagnifierToggleButton == null) return;
MagnifierToggleButton.Content = MagnifierWindow.HasInstance ? "关闭放大镜" : "开启放大镜";
}
private void PPTQuickPanel_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
@@ -125,6 +138,7 @@ namespace Ink_Canvas.Windows
if (Visibility == Visibility.Visible)
{
ApplyTheme();
SyncMagnifierButtonState();
}
}
@@ -747,31 +761,34 @@ namespace Ink_Canvas.Windows
private void ArrowButton_MouseEnter(object sender, MouseEventArgs e)
{
// 根据当前主题设置悬停颜色
bool isDark = ArrowButtonBackgroundBrush.Color.R < 128;
if (isDark)
{
ArrowButtonBackgroundBrush.Color = Color.FromArgb(230, 32, 32, 32);
}
else
{
ArrowButtonBackgroundBrush.Color = Color.FromArgb(230, 255, 255, 255);
}
ArrowButtonBackgroundBrush.Color = Color.FromArgb(220, 55, 55, 55);
}
private void ArrowButton_MouseLeave(object sender, MouseEventArgs e)
{
// 恢复主题颜色
ApplyTheme();
ArrowButtonBackgroundBrush.Color = Color.FromArgb(204, 31, 31, 31);
}
#endregion
#region
private static bool IsWithinSlider(object source)
{
var d = source as DependencyObject;
while (d != null)
{
if (d is Slider) return true;
d = (d is System.Windows.Media.Visual || d is System.Windows.Media.Media3D.Visual3D)
? System.Windows.Media.VisualTreeHelper.GetParent(d)
: LogicalTreeHelper.GetParent(d);
}
return false;
}
private void ContentBorder_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.OriginalSource is Slider) return; // 如果点击的是滑块,不处理拖动
if (IsWithinSlider(e.OriginalSource)) return;
_isDragging = true;
_dragStartPoint = e.GetPosition(MainCanvas);
@@ -816,7 +833,7 @@ namespace Ink_Canvas.Windows
private void ContentBorder_TouchDown(object sender, TouchEventArgs e)
{
if (e.OriginalSource is Slider) return;
if (IsWithinSlider(e.OriginalSource)) return;
_isDragging = true;
_dragStartPoint = e.GetTouchPoint(MainCanvas).Position;
@@ -1835,35 +1852,16 @@ namespace Ink_Canvas.Windows
{
try
{
bool isDarkTheme = settings.Appearance.Theme == 1 ||
(settings.Appearance.Theme == 2 && !ThemeHelper.IsSystemThemeLight());
if (isDarkTheme)
{
// 深色主题:使用80%不透明度的深色背景
ArrowButtonBackgroundBrush.Color = Color.FromArgb(204, 32, 32, 32); // #CC202020
ContentBackgroundBrush.Color = Color.FromArgb(204, 32, 32, 32); // #CC202020
ArrowPathFillBrush.Color = Colors.White;
VolumeIconFillBrush.Color = Colors.White;
VolumeIconFillBrush2.Color = Colors.White;
VolumeValueForegroundBrush.Color = Color.FromArgb(200, 255, 255, 255);
MagnifierTitleForegroundBrush.Color = Colors.White;
MagnifierDescForegroundBrush.Color = Color.FromArgb(200, 255, 255, 255);
Separator1BackgroundBrush.Color = Color.FromArgb(128, 255, 255, 255);
}
else
{
// 浅色主题:使用80%不透明度的白色背景
ArrowButtonBackgroundBrush.Color = Color.FromArgb(204, 255, 255, 255); // #CCFFFFFF
ContentBackgroundBrush.Color = Color.FromArgb(204, 255, 255, 255); // #CCFFFFFF
ArrowPathFillBrush.Color = Colors.Black;
VolumeIconFillBrush.Color = Colors.Black;
VolumeIconFillBrush2.Color = Colors.Black;
VolumeValueForegroundBrush.Color = Color.FromArgb(128, 0, 0, 0);
MagnifierTitleForegroundBrush.Color = Colors.Black;
MagnifierDescForegroundBrush.Color = Color.FromArgb(128, 0, 0, 0);
Separator1BackgroundBrush.Color = Color.FromArgb(255, 224, 224, 224);
}
// Game Bar 风格:始终使用深色半透明外壳,不随系统主题翻转
ArrowButtonBackgroundBrush.Color = Color.FromArgb(204, 31, 31, 31);
ContentBackgroundBrush.Color = Color.FromArgb(204, 31, 31, 31); // #CC1F1F1F
ArrowPathFillBrush.Color = Colors.White;
VolumeIconFillBrush.Color = Colors.White;
VolumeIconFillBrush2.Color = Colors.White;
VolumeValueForegroundBrush.Color = Color.FromArgb(230, 255, 255, 255);
MagnifierTitleForegroundBrush.Color = Colors.White;
MagnifierDescForegroundBrush.Color = Color.FromArgb(200, 255, 255, 255);
Separator1BackgroundBrush.Color = Color.FromArgb(60, 255, 255, 255);
}
catch (Exception ex)
{
@@ -1873,6 +1871,46 @@ namespace Ink_Canvas.Windows
#endregion
#region
private void MagnifierToggleButton_Click(object sender, RoutedEventArgs e)
{
try
{
if (MagnifierWindow.HasInstance)
{
MagnifierWindow.HideInstance();
MagnifierToggleButton.Content = "开启放大镜";
}
else
{
MagnifierWindow.Show((float)MagnifierZoomSlider.Value);
if (MagnifierWindow.HasInstance)
{
MagnifierToggleButton.Content = "关闭放大镜";
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换聚焦放大镜失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void MagnifierZoomSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (MagnifierZoomValueText != null)
{
MagnifierZoomValueText.Text = $"{e.NewValue:0.0}x";
}
if (MagnifierWindow.HasInstance)
{
MagnifierWindow.SetZoom((float)e.NewValue);
}
}
#endregion
#region
/// <summary>
@@ -1881,6 +1919,12 @@ namespace Ink_Canvas.Windows
public void UpdateVisibility(bool isInPPTMode)
{
Visibility = isInPPTMode ? Visibility.Visible : Visibility.Collapsed;
if (!isInPPTMode && MagnifierWindow.HasInstance)
{
MagnifierWindow.HideInstance();
if (MagnifierToggleButton != null)
MagnifierToggleButton.Content = "开启放大镜";
}
}
#endregion
@@ -339,14 +339,12 @@ namespace Ink_Canvas.Windows.SettingsViews.Helpers
};
bool isThemeRelated = false;
string controlNameLower = controlName.ToLower();
foreach (var themeControl in themeRelatedControls)
{
string themeControlLower = themeControl.ToLower();
if (controlNameLower.Contains(themeControlLower) ||
themeControlLower.Contains(controlNameLower) ||
controlNameLower == themeControlLower)
// OrdinalIgnoreCase 避免在循环里反复 ToLower() 生成中间字符串。
if (controlName.IndexOf(themeControl, StringComparison.OrdinalIgnoreCase) >= 0 ||
themeControl.IndexOf(controlName, StringComparison.OrdinalIgnoreCase) >= 0)
{
isThemeRelated = true;
break;
@@ -228,8 +228,14 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
private void ViewboxFloatingBarScaleTransformValueSlider_ValueChanged(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
var val = Math.Round(ViewboxFloatingBarScaleTransformValueSlider.Value, 2);
ViewboxFloatingBarScaleTransformValueSlider.Value = val;
var slider = ViewboxFloatingBarScaleTransformValueSlider;
var val = Math.Round(slider.Value, 2);
// 仅当四舍五入纠正了显示值时才回写;那次 set 会重入 ValueChanged 完成保存。
if (slider.Value != val)
{
slider.Value = val;
return;
}
SettingsManager.Settings.Appearance.ViewboxFloatingBarScaleTransformValue = val;
SettingsManager.SaveSettingsToFile();
var mw = GetMainWindow();
@@ -247,8 +253,13 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
private void ViewboxFloatingBarOpacityValueSlider_ValueChanged(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
var val = Math.Round(ViewboxFloatingBarOpacityValueSlider.Value, 2);
ViewboxFloatingBarOpacityValueSlider.Value = val;
var slider = ViewboxFloatingBarOpacityValueSlider;
var val = Math.Round(slider.Value, 2);
if (slider.Value != val)
{
slider.Value = val;
return;
}
SettingsManager.Settings.Appearance.ViewboxFloatingBarOpacityValue = val;
SettingsManager.SaveSettingsToFile();
var mw = GetMainWindow();
@@ -258,8 +269,13 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
private void ViewboxFloatingBarOpacityInPPTValueSlider_ValueChanged(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
var val = Math.Round(ViewboxFloatingBarOpacityInPPTValueSlider.Value, 2);
ViewboxFloatingBarOpacityInPPTValueSlider.Value = val;
var slider = ViewboxFloatingBarOpacityInPPTValueSlider;
var val = Math.Round(slider.Value, 2);
if (slider.Value != val)
{
slider.Value = val;
return;
}
SettingsManager.Settings.Appearance.ViewboxFloatingBarOpacityInPPTValue = val;
SettingsManager.SaveSettingsToFile();
var mw = GetMainWindow();
@@ -290,8 +306,13 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
private void ViewboxBlackBoardScaleTransformValueSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!_isLoaded) return;
var val = Math.Round(ViewboxBlackBoardScaleTransformValueSlider.Value, 2);
ViewboxBlackBoardScaleTransformValueSlider.Value = val;
var slider = ViewboxBlackBoardScaleTransformValueSlider;
var val = Math.Round(slider.Value, 2);
if (slider.Value != val)
{
slider.Value = val;
return;
}
SettingsManager.Settings.Appearance.ViewboxBlackBoardScaleTransformValue = val;
SettingsManager.SaveSettingsToFile();
var mw = GetMainWindow();
@@ -373,30 +373,30 @@
Toggled="ToggleSwitchSaveFullPageStrokes_Toggled"/>
<controls:LabeledSettingsCard x:Name="CardUseCustomSaveFileName"
Header="使用自定义保存文件名"
Description="开启后可选择保存文件的命名方式"
Header="{i18n:I18n Key=Automation_UseCustomSaveFileName_Header}"
Description="{i18n:I18n Key=Automation_UseCustomSaveFileName_Desc}"
Icon="{x:Static ui:SegoeFluentIcons.Edit}"
Toggled="ToggleSwitchUseCustomSaveFileName_Toggled"/>
<ui:SettingsExpander x:Name="CardSaveFileNamePreset" Header="文件名格式">
<ui:SettingsExpander x:Name="CardSaveFileNamePreset" Header="{i18n:I18n Key=Automation_SaveFileNameFormat}">
<ui:SettingsExpander.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Edit}"/>
</ui:SettingsExpander.HeaderIcon>
<ComboBox x:Name="ComboBoxSaveFileNamePreset"
MinWidth="260"
SelectionChanged="ComboBoxSaveFileNamePreset_SelectionChanged">
<ComboBoxItem Content="时间戳(默认)" Tag="{}{datetime}"/>
<ComboBoxItem Content="日期" Tag="{}{date}"/>
<ComboBoxItem Content="日期 + 时间" Tag="{}{date}_{time}"/>
<ComboBoxItem Content="日期 + 模式" Tag="{}{date}_{mode}"/>
<ComboBoxItem Content="日期 + 模式 + 页码" Tag="{}{date}_{mode}_Page-{page}"/>
<ComboBoxItem Content="日期 + 模式 + 页码 + 笔画数" Tag="{}{date}_{mode}_Page-{page}_Strokes-{count}"/>
<ComboBoxItem Content="自定义..." Tag="__custom__"/>
<ComboBoxItem Content="{i18n:I18n Key=Automation_SaveFileName_Timestamp}" Tag="{}{datetime}"/>
<ComboBoxItem Content="{i18n:I18n Key=Automation_SaveFileName_Date}" Tag="{}{date}"/>
<ComboBoxItem Content="{i18n:I18n Key=Automation_SaveFileName_DateTime}" Tag="{}{date}_{time}"/>
<ComboBoxItem Content="{i18n:I18n Key=Automation_SaveFileName_DateMode}" Tag="{}{date}_{mode}"/>
<ComboBoxItem Content="{i18n:I18n Key=Automation_SaveFileName_DateModePage}" Tag="{}{date}_{mode}_Page-{page}"/>
<ComboBoxItem Content="{i18n:I18n Key=Automation_SaveFileName_DateModePageCount}" Tag="{}{date}_{mode}_Page-{page}_Strokes-{count}"/>
<ComboBoxItem Content="{i18n:I18n Key=Automation_SaveFileName_Custom}" Tag="__custom__"/>
</ComboBox>
<ui:SettingsExpander.Items>
<ui:SettingsCard x:Name="CardCustomSaveFileNameTemplate"
Header="自定义模板"
Description="可用占位符:{date} {time} {datetime} {mode} {page} {count} {type}"
Header="{i18n:I18n Key=Automation_SaveFileName_CustomTemplate_Header}"
Description="{i18n:I18n Key=Automation_SaveFileName_CustomTemplate_Desc}"
Visibility="Collapsed">
<TextBox x:Name="TextBoxCustomSaveFileNameTemplate"
MinWidth="260"
@@ -294,8 +294,14 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
private void BrushAutoRestoreWidthSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!_isLoaded) return;
var val = Math.Round(BrushAutoRestoreWidthSlider.Value, 2);
BrushAutoRestoreWidthSlider.Value = val;
var slider = BrushAutoRestoreWidthSlider;
var val = Math.Round(slider.Value, 2);
// 仅在四舍五入纠正了显示值时回写;那次 set 会重入 ValueChanged 完成保存。
if (slider.Value != val)
{
slider.Value = val;
return;
}
SettingsManager.Settings.Canvas.BrushAutoRestoreWidth = val;
SettingsManager.SaveSettingsToFile();
}
@@ -8,6 +8,7 @@
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:controls="clr-namespace:Ink_Canvas.Controls;assembly=InkCanvas.Controls"
xmlns:i18n="clr-namespace:Ink_Canvas.MarkupExtensions"
mc:Ignorable="d"
Title="Debug">
@@ -32,8 +33,8 @@
Text="Debug" />
<controls:LabeledSettingsCard x:Name="ToggleSwitchDebugConsole"
Header="显示调试窗口"
Description="显示一个独立的控制台窗口,用于实时输出日志(开启后立即生效;关闭设置中的“启用日志记录”将不会输出内容)。"
Header="{i18n:I18n Key=Debug_ShowConsole_Header}"
Description="{i18n:I18n Key=Debug_ShowConsole_Desc}"
Icon="{x:Static ui:SegoeFluentIcons.DeveloperTools}"
IsOn="False"
Toggled="ToggleSwitchDebugConsole_Toggled"/>
@@ -216,8 +216,14 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
private void MLAvoidanceWeightSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!_isLoaded) return;
var val = Math.Round(MLAvoidanceWeightSlider.Value, 2);
MLAvoidanceWeightSlider.Value = val;
var slider = MLAvoidanceWeightSlider;
var val = Math.Round(slider.Value, 2);
// 仅当四舍五入纠正了显示值时才回写;那次 set 会重入 ValueChanged 完成保存。
if (slider.Value != val)
{
slider.Value = val;
return;
}
SettingsManager.Settings.RandSettings.MLAvoidanceWeight = val;
SettingsManager.SaveSettingsToFile();
}
@@ -281,8 +287,13 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
private void TimerVolumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!_isLoaded) return;
var val = Math.Round(TimerVolumeSlider.Value, 2);
TimerVolumeSlider.Value = val;
var slider = TimerVolumeSlider;
var val = Math.Round(slider.Value, 2);
if (slider.Value != val)
{
slider.Value = val;
return;
}
SettingsManager.Settings.RandSettings.TimerVolume = val;
SettingsManager.SaveSettingsToFile();
}
@@ -321,8 +332,13 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
private void ProgressiveReminderVolumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!_isLoaded) return;
var val = Math.Round(ProgressiveReminderVolumeSlider.Value, 2);
ProgressiveReminderVolumeSlider.Value = val;
var slider = ProgressiveReminderVolumeSlider;
var val = Math.Round(slider.Value, 2);
if (slider.Value != val)
{
slider.Value = val;
return;
}
SettingsManager.Settings.RandSettings.ProgressiveReminderVolume = val;
SettingsManager.SaveSettingsToFile();
}
@@ -155,9 +155,9 @@
</ui:NavigationViewItem>
<ui:NavigationViewItem
x:Name="SecurityPageItem"
Content="安全"
Content="{i18n:I18n Key=Settings_Nav_Security}"
Tag="SecurityPage"
ToolTipService.ToolTip="安全密码与进程保护">
ToolTipService.ToolTip="{i18n:I18n Key=Settings_Nav_Security_Tooltip}">
<ui:NavigationViewItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Permissions}"/>
</ui:NavigationViewItem.Icon>
@@ -195,9 +195,9 @@
</ui:NavigationViewItem>
<ui:NavigationViewItem
x:Name="HotkeyPageItem"
Content="快捷键"
Content="{i18n:I18n Key=Settings_Nav_Hotkey}"
Tag="HotkeyPage"
ToolTipService.ToolTip="快捷键设置">
ToolTipService.ToolTip="{i18n:I18n Key=Settings_Nav_Hotkey_Tooltip}">
<ui:NavigationViewItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.KeyboardStandard}"/>
</ui:NavigationViewItem.Icon>
@@ -587,10 +587,9 @@ namespace Ink_Canvas.Windows.SettingsViews
if (string.IsNullOrWhiteSpace(raw)) return;
string query = raw.Trim();
string queryLower = query.ToLower();
var entry = _searchIndex.FirstOrDefault(e => e.Text.Equals(query, StringComparison.OrdinalIgnoreCase))
?? _searchIndex.FirstOrDefault(e => e.Text.ToLower().Contains(queryLower));
?? _searchIndex.FirstOrDefault(e => e.Text.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0);
NavigateToSearchEntry(entry);
}
@@ -608,9 +607,8 @@ namespace Ink_Canvas.Windows.SettingsViews
return;
}
string queryLower = query.ToLower();
var suggestions = _searchIndex
.Where(e => e.Text.ToLower().Contains(queryLower))
.Where(e => e.Text.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0)
.Select(e => e.Text)
.Distinct()
.Take(50)
+59 -1
View File
@@ -349,10 +349,68 @@ namespace Ink_Canvas.Windows
ThemeHelper.ApplyTheme(this, settings, theme =>
{
if (theme == "Dark") SetDarkThemeBorder();
UpdateDigitDisplays();
// 复用当前状态下的显示逻辑,避免把暂停/超时/运行中的读数重置为初始设定时间。
RefreshDigitDisplayForCurrentState();
});
}
/// <summary>
/// 依据当前计时器状态刷新数字显示:
/// 运行中按 DateTime.Now、暂停中按 pauseTime 推算;处于超时模式时按超时值渲染;未启动时回退到初始设定值。
/// 供主题切换等场景直接复用 Timer_Elapsed 的渲染分支。
/// </summary>
private void RefreshDigitDisplayForCurrentState()
{
if (!isTimerRunning)
{
UpdateDigitDisplays();
return;
}
DateTime referenceTime = isPaused ? pauseTime : DateTime.Now;
TimeSpan timeSpan = referenceTime - startTime;
TimeSpan totalTimeSpan = new TimeSpan(hour, minute, second);
if (!isOvertimeMode)
{
TimeSpan leftTimeSpan = totalTimeSpan - timeSpan;
if (leftTimeSpan.Milliseconds > 0) leftTimeSpan += new TimeSpan(0, 0, 1);
if (leftTimeSpan < TimeSpan.Zero) leftTimeSpan = TimeSpan.Zero;
int displayHours = Math.Min(99, (int)leftTimeSpan.TotalHours);
SetDigitDisplay("Digit1Display", displayHours / 10);
SetDigitDisplay("Digit2Display", displayHours % 10);
SetDigitDisplay("Digit3Display", leftTimeSpan.Minutes / 10);
SetDigitDisplay("Digit4Display", leftTimeSpan.Minutes % 10);
SetDigitDisplay("Digit5Display", leftTimeSpan.Seconds / 10);
SetDigitDisplay("Digit6Display", leftTimeSpan.Seconds % 10);
SetColonDisplay(false);
}
else
{
TimeSpan overtimeSpan = timeSpan - totalTimeSpan;
if (overtimeSpan < TimeSpan.Zero) overtimeSpan = TimeSpan.Zero;
int displayHours = Math.Max(0, Math.Min(99, (int)overtimeSpan.TotalHours));
bool shouldShowRed = MainWindow.Settings?.RandSettings?.EnableOvertimeRedText == true;
int hoursTens = Math.Max(0, Math.Min(9, Math.Abs(displayHours / 10) % 10));
int hoursOnes = Math.Max(0, Math.Min(9, (displayHours % 10 + 10) % 10));
int minutesTens = Math.Max(0, Math.Min(9, Math.Abs(overtimeSpan.Minutes) / 10));
int minutesOnes = Math.Max(0, Math.Min(9, Math.Abs(overtimeSpan.Minutes) % 10));
int secondsTens = Math.Max(0, Math.Min(9, Math.Abs(overtimeSpan.Seconds) / 10));
int secondsOnes = Math.Max(0, Math.Min(9, Math.Abs(overtimeSpan.Seconds) % 10));
SetDigitDisplay("Digit1Display", hoursTens, shouldShowRed);
SetDigitDisplay("Digit2Display", hoursOnes, shouldShowRed);
SetDigitDisplay("Digit3Display", minutesTens, shouldShowRed);
SetDigitDisplay("Digit4Display", minutesOnes, shouldShowRed);
SetDigitDisplay("Digit5Display", secondsTens, shouldShowRed);
SetDigitDisplay("Digit6Display", secondsOnes, shouldShowRed);
SetColonDisplay(shouldShowRed);
}
}
private void UpdateDigitDisplays()
{
SetDigitDisplay("Digit1Display", hour / 10);