Список-ориентированные виджеты
- Обзор виджета списка
- GtkListItem — Объект, используемый для представления элементов модели списка
- GtkListItemFactory — Сопоставление элементов списка с виджетами
- GtkSignalListItemFactory — Фабрика элементов списка, предоставляющая сигналы
- GtkBuilderListItemFactory — Фабрика элементов списка, использующая файлы пользовательского интерфейса
- GtkListView — Виджет для отображения списков
- GtkGridView — Виджет для отображения таблиц
- GtkColumnView — Виджет для отображения списков в нескольких столбцах
- GtkColumnViewColumn — Столбец, добавляемый в GtkColumnView
- GtkDropDown — Выбор элемента из списка
GTK предоставляет мощные виджеты для отображения и редактирования списков данных. Этот документ предоставляет обзор концепций и того, как они работают вместе, чтобы позволить разработчикам реализовывать списки.
Список предназначен для использования всякий раз, когда разработчики хотят отобразить множество объектов примерно одинаковым образом.
Списки прекрасно подходят для очень коротких списков всего из 2 или 3 элементов, но обычно масштабируются и для миллионов элементов. Конечно, чем больше список, тем больше внимания нужно уделять выбору правильных структур данных, чтобы все работало хорошо.
Списки предназначены для использования с изменяемыми данными, как с самими элементами, так и со списком, добавляющим и удаляющим элементы. Конечно, они работают также хорошо и со статическими данными.
Терминология
Эти термины используются в документации при обсуждении списков, и вам следует знать, что они означают. Это часто общие термины, имеющие специфическое значение в этом контексте.
Представления или виджеты списков — это виджеты, которые содержат и управляют списками. Примеры таких виджетов — GtkListView или GtkGridView.
Представления отображают данные из модели. Модель — это GListModel, и модели могут быть предоставлены тремя способами или их комбинацией:
Существует много реализаций моделей списков. Есть модели, которые предоставляют специфические данные, например, GtkDirectoryList. И есть модели, такие как GListStore, которые позволяют вручную создавать списки.
Оборачивающие модели списков, такие как GtkFilterListModel или GtkSortListModel, изменяют, адаптируют или комбинируют другие модели.
И, наконец, разработчикам рекомендуется создавать свои собственные реализации GListModel. Интерфейс намеренно удерживается небольшим, чтобы сделать это проще.
Одна и та же модель может использоваться в нескольких разных представлениях и обертываться несколькими разными моделями одновременно.
Элементы в модели называются элементами. Все элементы — GObjects.
Каждый элемент в модели имеет позицию, которая представляет собой целое без знака, описывающее местоположение элемента в модели. Первый элемент в модели находится на позиции 0. Позиция элемента, конечно, может меняться по мере добавления или удаления других элементов из модели.
Важно понимать разницу между элементами и позициями, поскольку отображение позиции на элемент не является постоянным, поэтому разработчики должны подумать о том, хотят ли они отслеживать элементы или позиции при работе с моделями. Зачастую некоторые вещи очень сложно сделать одним способом, но очень легко другим.
Другой важной частью представления является фабрика. Каждая фабрика — это реализация GtkListItemFactory, которая отвечает за сопоставление элементов модели с виджетами, которые могут быть показаны в представлении.
Фабрики делают это, создавая listitem для каждого элемента, который в данный момент используется. Listitems всегда являются объектами GtkListItem. Они создаются только GTK и предоставляют информацию о том, какой элемент они должны отобразить.
Разные реализации фабрик используют различные методы, позволяющие разработчикам добавлять подходящие виджеты в listitems и связывать эти виджеты с элементом, управляемым listitem. Нахождение подходящей реализации фабрики для отображаемых данных, языка программирования и среды разработки — важная задача, которая может значительно упростить настройку представления.
Представления поддерживают выделения с помощью модели выделения. Модель выделения — это реализация интерфейса GtkSelectionModel поверх интерфейса GListModel, которая позволяет отмечать каждый элемент в модели как выделенный или не выделенный. Как и обычные модели, это можно реализовать, реализовав GtkSelectionModel напрямую или обернув модель одной из предоставленных GTK моделей для этой цели, таких как GtkNoSelection или GtkSingleSelection.
Поведение моделей выделения — то есть, какие элементы они позволяют выделять и какое влияние это оказывает на другие элементы — полностью зависит от модели выделения. Таким образом, возможны одиночные выделения, множественные выделения или совместное состояние выделения между разными моделями выделения и/или представлениями. Состояние выделения элемента представлено в listitem через свойство «selected».
Представления и listitems также поддерживают активацию. Активация означает, что двойной щелчок или нажатие клавиши Enter, находясь в фокусе строки, приведет к тому, что представление отправит сигнал активации, например, «activate». Это предоставляет простой способ настройки списков, но его также можно отключить для listitems, если это нежелательно.
Выделения и активация поддерживаются, среди прочего, с помощью действий виджетов. Это позволяет разработчикам добавлять виджеты в свои списки, которые изменяют выделения или вызывают активацию через интерфейс GtkActionable. Список всех поддерживаемых действий см. в соответствующей документации.
Под капотом
Хотя для коротких списков не проблема создать виджеты для каждого элемента в модели, когда списки вырастают до тысяч или миллионов элементов, это становится менее осуществимым. Поэтому представления создают только ограниченное количество listitems и повторно используют их, связывая их с новыми элементами. В целом, представления пытаются поддерживать доступные listitems только для элементов, которые фактически видны на экране.
Хотя такое поведение позволяет представлениям без труда масштабироваться до огромных списков, это накладывает некоторые ограничения на то, что можно делать с представлениями. Например, невозможно запросить представление для listitem, используемого для определенной позиции — его может не быть, и даже если он есть, этот listitem вскоре может быть повторно использован для новой позиции.
Также важно, чтобы разработчики сохраняли в элементе данные, которые им нужны, и не полагались на созданные ими виджеты, так как эти виджеты могут быть повторно использованы для новой позиции в любое время, что приведет к потере состояния.
Еще одно важное требование для представлений состоит в том, чтобы они знали, какие элементы не видны, чтобы их можно было повторно использовать. Представления достигают этого, реализуя интерфейс GtkScrollable и ожидая прямого размещения в GtkScrolledWindow.
Конечно, если вы используете модели с небольшим количеством элементов, это не имеет значения, и вы можете рассматривать представления как любой другой виджет. Но если вы используете большие списки, и ваша производительность страдает, вы должны об этом знать. Представления также позволяют настроить количество создаваемых listitems, например, с помощью gtk_grid_view_set_max_columns(), и разработчикам, столкнувшимся с проблемами производительности, следует обязательно изучить компромиссы этих параметров и поэкспериментировать с ними.
Выбор правильной модели
GTK предлагает широкий спектр обертывающих моделей, которые изменяют или дополняют существующую модель (или модели) каким-либо образом. Но когда дело доходит до хранения фактических данных, доступны лишь несколько готовых вариантов: GListStore и GtkStringList.
GListStore поддерживается сбалансированным деревом и имеет характеристики производительности, ожидаемые для этой структуры данных. Он работает достаточно хорошо для наборов данных размером до 1 000 000 элементов и может обрабатывать вставки и удаления. Он использует кэшированный итератор, чтобы сделать линейный доступ к элементам быстрым.
GtkStringList — это не универсальный хранилище — он может обрабатывать только строки. Он поддерживается динамически выделенным массивом и имеет характеристики производительности, ожидаемые для этой структуры данных. GtkStringList подходит для любого места, где вы бы иначе использовали char*[], и работает лучше, если набор данных не очень динамичен.
Если эти модели не подходят для вашего использования или требований к масштабируемости, вам нужно создать собственную модель GListModel. Это небольшой интерфейс и его несложно реализовать.
Для сравнения асимптотической производительности между реализациями на основе деревьев и массивов см. эту статью.
Отображение деревьев
Хотя GtkTreeView предоставляет встроенную поддержку деревьев, виджеты списков, и в частности GListModel, не делают этого. Это было решением по проектированию, поскольку распространенным случаем использования является отображение списков, а не деревьев, и это значительно упрощает предоставляемый интерфейс API.
Однако GTK предоставляет функциональность, позволяющую деревьям выглядеть и вести себя как списки для тех, кто по-прежнему хочет отображать списки. Это достигается с помощью модели GtkTreeListModel, преобразующей дерево в список. Виджет GtkTreeExpander затем можно использовать внутри listitem, чтобы позволить пользователям раскрывать и сворачивать строки и обеспечить аналогичный опыт, что и GtkTreeView.
Разработчики должны обратиться к справочнику по API этих объектов для более подробного обсуждения этой темы.
Стили списков
Одним из преимуществ новых виджетов списков по сравнению с GtkTreeViews и визуализаторами ячеек является то, что они полностью могут быть стилизованы с помощью GTK CSS. Это обеспечивает большую гибкость. Тематики, поставляемые с GTK, предоставляют несколько предопределённых стилей списков, которые можно использовать во многих ситуациях:
Рисунок 15. Богатый список
Этот стиль списка имеет низкую плотность, просторный и использует обводку фокуса. Он подходит для списков элементов управления, например, в диалогах настроек или панелях параметров. Используйте класс стиля .rich-list.
Рисунок 16. Навигационная боковая панель
Этот стиль списка имеет среднюю плотность, используя полный фон для обозначения фокуса и выделения. Используйте класс стиля .navigation-sidebar.
Рисунок 17. Таблица данных
Этот стиль списка представляет собой таблицу высокой плотности, по стилю похожую на традиционную таблицу. Отдельные ячейки могут быть выделены и отредактированы. Используйте класс стиля .data-table.
Сравнение с GtkTreeView
Разработчики, знакомые с GtkTreeView, могут задаться вопросом, как этот способ реализации списков сравнивается с уже известным им подходом. Этот раздел попытается описать сходства и различия между двумя подходами.
Этот новый подход пытается предоставить примерно ту же функциональность, что и старый, но часто использует совершенно другой подход для достижения этих целей.
Основное различие, и одна из основных причин этого нового развития, заключается в том, что элементы могут быть отображены с использованием обычных виджетов, и GtkCellRenderer больше не требуется. Это позволяет использовать все преимущества, которые предоставляют виджеты, такие как сложная компоновка и анимация виджетов, и не только делает визуализаторы ячеек устаревшими, но также и GtkCellArea.
Другое существенное различие заключается в значительной изменении модели данных. GtkTreeModel был довольно сложным интерфейсом для древовидной структуры данных, а 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 | GtkListView, GtkColumnView |
| GtkCellView | GtkListItemWidget |
| GtkComboBox | GtkDropDown |
| GtkIconView | GtkGridView |
| GtkTreeSortable | GtkColumnView |
| GtkTreeModelSort | GtkSortListModel |
| GtkTreeModelFilter | GtkFilterListModel |
| GtkCellLayout | GtkListItemFactory |
| GtkCellArea | GtkWidget |
| GtkCellRenderer | GtkWidget |
© 2005–2020 The GNOME Project
Licensed under the GNU Lesser General Public License version 2.1 or later.
https://developer-old.gnome.org/gtk4/4.0/ListContainers.html