[gnome-software/1166-repository-dialog-design-updates] gs-repos-dialog: Implement design updates
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/1166-repository-dialog-design-updates] gs-repos-dialog: Implement design updates
- Date: Mon, 28 Jun 2021 10:55:34 +0000 (UTC)
commit 3f487bc14dd8d1d242129a9e2730ca35c7cb5d3b
Author: Milan Crha <mcrha redhat com>
Date: Mon Jun 28 12:54:17 2021 +0200
gs-repos-dialog: Implement design updates
Closes https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1166
plugins/flatpak/gs-flatpak-utils.c | 15 +-
plugins/flatpak/gs-flatpak-utils.h | 3 +-
plugins/flatpak/gs-flatpak.c | 17 +-
plugins/flatpak/gs-flatpak.h | 2 +-
plugins/flatpak/gs-plugin-flatpak.c | 2 +-
plugins/fwupd/gs-plugin-fwupd.c | 2 +
plugins/packagekit/gs-plugin-packagekit.c | 3 +
plugins/rpm-ostree/gs-plugin-rpm-ostree.c | 3 +
po/POTFILES.in | 1 +
src/gs-repo-row.c | 319 +++++++++++---------
src/gs-repo-row.h | 15 +-
src/gs-repo-row.ui | 172 +++++------
src/gs-repos-dialog.c | 471 +++++++++++-------------------
src/gs-repos-dialog.ui | 47 +--
src/gs-repos-section.c | 176 +++++++++++
src/gs-repos-section.h | 27 ++
src/gtk-style.css | 12 +
src/meson.build | 1 +
18 files changed, 684 insertions(+), 604 deletions(-)
---
diff --git a/plugins/flatpak/gs-flatpak-utils.c b/plugins/flatpak/gs-flatpak-utils.c
index af2af9141..d57982dc5 100644
--- a/plugins/flatpak/gs-flatpak-utils.c
+++ b/plugins/flatpak/gs-flatpak-utils.c
@@ -9,6 +9,8 @@
#include <config.h>
#include <ostree.h>
+#include <glib/gi18n.h>
+
#include "gs-flatpak-app.h"
#include "gs-flatpak.h"
#include "gs-flatpak-utils.h"
@@ -62,7 +64,8 @@ gs_flatpak_error_convert (GError **perror)
}
GsApp *
-gs_flatpak_app_new_from_remote (FlatpakRemote *xremote)
+gs_flatpak_app_new_from_remote (FlatpakRemote *xremote,
+ gboolean is_user)
{
g_autofree gchar *title = NULL;
g_autofree gchar *url = NULL;
@@ -77,12 +80,16 @@ gs_flatpak_app_new_from_remote (FlatpakRemote *xremote)
flatpak_remote_get_name (xremote));
gs_app_set_size_download (app, GS_APP_SIZE_UNKNOWABLE);
+ gs_app_set_metadata (app, "GnomeSoftware::InstallationKind",
+ is_user ? _("User Installation") : _("System Installation"));
+
/* title */
title = flatpak_remote_get_title (xremote);
- if (title != NULL) {
+ if (title != NULL)
gs_app_set_summary (app, GS_APP_QUALITY_LOWEST, title);
- gs_app_set_origin_ui (app, title);
- }
+
+ /* origin_ui on a remote is not the remote title */
+ gs_app_set_origin_ui (app, _("Applications"));
/* url */
url = flatpak_remote_get_url (xremote);
diff --git a/plugins/flatpak/gs-flatpak-utils.h b/plugins/flatpak/gs-flatpak-utils.h
index 61cd62d37..dcbf2840e 100644
--- a/plugins/flatpak/gs-flatpak-utils.h
+++ b/plugins/flatpak/gs-flatpak-utils.h
@@ -13,7 +13,8 @@ G_BEGIN_DECLS
#include <gnome-software.h>
void gs_flatpak_error_convert (GError **perror);
-GsApp *gs_flatpak_app_new_from_remote (FlatpakRemote *xremote);
+GsApp *gs_flatpak_app_new_from_remote (FlatpakRemote *xremote,
+ gboolean is_user);
GsApp *gs_flatpak_app_new_from_repo_file (GFile *file,
GCancellable *cancellable,
GError **error);
diff --git a/plugins/flatpak/gs-flatpak.c b/plugins/flatpak/gs-flatpak.c
index ce6a6615d..4dae82e60 100644
--- a/plugins/flatpak/gs-flatpak.c
+++ b/plugins/flatpak/gs-flatpak.c
@@ -377,7 +377,7 @@ gs_flatpak_create_source (GsFlatpak *self, FlatpakRemote *xremote)
g_autoptr(GsApp) app = NULL;
/* create a temp GsApp */
- app = gs_flatpak_app_new_from_remote (xremote);
+ app = gs_flatpak_app_new_from_remote (xremote, flatpak_installation_get_is_user (self->installation));
gs_flatpak_claim_app (self, app);
/* we already have one, returned the ref'd cached copy */
@@ -3162,10 +3162,10 @@ gs_flatpak_launch (GsFlatpak *self,
}
gboolean
-gs_flatpak_app_remove_source (GsFlatpak *self,
- GsApp *app,
- GCancellable *cancellable,
- GError **error)
+gs_flatpak_app_disable_source (GsFlatpak *self,
+ GsApp *app,
+ GCancellable *cancellable,
+ GError **error)
{
g_autoptr(FlatpakRemote) xremote = NULL;
@@ -3183,8 +3183,9 @@ gs_flatpak_app_remove_source (GsFlatpak *self,
/* remove */
gs_app_set_state (app, GS_APP_STATE_REMOVING);
- if (!flatpak_installation_remove_remote (self->installation,
- gs_app_get_id (app),
+ flatpak_remote_set_disabled (xremote, TRUE);
+ if (!flatpak_installation_modify_remote (self->installation,
+ xremote,
cancellable,
error)) {
gs_flatpak_error_convert (error);
@@ -3198,7 +3199,7 @@ gs_flatpak_app_remove_source (GsFlatpak *self,
xb_silo_invalidate (self->silo);
g_rw_lock_reader_unlock (&self->silo_lock);
- gs_app_set_state (app, GS_APP_STATE_UNAVAILABLE);
+ gs_app_set_state (app, GS_APP_STATE_AVAILABLE);
gs_plugin_repository_changed (self->plugin, app);
diff --git a/plugins/flatpak/gs-flatpak.h b/plugins/flatpak/gs-flatpak.h
index 5eb7bb452..40b93d0e1 100644
--- a/plugins/flatpak/gs-flatpak.h
+++ b/plugins/flatpak/gs-flatpak.h
@@ -76,7 +76,7 @@ gboolean gs_flatpak_launch (GsFlatpak *self,
GsApp *app,
GCancellable *cancellable,
GError **error);
-gboolean gs_flatpak_app_remove_source (GsFlatpak *self,
+gboolean gs_flatpak_app_disable_source (GsFlatpak *self,
GsApp *app,
GCancellable *cancellable,
GError **error);
diff --git a/plugins/flatpak/gs-plugin-flatpak.c b/plugins/flatpak/gs-plugin-flatpak.c
index 364bc27d1..694007359 100644
--- a/plugins/flatpak/gs-plugin-flatpak.c
+++ b/plugins/flatpak/gs-plugin-flatpak.c
@@ -867,7 +867,7 @@ gs_plugin_app_remove (GsPlugin *plugin,
/* is a source */
if (gs_app_get_kind (app) == AS_COMPONENT_KIND_REPOSITORY)
- return gs_flatpak_app_remove_source (flatpak, app, cancellable, error);
+ return gs_flatpak_app_disable_source (flatpak, app, cancellable, error);
/* build and run transaction */
transaction = _build_transaction (plugin, flatpak, cancellable, error);
diff --git a/plugins/fwupd/gs-plugin-fwupd.c b/plugins/fwupd/gs-plugin-fwupd.c
index 3ce397cac..1e90c592c 100644
--- a/plugins/fwupd/gs-plugin-fwupd.c
+++ b/plugins/fwupd/gs-plugin-fwupd.c
@@ -1190,6 +1190,8 @@ gs_plugin_add_sources (GsPlugin *plugin,
gs_app_set_metadata (app, "fwupd::remote-id",
fwupd_remote_get_id (remote));
gs_app_set_management_plugin (app, "fwupd");
+ gs_app_set_metadata (app, "GnomeSoftware::PackagingFormat", "fwupd");
+ gs_app_set_origin_ui (app, _("Firmware"));
gs_app_list_add (list, app);
}
return TRUE;
diff --git a/plugins/packagekit/gs-plugin-packagekit.c b/plugins/packagekit/gs-plugin-packagekit.c
index 8988db2f9..e476cb919 100644
--- a/plugins/packagekit/gs-plugin-packagekit.c
+++ b/plugins/packagekit/gs-plugin-packagekit.c
@@ -11,6 +11,7 @@
#include <config.h>
+#include <glib/gi18n-lib.h>
#include <gnome-software.h>
#include <gsettings-desktop-schemas/gdesktop-enums.h>
#include <packagekit-glib2/packagekit.h>
@@ -295,6 +296,8 @@ gs_plugin_add_sources (GsPlugin *plugin,
gs_app_set_summary (app,
GS_APP_QUALITY_LOWEST,
pk_repo_detail_get_description (rd));
+ gs_plugin_packagekit_set_packaging_format (plugin, app);
+ gs_app_set_origin_ui (app, _("Packages"));
gs_app_list_add (list, app);
g_hash_table_insert (hash,
g_strdup (id),
diff --git a/plugins/rpm-ostree/gs-plugin-rpm-ostree.c b/plugins/rpm-ostree/gs-plugin-rpm-ostree.c
index 6b1a13519..869615183 100644
--- a/plugins/rpm-ostree/gs-plugin-rpm-ostree.c
+++ b/plugins/rpm-ostree/gs-plugin-rpm-ostree.c
@@ -14,6 +14,7 @@
#include <gio/gio.h>
#include <gio/gunixfdlist.h>
#include <glib/gstdio.h>
+#include <glib/gi18n-lib.h>
#include <libdnf/libdnf.h>
#include <ostree.h>
#include <rpm/rpmdb.h>
@@ -2147,6 +2148,8 @@ gs_plugin_add_sources (GsPlugin *plugin,
gs_app_set_name (app, GS_APP_QUALITY_LOWEST, description);
gs_app_set_summary (app, GS_APP_QUALITY_LOWEST, description);
+ gs_app_set_origin_ui (app, _("Operating System (RPM-OStree)"));
+
gs_app_list_add (list, app);
}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 8c620503f..9b3b3d873 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -90,6 +90,7 @@ plugins/eos-updater/gs-plugin-eos-updater.c
plugins/fedora-pkgdb-collections/gs-plugin-fedora-pkgdb-collections.c
plugins/flatpak/org.gnome.Software.Plugin.Flatpak.metainfo.xml.in
plugins/flatpak/gs-flatpak.c
+plugins/flatpak/gs-flatpak-utils.c
plugins/flatpak/gs-plugin-flatpak.c
plugins/fwupd/gs-fwupd-app.c
plugins/fwupd/gs-plugin-fwupd.c
diff --git a/src/gs-repo-row.c b/src/gs-repo-row.c
index e9529e9e3..ce27f52f3 100644
--- a/src/gs-repo-row.c
+++ b/src/gs-repo-row.c
@@ -15,146 +15,73 @@
typedef struct
{
GsApp *repo;
- GtkWidget *button;
GtkWidget *name_label;
+ GtkWidget *hostname_label;
GtkWidget *comment_label;
- GtkWidget *details_revealer;
- GtkWidget *status_label;
- GtkWidget *url_box;
- GtkWidget *url_label;
+ GtkWidget *disable_switch;
+ gulong switch_handler_id;
guint refresh_idle_id;
+ guint busy;
} GsRepoRowPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (GsRepoRow, gs_repo_row, GTK_TYPE_LIST_BOX_ROW)
enum {
- SIGNAL_BUTTON_CLICKED,
+ SIGNAL_SWITCH_CLICKED,
SIGNAL_LAST
};
static guint signals [SIGNAL_LAST] = { 0 };
-void
-gs_repo_row_set_name (GsRepoRow *row, const gchar *name)
-{
- GsRepoRowPrivate *priv = gs_repo_row_get_instance_private (row);
-
- gtk_label_set_text (GTK_LABEL (priv->name_label), name);
- gtk_widget_set_visible (priv->name_label, name != NULL);
-}
-
-void
-gs_repo_row_set_comment (GsRepoRow *row, const gchar *comment)
-{
- GsRepoRowPrivate *priv = gs_repo_row_get_instance_private (row);
-
- gtk_label_set_markup (GTK_LABEL (priv->comment_label), comment);
- gtk_widget_set_visible (priv->comment_label, comment != NULL);
-}
-
-void
-gs_repo_row_set_url (GsRepoRow *row, const gchar *url)
-{
- GsRepoRowPrivate *priv = gs_repo_row_get_instance_private (row);
-
- gtk_label_set_text (GTK_LABEL (priv->url_label), url);
- gtk_widget_set_visible (priv->url_box, url != NULL);
-}
-
-static gboolean
-repo_supports_removal (GsApp *repo)
-{
- const gchar *management_plugin = gs_app_get_management_plugin (repo);
-
- /* can't remove a repo, only enable/disable existing ones */
- if (g_strcmp0 (management_plugin, "fwupd") == 0 ||
- g_strcmp0 (management_plugin, "packagekit") == 0 ||
- g_strcmp0 (management_plugin, "rpm-ostree") == 0)
- return FALSE;
-
- return TRUE;
-}
-
static void
refresh_ui (GsRepoRow *row)
{
GsRepoRowPrivate *priv = gs_repo_row_get_instance_private (row);
+ gboolean active = FALSE;
+ gboolean sensitive = TRUE;
if (priv->repo == NULL) {
- gtk_widget_set_visible (priv->button, FALSE);
+ gtk_widget_set_sensitive (priv->disable_switch, FALSE);
+ gtk_switch_set_active (GTK_SWITCH (priv->disable_switch), FALSE);
return;
}
- gtk_widget_set_visible (priv->button, TRUE);
+ g_signal_handler_block (priv->disable_switch, priv->switch_handler_id);
+ gtk_widget_set_sensitive (priv->disable_switch, TRUE);
- /* set button text */
switch (gs_app_get_state (priv->repo)) {
case GS_APP_STATE_AVAILABLE:
case GS_APP_STATE_AVAILABLE_LOCAL:
- /* TRANSLATORS: this is a button in the software repositories
- dialog for enabling a repo */
- gtk_button_set_label (GTK_BUTTON (priv->button), _("_Enable"));
- /* enable button */
- gtk_widget_set_sensitive (priv->button, TRUE);
+ active = FALSE;
break;
case GS_APP_STATE_INSTALLED:
- if (repo_supports_removal (priv->repo)) {
- /* TRANSLATORS: this is a button in the software repositories dialog
- for removing a repo. The ellipsis indicates that further
- steps are required */
- gtk_button_set_label (GTK_BUTTON (priv->button), _("_Remove…"));
- } else {
- /* TRANSLATORS: this is a button in the software repositories dialog
- for disabling a repo. The ellipsis indicates that further
- steps are required */
- gtk_button_set_label (GTK_BUTTON (priv->button), _("_Disable…"));
- }
- /* enable button */
- gtk_widget_set_sensitive (priv->button, TRUE);
+ active = TRUE;
break;
case GS_APP_STATE_INSTALLING:
- /* TRANSLATORS: this is a button in the software repositories dialog
- that shows the status of a repo being enabled */
- gtk_button_set_label (GTK_BUTTON (priv->button), _("Enabling"));
- /* disable button */
- gtk_widget_set_sensitive (priv->button, FALSE);
+ active = TRUE;
+ sensitive = FALSE;
break;
case GS_APP_STATE_REMOVING:
- if (repo_supports_removal (priv->repo)) {
- /* TRANSLATORS: this is a button in the software repositories dialog
- that shows the status of a repo being removed */
- gtk_button_set_label (GTK_BUTTON (priv->button), _("Removing"));
- } else {
- /* TRANSLATORS: this is a button in the software repositories dialog
- that shows the status of a repo being disabled */
- gtk_button_set_label (GTK_BUTTON (priv->button), _("Disabling"));
- }
- /* disable button */
- gtk_widget_set_sensitive (priv->button, FALSE);
+ active = FALSE;
+ sensitive = FALSE;
break;
case GS_APP_STATE_UNAVAILABLE:
+ g_signal_handler_unblock (priv->disable_switch, priv->switch_handler_id);
gtk_widget_destroy (GTK_WIDGET (row));
return;
default:
break;
}
- /* set enabled/disabled label */
- switch (gs_app_get_state (priv->repo)) {
- case GS_APP_STATE_INSTALLED:
- /* TRANSLATORS: this is a label in the software repositories
- dialog that indicates that a repo is enabled. */
- gtk_label_set_text (GTK_LABEL (priv->status_label), _("Enabled"));
- break;
- case GS_APP_STATE_AVAILABLE:
- case GS_APP_STATE_AVAILABLE_LOCAL:
- /* TRANSLATORS: this is a label in the software repositories
- dialog that indicates that a repo is disabled. */
- gtk_label_set_text (GTK_LABEL (priv->status_label), _("Disabled"));
- break;
- default:
- break;
- }
+ /* disable main repo */
+ sensitive = sensitive &&
+ !priv->busy &&
+ !gs_app_has_quirk (priv->repo, GS_APP_QUIRK_PROVENANCE);
+
+ gtk_switch_set_active (GTK_SWITCH (priv->disable_switch), active);
+ gtk_widget_set_sensitive (priv->disable_switch, sensitive);
+
+ g_signal_handler_unblock (priv->disable_switch, priv->switch_handler_id);
}
static gboolean
@@ -180,10 +107,77 @@ repo_state_changed_cb (GsApp *repo, GParamSpec *pspec, GsRepoRow *row)
priv->refresh_idle_id = g_idle_add (refresh_idle, g_object_ref (row));
}
+static gchar *
+get_repo_installed_text (GsApp *repo)
+{
+ GsAppList *related;
+ guint cnt_addon = 0;
+ guint cnt_apps = 0;
+ g_autofree gchar *addons_text = NULL;
+ g_autofree gchar *apps_text = NULL;
+
+ related = gs_app_get_related (repo);
+ for (guint i = 0; i < gs_app_list_length (related); i++) {
+ GsApp *app_tmp = gs_app_list_index (related, i);
+ switch (gs_app_get_kind (app_tmp)) {
+ case AS_COMPONENT_KIND_WEB_APP:
+ case AS_COMPONENT_KIND_DESKTOP_APP:
+ cnt_apps++;
+ break;
+ case AS_COMPONENT_KIND_FONT:
+ case AS_COMPONENT_KIND_CODEC:
+ case AS_COMPONENT_KIND_INPUT_METHOD:
+ case AS_COMPONENT_KIND_ADDON:
+ cnt_addon++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (cnt_addon == 0) {
+ /* TRANSLATORS: This string is used to construct the 'X applications
+ installed' sentence, describing a software repository. */
+ return g_strdup_printf (ngettext ("%u application installed",
+ "%u applications installed",
+ cnt_apps), cnt_apps);
+ }
+ if (cnt_apps == 0) {
+ /* TRANSLATORS: This string is used to construct the 'X add-ons
+ installed' sentence, describing a software repository. */
+ return g_strdup_printf (ngettext ("%u add-on installed",
+ "%u add-ons installed",
+ cnt_addon), cnt_addon);
+ }
+
+ /* TRANSLATORS: This string is used to construct the 'X applications
+ and y add-ons installed' sentence, describing a software repository.
+ The correct form here depends on the number of applications. */
+ apps_text = g_strdup_printf (ngettext ("%u application",
+ "%u applications",
+ cnt_apps), cnt_apps);
+ /* TRANSLATORS: This string is used to construct the 'X applications
+ and y add-ons installed' sentence, describing a software repository.
+ The correct form here depends on the number of add-ons. */
+ addons_text = g_strdup_printf (ngettext ("%u add-on",
+ "%u add-ons",
+ cnt_addon), cnt_addon);
+ /* TRANSLATORS: This string is used to construct the 'X applications
+ and y add-ons installed' sentence, describing a software repository.
+ The correct form here depends on the total number of
+ applications and add-ons. */
+ return g_strdup_printf (ngettext ("%s and %s installed",
+ "%s and %s installed",
+ cnt_apps + cnt_addon),
+ apps_text, addons_text);
+}
+
void
gs_repo_row_set_repo (GsRepoRow *row, GsApp *repo)
{
GsRepoRowPrivate *priv = gs_repo_row_get_instance_private (row);
+ g_autofree gchar *comment = NULL;
+ const gchar *tmp;
g_assert (priv->repo == NULL);
@@ -191,6 +185,36 @@ gs_repo_row_set_repo (GsRepoRow *row, GsApp *repo)
g_signal_connect_object (priv->repo, "notify::state",
G_CALLBACK (repo_state_changed_cb),
row, 0);
+
+ gtk_label_set_label (GTK_LABEL (priv->name_label), gs_app_get_name (repo));
+
+ gtk_widget_set_visible (priv->hostname_label, FALSE);
+
+ tmp = gs_app_get_url (repo, AS_URL_KIND_HOMEPAGE);
+ if (tmp != NULL && *tmp != '\0') {
+ g_autoptr(GUri) uri = NULL;
+
+ uri = g_uri_parse (tmp, G_URI_FLAGS_PARSE_RELAXED, NULL);
+ if (uri && g_uri_get_host (uri) != NULL && *g_uri_get_host (uri) != '\0') {
+ gtk_label_set_label (GTK_LABEL (priv->hostname_label), g_uri_get_host (uri));
+ gtk_widget_set_visible (priv->hostname_label, TRUE);
+ }
+ }
+
+ comment = get_repo_installed_text (repo);
+ tmp = gs_app_get_metadata_item (priv->repo, "GnomeSoftware::InstallationKind");
+ if (tmp != NULL && *tmp != '\0') {
+ gchar *cnt;
+
+ /* Translators: The first '%s' is replaced with a text like '10 applications installed',
+ the second '%s' is replaced with installation kind, like in case of Flatpak 'User
Installation'. */
+ cnt = g_strdup_printf (C_("repo-row", "%s • %s"), comment, tmp);
+ g_clear_pointer (&comment, g_free);
+ comment = cnt;
+ }
+
+ gtk_label_set_label (GTK_LABEL (priv->comment_label), comment);
+
refresh_ui (row);
}
@@ -201,35 +225,21 @@ gs_repo_row_get_repo (GsRepoRow *row)
return priv->repo;
}
-void
-gs_repo_row_show_details (GsRepoRow *row)
+static void
+disable_switch_clicked_cb (GtkWidget *widget,
+ GParamSpec *param,
+ GsRepoRow *row)
{
- GsRepoRowPrivate *priv = gs_repo_row_get_instance_private (row);
+ GsRepoRowPrivate *priv;
- gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
- gtk_revealer_set_reveal_child (GTK_REVEALER (priv->details_revealer), TRUE);
-}
+ g_return_if_fail (GS_IS_REPO_ROW (row));
-void
-gs_repo_row_hide_details (GsRepoRow *row)
-{
- GsRepoRowPrivate *priv = gs_repo_row_get_instance_private (row);
+ priv = gs_repo_row_get_instance_private (row);
- gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), TRUE);
- gtk_revealer_set_reveal_child (GTK_REVEALER (priv->details_revealer), FALSE);
-}
-
-void
-gs_repo_row_show_status (GsRepoRow *row)
-{
- GsRepoRowPrivate *priv = gs_repo_row_get_instance_private (row);
- gtk_widget_set_visible (priv->status_label, TRUE);
-}
+ if (!priv->repo || priv->busy)
+ return;
-static void
-button_clicked_cb (GtkWidget *widget, GsRepoRow *row)
-{
- g_signal_emit (row, signals[SIGNAL_BUTTON_CLICKED], 0);
+ g_signal_emit (row, signals[SIGNAL_SWITCH_CLICKED], 0);
}
static void
@@ -257,8 +267,8 @@ gs_repo_row_init (GsRepoRow *row)
GsRepoRowPrivate *priv = gs_repo_row_get_instance_private (row);
gtk_widget_init_template (GTK_WIDGET (row));
- g_signal_connect (priv->button, "clicked",
- G_CALLBACK (button_clicked_cb), row);
+ priv->switch_handler_id = g_signal_connect (priv->disable_switch, "notify::active",
+ G_CALLBACK (disable_switch_clicked_cb), row);
}
static void
@@ -269,22 +279,19 @@ gs_repo_row_class_init (GsRepoRowClass *klass)
widget_class->destroy = gs_repo_row_destroy;
- signals [SIGNAL_BUTTON_CLICKED] =
- g_signal_new ("button-clicked",
+ signals [SIGNAL_SWITCH_CLICKED] =
+ g_signal_new ("switch-clicked",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GsRepoRowClass, button_clicked),
+ G_STRUCT_OFFSET (GsRepoRowClass, switch_clicked),
NULL, NULL, g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ G_TYPE_NONE, 0, G_TYPE_NONE);
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-repo-row.ui");
- gtk_widget_class_bind_template_child_private (widget_class, GsRepoRow, button);
gtk_widget_class_bind_template_child_private (widget_class, GsRepoRow, name_label);
+ gtk_widget_class_bind_template_child_private (widget_class, GsRepoRow, hostname_label);
gtk_widget_class_bind_template_child_private (widget_class, GsRepoRow, comment_label);
- gtk_widget_class_bind_template_child_private (widget_class, GsRepoRow, details_revealer);
- gtk_widget_class_bind_template_child_private (widget_class, GsRepoRow, status_label);
- gtk_widget_class_bind_template_child_private (widget_class, GsRepoRow, url_box);
- gtk_widget_class_bind_template_child_private (widget_class, GsRepoRow, url_label);
+ gtk_widget_class_bind_template_child_private (widget_class, GsRepoRow, disable_switch);
}
GtkWidget *
@@ -292,3 +299,45 @@ gs_repo_row_new (void)
{
return g_object_new (GS_TYPE_REPO_ROW, NULL);
}
+
+void
+gs_repo_row_set_busy (GsRepoRow *row,
+ gboolean value)
+{
+ GsRepoRowPrivate *priv;
+
+ g_return_if_fail (GS_IS_REPO_ROW (row));
+
+ priv = gs_repo_row_get_instance_private (row);
+
+ if (value)
+ g_return_if_fail (priv->busy + 1 > priv->busy);
+ else
+ g_return_if_fail (priv->busy > 0);
+
+ priv->busy += (value ? 1 : -1);
+
+ if (value && priv->busy == 1)
+ gtk_widget_set_sensitive (priv->disable_switch, FALSE);
+ else if (!value && !priv->busy)
+ refresh_ui (row);
+}
+
+gboolean
+gs_repo_row_get_busy (GsRepoRow *row)
+{
+ GsRepoRowPrivate *priv;
+
+ g_return_val_if_fail (GS_IS_REPO_ROW (row), FALSE);
+
+ priv = gs_repo_row_get_instance_private (row);
+ return priv->busy > 0;
+}
+
+void
+gs_repo_row_refresh (GsRepoRow *row)
+{
+ g_return_if_fail (GS_IS_REPO_ROW (row));
+
+ refresh_ui (row);
+}
diff --git a/src/gs-repo-row.h b/src/gs-repo-row.h
index ff2564ea5..813e4c841 100644
--- a/src/gs-repo-row.h
+++ b/src/gs-repo-row.h
@@ -20,21 +20,16 @@ G_DECLARE_DERIVABLE_TYPE (GsRepoRow, gs_repo_row, GS, REPO_ROW, GtkListBoxRow)
struct _GsRepoRowClass
{
GtkListBoxRowClass parent_class;
- void (*button_clicked) (GsRepoRow *row);
+ void (*switch_clicked) (GsRepoRow *row);
};
GtkWidget *gs_repo_row_new (void);
-void gs_repo_row_set_name (GsRepoRow *row,
- const gchar *name);
-void gs_repo_row_set_comment (GsRepoRow *row,
- const gchar *comment);
-void gs_repo_row_set_url (GsRepoRow *row,
- const gchar *url);
void gs_repo_row_set_repo (GsRepoRow *row,
GsApp *repo);
GsApp *gs_repo_row_get_repo (GsRepoRow *row);
-void gs_repo_row_show_details (GsRepoRow *row);
-void gs_repo_row_hide_details (GsRepoRow *row);
-void gs_repo_row_show_status (GsRepoRow *row);
+void gs_repo_row_set_busy (GsRepoRow *row,
+ gboolean value);
+gboolean gs_repo_row_get_busy (GsRepoRow *row);
+void gs_repo_row_refresh (GsRepoRow *row);
G_END_DECLS
diff --git a/src/gs-repo-row.ui b/src/gs-repo-row.ui
index 2cbe02532..926dfa6b7 100644
--- a/src/gs-repo-row.ui
+++ b/src/gs-repo-row.ui
@@ -2,118 +2,78 @@
<interface>
<!-- interface-requires gtk+ 3.10 -->
<template class="GsRepoRow" parent="GtkListBoxRow">
+ <property name="visible">True</property>
+ <style>
+ <class name="reporow"/>
+ </style>
<child>
- <object class="GtkBox">
+ <object class="GtkGrid">
<property name="visible">True</property>
- <property name="margin-top">18</property>
- <property name="margin-bottom">18</property>
- <property name="margin-start">18</property>
- <property name="margin-end">18</property>
- <property name="orientation">horizontal</property>
- <property name="spacing">16</property>
+ <property name="margin">8</property>
+ <property name="row-spacing">4</property>
+ <property name="column-spacing">4</property>
<child>
- <object class="GtkBox" id="vbox">
+ <object class="GtkLabel" id="name_label">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
+ <property name="halign">start</property>
<property name="hexpand">True</property>
- <child>
- <object class="GtkBox" id="hbox">
- <property name="visible">True</property>
- <property name="orientation">horizontal</property>
- <property name="spacing">4</property>
- <child>
- <object class="GtkLabel" id="name_label">
- <property name="visible">True</property>
- <property name="halign">start</property>
- <property name="ellipsize">end</property>
- </object>
- </child>
- <child>
- <object class="GtkLabel" id="status_label">
- <property name="visible">False</property>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- <packing>
- <property name="pack-type">end</property>
- </packing>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkLabel" id="comment_label">
- <property name="visible">False</property>
- <property name="halign">start</property>
- <property name="xalign">0</property>
- <property name="wrap">True</property>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkRevealer" id="details_revealer">
- <property name="visible">True</property>
- <property name="transition-type">slide-down</property>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="orientation">vertical</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkBox" id="url_box">
- <property name="visible">True</property>
- <property name="orientation">horizontal</property>
- <property name="spacing">8</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="halign">start</property>
- <property name="valign">start</property>
- <property name="label" translatable="yes">URL</property>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkLabel" id="url_label">
- <property name="visible">True</property>
- <property name="halign">start</property>
- <property name="valign">start</property>
- <property name="ellipsize">end</property>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="orientation">horizontal</property>
- <property name="spacing">8</property>
- <child>
- <object class="GtkButton" id="button">
- <property name="visible">True</property>
- <property name="use_underline">True</property>
- <property name="width_request">105</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="halign">start</property>
- <property name="valign">start</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
</object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="hostname_label">
+ <property name="visible">True</property>
+ <property name="halign">start</property>
+ <property name="hexpand">True</property>
+ <attributes>
+ <attribute name="scale" value="0.8"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="comment_label">
+ <property name="visible">True</property>
+ <property name="halign">start</property>
+ <property name="hexpand">True</property>
+ <attributes>
+ <attribute name="scale" value="0.8"/>
+ </attributes>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="disable_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <property name="hexpand">False</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
+ <property name="width">1</property>
+ <property name="height">3</property>
+ </packing>
</child>
</object>
</child>
diff --git a/src/gs-repos-dialog.c b/src/gs-repos-dialog.c
index 3d00b2510..eb44eb3a0 100644
--- a/src/gs-repos-dialog.c
+++ b/src/gs-repos-dialog.c
@@ -16,6 +16,7 @@
#include "gs-common.h"
#include "gs-os-release.h"
#include "gs-repo-row.h"
+#include "gs-repos-section.h"
#include "gs-third-party-repo-row.h"
#include "gs-utils.h"
#include <glib/gi18n.h>
@@ -25,15 +26,13 @@ struct _GsReposDialog
GtkDialog parent_instance;
GSettings *settings;
GsApp *third_party_repo;
+ GHashTable *sections; /* gchar * ~> GsReposSection * */
GCancellable *cancellable;
GsPluginLoader *plugin_loader;
- GtkWidget *label_description;
GtkWidget *label_empty;
GtkWidget *label_header;
- GtkWidget *listbox;
- GtkWidget *listbox_third_party;
- GtkWidget *row_third_party;
+ GtkWidget *content_box;
GtkWidget *spinner;
GtkWidget *stack;
};
@@ -42,6 +41,7 @@ G_DEFINE_TYPE (GsReposDialog, gs_repos_dialog, GTK_TYPE_DIALOG)
typedef struct {
GsReposDialog *dialog;
+ GsRepoRow *row;
GsApp *repo;
GsPluginAction action;
} InstallRemoveData;
@@ -50,6 +50,7 @@ static void
install_remove_data_free (InstallRemoveData *data)
{
g_clear_object (&data->dialog);
+ g_clear_object (&data->row);
g_clear_object (&data->repo);
g_slice_free (InstallRemoveData, data);
}
@@ -59,89 +60,6 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(InstallRemoveData, install_remove_data_free);
static void reload_sources (GsReposDialog *dialog);
static void reload_third_party_repo (GsReposDialog *dialog);
-static gchar *
-get_repo_installed_text (GsApp *repo)
-{
- GsAppList *related;
- guint cnt_addon = 0;
- guint cnt_apps = 0;
- g_autofree gchar *addons_text = NULL;
- g_autofree gchar *apps_text = NULL;
-
- related = gs_app_get_related (repo);
- for (guint i = 0; i < gs_app_list_length (related); i++) {
- GsApp *app_tmp = gs_app_list_index (related, i);
- switch (gs_app_get_kind (app_tmp)) {
- case AS_COMPONENT_KIND_WEB_APP:
- case AS_COMPONENT_KIND_DESKTOP_APP:
- cnt_apps++;
- break;
- case AS_COMPONENT_KIND_FONT:
- case AS_COMPONENT_KIND_CODEC:
- case AS_COMPONENT_KIND_INPUT_METHOD:
- case AS_COMPONENT_KIND_ADDON:
- cnt_addon++;
- break;
- default:
- break;
- }
- }
-
- if (cnt_apps == 0 && cnt_addon == 0) {
- /* nothing! */
- return NULL;
- }
- if (cnt_addon == 0) {
- /* TRANSLATORS: This string is used to construct the 'X applications
- installed' sentence, describing a software repository. */
- return g_strdup_printf (ngettext ("%u application installed",
- "%u applications installed",
- cnt_apps), cnt_apps);
- }
- if (cnt_apps == 0) {
- /* TRANSLATORS: This string is used to construct the 'X add-ons
- installed' sentence, describing a software repository. */
- return g_strdup_printf (ngettext ("%u add-on installed",
- "%u add-ons installed",
- cnt_addon), cnt_addon);
- }
-
- /* TRANSLATORS: This string is used to construct the 'X applications
- and y add-ons installed' sentence, describing a software repository.
- The correct form here depends on the number of applications. */
- apps_text = g_strdup_printf (ngettext ("%u application",
- "%u applications",
- cnt_apps), cnt_apps);
- /* TRANSLATORS: This string is used to construct the 'X applications
- and y add-ons installed' sentence, describing a software repository.
- The correct form here depends on the number of add-ons. */
- addons_text = g_strdup_printf (ngettext ("%u add-on",
- "%u add-ons",
- cnt_addon), cnt_addon);
- /* TRANSLATORS: This string is used to construct the 'X applications
- and y add-ons installed' sentence, describing a software repository.
- The correct form here depends on the total number of
- applications and add-ons. */
- return g_strdup_printf (ngettext ("%s and %s installed",
- "%s and %s installed",
- cnt_apps + cnt_addon),
- apps_text, addons_text);
-}
-
-static gboolean
-repo_supports_removal (GsApp *repo)
-{
- const gchar *management_plugin = gs_app_get_management_plugin (repo);
-
- /* can't remove a repo, only enable/disable existing ones */
- if (g_strcmp0 (management_plugin, "fwupd") == 0 ||
- g_strcmp0 (management_plugin, "packagekit") == 0 ||
- g_strcmp0 (management_plugin, "rpm-ostree") == 0)
- return FALSE;
-
- return TRUE;
-}
-
static void
repo_enabled_cb (GObject *source,
GAsyncResult *res,
@@ -153,6 +71,7 @@ repo_enabled_cb (GObject *source,
const gchar *action_str;
action_str = gs_plugin_action_to_string (install_remove_data->action);
+ gs_repo_row_set_busy (install_remove_data->row, FALSE);
if (!gs_plugin_loader_job_action_finish (plugin_loader, res, &error)) {
if (g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED)) {
@@ -194,21 +113,28 @@ enable_repo_response_cb (GtkDialog *confirm_dialog,
gtk_widget_destroy (GTK_WIDGET (confirm_dialog));
/* not agreed */
- if (response != GTK_RESPONSE_OK)
+ if (response != GTK_RESPONSE_OK) {
+ gs_repo_row_set_busy (install_data->row, FALSE);
return;
+ }
_enable_repo (g_steal_pointer (&install_data));
}
static void
-enable_repo (GsReposDialog *dialog, GsApp *repo)
+enable_repo (GsReposDialog *dialog,
+ GsRepoRow *row,
+ GsApp *repo)
{
g_autoptr(InstallRemoveData) install_data = NULL;
install_data = g_slice_new0 (InstallRemoveData);
install_data->action = GS_PLUGIN_ACTION_INSTALL;
- install_data->repo = g_object_ref (repo);
install_data->dialog = g_object_ref (dialog);
+ install_data->row = g_object_ref (row);
+ install_data->repo = g_object_ref (repo);
+
+ gs_repo_row_set_busy (row, TRUE);
/* user needs to confirm acceptance of an agreement */
if (gs_app_get_agreement (repo) != NULL) {
@@ -266,8 +192,10 @@ remove_repo_response_cb (GtkDialog *confirm_dialog,
gtk_widget_destroy (GTK_WIDGET (confirm_dialog));
/* not agreed */
- if (response != GTK_RESPONSE_OK)
+ if (response != GTK_RESPONSE_OK) {
+ gs_repo_row_set_busy (remove_data->row, FALSE);
return;
+ }
g_debug ("removing repo %s", gs_app_get_id (remove_data->repo));
plugin_job = gs_plugin_job_newv (remove_data->action,
@@ -281,52 +209,37 @@ remove_repo_response_cb (GtkDialog *confirm_dialog,
}
static void
-remove_confirm_repo (GsReposDialog *dialog, GsApp *repo)
+remove_confirm_repo (GsReposDialog *dialog,
+ GsRepoRow *row,
+ GsApp *repo)
{
InstallRemoveData *remove_data;
GtkWidget *confirm_dialog;
g_autofree gchar *message = NULL;
- g_autofree gchar *title = NULL;
GtkWidget *button;
GtkStyleContext *context;
remove_data = g_slice_new0 (InstallRemoveData);
remove_data->action = GS_PLUGIN_ACTION_REMOVE;
- remove_data->repo = g_object_ref (repo);
remove_data->dialog = g_object_ref (dialog);
+ remove_data->row = g_object_ref (row);
+ remove_data->repo = g_object_ref (repo);
- if (repo_supports_removal (repo)) {
- /* TRANSLATORS: this is a prompt message, and '%s' is a
- * repository name, e.g. 'GNOME Nightly' */
- title = g_strdup_printf (_("Remove “%s”?"),
- gs_app_get_name (repo));
- } else {
- /* TRANSLATORS: this is a prompt message, and '%s' is a
- * repository name, e.g. 'GNOME Nightly' */
- title = g_strdup_printf (_("Disable “%s”?"),
- gs_app_get_name (repo));
- }
- /* TRANSLATORS: longer dialog text */
- message = g_strdup (_("Software that has been installed from this "
- "repository will no longer receive updates, "
- "including security fixes."));
+ /* TRANSLATORS: The '%s' is replaced with a repository name, like "Fedora Modular - x86_64" */
+ message = g_strdup_printf (_("Software that has been installed from %s will cease receive updates."),
+ gs_app_get_name (repo));
/* ask for confirmation */
confirm_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
GTK_DIALOG_MODAL,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_CANCEL,
- "%s", title);
+ "%s", _("Disable Repository?"));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (confirm_dialog),
"%s", message);
- if (repo_supports_removal (repo)) {
- /* TRANSLATORS: this is button text to remove the repo */
- button = gtk_dialog_add_button (GTK_DIALOG (confirm_dialog), _("Remove"), GTK_RESPONSE_OK);
- } else {
- /* TRANSLATORS: this is button text to remove the repo */
- button = gtk_dialog_add_button (GTK_DIALOG (confirm_dialog), _("Disable"), GTK_RESPONSE_OK);
- }
+ /* TRANSLATORS: this is button text to disable a repo */
+ button = gtk_dialog_add_button (GTK_DIALOG (confirm_dialog), _("Disable"), GTK_RESPONSE_OK);
context = gtk_widget_get_style_context (button);
gtk_style_context_add_class (context, "destructive-action");
@@ -337,11 +250,14 @@ remove_confirm_repo (GsReposDialog *dialog, GsApp *repo)
gtk_window_set_modal (GTK_WINDOW (confirm_dialog), TRUE);
gtk_window_present (GTK_WINDOW (confirm_dialog));
+
+ gs_repo_row_set_busy (row, TRUE);
}
static void
-repo_button_clicked_cb (GsRepoRow *row,
- GsReposDialog *dialog)
+repo_section_switch_clicked_cb (GsReposSection *section,
+ GsRepoRow *row,
+ GsReposDialog *dialog)
{
GsApp *repo;
@@ -350,12 +266,13 @@ repo_button_clicked_cb (GsRepoRow *row,
switch (gs_app_get_state (repo)) {
case GS_APP_STATE_AVAILABLE:
case GS_APP_STATE_AVAILABLE_LOCAL:
- enable_repo (dialog, repo);
+ enable_repo (dialog, row, repo);
break;
case GS_APP_STATE_INSTALLED:
- remove_confirm_repo (dialog, repo);
+ remove_confirm_repo (dialog, row, repo);
break;
default:
+ gs_repo_row_refresh (row);
g_warning ("repo %s button clicked in unexpected state %s",
gs_app_get_id (repo),
gs_app_state_to_string (gs_app_get_state (repo)));
@@ -363,8 +280,9 @@ repo_button_clicked_cb (GsRepoRow *row,
}
}
-static GtkListBox *
-get_list_box_for_repo (GsReposDialog *dialog, GsApp *repo)
+static gboolean
+is_third_party_repo (GsReposDialog *dialog,
+ GsApp *repo)
{
if (dialog->third_party_repo != NULL) {
const gchar *source_repo;
@@ -374,19 +292,20 @@ get_list_box_for_repo (GsReposDialog *dialog, GsApp *repo)
source_third_party_package = gs_app_get_source_id_default (dialog->third_party_repo);
/* group repos from the same repo-release package together */
- if (g_strcmp0 (source_repo, source_third_party_package) == 0)
- return GTK_LIST_BOX (dialog->listbox_third_party);
+ return g_strcmp0 (source_repo, source_third_party_package) == 0;
}
- return GTK_LIST_BOX (dialog->listbox);
+ return FALSE;
}
static void
-add_repo (GsReposDialog *dialog, GsApp *repo)
+add_repo (GsReposDialog *dialog,
+ GsApp *repo,
+ GSList **third_party_repos)
{
- GtkWidget *row;
- g_autofree gchar *text = NULL;
GsAppState state;
+ GtkWidget *section;
+ gchar *origin_ui;
state = gs_app_get_state (repo);
if (!(state == GS_APP_STATE_AVAILABLE ||
@@ -400,21 +319,32 @@ add_repo (GsReposDialog *dialog, GsApp *repo)
return;
}
- row = gs_repo_row_new ();
- gs_repo_row_set_name (GS_REPO_ROW (row),
- gs_app_get_name (repo));
- text = get_repo_installed_text (repo);
- gs_repo_row_set_comment (GS_REPO_ROW (row), text);
- gs_repo_row_set_url (GS_REPO_ROW (row),
- gs_app_get_url (repo, AS_URL_KIND_HOMEPAGE));
- gs_repo_row_show_status (GS_REPO_ROW (row));
- gs_repo_row_set_repo (GS_REPO_ROW (row), repo);
-
- g_signal_connect (row, "button-clicked",
- G_CALLBACK (repo_button_clicked_cb), dialog);
-
- gtk_list_box_prepend (get_list_box_for_repo (dialog, repo), row);
- gtk_widget_show (row);
+ if (third_party_repos && is_third_party_repo (dialog, repo)) {
+ *third_party_repos = g_slist_prepend (*third_party_repos, repo);
+ return;
+ }
+
+ origin_ui = gs_app_get_origin_ui (repo);
+ if (!origin_ui)
+ origin_ui = gs_app_get_packaging_format (repo);
+ if (!origin_ui)
+ origin_ui = g_strdup (gs_app_get_management_plugin (repo));
+ section = g_hash_table_lookup (dialog->sections, origin_ui);
+ if (section) {
+ g_free (origin_ui);
+ } else {
+ section = gs_repos_section_new (origin_ui);
+ g_object_set (G_OBJECT (section),
+ "halign", GTK_ALIGN_FILL,
+ "hexpand", TRUE,
+ NULL);
+ g_signal_connect_object (section, "switch-clicked",
+ G_CALLBACK (repo_section_switch_clicked_cb), dialog, 0);
+ g_hash_table_insert (dialog->sections, origin_ui, section);
+ gtk_widget_show (section);
+ }
+
+ gs_repos_section_add_repo (GS_REPOS_SECTION (section), repo);
}
static void
@@ -492,30 +422,25 @@ third_party_repo_button_clicked_cb (GsThirdPartyRepoRow *row,
g_settings_set_boolean (dialog->settings, "show-nonfree-prompt", FALSE);
}
-static void
-refresh_third_party_repo (GsReposDialog *dialog)
+static gint
+repos_dialog_compare_sections_cb (gconstpointer aa,
+ gconstpointer bb)
{
- if (dialog->third_party_repo == NULL) {
- gtk_widget_hide (dialog->listbox_third_party);
- return;
- }
-
- gtk_widget_show (dialog->listbox_third_party);
-}
+ GsReposSection *section_a = (GsReposSection *) aa;
+ GsReposSection *section_b = (GsReposSection *) bb;
+ g_autofree gchar *sort_key_a = NULL;
+ g_autofree gchar *sort_key_b = NULL;
-static void
-remove_all_repo_rows_cb (GtkWidget *widget, gpointer user_data)
-{
- GtkContainer *container = GTK_CONTAINER (user_data);
+ /* Place firmware updates at the bottom */
+ if (g_strcmp0 (gs_repos_section_get_packaging_format (section_a), "fwupd") == 0)
+ return 1;
+ if (g_strcmp0 (gs_repos_section_get_packaging_format (section_b), "fwupd") == 0)
+ return -1;
- if (GS_IS_REPO_ROW (widget))
- gtk_container_remove (container, widget);
-}
+ sort_key_a = gs_utils_sort_key (gs_repos_section_get_title (section_a));
+ sort_key_b = gs_utils_sort_key (gs_repos_section_get_title (section_b));
-static void
-container_remove_all_repo_rows (GtkContainer *container)
-{
- gtk_container_foreach (container, remove_all_repo_rows_cb, container);
+ return g_strcmp0 (sort_key_a, sort_key_b);
}
static void
@@ -526,6 +451,8 @@ get_sources_cb (GsPluginLoader *plugin_loader,
GsApp *app;
g_autoptr(GError) error = NULL;
g_autoptr(GsAppList) list = NULL;
+ g_autoptr(GSList) other_repos = NULL;
+ g_autoptr(GList) sections = NULL;
/* get the results */
list = gs_plugin_loader_job_process_finish (plugin_loader, res, &error);
@@ -545,8 +472,8 @@ get_sources_cb (GsPluginLoader *plugin_loader,
}
/* remove previous */
- gs_container_remove_all (GTK_CONTAINER (dialog->listbox));
- container_remove_all_repo_rows (GTK_CONTAINER (dialog->listbox_third_party));
+ g_hash_table_remove_all (dialog->sections);
+ gs_container_remove_all (GTK_CONTAINER (dialog->content_box));
/* stop the spinner */
gs_stop_spinner (GTK_SPINNER (dialog->spinner));
@@ -566,11 +493,85 @@ get_sources_cb (GsPluginLoader *plugin_loader,
gtk_stack_set_visible_child_name (GTK_STACK (dialog->stack), "sources");
for (guint i = 0; i < gs_app_list_length (list); i++) {
app = gs_app_list_index (list, i);
- add_repo (dialog, app);
+ add_repo (dialog, app, &other_repos);
+ }
+
+ sections = g_hash_table_get_values (dialog->sections);
+ sections = g_list_sort (sections, repos_dialog_compare_sections_cb);
+ for (GList *link = sections; link; link = g_list_next (link)) {
+ GtkWidget *section = link->data;
+ gtk_container_add (GTK_CONTAINER (dialog->content_box), section);
}
- gtk_widget_set_visible (dialog->listbox,
- gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->listbox), 0) != NULL);
+ gtk_widget_set_visible (dialog->content_box, sections != NULL);
+
+ if (other_repos) {
+ GsReposSection *section;
+ GtkWidget *label;
+ GtkStyleContext *style;
+ g_autofree gchar *anchor = NULL;
+ g_autofree gchar *hint = NULL;
+
+ section = GS_REPOS_SECTION (gs_repos_section_new (_("Fedora Third Party Repositories")));
+ g_object_set (G_OBJECT (section),
+ "halign", GTK_ALIGN_FILL,
+ "hexpand", TRUE,
+ NULL);
+ g_signal_connect_object (section, "switch-clicked",
+ G_CALLBACK (repo_section_switch_clicked_cb), dialog, 0);
+ gtk_widget_show (GTK_WIDGET (section));
+
+ anchor = g_strdup_printf ("<a href=\"%s\">%s</a>",
+
"https://fedoraproject.org/wiki/Workstation/Third_Party_Software_Repositories",
+ /* TRANSLATORS: this is the clickable
+ * link on the third party repositories info bar */
+ _("more information"));
+ hint = g_strdup_printf (
+ /* TRANSLATORS: this is the third party repositories info bar. The '%s' is replaced
+ with a link consisting a text "more information", which constructs a sentence:
+ "Additional repositories from selected third parties - more information."*/
+ _("Additional repositories from selected third parties - %s."),
+ anchor);
+
+ label = gtk_label_new ("");
+ g_object_set (G_OBJECT (label),
+ "halign", GTK_ALIGN_START,
+ "hexpand", TRUE,
+ "label", hint,
+ "use-markup", TRUE,
+ "visible", TRUE,
+ "wrap", TRUE,
+ "xalign", 0.0,
+ NULL);
+ style = gtk_widget_get_style_context (label);
+ gtk_style_context_add_class (style, "dim-label");
+
+ gtk_box_pack_start (GTK_BOX (section), label, FALSE, TRUE, 0);
+ gtk_box_reorder_child (GTK_BOX (section), label, 1);
+
+ for (GSList *link = other_repos; link; link = g_slist_next (link)) {
+ GsApp *repo = link->data;
+ gs_repos_section_add_repo (section, repo);
+ }
+
+ gtk_container_add (GTK_CONTAINER (dialog->content_box), GTK_WIDGET (section));
+ }
+}
+
+static void
+reload_sources (GsReposDialog *dialog)
+{
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+
+ /* get the list of non-core software repositories */
+ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_GET_SOURCES,
+ "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_RELATED |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN_HOSTNAME,
+ NULL);
+ gs_plugin_loader_job_process_async (dialog->plugin_loader, plugin_job,
+ dialog->cancellable,
+ (GAsyncReadyCallback) get_sources_cb,
+ dialog);
}
static void
@@ -592,6 +593,7 @@ resolve_third_party_repo_cb (GsPluginLoader *plugin_loader,
return;
} else {
g_warning ("failed to resolve third party repo: %s", error->message);
+ reload_sources (dialog);
return;
}
}
@@ -603,26 +605,7 @@ resolve_third_party_repo_cb (GsPluginLoader *plugin_loader,
app = NULL;
g_set_object (&dialog->third_party_repo, app);
- gs_third_party_repo_row_set_app (GS_THIRD_PARTY_REPO_ROW (dialog->row_third_party), app);
-
- /* refresh widget */
- refresh_third_party_repo (dialog);
-}
-
-static void
-reload_sources (GsReposDialog *dialog)
-{
- g_autoptr(GsPluginJob) plugin_job = NULL;
-
- /* get the list of non-core software repositories */
- plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_GET_SOURCES,
- "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_RELATED |
- GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN_HOSTNAME,
- NULL);
- gs_plugin_loader_job_process_async (dialog->plugin_loader, plugin_job,
- dialog->cancellable,
- (GAsyncReadyCallback) get_sources_cb,
- dialog);
+ reload_sources (dialog);
}
static gboolean
@@ -649,8 +632,10 @@ reload_third_party_repo (GsReposDialog *dialog)
g_autoptr(GsPluginJob) plugin_job = NULL;
/* Fedora-specific functionality */
- if (!is_fedora ())
+ if (!is_fedora ()) {
+ reload_sources (dialog);
return;
+ }
plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_SEARCH_PROVIDES,
"search", third_party_repo_package,
@@ -663,64 +648,6 @@ reload_third_party_repo (GsReposDialog *dialog)
dialog);
}
-static gchar *
-get_row_sort_key (GtkListBoxRow *row)
-{
- GsApp *app;
- guint sort_order;
- g_autofree gchar *sort_key = NULL;
-
- /* sort third party repo rows first */
- if (GS_IS_THIRD_PARTY_REPO_ROW (row)) {
- sort_order = 1;
- app = gs_third_party_repo_row_get_app (GS_THIRD_PARTY_REPO_ROW (row));
- } else {
- sort_order = 2;
- app = gs_repo_row_get_repo (GS_REPO_ROW (row));
- }
-
- if (gs_app_get_name (app) != NULL) {
- sort_key = gs_utils_sort_key (gs_app_get_name (app));
- return g_strdup_printf ("%u:%s", sort_order, sort_key);
- } else {
- return g_strdup_printf ("%u:", sort_order);
- }
-}
-
-static gint
-list_sort_func (GtkListBoxRow *a,
- GtkListBoxRow *b,
- gpointer user_data)
-{
- g_autofree gchar *key1 = get_row_sort_key (a);
- g_autofree gchar *key2 = get_row_sort_key (b);
-
- /* compare the keys according to the algorithm above */
- return g_strcmp0 (key1, key2);
-}
-
-static void
-list_row_activated_cb (GtkListBox *list_box,
- GtkListBoxRow *row,
- GsReposDialog *dialog)
-{
- GtkListBoxRow *other_row;
-
- if (!GS_IS_REPO_ROW (row))
- return;
-
- gs_repo_row_show_details (GS_REPO_ROW (row));
-
- for (guint i = 0; (other_row = gtk_list_box_get_row_at_index (list_box, i)) != NULL; i++) {
- if (!GS_IS_REPO_ROW (other_row))
- continue;
- if (other_row == row)
- continue;
-
- gs_repo_row_hide_details (GS_REPO_ROW (other_row));
- }
-}
-
static gchar *
get_os_name (void)
{
@@ -742,7 +669,6 @@ get_os_name (void)
static void
reload_cb (GsPluginLoader *plugin_loader, GsReposDialog *dialog)
{
- reload_sources (dialog);
reload_third_party_repo (dialog);
}
@@ -765,6 +691,7 @@ gs_repos_dialog_dispose (GObject *object)
}
g_cancellable_cancel (dialog->cancellable);
+ g_clear_pointer (&dialog->sections, g_hash_table_unref);
g_clear_object (&dialog->cancellable);
g_clear_object (&dialog->settings);
g_clear_object (&dialog->third_party_repo);
@@ -775,7 +702,6 @@ gs_repos_dialog_dispose (GObject *object)
static void
gs_repos_dialog_init (GsReposDialog *dialog)
{
- g_autofree gchar *label_description_text = NULL;
g_autofree gchar *label_empty_text = NULL;
g_autofree gchar *os_name = NULL;
g_autoptr(GString) str = g_string_new (NULL);
@@ -784,47 +710,10 @@ gs_repos_dialog_init (GsReposDialog *dialog)
dialog->cancellable = g_cancellable_new ();
dialog->settings = g_settings_new ("org.gnome.software");
+ dialog->sections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
os_name = get_os_name ();
- gtk_list_box_set_sort_func (GTK_LIST_BOX (dialog->listbox),
- list_sort_func,
- dialog, NULL);
- g_signal_connect (dialog->listbox, "row-activated",
- G_CALLBACK (list_row_activated_cb), dialog);
-
- /* TRANSLATORS: This is the description text displayed in the Software Repositories dialog.
- %s gets replaced by the name of the actual distro, e.g. Fedora. */
- label_description_text = g_strdup_printf (_("These repositories supplement the default software
provided by %s."),
- os_name);
- gtk_label_set_text (GTK_LABEL (dialog->label_description), label_description_text);
-
- /* set up third party repository row */
- gtk_list_box_set_sort_func (GTK_LIST_BOX (dialog->listbox_third_party),
- list_sort_func,
- dialog, NULL);
- g_signal_connect (dialog->listbox_third_party, "row-activated",
- G_CALLBACK (list_row_activated_cb), dialog);
- g_signal_connect (dialog->row_third_party, "button-clicked",
- G_CALLBACK (third_party_repo_button_clicked_cb), dialog);
- gs_third_party_repo_row_set_name (GS_THIRD_PARTY_REPO_ROW (dialog->row_third_party),
- /* TRANSLATORS: info bar title in the software repositories dialog
*/
- _("Third Party Repositories"));
- g_string_append (str,
- /* TRANSLATORS: this is the third party repositories info bar. */
- _("Access additional software from selected third party sources."));
- g_string_append (str, " ");
- g_string_append (str,
- /* TRANSLATORS: this is the third party repositories info bar. */
- _("Some of this software is proprietary and therefore has restrictions on use,
sharing, and access to source code."));
- g_string_append_printf (str, " <a href=\"%s\">%s</a>",
-
"https://fedoraproject.org/wiki/Workstation/Third_Party_Software_Repositories",
- /* TRANSLATORS: this is the clickable
- * link on the third party repositories info bar */
- _("Find out more…"));
- gs_third_party_repo_row_set_comment (GS_THIRD_PARTY_REPO_ROW (dialog->row_third_party), str->str);
- refresh_third_party_repo (dialog);
-
/* TRANSLATORS: This is the description text displayed in the Software Repositories dialog.
%s gets replaced by the name of the actual distro, e.g. Fedora. */
label_empty_text = g_strdup_printf (_("These repositories supplement the default software provided by
%s."),
@@ -842,12 +731,9 @@ gs_repos_dialog_class_init (GsReposDialogClass *klass)
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-repos-dialog.ui");
- gtk_widget_class_bind_template_child (widget_class, GsReposDialog, label_description);
gtk_widget_class_bind_template_child (widget_class, GsReposDialog, label_empty);
gtk_widget_class_bind_template_child (widget_class, GsReposDialog, label_header);
- gtk_widget_class_bind_template_child (widget_class, GsReposDialog, listbox);
- gtk_widget_class_bind_template_child (widget_class, GsReposDialog, listbox_third_party);
- gtk_widget_class_bind_template_child (widget_class, GsReposDialog, row_third_party);
+ gtk_widget_class_bind_template_child (widget_class, GsReposDialog, content_box);
gtk_widget_class_bind_template_child (widget_class, GsReposDialog, spinner);
gtk_widget_class_bind_template_child (widget_class, GsReposDialog, stack);
}
@@ -865,7 +751,6 @@ gs_repos_dialog_new (GtkWindow *parent, GsPluginLoader *plugin_loader)
set_plugin_loader (dialog, plugin_loader);
gtk_stack_set_visible_child_name (GTK_STACK (dialog->stack), "waiting");
gs_start_spinner (GTK_SPINNER (dialog->spinner));
- reload_sources (dialog);
reload_third_party_repo (dialog);
return GTK_WIDGET (dialog);
diff --git a/src/gs-repos-dialog.ui b/src/gs-repos-dialog.ui
index 246fa477c..0f0e1a37f 100644
--- a/src/gs-repos-dialog.ui
+++ b/src/gs-repos-dialog.ui
@@ -99,57 +99,14 @@
<property name="vscrollbar_policy">automatic</property>
<property name="shadow_type">none</property>
<child>
- <object class="GtkBox" id="box1">
+ <object class="GtkBox" id="content_box">
<property name="visible">True</property>
<property name="margin_start">52</property>
<property name="margin_end">52</property>
<property name="margin_top">24</property>
<property name="margin_bottom">40</property>
<property name="orientation">vertical</property>
- <property name="spacing">4</property>
- <child>
- <object class="GtkLabel" id="label_description">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="wrap">True</property>
- <property name="max_width_chars">30</property>
- <property name="margin_bottom">16</property>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkListBox" id="listbox_third_party">
- <property name="visible">True</property>
- <property name="selection_mode">none</property>
- <property name="halign">fill</property>
- <property name="valign">start</property>
- <property name="margin_bottom">16</property>
- <style>
- <class name="content" />
- </style>
- <child>
- <object class="GsThirdPartyRepoRow" id="row_third_party">
- <property name="visible">True</property>
- <property name="activatable">False</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkListBox" id="listbox">
- <property name="halign">fill</property>
- <property name="valign">start</property>
- <property name="vexpand">True</property>
- <property name="margin_top">9</property>
- <property name="visible">True</property>
- <property name="selection_mode">none</property>
- <style>
- <class name="content" />
- </style>
- </object>
- </child>
+ <property name="spacing">20</property>
</object>
</child>
</object>
diff --git a/src/gs-repos-section.c b/src/gs-repos-section.c
new file mode 100644
index 000000000..685a7dc4a
--- /dev/null
+++ b/src/gs-repos-section.c
@@ -0,0 +1,176 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2021 Red Hat <www.redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "gs-repo-row.h"
+#include "gs-repos-section.h"
+
+struct _GsReposSection
+{
+ GtkBox parent_instance;
+ GtkWidget *title;
+ GtkListBox *list;
+ gchar *packaging_format;
+};
+
+G_DEFINE_TYPE (GsReposSection, gs_repos_section, GTK_TYPE_BOX)
+
+enum {
+ SIGNAL_SWITCH_CLICKED,
+ SIGNAL_LAST
+};
+
+static guint signals [SIGNAL_LAST] = { 0 };
+
+static void
+repo_switch_clicked_cb (GsRepoRow *row,
+ GsReposSection *section)
+{
+ g_signal_emit (section, signals[SIGNAL_SWITCH_CLICKED], 0, row);
+}
+
+static gchar *
+_get_app_sort_key (GsApp *app)
+{
+ if (gs_app_get_name (app) != NULL)
+ return gs_utils_sort_key (gs_app_get_name (app));
+
+ return NULL;
+}
+
+static gint
+_list_sort_func (GtkListBoxRow *a, GtkListBoxRow *b, gpointer user_data)
+{
+ GsApp *a1 = gs_repo_row_get_repo (GS_REPO_ROW (a));
+ GsApp *a2 = gs_repo_row_get_repo (GS_REPO_ROW (b));
+ g_autofree gchar *key1 = _get_app_sort_key (a1);
+ g_autofree gchar *key2 = _get_app_sort_key (a2);
+
+ return g_strcmp0 (key1, key2);
+}
+
+static void
+gs_repos_section_finalize (GObject *object)
+{
+ GsReposSection *self = GS_REPOS_SECTION (object);
+
+ g_free (self->packaging_format);
+
+ G_OBJECT_CLASS (gs_repos_section_parent_class)->finalize (object);
+}
+
+static void
+gs_repos_section_class_init (GsReposSectionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_repos_section_finalize;
+
+ signals [SIGNAL_SWITCH_CLICKED] =
+ g_signal_new ("switch-clicked",
+ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, GS_TYPE_REPO_ROW);
+
+ gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (klass), "repos-section");
+}
+
+static void
+gs_repos_section_init (GsReposSection *self)
+{
+ PangoAttrList *attrs = NULL;
+
+ attrs = pango_attr_list_new ();
+ pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
+
+ self->title = gtk_label_new ("");
+ g_object_set (G_OBJECT (self->title),
+ "visible", TRUE,
+ "halign", GTK_ALIGN_START,
+ "valign", GTK_ALIGN_CENTER,
+ "xalign", 0.0,
+ "yalign", 0.5,
+ "attributes", attrs,
+ NULL);
+
+ pango_attr_list_unref (attrs);
+
+ gtk_box_pack_start (GTK_BOX (self), self->title, FALSE, FALSE, 0);
+
+ self->list = GTK_LIST_BOX (gtk_list_box_new ());
+ g_object_set (G_OBJECT (self->list),
+ "visible", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ "hexpand", TRUE,
+ "valign", GTK_ALIGN_CENTER,
+ "vexpand", TRUE,
+ "margin-top", 4,
+ "margin-bottom", 4,
+ "selection-mode", GTK_SELECTION_NONE,
+ NULL);
+ gtk_list_box_set_sort_func (self->list, _list_sort_func, self, NULL);
+
+ gtk_box_pack_start (GTK_BOX (self), GTK_WIDGET (self->list), TRUE, TRUE, 0);
+}
+
+GtkWidget *
+gs_repos_section_new (const gchar *title)
+{
+ GsReposSection *self;
+
+ self = g_object_new (GS_TYPE_REPOS_SECTION,
+ "orientation", GTK_ORIENTATION_VERTICAL,
+ "spacing", 4,
+ NULL);
+
+ gtk_label_set_text (GTK_LABEL (self->title), title);
+
+ return GTK_WIDGET (self);
+}
+
+void
+gs_repos_section_add_repo (GsReposSection *self,
+ GsApp *repo)
+{
+ GtkWidget *row;
+
+ g_return_if_fail (GS_IS_REPOS_SECTION (self));
+ g_return_if_fail (GS_IS_APP (repo));
+
+ if (!self->packaging_format)
+ self->packaging_format = gs_app_get_packaging_format (repo);
+
+ row = gs_repo_row_new ();
+ gs_repo_row_set_repo (GS_REPO_ROW (row), repo);
+
+ g_signal_connect (row, "switch-clicked",
+ G_CALLBACK (repo_switch_clicked_cb), self);
+
+ gtk_list_box_prepend (self->list, row);
+ gtk_widget_show (row);
+}
+
+const gchar *
+gs_repos_section_get_title (GsReposSection *self)
+{
+ g_return_val_if_fail (GS_IS_REPOS_SECTION (self), NULL);
+
+ return gtk_label_get_text (GTK_LABEL (self->title));
+}
+
+const gchar *
+gs_repos_section_get_packaging_format (GsReposSection *self)
+{
+ g_return_val_if_fail (GS_IS_REPOS_SECTION (self), NULL);
+
+ return self->packaging_format;
+}
diff --git a/src/gs-repos-section.h b/src/gs-repos-section.h
new file mode 100644
index 000000000..2679017cd
--- /dev/null
+++ b/src/gs-repos-section.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2021 Red Hat <www.redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "gs-app.h"
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_REPOS_SECTION (gs_repos_section_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsReposSection, gs_repos_section, GS, REPOS_SECTION, GtkBox)
+
+GtkWidget *gs_repos_section_new (const gchar *title);
+void gs_repos_section_add_repo (GsReposSection *self,
+ GsApp *repo);
+const gchar *gs_repos_section_get_title (GsReposSection *self);
+const gchar *gs_repos_section_get_packaging_format (GsReposSection *self);
+
+G_END_DECLS
diff --git a/src/gtk-style.css b/src/gtk-style.css
index 641bac64b..16f25d0c1 100644
--- a/src/gtk-style.css
+++ b/src/gtk-style.css
@@ -41,6 +41,17 @@
font-size: x-small;
}
+repos-section list {
+ border-radius: 6px;
+ border: 1px solid darker(@theme_bg_color);
+ padding-bottom: 4px;
+ padding-top: 4px;
+}
+
+repos-section list .reporow:not(:last-child) {
+ border-bottom: 1px solid @theme_unfocused_bg_color;
+}
+
/* Adapted from Adwaita’s .needs-attention class for stacksidebar */
sidebar row.needs-attention > box > label {
animation: needs_attention 150ms ease-in;
@@ -446,6 +457,7 @@ summary-tile {
.app-updates-section {
border-radius: 4px;
border: 1px solid darker(@theme_bg_color);
+ -gtk-outline-radius: 50%;
}
.app-listbox-header-title {
diff --git a/src/meson.build b/src/meson.build
index 9fcacf268..060925f70 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -50,6 +50,7 @@ gnome_software_sources = [
'gs-progress-button.c',
'gs-removal-dialog.c',
'gs-repos-dialog.c',
+ 'gs-repos-section.c',
'gs-repo-row.c',
'gs-review-bar.c',
'gs-review-dialog.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]