[gnome-software: 1/13] progress-button: Allow showing an icon
- From: Adrien Plazas <aplazas src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software: 1/13] progress-button: Allow showing an icon
- Date: Mon, 12 Jul 2021 09:59:17 +0000 (UTC)
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]