初始提交:WinUI 3 一键运行轮盘工具
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using OneClickRun.App.Controls;
|
||||
using OneClickRun.App.Services;
|
||||
using OneClickRun.Core.Logic;
|
||||
using OneClickRun.Core.Models;
|
||||
using Windows.Graphics;
|
||||
using Windows.UI;
|
||||
|
||||
namespace OneClickRun.App;
|
||||
|
||||
/// <summary>
|
||||
/// 轮盘悬浮窗:无边框、透明背景、置顶、不进 Alt+Tab。
|
||||
/// 尺寸/位置一律使用物理像素;内容按 PerMonitorV2 DPI 自动缩放。
|
||||
/// </summary>
|
||||
public sealed partial class WheelWindow : Window
|
||||
{
|
||||
private const double WheelDiameterDips = 560;
|
||||
|
||||
private int _x;
|
||||
private int _y;
|
||||
private int _size;
|
||||
private double _scale = 1.0;
|
||||
|
||||
public event Action<int>? SectorClicked;
|
||||
public event Action? DismissRequested;
|
||||
|
||||
public WheelWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
Title = "OneClickRun Wheel";
|
||||
AppWindow.IsShownInSwitchers = false;
|
||||
AppWindow.SetPresenter(AppWindowPresenterKind.Overlapped);
|
||||
if (AppWindow.Presenter is OverlappedPresenter presenter)
|
||||
{
|
||||
presenter.SetBorderAndTitleBar(false, false);
|
||||
presenter.IsAlwaysOnTop = true;
|
||||
presenter.IsResizable = false;
|
||||
presenter.IsMaximizable = false;
|
||||
presenter.IsMinimizable = false;
|
||||
}
|
||||
SystemBackdrop = new TransparentBackdrop();
|
||||
// alpha=1 的“透明”背景:肉眼不可见,但可命中鼠标事件(用于点击轮盘外关闭)
|
||||
Root.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));
|
||||
Wheel.SectorClicked += i => SectorClicked?.Invoke(i);
|
||||
Wheel.DismissRequested += () => DismissRequested?.Invoke();
|
||||
}
|
||||
|
||||
public bool IsVisible => AppWindow.IsVisible;
|
||||
|
||||
public void SetItems(IReadOnlyList<CommandItem> items) => Wheel.SetItems(items);
|
||||
|
||||
public void SetHoverIndex(int index) => Wheel.SetHoverIndex(index);
|
||||
|
||||
public void SetPendingIndex(int index) => Wheel.SetPendingIndex(index);
|
||||
|
||||
public void ClearPending() => Wheel.ClearPending();
|
||||
|
||||
public void ClearHover() => Wheel.SetHoverIndex(-1);
|
||||
|
||||
/// <summary>按住模式轮询:光标所在扇区(窗口外也能计算方向)。</summary>
|
||||
public int GetSectorIndexAtCursor()
|
||||
{
|
||||
if (Wheel.Count == 0) return -1;
|
||||
NativeMethods.GetCursorPos(out var pt);
|
||||
double cx = _x + _size / 2.0;
|
||||
double cy = _y + _size / 2.0;
|
||||
double dx = pt.X - cx;
|
||||
double dy = pt.Y - cy;
|
||||
double dist = Math.Sqrt(dx * dx + dy * dy);
|
||||
if (dist < 76 * _scale || dist > 282 * _scale) return -1;
|
||||
return WheelMath.GetSectorIndex(Wheel.Count, WheelMath.AngleFromVector(dx, dy));
|
||||
}
|
||||
|
||||
/// <summary>滑动选择:松开时按光标相对轮盘中心方位决定扇区(死区 24 DIP)。</summary>
|
||||
public bool TryGetSwipeIndex(int count, out int index)
|
||||
{
|
||||
index = -1;
|
||||
if (!NativeMethods.GetCursorPos(out var pt)) return false;
|
||||
double cx = _x + _size / 2.0;
|
||||
double cy = _y + _size / 2.0;
|
||||
return WheelMath.TryGetSwipeIndex(count, pt.X - cx, pt.Y - cy, 24 * _scale, out index);
|
||||
}
|
||||
|
||||
/// <summary>在屏幕指定中心点显示轮盘(自动钳制到工作区并换算 DPI)。</summary>
|
||||
internal void ShowAt(POINT center)
|
||||
{
|
||||
var monitor = NativeMethods.MonitorFromPoint(center, NativeMethods.MONITOR_DEFAULTTONEAREST);
|
||||
_scale = GetMonitorScale(monitor);
|
||||
_size = (int)Math.Round(WheelDiameterDips * _scale);
|
||||
int half = _size / 2;
|
||||
|
||||
var info = new MONITORINFO { cbSize = Marshal.SizeOf<MONITORINFO>() };
|
||||
if (NativeMethods.GetMonitorInfo(monitor, ref info))
|
||||
{
|
||||
int left = Math.Max(info.rcWork.Left + half, Math.Min(info.rcWork.Right - half, center.X));
|
||||
int top = Math.Max(info.rcWork.Top + half, Math.Min(info.rcWork.Bottom - half, center.Y));
|
||||
center.X = left;
|
||||
center.Y = top;
|
||||
}
|
||||
|
||||
_x = center.X - half;
|
||||
_y = center.Y - half;
|
||||
AppWindow.MoveAndResize(new RectInt32(_x, _y, _size, _size));
|
||||
if (!AppWindow.IsVisible) AppWindow.Show();
|
||||
Activate();
|
||||
}
|
||||
|
||||
public void Hide() => AppWindow.Hide();
|
||||
|
||||
private static double GetMonitorScale(IntPtr monitor)
|
||||
{
|
||||
if (NativeMethods.GetDpiForMonitor(monitor, NativeMethods.MDT_EFFECTIVE_DPI, out uint dpiX, out _) == 0 && dpiX > 0)
|
||||
{
|
||||
return dpiX / 96.0;
|
||||
}
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user