diff options
author | Debarshi Ray <debarshir@gnome.org> | 2016-11-23 14:43:21 +0100 |
---|---|---|
committer | Debarshi Ray <debarshir@gnome.org> | 2017-01-07 13:59:56 +0100 |
commit | 96dd95da506b36d1a8b177ce609312aa6d25652b (patch) | |
tree | 915f78e8189cfd56097479f3fa999566105fe23d | |
parent | 30681a486c0266b5fd0f2a984fba2d1b0dfe8d29 (diff) | |
download | libgd-96dd95da506b36d1a8b177ce609312aa6d25652b.tar.gz |
Add GdMainBox
This is a widget that can use different GdMainBoxGeneric
implementations to render multiple GdMainBoxItems, and makes it easy
to switch between them. eg., it can be a list or a grid of items.
https://bugzilla.gnome.org/show_bug.cgi?id=774914
-rw-r--r-- | libgd/gd-main-box.c | 586 | ||||
-rw-r--r-- | libgd/gd-main-box.h | 61 |
2 files changed, 647 insertions, 0 deletions
diff --git a/libgd/gd-main-box.c b/libgd/gd-main-box.c new file mode 100644 index 0000000..669736e --- /dev/null +++ b/libgd/gd-main-box.c @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2016, 2017 Red Hat, Inc. + * + * This program 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 of the License, or (at your + * option) any later version. + * + * This program 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Debarshi Ray <debarshir@gnome.org> + * + */ + +#include "gd-main-box.h" +#include "gd-main-box-child.h" +#include "gd-main-box-generic.h" +#include "gd-main-icon-box.h" + +#define MAIN_BOX_TYPE_INITIAL -1 + +typedef struct _GdMainBoxPrivate GdMainBoxPrivate; + +struct _GdMainBoxPrivate +{ + GListModel *model; + GdMainBoxType current_type; + GtkWidget *current_box; + gboolean selection_mode; + gboolean show_primary_text; + gboolean show_secondary_text; +}; + +enum +{ + PROP_BOX_TYPE = 1, + PROP_SELECTION_MODE, + PROP_SHOW_PRIMARY_TEXT, + PROP_SHOW_SECONDARY_TEXT, + PROP_MODEL, + NUM_PROPERTIES +}; + +enum +{ + ITEM_ACTIVATED, + SELECTION_CHANGED, + SELECTION_MODE_REQUEST, + NUM_SIGNALS +}; + +static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; +static guint signals[NUM_SIGNALS] = { 0, }; + +G_DEFINE_TYPE_WITH_PRIVATE (GdMainBox, gd_main_box, GTK_TYPE_BIN) + +static void +gd_main_box_activate_item_for_child (GdMainBox *self, GdMainBoxChild *child) +{ + GdMainBoxPrivate *priv; + GdMainBoxItem *item; + + priv = gd_main_box_get_instance_private (self); + + if (priv->model == NULL) + return; + + item = gd_main_box_child_get_item (child); + if (item == NULL) + return; + + g_signal_emit (self, signals[ITEM_ACTIVATED], 0, item); +} + +static void +gd_main_box_apply_selection_mode (GdMainBox *self) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + + gd_main_box_generic_set_selection_mode (GD_MAIN_BOX_GENERIC (priv->current_box), priv->selection_mode); + + if (!priv->selection_mode) + { + if (priv->model != NULL) + gd_main_box_unselect_all (self); + } +} + +static void +gd_main_box_item_activated_cb (GdMainBox *self, GdMainBoxChild *child) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + + if (!priv->selection_mode) + gd_main_box_activate_item_for_child (self, child); +} + +static void +gd_main_box_model_items_changed_cb (GdMainBox *self, guint position, guint removed, guint added) +{ + if (removed == 0) + return; + + g_signal_emit (self, signals[SELECTION_CHANGED], 0); +} + +static void +gd_main_box_selection_changed_cb (GdMainBox *self) +{ + g_signal_emit (self, signals[SELECTION_CHANGED], 0); +} + +static void +gd_main_box_selection_mode_request_cb (GdMainBox *self) +{ + g_signal_emit (self, signals[SELECTION_MODE_REQUEST], 0); +} + +static void +gd_main_box_rebuild (GdMainBox *self) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + + if (priv->current_box != NULL) + gtk_widget_destroy (priv->current_box); + + switch (priv->current_type) + { + case GD_MAIN_BOX_ICON: + priv->current_box = gd_main_icon_box_new (); + break; + + case GD_MAIN_BOX_LIST: + default: + g_assert_not_reached (); + break; + } + + gtk_widget_set_hexpand (priv->current_box, TRUE); + gtk_widget_set_valign (priv->current_box, GTK_ALIGN_START); + g_object_bind_property (self, "show-primary-text", + priv->current_box, "show-primary-text", + G_BINDING_SYNC_CREATE); + g_object_bind_property (self, "show-secondary-text", + priv->current_box, "show-secondary-text", + G_BINDING_SYNC_CREATE); + gtk_container_add (GTK_CONTAINER (self), priv->current_box); + + g_signal_connect_swapped (priv->current_box, + "item-activated", + G_CALLBACK (gd_main_box_item_activated_cb), + self); + g_signal_connect_swapped (priv->current_box, + "selection-changed", + G_CALLBACK (gd_main_box_selection_changed_cb), + self); + g_signal_connect_swapped (priv->current_box, + "selection-mode-request", + G_CALLBACK (gd_main_box_selection_mode_request_cb), + self); + + gd_main_box_generic_set_model (GD_MAIN_BOX_GENERIC (priv->current_box), priv->model); + gd_main_box_apply_selection_mode (self); + + gtk_widget_show_all (GTK_WIDGET (self)); +} + +static gboolean +gd_main_box_draw (GtkWidget *widget, cairo_t *cr) +{ + GtkStyleContext *context; + gint height; + gint width; + + context = gtk_widget_get_style_context (widget); + height = gtk_widget_get_allocated_height (widget); + width = gtk_widget_get_allocated_width (widget); + gtk_render_background (context, cr, 0, 0, width, height); + + return GTK_WIDGET_CLASS (gd_main_box_parent_class)->draw (widget, cr); +} + +static void +gd_main_box_dispose (GObject *obj) +{ + GdMainBox *self = GD_MAIN_BOX (obj); + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + + g_clear_object (&priv->model); + + G_OBJECT_CLASS (gd_main_box_parent_class)->dispose (obj); +} + +static void +gd_main_box_init (GdMainBox *self) +{ + GdMainBoxPrivate *priv; + GtkStyleContext *context; + + priv = gd_main_box_get_instance_private (self); + + context = gtk_widget_get_style_context (GTK_WIDGET (self)); + gtk_style_context_add_class (context, "content-view"); + + /* so that we get constructed with the right view even at startup */ + priv->current_type = MAIN_BOX_TYPE_INITIAL; +} + +static void +gd_main_box_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ + GdMainBox *self = GD_MAIN_BOX (object); + + switch (property_id) + { + case PROP_BOX_TYPE: + g_value_set_int (value, gd_main_box_get_box_type (self)); + break; + case PROP_SELECTION_MODE: + g_value_set_boolean (value, gd_main_box_get_selection_mode (self)); + break; + case PROP_SHOW_PRIMARY_TEXT: + g_value_set_boolean (value, gd_main_box_get_show_primary_text (self)); + break; + case PROP_SHOW_SECONDARY_TEXT: + g_value_set_boolean (value, gd_main_box_get_show_secondary_text (self)); + break; + case PROP_MODEL: + g_value_set_object (value, gd_main_box_get_model (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gd_main_box_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ + GdMainBox *self = GD_MAIN_BOX (object); + + switch (property_id) + { + case PROP_BOX_TYPE: + gd_main_box_set_box_type (self, g_value_get_int (value)); + break; + case PROP_SELECTION_MODE: + gd_main_box_set_selection_mode (self, g_value_get_boolean (value)); + break; + case PROP_SHOW_PRIMARY_TEXT: + gd_main_box_set_show_primary_text (self, g_value_get_boolean (value)); + break; + case PROP_SHOW_SECONDARY_TEXT: + gd_main_box_set_show_secondary_text (self, g_value_get_boolean (value)); + break; + case PROP_MODEL: + gd_main_box_set_model (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gd_main_box_class_init (GdMainBoxClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass); + + oclass->get_property = gd_main_box_get_property; + oclass->set_property = gd_main_box_set_property; + oclass->dispose = gd_main_box_dispose; + wclass->draw = gd_main_box_draw; + + properties[PROP_BOX_TYPE] = g_param_spec_int ("box-type", + "Box type", + "Box type", + GD_MAIN_BOX_ICON, + GD_MAIN_BOX_LIST, + GD_MAIN_BOX_ICON, + G_PARAM_EXPLICIT_NOTIFY | + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_MODEL] = g_param_spec_object ("model", + "Model", + "The GListModel", + G_TYPE_LIST_MODEL, + G_PARAM_EXPLICIT_NOTIFY | + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_SELECTION_MODE] = g_param_spec_boolean ("selection-mode", + "Selection mode", + "Whether the view is in selection mode", + FALSE, + G_PARAM_EXPLICIT_NOTIFY | + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_SHOW_PRIMARY_TEXT] = g_param_spec_boolean ("show-primary-text", + "Show primary text", + "Whether each GdMainBoxItem's primary-text is going " + "to be shown", + FALSE, + G_PARAM_EXPLICIT_NOTIFY | + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_SHOW_SECONDARY_TEXT] = g_param_spec_boolean ("show-secondary-text", + "Show secondary text", + "Whether each GdMainBoxItem's secondary-text is " + "going to be shown", + FALSE, + G_PARAM_EXPLICIT_NOTIFY | + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + signals[ITEM_ACTIVATED] = g_signal_new ("item-activated", + GD_TYPE_MAIN_BOX, + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + GD_TYPE_MAIN_BOX_ITEM); + + signals[SELECTION_CHANGED] = g_signal_new ("selection-changed", + GD_TYPE_MAIN_BOX, + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + signals[SELECTION_MODE_REQUEST] = g_signal_new ("selection-mode-request", + GD_TYPE_MAIN_BOX, + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + g_object_class_install_properties (oclass, NUM_PROPERTIES, properties); +} + +GtkWidget * +gd_main_box_new (GdMainBoxType type) +{ + return g_object_new (GD_TYPE_MAIN_BOX, "box-type", type, NULL); +} + +GdMainBoxType +gd_main_box_get_box_type (GdMainBox *self) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + return priv->current_type; +} + +void +gd_main_box_set_box_type (GdMainBox *self, GdMainBoxType type) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + + if (type == priv->current_type) + return; + + priv->current_type = type; + gd_main_box_rebuild (self); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BOX_TYPE]); +} + +gboolean +gd_main_box_get_selection_mode (GdMainBox *self) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + return priv->selection_mode; +} + +gboolean +gd_main_box_get_show_primary_text (GdMainBox *self) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + return priv->show_primary_text; +} + +gboolean +gd_main_box_get_show_secondary_text (GdMainBox *self) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + return priv->show_secondary_text; +} + +void +gd_main_box_set_selection_mode (GdMainBox *self, gboolean selection_mode) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + + if (selection_mode == priv->selection_mode) + return; + + priv->selection_mode = selection_mode; + gd_main_box_apply_selection_mode (self); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTION_MODE]); +} + +void +gd_main_box_set_show_primary_text (GdMainBox *self, gboolean show_primary_text) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + + if (show_primary_text == priv->show_primary_text) + return; + + priv->show_primary_text = show_primary_text; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_PRIMARY_TEXT]); +} + +void +gd_main_box_set_show_secondary_text (GdMainBox *self, gboolean show_secondary_text) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + + if (show_secondary_text == priv->show_secondary_text) + return; + + priv->show_secondary_text = show_secondary_text; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_SECONDARY_TEXT]); +} + +/** + * gd_main_box_get_model: + * @self: + * + * Returns: (transfer none): + */ +GListModel * +gd_main_box_get_model (GdMainBox *self) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + return priv->model; +} + +/** + * gd_main_box_set_model: + * @self: + * @model: (allow-none): + * + */ +void +gd_main_box_set_model (GdMainBox *self, GListModel *model) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + + if (model == priv->model) + return; + + if (priv->model) + g_signal_handlers_disconnect_by_func (priv->model, gd_main_box_model_items_changed_cb, self); + + g_clear_object (&priv->model); + + if (model != NULL) + { + priv->model = g_object_ref (model); + g_signal_connect_object (priv->model, + "items-changed", + G_CALLBACK (gd_main_box_model_items_changed_cb), + self, + G_CONNECT_SWAPPED); + } + + gd_main_box_generic_set_model (GD_MAIN_BOX_GENERIC (priv->current_box), priv->model); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]); +} + +/** + * gd_main_box_get_generic_box: + * @self: + * + * Returns: (transfer none): + */ +GtkWidget * +gd_main_box_get_generic_box (GdMainBox *self) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + return priv->current_box; +} + +/** + * gd_main_box_get_selection: + * @self: + * + * Returns: (element-type GdMainBoxItem) (transfer full): + */ +GList * +gd_main_box_get_selection (GdMainBox *self) +{ + GdMainBoxPrivate *priv; + GList *l; + GList *selected_children; + GList *selection = NULL; + + priv = gd_main_box_get_instance_private (self); + + selected_children = gd_main_box_generic_get_selected_children (GD_MAIN_BOX_GENERIC (priv->current_box)); + for (l = selected_children; l != NULL; l = l->next) + { + GdMainBoxChild *child = GD_MAIN_BOX_CHILD (l->data); + GdMainBoxItem *item; + + item = gd_main_box_child_get_item (child); + selection = g_list_prepend (selection, g_object_ref (item)); + } + + selection = g_list_reverse (selection); + g_list_free (selected_children); + return selection; +} + +void +gd_main_box_select_all (GdMainBox *self) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + gd_main_box_generic_select_all (GD_MAIN_BOX_GENERIC (priv->current_box)); +} + +void +gd_main_box_unselect_all (GdMainBox *self) +{ + GdMainBoxPrivate *priv; + + priv = gd_main_box_get_instance_private (self); + gd_main_box_generic_unselect_all (GD_MAIN_BOX_GENERIC (priv->current_box)); +} diff --git a/libgd/gd-main-box.h b/libgd/gd-main-box.h new file mode 100644 index 0000000..1ff8038 --- /dev/null +++ b/libgd/gd-main-box.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016, 2017 Red Hat, Inc. + * + * This program 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 of the License, or (at your + * option) any later version. + * + * This program 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Debarshi Ray <debarshir@gnome.org> + * + */ + +#ifndef __GD_MAIN_BOX_H__ +#define __GD_MAIN_BOX_H__ + +#include <gio/gio.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GD_TYPE_MAIN_BOX gd_main_box_get_type() +G_DECLARE_DERIVABLE_TYPE (GdMainBox, gd_main_box, GD, MAIN_BOX, GtkBin) + +typedef enum +{ + GD_MAIN_BOX_ICON, + GD_MAIN_BOX_LIST +} GdMainBoxType; + +struct _GdMainBoxClass +{ + GtkBinClass parent_class; +}; + +GtkWidget * gd_main_box_new (GdMainBoxType type); +GdMainBoxType gd_main_box_get_box_type (GdMainBox *self); +GListModel * gd_main_box_get_model (GdMainBox *self); +GList * gd_main_box_get_selection (GdMainBox *self); +gboolean gd_main_box_get_selection_mode (GdMainBox *self); +gboolean gd_main_box_get_show_primary_text (GdMainBox *self); +gboolean gd_main_box_get_show_secondary_text (GdMainBox *self); +void gd_main_box_select_all (GdMainBox *self); +void gd_main_box_set_box_type (GdMainBox *self, GdMainBoxType type); +void gd_main_box_set_model (GdMainBox *self, GListModel *model); +void gd_main_box_set_selection_mode (GdMainBox *self, gboolean selection_mode); +void gd_main_box_set_show_primary_text (GdMainBox *self, gboolean show_primary_text); +void gd_main_box_set_show_secondary_text (GdMainBox *self, gboolean show_secondary_text); +void gd_main_box_unselect_all (GdMainBox *self); + +G_END_DECLS + +#endif /* __GD_MAIN_BOX_H__ */ |