[gnome-software] Bring the install list closer to mockups
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software] Bring the install list closer to mockups
- Date: Thu, 22 Aug 2013 01:54:43 +0000 (UTC)
commit fabc44f83262f21d017003fd2ea81ae80a33ba70
Author: Matthias Clasen <mclasen redhat com>
Date: Wed Aug 21 21:37:34 2013 -0400
Bring the install list closer to mockups
This commit introduces somewhat tricky pango code to break
the description into three lines, and ellipsize the last one
sufficiently to fit a 'Read More' link in there. We also add
size groups to keep icons and names aligned.
src/gs-app-widget.c | 279 ++++++++++++++++++++++++++++++++++++---------------
src/gs-app-widget.h | 3 +
src/gs-main.c | 8 ++
3 files changed, 211 insertions(+), 79 deletions(-)
---
diff --git a/src/gs-app-widget.c b/src/gs-app-widget.c
index 970dd74..ee23b99 100644
--- a/src/gs-app-widget.c
+++ b/src/gs-app-widget.c
@@ -34,7 +34,10 @@ struct _GsAppWidgetPrivate
gchar *status;
GsAppWidgetKind kind;
GtkWidget *widget_button;
- GtkWidget *widget_description;
+ GtkWidget *widget_description1;
+ GtkWidget *widget_description2;
+ GtkWidget *widget_description3;
+ GtkWidget *widget_read_more;
GtkWidget *widget_image;
GtkWidget *widget_name;
GtkWidget *widget_spinner;
@@ -59,7 +62,6 @@ static guint signals [SIGNAL_LAST] = { 0 };
static void
gs_app_widget_refresh (GsAppWidget *app_widget)
{
- const gchar *tmp;
GsAppWidgetPrivate *priv = app_widget->priv;
GtkStyleContext *context;
@@ -68,16 +70,12 @@ gs_app_widget_refresh (GsAppWidget *app_widget)
gtk_label_set_label (GTK_LABEL (priv->widget_name),
gs_app_get_name (priv->app));
- tmp = gs_app_get_description (priv->app);
- if (tmp == NULL)
- tmp = _("The author of this software has not included a long description...");
- gtk_label_set_markup (GTK_LABEL (priv->widget_description), tmp);
gtk_label_set_label (GTK_LABEL (priv->widget_version),
gs_app_get_version (priv->app));
- gtk_image_set_from_pixbuf (GTK_IMAGE (priv->widget_image),
- gs_app_get_pixbuf (priv->app));
+ if (gs_app_get_pixbuf (priv->app))
+ gtk_image_set_from_pixbuf (GTK_IMAGE (priv->widget_image),
+ gs_app_get_pixbuf (priv->app));
gtk_widget_set_visible (priv->widget_name, TRUE);
- gtk_widget_set_visible (priv->widget_description, TRUE);
gtk_widget_set_visible (priv->widget_version, TRUE);
gtk_widget_set_visible (priv->widget_image, TRUE);
gtk_widget_set_visible (priv->widget_button, TRUE);
@@ -143,38 +141,38 @@ gs_app_widget_get_kind (GsAppWidget *app_widget)
static guint
_g_string_replace (GString *string, const gchar *search, const gchar *replace)
{
- gchar *tmp;
- guint cnt = 0;
- guint replace_len;
- guint search_len;
-
- search_len = strlen (search);
- replace_len = strlen (replace);
-
- do {
- tmp = g_strstr_len (string->str, -1, search);
- if (tmp == NULL)
- goto out;
-
- /* reallocate the string if required */
- if (search_len > replace_len) {
- g_string_erase (string,
- tmp - string->str,
- search_len - replace_len);
- }
- if (search_len < replace_len) {
- g_string_insert_len (string,
- tmp - string->str,
- search,
- replace_len - search_len);
- }
-
- /* just memcmp in the new string */
- memcpy (tmp, replace, replace_len);
- cnt++;
- } while (TRUE);
+ gchar *tmp;
+ guint cnt = 0;
+ guint replace_len;
+ guint search_len;
+
+ search_len = strlen (search);
+ replace_len = strlen (replace);
+
+ do {
+ tmp = g_strstr_len (string->str, -1, search);
+ if (tmp == NULL)
+ goto out;
+
+ /* reallocate the string if required */
+ if (search_len > replace_len) {
+ g_string_erase (string,
+ tmp - string->str,
+- search_len - replace_len);
+ }
+ if (search_len < replace_len) {
+ g_string_insert_len (string,
+ tmp - string->str,
+ search,
+ replace_len - search_len);
+ }
+
+ /* just memcmp in the new string */
+ memcpy (tmp, replace, replace_len);
+ cnt++;
+ } while (TRUE);
out:
- return cnt;
+ return cnt;
}
/**
@@ -268,6 +266,108 @@ gs_app_widget_button_clicked_cb (GtkWidget *widget, GsAppWidget *app_widget)
g_signal_emit (app_widget, signals[SIGNAL_BUTTON_CLICKED], 0);
}
+static void
+break_lines (PangoContext *context,
+ const gchar *text, const gchar *end,
+ gint width,
+ gchar **line1, gchar **line2, gchar **line3)
+{
+ PangoLayout *layout;
+ PangoLayoutIter *iter;
+ gchar *tmp;
+ gchar *p, *p2, *r;
+ gint i2 = 0, i3 = 0, i4 = 0;
+ gint lines;
+
+ layout = pango_layout_new (context);
+ pango_layout_set_width (layout, width * PANGO_SCALE);
+ pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
+
+ tmp = g_strconcat (text, end, NULL);
+ pango_layout_set_text (layout, tmp, -1);
+ g_free (tmp);
+
+ lines = pango_layout_get_line_count (layout);
+
+ iter = pango_layout_get_iter (layout);
+ if (pango_layout_iter_next_line (iter))
+ i2 = pango_layout_iter_get_index (iter);
+ else
+ goto out;
+ if (pango_layout_iter_next_line (iter))
+ i3 = pango_layout_iter_get_index (iter);
+ else
+ goto out;
+ if (pango_layout_iter_next_line (iter))
+ i4 = pango_layout_iter_get_index (iter);
+ else
+ goto out;
+ pango_layout_iter_free (iter);
+
+ p = (gchar *)text + i4;
+ while (text <= p && pango_layout_get_line_count (layout) > 3) {
+ p = g_utf8_prev_char (p);
+ r = g_strndup (text, p - text);
+ p2 = g_strconcat (r, "...", end, NULL);
+ pango_layout_set_text (layout, p2, -1);
+ g_free (p2);
+ g_free (r);
+ }
+
+out:
+ g_object_unref (layout);
+
+ if (lines == 1) {
+ *line1 = g_strdup (text);
+ *line2 = NULL;
+ *line3 = NULL;
+ }
+ else if (lines == 2) {
+ i2 = MIN (i2, (gint)strlen (text));
+ *line1 = g_strndup (text, i2);
+ *line2 = g_strdup (text + i2);
+ *line3 = NULL;
+ }
+ else {
+ i2 = MIN (i2, (gint)strlen (text));
+ i3 = MIN (i3, (gint)strlen (text));
+ *line1 = g_strndup (text, i2);
+ *line2 = g_strndup (text + i2, i3 - i2);
+ *line3 = g_strdup (text + i3);
+ }
+}
+
+static void
+size_allocate_cb (GtkWidget *box,
+ GtkAllocation *allocation,
+ GsAppWidget *app_widget)
+{
+ gchar *tmp;
+ gchar *line1, *line2, *line3;
+ GString *s = NULL;
+
+ tmp = (gchar *)gs_app_get_description (app_widget->priv->app);
+ if (tmp == NULL) {
+ tmp = _("The author of this software has not included a long description.");
+ }
+ else {
+ s = g_string_new (tmp);
+ _g_string_replace (s, "\n", " ");
+ tmp = s->str;
+ }
+
+ break_lines (gtk_widget_get_pango_context (box),
+ tmp, _("Read More"), allocation->width,
+ &line1, &line2, &line3);
+
+ gtk_label_set_label (GTK_LABEL (app_widget->priv->widget_description1), line1);
+ gtk_label_set_label (GTK_LABEL (app_widget->priv->widget_description2), line2);
+ gtk_label_set_label (GTK_LABEL (app_widget->priv->widget_description3), line3);
+
+ if (s)
+ g_string_free (s, TRUE);
+}
+
/**
* gs_app_widget_init:
**/
@@ -275,7 +375,8 @@ static void
gs_app_widget_init (GsAppWidget *app_widget)
{
GsAppWidgetPrivate *priv;
- GtkWidget *box;
+ GtkWidget *box, *box2;
+ gchar *tmp;
PangoAttrList *attr_list;
g_return_if_fail (GS_IS_APP_WIDGET (app_widget));
@@ -292,13 +393,13 @@ gs_app_widget_init (GsAppWidget *app_widget)
gtk_widget_set_margin_bottom (GTK_WIDGET (app_widget), 9);
/* pixbuf */
- priv->widget_image = gtk_image_new_from_icon_name ("edit-paste",
+ priv->widget_image = gtk_image_new_from_icon_name ("missing-image",
GTK_ICON_SIZE_DIALOG);
- gtk_widget_set_margin_right (GTK_WIDGET (priv->widget_image), 9);
+ gtk_image_set_pixel_size (GTK_IMAGE (priv->widget_image), 64);
+
+ gtk_widget_set_margin_right (priv->widget_image, 9);
gtk_widget_set_valign (priv->widget_image, GTK_ALIGN_START);
- gtk_box_pack_start (GTK_BOX (app_widget),
- GTK_WIDGET (priv->widget_image),
- FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (app_widget), priv->widget_image, FALSE, FALSE, 0);
/* name > version */
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
@@ -317,32 +418,45 @@ gs_app_widget_init (GsAppWidget *app_widget)
pango_attr_list_unref (attr_list);
priv->widget_version = gtk_label_new ("version");
gtk_misc_set_alignment (GTK_MISC (priv->widget_version), 0.0, 0.5);
- gtk_box_pack_start (GTK_BOX (box),
- GTK_WIDGET (priv->widget_name),
- FALSE, FALSE, 0);
- gtk_box_pack_start (GTK_BOX (box),
- GTK_WIDGET (priv->widget_version),
- FALSE, FALSE, 6);
- gtk_box_pack_start (GTK_BOX (app_widget),
- GTK_WIDGET (box),
- FALSE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box), priv->widget_name, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box), priv->widget_version, FALSE, FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (app_widget), box, FALSE, TRUE, 0);
/* description */
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- gtk_widget_set_visible (box, TRUE);
- priv->widget_description = gtk_label_new ("description");
- gtk_misc_set_alignment (GTK_MISC (priv->widget_description), 0.0, 0.0);
- gtk_label_set_line_wrap (GTK_LABEL (priv->widget_description), TRUE);
- gtk_box_pack_start (GTK_BOX (box),
- GTK_WIDGET (priv->widget_description),
- TRUE, TRUE, 0);
- gtk_box_pack_start (GTK_BOX (app_widget),
- GTK_WIDGET (box),
- TRUE, TRUE, 0);
+ gtk_widget_set_hexpand (box, TRUE);
+ gtk_widget_set_halign (box, GTK_ALIGN_FILL);
+ priv->widget_description1 = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (priv->widget_description1), 0.0, 0.5);
+ gtk_label_set_ellipsize (GTK_LABEL (priv->widget_description1), PANGO_ELLIPSIZE_END);
+ gtk_container_add (GTK_CONTAINER (box), priv->widget_description1);
+ priv->widget_description2 = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (priv->widget_description2), 0.0, 0.5);
+ gtk_label_set_ellipsize (GTK_LABEL (priv->widget_description2), PANGO_ELLIPSIZE_END);
+ gtk_container_add (GTK_CONTAINER (box), priv->widget_description2);
+ box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (box), box2, TRUE, TRUE, 0);
+ priv->widget_description3 = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (priv->widget_description3), 0.0, 0.5);
+ gtk_label_set_ellipsize (GTK_LABEL (priv->widget_description3), PANGO_ELLIPSIZE_END);
+ gtk_widget_set_size_request (priv->widget_description3, 100, -1);
+ gtk_container_add (GTK_CONTAINER (box2), priv->widget_description3);
+ priv->widget_read_more = gtk_label_new (NULL);
+ tmp = g_markup_printf_escaped ("<a href=''>%s</a>", _("Read More"));
+ gtk_label_set_markup (GTK_LABEL (priv->widget_read_more), tmp);
+ g_free (tmp);
+ gtk_misc_set_alignment (GTK_MISC (priv->widget_read_more), 1, 0.5);
+ gtk_widget_set_halign (priv->widget_read_more, GTK_ALIGN_END);
+ gtk_box_pack_start (GTK_BOX (box2), priv->widget_read_more, TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX (app_widget), box, TRUE, TRUE, 0);
+ g_signal_connect (box, "size-allocate", G_CALLBACK (size_allocate_cb), app_widget);
+ gtk_widget_show_all (box);
+
/* button */
priv->widget_button = gtk_button_new_with_label ("button");
- gtk_widget_set_margin_right (GTK_WIDGET (priv->widget_button), 9);
+ gtk_widget_set_margin_right (priv->widget_button, 9);
gtk_widget_set_size_request (priv->widget_button, 100, -1);
gtk_widget_set_vexpand (priv->widget_button, FALSE);
gtk_widget_set_hexpand (priv->widget_button, FALSE);
@@ -352,30 +466,37 @@ gs_app_widget_init (GsAppWidget *app_widget)
/* spinner */
priv->widget_spinner = gtk_spinner_new ();
- gtk_widget_set_halign (box, GTK_ALIGN_END);
- gtk_widget_set_valign (box, GTK_ALIGN_CENTER);
- gtk_widget_set_margin_left (GTK_WIDGET (priv->widget_spinner), 6);
- gtk_widget_set_margin_right (GTK_WIDGET (priv->widget_spinner), 6);
+ gtk_widget_set_halign (priv->widget_spinner, GTK_ALIGN_END);
+ gtk_widget_set_valign (priv->widget_spinner, GTK_ALIGN_CENTER);
+ gtk_widget_set_margin_left (priv->widget_spinner, 6);
+ gtk_widget_set_margin_right (priv->widget_spinner, 6);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
gtk_widget_set_size_request (box, 200, -1);
gtk_widget_set_halign (box, GTK_ALIGN_END);
gtk_widget_set_valign (box, GTK_ALIGN_CENTER);
gtk_widget_set_visible (box, TRUE);
- gtk_box_pack_end (GTK_BOX (box),
- GTK_WIDGET (priv->widget_button),
- FALSE, FALSE, 0);
- gtk_box_pack_end (GTK_BOX (box),
- GTK_WIDGET (priv->widget_spinner),
- FALSE, FALSE, 0);
- gtk_box_pack_end (GTK_BOX (app_widget),
- GTK_WIDGET (box),
- FALSE, FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (box), priv->widget_button, FALSE, FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (box), priv->widget_spinner, FALSE, FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (app_widget), box, FALSE, FALSE, 0);
/* refresh */
gs_app_widget_refresh (app_widget);
}
+void
+gs_app_widget_set_size_groups (GsAppWidget *app_widget,
+ GtkSizeGroup *image,
+ GtkSizeGroup *name)
+{
+ GtkWidget *box;
+
+ gtk_size_group_add_widget (image, app_widget->priv->widget_image);
+
+ box = gtk_widget_get_parent (app_widget->priv->widget_name);
+ gtk_size_group_add_widget (name, box);
+}
+
/**
* gs_app_widget_new:
**/
diff --git a/src/gs-app-widget.h b/src/gs-app-widget.h
index e90d626..6be1aa3 100644
--- a/src/gs-app-widget.h
+++ b/src/gs-app-widget.h
@@ -73,6 +73,9 @@ void gs_app_widget_set_status (GsAppWidget *app_widget,
GsAppWidgetKind gs_app_widget_get_kind (GsAppWidget *app_widget);
void gs_app_widget_set_kind (GsAppWidget *app_widget,
GsAppWidgetKind kind);
+void gs_app_widget_set_size_groups (GsAppWidget *app_widget,
+ GtkSizeGroup *image,
+ GtkSizeGroup *name);
G_END_DECLS
diff --git a/src/gs-main.c b/src/gs-main.c
index 9dae055..16dbd97 100644
--- a/src/gs-main.c
+++ b/src/gs-main.c
@@ -55,6 +55,8 @@ typedef struct {
guint tab_back_id;
gint pending_apps;
GsShellUpdates *shell_updates;
+ GtkSizeGroup *sizegroup_image;
+ GtkSizeGroup *sizegroup_name;
} GsMainPrivate;
static void gs_main_set_overview_mode_ui (GsMainPrivate *priv, GsMainMode mode, GsApp *app);
@@ -545,6 +547,9 @@ gs_main_get_installed_cb (GObject *source_object,
gs_app_get_kind (app) == GS_APP_KIND_SYSTEM ?
GS_APP_WIDGET_KIND_BLANK : GS_APP_WIDGET_KIND_REMOVE);
gs_app_widget_set_app (GS_APP_WIDGET (widget), app);
gtk_container_add (GTK_CONTAINER (priv->list_box_installed), widget);
+ gs_app_widget_set_size_groups (GS_APP_WIDGET (widget),
+ priv->sizegroup_image,
+ priv->sizegroup_name);
gtk_widget_show (widget);
}
@@ -1424,6 +1429,9 @@ gs_main_startup_cb (GApplication *application, GsMainPrivate *priv)
gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (priv->list_box_installed));
gtk_widget_show (GTK_WIDGET (priv->list_box_installed));
+ priv->sizegroup_image = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ priv->sizegroup_name = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
/* setup buttons */
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_back"));
g_signal_connect (widget, "clicked",
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]