[gnome-software: 1/13] progress-button: Allow showing an icon




commit 587f30fe1b317071132df639fae5ec5f84ffcd13
Author: Adrien Plazas <kekun plazas laposte net>
Date:   Thu Jun 24 11:55:06 2021 +0200

    progress-button: Allow showing an icon
    
    This adds the icon-name and show-icon properties (and matching
    accessors) and use them to allow showing an icon instead of a label,
    with a smooth transition between the two thanks to a stack.
    
    This also overrides the label property and add new accessors, as the
    ones from GtkLabel destroy the widget's layout and hence can't be used.

 src/gnome-software.gresource.xml |   1 +
 src/gs-app-row.c                 |  18 +--
 src/gs-progress-button.c         | 253 +++++++++++++++++++++++++++++++++++++++
 src/gs-progress-button.h         |   9 ++
 src/gs-progress-button.ui        |  25 ++++
 5 files changed, 297 insertions(+), 9 deletions(-)
---
diff --git a/src/gnome-software.gresource.xml b/src/gnome-software.gresource.xml
index 8da93bec8..f04199dab 100644
--- a/src/gnome-software.gresource.xml
+++ b/src/gnome-software.gresource.xml
@@ -22,6 +22,7 @@
   <file preprocess="xml-stripblanks">gs-overview-page.ui</file>
   <file preprocess="xml-stripblanks">gs-origin-popover-row.ui</file>
   <file preprocess="xml-stripblanks">gs-prefs-dialog.ui</file>
+  <file preprocess="xml-stripblanks">gs-progress-button.ui</file>
   <file preprocess="xml-stripblanks">gs-removal-dialog.ui</file>
   <file preprocess="xml-stripblanks">gs-repo-row.ui</file>
   <file preprocess="xml-stripblanks">gs-repos-dialog.ui</file>
diff --git a/src/gs-app-row.c b/src/gs-app-row.c
index 5e464e323..369d2a184 100644
--- a/src/gs-app-row.c
+++ b/src/gs-app-row.c
@@ -124,37 +124,37 @@ gs_app_row_refresh_button (GsAppRow *app_row, gboolean missing_search_result)
                if (missing_search_result) {
                        /* TRANSLATORS: this is a button next to the search results that
                         * allows the application to be easily installed */
-                       gtk_button_set_label (GTK_BUTTON (priv->button), _("Visit website"));
+                       gs_progress_button_set_label (GS_PROGRESS_BUTTON (priv->button), _("Visit website"));
                } else {
                        /* TRANSLATORS: this is a button next to the search results that
                         * allows the application to be easily installed.
                         * The ellipsis indicates that further steps are required */
-                       gtk_button_set_label (GTK_BUTTON (priv->button), _("Install…"));
+                       gs_progress_button_set_label (GS_PROGRESS_BUTTON (priv->button), _("Install…"));
                }
                break;
        case GS_APP_STATE_QUEUED_FOR_INSTALL:
                gtk_widget_set_visible (priv->button, TRUE);
                /* TRANSLATORS: this is a button next to the search results that
                 * allows to cancel a queued install of the application */
-               gtk_button_set_label (GTK_BUTTON (priv->button), _("Cancel"));
+               gs_progress_button_set_label (GS_PROGRESS_BUTTON (priv->button), _("Cancel"));
                break;
        case GS_APP_STATE_AVAILABLE:
        case GS_APP_STATE_AVAILABLE_LOCAL:
                gtk_widget_set_visible (priv->button, TRUE);
                /* TRANSLATORS: this is a button next to the search results that
                 * allows the application to be easily installed */
-               gtk_button_set_label (GTK_BUTTON (priv->button), _("Install"));
+               gs_progress_button_set_label (GS_PROGRESS_BUTTON (priv->button), _("Install"));
                break;
        case GS_APP_STATE_UPDATABLE_LIVE:
                gtk_widget_set_visible (priv->button, TRUE);
                if (priv->show_update) {
                        /* TRANSLATORS: this is a button in the updates panel
                         * that allows the app to be easily updated live */
-                       gtk_button_set_label (GTK_BUTTON (priv->button), _("Update"));
+                       gs_progress_button_set_label (GS_PROGRESS_BUTTON (priv->button), _("Update"));
                } else {
                        /* TRANSLATORS: this is a button next to the search results that
                         * allows the application to be easily removed */
-                       gtk_button_set_label (GTK_BUTTON (priv->button), _("Uninstall"));
+                       gs_progress_button_set_label (GS_PROGRESS_BUTTON (priv->button), _("Uninstall"));
                }
                break;
        case GS_APP_STATE_UPDATABLE:
@@ -163,19 +163,19 @@ gs_app_row_refresh_button (GsAppRow *app_row, gboolean missing_search_result)
                        gtk_widget_set_visible (priv->button, TRUE);
                /* TRANSLATORS: this is a button next to the search results that
                 * allows the application to be easily removed */
-               gtk_button_set_label (GTK_BUTTON (priv->button), _("Uninstall"));
+               gs_progress_button_set_label (GS_PROGRESS_BUTTON (priv->button), _("Uninstall"));
                break;
        case GS_APP_STATE_INSTALLING:
                gtk_widget_set_visible (priv->button, TRUE);
                /* TRANSLATORS: this is a button next to the search results that
                 * shows the status of an application being installed */
-               gtk_button_set_label (GTK_BUTTON (priv->button), _("Installing"));
+               gs_progress_button_set_label (GS_PROGRESS_BUTTON (priv->button), _("Installing"));
                break;
        case GS_APP_STATE_REMOVING:
                gtk_widget_set_visible (priv->button, TRUE);
                /* TRANSLATORS: this is a button next to the search results that
                 * shows the status of an application being erased */
-               gtk_button_set_label (GTK_BUTTON (priv->button), _("Uninstalling"));
+               gs_progress_button_set_label (GS_PROGRESS_BUTTON (priv->button), _("Uninstalling"));
                break;
        default:
                break;
diff --git a/src/gs-progress-button.c b/src/gs-progress-button.c
index 878292240..3921e8979 100644
--- a/src/gs-progress-button.c
+++ b/src/gs-progress-button.c
@@ -16,11 +16,27 @@ struct _GsProgressButton
 {
        GtkButton        parent_instance;
 
+       GtkWidget       *image;
+       GtkWidget       *label;
+       GtkWidget       *stack;
+
        GtkCssProvider  *css_provider;
+       char            *label_text;
+       char            *icon_name;
+       gboolean         show_icon;
 };
 
 G_DEFINE_TYPE (GsProgressButton, gs_progress_button, GTK_TYPE_BUTTON)
 
+typedef enum {
+       PROP_ICON_NAME = 1,
+       PROP_SHOW_ICON,
+       /* Overrides: */
+       PROP_LABEL,
+} GsProgressButtonProperty;
+
+static GParamSpec *obj_props[PROP_SHOW_ICON + 1] = { NULL, };
+
 void
 gs_progress_button_set_progress (GsProgressButton *button, guint percentage)
 {
@@ -53,6 +69,192 @@ gs_progress_button_set_show_progress (GsProgressButton *button, gboolean show_pr
                gtk_style_context_remove_class (context, "install-progress");
 }
 
+/**
+ * gs_progress_button_get_label:
+ * @button: a #GsProgressButton
+ *
+ * Get the label of @button.
+ *
+ * It should be used rather than gtk_button_get_label() as it can only retrieve
+ * the text from the label set by gtk_button_set_label(), which also cannot be
+ * used.
+ *
+ * Returns: the label of @button
+ *
+ * Since: 41
+ */
+const gchar *
+gs_progress_button_get_label (GsProgressButton *button)
+{
+       g_return_val_if_fail (GS_IS_PROGRESS_BUTTON (button), NULL);
+
+       return button->label_text;
+}
+
+/**
+ * gs_progress_button_set_label:
+ * @button: a #GsProgressButton
+ * @label: a string
+ *
+ * Set the label of @button.
+ *
+ * It should be used rather than gtk_button_set_label() as it will replace the
+ * content of @button by a new label, breaking it.
+ *
+ * Since: 41
+ */
+void
+gs_progress_button_set_label (GsProgressButton *button, const gchar *label)
+{
+       g_return_if_fail (GS_IS_PROGRESS_BUTTON (button));
+
+       if (g_strcmp0 (button->label_text, label) == 0)
+               return;
+
+       g_free (button->label_text);
+       button->label_text = g_strdup (label);
+
+       g_object_notify (G_OBJECT (button), "label");
+}
+
+/**
+ * gs_progress_button_get_icon_name:
+ * @button: a #GsProgressButton
+ *
+ * Get the value of #GsProgressButton:icon-name.
+ *
+ * Returns: (nullable): the name of the icon
+ *
+ * Since: 41
+ */
+const gchar *
+gs_progress_button_get_icon_name (GsProgressButton *button)
+{
+       g_return_val_if_fail (GS_IS_PROGRESS_BUTTON (button), NULL);
+
+       return button->icon_name;
+}
+
+/**
+ * gs_progress_button_set_icon_name:
+ * @button: a #GsProgressButton
+ * @icon_name: (nullable): the name of the icon
+ *
+ * Set the value of #GsProgressButton:icon-name.
+ *
+ * Since: 41
+ */
+void
+gs_progress_button_set_icon_name (GsProgressButton *button, const gchar *icon_name)
+{
+       g_return_if_fail (GS_IS_PROGRESS_BUTTON (button));
+
+       if (g_strcmp0 (button->icon_name, icon_name) == 0)
+               return;
+
+       g_free (button->icon_name);
+       button->icon_name = g_strdup (icon_name);
+
+       g_object_notify_by_pspec (G_OBJECT (button), obj_props[PROP_ICON_NAME]);
+}
+
+/**
+ * gs_progress_button_get_show_icon:
+ * @button: a #GsProgressButton
+ *
+ * Get the value of #GsProgressButton:show-icon.
+ *
+ * Returns: %TRUE if the icon is shown, %FALSE if the label is shown
+ *
+ * Since: 41
+ */
+gboolean
+gs_progress_button_get_show_icon (GsProgressButton *button)
+{
+       g_return_val_if_fail (GS_IS_PROGRESS_BUTTON (button), FALSE);
+
+       return button->show_icon;
+}
+
+/**
+ * gs_progress_button_set_show_icon:
+ * @button: a #GsProgressButton
+ * @show_icon: %TRUE to set show the icon, %FALSE to show the label
+ *
+ * Set the value of #GsProgressButton:show-icon.
+ *
+ * Since: 41
+ */
+void
+gs_progress_button_set_show_icon (GsProgressButton *button, gboolean show_icon)
+{
+       GtkStyleContext *style;
+
+       g_return_if_fail (GS_IS_PROGRESS_BUTTON (button));
+
+       show_icon = !!show_icon;
+
+       if (button->show_icon == show_icon)
+               return;
+
+       button->show_icon = show_icon;
+
+       style = gtk_widget_get_style_context (GTK_WIDGET (button));
+       if (show_icon) {
+               gtk_stack_set_visible_child (GTK_STACK (button->stack), button->image);
+               gtk_style_context_remove_class (style, "text-button");
+               gtk_style_context_add_class (style, "image-button");
+       } else {
+               gtk_stack_set_visible_child (GTK_STACK (button->stack), button->label);
+               gtk_style_context_remove_class (style, "image-button");
+               gtk_style_context_add_class (style, "text-button");
+       }
+
+       g_object_notify_by_pspec (G_OBJECT (button), obj_props[PROP_SHOW_ICON]);
+}
+
+static void
+gs_progress_button_page_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+       GsProgressButton *self = GS_PROGRESS_BUTTON (object);
+
+       switch ((GsProgressButtonProperty) prop_id) {
+       case PROP_LABEL:
+               g_value_set_string (value, gs_progress_button_get_label (self));
+               break;
+       case PROP_ICON_NAME:
+               g_value_set_string (value, gs_progress_button_get_icon_name (self));
+               break;
+       case PROP_SHOW_ICON:
+               g_value_set_boolean (value, gs_progress_button_get_show_icon (self));
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+gs_progress_button_page_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+       GsProgressButton *self = GS_PROGRESS_BUTTON (object);
+
+       switch ((GsProgressButtonProperty) prop_id) {
+       case PROP_LABEL:
+               gs_progress_button_set_label (self, g_value_get_string (value));
+               break;
+       case PROP_ICON_NAME:
+               gs_progress_button_set_icon_name (self, g_value_get_string (value));
+               break;
+       case PROP_SHOW_ICON:
+               gs_progress_button_set_show_icon (self, g_value_get_boolean (value));
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
 static void
 gs_progress_button_dispose (GObject *object)
 {
@@ -63,9 +265,22 @@ gs_progress_button_dispose (GObject *object)
        G_OBJECT_CLASS (gs_progress_button_parent_class)->dispose (object);
 }
 
+static void
+gs_progress_button_finalize (GObject *object)
+{
+       GsProgressButton *button = GS_PROGRESS_BUTTON (object);
+
+       g_clear_pointer (&button->label_text, g_free);
+       g_clear_pointer (&button->icon_name, g_free);
+
+       G_OBJECT_CLASS (gs_progress_button_parent_class)->finalize (object);
+}
+
 static void
 gs_progress_button_init (GsProgressButton *button)
 {
+       gtk_widget_init_template (GTK_WIDGET (button));
+
        button->css_provider = gtk_css_provider_new ();
        gtk_style_context_add_provider (gtk_widget_get_style_context (GTK_WIDGET (button)),
                                        GTK_STYLE_PROVIDER (button->css_provider),
@@ -76,8 +291,46 @@ static void
 gs_progress_button_class_init (GsProgressButtonClass *klass)
 {
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
+       object_class->get_property = gs_progress_button_page_get_property;
+       object_class->set_property = gs_progress_button_page_set_property;
        object_class->dispose = gs_progress_button_dispose;
+       object_class->finalize = gs_progress_button_finalize;
+
+       /**
+        * GsApp:icon-name: (nullable):
+        *
+        * The name of the icon for the button.
+        *
+        * Since: 41
+        */
+       obj_props[PROP_ICON_NAME] =
+               g_param_spec_string ("icon-name", NULL, NULL,
+                                    NULL,
+                                    G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+       /**
+        * GsApp:show-icon:
+        *
+        * Whether to show the icon in place of the label.
+        *
+        * Since: 41
+        */
+       obj_props[PROP_SHOW_ICON] =
+               g_param_spec_boolean ("show-icon", NULL, NULL,
+                                     FALSE,
+                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+       g_object_class_install_properties (object_class, G_N_ELEMENTS (obj_props), obj_props);
+
+       g_object_class_override_property (object_class, PROP_LABEL, "label");
+
+       gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/Software/gs-progress-button.ui");
+
+       gtk_widget_class_bind_template_child (widget_class, GsProgressButton, image);
+       gtk_widget_class_bind_template_child (widget_class, GsProgressButton, label);
+       gtk_widget_class_bind_template_child (widget_class, GsProgressButton, stack);
 }
 
 GtkWidget *
diff --git a/src/gs-progress-button.h b/src/gs-progress-button.h
index e45f0d401..42b2a118e 100644
--- a/src/gs-progress-button.h
+++ b/src/gs-progress-button.h
@@ -18,6 +18,15 @@ G_BEGIN_DECLS
 G_DECLARE_FINAL_TYPE (GsProgressButton, gs_progress_button, GS, PROGRESS_BUTTON, GtkButton)
 
 GtkWidget      *gs_progress_button_new                 (void);
+const gchar    *gs_progress_button_get_label           (GsProgressButton       *button);
+void            gs_progress_button_set_label           (GsProgressButton       *button,
+                                                        const gchar            *label);
+const gchar    *gs_progress_button_get_icon_name       (GsProgressButton       *button);
+void            gs_progress_button_set_icon_name       (GsProgressButton       *button,
+                                                        const gchar            *icon_name);
+gboolean        gs_progress_button_get_show_icon       (GsProgressButton       *button);
+void            gs_progress_button_set_show_icon       (GsProgressButton       *button,
+                                                        gboolean                show_icon);
 void            gs_progress_button_set_progress        (GsProgressButton       *button,
                                                         guint                   percentage);
 void            gs_progress_button_set_show_progress   (GsProgressButton       *button,
diff --git a/src/gs-progress-button.ui b/src/gs-progress-button.ui
new file mode 100644
index 000000000..33e40bfae
--- /dev/null
+++ b/src/gs-progress-button.ui
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GsProgressButton" parent="GtkButton">
+    <child>
+      <object class="GtkStack" id="stack">
+        <property name="homogeneous">False</property>
+        <property name="interpolate-size">True</property>
+        <property name="transition-type">crossfade</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="GtkLabel" id="label">
+            <property name="label" bind-source="GsProgressButton" bind-property="label" 
bind-flags="sync-create"/>
+            <property name="visible">True</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkImage" id="image">
+            <property name="icon-name" bind-source="GsProgressButton" bind-property="icon-name" 
bind-flags="sync-create"/>
+            <property name="visible">True</property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>


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