有没有与ClipCursor相反,可以排除光标区域的函数?

The Old New Thing (Raymond Chen) 新闻

摘要

一位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-&gt;hdc, &amp;g_rcExclude, (HBRUSH)(COLOR_WINDOWTEXT + 1)); RECT rcItem = ItemRect(g_pt); FillRect(pps-&gt;hdc, &amp;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(&amp;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 &lt; rightMargin) { x = g_rcExclude.left; dx = leftMargin; } else { x = g_rcExclude.right; dx = rightMargin; } if (topMargin &lt; bottomMargin) { y = g_rcExclude.top; dy = topMargin; } else { y = g_rcExclude.bottom; dy = bottomMargin; } if (dx &lt; dy) { ptNew.x = x; } else { ptNew.y = y; } POINT ptScreen = ptNew; ClientToScreen(hwnd, &amp;ptScreen); SetCursorPos(ptScreen.x, ptScreen.y); } if (g_pt.x != ptNew.x || g_pt.y != ptNew.y) { RECT rcItem = ItemRect(g_pt); InvalidateRect(hwnd, &amp;rcItem, TRUE); g_pt = ptNew; rcItem = ItemRect(g_pt); InvalidateRect(hwnd, &amp;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(&amp;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 &lt; rightMargin) { x = g_rcExclude.left; dx = leftMargin; } else { x = g_rcExclude.right; dx = rightMargin; } if (topMargin &lt; bottomMargin) { y = g_rcExclude.top; dy = topMargin; } else { y = g_rcExclude.bottom; dy = bottomMargin; } if (dx &lt; 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, &amp;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, &amp;rcItem, TRUE); g_pt = ptNew; rcItem = ItemRect(g_pt); InvalidateRect(hwnd, &amp;rcItem, TRUE); } } </pre> <p>这次,我们不再惩罚鼠标移入禁止矩形,但对象不会跟随鼠标进入禁止区域。</p> <p>原文 <a href="https://devblogs.microsoft.com/oldnewthing/20260610-00/?p=112412">What&#8217;s the opposite of &lt;CODE&gt;Clip­Cursor&lt;/CODE&gt; that lets me &lt;I&gt;exclude&lt;/I&gt; the cursor from a region?</a> 首次出现在 <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a> 上。</p>
查看原文

相似文章

别劫持我的鼠标指针

Hacker News Top

作者批评了用自定义动画效果替换标准网页光标的趋势,认为这些装饰严重损害了可用性。作者呼吁开发者优先考虑实用的用户体验(UX),而不是现代 AI 辅助编码工具所催生的炫目视觉效果。