/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * vi:set noexpandtab tabstop=8 shiftwidth=8: * * Copyright (C) 2013 Matthias Clasen * Copyright (C) 2019 Richard Hughes * * SPDX-License-Identifier: GPL-2.0+ */ #include "config.h" #include #include "gs-summary-tile.h" #include "gs-layout-manager.h" #include "gs-common.h" #define GS_TYPE_SUMMARY_TILE_LAYOUT (gs_summary_tile_layout_get_type ()) G_DECLARE_FINAL_TYPE (GsSummaryTileLayout, gs_summary_tile_layout, GS, SUMMARY_TILE_LAYOUT, GsLayoutManager) struct _GsSummaryTileLayout { GsLayoutManager parent_instance; gint preferred_width; }; G_DEFINE_TYPE (GsSummaryTileLayout, gs_summary_tile_layout, GS_TYPE_LAYOUT_MANAGER) static void gs_summary_tile_layout_measure (GtkLayoutManager *layout_manager, GtkWidget *widget, GtkOrientation orientation, gint for_size, gint *minimum, gint *natural, gint *minimum_baseline, gint *natural_baseline) { GsSummaryTileLayout *self = GS_SUMMARY_TILE_LAYOUT (layout_manager); GTK_LAYOUT_MANAGER_CLASS (gs_summary_tile_layout_parent_class)->measure (layout_manager, widget, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline); /* Limit the natural width */ if (self->preferred_width > 0 && orientation == GTK_ORIENTATION_HORIZONTAL) *natural = MAX (*minimum, self->preferred_width); } static void gs_summary_tile_layout_class_init (GsSummaryTileLayoutClass *klass) { GtkLayoutManagerClass *layout_manager_class = GTK_LAYOUT_MANAGER_CLASS (klass); layout_manager_class->measure = gs_summary_tile_layout_measure; } static void gs_summary_tile_layout_init (GsSummaryTileLayout *self) { } /* ********************************************************************* */ struct _GsSummaryTile { GsAppTile parent_instance; GtkWidget *image; GtkWidget *name; GtkWidget *summary; GtkWidget *bin; GtkWidget *stack; gint preferred_width; }; G_DEFINE_TYPE (GsSummaryTile, gs_summary_tile, GS_TYPE_APP_TILE) typedef enum { PROP_PREFERRED_WIDTH = 1, } GsSummaryTileProperty; static GParamSpec *obj_props[PROP_PREFERRED_WIDTH + 1] = { NULL, }; static void gs_summary_tile_refresh (GsAppTile *self) { GsSummaryTile *tile = GS_SUMMARY_TILE (self); GsApp *app = gs_app_tile_get_app (self); g_autoptr(GIcon) icon = NULL; gboolean installed; g_autofree gchar *name = NULL; const gchar *summary; if (app == NULL) return; gtk_image_set_pixel_size (GTK_IMAGE (tile->image), 64); gtk_stack_set_visible_child_name (GTK_STACK (tile->stack), "content"); /* set name */ gtk_label_set_label (GTK_LABEL (tile->name), gs_app_get_name (app)); summary = gs_app_get_summary (app); gtk_label_set_label (GTK_LABEL (tile->summary), summary); gtk_widget_set_visible (tile->summary, summary && summary[0]); icon = gs_app_get_icon_for_size (app, gtk_image_get_pixel_size (GTK_IMAGE (tile->image)), gtk_widget_get_scale_factor (tile->image), "system-component-application"); gtk_image_set_from_gicon (GTK_IMAGE (tile->image), icon); switch (gs_app_get_state (app)) { case GS_APP_STATE_INSTALLED: case GS_APP_STATE_UPDATABLE: case GS_APP_STATE_UPDATABLE_LIVE: installed = TRUE; name = g_strdup_printf (_("%s (Installed)"), gs_app_get_name (app)); break; case GS_APP_STATE_INSTALLING: installed = FALSE; name = g_strdup_printf (_("%s (Installing)"), gs_app_get_name (app)); break; case GS_APP_STATE_REMOVING: installed = TRUE; name = g_strdup_printf (_("%s (Removing)"), gs_app_get_name (app)); break; case GS_APP_STATE_QUEUED_FOR_INSTALL: case GS_APP_STATE_AVAILABLE: default: installed = FALSE; name = g_strdup (gs_app_get_name (app)); break; } gtk_widget_set_visible (tile->bin, installed); if (name != NULL) { gtk_accessible_update_property (GTK_ACCESSIBLE (tile), GTK_ACCESSIBLE_PROPERTY_LABEL, name, GTK_ACCESSIBLE_PROPERTY_DESCRIPTION, gs_app_get_summary (app), -1); } } static void gs_summary_tile_init (GsSummaryTile *tile) { tile->preferred_width = -1; gtk_widget_init_template (GTK_WIDGET (tile)); } static void gs_summary_tile_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GsSummaryTile *app_tile = GS_SUMMARY_TILE (object); switch ((GsSummaryTileProperty) prop_id) { case PROP_PREFERRED_WIDTH: g_value_set_int (value, app_tile->preferred_width); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gs_summary_tile_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GsSummaryTile *app_tile = GS_SUMMARY_TILE (object); GtkLayoutManager *layout_manager; switch ((GsSummaryTileProperty) prop_id) { case PROP_PREFERRED_WIDTH: app_tile->preferred_width = g_value_get_int (value); layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (app_tile)); GS_SUMMARY_TILE_LAYOUT (layout_manager)->preferred_width = app_tile->preferred_width; gtk_layout_manager_layout_changed (layout_manager); g_object_notify_by_pspec (object, obj_props[PROP_PREFERRED_WIDTH]); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gs_summary_tile_class_init (GsSummaryTileClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GsAppTileClass *tile_class = GS_APP_TILE_CLASS (klass); object_class->get_property = gs_summary_tile_get_property; object_class->set_property = gs_summary_tile_set_property; tile_class->refresh = gs_summary_tile_refresh; /** * GsAppTile:preferred-width: * * The only purpose of this property is to be retrieved as the * natural width by gtk_widget_get_preferred_width() fooling the * parent #GtkFlowBox container and making it switch to more columns * (children per row) if it is able to place n+1 children in a row * having this specified width. If this value is less than a minimum * width of this app tile then the minimum is returned instead. Set * this property to -1 to turn off this feature and return the default * natural width instead. */ obj_props[PROP_PREFERRED_WIDTH] = g_param_spec_int ("preferred-width", "Preferred width", "The preferred width of this widget, its only purpose is to trick the parent container", -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, G_N_ELEMENTS (obj_props), obj_props); gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-summary-tile.ui"); gtk_widget_class_set_layout_manager_type (widget_class, GS_TYPE_SUMMARY_TILE_LAYOUT); /* Override the 'button' class name, to be able to turn off hover states */ gtk_widget_class_set_css_name (widget_class, "gs-summary-tile"); gtk_widget_class_bind_template_child (widget_class, GsSummaryTile, image); gtk_widget_class_bind_template_child (widget_class, GsSummaryTile, name); gtk_widget_class_bind_template_child (widget_class, GsSummaryTile, summary); gtk_widget_class_bind_template_child (widget_class, GsSummaryTile, bin); gtk_widget_class_bind_template_child (widget_class, GsSummaryTile, stack); } GtkWidget * gs_summary_tile_new (GsApp *app) { return g_object_new (GS_TYPE_SUMMARY_TILE, "app", app, NULL); }