[gnome-software] Restyle the updates panel to have a separate section for each update type
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software] Restyle the updates panel to have a separate section for each update type
- Date: Fri, 19 May 2017 16:10:55 +0000 (UTC)
commit dfb0866b62339069e1346229bb28eea90b218afc
Author: Richard Hughes <richard hughsie com>
Date: Fri May 12 19:02:12 2017 +0100
Restyle the updates panel to have a separate section for each update type
This is internally implemented having up to 4 GtkListBox objects, each with a
header which is only shown if there are more than one update types.
This moves a lot of functionality out of the GsUpdateList object, and strips it
back to a simple update list without any of the now-unused update-type logic.
This makes the code now match the latest mockups.
src/gs-update-list.c | 338 +---------------------
src/gs-update-list.h | 11 +-
src/gs-updates-page.c | 746 +++++++++++++++++++++++++++++++++++-------------
src/gs-updates-page.ui | 19 +--
src/gtk-style-hc.css | 5 +
src/gtk-style.css | 11 +-
6 files changed, 573 insertions(+), 557 deletions(-)
---
diff --git a/src/gs-update-list.c b/src/gs-update-list.c
index 0c67f43..0180ce8 100644
--- a/src/gs-update-list.c
+++ b/src/gs-update-list.c
@@ -1,6 +1,6 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
- * Copyright (C) 2013-2014 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2013-2017 Richard Hughes <richard hughsie com>
*
* Licensed under the GNU General Public License Version 2
*
@@ -28,65 +28,15 @@
#include "gs-app-row.h"
#include "gs-common.h"
-typedef enum {
- GS_UPDATE_LIST_SECTION_OFFLINE_FIRMWARE,
- GS_UPDATE_LIST_SECTION_OFFLINE,
- GS_UPDATE_LIST_SECTION_ONLINE,
- GS_UPDATE_LIST_SECTION_ONLINE_FIRMWARE,
- GS_UPDATE_LIST_SECTION_LAST
-} GsUpdateListSection;
-
typedef struct
{
GtkSizeGroup *sizegroup_image;
GtkSizeGroup *sizegroup_name;
- GtkSizeGroup *sizegroup_button;
- GtkSizeGroup *sizegroup_header;
- gboolean force_headers;
- GsUpdateListSection sections_cnt[GS_UPDATE_LIST_SECTION_LAST];
} GsUpdateListPrivate;
-enum {
- SIGNAL_BUTTON_CLICKED,
- SIGNAL_UPDATE_ALL,
- SIGNAL_LAST
-};
-
-static guint signals [SIGNAL_LAST] = { 0 };
-
G_DEFINE_TYPE_WITH_PRIVATE (GsUpdateList, gs_update_list, GTK_TYPE_LIST_BOX)
static void
-gs_update_list_button_clicked_cb (GsAppRow *app_row,
- GsUpdateList *update_list)
-{
- GsApp *app = gs_app_row_get_app (app_row);
- g_signal_emit (update_list, signals[SIGNAL_BUTTON_CLICKED], 0, app);
-}
-
-static GsUpdateListSection
-gs_update_list_get_app_section (GsApp *app)
-{
- if (gs_app_get_state (app) == AS_APP_STATE_UPDATABLE_LIVE) {
- if (gs_app_get_kind (app) == AS_APP_KIND_FIRMWARE)
- return GS_UPDATE_LIST_SECTION_ONLINE_FIRMWARE;
- return GS_UPDATE_LIST_SECTION_ONLINE;
- }
- if (gs_app_get_kind (app) == AS_APP_KIND_FIRMWARE)
- return GS_UPDATE_LIST_SECTION_OFFLINE_FIRMWARE;
- return GS_UPDATE_LIST_SECTION_OFFLINE;
-}
-
-void
-gs_update_list_remove_all (GsUpdateList *update_list)
-{
- GsUpdateListPrivate *priv = gs_update_list_get_instance_private (update_list);
- for (guint i = 0; i < GS_UPDATE_LIST_SECTION_LAST; i++)
- priv->sections_cnt[i] = 0;
- gs_container_remove_all (GTK_CONTAINER (update_list));
-}
-
-static void
gs_update_list_app_state_notify_cb (GsApp *app, GParamSpec *pspec, gpointer user_data)
{
if (gs_app_get_state (app) == AS_APP_STATE_INSTALLED) {
@@ -99,289 +49,40 @@ void
gs_update_list_add_app (GsUpdateList *update_list, GsApp *app)
{
GsUpdateListPrivate *priv = gs_update_list_get_instance_private (update_list);
- GsUpdateListSection section;
GtkWidget *app_row;
- /* keep track */
- section = gs_update_list_get_app_section (app);
- priv->sections_cnt[section]++;
-
app_row = gs_app_row_new (app);
- gs_app_row_set_show_update (GS_APP_ROW (app_row), TRUE);
- gs_app_row_set_show_buttons (GS_APP_ROW (app_row), TRUE);
- g_signal_connect (app_row, "button-clicked",
- G_CALLBACK (gs_update_list_button_clicked_cb),
- update_list);
+ gs_app_row_set_show_update (GS_APP_ROW (app_row), FALSE);
+ gs_app_row_set_show_buttons (GS_APP_ROW (app_row), FALSE);
gtk_container_add (GTK_CONTAINER (update_list), app_row);
gs_app_row_set_size_groups (GS_APP_ROW (app_row),
priv->sizegroup_image,
priv->sizegroup_name,
- priv->sizegroup_button);
+ NULL);
g_signal_connect (app, "notify::state",
G_CALLBACK (gs_update_list_app_state_notify_cb),
app_row);
gtk_widget_show (app_row);
}
-/* returns if the updates have section headers */
-gboolean
-gs_update_list_has_headers (GsUpdateList *update_list)
-{
- GsUpdateListPrivate *priv = gs_update_list_get_instance_private (update_list);
- guint cnt = 0;
-
- /* forced on by the distro upgrade for example */
- if (priv->force_headers)
- return TRUE;
-
- /* more than one type of thing */
- for (guint i = 0; i < GS_UPDATE_LIST_SECTION_LAST; i++) {
- if (priv->sections_cnt[i] > 0)
- cnt++;
- }
- return cnt > 1;
-}
-
-/* forces the update list into having section headers even if it does not need
- * them, for instance if we're showing a big system upgrade banner at the top */
-void
-gs_update_list_set_force_headers (GsUpdateList *update_list, gboolean force_headers)
-{
- GsUpdateListPrivate *priv = gs_update_list_get_instance_private (update_list);
- priv->force_headers = force_headers;
-}
-
-GsAppList *
-gs_update_list_get_apps (GsUpdateList *update_list)
-{
- GsAppList *apps;
- GList *l;
- g_autoptr(GList) children = NULL;
-
- apps = gs_app_list_new ();
- children = gtk_container_get_children (GTK_CONTAINER (update_list));
- for (l = children; l != NULL; l = l->next) {
- GsAppRow *app_row = GS_APP_ROW (l->data);
- gs_app_list_add (apps, gs_app_row_get_app (app_row));
- }
- return apps;
-}
-
-static GsAppList *
-gs_update_list_get_apps_for_section (GsUpdateList *update_list,
- GsUpdateListSection section)
-{
- GsAppList *apps;
- GList *l;
- g_autoptr(GList) children = NULL;
-
- apps = gs_app_list_new ();
- children = gtk_container_get_children (GTK_CONTAINER (update_list));
- for (l = children; l != NULL; l = l->next) {
- GsAppRow *app_row = GS_APP_ROW (l->data);
- GsApp *app = gs_app_row_get_app (app_row);
- if (gs_update_list_get_app_section (app) != section)
- continue;
- gs_app_list_add (apps, gs_app_row_get_app (app_row));
- }
- return apps;
-}
-
-static void
-gs_update_list_update_offline_firmware_cb (GtkButton *button,
- GsUpdateList *update_list)
-{
- g_autoptr(GsAppList) apps = gs_update_list_get_apps_for_section (update_list,
-
GS_UPDATE_LIST_SECTION_OFFLINE_FIRMWARE);
- g_signal_emit (update_list, signals[SIGNAL_UPDATE_ALL], 0, apps);
-}
-
-static void
-gs_update_list_update_offline_cb (GtkButton *button, GsUpdateList *update_list)
-{
- g_autoptr(GsAppList) apps = gs_update_list_get_apps_for_section (update_list,
- GS_UPDATE_LIST_SECTION_OFFLINE);
- g_signal_emit (update_list, signals[SIGNAL_UPDATE_ALL], 0, apps);
-}
-
-static void
-gs_update_list_update_online_cb (GtkButton *button, GsUpdateList *update_list)
-{
- g_autoptr(GsAppList) apps = gs_update_list_get_apps_for_section (update_list,
- GS_UPDATE_LIST_SECTION_ONLINE);
- gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
- g_signal_emit (update_list, signals[SIGNAL_UPDATE_ALL], 0, apps);
-}
-
-static GtkWidget *
-gs_update_list_get_section_header (GsUpdateList *update_list,
- GsUpdateListSection section)
-{
- GsUpdateListPrivate *priv = gs_update_list_get_instance_private (update_list);
- GtkStyleContext *context;
- GtkWidget *header;
- GtkWidget *label;
- GtkWidget *button = NULL;
-
- /* get labels and buttons for everything */
- if (section == GS_UPDATE_LIST_SECTION_OFFLINE_FIRMWARE) {
- /* TRANSLATORS: This is the header for system firmware that
- * requires a reboot to apply */
- label = gtk_label_new (_("Integrated Firmware"));
- /* TRANSLATORS: This is the button for upgrading all
- * system firmware */
- button = gtk_button_new_with_label (_("Restart & Update"));
- g_signal_connect (button, "clicked",
- G_CALLBACK (gs_update_list_update_offline_firmware_cb),
- update_list);
- } else if (section == GS_UPDATE_LIST_SECTION_OFFLINE) {
- /* TRANSLATORS: This is the header for offline OS and offline
- * app updates that require a reboot to apply */
- label = gtk_label_new (_("Requires Restart"));
- /* TRANSLATORS: This is the button for upgrading all
- * offline updates */
- button = gtk_button_new_with_label (_("Restart & Update"));
- g_signal_connect (button, "clicked",
- G_CALLBACK (gs_update_list_update_offline_cb),
- update_list);
- } else if (section == GS_UPDATE_LIST_SECTION_ONLINE) {
- /* TRANSLATORS: This is the header for online runtime and
- * app updates, typically flatpaks or snaps */
- label = gtk_label_new (_("Application Updates"));
- /* TRANSLATORS: This is the button for upgrading all
- * online-updatable applications */
- button = gtk_button_new_with_label (_("Update All"));
- g_signal_connect (button, "clicked",
- G_CALLBACK (gs_update_list_update_online_cb),
- update_list);
- } else if (section == GS_UPDATE_LIST_SECTION_ONLINE_FIRMWARE) {
- /* TRANSLATORS: This is the header for device firmware that can
- * be installed online */
- label = gtk_label_new (_("Device Firmware"));
- } else {
- g_assert_not_reached ();
- }
-
- /* create header */
- header = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
- gtk_size_group_add_widget (priv->sizegroup_header, header);
- context = gtk_widget_get_style_context (header);
- gtk_style_context_add_class (context, "app-listbox-header");
-
- /* put label into the header */
- gtk_box_pack_start (GTK_BOX (header), label, TRUE, TRUE, 0);
- gtk_widget_set_visible (label, TRUE);
- gtk_widget_set_margin_start (label, 6);
- gtk_label_set_xalign (GTK_LABEL (label), 0.0);
- context = gtk_widget_get_style_context (label);
- gtk_style_context_add_class (context, "app-listbox-header-title");
-
- /* add button if one is specified */
- if (button != NULL) {
- gtk_box_pack_end (GTK_BOX (header), button, FALSE, FALSE, 0);
- gtk_widget_set_visible (button, TRUE);
- gtk_widget_set_margin_end (button, 6);
- gtk_size_group_add_widget (priv->sizegroup_button, button);
- }
-
- /* success */
- return header;
-}
-
static void
list_header_func (GtkListBoxRow *row,
GtkListBoxRow *before,
gpointer user_data)
{
- GsApp *app = gs_app_row_get_app (GS_APP_ROW (row));
- GsUpdateListSection before_section = GS_UPDATE_LIST_SECTION_LAST;
- GsUpdateListSection section;
- GsUpdateList *update_list = GS_UPDATE_LIST (user_data);
GtkWidget *header;
-
- /* first entry */
- gtk_list_box_row_set_header (row, NULL);
- if (before != NULL) {
- GsApp *before_app = gs_app_row_get_app (GS_APP_ROW (before));
- before_section = gs_update_list_get_app_section (before_app);
- }
-
- /* section changed or forced to have headers */
- section = gs_update_list_get_app_section (app);
- if (gs_update_list_has_headers (update_list) &&
- before_section != section) {
- header = gs_update_list_get_section_header (update_list, section);
- } else {
- header = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
- }
+ header = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
gtk_list_box_row_set_header (row, header);
}
-static gchar *
-get_app_sort_key (GsApp *app)
-{
- GString *key;
-
- key = g_string_sized_new (64);
-
- /* Sections:
- * 1. offline integrated firmware
- * 2. offline os updates (OS-update, apps, runtimes, addons, other)
- * 3. online apps (apps, runtimes, addons, other)
- * 4. online device firmware */
- g_string_append_printf (key, "%u:", gs_update_list_get_app_section (app));
-
- /* sort apps by kind */
- switch (gs_app_get_kind (app)) {
- case AS_APP_KIND_OS_UPDATE:
- g_string_append (key, "1:");
- break;
- case AS_APP_KIND_DESKTOP:
- g_string_append (key, "2:");
- break;
- case AS_APP_KIND_WEB_APP:
- g_string_append (key, "3:");
- break;
- case AS_APP_KIND_RUNTIME:
- g_string_append (key, "4:");
- break;
- case AS_APP_KIND_ADDON:
- g_string_append (key, "5:");
- break;
- case AS_APP_KIND_CODEC:
- g_string_append (key, "6:");
- break;
- case AS_APP_KIND_FONT:
- g_string_append (key, "6:");
- break;
- case AS_APP_KIND_INPUT_METHOD:
- g_string_append (key, "7:");
- break;
- case AS_APP_KIND_SHELL_EXTENSION:
- g_string_append (key, "8:");
- break;
- default:
- g_string_append (key, "9:");
- break;
- }
-
- /* finally, sort by short name */
- g_string_append (key, gs_app_get_name (app));
- return g_string_free (key, FALSE);
-}
-
static gint
list_sort_func (GtkListBoxRow *a,
GtkListBoxRow *b,
gpointer user_data)
{
GsApp *a1 = gs_app_row_get_app (GS_APP_ROW (a));
- GsApp *a2 = gs_app_row_get_app (GS_APP_ROW (b));
- g_autofree gchar *key1 = get_app_sort_key (a1);
- g_autofree gchar *key2 = get_app_sort_key (a2);
-
- /* compare the keys according to the algorithm above */
- return g_strcmp0 (key1, key2);
+ GsApp *b1 = gs_app_row_get_app (GS_APP_ROW (b));
+ return g_strcmp0 (gs_app_get_name (a1), gs_app_get_name (b1));
}
static void
@@ -392,8 +93,6 @@ gs_update_list_dispose (GObject *object)
g_clear_object (&priv->sizegroup_image);
g_clear_object (&priv->sizegroup_name);
- g_clear_object (&priv->sizegroup_button);
- g_clear_object (&priv->sizegroup_header);
G_OBJECT_CLASS (gs_update_list_parent_class)->dispose (object);
}
@@ -404,8 +103,6 @@ gs_update_list_init (GsUpdateList *update_list)
GsUpdateListPrivate *priv = gs_update_list_get_instance_private (update_list);
priv->sizegroup_image = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
priv->sizegroup_name = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
- priv->sizegroup_button = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
- priv->sizegroup_header = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
gtk_list_box_set_header_func (GTK_LIST_BOX (update_list),
list_header_func,
@@ -413,31 +110,12 @@ gs_update_list_init (GsUpdateList *update_list)
gtk_list_box_set_sort_func (GTK_LIST_BOX (update_list),
list_sort_func,
update_list, NULL);
-
- /* set each section count to zero */
- for (guint i = 0; i < GS_UPDATE_LIST_SECTION_LAST; i++)
- priv->sections_cnt[i] = 0;
}
static void
gs_update_list_class_init (GsUpdateListClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- signals [SIGNAL_BUTTON_CLICKED] =
- g_signal_new ("button-clicked",
- G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GsUpdateListClass, button_clicked),
- NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
- G_TYPE_NONE, 1, GS_TYPE_APP);
-
- signals [SIGNAL_UPDATE_ALL] =
- g_signal_new ("update-all",
- G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GsUpdateListClass, update_all),
- NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
- G_TYPE_NONE, 1, GS_TYPE_APP_LIST);
-
object_class->dispose = gs_update_list_dispose;
}
@@ -445,9 +123,7 @@ GtkWidget *
gs_update_list_new (void)
{
GsUpdateList *update_list;
-
update_list = g_object_new (GS_TYPE_UPDATE_LIST, NULL);
-
return GTK_WIDGET (update_list);
}
diff --git a/src/gs-update-list.h b/src/gs-update-list.h
index 9da6a52..a9655be 100644
--- a/src/gs-update-list.h
+++ b/src/gs-update-list.h
@@ -1,6 +1,6 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
- * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2013-2017 Richard Hughes <richard hughsie com>
*
* Licensed under the GNU General Public License Version 2
*
@@ -35,20 +35,11 @@ G_DECLARE_DERIVABLE_TYPE (GsUpdateList, gs_update_list, GS, UPDATE_LIST, GtkList
struct _GsUpdateListClass
{
GtkListBoxClass parent_class;
- void (*button_clicked) (GsUpdateList *update_list,
- GsApp *app);
- void (*update_all) (GsUpdateList *update_list,
- GsAppList *apps);
};
GtkWidget *gs_update_list_new (void);
void gs_update_list_add_app (GsUpdateList *update_list,
GsApp *app);
-void gs_update_list_remove_all (GsUpdateList *update_list);
-GsAppList *gs_update_list_get_apps (GsUpdateList *update_list);
-gboolean gs_update_list_has_headers (GsUpdateList *update_list);
-void gs_update_list_set_force_headers (GsUpdateList *update_list,
- gboolean force_headers);
G_END_DECLS
diff --git a/src/gs-updates-page.c b/src/gs-updates-page.c
index 699d970..10b64d1 100644
--- a/src/gs-updates-page.c
+++ b/src/gs-updates-page.c
@@ -31,7 +31,6 @@
#include "gs-plugin-private.h"
#include "gs-removal-dialog.h"
#include "gs-update-dialog.h"
-#include "gs-update-list.h"
#include "gs-update-monitor.h"
#include "gs-upgrade-banner.h"
#include "gs-application.h"
@@ -59,6 +58,14 @@ typedef enum {
GS_UPDATES_PAGE_STATE_LAST,
} GsUpdatesPageState;
+typedef enum {
+ GS_UPDATE_PAGE_SECTION_OFFLINE_FIRMWARE,
+ GS_UPDATE_PAGE_SECTION_OFFLINE,
+ GS_UPDATE_PAGE_SECTION_ONLINE,
+ GS_UPDATE_PAGE_SECTION_ONLINE_FIRMWARE,
+ GS_UPDATE_PAGE_SECTION_LAST
+} GsUpdatePageSection;
+
struct _GsUpdatesPage
{
GsPage parent_instance;
@@ -72,8 +79,6 @@ struct _GsUpdatesPage
GSettings *desktop_settings;
gboolean cache_valid;
guint action_cnt;
- gboolean all_updates_are_live;
- gboolean any_require_reboot;
GsShell *shell;
GsPluginStatus last_status;
GsUpdatesPageState state;
@@ -92,13 +97,18 @@ struct _GsUpdatesPage
GtkWidget *label_updates_failed;
GtkWidget *label_updates_last_checked;
GtkWidget *label_updates_spinner;
- GtkWidget *list_box_updates;
GtkWidget *scrolledwindow_updates;
GtkWidget *spinner_updates;
GtkWidget *stack_updates;
GtkWidget *upgrade_banner;
GtkWidget *box_end_of_life;
GtkWidget *label_end_of_life;
+
+ GtkSizeGroup *sizegroup_image;
+ GtkSizeGroup *sizegroup_name;
+ GtkSizeGroup *sizegroup_button;
+ GtkSizeGroup *sizegroup_header;
+ GtkListBox *listboxes[GS_UPDATE_PAGE_SECTION_LAST];
};
enum {
@@ -146,6 +156,73 @@ gs_updates_page_invalidate (GsUpdatesPage *self)
self->cache_valid = FALSE;
}
+static GsUpdatePageSection
+_get_app_section (GsApp *app)
+{
+ if (gs_app_get_state (app) == AS_APP_STATE_UPDATABLE_LIVE) {
+ if (gs_app_get_kind (app) == AS_APP_KIND_FIRMWARE)
+ return GS_UPDATE_PAGE_SECTION_ONLINE_FIRMWARE;
+ return GS_UPDATE_PAGE_SECTION_ONLINE;
+ }
+ if (gs_app_get_kind (app) == AS_APP_KIND_FIRMWARE)
+ return GS_UPDATE_PAGE_SECTION_OFFLINE_FIRMWARE;
+ return GS_UPDATE_PAGE_SECTION_OFFLINE;
+}
+
+static GsAppList *
+_get_apps_for_section (GsUpdatesPage *self, GsUpdatePageSection section)
+{
+ GList *l;
+ GsAppList *apps;
+ GtkContainer *container;
+ g_autoptr(GList) children = NULL;
+
+ apps = gs_app_list_new ();
+ if (self->listboxes[section] == NULL)
+ return apps;
+ container = GTK_CONTAINER (self->listboxes[section]);
+ children = gtk_container_get_children (container);
+ for (l = children; l != NULL; l = l->next) {
+ GsAppRow *app_row = GS_APP_ROW (l->data);
+ GsApp *app = gs_app_row_get_app (app_row);
+ if (_get_app_section (app) != section)
+ continue;
+ gs_app_list_add (apps, gs_app_row_get_app (app_row));
+ }
+ return apps;
+}
+
+static GsAppList *
+_get_all_apps (GsUpdatesPage *self)
+{
+ GsAppList *apps = gs_app_list_new ();
+ for (guint i = 0; i < GS_UPDATE_PAGE_SECTION_LAST; i++) {
+ g_autoptr(GsAppList) apps_tmp = NULL;
+ apps_tmp = _get_apps_for_section (self, i);
+ gs_app_list_add_list (apps, apps_tmp);
+ }
+ return apps;
+}
+
+static gboolean
+_get_has_headers (GsUpdatesPage *self)
+{
+ guint cnt = 0;
+
+ /* forced on */
+ if (self->result_flags & GS_UPDATES_PAGE_FLAG_HAS_UPGRADES)
+ return TRUE;
+
+ /* more than one type of thing */
+ for (guint i = 0; i < GS_UPDATE_PAGE_SECTION_LAST; i++) {
+ g_autoptr(GsAppList) apps_tmp = NULL;
+ apps_tmp = _get_apps_for_section (self, i);
+ if (gs_app_list_length (apps_tmp) > 0)
+ cnt++;
+ }
+ return cnt > 1;
+}
+
static GDateTime *
time_next_midnight (void)
{
@@ -248,7 +325,6 @@ gs_updates_page_get_state_string (GsPluginStatus status)
static void
gs_updates_page_update_ui_state (GsUpdatesPage *self)
{
- GsUpdateList *update_list;
gboolean allow_mobile_refresh = TRUE;
g_autofree gchar *checked_str = NULL;
g_autofree gchar *spinner_str = NULL;
@@ -326,11 +402,9 @@ gs_updates_page_update_ui_state (GsUpdatesPage *self)
gs_plugin_loader_get_network_available (self->plugin_loader));
/* headerbar update button */
- update_list = GS_UPDATE_LIST (self->list_box_updates);
- gs_update_list_set_force_headers (update_list,
- self->result_flags & GS_UPDATES_PAGE_FLAG_HAS_UPGRADES);
gtk_widget_set_visible (self->button_update_all,
- !gs_update_list_has_headers (update_list));
+ self->state == GS_UPDATES_PAGE_STATE_IDLE &&
+ !_get_has_headers (self));
/* stack */
switch (self->state) {
@@ -443,11 +517,428 @@ gs_updates_page_network_available_notify_cb (GsPluginLoader *plugin_loader,
}
static void
+_app_state_notify_cb (GsApp *app, GParamSpec *pspec, gpointer user_data)
+{
+ if (gs_app_get_state (app) == AS_APP_STATE_INSTALLED) {
+ GsAppRow *app_row = GS_APP_ROW (user_data);
+ gs_app_row_unreveal (app_row);
+ }
+}
+
+static gchar *
+_get_app_sort_key (GsApp *app)
+{
+ GString *key;
+
+ key = g_string_sized_new (64);
+
+ /* Sections:
+ * 1. offline integrated firmware
+ * 2. offline os updates (OS-update, apps, runtimes, addons, other)
+ * 3. online apps (apps, runtimes, addons, other)
+ * 4. online device firmware */
+ g_string_append_printf (key, "%u:", _get_app_section (app));
+
+ /* sort apps by kind */
+ switch (gs_app_get_kind (app)) {
+ case AS_APP_KIND_OS_UPDATE:
+ g_string_append (key, "1:");
+ break;
+ case AS_APP_KIND_DESKTOP:
+ g_string_append (key, "2:");
+ break;
+ case AS_APP_KIND_WEB_APP:
+ g_string_append (key, "3:");
+ break;
+ case AS_APP_KIND_RUNTIME:
+ g_string_append (key, "4:");
+ break;
+ case AS_APP_KIND_ADDON:
+ g_string_append (key, "5:");
+ break;
+ case AS_APP_KIND_CODEC:
+ g_string_append (key, "6:");
+ break;
+ case AS_APP_KIND_FONT:
+ g_string_append (key, "6:");
+ break;
+ case AS_APP_KIND_INPUT_METHOD:
+ g_string_append (key, "7:");
+ break;
+ case AS_APP_KIND_SHELL_EXTENSION:
+ g_string_append (key, "8:");
+ break;
+ default:
+ g_string_append (key, "9:");
+ break;
+ }
+
+ /* finally, sort by short name */
+ g_string_append (key, gs_app_get_name (app));
+ return g_string_free (key, FALSE);
+}
+
+static gint
+_list_sort_func (GtkListBoxRow *a, GtkListBoxRow *b, gpointer user_data)
+{
+ GsApp *a1 = gs_app_row_get_app (GS_APP_ROW (a));
+ GsApp *a2 = gs_app_row_get_app (GS_APP_ROW (b));
+ g_autofree gchar *key1 = _get_app_sort_key (a1);
+ g_autofree gchar *key2 = _get_app_sort_key (a2);
+
+ /* compare the keys according to the algorithm above */
+ return g_strcmp0 (key1, key2);
+}
+
+static void
+_cancel_trigger_failed_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ GsUpdatesPage *self = GS_UPDATES_PAGE (user_data);
+ g_autoptr(GError) error = NULL;
+ if (!gs_plugin_loader_job_action_finish (self->plugin_loader, res, &error)) {
+ g_warning ("failed to cancel trigger: %s", error->message);
+ return;
+ }
+}
+
+static void
+_reboot_failed_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ GsUpdatesPage *self = GS_UPDATES_PAGE (user_data);
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsAppList) apps = NULL;
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+ g_autoptr(GVariant) retval = NULL;
+
+ /* get result */
+ retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);
+ if (retval != NULL)
+ return;
+
+ if (error != NULL) {
+ g_warning ("Calling org.gnome.SessionManager.Reboot failed: %s",
+ error->message);
+ }
+
+ /* cancel trigger */
+ apps = _get_all_apps (self);
+ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_UPDATE_CANCEL,
+ "app", gs_app_list_index (apps, 0),
+ "failure-flags", GS_PLUGIN_FAILURE_FLAGS_USE_EVENTS,
+ NULL);
+ gs_plugin_loader_job_process_async (self->plugin_loader, plugin_job,
+ self->cancellable,
+ _cancel_trigger_failed_cb,
+ self);
+}
+
+typedef struct {
+ GsUpdatesPage *self;
+ GsAppList *apps;
+} GsUpdatesPageUpdateHelper;
+
+static void
+_update_helper_free (GsUpdatesPageUpdateHelper *helper)
+{
+ g_object_unref (helper->self);
+ g_object_unref (helper->apps);
+ g_free (helper);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GsUpdatesPageUpdateHelper, _update_helper_free);
+
+static void
+_perform_update_cb (GsPluginLoader *plugin_loader, GAsyncResult *res, gpointer user_data)
+{
+ gboolean do_reboot = FALSE;
+ gboolean do_reboot_notification = FALSE;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsUpdatesPageUpdateHelper) helper = (GsUpdatesPageUpdateHelper *) user_data;
+
+ /* unconditionally re-enable this */
+ gtk_widget_set_sensitive (GTK_WIDGET (helper->self->button_update_all), TRUE);
+
+ /* get the results */
+ if (!gs_plugin_loader_job_action_finish (plugin_loader, res, &error)) {
+ g_warning ("failed to perform update: %s", error->message);
+ return;
+ }
+
+ /* look at each app in turn */
+ for (guint i = 0; helper->apps != NULL && i < gs_app_list_length (helper->apps); i++) {
+ GsApp *app = gs_app_list_index (helper->apps, i);
+ if (gs_app_get_state (app) != AS_APP_STATE_UPDATABLE_LIVE)
+ do_reboot = TRUE;
+ if (gs_app_has_quirk (app, AS_APP_QUIRK_NEEDS_REBOOT))
+ do_reboot_notification = TRUE;
+ }
+
+ /* trigger reboot if any application was not updatable live */
+ if (do_reboot) {
+ g_autoptr(GDBusConnection) bus = NULL;
+ bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_dbus_connection_call (bus,
+ "org.gnome.SessionManager",
+ "/org/gnome/SessionManager",
+ "org.gnome.SessionManager",
+ "Reboot",
+ NULL, NULL, G_DBUS_CALL_FLAGS_NONE,
+ G_MAXINT, NULL,
+ _reboot_failed_cb,
+ helper->self);
+
+ /* when we are not doing an offline update, show a notification
+ * if any application requires a reboot */
+ } else if (do_reboot_notification) {
+ g_autoptr(GNotification) n = NULL;
+ /* TRANSLATORS: we've just live-updated some apps */
+ n = g_notification_new (_("Updates have been installed"));
+ /* TRANSLATORS: the new apps will not be run until we restart */
+ g_notification_set_body (n, _("A restart is required for them to take effect."));
+ /* TRANSLATORS: button text */
+ g_notification_add_button (n, _("Not Now"), "app.nop");
+ /* TRANSLATORS: button text */
+ g_notification_add_button_with_target (n, _("Restart"), "app.reboot", NULL);
+ g_notification_set_default_action_and_target (n, "app.set-mode", "s", "updates");
+ g_application_send_notification (g_application_get_default (), "restart-required", n);
+ }
+}
+
+static void
+_update_all (GsUpdatesPage *self, GsAppList *apps)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GCancellable) cancellable = g_cancellable_new ();
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+ GsUpdatesPageUpdateHelper *helper = g_new0 (GsUpdatesPageUpdateHelper, 1);
+
+ helper->self = g_object_ref (self);
+ helper->apps = g_object_ref (apps);
+ g_set_object (&self->cancellable, cancellable);
+ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_UPDATE,
+ "list", apps,
+ "failure-flags", GS_PLUGIN_FAILURE_FLAGS_USE_EVENTS,
+ NULL);
+ gs_plugin_loader_job_process_async (self->plugin_loader, plugin_job,
+ self->cancellable,
+ (GAsyncReadyCallback) _perform_update_cb,
+ helper);
+}
+
+static void
+_button_update_offline_firmware_cb (GtkButton *button, GsUpdatesPage *self)
+{
+ g_autoptr(GsAppList) apps = _get_apps_for_section (self, GS_UPDATE_PAGE_SECTION_OFFLINE_FIRMWARE);
+ _update_all (self, apps);
+}
+
+static void
+_button_update_offline_cb (GtkButton *button, GsUpdatesPage *self)
+{
+ g_autoptr(GsAppList) apps = _get_apps_for_section (self, GS_UPDATE_PAGE_SECTION_OFFLINE);
+ _update_all (self, apps);
+}
+
+static void
+_button_update_online_cb (GtkButton *button, GsUpdatesPage *self)
+{
+ g_autoptr(GsAppList) apps = _get_apps_for_section (self, GS_UPDATE_PAGE_SECTION_ONLINE);
+ gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
+ _update_all (self, apps);
+}
+
+static GtkWidget *
+_get_section_header (GsUpdatesPage *self, GsUpdatePageSection section)
+{
+ GtkStyleContext *context;
+ GtkWidget *header;
+ GtkWidget *label;
+ GtkWidget *button = NULL;
+
+ /* get labels and buttons for everything */
+ if (section == GS_UPDATE_PAGE_SECTION_OFFLINE_FIRMWARE) {
+ /* TRANSLATORS: This is the header for system firmware that
+ * requires a reboot to apply */
+ label = gtk_label_new (_("Integrated Firmware"));
+ /* TRANSLATORS: This is the button for upgrading all
+ * system firmware */
+ button = gtk_button_new_with_label (_("Restart & Update"));
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (_button_update_offline_firmware_cb),
+ self);
+ } else if (section == GS_UPDATE_PAGE_SECTION_OFFLINE) {
+ /* TRANSLATORS: This is the header for offline OS and offline
+ * app updates that require a reboot to apply */
+ label = gtk_label_new (_("Requires Restart"));
+ /* TRANSLATORS: This is the button for upgrading all
+ * offline updates */
+ button = gtk_button_new_with_label (_("Restart & Update"));
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (_button_update_offline_cb),
+ self);
+ } else if (section == GS_UPDATE_PAGE_SECTION_ONLINE) {
+ /* TRANSLATORS: This is the header for online runtime and
+ * app updates, typically flatpaks or snaps */
+ label = gtk_label_new (_("Application Updates"));
+ /* TRANSLATORS: This is the button for upgrading all
+ * online-updatable applications */
+ button = gtk_button_new_with_label (_("Update All"));
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (_button_update_online_cb),
+ self);
+ } else if (section == GS_UPDATE_PAGE_SECTION_ONLINE_FIRMWARE) {
+ /* TRANSLATORS: This is the header for device firmware that can
+ * be installed online */
+ label = gtk_label_new (_("Device Firmware"));
+ } else {
+ g_assert_not_reached ();
+ }
+
+ /* create header */
+ header = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
+ gtk_size_group_add_widget (self->sizegroup_header, header);
+ context = gtk_widget_get_style_context (header);
+ gtk_style_context_add_class (context, "app-listbox-header");
+
+ /* put label into the header */
+ gtk_box_pack_start (GTK_BOX (header), label, TRUE, TRUE, 0);
+ gtk_widget_set_visible (label, TRUE);
+ gtk_widget_set_margin_start (label, 6);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ context = gtk_widget_get_style_context (label);
+ gtk_style_context_add_class (context, "app-listbox-header-title");
+
+ /* add button if one is specified */
+ if (button != NULL) {
+ gtk_box_pack_end (GTK_BOX (header), button, FALSE, FALSE, 0);
+ gtk_widget_set_visible (button, TRUE);
+ gtk_widget_set_margin_end (button, 6);
+ gtk_size_group_add_widget (self->sizegroup_button, button);
+ context = gtk_widget_get_style_context (button);
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_SUGGESTED_ACTION);
+ }
+
+ /* success */
+ return header;
+}
+
+static void
+_list_header_func (GtkListBoxRow *row, GtkListBoxRow *before, gpointer user_data)
+{
+ GsUpdatesPage *self = GS_UPDATES_PAGE (user_data);
+ GsApp *app = gs_app_row_get_app (GS_APP_ROW (row));
+ GtkWidget *header;
+
+ /* section changed or forced to have headers */
+ if (_get_has_headers (self) && before == NULL) {
+ GsUpdatePageSection section;
+ section = _get_app_section (app);
+ header = _get_section_header (self, section);
+ } else {
+ header = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+ }
+ gtk_list_box_row_set_header (row, header);
+}
+
+static void
+_app_row_activated_cb (GtkListBox *list_box, GtkListBoxRow *row, GsUpdatesPage *self)
+{
+ GsApp *app = gs_app_row_get_app (GS_APP_ROW (row));
+ GtkWidget *dialog;
+ g_autofree gchar *str = NULL;
+
+ /* debug */
+ str = gs_app_to_string (app);
+ g_debug ("%s", str);
+
+ dialog = gs_update_dialog_new (self->plugin_loader);
+ gs_update_dialog_show_update_details (GS_UPDATE_DIALOG (dialog), app);
+ gs_shell_modal_dialog_present (self->shell, GTK_DIALOG (dialog));
+
+ /* just destroy */
+ g_signal_connect_swapped (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy), dialog);
+}
+
+static void
+_create_listbox_section (GsUpdatesPage *self, GsUpdatePageSection sect)
+{
+ GtkStyleContext *context;
+
+ self->listboxes[sect] = GTK_LIST_BOX (gtk_list_box_new ());
+ gtk_list_box_set_selection_mode (self->listboxes[sect],
+ GTK_SELECTION_NONE);
+ gtk_list_box_set_sort_func (self->listboxes[sect],
+ _list_sort_func,
+ self, NULL);
+ gtk_list_box_set_header_func (self->listboxes[sect],
+ _list_header_func,
+ self, NULL);
+ g_signal_connect (self->listboxes[sect], "row-activated",
+ G_CALLBACK (_app_row_activated_cb), self);
+ gtk_widget_set_visible (GTK_WIDGET (self->listboxes[sect]), TRUE);
+ gtk_box_pack_start (GTK_BOX (self->updates_box),
+ GTK_WIDGET (self->listboxes[sect]),
+ TRUE, TRUE, 0);
+ gtk_widget_set_margin_top (GTK_WIDGET (self->listboxes[sect]), 24);
+
+ /* reorder the children */
+ for (guint i = 0; i < GS_UPDATE_PAGE_SECTION_LAST; i++) {
+ if (self->listboxes[i] == NULL)
+ continue;
+ gtk_box_reorder_child (GTK_BOX (self->updates_box),
+ GTK_WIDGET (self->listboxes[i]), i);
+ }
+
+ /* make rounded edges */
+ context = gtk_widget_get_style_context (GTK_WIDGET (self->listboxes[sect]));
+ gtk_style_context_add_class (context, "app-updates-section");
+}
+
+static void
+_app_row_button_clicked_cb (GsAppRow *app_row, GsUpdatesPage *self)
+{
+ g_autoptr(GCancellable) cancellable = g_cancellable_new ();
+ GsApp *app = gs_app_row_get_app (app_row);
+ if (gs_app_get_state (app) != AS_APP_STATE_UPDATABLE_LIVE)
+ return;
+ g_set_object (&self->cancellable, cancellable);
+ gs_page_update_app (GS_PAGE (self), app, self->cancellable);
+}
+
+static void
+_add_app_row (GsUpdatesPage *self, GsApp *app)
+{
+ GsUpdatePageSection section;
+ GtkWidget *app_row;
+
+ /* create if required */
+ section = _get_app_section (app);
+ if (self->listboxes[section] == NULL)
+ _create_listbox_section (self, section);
+
+ app_row = gs_app_row_new (app);
+ gs_app_row_set_show_update (GS_APP_ROW (app_row), TRUE);
+ gs_app_row_set_show_buttons (GS_APP_ROW (app_row), TRUE);
+ g_signal_connect (app_row, "button-clicked",
+ G_CALLBACK (_app_row_button_clicked_cb),
+ self);
+ gtk_container_add (GTK_CONTAINER (self->listboxes[section]), app_row);
+
+ gs_app_row_set_size_groups (GS_APP_ROW (app_row),
+ self->sizegroup_image,
+ self->sizegroup_name,
+ self->sizegroup_button);
+ g_signal_connect (app, "notify::state",
+ G_CALLBACK (_app_state_notify_cb),
+ app_row);
+}
+
+static void
gs_updates_page_get_updates_cb (GsPluginLoader *plugin_loader,
GAsyncResult *res,
GsUpdatesPage *self)
{
- guint i;
GtkWidget *widget;
g_autoptr(GError) error = NULL;
g_autoptr(GsAppList) list = NULL;
@@ -471,27 +962,28 @@ gs_updates_page_get_updates_cb (GsPluginLoader *plugin_loader,
}
/* add the results */
- self->all_updates_are_live = TRUE;
- self->any_require_reboot = FALSE;
- for (i = 0; list != NULL && i < gs_app_list_length (list); i++) {
+ for (guint i = 0; i < gs_app_list_length (list); i++) {
GsApp *app = gs_app_list_index (list, i);
- if (gs_app_get_state (app) != AS_APP_STATE_UPDATABLE_LIVE)
- self->all_updates_are_live = FALSE;
- if (gs_app_has_quirk (app, AS_APP_QUIRK_NEEDS_REBOOT))
- self->any_require_reboot = TRUE;
- gs_update_list_add_app (GS_UPDATE_LIST (self->list_box_updates), app);
+ _add_app_row (self, app);
+ }
+
+ /* invalidate the headers */
+ for (guint i = 0; i < GS_UPDATE_PAGE_SECTION_LAST; i++) {
+ if (self->listboxes[i] != NULL)
+ gtk_list_box_invalidate_headers (self->listboxes[i]);
}
/* change the button as to whether a reboot is required to
* apply all the updates */
- if (self->all_updates_are_live) {
- gtk_button_set_label (GTK_BUTTON (self->button_update_all),
- /* TRANSLATORS: all updates will be installed */
- _("U_pdate All"));
- } else {
+ if (self->listboxes[GS_UPDATE_PAGE_SECTION_OFFLINE] != NULL ||
+ self->listboxes[GS_UPDATE_PAGE_SECTION_OFFLINE_FIRMWARE] != NULL) {
gtk_button_set_label (GTK_BUTTON (self->button_update_all),
/* TRANSLATORS: this is an offline update */
_("_Restart & Update"));
+ } else {
+ gtk_button_set_label (GTK_BUTTON (self->button_update_all),
+ /* TRANSLATORS: all updates will be installed */
+ _("U_pdate All"));
}
/* update the counter */
@@ -619,7 +1111,12 @@ gs_updates_page_load (GsUpdatesPage *self)
if (self->action_cnt > 0)
return;
- gs_update_list_remove_all (GS_UPDATE_LIST (self->list_box_updates));
+
+ /* remove all existing apps */
+ for (guint i = 0; i < GS_UPDATE_PAGE_SECTION_LAST; i++)
+ self->listboxes[i] = NULL;
+ gs_container_remove_all (GTK_CONTAINER (self->updates_box));
+
refine_flags = GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON |
GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPDATE_DETAILS |
GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROVENANCE |
@@ -710,49 +1207,6 @@ gs_updates_page_switch_to (GsPage *page,
}
static void
-show_update_details (GsApp *app, GsUpdatesPage *self)
-{
- GtkWidget *dialog;
- g_autofree gchar *str = NULL;
-
- /* debug */
- str = gs_app_to_string (app);
- g_debug ("%s", str);
-
- dialog = gs_update_dialog_new (self->plugin_loader);
- gs_update_dialog_show_update_details (GS_UPDATE_DIALOG (dialog), app);
- gs_shell_modal_dialog_present (self->shell, GTK_DIALOG (dialog));
-
- /* just destroy */
- g_signal_connect_swapped (dialog, "response",
- G_CALLBACK (gtk_widget_destroy), dialog);
-}
-
-static void
-gs_updates_page_activated_cb (GtkListBox *list_box,
- GtkListBoxRow *row,
- GsUpdatesPage *self)
-{
- GsApp *app;
-
- app = gs_app_row_get_app (GS_APP_ROW (row));
-
- show_update_details (app, self);
-}
-
-static void
-gs_updates_page_button_clicked_cb (GsUpdateList *update_list,
- GsApp *app,
- GsUpdatesPage *self)
-{
- g_autoptr(GCancellable) cancellable = g_cancellable_new ();
- if (gs_app_get_state (app) != AS_APP_STATE_UPDATABLE_LIVE)
- return;
- g_set_object (&self->cancellable, cancellable);
- gs_page_update_app (GS_PAGE (self), app, self->cancellable);
-}
-
-static void
gs_updates_page_refresh_cb (GsPluginLoader *plugin_loader,
GAsyncResult *res,
GsUpdatesPage *self)
@@ -948,131 +1402,14 @@ gs_updates_page_pending_apps_changed_cb (GsPluginLoader *plugin_loader,
}
static void
-cancel_trigger_failed_cb (GObject *source, GAsyncResult *res, gpointer user_data)
-{
- GsUpdatesPage *self = GS_UPDATES_PAGE (user_data);
- g_autoptr(GError) error = NULL;
- if (!gs_plugin_loader_job_action_finish (self->plugin_loader, res, &error)) {
- g_warning ("failed to cancel trigger: %s", error->message);
- return;
- }
-}
-
-static void
-gs_updates_page_reboot_failed_cb (GObject *source, GAsyncResult *res, gpointer user_data)
-{
- GsUpdatesPage *self = GS_UPDATES_PAGE (user_data);
- g_autoptr(GError) error = NULL;
- g_autoptr(GsAppList) apps = NULL;
- g_autoptr(GsPluginJob) plugin_job = NULL;
- g_autoptr(GVariant) retval = NULL;
-
- /* get result */
- retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);
- if (retval != NULL)
- return;
-
- if (error != NULL) {
- g_warning ("Calling org.gnome.SessionManager.Reboot failed: %s",
- error->message);
- }
-
- /* cancel trigger */
- apps = gs_update_list_get_apps (GS_UPDATE_LIST (self->list_box_updates));
- plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_UPDATE_CANCEL,
- "app", gs_app_list_index (apps, 0),
- "failure-flags", GS_PLUGIN_FAILURE_FLAGS_USE_EVENTS,
- NULL);
- gs_plugin_loader_job_process_async (self->plugin_loader, plugin_job,
- self->cancellable,
- cancel_trigger_failed_cb,
- self);
-}
-
-static void
-gs_updates_page_perform_update_cb (GsPluginLoader *plugin_loader,
- GAsyncResult *res,
- GsUpdatesPage *self)
-{
- g_autoptr(GError) error = NULL;
-
- /* unconditionally re-enable this */
- gtk_widget_set_sensitive (GTK_WIDGET (self->button_update_all), TRUE);
-
- /* get the results */
- if (!gs_plugin_loader_job_action_finish (plugin_loader, res, &error)) {
- g_warning ("Failed to perform update: %s", error->message);
- return;
- }
-
- /* trigger reboot if any application was not updatable live */
- if (!self->all_updates_are_live) {
- g_autoptr(GDBusConnection) bus = NULL;
- bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
- g_dbus_connection_call (bus,
- "org.gnome.SessionManager",
- "/org/gnome/SessionManager",
- "org.gnome.SessionManager",
- "Reboot",
- NULL, NULL, G_DBUS_CALL_FLAGS_NONE,
- G_MAXINT, NULL,
- gs_updates_page_reboot_failed_cb,
- self);
-
- /* when we are not doing an offline update, show a notification
- * if any application requires a reboot */
- } else if (self->any_require_reboot) {
- g_autoptr(GNotification) n = NULL;
- /* TRANSLATORS: we've just live-updated some apps */
- n = g_notification_new (_("Updates have been installed"));
- /* TRANSLATORS: the new apps will not be run until we restart */
- g_notification_set_body (n, _("A restart is required for them to take effect."));
- /* TRANSLATORS: button text */
- g_notification_add_button (n, _("Not Now"), "app.nop");
- /* TRANSLATORS: button text */
- g_notification_add_button_with_target (n, _("Restart"), "app.reboot", NULL);
- g_notification_set_default_action_and_target (n, "app.set-mode", "s", "updates");
- g_application_send_notification (g_application_get_default (), "restart-required", n);
- }
-}
-
-static void
-update_all (GsUpdatesPage *self, GsAppList *apps)
-{
- g_autoptr(GError) error = NULL;
- g_autoptr(GCancellable) cancellable = g_cancellable_new ();
- g_autoptr(GsPluginJob) plugin_job = NULL;
-
- g_set_object (&self->cancellable, cancellable);
- plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_UPDATE,
- "list", apps,
- "failure-flags", GS_PLUGIN_FAILURE_FLAGS_USE_EVENTS,
- NULL);
- gs_plugin_loader_job_process_async (self->plugin_loader, plugin_job,
- self->cancellable,
- (GAsyncReadyCallback) gs_updates_page_perform_update_cb,
- self);
-}
-
-static void
gs_updates_page_header_update_all_cb (GtkButton *button,
GsUpdatesPage *self)
{
- g_autoptr(GsAppList) apps = NULL;
-
- apps = gs_update_list_get_apps (GS_UPDATE_LIST (self->list_box_updates));
- update_all (self, apps);
+ g_autoptr(GsAppList) apps = _get_all_apps (self);
+ _update_all (self, apps);
gtk_widget_set_sensitive (GTK_WIDGET (self->button_update_all), FALSE);
}
-static void
-gs_updates_page_update_all_cb (GsUpdateList *update_list,
- GsAppList *apps,
- GsUpdatesPage *self)
-{
- update_all (self, apps);
-}
-
typedef struct {
GsApp *app;
GsUpdatesPage *self;
@@ -1172,7 +1509,7 @@ upgrade_reboot_failed_cb (GObject *source,
NULL);
gs_plugin_loader_job_process_async (self->plugin_loader, plugin_job,
self->cancellable,
- cancel_trigger_failed_cb,
+ _cancel_trigger_failed_cb,
self);
}
@@ -1324,8 +1661,7 @@ gs_updates_page_invalidate_downloaded_upgrade (GsUpdatesPage *self)
static gboolean
gs_shell_update_are_updates_in_progress (GsUpdatesPage *self)
{
- GsUpdateList *update_list = GS_UPDATE_LIST (self->list_box_updates);
- g_autoptr(GsAppList) list = gs_update_list_get_apps (update_list);
+ g_autoptr(GsAppList) list = _get_all_apps (self);
for (guint i = 0; i < gs_app_list_length (list); i++) {
GsApp *app = gs_app_list_index (list, i);
switch (gs_app_get_state (app)) {
@@ -1436,14 +1772,6 @@ gs_updates_page_setup (GsPage *page,
self->builder = g_object_ref (builder);
self->cancellable = g_object_ref (cancellable);
- /* setup updates */
- g_signal_connect (self->list_box_updates, "row-activated",
- G_CALLBACK (gs_updates_page_activated_cb), self);
- g_signal_connect (self->list_box_updates, "button-clicked",
- G_CALLBACK (gs_updates_page_button_clicked_cb), self);
- g_signal_connect (self->list_box_updates, "update-all",
- G_CALLBACK (gs_updates_page_update_all_cb), self);
-
/* setup system upgrades */
g_signal_connect (self->upgrade_banner, "download-clicked",
G_CALLBACK (gs_updates_page_upgrade_download_cb), self);
@@ -1489,6 +1817,12 @@ gs_updates_page_setup (GsPage *page,
G_CALLBACK (gs_updates_page_button_network_settings_cb),
self);
+ /* visually aligned */
+ self->sizegroup_image = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ self->sizegroup_name = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ self->sizegroup_button = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ self->sizegroup_header = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
+
/* set initial state */
if (!gs_plugin_loader_get_allow_updates (self->plugin_loader))
self->state = GS_UPDATES_PAGE_STATE_MANAGED;
@@ -1509,12 +1843,24 @@ gs_updates_page_dispose (GObject *object)
g_clear_object (&self->cancellable_upgrade_download);
}
+ for (guint i = 0; i < GS_UPDATE_PAGE_SECTION_LAST; i++) {
+ if (self->listboxes[i] != NULL) {
+ gtk_widget_destroy (GTK_WIDGET (self->listboxes[i]));
+ self->listboxes[i] = NULL;
+ }
+ }
+
g_clear_object (&self->builder);
g_clear_object (&self->plugin_loader);
g_clear_object (&self->cancellable);
g_clear_object (&self->settings);
g_clear_object (&self->desktop_settings);
+ g_clear_object (&self->sizegroup_image);
+ g_clear_object (&self->sizegroup_name);
+ g_clear_object (&self->sizegroup_button);
+ g_clear_object (&self->sizegroup_header);
+
G_OBJECT_CLASS (gs_updates_page_parent_class)->dispose (object);
}
@@ -1538,7 +1884,6 @@ gs_updates_page_class_init (GsUpdatesPageClass *klass)
gtk_widget_class_bind_template_child (widget_class, GsUpdatesPage, label_updates_failed);
gtk_widget_class_bind_template_child (widget_class, GsUpdatesPage, label_updates_last_checked);
gtk_widget_class_bind_template_child (widget_class, GsUpdatesPage, label_updates_spinner);
- gtk_widget_class_bind_template_child (widget_class, GsUpdatesPage, list_box_updates);
gtk_widget_class_bind_template_child (widget_class, GsUpdatesPage, scrolledwindow_updates);
gtk_widget_class_bind_template_child (widget_class, GsUpdatesPage, spinner_updates);
gtk_widget_class_bind_template_child (widget_class, GsUpdatesPage, stack_updates);
@@ -1558,6 +1903,11 @@ gs_updates_page_init (GsUpdatesPage *self)
self->settings = g_settings_new ("org.gnome.software");
self->desktop_settings = g_settings_new ("org.gnome.desktop.interface");
+ self->sizegroup_image = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ self->sizegroup_name = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ self->sizegroup_button = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ self->sizegroup_header = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
+
ampm = nl_langinfo (AM_STR);
if (ampm != NULL && *ampm != '\0')
self->ampm_available = TRUE;
diff --git a/src/gs-updates-page.ui b/src/gs-updates-page.ui
index dc4a065..dc6023f 100644
--- a/src/gs-updates-page.ui
+++ b/src/gs-updates-page.ui
@@ -17,7 +17,6 @@
<property name="border_width">0</property>
<property name="orientation">horizontal</property>
<property name="spacing">18</property>
- <property name="margin_bottom">18</property>
<style>
<class name="eol-box"/>
</style>
@@ -134,33 +133,19 @@
<object class="GtkBox" id="list_box_updates_box">
<property name="visible">True</property>
<property name="orientation">vertical</property>
- <style>
- <class name="app-list"/>
- </style>
<child>
<object class="GsUpgradeBanner" id="upgrade_banner">
<property name="visible">False</property>
<property name="hexpand">True</property>
<property name="vexpand">False</property>
+ <property name="margin_top">18</property>
</object>
</child>
<child>
<object class="GtkBox" id="updates_box">
<property name="visible">True</property>
<property name="orientation">vertical</property>
- <child>
- <object class="GsUpdateList" id="list_box_updates">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="selection_mode">none</property>
- </object>
- </child>
- <child>
- <object class="GtkSeparator" id="list_box_updates_separator">
- <property name="visible">True</property>
- <property name="orientation">horizontal</property>
- </object>
- </child>
+ <property name="margin_bottom">18</property>
</object>
</child>
</object>
diff --git a/src/gtk-style-hc.css b/src/gtk-style-hc.css
index 31b4517..5dd475c 100644
--- a/src/gtk-style-hc.css
+++ b/src/gtk-style-hc.css
@@ -143,6 +143,11 @@
border-color: #000000;
}
+.app-updates-section {
+ border-radius: 4px;
+ border: 1px solid #000000;
+}
+
.app-listbox-header-title {
font-size: larger;
}
diff --git a/src/gtk-style.css b/src/gtk-style.css
index 50386f4..c259469 100644
--- a/src/gtk-style.css
+++ b/src/gtk-style.css
@@ -300,7 +300,12 @@
.app-listbox-header {
padding: 6px 6px 6px 10px;
background-image: none;
- background-color: shade(@theme_bg_color, 0.9);
+ border-bottom: 1px solid @theme_bg_color;
+}
+
+.app-updates-section {
+ border-radius: 4px;
+ border: 1px solid darker(@theme_bg_color);
}
.app-listbox-header-title {
@@ -398,11 +403,15 @@ flowboxchild {
.upgrade-banner {
background-color: #1c5288;
padding: 0px;
+ border-radius: 4px;
+ border: 1px solid darker(@theme_bg_color);
color: @theme_selected_fg_color;
}
.upgrade-buttons {
padding: 18px;
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
}
.upgrade-progressbar {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]