有没有与ClipCursor相反,可以排除光标区域的函数?
摘要
一位Windows开发者询问如何排除光标区域,即ClipCursor的反向操作。文章介绍了一种避免闪烁的技术:不移动光标,而是将对象的位置吸附到最近的合法位置。
<p>有客户希望防止用户将对象拖放到窗口的特定区域。他们当前的实现会检测鼠标是否位于非法位置,并使用<code>SetCursorPos</code>将其移到附近的合法位置。但这会导致闪烁,因为光标实际上先进入了非法区域,然后才跳出来。</p>
<p>让我们用<a title="示例程序" href="https://devblogs.microsoft.com/oldnewthing/20030723-00/?p=43073">我们的示例程序</a>来说明这一点。</p>
<pre>POINT g_pt;
const RECT g_rcExclude = { 100, 100, 200, 200 };
RECT ItemRect(POINT pt)
{
return RECT{ pt.x - 10, pt.y - 10, pt.x + 10, pt.y + 10 };
}
</pre>
<p><code>g_pt</code>变量保存对象的位置,<code>g_rcExclude</code>是禁止对象进入的矩形区域。<code>ItemRect</code>函数为对象生成一个边界矩形,以便我们绘制内容。</p>
<pre>void
PaintContent(HWND hwnd, PAINTSTRUCT* pps)
{
FillRect(pps->hdc, &g_rcExclude, (HBRUSH)(COLOR_WINDOWTEXT + 1));
RECT rcItem = ItemRect(g_pt);
FillRect(pps->hdc, &rcItem, (HBRUSH)(COLOR_APPWORKSPACE + 1));
}
</pre>
<p>绘制内容很简单:用文本颜色绘制禁止矩形,用应用程序工作区颜色绘制对象。</p>
<pre>void OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
{
POINT ptNew = { x, y };
if (PtInRect(&g_rcExclude, ptNew)) {
// 限制到最近的合法位置
int leftMargin = ptNew.x - g_rcExclude.left;
int topMargin = ptNew.y - g_rcExclude.top;
int rightMargin = g_rcExclude.right - ptNew.x;
int bottomMargin = g_rcExclude.bottom - ptNew.y;
int dx, dy;
int x, y;
if (leftMargin < rightMargin) {
x = g_rcExclude.left;
dx = leftMargin;
} else {
x = g_rcExclude.right;
dx = rightMargin;
}
if (topMargin < bottomMargin) {
y = g_rcExclude.top;
dy = topMargin;
} else {
y = g_rcExclude.bottom;
dy = bottomMargin;
}
if (dx < dy) {
ptNew.x = x;
} else {
ptNew.y = y;
}
POINT ptScreen = ptNew;
ClientToScreen(hwnd, &ptScreen);
SetCursorPos(ptScreen.x, ptScreen.y);
}
if (g_pt.x != ptNew.x || g_pt.y != ptNew.y) {
RECT rcItem = ItemRect(g_pt);
InvalidateRect(hwnd, &rcItem, TRUE);
g_pt = ptNew;
rcItem = ItemRect(g_pt);
InvalidateRect(hwnd, &rcItem, TRUE);
}
}
// 添加到WndProc
HANDLE_MSG(hwnd, WM_MOUSEMOVE, OnMouseMove);
</pre>
<p>当鼠标移动时,我们获取鼠标位置并检查是否在禁止矩形内。如果是,则将坐标更新到最近的合法位置,并使用<code>SetCursorPos</code>将鼠标移到那里。</p>
<p>无论是否需要更新坐标,如果结果产生了新位置,则使对象旧位置失效(以便在下一次绘制循环中擦除),更新对象位置,然后使对象新位置失效(以便在下一次绘制循环中绘制)。</p>
<p>运行此程序时,你可以尝试将鼠标移入禁止矩形,但程序会将鼠标推出来。然而,这会导致大量闪烁,因为鼠标会短暂进入禁止矩形后才被驱逐。</p>
<p>客户发现有一个<code>ClipCursor</code>函数可以将鼠标限制在矩形<i>内部</i>,但是否有反向版本可以将鼠标强制保持在矩形<i>外部</i>?</p>
<p>没有这样的函数,但这没关系。</p>
<p>当鼠标位于非法位置时,你只需假装它在合法位置即可。让用户将鼠标移入非法位置,但将反馈显示在最近的合法位置,如果用户放下对象,则将其放置在最近的合法位置。</p>
<p>在上述程序中,这意味着我们移除对<code>SetCursorPos</code>的调用。</p>
<pre>void OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
{
POINT ptNew = { x, y };
if (PtInRect(&g_rcExclude, ptNew)) {
// 限制到最近的合法位置
int leftMargin = ptNew.x - g_rcExclude.left;
int topMargin = ptNew.y - g_rcExclude.top;
int rightMargin = g_rcExclude.right - ptNew.x;
int bottomMargin = g_rcExclude.bottom - ptNew.y;
int dx, dy;
int x, y;
if (leftMargin < rightMargin) {
x = g_rcExclude.left;
dx = leftMargin;
} else {
x = g_rcExclude.right;
dx = rightMargin;
}
if (topMargin < bottomMargin) {
y = g_rcExclude.top;
dy = topMargin;
} else {
y = g_rcExclude.bottom;
dy = bottomMargin;
}
if (dx < dy) {
ptNew.x = x;
} else {
ptNew.y = y;
}
<span style="border: dashed 1px currentcolor; border-bottom: none;">// <span style="text-decoration: line-through;">POINT ptScreen = ptNew;</span> </span>
<span style="border: 1px currentcolor; border-style: none dashed;">// <span style="text-decoration: line-through;">ClientToScreen(hwnd, &ptScreen);</span> </span>
<span style="border: dashed 1px currentcolor; border-top: none;">// <span style="text-decoration: line-through;">SetCursorPos(ptScreen.x, ptScreen.y);</span></span>
}
if (g_pt.x != ptNew.x || g_pt.y != ptNew.y) {
RECT rcItem = ItemRect(g_pt);
InvalidateRect(hwnd, &rcItem, TRUE);
g_pt = ptNew;
rcItem = ItemRect(g_pt);
InvalidateRect(hwnd, &rcItem, TRUE);
}
}
</pre>
<p>这次,我们不再惩罚鼠标移入禁止矩形,但对象不会跟随鼠标进入禁止区域。</p>
<p>原文 <a href="https://devblogs.microsoft.com/oldnewthing/20260610-00/?p=112412">What’s the opposite of <CODE>ClipCursor</CODE> that lets me <I>exclude</I> the cursor from a region?</a> 首次出现在 <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a> 上。</p>
相似文章
@ericzakariasson:在 Cursor 里试试 shadcn 插件!
一条推文建议在 Cursor 编辑器中试用 shadcn 插件。
别劫持我的鼠标指针
作者批评了用自定义动画效果替换标准网页光标的趋势,认为这些装饰严重损害了可用性。作者呼吁开发者优先考虑实用的用户体验(UX),而不是现代 AI 辅助编码工具所催生的炫目视觉效果。
@ericzakariasson: cursor 设计模式 + remotion 真的不可思议 remotion 中一切都是代码,因此可以直接在视频上标注更改,然后 cursor 自动更新代码…
Cursor 的设计模式可以与 Remotion(基于代码的视频编辑工具)结合,直接在视频上标注更改,而 Cursor 会自动更新底层代码。
为什么Cursor收购案应该让每个软件开发者担忧
文章对Cursor编码助手被收购表示担忧,强调了能够访问专有源代码的工具所需的独特信任,以及当所有权转移到拥有多元化利益的大型实体时存在的风险。
@ClementDelangue:Cursor 即将告别 Claude?
一条推文暗示,Claude AI 可能很快会从 Cursor 编辑器中移除。