初始提交:WinUI 3 一键运行轮盘工具

This commit is contained in:
2026-08-30 01:37:56 +08:00
commit 83011e5d29
58 changed files with 4373 additions and 0 deletions
+121
View File
@@ -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;
}
}