[gnome-software] Responsive scaling in application categories
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software] Responsive scaling in application categories
- Date: Wed, 27 Jan 2016 10:54:53 +0000 (UTC)
commit a8e93c9caa8c481c7fe05f5979dbdd39587e4ddf
Author: Rafal Luzynski <digitalfreak lingonborough com>
Date: Fri Jan 15 02:06:18 2016 +0100
Responsive scaling in application categories
Automatically switch between 2 and 3 columns depending on the
window size. Choose 3 columns when there is enough space to allocate
at least 270px for each application tile. Tile width limited to 420px.
Implemented with GtkFlowBox, GsFixedSizeBin and some more hacks.
https://bugzilla.gnome.org/show_bug.cgi?id=758669
Signed-off-by: Richard Hughes <richard hughsie com>
src/app-tile.ui | 2 +
src/gs-app-tile.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++
src/gs-shell-category.c | 47 ++++++---------------
src/gs-shell-category.ui | 34 ++++++++++------
4 files changed, 136 insertions(+), 46 deletions(-)
---
diff --git a/src/app-tile.ui b/src/app-tile.ui
index 9914c49..dfbfbcc 100644
--- a/src/app-tile.ui
+++ b/src/app-tile.ui
@@ -4,6 +4,8 @@
<template class="GsAppTile" parent="GtkButton">
<property name="visible">True</property>
<property name="hexpand">True</property>
+ <!-- This is the minimum (sic!) width of a tile when the GtkFlowBox parent container switches to 3
columns -->
+ <property name="preferred-width">270</property>
<style>
<class name="view"/>
<class name="tile"/>
diff --git a/src/gs-app-tile.c b/src/gs-app-tile.c
index faa45d7..9e01f76 100644
--- a/src/gs-app-tile.c
+++ b/src/gs-app-tile.c
@@ -39,10 +39,16 @@ struct _GsAppTile
GtkWidget *eventbox;
GtkWidget *stack;
GtkWidget *stars;
+ gint preferred_width;
};
G_DEFINE_TYPE (GsAppTile, gs_app_tile, GTK_TYPE_BUTTON)
+enum {
+ PROP_0,
+ PROP_PREFERRED_WIDTH
+};
+
GsApp *
gs_app_tile_get_app (GsAppTile *tile)
{
@@ -173,16 +179,109 @@ static void
gs_app_tile_init (GsAppTile *tile)
{
gtk_widget_set_has_window (GTK_WIDGET (tile), FALSE);
+ tile->preferred_width = -1;
gtk_widget_init_template (GTK_WIDGET (tile));
gs_star_widget_set_icon_size (GS_STAR_WIDGET (tile->stars), 12);
}
static void
+gs_app_tile_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GsAppTile *app_tile = GS_APP_TILE (object);
+
+ switch (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_app_tile_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GsAppTile *app_tile = GS_APP_TILE (object);
+
+ switch (prop_id) {
+ case PROP_PREFERRED_WIDTH:
+ app_tile->preferred_width = g_value_get_int (value);
+ gtk_widget_queue_resize (GTK_WIDGET (app_tile));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_app_get_preferred_width (GtkWidget *widget,
+ gint *min, gint *nat)
+{
+#if GTK_CHECK_VERSION(3,20,0)
+ gint m;
+#else
+ gint m, n;
+#endif
+ GsAppTile *app_tile = GS_APP_TILE (widget);
+
+ if (app_tile->preferred_width < 0) {
+ /* Just retrieve the default values */
+ GTK_WIDGET_CLASS (gs_app_tile_parent_class)->get_preferred_width (widget, min, nat);
+ return;
+ }
+
+/* It's because of some bugs in gtkbutton.c 3.18 and before.
+ * We can remove this when we branch for 3.20 *and* require GTK 3.20. */
+#if GTK_CHECK_VERSION(3,20,0)
+ GTK_WIDGET_CLASS (gs_app_tile_parent_class)->get_preferred_width (widget, &m, NULL);
+#else
+ GTK_WIDGET_CLASS (gs_app_tile_parent_class)->get_preferred_width (widget, &m, &n);
+#endif
+
+ if (min != NULL)
+ *min = m;
+ if (nat != NULL)
+ *nat = MAX (m, app_tile->preferred_width);
+}
+
+static void
gs_app_tile_class_init (GsAppTileClass *klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ object_class->get_property = gs_app_tile_get_property;
+ object_class->set_property = gs_app_tile_set_property;
+
widget_class->destroy = gs_app_tile_destroy;
+ widget_class->get_preferred_width = gs_app_get_preferred_width;
+
+ /**
+ * 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.
+ */
+ g_object_class_install_property (object_class, 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));
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/app-tile.ui");
diff --git a/src/gs-shell-category.c b/src/gs-shell-category.c
index 908bfd5..71ecb3b 100644
--- a/src/gs-shell-category.c
+++ b/src/gs-shell-category.c
@@ -38,10 +38,8 @@ struct _GsShellCategory
GCancellable *cancellable;
GsShell *shell;
GsCategory *category;
- GtkWidget *col0_placeholder;
- GtkWidget *col1_placeholder;
- GtkWidget *category_detail_grid;
+ GtkWidget *category_detail_box;
GtkWidget *listbox_filter;
GtkWidget *scrolledwindow_category;
GtkWidget *scrolledwindow_filter;
@@ -95,8 +93,7 @@ gs_shell_category_get_apps_cb (GObject *source_object,
g_autoptr(GsAppList) list = NULL;
/* show an empty space for no results */
- gtk_grid_remove_column (GTK_GRID (self->category_detail_grid), 1);
- gtk_grid_remove_column (GTK_GRID (self->category_detail_grid), 0);
+ gs_container_remove_all (GTK_CONTAINER (self->category_detail_box));
list = gs_plugin_loader_get_category_apps_finish (plugin_loader,
res,
@@ -112,18 +109,16 @@ gs_shell_category_get_apps_cb (GObject *source_object,
tile = gs_app_tile_new (app);
g_signal_connect (tile, "clicked",
G_CALLBACK (app_tile_clicked), self);
- gtk_grid_attach (GTK_GRID (self->category_detail_grid), tile, (i % 2), i / 2, 1, 1);
+ gtk_container_add (GTK_CONTAINER (self->category_detail_box), tile);
+ gtk_widget_set_can_focus (gtk_widget_get_parent (tile), FALSE);
}
-
- if (i == 1)
- gtk_grid_attach (GTK_GRID (self->category_detail_grid), self->col1_placeholder, 1, 0, 1, 1);
}
static void
gs_shell_category_populate_filtered (GsShellCategory *self, GsCategory *subcategory)
{
GtkWidget *tile;
- guint i;
+ guint i, count;
g_assert (subcategory != NULL);
@@ -137,17 +132,14 @@ gs_shell_category_populate_filtered (GsShellCategory *self, GsCategory *subcateg
gs_category_get_id (self->category),
gs_category_get_id (subcategory));
- gtk_grid_remove_column (GTK_GRID (self->category_detail_grid), 1);
- gtk_grid_remove_column (GTK_GRID (self->category_detail_grid), 0);
-
- for (i = 0; i < MIN (30, gs_category_get_size (subcategory)); i++) {
+ gs_container_remove_all (GTK_CONTAINER (self->category_detail_box));
+ count = MIN(30, gs_category_get_size (subcategory));
+ for (i = 0; i < count; i++) {
tile = gs_app_tile_new (NULL);
- gtk_grid_attach (GTK_GRID (self->category_detail_grid), tile, (i % 2), i / 2, 1, 1);
+ gtk_container_add (GTK_CONTAINER (self->category_detail_box), tile);
+ gtk_widget_set_can_focus (gtk_widget_get_parent (tile), FALSE);
}
- gtk_grid_attach (GTK_GRID (self->category_detail_grid), self->col0_placeholder, 0, 0, 1, 1);
- gtk_grid_attach (GTK_GRID (self->category_detail_grid), self->col1_placeholder, 1, 0, 1, 1);
-
gs_plugin_loader_get_category_apps_async (self->plugin_loader,
subcategory,
GS_PLUGIN_REFINE_FLAGS_DEFAULT |
@@ -181,15 +173,12 @@ gs_shell_category_create_filter_list (GsShellCategory *self,
GsCategory *s;
g_autoptr(GList) list = NULL;
- gs_container_remove_all (GTK_CONTAINER (self->category_detail_grid));
+ gs_container_remove_all (GTK_CONTAINER (self->category_detail_box));
list = gs_category_get_subcategories (category);
if (!list)
return;
- gtk_grid_attach (GTK_GRID (self->category_detail_grid), self->col0_placeholder, 0, 0, 1, 1);
- gtk_grid_attach (GTK_GRID (self->category_detail_grid), self->col1_placeholder, 1, 0, 1, 1);
-
gs_container_remove_all (GTK_CONTAINER (self->listbox_filter));
for (l = list; l; l = l->next) {
@@ -250,12 +239,6 @@ static void
gs_shell_category_init (GsShellCategory *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
-
- self->col0_placeholder = g_object_ref_sink (gtk_label_new (""));
- self->col1_placeholder = g_object_ref_sink (gtk_label_new (""));
-
- gtk_widget_show (self->col0_placeholder);
- gtk_widget_show (self->col1_placeholder);
}
static void
@@ -271,8 +254,6 @@ gs_shell_category_dispose (GObject *object)
g_clear_object (&self->builder);
g_clear_object (&self->category);
g_clear_object (&self->plugin_loader);
- g_clear_object (&self->col0_placeholder);
- g_clear_object (&self->col1_placeholder);
G_OBJECT_CLASS (gs_shell_category_parent_class)->dispose (object);
}
@@ -287,7 +268,7 @@ gs_shell_category_class_init (GsShellCategoryClass *klass)
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/Software/gs-shell-category.ui");
- gtk_widget_class_bind_template_child (widget_class, GsShellCategory, category_detail_grid);
+ gtk_widget_class_bind_template_child (widget_class, GsShellCategory, category_detail_box);
gtk_widget_class_bind_template_child (widget_class, GsShellCategory, listbox_filter);
gtk_widget_class_bind_template_child (widget_class, GsShellCategory, scrolledwindow_category);
gtk_widget_class_bind_template_child (widget_class, GsShellCategory, scrolledwindow_filter);
@@ -312,7 +293,7 @@ key_event (GtkWidget *listbox, GdkEvent *event, GsShellCategory *self)
GTK_SCROLL_PAGE_DOWN, FALSE, &handled);
else if (keyval == GDK_KEY_Tab ||
keyval == GDK_KEY_KP_Tab)
- gtk_widget_child_focus (self->category_detail_grid, GTK_DIR_TAB_FORWARD);
+ gtk_widget_child_focus (self->category_detail_box, GTK_DIR_TAB_FORWARD);
else
return FALSE;
@@ -335,7 +316,7 @@ gs_shell_category_setup (GsShellCategory *self,
g_signal_connect (self->listbox_filter, "row-selected", G_CALLBACK (filter_selected), self);
adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (self->scrolledwindow_category));
- gtk_container_set_focus_vadjustment (GTK_CONTAINER (self->category_detail_grid), adj);
+ gtk_container_set_focus_vadjustment (GTK_CONTAINER (self->category_detail_box), adj);
g_signal_connect (self->listbox_filter, "key-press-event",
G_CALLBACK (key_event), self);
diff --git a/src/gs-shell-category.ui b/src/gs-shell-category.ui
index 9263601..67c96a1 100644
--- a/src/gs-shell-category.ui
+++ b/src/gs-shell-category.ui
@@ -46,20 +46,28 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
- <object class="GtkGrid" id="category_detail_grid">
- <property name="margin_start">27</property>
- <property name="margin_end">27</property>
- <property name="margin_top">24</property>
- <property name="margin_bottom">24</property>
- <property name="halign">fill</property>
+ <object class="GsFixedSizeBin" id="gs_fixed_bin">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="row_spacing">12</property>
- <property name="column_spacing">12</property>
- <property name="row_homogeneous">True</property>
- <property name="column_homogeneous">True</property>
- <property name="hexpand">True</property>
- <property name="valign">start</property>
+ <!-- This is 3*420 plus margins, paddings, CSS borders -->
+ <property name="preferred-width">1338</property>
+ <child>
+ <object class="GtkFlowBox" id="category_detail_box">
+ <property name="margin_start">24</property>
+ <property name="margin_end">24</property>
+ <property name="margin_top">21</property>
+ <property name="margin_bottom">21</property>
+ <property name="halign">fill</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">6</property>
+ <property name="homogeneous">True</property>
+ <property name="hexpand">True</property>
+ <property name="valign">start</property>
+ <property name="min-children-per-line">2</property>
+ <property name="max-children-per-line">3</property>
+ </object>
+ </child>
</object>
</child>
</object>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]