列表小部件概述 [源代码]

GTK 提供了功能强大的小部件来显示和编辑数据列表。此文档概述了相关概念,以及概念如何协同工作来使开发者能够实现列表。

当开发者希望以大致相同的方式显示许多对象时,应该使用列表。

使用列表来显示仅包含 2 或 3 个元素的极短列表非常可行,但通常能够扩展到数百万个项目。当然,列表越大,越需要谨慎选择正确的数据结构以保持正常运行。

列表旨在与不断变化的数据一起使用,既包括项目本身的变化,也包括列表中项目添加和删除的变化。当然,它们也可以与静态数据一起正常工作。

术语

讨论列表时,此文档将在整篇文档中使用以下术语,您应该了解它们所指的内容。这些通常是通用术语,在此上下文中具有特定含义。

视图列表小部件是容纳和管理列表的小部件。这些小部件的示例包括 GtkListViewGtkGridView

视图显示来自 模型 的数据。模型实现了 GListModel 接口,并且可以通过多种方式提供

  • 已经存在许多特定数据类型的列表模型实现,例如 GtkDirectoryListGtkStringList

  • 有一些通用的列表模型实现,例如 GListStore,它允许构建任意对象的列表。

  • 包装列表模型,例如 GtkFilterListModelGtkSortListModel,修改、调整或合并其他模型。

  • 最后但并非最不重要的一点是,鼓励开发者创建他们自己的 GListModel 实现。该接口故意保持较小规模,以便于实现这一目标。

同一个模型可以用于多个不同的视图,并同时用多个不同的模型包装。

模型中的元素称为 项目。所有项目都是 GObject 实例。

模型中的每个项目都有一个 位置,这是一个无符号整数,表示项目在模型中的位置。模型中的第一个项目位于位置 0。随着向模型中添加或删除其他项目,项目的的位置可能会发生变化。

了解项目和位置之间的差异非常重要,因为从位置到项目的映射不是永久性的,因此开发者在使用模型时应考虑要跟踪项目还是位置。通常,有些事情以一种方式进行非常困难,而以另一种方式却非常容易。

视图的另一个重要部分是工厂。每个工厂都是 GtkListItemFactory 实现,用来负责映射模型项到视图中可以显示的小组件。

工厂执行此操作的方式是为当前正在使用的每个项创建一个列表项。列表项总是为 GtkListItem 实例。它们始终由 GTK 创建,并提供有关它们要显示的项的信息。

不同的工厂实现使用不同的方法允许开发者向列表项添加正确的组件,并将这些组件链接到列表项管理的项。找到一个适合要显示的数据、编程语言和开发环境的工厂实现是一项重要任务,可以极大地简化视图的设置。

视图通过选择模型支持选择。选择模型是 GtkSelectionModel 接口在 GListModel 接口之上的实现,允许将模型中的每个项标记为已选择或未选择。如同常规模型一样,这可以通过直接实现 GtkSelectionModel 或使用 GTK 为此目的提供的某个模型来包装模型来实现,例如 GtkNoSelectionGtkSingleSelection

选择模型的行为(即它们允许选择的项以及这将对其他项产生什么影响)完全由选择模型决定。因此,单选、多选或在不同的选择模型和/或视图之间共享选择状态都是可能的。该项的选择状态通过 GtkListItem:selected 属性在列表项中公开。

视图和列表项还支持激活。激活意味着在获得焦点的行内双击或按 Enter 键将会导致视图发出一个信号,例如 GtkListView::activate。这提供了设置列表的一种便捷方式,但也可以在列表项中关闭此方式(如果这是不需要的)。

选择和激活可以通过 操作 组件和其他组件来支持。这允许开发者向他们的列表中添加导致选择发生改变或通过 GtkActionable 接口触发激活的小组件。有关所有支持操作的列表,请参阅相关的文档。

幕后

虽然对于简短的列表来说,根据模型中的每个项实例化小组件不成问题,但一旦列表增长到数千或数百万个元素,这样做就不太可行了。因此,视图只创建有限数量的列表项,并通过将它们绑定到新项来对它们进行回收利用。通常,视图尝试仅为实际可以在屏幕上看到的项保持列表项的可用性。

虽然这种行为允许视图轻松地扩展到巨大的列表,但它对视图可以做什么有一些影响。例如,不能为某个位置使用的列表项查询视图,可能不存在,即使存在,该列表项也可能很快被回收利用到新的位置。

同样重要的是,开发者在项中保存他们关心的状态,并且不要依赖于他们创建的小组件,因为这些小组件可以随时被回收利用到新位置,从而导致任何状态丢失。

对视图的另一个重要要求是视图需要知道哪些项目不可见,这样它们可以循环利用。视图通过实现 GtkScrollable 接口,并期望被直接放置到 GtkScrolledWindow 中来实现这一点。

当然,如果您只使用带有少数项目的模型,那么这一点并不重要,您可以像对待任何其他小控件一样对待视图。但是,如果您使用大型列表并且性能下降,您应该意识到这一点。视图还允许调整它们创建的列表项数量,例如使用 gtk_grid_view_set_max_columns(),遇到性能问题的开发人员肯定应该研究它们的权衡并对其进行实验。

选择合适的模型

GTK 提供各种包装模型,它们以某种方式更改或补充现有模型(或模型)。但涉及存储实际数据时,仅有少数现成的选项可用:GListStoreGtkStringListGtkDirectoryList

GListStore 受平衡树支持,其性能特性是针对该数据结构所预期的。它非常适合大小在 1,000,000 范围内的数据集,并且可以处理插入和删除操作。它使用缓存迭代器可快速线性访问项目。

GtkStringList 不是通用存储,它只能处理字符串。它受动态分配数组支持,其性能特性是针对该数据结构所预期的。GtkStringList 适用于原本您会使用 char*[] 的任何地方,如果数据集不是非常动态,则效果最佳。

GtkDirectoryList 是一个包装 g_file_enumerate_children_async() 的列表模型。它显示一个 GListModel,并使用该函数返回的 GFile 异步填充该模型。

如果这些模型不适合您的用例或可伸缩性要求,您应该进行自定义 GListModel 实施。这是一个小的接口,其实现难度不大。

有关基于树和基于数组的实施之间的渐近性能比较,请参阅 这篇文章

显示树

虽然 GtkTreeView 为树提供内置支持,但列表小控件(尤其是 GListModel)不支持。这是一个设计选择,因为常见用例是显示列表而不是树,并且该选择极大地简化了所提供的 API 接口。

但是,GTK 提供一些功能,可使列表对于需要树的用例看起来和表现得像树。这通过使用 GtkTreeListModel 模型将树展平为列表来实现。然后,可以使用 GtkTreeExpander 小控件在列表项内部,以允许用户展开和折叠行,并提供与 GtkTreeView 类似的体验。

开发人员应参考这些对象的 API 参考以获取更多关于该主题的讨论。

列表样式

GtkTreeView 和单元格渲染器相比,新列表小控件的一个优点是它们可以使用 GTK CSS 进行样式化。这提供了很大的灵活性。随附 GTK 的主题提供了一些预定义的列表样式,这些列表样式可在许多情况下使用。

Rich list

富列表样式密度低,宽敞,并使用轮廓焦点环。它适用于控件列表,例如,在首选项对话框或设置面板中。使用 .rich-list 样式类。

Navigation sidebar

列表的边栏方式密度中等,使用完整背景来表示焦点和选择。使用 .navigation-sidbar 样式 类。

Data table

列表的数据表格方式密度较高,样式与传统树视图类似。各个单元格可以是可选择的和可编辑的。使用 .data-table 样式 类。

部分

列表模型可以选择通过实现 GtkSectionModel 接口将它们的项目分组到部分GtkListView 可以通过安装单独的头部工厂来显示部分的头部。

很多 GTK 列表模型本质上支持部分,或者它们会通过模型的部分传递,也就是说它们 包装。

与 GtkTreeView 的比较

熟悉 GtkTreeView 的开发人员可能想知道这种制作列表的方法与他们所知道的方式相比如何。本节将概述两人的相似之处和差异。

这种新方法试图提供与旧方法大致相同的功能,但通常使用非常不同的方式来实现这些 目标。

最大的差异和这种新发展的主要原因之一是,现在可以使用常规小工具显示项目,并且不再需要单独的单元格渲染器机制。这带来了小工具提供的所有优点,例如复杂布局、动画和 CSS 样式。

另一个很大的差异是对数据模型进行大改。GtkTreeModel 是树形数据结构的一个相当复杂的接口。GListModel 被特意设计成仅用于列表的一个非常简单的数据结构。(请参阅 上方 了解如何使用这种新模型完成树。)另一个重大的变化是,新模型允许通过 GListModel::items-changed 信号进行批量更改,而 GtkTreeModel 一次只允许单个项目进行更改。此处的目标当然是鼓励实现自定义列表 模型。

新模型的另一个后果是,现在可以通过保留项目直接引用模型中一行内容,而 GtkTreeRowReference 是一种用于实现相同功能非常缓慢的机制。由于项目都是真实对象,因此开发人员可以使它们发出更改信号,从而导致列表项和它们的子项进行更新,这是 GtkTreeModel 无法做到的。

选中处理也不同。尽管以前是通过每个小工具的自定义代码管理选中的内容,但现在选中状态应由选中模型管理。尤其可以用于有特殊 要求的复杂用例。

最后,以下是等效功能的快速比较表,供在转换 代码时查找

GtkTreeModel GListModel
GtkTreePath guint 位置,GtkTreeListRow
GtkTreeIter guint 位置
GtkTreeRowReference GObject 项目
GtkListStore GListStore
GtkTreeStore GtkTreeListModel, GtkTreeExpander
GtkTreeSelection GtkSelectionModel
GtkTreeViewColumn GtkColumnView
GtkTreeView GtkListViewGtkColumnView
GtkCellView GtkListItem
GtkComboBox GtkDropDown
GtkIconView GtkGridView
GtkTreeSortable GtkColumnView
GtkTreeModelSort GtkSortListModel
GtkTreeModelFilter GtkFilterListModel
GtkCellLayout GtkListItemFactory
GtkCellArea GtkWidget
GtkCellRenderer GtkWidget