2.2 GTK输入与事件处理概述

本章详细介绍GTK如何处理输入。如果你想了解用户的按键或鼠标移动操作如何转化为GTK控件的变化,那么应该阅读本章。如果你打算实现自己的控件,这些知识也会很有帮助。
2.2.1 设备(Device)与事件
每个计算机用户都会接触到的最基本输入设备是键盘和鼠标;除此之外,GTK 还支持触控板、触摸屏以及更特殊的输入设备(如绘图板)。在 GTK 内部,每一种此类输入设备都由一个 GdkDevice 对象表示。
为简化对这些输入设备之间差异的处理,GTK 提出了逻辑设备和物理设备的概念。具有多种不同特性的具体物理设备(鼠标可能有 2 个、3 个或 8 个按键,键盘有不同的布局,可能有也可能没有独立的数字小键盘等)被表示为物理设备。每个物理设备都与一个虚拟的逻辑设备相关联。逻辑设备通常以“指针/键盘”对的形式存在——你可以将这样的一对设备视为一个“操作位(seat)”。
GTK 控件通常与逻辑设备交互,因此可以与任何指向设备或键盘配合使用。
当用户与输入设备交互时(例如移动鼠标或按下键盘上的按键),GTK 会从窗口系统接收事件。这些事件通常指向特定的绘面——对于指针事件,指向指针下方的绘面(抓取操作会使情况复杂化);对于键盘事件,指向拥有键盘焦点的绘面。
GDK 会将这些原始的窗口系统事件转换为 GdkEvents。典型的输入事件包括按钮点击、指针移动、按键按下或触摸事件。这些事件都以 GdkEvents 的形式表示,但你可以使用 gdk_event_get_event_type() 函数通过查看事件类型来区分不同的事件。
有些事件(如触摸事件或按钮的按下-释放对)会相互关联形成“事件序列(event sequence)”,该序列可唯一标识与同一交互相关的事件。
当 GTK 创建 GdkSurface 时,会连接到其上的“event”信号,该信号会接收所有这些输入事件。绘面具有信号和属性,例如用于处理与窗口管理相关的事件。
2.2.2 事件传播
在 GTK 端最初接收输入事件的函数负责多项任务:

  1. 找到接收事件的控件。
  2. 当焦点或悬停位置从一个控件切换到另一个控件时,生成交叉事件(即进入事件和离开事件)。
  3. 将事件发送到控件。
    事件会在控件层级结构中沿着目标控件经历三个阶段的向下和向上传播(参见 GtkPropagationPhase)。

图片1

图 14事件传播阶段
对于键盘事件,顶级窗口会首先激活助记键和全局键。如果这些操作没有消耗事件,那么事件传播的目标控件就是窗口当前的焦点控件(参见 gtk_window_get_focus())。
对于指针事件,目标控件是通过在事件坐标处选取控件来确定的(参见 gtk_widget_pick())。
在第一个阶段(“捕获(capture)”阶段),事件会从最顶层的控件(顶级 GtkWindow 或抓取控件)向下传递到目标 GtkWidget。以 GTK_PHASE_CAPTURE 方式附加的事件控制器(Event controller)有机会对事件做出反应。
“捕获”阶段之后,预定作为事件目标的控件会运行以 GTK_PHASE_TARGET 方式附加到自身的事件控制器。这一阶段称为“目标(arget)”阶段,且仅在该控件上发生。
在最后一个阶段(“冒泡(bubble)”阶段),事件会从目标控件向上传递到最顶层的控件,同时运行以 GTK_PHASE_BUBBLE 方式附加的事件控制器。
事件不会传递给不敏感或未映射的控件。
在传播阶段的任何时候,控制器都可以表明已处理收到的事件,因此应停止传播。如果使用了手势,当手势为自身声明事件触摸序列(或指针事件)时,可能会发生这种情况。有关手势和序列的更多信息,请参见下面的“手势状态”部分。
2.2.3 键盘输入
每个 GtkWindow 都维护着一个焦点位置(通过“focus-widget”属性)。焦点控件是发送到窗口的键盘事件的目标控件。只有“can-focus”属性设为 TRUE 的控件才能获得焦点。这类控件通常是输入控件,如输入框或文本字段,但按钮等也可以获得焦点。
点击输入控件可以使其获得焦点,不过也可以通过特定的键盘事件来移动焦点(这被称为“键盘导航”)。GTK 保留 Tab 键用于将焦点移动到下一个位置,Shift-Tab 用于将焦点移回上一个位置。此外,许多容器支持使用方向键进行“定向导航”。
许多控件可以通过“激活”来触发动作。例如,点击按钮或开关可以激活它们,也可以通过键盘的回车键或空格键来激活。
除了键盘导航、激活控件以及直接在输入框或文本视图中输入内容外,GTK 控件还可以使用键盘事件来激活“快捷键”。快捷键通常用于快速移动焦点或激活当前没有焦点的控件。
GTK 传统上支持多种快捷键:
• 全局键(Accelerator):这类快捷键无论焦点在何处都能激活,通常用于触发全局操作,例如 Ctrl-Q 用于退出应用程序。
• 助记键(Mnemonic):通常使用 Alt 键配合某个字母来触发。它们用于标签与控件相关联的场景,标签中对应的字母会带有下划线作为标识。特殊情况下,在菜单中(即 GtkPopoverMenu 内部),无需修饰键即可触发助记键。
• 绑定键(Key binding):特定于单个控件,例如输入框中的 Ctrl-C 或 Ctrl-V 分别用于复制到剪贴板或从剪贴板粘贴。只有当控件获得焦点时,它们才能被触发。
GTK 在全局范围内的捕获阶段处理全局键和助记键,在目标阶段本地处理绑定键。
在底层,所有快捷键都以 GtkShortcut 实例的形式存在,并由 GtkShortcutController 进行管理。
2.2.4 事件控制器与手势(gesture)
事件控制器是独立的对象,能够对接收的 GdkEvent 执行特定操作。它们与 GtkWidget 绑定,并且可以指定在事件传播的哪个阶段对事件进行处理。
手势是一组特定的控制器,用于处理指针和/或触摸事件。每种手势实现都会尝试从接收的事件中识别特定动作,并相应地通知状态/进度,以便控件对这些动作做出反应。在多点触控手势中,每个交互的触摸序列都会被独立跟踪。
由于手势是“简单”的单元,将多个手势绑定在一起以执行更高级别的操作并不少见。组合手势会同时处理相同的事件序列,且这些序列在所有组合手势中共享相同的状态。组合的例子包括:
• “拖动(drag)”和“滑动(swipe)”手势可能组合。前者会在拖动发生时报告事件,后者则仅在识别完成后告知滑动的 X/Y 速度。
• 将“拖动”手势与“平移(pan)”手势组合时,实际上仅允许在平移方向上进行拖动,因为这两种手势共享状态。
• 如果需要同时支持“按压(press)”和“长按(long press)”,则这两种手势需要组合。
快捷键由 GtkShortcutController 处理,这是一个复杂的事件处理器,它可以根据自身的“作用域(scope)”,要么自己激活快捷键,要么将快捷键传播给另一个控制器。
2.2.5 手势状态
手势对每个独立的触摸序列都有“状态(state)”的概念。当首次接收到某个触摸序列的事件时,该触摸序列处于“无(none)”状态,这意味着该触摸序列正由手势处理以可能触发动作,但事件传播不会停止。
当手势进入识别阶段,或在之后的某个时刻,控件可以选择声明这些触摸序列(单独或组合),从而在事件经过该控件中每个手势及相应传播阶段后停止事件传播。每当发生这种情况,触摸序列会在传播链下游被取消,以告知后续环节不会再收到相关事件。
此外,控件也可以在之后的某个时刻选择拒绝触摸序列,从而让这些序列重新进入事件传播流程。如果在捕获阶段发生这种情况,且该控件中没有其他声明手势,系统会模拟一个 GDK_TOUCH_BEGIN 或 GDK_BUTTON_PRESS 事件并向下传播,以保持一致性。
组合手势对于特定触摸序列始终共享相同的状态,因此在一个手势上设置的状态会传递到组内其他手势。它们在同一控件内也是互斥的——对于给定序列,只能有一个手势组进行声明。如果另一个手势组后来声明了同一序列,第一个组会自动拒绝该序列。

通过网盘分享的知识:GTK 4 Reference Manual
链接: https://pan.baidu.com/s/57mKoejMZPrxs_wh3a7Kj9g
--来自百度网盘超级会员v7的分享

posted @ 2025-08-05 08:24  Hoijuon  阅读(35)  评论(0)    收藏  举报