From 16024fba7c0e2cee025cbbe4f1241d1a7db63540 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 3 Oct 2018 18:49:48 +0200 Subject: gtk: Add GtkSelectionModel The selection model is a list model interface that takes care of selections and is to be used by the list model widgets to manage their selections. --- docs/reference/gtk/gtk4-docs.xml | 1 + docs/reference/gtk/gtk4-sections.txt | 41 +++++ docs/reference/gtk/gtk4.types.in | 2 + gtk/gtk.h | 1 + gtk/gtkselectionmodel.c | 301 +++++++++++++++++++++++++++++++++++ gtk/gtkselectionmodel.h | 125 +++++++++++++++ gtk/meson.build | 2 + 7 files changed, 473 insertions(+) create mode 100644 gtk/gtkselectionmodel.c create mode 100644 gtk/gtkselectionmodel.h diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index a5e25803fa..d841e608b8 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -49,6 +49,7 @@ + diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 1929eaf4a1..fd93df5a50 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -414,6 +414,47 @@ gtk_list_box_get_type gtk_list_box_row_get_type +
+gtkselectionmodel +GtkSelectionModel +GtkSelectionModel +gtk_selection_model_is_selected +gtk_selection_model_select_item +gtk_selection_model_unselect_item +gtk_selection_model_select_range +gtk_selection_model_unselect_range +gtk_selection_model_select_all +gtk_selection_model_unselect_all + +gtk_selection_model_selection_changed + +GTK_SELECTION_MODEL +GTK_SELECTION_MODEL_CLASS +GTK_SELECTION_MODEL_GET_CLASS +GTK_IS_SELECTION_MODEL +GTK_IS_SELECTION_MODEL_CLASS +GTK_TYPE_SELECTION_MODEL + +gtk_selection_model_get_type +
+ +
+gtksingleselection +GtkSingleSelection +GtkSingleSelection +GTK_INVALID_LIST_POSITION +gtk_single_selection_new +gtk_single_selection_get_selected +gtk_single_selection_set_selected +gtk_single_selection_get_selected_item +gtk_single_selection_get_autoselect +gtk_single_selection_set_autoselect +gtk_single_selection_get_can_unselect +gtk_single_selection_set_can_unselect + +gtk_single_selection_get_type +
+
gtkbuildable GtkBuildable diff --git a/docs/reference/gtk/gtk4.types.in b/docs/reference/gtk/gtk4.types.in index ecb383d45b..79d73c04a4 100644 --- a/docs/reference/gtk/gtk4.types.in +++ b/docs/reference/gtk/gtk4.types.in @@ -142,6 +142,7 @@ gtk_scrollbar_get_type gtk_scrolled_window_get_type gtk_search_bar_get_type gtk_search_entry_get_type +gtk_selection_model_get_type gtk_separator_get_type gtk_separator_menu_item_get_type gtk_separator_tool_item_get_type @@ -151,6 +152,7 @@ gtk_shortcuts_window_get_type gtk_shortcuts_section_get_type gtk_shortcuts_group_get_type gtk_shortcuts_shortcut_get_type +gtk_single_selection_get_type gtk_size_group_get_type gtk_snapshot_get_type gtk_spin_button_get_type diff --git a/gtk/gtk.h b/gtk/gtk.h index e5e1995bc0..90ee2e8654 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -184,6 +184,7 @@ #include #include #include +#include #include #include #include diff --git a/gtk/gtkselectionmodel.c b/gtk/gtkselectionmodel.c new file mode 100644 index 0000000000..0be3961bc2 --- /dev/null +++ b/gtk/gtkselectionmodel.c @@ -0,0 +1,301 @@ +/* + * Copyright © 2018 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + +#include "config.h" + +#include "gtkselectionmodel.h" + +#include "gtkintl.h" +#include "gtkmarshalers.h" + +/** + * SECTION:gtkselectionmodel + * @Title: GtkSelectionModel + * @Short_description: An extension of the list model interface that handles selections + * @See_also: #GListModel, #GtkSingleSelection + * + * #GtkSelectionModel is an interface that extends the #GListModel interface by adding + * support for selections. This support is then used by widgets using list models to add + * the ability to select and unselect various items. + * + * GTK provides default implementations of the mode common selection modes such as + * #GtkSingleSelection, so you will only need to implement this interface if you want + * detailed control about how selections should be handled. + * + * A #GtkSelectionModel supports a single boolean per row indicating if a row is selected + * or not. This can be queried via gtk_selection_model_is_selected(). When the selected + * state of one or more rows changes, the model will emit the + * GtkSelectionModel::selection-changed signal by calling the + * gtk_selection_model_selection_changed() function. The positions given in that signal + * may have their selection state changed, though that is not a requirement. + * If new items added to the model via the #GListModel::items-changed signal are selected + * or not is up to the implementation. + * + * Additionally, the interface can expose functionality to select and unselect items. + * If these functions are implemented, GTK's list widgets will allow users to select and + * unselect items. However, #GtkSelectionModels are free to only implement them + * partially or not at all. In that case the widgets will not support the unimplemented + * operations. + * + * When selecting or unselecting is supported by a model, the return values of the + * selection functions do NOT indicate if selection or unselection happened. They are + * only meant to indicate complete failure, like when this mode of selecting is not + * supported by the model. + * Selections may happen asynchronously, so the only reliable way to find out when an + * item was selected is to listen to the signals that indicate selection. + */ + +G_DEFINE_INTERFACE (GtkSelectionModel, gtk_selection_model, G_TYPE_LIST_MODEL) + +enum { + SELECTION_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static gboolean +gtk_selection_model_default_is_selected (GtkSelectionModel *model, + guint position) +{ + return FALSE; +} + +static gboolean +gtk_selection_model_default_select_item (GtkSelectionModel *model, + guint position, + gboolean exclusive) +{ + return FALSE; +} +static gboolean +gtk_selection_model_default_unselect_item (GtkSelectionModel *model, + guint position) +{ + return FALSE; +} + +static gboolean +gtk_selection_model_default_select_range (GtkSelectionModel *model, + guint position, + guint n_items, + gboolean exclusive) +{ + return FALSE; +} + +static gboolean +gtk_selection_model_default_unselect_range (GtkSelectionModel *model, + guint position, + guint n_items) +{ + return FALSE; +} + +static gboolean +gtk_selection_model_default_select_all (GtkSelectionModel *model) +{ + return gtk_selection_model_select_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)), FALSE); +} + +static gboolean +gtk_selection_model_default_unselect_all (GtkSelectionModel *model) +{ + return gtk_selection_model_unselect_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));; +} + +static gboolean +gtk_selection_model_default_query_range (GtkSelectionModel *model, + guint *position, + guint *n_items) +{ + *n_items = 1; + return gtk_selection_model_is_selected (model, *position); +} + +static void +gtk_selection_model_default_init (GtkSelectionModelInterface *iface) +{ + iface->is_selected = gtk_selection_model_default_is_selected; + iface->select_item = gtk_selection_model_default_select_item; + iface->unselect_item = gtk_selection_model_default_unselect_item; + iface->select_range = gtk_selection_model_default_select_range; + iface->unselect_range = gtk_selection_model_default_unselect_range; + iface->select_all = gtk_selection_model_default_select_all; + iface->unselect_all = gtk_selection_model_default_unselect_all; + iface->query_range = gtk_selection_model_default_query_range; + + /** + * GtkSelectionModel::selection-changed + * @model: a #GtkSelectionModel + * @position: The first item that may have changed + * @n_items: number of items with changes + * + * Emitted when the selection state of some of the items in @model changes. + * + * Note that this signal does not specify the new selection state of the items, + * they need to be queried manually. + * It is also not necessary for a model to change the selection state of any of + * the items in the selection model, though it would be rather useless to emit + * such a signal. + */ + signals[SELECTION_CHANGED] = + g_signal_new ("selection-changed", + GTK_TYPE_SELECTION_MODEL, + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + _gtk_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + g_signal_set_va_marshaller (signals[SELECTION_CHANGED], + GTK_TYPE_SELECTION_MODEL, + _gtk_marshal_VOID__UINT_UINTv); +} + +/** + * gtk_selection_model_is_selected: + * @model: a #GtkSelectionModel + * @position: the position of the item to query + * + * Checks if the given item is selected. + * + * Returns: %TRUE if the item is selected + **/ +gboolean +gtk_selection_model_is_selected (GtkSelectionModel *model, + guint position) +{ + GtkSelectionModelInterface *iface; + + g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0); + + iface = GTK_SELECTION_MODEL_GET_IFACE (model); + return iface->is_selected (model, position); +} + +gboolean +gtk_selection_model_select_item (GtkSelectionModel *model, + guint position, + gboolean exclusive) +{ + GtkSelectionModelInterface *iface; + + g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0); + + iface = GTK_SELECTION_MODEL_GET_IFACE (model); + return iface->select_item (model, position, exclusive); +} + +gboolean +gtk_selection_model_unselect_item (GtkSelectionModel *model, + guint position) +{ + GtkSelectionModelInterface *iface; + + g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0); + + iface = GTK_SELECTION_MODEL_GET_IFACE (model); + return iface->unselect_item (model, position); +} + +gboolean +gtk_selection_model_select_range (GtkSelectionModel *model, + guint position, + guint n_items, + gboolean exclusive) +{ + GtkSelectionModelInterface *iface; + + g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0); + + iface = GTK_SELECTION_MODEL_GET_IFACE (model); + return iface->select_range (model, position, n_items, exclusive); +} + +gboolean +gtk_selection_model_unselect_range (GtkSelectionModel *model, + guint position, + guint n_items) +{ + GtkSelectionModelInterface *iface; + + g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0); + + iface = GTK_SELECTION_MODEL_GET_IFACE (model); + return iface->unselect_range (model, position, n_items); +} + +gboolean +gtk_selection_model_select_all (GtkSelectionModel *model) +{ + GtkSelectionModelInterface *iface; + + g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0); + + iface = GTK_SELECTION_MODEL_GET_IFACE (model); + return iface->select_all (model); +} + +gboolean +gtk_selection_model_unselect_all (GtkSelectionModel *model) +{ + GtkSelectionModelInterface *iface; + + g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0); + + iface = GTK_SELECTION_MODEL_GET_IFACE (model); + return iface->unselect_all (model); +} + +/** + * gtk_selection_model_query_range: + * @model: a #GtkSelectionModel + * @position: (inout): specifies the position on input, and the first element of the range on output + * @n_items: (out): returns the size of the range + * + * This function allows to query the selection status of multiple elements at once. + * It is passed a position and returns a range of elements of uniform selection status. + * The returned range is guaranteed to include the passed-in position. + * The selection status is returned from this function. + * + * Returns: %TRUE if the elements in the returned range are selected, %FALSE otherwise + */ +gboolean +gtk_selection_model_query_range (GtkSelectionModel *model, + guint *position, + guint *n_items) +{ + GtkSelectionModelInterface *iface; + + g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE); + + iface = GTK_SELECTION_MODEL_GET_IFACE (model); + return iface->query_range (model, position, n_items); +} + +void +gtk_selection_model_selection_changed (GtkSelectionModel *model, + guint position, + guint n_items) +{ + g_return_if_fail (GTK_IS_SELECTION_MODEL (model)); + + g_signal_emit (model, signals[SELECTION_CHANGED], 0, position, n_items); +} + diff --git a/gtk/gtkselectionmodel.h b/gtk/gtkselectionmodel.h new file mode 100644 index 0000000000..65c424716d --- /dev/null +++ b/gtk/gtkselectionmodel.h @@ -0,0 +1,125 @@ +/* + * Copyright © 2018 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + +#ifndef __GTK_SELECTION_MODEL_H__ +#define __GTK_SELECTION_MODEL_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_SELECTION_MODEL (gtk_selection_model_get_type ()) + +GDK_AVAILABLE_IN_ALL +G_DECLARE_INTERFACE (GtkSelectionModel, gtk_selection_model, GTK, SELECTION_MODEL, GListModel) + +/** + * GtkSelectionModelInterface: + * @is_selected: Return if the item at the given position is selected. + * @select_item: Select the item in the given position. If the operation + * is known to fail, return %FALSE. + * @unselect_item: Unselect the item in the given position. If the + * operation is known to fail, return %FALSE. + * @select_range: Select all items in the given range. If the operation + * is unsupported or known to fail for all items, return %FALSE. + * @unselect_range: Unselect all items in the given range. If the + * operation is unsupported or known to fail for all items, return + * %FALSE. + * @select_all: Select all items in the model. If the operation is + * unsupported or known to fail for all items, return %FALSE. + * @unselect_all: Unselect all items in the model. If the operation is + * unsupported or known to fail for all items, return %FALSE. + * + * The list of virtual functions for the #GtkSelectionModel interface. + * All getter functions are mandatory to implement, but the model does + * not need to implement any functions to support selecting or unselecting + * items. Of course, if the model does not do that, it means that users + * cannot select or unselect items in a list widgets using the model. + */ +struct _GtkSelectionModelInterface +{ + /*< private >*/ + GTypeInterface g_iface; + + /*< public >*/ + gboolean (* is_selected) (GtkSelectionModel *model, + guint position); + + gboolean (* select_item) (GtkSelectionModel *model, + guint position, + gboolean exclusive); + gboolean (* unselect_item) (GtkSelectionModel *model, + guint position); + gboolean (* select_range) (GtkSelectionModel *model, + guint position, + guint n_items, + gboolean exclusive); + gboolean (* unselect_range) (GtkSelectionModel *model, + guint position, + guint n_items); + gboolean (* select_all) (GtkSelectionModel *model); + gboolean (* unselect_all) (GtkSelectionModel *model); + gboolean (* query_range) (GtkSelectionModel *model, + guint *position, + guint *n_items); +}; + +GDK_AVAILABLE_IN_ALL +gboolean gtk_selection_model_is_selected (GtkSelectionModel *model, + guint position); + +GDK_AVAILABLE_IN_ALL +gboolean gtk_selection_model_select_item (GtkSelectionModel *model, + guint position, + gboolean exclusive); +GDK_AVAILABLE_IN_ALL +gboolean gtk_selection_model_unselect_item (GtkSelectionModel *model, + guint position); +GDK_AVAILABLE_IN_ALL +gboolean gtk_selection_model_select_range (GtkSelectionModel *model, + guint position, + guint n_items, + gboolean exclusive); +GDK_AVAILABLE_IN_ALL +gboolean gtk_selection_model_unselect_range (GtkSelectionModel *model, + guint position, + guint n_items); +GDK_AVAILABLE_IN_ALL +gboolean gtk_selection_model_select_all (GtkSelectionModel *model); +GDK_AVAILABLE_IN_ALL +gboolean gtk_selection_model_unselect_all (GtkSelectionModel *model); + +GDK_AVAILABLE_IN_ALL +gboolean gtk_selection_model_query_range (GtkSelectionModel *model, + guint *position, + guint *n_items); + +/* for implementations only */ +GDK_AVAILABLE_IN_ALL +void gtk_selection_model_selection_changed (GtkSelectionModel *model, + guint position, + guint n_items); + +G_END_DECLS + +#endif /* __GTK_SELECTION_MODEL_H__ */ diff --git a/gtk/meson.build b/gtk/meson.build index c700b37289..f6fd1023ca 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -323,6 +323,7 @@ gtk_public_sources = files([ 'gtksearchbar.c', 'gtksearchentry.c', 'gtkselection.c', + 'gtkselectionmodel.c', 'gtkseparator.c', 'gtkseparatormenuitem.c', 'gtkseparatortoolitem.c', @@ -557,6 +558,7 @@ gtk_public_headers = files([ 'gtksearchbar.h', 'gtksearchentry.h', 'gtkselection.h', + 'gtkselectionmodel.h', 'gtkseparator.h', 'gtkseparatormenuitem.h', 'gtkseparatortoolitem.h', -- cgit v1.2.1