GTK 动态

GTK 4.2 计划于 3 月发布 – 这不会是一个巨大的版本,只是增量改进。但除了通常的错误修复和性能改进之外,还有一些值得单独指出的内容。

新的 GL 渲染器

Christian Hergert 一直在努力工作,为 GTK 创建一个新的 GL 渲染器。这项工作的最初动机是希望提高我们在 MacOS 上的渲染性能,因为 MacOS 上的 GL 驱动程序不像 Linux 上那样宽容。除此之外,从头开始使用新的渲染器,我们可以有机会应用我们在当前 GL 渲染器上工作时学到的所有东西,并重新组织代码,以便将来进行改进,例如重新排序和批量处理绘图命令。

新的渲染器尚未合并,但它正在接近功能对等,很可能进入 4.2 版本。我们可能会同时包含旧的和新的渲染器,至少一段时间是这样。

弹出框阴影

自从引入 GtkPopover 及其标志性的“喙”以来,弹出框一直会裁剪其边框之外的所有内容,因为我们需要喙的尖端保持一致的位置。随着 GTK4 中基于 xdg-popup 的新实现,我们有了一个表达力足够的定位协议,可以以一种方式放置弹出框,使“喙”指向它应该指向的位置,同时允许弹出框下方和周围有阴影。与窗口阴影一样,弹出框阴影位于输入区域之外,因此点击会穿透到下面的窗口。

这是一个小改动,但它可能会在赋予 UI 深度和结构方面产生显著的影响。

更好的输入

GtkIMContextSimple 是内置于 GTK 中的输入法实现。当没有平台方法可用时,例如 Wayland 文本协议,会使用它。GtkIMContextSimple 只做一些事情。其中之一是使用 Control-Shift-u 来解释 Unicode 字符的十六进制输入。另一个是它处理组合序列,例如

<Compose Key> <a> <acute>

输入 á 字符。

大多数组合序列都以 组合键 开头,GNOME 40 中的键盘设置将包括一种方法,可以将键盘上的一个键分配给此功能。

在 GTK 方面,我们解决了一些关于组合序列支持的长期抱怨。除了其内置序列之外,GTK 还解析 X11 组合文件。这些文件的格式在 Compose(5) 中描述,但到目前为止,GTK 对此格式的支持相当不完整。使用 GTK 4.2,我们正在改进这一点,以

  • 允许最多 20 个键的序列(以前,限制为 7 个)
  • 生成多个字符(特别是,这允许 Unicode Emoji 序列)
  • 支持十六进制码位

这些对于那些通过编辑 ~/.Compose 来制作自己的组合序列的人来说是很不错的改进。但我们其他人呢?使用组合序列的一个传统难题是,您必须牢记这些序列,并且盲目地键入它们。在序列完成并且最终字符出现之前,没有任何视觉反馈。不久前,IBus 通过将不完整序列的字符显示为带下划线的预编辑文本来改进了这一点,类似于我们对十六进制 Unicode 输入所做的方式。

在为 GTK 复制他们的方法后,最初的用户反馈褒贬不一,主要是因为组合键 (⎄) 的官方字形在意外出现时有点分散注意力。所以我回到了绘图板,提出了另一种方法

我希望这样效果更好。欢迎反馈!

所有这些输入更改也将出现在 GTK 3.24.26 中。

GTK 4.0.1

在 4.0 之后,我们都休息了一会儿,做了一些其他事情,但现在是 GTK 4.0.1 版本的时候了。

这是 4.0 之后的第一个版本,它自然包含许多小的错误修复、主题和文档改进等。但是有一些值得指出的亮点。

更好的媒体支持

此版本中较大的进展之一:我们设法使 gstreamer 媒体后端使用 GL 纹理,这避免了在使用硬件加速进行解码(例如 vaapi)时在 gpu 和 cpu 之间来回传输帧数据。这需要仔细的编排才能弥合 gstreamer 和 GTK 处理 GL 的方式之间的差异,但我们设法在许多情况下使其工作。

这是否意味着 GtkVideo 现在可以支持功能齐全的媒体播放器应用程序了?远非如此。它仍然只允许您播放来自文件或 url 的媒体,并且不支持多声道音频、视频覆盖、设备选择、输入以及您可能在媒体播放器中需要的其他功能。

如果有人将 GTK 媒体后端中的代码翻转过来,使其成为一个 GStreamer 插件,其接收器将其视频帧作为 GdkPaintable 公开,那将非常好。这将使您可以使用 gstreamer API 来获取上述所有功能,同时仍然平稳地集成到 GTK 中。

更好的 CI

为了保持我们新的 MacOS 后端的正常工作状态,我们已经开始为该平台设置 CI 构建,包括 GTK 本身及其依赖项 (pangogdk-pixbuf)。

GTK 4.0

2020 年是很漫长的一年。有什么比发布主要版本更好的方式来结束它!今天,我们发布了 GTK 4.0。

GTK 4.0 是一个由一小群敬业的开发人员辛勤工作的结果。我们将单独发布一篇帖子来回顾统计数据,但简短的总结是,自 2016 年 11 月的 3.89.1 版本以来,我们添加了超过 18000 个提交,并发布了超过 20 个开发版本。

祝贺并非常感谢每一位参与此项工作的人,特别是 Benjamin、Emmanuele、Timm、Carlos、Jonas 和 Christian!

新功能

不可能在一篇文章中总结 4 年的开发工作。在过去的一年中,我们撰写了关于此版本中许多新功能的详细文章:数据传输事件控制器布局管理器渲染节点媒体播放可缩放列表着色器辅助功能。以下是一些以视觉形式呈现的亮点

媒体播放

拖放

布局管理器和转换

可缩放列表和网格

着色器

旧功能

GTK 4 现在是稳定的,我们认为它已准备好使用。这并不意味着 GTK 3 已经消亡 – 我们将在可预见的未来继续支持和更新它(最新版本 3.24.24 几天前悄然发布)。然而,这确实意味着 GTK 2 已经走到了尽头。我们将在未来几天发布最后一个 2.x 版本,并鼓励所有人将其 GTK 2 应用程序移植到 GTK 3 或 4。

如何获取它

源 tarball 可以在通常的位置找到。二进制包应很快出现在主要发行版中。

GNOME 40 版本将有许多应用程序移植到 GTK 4。如果您想立即试用 GTK 4.0,可以使用 gtk4-demogtk4-widget-factory 的每晚 flatpak 构建。

$ flatpak install https://nightly.gnome.org/repo/appstream/org.gtk.Demo4.flatpakref
$ flatpak run org.gtk.Demo4

如果您渴望将您的应用程序移植到 GTK 4,我们的迁移指南可在此处找到,作为文档的一部分。

如何支持 GTK

如果没有众多志愿者贡献错误报告、补丁、翻译或想法,GTK 就无法开发。感谢你们所有人。我们也感谢 GNOME 基金会为 GTK 提供开发资源、基础设施和旅行援助的支持。

向 GNOME 基金会捐款是支持未来 GTK 开发的好方法。

接下来是什么

我们非常感谢所有早期测试人员向我们提供的错误报告和反馈,这使此版本更好。但我们完全预期会快速发布 4.0.1 版本,以修复在 .0 版本之后才会暴露出来的疏忽和陷阱。

现在我们有了 4.0 版本,我们需要将库生态系统引入其中,以使应用程序能够使用它。vte、webkit 和 gtksourceview 是一些最常用的 GTK 库。我们预计这些库的 GTK 4 端口很快就会可用。

如果出现更严重的问题,我们将在 GNOME 40 的时间内发布 4.2 版本,否则我们可能会等到夏天才发布。

GTK 4 之后的未来会怎样,仍有待观察。我们有一些有趣的项目正在进行中,但也想听听应用程序开发者希望在 GTK 中看到哪些功能。请告诉我们!

庆祝活动

我们将在本周五举办一个(线上)聚会来庆祝 4.0 版本的发布。欢迎随时加入!

GTK 4 中的可访问性

上周发布的 GTK 3.99.3 版本 中的重大消息是,我们为新的可访问性实现提供了第一个重要的后端。因此,现在是深入了解 GTK 4 中的可访问性的好时机。

概述

让我们从快速回顾一下 Linux 上可访问性的工作原理开始。其中的参与者是应用程序和 辅助技术 (AT),例如屏幕阅读器(例如,Orca)、放大镜等。

辅助技术 (AT) 的目的通常是为用户提供与应用程序交互的替代方式,这些方式是根据他们的需求量身定制的(例如,放大视图、大声朗读文本或语音命令)。为此,辅助技术 (AT) 需要大量关于应用程序 UI 的详细信息,这就是可访问性堆栈发挥作用的地方——它是应用程序(或其工具包)和辅助技术 (AT) 之间的连接层。

应用程序和辅助技术 (AT) 通过可访问性总线相互通信,这是一个独立的 D-Bus 会话总线实例,使用 AT-SPI 项目描述的接口。应用程序的 UI 元素在总线上表示为对象,这些对象实现了某种抽象接口,例如 TextValue。应用程序发出信号来传达 UI 中的更改,并且辅助技术 (AT) 可以调用对象上的方法来获取信息或进行更改(例如,更改 Value 接口的当前值以移动它所代表的 GtkScale)。

发生了什么变化

在 GTK 2 和 3 中,这是以一种笨拙的间接方式完成的:GTK 小部件具有辅助的可访问对象,这些对象是 ATK 接口的实现(转换 1:GTK ➙ ATK)。然后,这些对象被转换为 AT-SPI 对象(转换 2:ATK ➙ AT-SPI),这些对象通过 at-spi2-atk 中的适配器代码在可访问性总线上表示。在另一端,辅助技术 (AT) 然后使用 pyatspi 将 AT-SPI 接口转换为 Python 对象(转换 3:AT-SPI ➙ Python)。

这个多步骤过程效率低下、有损耗且难以维护;它需要在至少三个组件上实现相同的功能,并且导致记录的 AT-SPI 方法和属性与实际通过可访问性总线发送的方法和属性之间存在差异。

在 GTK 4 中,我们通过删除 ATK 和 at-spi2-atk 来简化应用程序端。小部件现在实现了一个 GtkAccessible 接口,该接口允许它们设置许多角色、状态、属性和关系,这些角色、状态、属性和关系或多或少直接取自 W3C 发布的 WAI-ARIA 规范。GTK 可访问性 API 的 AT-SPI 后端然后会获取这些受 ARIA 启发的属性(以及小部件本身的知识),并将小部件表示为可访问性总线上的对象,为它们实现相关的 AT-SPI 接口。

这是一种更加直接的方法,与 Qt 和 Web 浏览器已经做的事情相匹配。

应用程序 API

以下是您在应用程序中使用 GTK 4 时最有可能遇到的可访问性 API 的要点

设置可访问的角色。角色是对小部件语义的描述,辅助技术 (AT) 将使用它来决定应该向其用户呈现什么样的行为。设置角色是一次性操作,这意味着它必须在小部件创建时完成,无论是在 class_init 中,还是在实例初始化期间

gtk_widget_class_set_accessible_role (widget_class,  
                                      GTK_ACCESSIBLE_ROLE_BUTTON);

更新小部件的可访问状态或属性。每当小部件的可访问表示发生更改时,都应该执行此操作

gtk_accessible_update_property (GTK_ACCESSIBLE (widget),
                       GTK_ACCESSIBLE_PROPERTY_VALUE_MIN, minimum,
                       GTK_ACCESSIBLE_PROPERTY_VALUE_NOW, value,
                       GTK_ACCESSIBLE_PROPERTY_VALUE_MAX, maximum,
                       -1);

GTK 参考文档中有一个可访问性 API 的概述,其中包含针对应用程序开发人员和小部件编写人员的指导。

下一步是什么?

对于 GTK 4.0,我们将重点关注完成 AT-SPI 后端。但是,通过新的 API 和后端分离,我们有了一条明确的路径,可以为其他平台制作可访问性后端,这是我们希望在后续 GTK 4 版本中研究的内容。

在 Linux 上,我们希望与其他利益相关者合作,使 AT-SPI 接口现代化,以最终克服在某些地方仍然可见的 CORBA 遗留问题。其中的一部分是将从可访问性总线转向应用程序和辅助技术 (AT) 之间的对等连接;这将增强可访问性堆栈的安全性,并弥补 Flatpak 等技术使用的沙箱中的漏洞。

未来,我们希望引入工具来确保应用程序开发人员意识到缺少可访问性注释,例如为他们 UI 中的图标和图像提供标签属性或 labelled-by 关系;或者确保每个 UI 元素都正确地表示在可访问的树中。我们已经为 GtkAccessible 接口提供了一个测试后端,该后端可用于编写单元测试并验证在必要时是否更新了角色和属性。

GTK 3.99.2

GTK 3.99.2 版本延续了 3.99.1 的主题:api 清理、新的和改进的演示、更好的文档。您可以在此处查看详细信息。

关于文档主题的一点说明是,我们依赖于一些未发布的 gtk-doc 功能。因此,我们现在将 gtk-doc 作为子项目包含在 gtk 发布 tarball 中。如果您是发行商,请不要惊讶于现在构建 GTK 会安装 gtk-doc 工具。

此快照中的重大消息是我们在更多地展示新的基于 GL 的渲染堆栈的强大功能方面所做的工作。

热身:Shadertoy

gtk4-demo 现在包含一个 Shadertoy 演示。

该演示使用 GtkGLArea 小部件来运行与 shadertoy.com 上找到的 GLSL 代码片段兼容的代码片段。如果您将示例粘贴到此演示的编辑器中,则可以在那里找到的许多示例都可以工作。

这很有趣,但有些局限性。GLSL 被限制在它的“沙箱”中,即 GtkGLArea 小部件,它使用 GL api 来编译和使用着色器。

着色器作为一等对象

这不是我们第一次尝试制作一个类似 shadertoy 的东西。当我们第一次看到它时,我们认为我们会制作一个应用程序可以使用的着色器抽象。当发现要使其在不同的渲染器和后端上工作需要我们编写自己的着色器编译器时,我们就把它放在一边了——工作量太大了。

但是在我们的 shadertoy 成功之后,我们重新审视了将着色器作为一等对象的想法,目标更加适中:我们使用 GLSL,并且不尝试让着色器与 OpenGL 渲染器以外的任何东西一起工作。

在 3.99.2 中,我们现在有

有了这些部分,我们制作了一个演示,展示了着色器的各种用途。它可能有点超载,并且一些效果有点过分,但它表达了要点:您可以在小部件中使用着色器。

 

我们尚未完成的是添加具有内置着色器支持的小部件。该演示展示了一些可能的候选项

着色器可绘制对象。您可能记得,GdkPaintable 是一个非常灵活的接口,适用于任何可以“绘制”的东西。着色器当然符合条件。gtk-demo 中的 GskShaderPaintable 使用一个没有输入纹理的着色器来仅生成像素,我们将其添加到 GtkPicture 小部件中,使其出现在小部件树中。

一个着色器容器。这是一个非常简单的容器,可以使用着色器在子部件之上绘制效果。它适用于采用单个输入纹理(用于子部件)的着色器。

一个着色器堆栈。这是一个类似堆栈的容器,显示多个子部件中的一个,并在可见子部件更改时使用着色器进行过渡。它适用于需要两个输入纹理(用于旧的和新的活动子部件)的着色器。

值得庆幸的是,在 GTK 4 中制作自定义小部件比以前容易得多,因此渲染节点 API 应该足以让你开始一些有趣的实验。你当然可以把 gtk4-demo 代码作为起点。

你可以调试它

除了小部件之外,着色器支持是完全集成的。GTK 检查器可以像处理任何其他渲染节点一样处理着色器节点,你可以序列化它们,例如在 gtk4-node-editor 中加载生成的文件

如果你需要查看 GTK 发送到着色器编译器的输入,设置环境变量

GDK_DEBUG=shaders

可能会有所帮助。

下一步是什么?

在这个 GL 冒险之后,我们将专注于实现更多新的辅助功能基础设施。

GtkColumnView

在我最近关于 GTK 4 中列表视图模型的系列文章中,我留下了一个未完成的部分,即对 GtkColumnView 的详细介绍。这将很容易成为该系列中最复杂的部分。我们正在进入 GtkTreeView 的核心领域——任何旨在取代其大部分功能的东西都将是一个复杂的庞然大物。

概述

就像我们为 GtkListView 所做的那样,我们将从一个高层次的概述和一张图片开始。

如果你回顾列表视图图片,你会记得我们使用列表项工厂为模型中需要显示的每个项目创建一个小部件。

在列视图中,我们需要为每个项目提供多个小部件,每个列一个。我们通过为每个列提供自己的列表项工厂来实现这一点。每当我们需要显示一个新项目时,我们将来自每个列工厂的小部件组合成一个新项目的行。

在内部,列视图实际上使用列表视图来保存行。这很好,因为我在上一篇文章中解释的所有关于项目重用以及如何使用列表项工厂的内容都同样适用。

当然,有些事情是不同的。例如,列视图必须组织大小分配,以便所有行中的小部件排列成适当的列。

注意:就像 GtkListView 一样,列视图仅为当前视图中的模型段创建小部件,因此它共享垂直可伸缩性。水平方向的情况并非如此——每一行都完全填充了每个列的小部件,即使它们在左侧或右侧的视图之外。因此,如果你添加大量列,事情会变得很慢。

标题和其他复杂情况

列对象还包含其他数据,例如标题。列视图使用这些数据来显示每个列的标题。如果列视图被标记为可重新排序,则可以通过拖放标题小部件来重新排列列。如果列被标记为可调整大小,则可以拖动两列之间的边框来调整它们的大小。

如果你注意到了,你现在可能想知道这种大小调整如何与行中的单元格可以是任意小部件的事实结合在一起,这些小部件希望至少具有可用于绘制其内容的最小大小。答案是,我们正在使用 GTK 4 渲染机制的另一个新功能:小部件可以控制如何处理其边界(由子小部件)之外的绘制,使用

 gtk_widget_set_overflow (cell, GTK_OVERFLOW_HIDDEN)

排序、选择和对 treeview 对等性的追求

由于我们希望在功能上与 GtkTreeview 相匹配,因此我们尚未完成。用户喜欢在树视图中做的另一件事是单击标题,以按该列对内容进行排序。GtkColumnView 标题也允许这样做。

你可能还记得上一篇文章,排序是通过将你的数据包装在 GtkSortListModel 中,并为其提供合适的排序器对象来完成的。由于我们希望根据你单击的列标题具有不同的排序顺序,因此我们为每个列提供自己的排序器,你可以使用以下代码进行设置

gtk_column_view_column_set_sorter (column, sorter)

但是我们如何从你刚刚单击的列中获取正确的排序器,并将其附加到排序模型?请记住,排序模型会是我们传递给列视图的最外层模型,因为它始终是一个选择模型,因此列视图无法自行在排序列表模型上切换排序器。

我们提出的解决方案是使列视图提供一个内部使用列排序器的排序器,使用

gtk_column_view_get_sorter (view)

你可以在设置模型时,将此排序器一次性提供给你的排序模型,然后当用户单击列标题以激活不同的列排序器时,内容将自动更新。

这听起来很复杂,但它运行得出奇地好。这种方法的一个好处是,我们实际上可以同时按多个列进行排序——因为我们拥有所有可用的列排序器,并且我们知道你最后单击了哪个排序器。

相比之下,选择处理很简单。它的工作方式与 GtkListView 中的相同。

总结

GtkColumnView 是一个复杂的小部件,但我希望这个系列的文章能让开始使用它变得更容易一些。

关于列表模型

在上一篇文章中,我承诺将深入研究列表模型以及 GTK 4 在这方面提供的功能。让我们首先看一下GListModel接口

struct _GListModelInterface
{
  GTypeInterface g_iface;

  GType    (* get_item_type) (GListModel *list);
  guint    (* get_n_items)   (GListModel *list);
  gpointer (* get_item)      (GListModel *list,
                              guint       position);
};

实现接口的一个重要部分是,你需要发出
当需要时使用 ::items-changed 信号,使用辅助函数
GLib 为此目的提供

void g_list_model_items_changed (GListModel *list,
                                 guint       position,
                                 guint       removed,
                                 guint       added)

关于此接口的一些注意事项

  • 非常简洁;这使其易于实现
  • API 以位置为单位,仅处理列表成员的更改——跟踪项目本身的更改由你负责

列表模型动物园

GTK 提供了一个相当多的列表模型实现集合。经过仔细检查,它们分为几个不同的组。

列表模型构建工具包

第一组可以称为列表模型构建工具包:让你通过修改或组合你已有的模型来构建新模型的模型。

该组中的第一个模型 GtkSliceListModel,获取现有模型的切片(由偏移量和大小给出),并创建一个仅包含这些项的新模型。如果你想在分页视图中呈现一个大列表,这将很有用——向前和向后按钮只会将偏移量增加或减少大小。切片模型还可以用于通过随着时间的推移使切片变大来增量填充列表。GTK 在某些地方使用了这种技术。

该组中的下一个模型 GtkFlattenListModel,采用多个列表模型并将它们组合为一个。由于这完全与列表模型有关,因此要组合的模型以列表模型的列表模型形式传递给平展模型。当你需要组合来自多个来源的数据时,这很有用,例如 GTK 在打印对话框中处理纸张尺寸时所做的那样。

Paper size list in print dialog
一个扁平的列表

请注意,原始模型仍然存在于扁平化模型之后,并且它们的更新将按照预期由扁平列表模型传播。

有时,你的数据存在于列表模型中,但它的形式不太正确。在这种情况下,你可以使用 GtkMapListModel 将原始模型中的每个项目替换为不同的项目。

具体模型

GTK 及其依赖项包含许多用于我们自己处理的数据类型的具体模型。

这里的第一个例子是实现了列表模型接口的 Pango 对象:PangoFontMap 是 PangoFontFamily 对象的列表模型,而 PangoFontFamily 是 PangoFontFace 对象的列表模型。字体选择器正在使用这些模型。

font chooser dialog
一个 Pango 列表模型

下一个例子是 GtkDirectoryListGtkBookmarkList 对象,它们将在文件选择器中用于表示目录内容和书签。关于这些对象一个有趣的细节是,它们都需要执行 I/O 操作来填充其内容,并且它们会异步执行,以避免长时间阻塞 UI。

这组中的最后一个模型稍微不太具体:GtkStringList 是围绕着非常常见的字符串数组的简单列表模型包装器。这种列表模型经常使用的一个例子是 GtkDropDown。这非常常见,以至于 GtkDropDown 有一个方便的构造函数,它接受一个字符串数组并为你创建 GtkStringList

GtkWidget *
    gtk_drop_down_new_from_strings (const char * const * strings)

选择

下一组模型使用新接口扩展了 GListModel:GtkSelectionModel。对于底层模型中的每个项目,GtkSelectionModel 都会维护它是否被选中的信息。

我们不会详细讨论该接口,因为你不太可能需要自己实现它,但最重要的几点是

gboolean gtk_selection_model_is_selected (GtkSelectionModel *model)
                                          guint              pos)
GtkBitset *
       gtk_selection_model_get_selection (GtkSelectionModel *model)

因此,你可以获取单个项目的选择信息,也可以以位集的形式获取整体的选择信息。当然,还有一个 ::selection-changed 信号,其工作方式与 GListModel 的 ::items-changed 信号非常相似。

GTK 有三个 GtkSelectionModel 实现:GtkSingleSelectionGtkMultiSelectionGtkNoSelection,它们在可以同时选择的项目数量上有所不同(1 个、多个或 0 个)。

GtkGridView 颜色演示展示了多选的实际应用,带有橡皮筋效果

 

当你使用 GTK 的新列表小部件时,你很可能会遇到选择模型,因为它们都希望其模型为选择模型。

重要的模型

我想提到的最后一组模型是执行你在列表中期望的典型操作的模型:过滤和排序。这些模型是 GtkFilterListModelGtkSortListModel。它们都使用辅助对象来实现其操作:GtkFilter 和 GtkSorter。这两者都有子类来处理常见情况:排序和过滤字符串或数字,或者使用回调。

在 GTK 3.99 的准备阶段,我们在这两个模型上花费了大量精力,并使它们以增量方式工作,以避免在使用大型模型时长时间阻塞 UI。

GtkListView 单词演示展示了对 500,000 个单词列表的交互式过滤

剩余的模型

GTK 中还有一些列表模型实现,它们不完全适合上述任何组,例如 GtkTreeListModelGtkSelectionFilterModelGtkShortcutController。我今天将跳过这些。

无处不在的模型

最后,我将简要列出返回列表模型的 GTK API

  • gdk_display_get_monitors
  • gtk_widget_observe_children
  • gtk_widget_observe_controllers
  • gtk_constraint_layout_observe_constraints
  • gtk_constraint_layout_observe_guides
  • gtk_file_chooser_get_files
  • gtk_drop_down_get_model
  • gtk_list_view_get_model
  • gtk_grid_view_get_model
  • gtk_column_view_get_model
  • gtk_column_view_get_columns
  • gtk_window_get_toplevels
  • gtk_assistant_get_pages
  • gtk_stack_get_pages
  • gtk_notebook_get_pages

总而言之,列表模型在 GTK 4 中无处不在。它们灵活且有趣,你应该使用它们!

GtkListView 入门

一些 GTK4 的早期采用者指出,新的列表小部件并不容易学习。特别是,GtkExpression 和 GtkBuilderListItemFactory 很难理解。这并不太令人惊讶 - 一个完整的列表小部件,带有列、选择、排序和树结构等,是一个复杂的野兽。

但是,让我们看看是否可以逐一解开这些东西,并使其更容易理解。

概述

让我们从相关组件及其交互的高级视图开始:模型、列表项工厂和列表视图。

它们是我们在创建列表视图时发生的三件事

view = gtk_list_view_new (model, factory);

我们使用的模型是 GListModel。这些模型始终包含 GObject,因此你必须以对象的形式提供数据。这是与 GtkTreeview 的第一个显着区别,后者使用直接包含基本类型的 GtkTreeModel。

对于某些简单的情况,GTK 提供了现成的模型,例如 GtkStringList。但通常,你必须制作自己的模型。值得庆幸的是,GListModel 是一个比 GtkTreeModel 简单得多的接口,因此这并不太难。

列表项工厂 的职责是生成一个行小部件,并在列表视图需要时将其连接到模型中的一个项。

列表视图将创建比填充其可见区域所需的行多一些,以便更好地估计滚动条的大小,并且在决定滚动视图时有一些“缓冲”。

一旦你滚动,我们不一定需要要求工厂制作更多行——我们可以回收在另一端滚动出视图的行。

值得庆幸的是,所有这些都在幕后自动发生。你所要做的就是提供一个列表项工厂。

创建项

GTK 提供了两种不同的创建项的方法。你可以手动使用 GtkSignalListItemFactory 来完成,也可以使用 GtkBuilderListItemFactory 从 ui 文件实例化你的行小部件。

手动方法更容易理解,所以让我们先看看它。

factory = gtk_signal_list_item_factory_new ();
g_signal_connect (factory, "setup", setup_listitem_cb, NULL);
g_signal_connect (factory, "bind", bind_listitem_cb, NULL);

当工厂需要创建一个新的行小部件时,会发出 “setup” 信号;当一个行小部件需要连接到模型中的一个项时,会发出 “bind” 信号。

这两个信号都以 GtkListItem 作为参数,GtkListItem 是一个包装器对象,可让你访问模型项(使用 gtk_list_item_get_item())并且还允许你传递新的行小部件(使用 gtk_list_item_set_child())。

static void
setup_listitem_cb (GtkListItemFactory *factory,
                   GtkListItem        *list_item)
{
  GtkWidget *label = gtk_label_new ("");
  gtk_list_item_set_child (list_item, label);
}

通常,你的行将比单个标签更复杂。你可以根据需要创建复杂的小部件并将它们分组到容器中。

static void
bind_listitem_cb (GtkListItemFactory *factory,
                  GtkListItem        *list_item)
{
  GtkWidget *label;
  MyObject *obj;

  label = gtk_list_item_get_child (list_item);
  obj = gtk_list_item_get_item (list_item);
  gtk_label_set_label (GTK_LABEL (label),
                       my_object_get_string (obj));
}

如果你的 “bind” 处理程序连接到项上的信号或执行其他需要清理的操作,则可以使用 “unbind” 信号来执行清理。“setup” 信号有一个类似的对应信号,称为 “teardown”。

构建器方式

我们的 “setup” 处理程序基本上是创建小型小部件层次结构的配方。GTK 有一种更声明性的方法来执行此操作:GtkBuilder ui 文件。这就是 GtkBuilderListItemFactory 的工作方式:你给它一个 ui 文件,当它需要创建一行时,它会实例化该 ui 文件。

ui = "<interface><template class="GtkListItem">...";
bytes = g_bytes_new_static (ui, strlen (ui));
gtk_builder_list_item_factory_new_from_bytes (scope, bytes);

你现在可能会想:等一下,你正在为我模型中的数千个项中的每一个解析一个 xml 文件,这不是很耗费资源吗?

对此问题有两个答案

  • 我们不是真的在为每个项解析 xml;我们解析一次,存储回调序列,然后稍后重放它。
  • GtkBuilder 最昂贵的部分实际上不是 xml 解析,而是对象的创建;回收行对此有帮助。

很容易看出 ui 文件如何替换 “setup” 处理程序,但是 “bind” 呢?在上面的示例中,绑定回调获取项的属性 (MyObject:string 属性),并使用它们的值设置小部件的属性 (GtkLabel:label 属性)。换句话说,“bind” 处理程序正在执行属性绑定。为简单起见,我们在这里仅创建了一次性绑定,但是我们也可以使用 g_object_bind_property() 来创建持久绑定。

GtkBuilder ui 文件可以 在对象之间设置属性绑定,但有一个问题:模型项在 ui 文件中不是“存在的”,它仅在“bind” 时才与行小部件关联。

这就是 GtkExpression 的用武之地。GtkExpression 的核心是一种描述尚未存在的对象之间绑定的方法。在我们的例子中,我们想要实现的是

label->label = list_item->item->string

不幸的是,当将其作为 ui 文件的一部分转换为 xml 时,它会变得有点笨拙

<interface>
  <template class="GtkListItem">
    <property name="child">
      <object class="GtkLabel">
        <binding name="label">
          <lookup name="string">
            <lookup name="item">GtkListItem</lookup>
          </lookup>
        </binding>
      </object>
    </property>
  </template>
</interface>

请记住,ui 模板中的类名 (GtkListItem) 用作引用正在实例化的对象的“this”指针。

因此,<lookup name=”item”>GtkListItem</lookup> 的意思是:创建的列表项的 “item” 属性的值。<lookup name=”string”> 的意思是:该对象的 “string” 属性。并且 <binding name=”label”> 表示将小部件的 “label” 属性设置为该属性的值。

由于表达式的工作方式,当列表项工厂将列表项的 “item” 属性设置为新值时,所有这些都将重新评估,这正是我们需要使行小部件回收工作的方式。

专家级困惑

当你嵌套事物时,GtkBuilderListItemFactory 和 GtkExpression 会变得非常令人困惑——列表项工厂本身可以在 ui 文件中构建,并且可以将其自己的 UI 文件作为属性给出,因此你最终会得到如下结构

<object class="GtkListView">
  <property name="factory">
    <object class="GtkBuilderListItemFactory">
      <property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
  <property name="child">
  ...
]]>
      </property>
    </object>
  </property>
...

即使对于 GTK 专家来说,这也会令人困惑。

我的建议是,在刚开始使用 GtkListView 时,请避免这样做——你不必在 UI 文件中创建列表项工厂,并且可以将它的 UI 模板指定为资源而不是直接嵌入它。

深入研究

我们今天在这里描述的所有内容也适用于网格视图,只需进行少量调整即可。

到目前为止,我们一直专注于视图方面。关于模型也有很多要说的。

然后是列视图,它值得单独发布一篇博文。

GTK 3.99.1

距离我们发布 GTK 3.99 已经过去一个月了,是时候发布另一个快照了。这是:https://download.gnome.org/sources/gtk/3.99/gtk-3.99.1.tar.xz

此快照专注于完善和完成。

未竟事宜

我们已经处理了 API 中一些未竟事宜。

最明显的用户可见更改可能是按钮类层次结构的简化。GtkCheckButton 不再从 GtkToggleButton 派生,它们现在是两个独立的窗口小部件,并且都可以分组为互斥的(也称为“单选组”)。在这种新的设置中,实际上不再需要 GtkRadioButton,因此它被移除了。

我们新的列表小部件(GtkListView 和 GtkGridView)的 API 也进行了一些小的调整。我们现在明确要求模型为 GtkSelectionModel 类型,以明确小部件处理选择。并且我们已经删除了额外的“with_factory”构造函数,而只是在 new() 中采用可为空的工厂参数,从而剩下

GtkWidget * gtk_list_view_new (GtkSelectionModel  *model,
                               GtkListItemFactory *factory);

作为 API 清理的更多一部分,我们已经删除了所有 CSS 样式类的定义——我们的窗口小部件支持哪些样式类在其文档中定义,并且这些额外的定义实际上并没有明确定义或有用。

我们的主题现在正在圆角化 GtkFrame 窗口小部件绘制的框架的角落。这要求我们使框架剪裁其子项——不是真正的 API 更改,而是一种值得提及的行为更改。

更多演示

过去一个月,我们在 gtk4-demo 上投入了大量精力。

我们已经使源代码高亮显示现代化。我们现在正在使用 highlight 命令行实用程序。除此之外,这使我们能够对 xml 和 css 进行语法高亮显示,并支持暗色主题。

Highlighting XML in a dark theme
高亮显示

演示列表具有更好的过滤和更好的外观。新外观是 Adwaita 现在支持的几种预定义的列表样式之一:丰富列表、导航侧边栏和数据表。

 

Rich List list style
丰富列表
Navigation Sidebar list style
导航侧边栏
Data Table list style
数据表

我们从 gtk4-demo 中删除了一些过时的演示,并改进了许多现有的演示。这是我们拖放演示现在呈现的样子

Drag-and-Drop demo
拖放演示

还添加了许多新演示。这是用于布局管理器和转换的新演示

性能和其他错误

许多错误已被修复;感谢我们热情的测试人员和错误报告员。

我们最近终于追踪到的一个长期存在的问题导致我们的 GL 渲染器在存在非平凡的投影变换时错误地剪切。现在已经纠正了这一点(结果可以在上面的转换演示中看到)。

作为之前提到的高亮显示改进的一部分,gtk_text_view_buffer_insert_markup() 的速度快得多。此改进仅发生是因为 highlight 实用程序可以生成 Pango 标记。感谢实施该功能的人!

我们解决的另一个性能问题是在具有许多字体的系统上加载字体选择器对话框的时间。我们现在正在增量填充字体列表。除了此更改之外,调查还导致了 fontconfig 和 Pango 中的性能改进,这将使这些库的任何用户受益。

我可以开始移植了吗?

答案是:可以!

现在是了解 GTK4 的好时机,开始移植您的应用程序,并向我们提供有关新旧 API 的反馈。我们也渴望看到您在使用 GTK4 方面的意想不到的想法——我们上面展示的一些演示或许可以给您一些启发。

下一步是什么?

我们希望尽快为我们新的辅助功能接口加入 at-spi 后端;它应该包含在下一个 GTK 快照中。

GTK 3.99

本周,我们发布了 GTK 3.99,这只能意味着一件事:GTK4 真的越来越近了!

早在二月,当发布 3.98 时,我们概述了我们希望在发布功能完整的 3.99 版本之前实现的功能。这是列表

  • 用于键盘快捷键的事件控制器
  • 可移动的弹出窗口
  • 行回收列表和网格视图
  • 改进的辅助功能基础设施
  • 动画 API
我们做得如何?

我们已经从 4.0 阻止列表中删除了动画 API,因为它需要更广泛的内部重组,并且我们无法及时完成。但是所有其他功能都已添加到各种 3.98.x 快照中,辅助功能基础设施是最近才添加的最后一个保留功能。

其中一些功能已在此处介绍过,例如可移动的弹出窗口可缩放列表。其他功能希望将来能在这里进行详细审查。在那之前,如果您对新的辅助功能基础设施感到好奇,可以观看 Emmanuele 的 GUADEC 演讲

还有什么新功能?

我想强调的一个领域是为充实新的可缩放列表基础设施所做的工作量。我们的过滤器和排序模型现在以增量方式工作,因此当在后台过滤或排序大型列表时,UI 可以保持响应。

已合并新的 macOS GDK 后端。它仍然有一些粗糙的角落,我们希望在现在到 4.0 版本之间将其平滑。

并且修复了许多小的回归,从微调按钮大小到树视图单元格编辑,再到自动滚动,再到检查器导航,再到稍微渲染错误的阴影。

我现在可以移植了吗?

GTK 3.99 是初次了解移植应用程序的绝佳时机。

我们非常感谢早期采用者勇敢地使用 3.96 或 3.98 快照进行试用移植,并为我们提供了宝贵的反馈。由于有如此多的更改,我们不可避免地会在 API 中犯错,并且在我们仍然可以解决问题时获得反馈将真正对我们有所帮助。告诉我们我们忘记在文档中涵盖的内容、缺少示例或迁移指南中的空白也非常感谢。

我们知道,一些移植工作将因对 GTK 3 的间接依赖而受阻。例如,如果您使用的是 webkit webview 或 GtkSourceView 或 vte,您可能会发现很难尝试 GTK 4。

值得庆幸的是,其中一些库的移植工作已经进展顺利。其他库(例如libgweather)需要做一些工作才能将其核心功能与 GTK 3 依赖项分离。

我可以帮忙吗?

如上一节所述,有关新 API、文档和移植指南的任何反馈都非常受欢迎并且很有帮助。

我们可以在许多其他领域使用帮助。如果您熟悉 OS X API,则可以在完成 macOS 后端方面做出真正的改变。

我们也开始集成基于 ANGLE 的 GL 渲染器,但是我们的着色器需要使其与 EGL 配合使用,然后我们才能利用它。对此的帮助将不胜感激。

下一步是什么?

我们致力于在年底之前发布 GTK 4。从现在到那时,我们正在做更多关于辅助功能后端、改进 macOS 后端、编写文档和示例的工作。