[gnome-software/1364-implement-other-apps-by-author-section-in-app-details-page] Proof of concept
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/1364-implement-other-apps-by-author-section-in-app-details-page] Proof of concept
- Date: Wed, 9 Mar 2022 17:23:25 +0000 (UTC)
commit 4cc0f77faedbd3867de2c04cdadfed16f6fa7e90
Author: Milan Crha <mcrha redhat com>
Date: Wed Mar 9 18:23:01 2022 +0100
Proof of concept
lib/gs-appstream.c | 74 +++++++++++++++++++--------
lib/gs-appstream.h | 6 +++
lib/gs-plugin-loader.c | 6 +++
lib/gs-plugin-types.h | 2 +
lib/gs-plugin-vfuncs.h | 21 ++++++++
lib/gs-plugin.c | 6 +++
plugins/core/gs-plugin-appstream.c | 22 +++++++++
plugins/flatpak/gs-flatpak.c | 74 +++++++++++++++++++++++++++
plugins/flatpak/gs-flatpak.h | 6 +++
plugins/flatpak/gs-plugin-flatpak.c | 21 ++++++++
src/gs-details-page.c | 99 ++++++++++++++++++++++++++++++++++++-
src/gs-details-page.ui | 42 ++++++++++++++++
12 files changed, 357 insertions(+), 22 deletions(-)
---
diff --git a/lib/gs-appstream.c b/lib/gs-appstream.c
index 14cf8887f..70d7aa472 100644
--- a/lib/gs-appstream.c
+++ b/lib/gs-appstream.c
@@ -1342,32 +1342,24 @@ gs_appstream_silo_search_component (GPtrArray *array, XbNode *component, const g
return matches_sum;
}
-gboolean
-gs_appstream_search (GsPlugin *plugin,
- XbSilo *silo,
- const gchar * const *values,
- GsAppList *list,
- GCancellable *cancellable,
- GError **error)
+typedef struct _Query {
+ AsSearchTokenMatch match_value;
+ const gchar *xpath;
+} Query;
+
+static gboolean
+gs_appstream_do_search (GsPlugin *plugin,
+ XbSilo *silo,
+ const gchar * const *values,
+ const Query queries[],
+ GsAppList *list,
+ GCancellable *cancellable,
+ GError **error)
{
g_autoptr(GError) error_local = NULL;
g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func ((GDestroyNotify)
gs_appstream_search_helper_free);
g_autoptr(GPtrArray) components = NULL;
g_autoptr(GTimer) timer = g_timer_new ();
- const struct {
- AsSearchTokenMatch match_value;
- const gchar *xpath;
- } queries[] = {
- { AS_SEARCH_TOKEN_MATCH_MIMETYPE, "mimetypes/mimetype[text()~=stem(?)]" },
- { AS_SEARCH_TOKEN_MATCH_PKGNAME, "pkgname[text()~=stem(?)]" },
- { AS_SEARCH_TOKEN_MATCH_SUMMARY, "summary[text()~=stem(?)]" },
- { AS_SEARCH_TOKEN_MATCH_NAME, "name[text()~=stem(?)]" },
- { AS_SEARCH_TOKEN_MATCH_KEYWORD, "keywords/keyword[text()~=stem(?)]" },
- { AS_SEARCH_TOKEN_MATCH_ID, "id[text()~=stem(?)]" },
- { AS_SEARCH_TOKEN_MATCH_ID, "launchable[text()~=stem(?)]" },
- { AS_SEARCH_TOKEN_MATCH_ORIGIN, "../components[@origin~=stem(?)]" },
- { AS_SEARCH_TOKEN_MATCH_NONE, NULL }
- };
/* add some weighted queries */
for (guint i = 0; queries[i].xpath != NULL; i++) {
@@ -1437,6 +1429,46 @@ gs_appstream_search (GsPlugin *plugin,
return TRUE;
}
+gboolean
+gs_appstream_search (GsPlugin *plugin,
+ XbSilo *silo,
+ const gchar * const *values,
+ GsAppList *list,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const Query queries[] = {
+ { AS_SEARCH_TOKEN_MATCH_MIMETYPE, "mimetypes/mimetype[text()~=stem(?)]" },
+ { AS_SEARCH_TOKEN_MATCH_PKGNAME, "pkgname[text()~=stem(?)]" },
+ { AS_SEARCH_TOKEN_MATCH_SUMMARY, "summary[text()~=stem(?)]" },
+ { AS_SEARCH_TOKEN_MATCH_NAME, "name[text()~=stem(?)]" },
+ { AS_SEARCH_TOKEN_MATCH_KEYWORD, "keywords/keyword[text()~=stem(?)]" },
+ { AS_SEARCH_TOKEN_MATCH_ID, "id[text()~=stem(?)]" },
+ { AS_SEARCH_TOKEN_MATCH_ID, "launchable[text()~=stem(?)]" },
+ { AS_SEARCH_TOKEN_MATCH_ORIGIN, "../components[@origin~=stem(?)]" },
+ { AS_SEARCH_TOKEN_MATCH_NONE, NULL }
+ };
+
+ return gs_appstream_do_search (plugin, silo, values, queries, list, cancellable, error);
+}
+
+gboolean
+gs_appstream_search_other_apps (GsPlugin *plugin,
+ XbSilo *silo,
+ const gchar * const *values,
+ GsAppList *list,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const Query queries[] = {
+ { AS_SEARCH_TOKEN_MATCH_PKGNAME, "developer_name[text()~=stem(?)]" },
+ { AS_SEARCH_TOKEN_MATCH_SUMMARY, "project_group[text()~=stem(?)]" },
+ { AS_SEARCH_TOKEN_MATCH_NONE, NULL }
+ };
+
+ return gs_appstream_do_search (plugin, silo, values, queries, list, cancellable, error);
+}
+
gboolean
gs_appstream_add_category_apps (XbSilo *silo,
GsCategory *category,
diff --git a/lib/gs-appstream.h b/lib/gs-appstream.h
index fbc68a1a4..1264a07b2 100644
--- a/lib/gs-appstream.h
+++ b/lib/gs-appstream.h
@@ -29,6 +29,12 @@ gboolean gs_appstream_search (GsPlugin *plugin,
GsAppList *list,
GCancellable *cancellable,
GError **error);
+gboolean gs_appstream_search_other_apps (GsPlugin *plugin,
+ XbSilo *silo,
+ const gchar * const *values,
+ GsAppList *list,
+ GCancellable *cancellable,
+ GError **error);
gboolean gs_appstream_add_categories (XbSilo *silo,
GPtrArray *list,
GCancellable *cancellable,
diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c
index 6c72bb022..c40db1294 100644
--- a/lib/gs-plugin-loader.c
+++ b/lib/gs-plugin-loader.c
@@ -679,6 +679,7 @@ gs_plugin_loader_call_vfunc (GsPluginLoaderHelper *helper,
break;
case GS_PLUGIN_ACTION_SEARCH_FILES:
case GS_PLUGIN_ACTION_SEARCH_PROVIDES:
+ case GS_PLUGIN_ACTION_SEARCH_OTHER_APPS:
{
GsPluginSearchFunc plugin_func = func;
const gchar *search[2] = { gs_plugin_job_get_search (helper->plugin_job), NULL };
@@ -3202,6 +3203,7 @@ gs_plugin_loader_process_thread_cb (GTask *task,
case GS_PLUGIN_ACTION_LAUNCH:
case GS_PLUGIN_ACTION_REMOVE:
case GS_PLUGIN_ACTION_SEARCH:
+ case GS_PLUGIN_ACTION_SEARCH_OTHER_APPS:
case GS_PLUGIN_ACTION_UPDATE:
case GS_PLUGIN_ACTION_INSTALL_REPO:
case GS_PLUGIN_ACTION_REMOVE_REPO:
@@ -3383,6 +3385,7 @@ gs_plugin_loader_process_thread_cb (GTask *task,
gs_app_list_filter (list, gs_plugin_loader_app_is_valid_updatable, helper);
break;
case GS_PLUGIN_ACTION_GET_RECENT:
+ case GS_PLUGIN_ACTION_SEARCH_OTHER_APPS:
gs_app_list_filter (list, gs_plugin_loader_app_is_non_compulsory, NULL);
gs_app_list_filter (list, gs_plugin_loader_app_is_desktop, NULL);
gs_app_list_filter (list, gs_plugin_loader_app_is_valid_filter, helper);
@@ -3779,6 +3782,7 @@ gs_plugin_loader_job_process_async (GsPluginLoader *plugin_loader,
case GS_PLUGIN_ACTION_SEARCH:
case GS_PLUGIN_ACTION_SEARCH_FILES:
case GS_PLUGIN_ACTION_SEARCH_PROVIDES:
+ case GS_PLUGIN_ACTION_SEARCH_OTHER_APPS:
case GS_PLUGIN_ACTION_URL_TO_APP:
if (gs_plugin_job_get_search (plugin_job) == NULL) {
g_task_return_new_error (task,
@@ -3795,6 +3799,7 @@ gs_plugin_loader_job_process_async (GsPluginLoader *plugin_loader,
/* sorting fallbacks */
switch (action) {
case GS_PLUGIN_ACTION_SEARCH:
+ case GS_PLUGIN_ACTION_SEARCH_OTHER_APPS:
if (gs_plugin_job_get_sort_func (plugin_job, NULL) == NULL) {
gs_plugin_job_set_sort_func (plugin_job,
gs_plugin_loader_app_sort_match_value_cb, NULL);
@@ -3868,6 +3873,7 @@ gs_plugin_loader_job_process_async (GsPluginLoader *plugin_loader,
case GS_PLUGIN_ACTION_SEARCH:
case GS_PLUGIN_ACTION_SEARCH_FILES:
case GS_PLUGIN_ACTION_SEARCH_PROVIDES:
+ case GS_PLUGIN_ACTION_SEARCH_OTHER_APPS:
if (gs_plugin_job_get_timeout (plugin_job) > 0) {
helper->timeout_id =
g_timeout_add_seconds (gs_plugin_job_get_timeout (plugin_job),
diff --git a/lib/gs-plugin-types.h b/lib/gs-plugin-types.h
index 5723dec72..10debd1b4 100644
--- a/lib/gs-plugin-types.h
+++ b/lib/gs-plugin-types.h
@@ -261,6 +261,7 @@ typedef enum {
* @GS_PLUGIN_ACTION_REMOVE_REPO: Remove a repository (Since: 41)
* @GS_PLUGIN_ACTION_ENABLE_REPO: Enable a repository (Since: 41)
* @GS_PLUGIN_ACTION_DISABLE_REPO: Disable a repository (Since: 41)
+ * @GS_PLUGIN_ACTION_SEARCH_OTHER_APPS: Get the search results for a developer (Since: 43)
*
* The plugin action.
**/
@@ -293,6 +294,7 @@ typedef enum {
GS_PLUGIN_ACTION_REMOVE_REPO,
GS_PLUGIN_ACTION_ENABLE_REPO,
GS_PLUGIN_ACTION_DISABLE_REPO,
+ GS_PLUGIN_ACTION_SEARCH_OTHER_APPS,
GS_PLUGIN_ACTION_LAST /*< skip >*/
} GsPluginAction;
diff --git a/lib/gs-plugin-vfuncs.h b/lib/gs-plugin-vfuncs.h
index 83364376c..9f38d6145 100644
--- a/lib/gs-plugin-vfuncs.h
+++ b/lib/gs-plugin-vfuncs.h
@@ -125,6 +125,27 @@ gboolean gs_plugin_add_search_what_provides (GsPlugin *plugin,
GCancellable *cancellable,
GError **error);
+/**
+ * gs_plugin_add_search_other_apps:
+ * @plugin: a #GsPlugin
+ * @values: a NULL terminated list of search terms, e.g. [ "gnome" ]
+ * @list: a #GsAppList
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Called when searching for applications provided by certain developer.
+ *
+ * Plugins are expected to add new apps using gs_app_list_add().
+ *
+ * Returns: %TRUE for success or if not relevant
+ *
+ * Since: 43
+ **/
+gboolean gs_plugin_add_search_other_apps (GsPlugin *plugin,
+ gchar **values,
+ GsAppList *list,
+ GCancellable *cancellable,
+ GError **error);
/**
* gs_plugin_add_alternates
* @plugin: a #GsPlugin
diff --git a/lib/gs-plugin.c b/lib/gs-plugin.c
index b34bbd8dc..92e39bdc7 100644
--- a/lib/gs-plugin.c
+++ b/lib/gs-plugin.c
@@ -1444,6 +1444,8 @@ gs_plugin_action_to_function_name (GsPluginAction action)
return "gs_plugin_add_search_files";
if (action == GS_PLUGIN_ACTION_SEARCH_PROVIDES)
return "gs_plugin_add_search_what_provides";
+ if (action == GS_PLUGIN_ACTION_SEARCH_OTHER_APPS)
+ return "gs_plugin_add_search_other_apps";
if (action == GS_PLUGIN_ACTION_GET_CATEGORY_APPS)
return "gs_plugin_add_category_apps";
if (action == GS_PLUGIN_ACTION_GET_CATEGORIES)
@@ -1506,6 +1508,8 @@ gs_plugin_action_to_string (GsPluginAction action)
return "search-files";
if (action == GS_PLUGIN_ACTION_SEARCH_PROVIDES)
return "search-provides";
+ if (action == GS_PLUGIN_ACTION_SEARCH_OTHER_APPS)
+ return "search-other-apps";
if (action == GS_PLUGIN_ACTION_GET_CATEGORIES)
return "get-categories";
if (action == GS_PLUGIN_ACTION_GET_CATEGORY_APPS)
@@ -1576,6 +1580,8 @@ gs_plugin_action_from_string (const gchar *action)
return GS_PLUGIN_ACTION_SEARCH_FILES;
if (g_strcmp0 (action, "search-provides") == 0)
return GS_PLUGIN_ACTION_SEARCH_PROVIDES;
+ if (g_strcmp0 (action, "search-other-apps") == 0)
+ return GS_PLUGIN_ACTION_SEARCH_OTHER_APPS;
if (g_strcmp0 (action, "get-categories") == 0)
return GS_PLUGIN_ACTION_GET_CATEGORIES;
if (g_strcmp0 (action, "get-category-apps") == 0)
diff --git a/plugins/core/gs-plugin-appstream.c b/plugins/core/gs-plugin-appstream.c
index 60e206ff9..ae1fa6f3c 100644
--- a/plugins/core/gs-plugin-appstream.c
+++ b/plugins/core/gs-plugin-appstream.c
@@ -1311,6 +1311,28 @@ gs_plugin_add_search (GsPlugin *plugin,
error);
}
+gboolean
+gs_plugin_add_search_other_apps (GsPlugin *plugin,
+ gchar **values,
+ GsAppList *list,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (plugin);
+ g_autoptr(GRWLockReaderLocker) locker = NULL;
+
+ if (!gs_plugin_appstream_check_silo (self, cancellable, error))
+ return FALSE;
+
+ locker = g_rw_lock_reader_locker_new (&self->silo_lock);
+ return gs_appstream_search_other_apps (plugin,
+ self->silo,
+ (const gchar * const *) values,
+ list,
+ cancellable,
+ error);
+}
+
static void list_installed_apps_thread_cb (GTask *task,
gpointer source_object,
gpointer task_data,
diff --git a/plugins/flatpak/gs-flatpak.c b/plugins/flatpak/gs-flatpak.c
index 3b3b641fa..a27de5abc 100644
--- a/plugins/flatpak/gs-flatpak.c
+++ b/plugins/flatpak/gs-flatpak.c
@@ -3949,6 +3949,80 @@ gs_flatpak_search (GsFlatpak *self,
return TRUE;
}
+gboolean
+gs_flatpak_search_other_apps (GsFlatpak *self,
+ const gchar * const *values,
+ GsAppList *list,
+ gboolean interactive,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GsAppList) list_tmp = gs_app_list_new ();
+ g_autoptr(GRWLockReaderLocker) locker = NULL;
+ g_autoptr(GMutexLocker) app_silo_locker = NULL;
+ g_autoptr(GPtrArray) silos_to_remove = g_ptr_array_new ();
+ GHashTableIter iter;
+ gpointer key, value;
+
+ if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error))
+ return FALSE;
+
+ locker = g_rw_lock_reader_locker_new (&self->silo_lock);
+ if (!gs_appstream_search_other_apps (self->plugin, self->silo, values, list_tmp,
+ cancellable, error))
+ return FALSE;
+
+ gs_flatpak_ensure_remote_title (self, interactive, cancellable);
+
+ gs_flatpak_claim_app_list (self, list_tmp, interactive);
+ gs_app_list_add_list (list, list_tmp);
+
+ /* Also search silos from installed apps which were missing from self->silo */
+ app_silo_locker = g_mutex_locker_new (&self->app_silos_mutex);
+ g_hash_table_iter_init (&iter, self->app_silos);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ g_autoptr(XbSilo) app_silo = g_object_ref (value);
+ g_autoptr(GsAppList) app_list_tmp = gs_app_list_new ();
+ const char *app_ref = (char *)key;
+ g_autoptr(FlatpakInstalledRef) installed_ref = NULL;
+ g_auto(GStrv) split = NULL;
+ FlatpakRefKind kind;
+
+ /* Ignore any silos of apps that have since been removed.
+ * FIXME: can we use self->installed_refs here? */
+ split = g_strsplit (app_ref, "/", -1);
+ g_assert (g_strv_length (split) == 4);
+ if (g_strcmp0 (split[0], "app") == 0)
+ kind = FLATPAK_REF_KIND_APP;
+ else
+ kind = FLATPAK_REF_KIND_RUNTIME;
+ installed_ref = flatpak_installation_get_installed_ref (gs_flatpak_get_installation (self,
interactive),
+ kind,
+ split[1],
+ split[2],
+ split[3],
+ NULL, NULL);
+ if (installed_ref == NULL) {
+ g_ptr_array_add (silos_to_remove, (gpointer) app_ref);
+ continue;
+ }
+
+ if (!gs_appstream_search (self->plugin, app_silo, values, app_list_tmp,
+ cancellable, error))
+ return FALSE;
+
+ gs_flatpak_claim_app_list (self, app_list_tmp, interactive);
+ gs_app_list_add_list (list, app_list_tmp);
+ }
+
+ for (guint i = 0; i < silos_to_remove->len; i++) {
+ const char *silo = g_ptr_array_index (silos_to_remove, i);
+ g_hash_table_remove (self->app_silos, silo);
+ }
+
+ return TRUE;
+}
+
gboolean
gs_flatpak_add_category_apps (GsFlatpak *self,
GsCategory *category,
diff --git a/plugins/flatpak/gs-flatpak.h b/plugins/flatpak/gs-flatpak.h
index 3c4724897..6d35c00a9 100644
--- a/plugins/flatpak/gs-flatpak.h
+++ b/plugins/flatpak/gs-flatpak.h
@@ -125,6 +125,12 @@ gboolean gs_flatpak_search (GsFlatpak *self,
gboolean interactive,
GCancellable *cancellable,
GError **error);
+gboolean gs_flatpak_search_other_apps (GsFlatpak *self,
+ const gchar * const *values,
+ GsAppList *list,
+ gboolean interactive,
+ GCancellable *cancellable,
+ GError **error);
gboolean gs_flatpak_add_categories (GsFlatpak *self,
GPtrArray *list,
gboolean interactive,
diff --git a/plugins/flatpak/gs-plugin-flatpak.c b/plugins/flatpak/gs-plugin-flatpak.c
index 15e60404d..131313380 100644
--- a/plugins/flatpak/gs-plugin-flatpak.c
+++ b/plugins/flatpak/gs-plugin-flatpak.c
@@ -1811,6 +1811,27 @@ gs_plugin_add_search_what_provides (GsPlugin *plugin,
return gs_plugin_flatpak_do_search (plugin, search, list, cancellable, error);
}
+gboolean
+gs_plugin_add_search_other_apps (GsPlugin *plugin,
+ gchar **values,
+ GsAppList *list,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GsPluginFlatpak *self = GS_PLUGIN_FLATPAK (plugin);
+ gboolean interactive = gs_plugin_has_flags (plugin, GS_PLUGIN_FLAGS_INTERACTIVE);
+
+ for (guint i = 0; i < self->installations->len; i++) {
+ GsFlatpak *flatpak = g_ptr_array_index (self->installations, i);
+ if (!gs_flatpak_search_other_apps (flatpak, (const gchar * const *) values, list,
+ interactive, cancellable, error)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
gboolean
gs_plugin_add_categories (GsPlugin *plugin,
GPtrArray *list,
diff --git a/src/gs-details-page.c b/src/gs-details-page.c
index 9c539482c..9d69428e7 100644
--- a/src/gs-details-page.c
+++ b/src/gs-details-page.c
@@ -32,6 +32,7 @@
#include "gs-progress-button.h"
#include "gs-screenshot-carousel.h"
#include "gs-star-widget.h"
+#include "gs-summary-tile.h"
#include "gs-review-histogram.h"
#include "gs-review-dialog.h"
#include "gs-review-row.h"
@@ -147,6 +148,9 @@ struct _GsDetailsPage
GsLicenseTile *license_tile;
GtkInfoBar *translation_infobar;
GtkButton *translation_infobar_button;
+ GtkWidget *other_apps_heading;
+ GtkWidget *box_other_apps;
+ gchar *last_developer_name;
};
G_DEFINE_TYPE (GsDetailsPage, gs_details_page, GS_TYPE_PAGE)
@@ -944,6 +948,55 @@ update_action_row_from_link (AdwActionRow *row,
return (url != NULL);
}
+static void
+gs_details_page_app_tile_clicked (GsAppTile *tile,
+ gpointer user_data)
+{
+ GsDetailsPage *self = GS_DETAILS_PAGE (user_data);
+ GsApp *app;
+
+ app = gs_app_tile_get_app (tile);
+ gs_details_page_set_app (self, app);
+}
+
+static void
+gs_details_page_search_other_apps_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GsDetailsPage *self = GS_DETAILS_PAGE (user_data);
+ g_autoptr(GsAppList) list = NULL;
+ g_autoptr(GError) local_error = NULL;
+ gboolean any_added = FALSE;
+
+ list = gs_plugin_loader_job_process_finish (GS_PLUGIN_LOADER (source_object), result, &local_error);
+ if (list == NULL) {
+ if (g_error_matches (local_error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED) ||
+ g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_debug ("search cancelled");
+ return;
+ }
+ g_warning ("failed to get other apps: %s", local_error->message);
+ return;
+ }
+
+ if (!self->app || !gs_page_is_active (GS_PAGE (self)))
+ return;
+
+ for (guint i = 0; i < gs_app_list_length (list); i++) {
+ GsApp *app = gs_app_list_index (list, i);
+ if (g_strcmp0 (gs_app_get_id (app), gs_app_get_id (self->app)) != 0) {
+ GtkWidget *tile = gs_summary_tile_new (app);
+ g_signal_connect (tile, "clicked", G_CALLBACK (gs_details_page_app_tile_clicked),
self);
+ gtk_flow_box_insert (GTK_FLOW_BOX (self->box_other_apps), tile, -1);
+
+ any_added = TRUE;
+ }
+ }
+
+ gtk_widget_set_visible (self->box_other_apps, any_added);
+}
+
static void
gs_details_page_refresh_all (GsDetailsPage *self)
{
@@ -1024,8 +1077,49 @@ gs_details_page_refresh_all (GsDetailsPage *self)
tmp = gs_app_get_developer_name (self->app);
if (tmp == NULL)
tmp = gs_app_get_project_group (self->app);
- if (tmp != NULL)
+ if (tmp != NULL) {
gtk_label_set_label (GTK_LABEL (self->developer_name_label), tmp);
+
+ if (g_strcmp0 (tmp, self->last_developer_name) != 0) {
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+ g_autofree gchar *heading = NULL;
+
+ /* Hide the section, it will be shown only if any other app had been found */
+ gtk_widget_set_visible (self->box_other_apps, FALSE);
+
+ g_clear_pointer (&self->last_developer_name, g_free);
+ self->last_developer_name = g_strdup (tmp);
+
+ /* Translators: the '%s' is replaced with a developer name or a project group */
+ heading = g_strdup_printf (_("Other Apps by %s"), self->last_developer_name);
+ gtk_label_set_label (GTK_LABEL (self->other_apps_heading), heading);
+ gs_widget_remove_all (self->box_other_apps, (GsRemoveFunc) gtk_flow_box_remove);
+
+ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_SEARCH_OTHER_APPS,
+ "search", self->last_developer_name,
+ "max-results", 20,
+ "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON |
+
GS_PLUGIN_REFINE_FLAGS_REQUIRE_VERSION |
+
GS_PLUGIN_REFINE_FLAGS_REQUIRE_HISTORY |
+
GS_PLUGIN_REFINE_FLAGS_REQUIRE_SETUP_ACTION |
+
GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEW_RATINGS |
+
GS_PLUGIN_REFINE_FLAGS_REQUIRE_DESCRIPTION |
+
GS_PLUGIN_REFINE_FLAGS_REQUIRE_LICENSE |
+
GS_PLUGIN_REFINE_FLAGS_REQUIRE_PERMISSIONS |
+
GS_PLUGIN_REFINE_FLAGS_REQUIRE_RATING,
+ "dedupe-flags",
GS_APP_LIST_FILTER_FLAG_KEY_ID_PROVIDES,
+ NULL);
+ gs_plugin_loader_job_process_async (self->plugin_loader, plugin_job,
+ self->cancellable,
+ gs_details_page_search_other_apps_cb,
+ self);
+ }
+ } else if (tmp == NULL) {
+ g_clear_pointer (&self->last_developer_name, g_free);
+ gs_widget_remove_all (self->box_other_apps, (GsRemoveFunc) gtk_flow_box_remove);
+ gtk_widget_set_visible (self->box_other_apps, FALSE);
+ }
+
gtk_widget_set_visible (GTK_WIDGET (self->developer_name_label), tmp != NULL);
gtk_widget_set_visible (GTK_WIDGET (self->developer_verified_image), gs_app_has_quirk (self->app,
GS_APP_QUIRK_DEVELOPER_VERIFIED));
@@ -2145,6 +2239,7 @@ gs_details_page_dispose (GObject *object)
g_clear_object (&self->size_group_origin_popover);
g_clear_object (&self->odrs_provider);
g_clear_object (&self->app_info_monitor);
+ g_clear_pointer (&self->last_developer_name, g_free);
G_OBJECT_CLASS (gs_details_page_parent_class)->dispose (object);
}
@@ -2273,6 +2368,8 @@ gs_details_page_class_init (GsDetailsPageClass *klass)
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, license_tile);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, translation_infobar);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, translation_infobar_button);
+ gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, other_apps_heading);
+ gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, box_other_apps);
gtk_widget_class_bind_template_callback (widget_class, gs_details_page_link_row_activated_cb);
gtk_widget_class_bind_template_callback (widget_class,
gs_details_page_license_tile_get_involved_activated_cb);
diff --git a/src/gs-details-page.ui b/src/gs-details-page.ui
index cda8392a1..5fb88fd0e 100644
--- a/src/gs-details-page.ui
+++ b/src/gs-details-page.ui
@@ -899,6 +899,48 @@
</child>
</object>
</child>
+ <child>
+ <object class="AdwClamp">
+ <property name="visible" bind-source="box_other_apps"
bind-property="visible" bind-flags="sync-create"/>
+ <property name="maximum-size">860</property>
+ <!-- ~⅔ of the maximum size. -->
+ <property name="tightening-threshold">576</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <child>
+ <object class="GtkBox">
+ <property name="halign">center</property>
+ <property name="hexpand">False</property>
+ <property name="orientation">vertical</property>
+ <property name="valign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="other_apps_heading">
+ <property name="xalign">0</property>
+ <!-- the label is set in the code -->
+ <property name="label">Other Apps by ...</property>
+ <property name="margin-top">21</property>
+ <property name="margin-bottom">6</property>
+ <style>
+ <class name="heading"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFlowBox" id="box_other_apps">
+ <property name="homogeneous">True</property>
+ <property name="column-spacing">14</property>
+ <property name="row-spacing">14</property>
+ <property name="valign">start</property>
+ <accessibility>
+ <relation name="labelled-by">other_apps_heading</relation>
+ </accessibility>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
</object>
</child>
</object>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]