listview: Add initial support for displaying selections

This commit is contained in:
Benjamin Otte 2018-10-03 18:53:06 +02:00 committed by Matthias Clasen
parent 01386aef29
commit d8c116f20a
3 changed files with 81 additions and 16 deletions

View File

@ -28,7 +28,7 @@ struct _GtkListItemManager
GObject parent_instance; GObject parent_instance;
GtkWidget *widget; GtkWidget *widget;
GListModel *model; GtkSelectionModel *model;
GtkListItemFactory *factory; GtkListItemFactory *factory;
}; };
@ -107,7 +107,7 @@ gtk_list_item_manager_get_factory (GtkListItemManager *self)
void void
gtk_list_item_manager_set_model (GtkListItemManager *self, gtk_list_item_manager_set_model (GtkListItemManager *self,
GListModel *model) GtkSelectionModel *model)
{ {
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self)); g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model)); g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
@ -121,7 +121,7 @@ gtk_list_item_manager_set_model (GtkListItemManager *self,
self->model = g_object_ref (model); self->model = g_object_ref (model);
} }
GListModel * GtkSelectionModel *
gtk_list_item_manager_get_model (GtkListItemManager *self) gtk_list_item_manager_get_model (GtkListItemManager *self)
{ {
g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL); g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL);
@ -255,14 +255,16 @@ gtk_list_item_manager_acquire_list_item (GtkListItemManager *self,
{ {
GtkListItem *result; GtkListItem *result;
gpointer item; gpointer item;
gboolean selected;
g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL); g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL);
g_return_val_if_fail (prev_sibling == NULL || GTK_IS_WIDGET (prev_sibling), NULL); g_return_val_if_fail (prev_sibling == NULL || GTK_IS_WIDGET (prev_sibling), NULL);
result = gtk_list_item_factory_create (self->factory); result = gtk_list_item_factory_create (self->factory);
item = g_list_model_get_item (self->model, position); item = g_list_model_get_item (G_LIST_MODEL (self->model), position);
gtk_list_item_factory_bind (self->factory, result, position, item, FALSE); selected = gtk_selection_model_is_selected (self->model, position);
gtk_list_item_factory_bind (self->factory, result, position, item, selected);
g_object_unref (item); g_object_unref (item);
gtk_widget_insert_after (GTK_WIDGET (result), self->widget, prev_sibling); gtk_widget_insert_after (GTK_WIDGET (result), self->widget, prev_sibling);
@ -297,7 +299,7 @@ gtk_list_item_manager_try_reacquire_list_item (GtkListItemManager *self,
g_return_val_if_fail (prev_sibling == NULL || GTK_IS_WIDGET (prev_sibling), NULL); g_return_val_if_fail (prev_sibling == NULL || GTK_IS_WIDGET (prev_sibling), NULL);
/* XXX: can we avoid temporarily allocating items on failure? */ /* XXX: can we avoid temporarily allocating items on failure? */
item = g_list_model_get_item (self->model, position); item = g_list_model_get_item (G_LIST_MODEL (self->model), position);
if (g_hash_table_steal_extended (change->items, item, NULL, (gpointer *) &result)) if (g_hash_table_steal_extended (change->items, item, NULL, (gpointer *) &result))
{ {
gtk_list_item_factory_update (self->factory, result, position, FALSE); gtk_list_item_factory_update (self->factory, result, position, FALSE);
@ -334,9 +336,11 @@ gtk_list_item_manager_move_list_item (GtkListItemManager *self,
GtkWidget *prev_sibling) GtkWidget *prev_sibling)
{ {
gpointer item; gpointer item;
gboolean selected;
item = g_list_model_get_item (self->model, position); item = g_list_model_get_item (G_LIST_MODEL (self->model), position);
gtk_list_item_factory_bind (self->factory, GTK_LIST_ITEM (list_item), position, item, FALSE); selected = gtk_selection_model_is_selected (self->model, position);
gtk_list_item_factory_bind (self->factory, GTK_LIST_ITEM (list_item), position, item, selected);
gtk_widget_insert_after (list_item, _gtk_widget_get_parent (list_item), prev_sibling); gtk_widget_insert_after (list_item, _gtk_widget_get_parent (list_item), prev_sibling);
g_object_unref (item); g_object_unref (item);
} }
@ -355,10 +359,13 @@ gtk_list_item_manager_update_list_item (GtkListItemManager *self,
GtkWidget *item, GtkWidget *item,
guint position) guint position)
{ {
gboolean selected;
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self)); g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
g_return_if_fail (GTK_IS_LIST_ITEM (item)); g_return_if_fail (GTK_IS_LIST_ITEM (item));
gtk_list_item_factory_update (self->factory, GTK_LIST_ITEM (item), position, FALSE); selected = gtk_selection_model_is_selected (self->model, position);
gtk_list_item_factory_update (self->factory, GTK_LIST_ITEM (item), position, selected);
} }
/* /*

View File

@ -24,6 +24,7 @@
#include "gtk/gtktypes.h" #include "gtk/gtktypes.h"
#include "gtk/gtklistitemfactoryprivate.h" #include "gtk/gtklistitemfactoryprivate.h"
#include "gtk/gtkselectionmodel.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@ -46,8 +47,8 @@ void gtk_list_item_manager_set_factory (GtkListItemMana
GtkListItemFactory *factory); GtkListItemFactory *factory);
GtkListItemFactory * gtk_list_item_manager_get_factory (GtkListItemManager *self); GtkListItemFactory * gtk_list_item_manager_get_factory (GtkListItemManager *self);
void gtk_list_item_manager_set_model (GtkListItemManager *self, void gtk_list_item_manager_set_model (GtkListItemManager *self,
GListModel *model); GtkSelectionModel *model);
GListModel * gtk_list_item_manager_get_model (GtkListItemManager *self); GtkSelectionModel * gtk_list_item_manager_get_model (GtkListItemManager *self);
guint gtk_list_item_manager_get_size (GtkListItemManager *self); guint gtk_list_item_manager_get_size (GtkListItemManager *self);

View File

@ -27,6 +27,8 @@
#include "gtklistitemfactoryprivate.h" #include "gtklistitemfactoryprivate.h"
#include "gtklistitemmanagerprivate.h" #include "gtklistitemmanagerprivate.h"
#include "gtkscrollable.h" #include "gtkscrollable.h"
#include "gtkselectionmodel.h"
#include "gtksingleselection.h"
#include "gtkwidgetprivate.h" #include "gtkwidgetprivate.h"
/* Maximum number of list items created by the listview. /* Maximum number of list items created by the listview.
@ -908,7 +910,7 @@ gtk_list_view_model_items_changed_cb (GListModel *model,
guint i, offset, anchor_pos; guint i, offset, anchor_pos;
row = gtk_list_view_get_row (self, position, &offset); row = gtk_list_view_get_row (self, position, &offset);
for (new_row = gtk_rb_tree_node_get_previous (row); for (new_row = row ? gtk_rb_tree_node_get_previous (row) : gtk_rb_tree_get_last (self->rows);
new_row && new_row->widget == NULL; new_row && new_row->widget == NULL;
new_row = gtk_rb_tree_node_get_previous (new_row)) new_row = gtk_rb_tree_node_get_previous (new_row))
{ } { }
@ -969,6 +971,9 @@ gtk_list_view_model_items_changed_cb (GListModel *model,
anchor_pos = gtk_list_item_get_position (GTK_LIST_ITEM (self->anchor)); anchor_pos = gtk_list_item_get_position (GTK_LIST_ITEM (self->anchor));
anchor_pos = position + (anchor_pos - position) * added / removed; anchor_pos = position + (anchor_pos - position) * added / removed;
if (anchor_pos >= g_list_model_get_n_items (self->model) &&
anchor_pos > 0)
anchor_pos--;
} }
gtk_list_view_set_anchor (self, anchor_pos, self->anchor_align, change, position); gtk_list_view_set_anchor (self, anchor_pos, self->anchor_align, change, position);
} }
@ -997,6 +1002,34 @@ gtk_list_view_model_items_changed_cb (GListModel *model,
gtk_list_item_manager_end_change (self->item_manager, change); gtk_list_item_manager_end_change (self->item_manager, change);
} }
static void
gtk_list_view_model_selection_changed_cb (GListModel *model,
guint position,
guint n_items,
GtkListView *self)
{
ListRow *row;
guint offset;
row = gtk_list_view_get_row (self, position, &offset);
if (offset)
{
position += row->n_rows - offset;
n_items -= row->n_rows - offset;
row = gtk_rb_tree_node_get_next (row);
}
while (n_items > 0)
{
if (row->widget)
gtk_list_item_manager_update_list_item (self->item_manager, row->widget, position);
position += row->n_rows;
n_items -= MIN (n_items, row->n_rows);
row = gtk_rb_tree_node_get_next (row);
}
}
static void static void
gtk_list_view_clear_model (GtkListView *self) gtk_list_view_clear_model (GtkListView *self)
{ {
@ -1005,6 +1038,9 @@ gtk_list_view_clear_model (GtkListView *self)
gtk_list_view_remove_rows (self, NULL, 0, g_list_model_get_n_items (self->model)); gtk_list_view_remove_rows (self, NULL, 0, g_list_model_get_n_items (self->model));
g_signal_handlers_disconnect_by_func (self->model,
gtk_list_view_model_selection_changed_cb,
self);
g_signal_handlers_disconnect_by_func (self->model, g_signal_handlers_disconnect_by_func (self->model,
gtk_list_view_model_items_changed_cb, gtk_list_view_model_items_changed_cb,
self); self);
@ -1264,9 +1300,12 @@ gtk_list_view_get_model (GtkListView *self)
/** /**
* gtk_list_view_set_model: * gtk_list_view_set_model:
* @self: a #GtkListView * @self: a #GtkListView
* @file: (allow-none) (transfer none): the model to use or %NULL for none * @model: (allow-none) (transfer none): the model to use or %NULL for none
* *
* Sets the #GListModel to use for * Sets the #GListModel to use.
*
* If the @model is a #GtkSelectionModel, it is used for managing the selection.
* Otherwise, @self creates a #GtkSingleSelection for the selection.
**/ **/
void void
gtk_list_view_set_model (GtkListView *self, gtk_list_view_set_model (GtkListView *self,
@ -1280,20 +1319,38 @@ gtk_list_view_set_model (GtkListView *self,
gtk_list_view_clear_model (self); gtk_list_view_clear_model (self);
gtk_list_item_manager_set_model (self->item_manager, model);
if (model) if (model)
{ {
GtkSelectionModel *selection_model;
self->model = g_object_ref (model); self->model = g_object_ref (model);
if (GTK_IS_SELECTION_MODEL (model))
selection_model = GTK_SELECTION_MODEL (g_object_ref (model));
else
selection_model = GTK_SELECTION_MODEL (gtk_single_selection_new (model));
gtk_list_item_manager_set_model (self->item_manager, selection_model);
g_signal_connect (model, g_signal_connect (model,
"items-changed", "items-changed",
G_CALLBACK (gtk_list_view_model_items_changed_cb), G_CALLBACK (gtk_list_view_model_items_changed_cb),
self); self);
g_signal_connect (selection_model,
"selection-changed",
G_CALLBACK (gtk_list_view_model_selection_changed_cb),
self);
g_object_unref (selection_model);
gtk_list_view_add_rows (self, 0, g_list_model_get_n_items (model)); gtk_list_view_add_rows (self, 0, g_list_model_get_n_items (model));
gtk_list_view_set_anchor (self, 0, 0, NULL, (guint) -1); gtk_list_view_set_anchor (self, 0, 0, NULL, (guint) -1);
} }
else
{
gtk_list_item_manager_set_model (self->item_manager, NULL);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
} }