GUI库:如何正确处理焦点

GUI library: how to correctly handle focus?

本文关键字:正确处理 焦点 GUI      更新时间:2023-10-16

我正在为我的游戏开发项目制作一个非常小的游戏内 GUI 库,但我很难找到一种干净的方式来处理焦点。

我的库支持嵌套的列表框和小部件层次结构,但我似乎找不到一种方法来防止处理小部件焦点时的奇怪行为。示例表单:

|------------|
| Form   [X] |
|------------|
|            |
| [Button01] |
|            |
| [List1][v] |
|            |
| [Button02] |
|            |
|------------|

我的设计具有一个存储Widget对象列表的Context对象。一个Widget可以有任意数量的孩子。我有递归迭代Widget的所有孩子/父母的功能。

我目前的重点逻辑是:

  1. 如果上下文繁忙(拖动、调整大小、编辑...),请不要更改焦点

  2. 如果鼠标按下在上下文之外或上下文未聚焦,则取消聚焦所有内容

  3. 找到压力最大的孩子(如果有的话)

  4. 在层次结构中找到"最深"的受压孩子

  5. 像最深的孩子一样深的小部件之外,取消聚焦所有内容,并聚焦上下文

但是,这在许多情况下会失败,尤其是对于嵌套列表和文本框。我试图在网上找到一个好的解决方案,但找不到任何文章/教程。我不确定我是否应该专注于特殊的小部件,或者是否有一种"好"的算法在任何情况下都能正常工作。


GUI 库通常如何处理小部件焦点?

是否有特殊说明来专门关注列表框或组合框等小部件?

这是我的 GUI 库的源代码,这里有一个视频。

我建议定义一个类似数据的树状结构,该结构将节点定义为数据(窗口句柄,bool isFocus和您需要的一些其他数据)加上一些指向节点的指针 - 父级,第一个孩子,左兄弟姐妹,右兄弟姐妹。此外,将节点指针存储在窗口数据区域中,以便可以从给定的窗口句柄检索节点指针。从节点指针,数据无论如何都会给你窗口句柄。现在,每当根窗口失去焦点时,因为您在其他应用程序上单击鼠标或点击 alt+tab 或其他内容,主窗口就会失去焦点,并且您会获得适当的终止焦点消息。处理它,从窗口句柄重置所有是节点树的焦点。每当你点击某个窗口时,你会收到增益焦点消息,从窗口句柄找出节点,将isFocus设置为true。并将其所有父节点的 isFocus 设置为 true,直到到达顶部节点(根)。有帮助吗?

我不知道

任何 UI 算法或 UI 的正确方法是什么,但是我很久以前确实实现了一个通用 UI 库(不是最好的,但它在当时达到了它的目的),也许使用的方法可以满足您的需求。

为了处理从父母到孩子的事件,使用了两个函数,具有以下原型:

int handleEventReceived(int eventType, void* data);
int getEventConsumers(int eventType, vector<IConsumers*>& consumers);

在枚举中找到 Ofc 事件类型。

在所有子项

中实现第一个函数后,可以使用递归函数将事件发送到每个子项和子项的子项(因此是广播)。当子项回答事件时,它返回 0,否则返回一些错误代码(例如 ERRROR_EVENT_NOT_TREATED)。

但是,如果您只希望 UI 的特定部分响应某个事件,请使用第二个函数。

第二个函数返回"最大响应优先级",以及与该优先级关联的消费者。例如,如果您的 contaier 有 3 个优先级为 5 的按钮和 2 个优先级为 6 的按钮,它将返回 6 和一个包含这两个按钮的数组,您可以将事件发送到该数组。

在您的情况下,您说您需要处理子组件的焦点。使用上述方法,我将创建一个事件ET_FOCUS_RECEIVED,并根据需要在每个UI元素上放置该事件的不同优先级。

在你的情况下,如果你把事件的优先级随着元素在层次结构中的深入而增加,来自相同深度的元素将具有相同的优先级(所以和最深的孩子一样深的小部件都会处理事件)。