关于列表模型

在上一个帖子中,我承诺会深入研究列表模型以及 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 为此目的提供的辅助函数
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 对象,它们将在文件选择器中用于表示目录内容和书签。关于这些的一个有趣细节是,它们都需要进行 IO 操作来填充其内容,并且它们会异步执行此操作,以避免长时间阻塞 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 的新列表小部件时,您很可能会遇到选择模型,因为它们都希望其模型是选择模型。

重要的模型

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

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

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

剩下的

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

模型无处不在

我将以一个简短的 GTK API 列表结束,这些 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 中无处不在。它们灵活而有趣,您应该使用它们!