[gnome-software] Make the GsAppTile an abstract class



commit 5dc0a4e7df88ada338c0a07c4bd47cb4f3ebb8c6
Author: Joaquim Rocha <jrocha endlessm com>
Date:   Thu Jun 23 13:31:15 2016 +0200

    Make the GsAppTile an abstract class
    
    These changes make the GsAppTile an abstract class and move its former
    functionality to a new class called GsSummaryTile.
    
    The reason for the change is that all app tiles should extend this
    class, allowing for a more more abstract code which simplifies the use
    of different app tiles in the future.

 src/Makefile.am                  |    4 +-
 src/gnome-software.gresource.xml |    2 +-
 src/gs-app-tile.c                |  271 ++------------------------------
 src/gs-app-tile.h                |   13 ++-
 src/gs-popular-tile.c            |   21 ++-
 src/gs-shell-category.c          |    6 +-
 src/gs-summary-tile.c            |  321 ++++++++++++++++++++++++++++++++++++++
 src/gs-summary-tile.h            |   42 +++++
 src/gs-summary-tile.ui           |  131 ++++++++++++++++
 9 files changed, 543 insertions(+), 268 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 86e2e7b..dae7135 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -48,7 +48,7 @@ UI_FILES =                                            \
        gs-app-addon-row.ui                             \
        gs-app-folder-dialog.ui                         \
        gs-app-row.ui                                   \
-       gs-app-tile.ui                                  \
+       gs-summary-tile.ui                              \
        gs-auth-dialog.ui                               \
        gs-category-tile.ui                             \
        gs-feature-tile.ui                              \
@@ -166,6 +166,8 @@ gnome_software_SOURCES =                            \
        gs-category-tile.h                              \
        gs-app-tile.c                                   \
        gs-app-tile.h                                   \
+       gs-summary-tile.c                               \
+       gs-summary-tile.h                               \
        gs-app-folder-dialog.c                          \
        gs-app-folder-dialog.h                          \
        gs-first-run-dialog.c                           \
diff --git a/src/gnome-software.gresource.xml b/src/gnome-software.gresource.xml
index 23a127a..d3a9391 100644
--- a/src/gnome-software.gresource.xml
+++ b/src/gnome-software.gresource.xml
@@ -6,7 +6,7 @@
   <file preprocess="xml-stripblanks">gs-app-addon-row.ui</file>
   <file preprocess="xml-stripblanks">gs-app-folder-dialog.ui</file>
   <file preprocess="xml-stripblanks">gs-app-row.ui</file>
-  <file preprocess="xml-stripblanks">gs-app-tile.ui</file>
+  <file preprocess="xml-stripblanks">gs-summary-tile.ui</file>
   <file preprocess="xml-stripblanks">gs-auth-dialog.ui</file>
   <file preprocess="xml-stripblanks">gs-category-tile.ui</file>
   <file preprocess="xml-stripblanks">gs-feature-tile.ui</file>
diff --git a/src/gs-app-tile.c b/src/gs-app-tile.c
index 4841c22..81912cd 100644
--- a/src/gs-app-tile.c
+++ b/src/gs-app-tile.c
@@ -28,285 +28,50 @@
 #include "gs-star-widget.h"
 #include "gs-common.h"
 
-struct _GsAppTile
-{
-       GtkButton        parent_instance;
-
-       GsApp           *app;
-       GtkWidget       *image;
-       GtkWidget       *name;
-       GtkWidget       *summary;
-       GtkWidget       *eventbox;
-       GtkWidget       *stack;
-       gint             preferred_width;
-};
-
-G_DEFINE_TYPE (GsAppTile, gs_app_tile, GTK_TYPE_BUTTON)
-
-enum {
-       PROP_0,
-       PROP_PREFERRED_WIDTH
-};
+G_DEFINE_ABSTRACT_TYPE (GsAppTile, gs_app_tile, GTK_TYPE_BUTTON)
 
 GsApp *
 gs_app_tile_get_app (GsAppTile *tile)
 {
-       g_return_val_if_fail (GS_IS_APP_TILE (tile), NULL);
-
-       return tile->app;
-}
-
-static gboolean
-app_state_changed_idle (gpointer user_data)
-{
-       GsAppTile *tile = GS_APP_TILE (user_data);
-       AtkObject *accessible;
-       GtkWidget *label;
-       gboolean installed;
-       g_autofree gchar *name = NULL;
-
-       accessible = gtk_widget_get_accessible (GTK_WIDGET (tile));
-
-       label = gtk_bin_get_child (GTK_BIN (tile->eventbox));
-       switch (gs_app_get_state (tile->app)) {
-       case AS_APP_STATE_INSTALLED:
-       case AS_APP_STATE_UPDATABLE:
-       case AS_APP_STATE_UPDATABLE_LIVE:
-               installed = TRUE;
-               name = g_strdup_printf ("%s (%s)",
-                                       gs_app_get_name (tile->app),
-                                       _("Installed"));
-               /* TRANSLATORS: this is the small blue label on the tile
-                * that tells the user the application is installed */
-               gtk_label_set_label (GTK_LABEL (label), _("Installed"));
-               break;
-       case AS_APP_STATE_INSTALLING:
-               installed = TRUE;
-               name = g_strdup_printf ("%s (%s)",
-                                       gs_app_get_name (tile->app),
-                                       _("Installing"));
-               /* TRANSLATORS: this is the small blue label on the tile
-                * that tells the user the application is being installed */
-               gtk_label_set_label (GTK_LABEL (label), _("Installing"));
-               break;
-       case AS_APP_STATE_REMOVING:
-               installed = TRUE;
-               name = g_strdup_printf ("%s (%s)",
-                                       gs_app_get_name (tile->app),
-                                       _("Removing"));
-               /* TRANSLATORS: this is the small blue label on the tile
-                * that tells the user the application is being removed */
-               gtk_label_set_label (GTK_LABEL (label), _("Removing"));
-               break;
-       case AS_APP_STATE_QUEUED_FOR_INSTALL:
-       case AS_APP_STATE_AVAILABLE:
-       default:
-               installed = FALSE;
-               name = g_strdup (gs_app_get_name (tile->app));
-               break;
-       }
+       GsAppTileClass *klass;
 
-       gtk_widget_set_visible (tile->eventbox, installed);
+       g_return_val_if_fail (GS_IS_APP_TILE (tile), NULL);
 
-       if (GTK_IS_ACCESSIBLE (accessible)) {
-               atk_object_set_name (accessible, name);
-               atk_object_set_description (accessible, gs_app_get_summary (tile->app));
-       }
+       klass = GS_APP_TILE_GET_CLASS (tile);
+       g_assert (klass->get_app);
 
-       g_object_unref (tile);
-       return G_SOURCE_REMOVE;
-}
-
-static void
-app_state_changed (GsApp *app, GParamSpec *pspec, GsAppTile *tile)
-{
-       g_idle_add (app_state_changed_idle, g_object_ref (tile));
+       return klass->get_app(tile);
 }
 
 void
 gs_app_tile_set_app (GsAppTile *tile, GsApp *app)
 {
-       const GdkPixbuf *pixbuf;
-       g_autofree gchar *text = NULL;
+       GsAppTileClass *klass;
 
        g_return_if_fail (GS_IS_APP_TILE (tile));
-       g_return_if_fail (GS_IS_APP (app) || app == NULL);
-
-       gtk_image_clear (GTK_IMAGE (tile->image));
-       gtk_image_set_pixel_size (GTK_IMAGE (tile->image), 64);
-
-       if (tile->app)
-               g_signal_handlers_disconnect_by_func (tile->app, app_state_changed, tile);
-
-       g_set_object (&tile->app, app);
-       if (!app)
-               return;
-
-       gtk_stack_set_visible_child_name (GTK_STACK (tile->stack), "content");
-
-       g_signal_connect (tile->app, "notify::state",
-                         G_CALLBACK (app_state_changed), tile);
-       app_state_changed (tile->app, NULL, tile);
-
-       pixbuf = gs_app_get_pixbuf (app);
-       if (pixbuf != NULL)
-               gs_image_set_from_pixbuf (GTK_IMAGE (tile->image), pixbuf);
-       gtk_label_set_label (GTK_LABEL (tile->name), gs_app_get_name (app));
-
-       /* perhaps set custom css */
-       gs_utils_widget_set_css_app (app, GTK_WIDGET (tile),
-                                    "GnomeSoftware::AppTile-css");
-
-       /* some kinds have boring summaries */
-       switch (gs_app_get_kind (app)) {
-       case AS_APP_KIND_SHELL_EXTENSION:
-               text = g_strdup (gs_app_get_description (app));
-               g_strdelimit (text, "\n\t", ' ');
-               break;
-       default:
-               text = g_strdup (gs_app_get_summary (app));
-               break;
-       }
-
-       gtk_label_set_label (GTK_LABEL (tile->summary), text);
-       gtk_widget_set_visible (tile->summary, text && text[0]);
-}
-
-static void
-gs_app_tile_destroy (GtkWidget *widget)
-{
-       GsAppTile *tile = GS_APP_TILE (widget);
+       g_return_if_fail (!app || GS_IS_APP (app));
 
-       if (tile->app)
-               g_signal_handlers_disconnect_by_func (tile->app, app_state_changed, tile);
-       g_clear_object (&tile->app);
+       klass = GS_APP_TILE_GET_CLASS (tile);
+       g_assert (klass->get_app);
 
-       GTK_WIDGET_CLASS (gs_app_tile_parent_class)->destroy (widget);
+       klass->set_app(tile, app);
 }
 
-static void
-gs_app_tile_init (GsAppTile *tile)
-{
-       gtk_widget_set_has_window (GTK_WIDGET (tile), FALSE);
-       tile->preferred_width = -1;
-       gtk_widget_init_template (GTK_WIDGET (tile));
-}
-
-static void
-gs_app_tile_get_property (GObject *object,
-                         guint prop_id,
-                         GValue *value,
-                         GParamSpec *pspec)
-{
-       GsAppTile *app_tile = GS_APP_TILE (object);
-
-       switch (prop_id) {
-       case PROP_PREFERRED_WIDTH:
-               g_value_set_int (value, app_tile->preferred_width);
-               break;
-       default:
-               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-               break;
-       }
-}
-
-static void
-gs_app_tile_set_property (GObject *object,
-                         guint prop_id,
-                         const GValue *value,
-                         GParamSpec *pspec)
-{
-       GsAppTile *app_tile = GS_APP_TILE (object);
-
-       switch (prop_id) {
-       case PROP_PREFERRED_WIDTH:
-               app_tile->preferred_width = g_value_get_int (value);
-               gtk_widget_queue_resize (GTK_WIDGET (app_tile));
-               break;
-       default:
-               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-               break;
-       }
-}
-
-static void
-gs_app_get_preferred_width (GtkWidget *widget,
-                           gint *min, gint *nat)
-{
-#if GTK_CHECK_VERSION(3,20,0)
-       gint m;
-#else
-       gint m, n;
-#endif
-       GsAppTile *app_tile = GS_APP_TILE (widget);
-
-       if (app_tile->preferred_width < 0) {
-               /* Just retrieve the default values */
-               GTK_WIDGET_CLASS (gs_app_tile_parent_class)->get_preferred_width (widget, min, nat);
-               return;
-       }
-
-/* It's because of some bugs in gtkbutton.c 3.18 and before.
- * We can remove this when we branch for 3.20 *and* require GTK 3.20. */
-#if GTK_CHECK_VERSION(3,20,0)
-       GTK_WIDGET_CLASS (gs_app_tile_parent_class)->get_preferred_width (widget, &m, NULL);
-#else
-       GTK_WIDGET_CLASS (gs_app_tile_parent_class)->get_preferred_width (widget, &m, &n);
-#endif
-
-       if (min != NULL)
-               *min = m;
-       if (nat != NULL)
-               *nat = MAX (m, app_tile->preferred_width);
-}
-
-static void
+void
 gs_app_tile_class_init (GsAppTileClass *klass)
-{
-       GObjectClass *object_class = G_OBJECT_CLASS (klass);
-       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-
-       object_class->get_property = gs_app_tile_get_property;
-       object_class->set_property = gs_app_tile_set_property;
+{}
 
-       widget_class->destroy = gs_app_tile_destroy;
-       widget_class->get_preferred_width = gs_app_get_preferred_width;
-
-       /**
-        * GsAppTile:preferred-width:
-        *
-        * The only purpose of this property is to be retrieved as the
-        * natural width by gtk_widget_get_preferred_width() fooling the
-        * parent #GtkFlowBox container and making it switch to more columns
-        * (children per row) if it is able to place n+1 children in a row
-        * having this specified width.  If this value is less than a minimum
-        * width of this app tile then the minimum is returned instead.  Set
-        * this property to -1 to turn off this feature and return the default
-        * natural width instead.
-        */
-       g_object_class_install_property (object_class, PROP_PREFERRED_WIDTH,
-               g_param_spec_int ("preferred-width",
-                                 "Preferred width",
-                                 "The preferred width of this widget, its only purpose is to trick the 
parent container",
-                                 -1, G_MAXINT, -1,
-                                 G_PARAM_READWRITE));
-
-       gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-app-tile.ui");
-
-       gtk_widget_class_bind_template_child (widget_class, GsAppTile, image);
-       gtk_widget_class_bind_template_child (widget_class, GsAppTile, name);
-       gtk_widget_class_bind_template_child (widget_class, GsAppTile, summary);
-       gtk_widget_class_bind_template_child (widget_class, GsAppTile, eventbox);
-       gtk_widget_class_bind_template_child (widget_class, GsAppTile, stack);
-}
+void
+gs_app_tile_init (GsAppTile *tile)
+{}
 
 GtkWidget *
-gs_app_tile_new (GsApp *cat)
+gs_app_tile_new (GsApp *app)
 {
        GsAppTile *tile;
 
        tile = g_object_new (GS_TYPE_APP_TILE, NULL);
-       gs_app_tile_set_app (tile, cat);
+       gs_app_tile_set_app (tile, app);
 
        return GTK_WIDGET (tile);
 }
diff --git a/src/gs-app-tile.h b/src/gs-app-tile.h
index e089275..8b22aff 100644
--- a/src/gs-app-tile.h
+++ b/src/gs-app-tile.h
@@ -30,9 +30,18 @@ G_BEGIN_DECLS
 
 #define GS_TYPE_APP_TILE (gs_app_tile_get_type ())
 
-G_DECLARE_FINAL_TYPE (GsAppTile, gs_app_tile, GS, APP_TILE, GtkButton)
+G_DECLARE_DERIVABLE_TYPE (GsAppTile, gs_app_tile, GS, APP_TILE, GtkButton)
 
-GtkWidget      *gs_app_tile_new        (GsApp          *app);
+struct _GsAppTileClass
+{
+       GtkButtonClass          parent_class;
+
+       void                    (*set_app)              (GsAppTile      *tile,
+                                                        GsApp          *app);
+        GsApp                  *(*get_app)             (GsAppTile      *tile);
+};
+
+GtkWidget      *gs_app_tile_new        (GsApp *app);
 GsApp          *gs_app_tile_get_app    (GsAppTile      *tile);
 void            gs_app_tile_set_app    (GsAppTile      *tile,
                                         GsApp          *cat);
diff --git a/src/gs-popular-tile.c b/src/gs-popular-tile.c
index 5a5e893..341251f 100644
--- a/src/gs-popular-tile.c
+++ b/src/gs-popular-tile.c
@@ -40,12 +40,12 @@ struct _GsPopularTile
        GtkWidget       *stars;
 };
 
-G_DEFINE_TYPE (GsPopularTile, gs_popular_tile, GTK_TYPE_BUTTON)
+G_DEFINE_TYPE (GsPopularTile, gs_popular_tile, GS_TYPE_APP_TILE)
 
-GsApp *
-gs_popular_tile_get_app (GsPopularTile *tile)
+static GsApp *
+gs_popular_tile_get_app (GsAppTile *app_tile)
 {
-       g_return_val_if_fail (GS_IS_POPULAR_TILE (tile), NULL);
+       GsPopularTile *tile = GS_POPULAR_TILE (app_tile);
 
        return tile->app;
 }
@@ -100,10 +100,11 @@ app_state_changed (GsApp *app, GParamSpec *pspec, GsPopularTile *tile)
        g_idle_add (app_state_changed_idle, g_object_ref (tile));
 }
 
-void
-gs_popular_tile_set_app (GsPopularTile *tile, GsApp *app)
+static void
+gs_popular_tile_set_app (GsAppTile *app_tile, GsApp *app)
 {
-       g_return_if_fail (GS_IS_POPULAR_TILE (tile));
+       GsPopularTile *tile = GS_POPULAR_TILE (app_tile);
+
        g_return_if_fail (GS_IS_APP (app) || app == NULL);
 
        if (tile->app)
@@ -160,9 +161,13 @@ static void
 gs_popular_tile_class_init (GsPopularTileClass *klass)
 {
        GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+       GsAppTileClass *app_tile_class = GS_APP_TILE_CLASS (klass);
 
        widget_class->destroy = gs_popular_tile_destroy;
 
+       app_tile_class->set_app = gs_popular_tile_set_app;
+       app_tile_class->get_app = gs_popular_tile_get_app;
+
        gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-popular-tile.ui");
 
        gtk_widget_class_bind_template_child (widget_class, GsPopularTile, label);
@@ -178,7 +183,7 @@ gs_popular_tile_new (GsApp *app)
        GsPopularTile *tile;
 
        tile = g_object_new (GS_TYPE_POPULAR_TILE, NULL);
-       gs_popular_tile_set_app (tile, app);
+       gs_app_tile_set_app (GS_APP_TILE (tile), app);
 
        return GTK_WIDGET (tile);
 }
diff --git a/src/gs-shell-category.c b/src/gs-shell-category.c
index 92cb1e9..c40bc14 100644
--- a/src/gs-shell-category.c
+++ b/src/gs-shell-category.c
@@ -26,7 +26,7 @@
 #include <glib/gi18n.h>
 
 #include "gs-common.h"
-#include "gs-app-tile.h"
+#include "gs-summary-tile.h"
 #include "gs-shell-category.h"
 
 struct _GsShellCategory
@@ -101,7 +101,7 @@ gs_shell_category_get_apps_cb (GObject *source_object,
 
        for (i = 0; i < gs_app_list_length (list); i++) {
                app = gs_app_list_index (list, i);
-               tile = gs_app_tile_new (app);
+               tile = gs_summary_tile_new (app);
                g_signal_connect (tile, "clicked",
                                  G_CALLBACK (app_tile_clicked), self);
                gtk_container_add (GTK_CONTAINER (self->category_detail_box), tile);
@@ -140,7 +140,7 @@ gs_shell_category_reload (GsPage *page)
        gs_container_remove_all (GTK_CONTAINER (self->category_detail_box));
        count = MIN(30, gs_category_get_size (self->subcategory));
        for (i = 0; i < count; i++) {
-               tile = gs_app_tile_new (NULL);
+               tile = gs_summary_tile_new (NULL);
                gtk_container_add (GTK_CONTAINER (self->category_detail_box), tile);
                gtk_widget_set_can_focus (gtk_widget_get_parent (tile), FALSE);
        }
diff --git a/src/gs-summary-tile.c b/src/gs-summary-tile.c
new file mode 100644
index 0000000..4454655
--- /dev/null
+++ b/src/gs-summary-tile.c
@@ -0,0 +1,321 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Matthias Clasen <mclasen redhat com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gs-summary-tile.h"
+#include "gs-star-widget.h"
+#include "gs-common.h"
+
+struct _GsSummaryTile
+{
+       GsAppTile        parent_instance;
+
+       GsApp           *app;
+       GtkWidget       *image;
+       GtkWidget       *name;
+       GtkWidget       *summary;
+       GtkWidget       *eventbox;
+       GtkWidget       *stack;
+       gint             preferred_width;
+};
+
+G_DEFINE_TYPE (GsSummaryTile, gs_summary_tile, GS_TYPE_APP_TILE)
+
+enum {
+       PROP_0,
+       PROP_PREFERRED_WIDTH
+};
+
+static GsApp *
+gs_summary_tile_get_app (GsAppTile *tile)
+{
+       return GS_SUMMARY_TILE (tile)->app;
+}
+
+static gboolean
+app_state_changed_idle (gpointer user_data)
+{
+       GsSummaryTile *tile = GS_SUMMARY_TILE (user_data);
+       AtkObject *accessible;
+       GtkWidget *label;
+       gboolean installed;
+       g_autofree gchar *name = NULL;
+
+       accessible = gtk_widget_get_accessible (GTK_WIDGET (tile));
+
+       label = gtk_bin_get_child (GTK_BIN (tile->eventbox));
+       switch (gs_app_get_state (tile->app)) {
+       case AS_APP_STATE_INSTALLED:
+       case AS_APP_STATE_UPDATABLE:
+       case AS_APP_STATE_UPDATABLE_LIVE:
+               installed = TRUE;
+               name = g_strdup_printf ("%s (%s)",
+                                       gs_app_get_name (tile->app),
+                                       _("Installed"));
+               /* TRANSLATORS: this is the small blue label on the tile
+                * that tells the user the application is installed */
+               gtk_label_set_label (GTK_LABEL (label), _("Installed"));
+               break;
+       case AS_APP_STATE_INSTALLING:
+               installed = TRUE;
+               name = g_strdup_printf ("%s (%s)",
+                                       gs_app_get_name (tile->app),
+                                       _("Installing"));
+               /* TRANSLATORS: this is the small blue label on the tile
+                * that tells the user the application is being installed */
+               gtk_label_set_label (GTK_LABEL (label), _("Installing"));
+               break;
+       case AS_APP_STATE_REMOVING:
+               installed = TRUE;
+               name = g_strdup_printf ("%s (%s)",
+                                       gs_app_get_name (tile->app),
+                                       _("Removing"));
+               /* TRANSLATORS: this is the small blue label on the tile
+                * that tells the user the application is being removed */
+               gtk_label_set_label (GTK_LABEL (label), _("Removing"));
+               break;
+       case AS_APP_STATE_QUEUED_FOR_INSTALL:
+       case AS_APP_STATE_AVAILABLE:
+       default:
+               installed = FALSE;
+               name = g_strdup (gs_app_get_name (tile->app));
+               break;
+       }
+
+       gtk_widget_set_visible (tile->eventbox, installed);
+
+       if (GTK_IS_ACCESSIBLE (accessible)) {
+               atk_object_set_name (accessible, name);
+               atk_object_set_description (accessible, gs_app_get_summary (tile->app));
+       }
+
+       g_object_unref (tile);
+       return G_SOURCE_REMOVE;
+}
+
+static void
+app_state_changed (GsApp *app, GParamSpec *pspec, GsSummaryTile *tile)
+{
+       g_idle_add (app_state_changed_idle, g_object_ref (tile));
+}
+
+static void
+gs_summary_tile_set_app (GsAppTile *app_tile, GsApp *app)
+{
+       const GdkPixbuf *pixbuf;
+       GsSummaryTile *tile = GS_SUMMARY_TILE (app_tile);
+       g_autofree gchar *text = NULL;
+
+       g_return_if_fail (GS_IS_APP (app) || app == NULL);
+
+       gtk_image_clear (GTK_IMAGE (tile->image));
+       gtk_image_set_pixel_size (GTK_IMAGE (tile->image), 64);
+
+       if (tile->app)
+               g_signal_handlers_disconnect_by_func (tile->app, app_state_changed, tile);
+
+       g_set_object (&tile->app, app);
+       if (!app)
+               return;
+
+       gtk_stack_set_visible_child_name (GTK_STACK (tile->stack), "content");
+
+       g_signal_connect (tile->app, "notify::state",
+                         G_CALLBACK (app_state_changed), tile);
+       app_state_changed (tile->app, NULL, tile);
+
+       pixbuf = gs_app_get_pixbuf (app);
+       if (pixbuf != NULL)
+               gs_image_set_from_pixbuf (GTK_IMAGE (tile->image), pixbuf);
+       gtk_label_set_label (GTK_LABEL (tile->name), gs_app_get_name (app));
+
+       /* perhaps set custom css */
+       gs_utils_widget_set_css_app (app, GTK_WIDGET (tile),
+                                    "GnomeSoftware::AppTile-css");
+
+       /* some kinds have boring summaries */
+       switch (gs_app_get_kind (app)) {
+       case AS_APP_KIND_SHELL_EXTENSION:
+               text = g_strdup (gs_app_get_description (app));
+               g_strdelimit (text, "\n\t", ' ');
+               break;
+       default:
+               text = g_strdup (gs_app_get_summary (app));
+               break;
+       }
+
+       gtk_label_set_label (GTK_LABEL (tile->summary), text);
+       gtk_widget_set_visible (tile->summary, text && text[0]);
+}
+
+static void
+gs_summary_tile_destroy (GtkWidget *widget)
+{
+       GsSummaryTile *tile = GS_SUMMARY_TILE (widget);
+
+       if (tile->app)
+               g_signal_handlers_disconnect_by_func (tile->app, app_state_changed, tile);
+       g_clear_object (&tile->app);
+
+       GTK_WIDGET_CLASS (gs_summary_tile_parent_class)->destroy (widget);
+}
+
+static void
+gs_summary_tile_init (GsSummaryTile *tile)
+{
+       gtk_widget_set_has_window (GTK_WIDGET (tile), FALSE);
+       tile->preferred_width = -1;
+       gtk_widget_init_template (GTK_WIDGET (tile));
+}
+
+static void
+gs_summary_tile_get_property (GObject *object,
+                                guint prop_id,
+                                GValue *value,
+                                GParamSpec *pspec)
+{
+       GsSummaryTile *app_tile = GS_SUMMARY_TILE (object);
+
+       switch (prop_id) {
+       case PROP_PREFERRED_WIDTH:
+               g_value_set_int (value, app_tile->preferred_width);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+gs_summary_tile_set_property (GObject *object,
+                                guint prop_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+       GsSummaryTile *app_tile = GS_SUMMARY_TILE (object);
+
+       switch (prop_id) {
+       case PROP_PREFERRED_WIDTH:
+               app_tile->preferred_width = g_value_get_int (value);
+               gtk_widget_queue_resize (GTK_WIDGET (app_tile));
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+gs_app_get_preferred_width (GtkWidget *widget,
+                           gint *min, gint *nat)
+{
+#if GTK_CHECK_VERSION(3,20,0)
+       gint m;
+#else
+       gint m, n;
+#endif
+       GsSummaryTile *app_tile = GS_SUMMARY_TILE (widget);
+
+       if (app_tile->preferred_width < 0) {
+               /* Just retrieve the default values */
+               GTK_WIDGET_CLASS (gs_summary_tile_parent_class)->get_preferred_width (widget, min, nat);
+               return;
+       }
+
+       /* It's because of some bugs in gtkbutton.c 3.18 and before.
+        * We can remove this when we branch for 3.20 *and* require GTK 3.20. */
+#if GTK_CHECK_VERSION(3,20,0)
+       GTK_WIDGET_CLASS (gs_summary_tile_parent_class)->get_preferred_width (widget, &m, NULL);
+#else
+       GTK_WIDGET_CLASS (gs_summary_tile_parent_class)->get_preferred_width (widget, &m, &n);
+#endif
+
+       if (min != NULL)
+               *min = m;
+       if (nat != NULL)
+               *nat = MAX (m, app_tile->preferred_width);
+}
+
+static void
+gs_summary_tile_class_init (GsSummaryTileClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+       GsAppTileClass *tile_class = GS_APP_TILE_CLASS (klass);
+
+       object_class->get_property = gs_summary_tile_get_property;
+       object_class->set_property = gs_summary_tile_set_property;
+
+       widget_class->destroy = gs_summary_tile_destroy;
+       widget_class->get_preferred_width = gs_app_get_preferred_width;
+
+       tile_class->set_app = gs_summary_tile_set_app;
+       tile_class->get_app = gs_summary_tile_get_app;
+
+       /**
+        * GsAppTile:preferred-width:
+        *
+        * The only purpose of this property is to be retrieved as the
+        * natural width by gtk_widget_get_preferred_width() fooling the
+        * parent #GtkFlowBox container and making it switch to more columns
+        * (children per row) if it is able to place n+1 children in a row
+        * having this specified width.  If this value is less than a minimum
+        * width of this app tile then the minimum is returned instead.  Set
+        * this property to -1 to turn off this feature and return the default
+        * natural width instead.
+        */
+       g_object_class_install_property (object_class, PROP_PREFERRED_WIDTH,
+                                        g_param_spec_int ("preferred-width",
+                                                          "Preferred width",
+                                                          "The preferred width of this widget, its only 
purpose is to trick the parent container",
+                                                          -1, G_MAXINT, -1,
+                                                          G_PARAM_READWRITE));
+
+       gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-summary-tile.ui");
+
+       gtk_widget_class_bind_template_child (widget_class, GsSummaryTile,
+                                             image);
+       gtk_widget_class_bind_template_child (widget_class, GsSummaryTile,
+                                             name);
+       gtk_widget_class_bind_template_child (widget_class, GsSummaryTile,
+                                             summary);
+       gtk_widget_class_bind_template_child (widget_class, GsSummaryTile,
+                                             eventbox);
+       gtk_widget_class_bind_template_child (widget_class, GsSummaryTile,
+                                             stack);
+}
+
+GtkWidget *
+gs_summary_tile_new (GsApp *cat)
+{
+       GsAppTile *tile;
+
+       tile = g_object_new (GS_TYPE_SUMMARY_TILE, NULL);
+       gs_summary_tile_set_app (tile, cat);
+
+       return GTK_WIDGET (tile);
+}
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-summary-tile.h b/src/gs-summary-tile.h
new file mode 100644
index 0000000..cd5c421
--- /dev/null
+++ b/src/gs-summary-tile.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Matthias Clasen <mclasen redhat com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef GS_SUMMARY_TILE_H
+#define GS_SUMMARY_TILE_H
+
+#include <gtk/gtk.h>
+
+#include "gs-app.h"
+#include "gs-app-tile.h"
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_SUMMARY_TILE (gs_summary_tile_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsSummaryTile, gs_summary_tile, GS, SUMMARY_TILE, GsAppTile)
+
+GtkWidget      *gs_summary_tile_new    (GsApp          *app);
+
+G_END_DECLS
+
+#endif /* GS_SUMMARY_TILE_H */
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-summary-tile.ui b/src/gs-summary-tile.ui
new file mode 100644
index 0000000..06c615d
--- /dev/null
+++ b/src/gs-summary-tile.ui
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.10 -->
+  <template class="GsSummaryTile" parent="GsAppTile">
+    <property name="visible">True</property>
+    <property name="hexpand">True</property>
+    <!-- This is the minimum (sic!) width of a tile when the GtkFlowBox parent container switches to 3 
columns -->
+    <property name="preferred-width">270</property>
+    <style>
+      <class name="view"/>
+      <class name="tile"/>
+    </style>
+    <child>
+      <object class="GtkStack" id="stack">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkImage" id="waiting">
+            <property name="visible">True</property>
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <property name="pixel-size">16</property>
+            <property name="icon-name">content-loading-symbolic</property>
+            <style>
+              <class name="dim-label"/>
+            </style>
+          </object>
+          <packing>
+            <property name="name">waiting</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkOverlay" id="overlay">
+            <property name="visible">True</property>
+            <property name="halign">fill</property>
+            <property name="valign">fill</property>
+            <child type="overlay">
+              <object class="GtkEventBox" id="eventbox">
+                <property name="no_show_all">True</property>
+                <property name="visible_window">True</property>
+                <property name="halign">start</property>
+                <property name="valign">start</property>
+                <property name="margin-top">58</property>
+                <property name="margin-start">12</property>
+                <style>
+                  <class name="installed-overlay-box"/>
+                </style>
+                <child>
+                  <object class="GtkLabel" id="installed-label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Installed</property>
+                    <property name="margin-start">16</property>
+                    <property name="margin-end">16</property>
+                    <property name="margin-top">4</property>
+                    <property name="margin-bottom">4</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkGrid" id="grid">
+                <property name="visible">True</property>
+                <property name="margin-top">14</property>
+                <property name="margin-bottom">15</property>
+                <property name="margin-start">17</property>
+                <property name="margin-end">17</property>
+                <property name="row-spacing">3</property>
+                <property name="column-spacing">12</property>
+                <child>
+                  <object class="GtkImage" id="image">
+                    <property name="visible">True</property>
+                    <property name="width-request">64</property>
+                    <property name="height-request">64</property>
+                  </object>
+                  <packing>
+                    <property name="left-attach">0</property>
+                    <property name="top-attach">0</property>
+                    <property name="width">1</property>
+                    <property name="height">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="name">
+                    <property name="visible">True</property>
+                    <property name="ellipsize">end</property>
+                    <property name="xalign">0.0</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                    <style>
+                      <class name="app-tile-label"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="left-attach">1</property>
+                    <property name="top-attach">0</property>
+                    <property name="width">1</property>
+                    <property name="height">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="summary">
+                    <property name="visible">True</property>
+                    <property name="ellipsize">end</property>
+                    <property name="xalign">0.0</property>
+                    <property name="yalign">0.0</property>
+                    <property name="lines">2</property>
+                    <property name="vexpand">True</property>
+                    <property name="single-line-mode">True</property>
+                    <style>
+                      <class name="app-tile-label"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="left-attach">1</property>
+                    <property name="top-attach">2</property>
+                    <property name="width">1</property>
+                    <property name="height">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="name">content</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]