[gimp] app: display the release notes as a GtkListBox instead of text.



commit 3011795407db45919bc8cd312da4fd00c1a61463
Author: Jehan <jehan girinstud io>
Date:   Fri Mar 4 21:26:05 2022 +0100

    app: display the release notes as a GtkListBox instead of text.
    
    What it means is that we will be a bit strict over our <release>
    formatting which will have to always be a <p> introduction followed by a
    list of items. This is what gimp_appstream_to_pango_markups() expects.
    
    Since so far, this is how all our <release> tags were formatted anyway,
    this is not too much of a problem.
    
    Note that I keep the less strict gimp_appstream_to_pango_markup() and
    use it for extension's appstream description as we will have no control
    over these.
    
    The main reason for this new rule and new display of our release notes
    is that I am going to add the ability to click independent release items
    so that people can get "blinking" indications of what changed when
    relevant.

 app/core/gimp-utils.c        | 134 ++++++++++++++++++++++++++++++-------------
 app/core/gimp-utils.h        |   3 +
 app/dialogs/welcome-dialog.c |  73 ++++++++++++++++-------
 3 files changed, 147 insertions(+), 63 deletions(-)
---
diff --git a/app/core/gimp-utils.c b/app/core/gimp-utils.c
index 5bd0f493fd..ba6c2ee1a7 100644
--- a/app/core/gimp-utils.c
+++ b/app/core/gimp-utils.c
@@ -75,9 +75,15 @@ typedef struct
   const gchar *lang;
   GString     *original;
   gint         foreign_level;
+
+  gchar      **introduction;
+  GList      **release_items;
 } ParseState;
 
 
+static gchar      * gimp_appstream_parse           (const gchar          *as_text,
+                                                    gchar               **introduction,
+                                                    GList               **release_items);
 static void         appstream_text_start_element   (GMarkupParseContext  *context,
                                                     const gchar          *element_name,
                                                     const gchar         **attribute_names,
@@ -955,50 +961,20 @@ gimp_ascii_strtod (const gchar  *nptr,
 gchar *
 gimp_appstream_to_pango_markup (const gchar *as_text)
 {
-  static const GMarkupParser appstream_text_parser =
-    {
-      appstream_text_start_element,
-      appstream_text_end_element,
-      appstream_text_characters,
-      NULL, /*  passthrough */
-      NULL  /*  error       */
-    };
-
-  GimpXmlParser *xml_parser;
-  gchar         *markup = NULL;
-  GError        *error  = NULL;
-  ParseState     state;
-
-  state.level           = 0;
-  state.foreign_level   = -1;
-  state.text            = g_string_new (NULL);
-  state.numbered_list   = FALSE;
-  state.unnumbered_list = FALSE;
-  state.lang            = g_getenv ("LANGUAGE");
-  state.original        = NULL;
-
-  xml_parser  = gimp_xml_parser_new (&appstream_text_parser, &state);
-  if (as_text &&
-      ! gimp_xml_parser_parse_buffer (xml_parser, as_text, -1, &error))
-    {
-      g_printerr ("%s: %s\n", G_STRFUNC, error->message);
-      g_error_free (error);
-    }
-
-  /* Append possibly last original text without proper localization. */
-  if (state.original)
-    {
-      g_string_append (state.text, state.original->str);
-      g_string_free (state.original, TRUE);
-    }
+  return gimp_appstream_parse (as_text, NULL, NULL);
+}
 
-  markup = g_string_free (state.text, FALSE);
-  gimp_xml_parser_free (xml_parser);
+void
+gimp_appstream_to_pango_markups (const gchar  *as_text,
+                                 gchar       **introduction,
+                                 GList       **release_items)
+{
+  gchar * markup;
 
-  return markup;
+  markup = gimp_appstream_parse (as_text, introduction, release_items);
+  g_free (markup);
 }
 
-
 gint
 gimp_g_list_compare (GList *list1,
                      GList *list2)
@@ -1308,6 +1284,61 @@ gimp_create_image_from_buffer (Gimp        *gimp,
 
 /* Private functions */
 
+
+static gchar *
+gimp_appstream_parse (const gchar  *as_text,
+                      gchar       **introduction,
+                      GList       **release_items)
+{
+  static const GMarkupParser appstream_text_parser =
+    {
+      appstream_text_start_element,
+      appstream_text_end_element,
+      appstream_text_characters,
+      NULL, /*  passthrough */
+      NULL  /*  error       */
+    };
+
+  GimpXmlParser *xml_parser;
+  gchar         *markup = NULL;
+  GError        *error  = NULL;
+  ParseState     state;
+
+  state.level           = 0;
+  state.foreign_level   = -1;
+  state.text            = g_string_new (NULL);
+  state.list_num        = 0;
+  state.numbered_list   = FALSE;
+  state.unnumbered_list = FALSE;
+  state.lang            = g_getenv ("LANGUAGE");
+  state.original        = NULL;
+  state.introduction    = introduction;
+  state.release_items   = release_items;
+
+  xml_parser  = gimp_xml_parser_new (&appstream_text_parser, &state);
+  if (as_text &&
+      ! gimp_xml_parser_parse_buffer (xml_parser, as_text, -1, &error))
+    {
+      g_printerr ("%s: %s\n", G_STRFUNC, error->message);
+      g_error_free (error);
+    }
+
+  /* Append possibly last original text without proper localization. */
+  if (state.original)
+    {
+      g_string_append (state.text, state.original->str);
+      g_string_free (state.original, TRUE);
+    }
+
+  if (release_items)
+    *release_items = g_list_reverse (*release_items);
+
+  markup = g_string_free (state.text, FALSE);
+  gimp_xml_parser_free (xml_parser);
+
+  return markup;
+}
+
 static void
 appstream_text_start_element (GMarkupParseContext  *context,
                               const gchar          *element_name,
@@ -1367,6 +1398,7 @@ appstream_text_start_element (GMarkupParseContext  *context,
   else if (g_strcmp0 (element_name, "ul") == 0)
     {
       state->unnumbered_list = TRUE;
+      state->list_num        = 0;
     }
   else if (g_strcmp0 (element_name, "ol") == 0)
     {
@@ -1407,6 +1439,10 @@ appstream_text_end_element (GMarkupParseContext  *context,
     {
       if (state->foreign_level < 0)
         {
+          if (state->introduction &&
+              *state->introduction == NULL)
+            *state->introduction = g_strdup (state->original ? state->original->str : state->text->str);
+
           if (state->original)
             g_string_append (state->original, "\n\n");
           else
@@ -1440,8 +1476,24 @@ appstream_text_characters (GMarkupParseContext  *context,
 {
   ParseState *state = user_data;
 
-  if (state->foreign_level < 0)
+  if (state->foreign_level < 0 && text_len > 0)
     {
+      if (state->list_num > 0 && state->release_items)
+        {
+          GList **items = state->release_items;
+
+          if (state->list_num == g_list_length (*(state->release_items)))
+            {
+              gchar *tmp = (*items)->data;
+
+              (*items)->data = g_strconcat (tmp, text, NULL);
+              g_free (tmp);
+            }
+          else
+            {
+              *items = g_list_prepend (*items, g_strdup (text));
+            }
+        }
       if (state->original)
         g_string_append (state->original, text);
       else
diff --git a/app/core/gimp-utils.h b/app/core/gimp-utils.h
index 4523b9247c..ccc8906b48 100644
--- a/app/core/gimp-utils.h
+++ b/app/core/gimp-utils.h
@@ -102,6 +102,9 @@ gboolean     gimp_ascii_strtod                     (const gchar       *nptr,
                                                     gdouble           *result);
 
 gchar     *  gimp_appstream_to_pango_markup        (const gchar       *as_text);
+void         gimp_appstream_to_pango_markups       (const gchar       *as_text,
+                                                    gchar            **introduction,
+                                                    GList            **release_items);
 
 gint         gimp_g_list_compare                   (GList             *list1,
                                                     GList             *list2);
diff --git a/app/dialogs/welcome-dialog.c b/app/dialogs/welcome-dialog.c
index 004b9f6749..f4e1b15f3a 100644
--- a/app/dialogs/welcome-dialog.c
+++ b/app/dialogs/welcome-dialog.c
@@ -74,15 +74,15 @@ welcome_dialog_create (Gimp *gimp)
   GtkWidget     *vbox;
   GtkWidget     *hbox;
   GtkWidget     *image;
+  GtkWidget     *listbox;
   GtkWidget     *widget;
 
-  GtkTextBuffer *buffer;
-  GtkTextIter    iter;
-
   gchar         *release_link;
   gchar         *appdata_path;
   gchar         *title;
   gchar         *markup;
+  gchar         *release_introduction = NULL;
+  GList         *release_items        = NULL;
   gchar         *tmp;
 
   gint           row;
@@ -344,26 +344,55 @@ welcome_dialog_create (Gimp *gimp)
 
       /* Release note contents. */
 
-      scrolled_window = gtk_scrolled_window_new (NULL, NULL);
-      gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
-      gtk_widget_show (scrolled_window);
-
-      widget = gtk_text_view_new ();
-      gtk_widget_set_vexpand (widget, TRUE);
-      gtk_widget_set_hexpand (widget, TRUE);
-      gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (widget), GTK_WRAP_WORD_CHAR);
-      gtk_text_view_set_editable (GTK_TEXT_VIEW (widget), FALSE);
-      gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (widget), FALSE);
-      gtk_text_view_set_justification (GTK_TEXT_VIEW (widget), GTK_JUSTIFY_LEFT);
-      buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
-      gtk_text_buffer_get_start_iter (buffer, &iter);
-
-      markup = gimp_appstream_to_pango_markup (release_notes);
-      gtk_text_buffer_insert_markup (buffer, &iter, markup, -1);
-      g_free (markup);
+      gimp_appstream_to_pango_markups (release_notes,
+                                       &release_introduction,
+                                       &release_items);
+      if (release_introduction)
+        {
+          widget = gtk_label_new (NULL);
+          gtk_label_set_markup (GTK_LABEL (widget), release_introduction);
+          gtk_label_set_max_width_chars (GTK_LABEL (widget), 70);
+          gtk_label_set_selectable (GTK_LABEL (widget), FALSE);
+          gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_LEFT);
+          gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+          gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+          gtk_widget_show (widget);
+
+          g_free (release_introduction);
+        }
 
-      gtk_container_add (GTK_CONTAINER (scrolled_window), widget);
-      gtk_widget_show (widget);
+      if (release_items)
+        {
+          GList *item;
+
+          scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+          gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
+          gtk_widget_show (scrolled_window);
+
+          listbox = gtk_list_box_new ();
+
+          for (item = release_items; item; item = item->next)
+            {
+              GtkWidget *row;
+
+              row = gtk_list_box_row_new ();
+              widget = gtk_label_new (NULL);
+              gtk_label_set_markup (GTK_LABEL (widget), item->data);
+              gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+              gtk_label_set_line_wrap_mode (GTK_LABEL (widget), PANGO_WRAP_WORD);
+              gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_LEFT);
+              gtk_widget_set_halign (widget, GTK_ALIGN_START);
+              gtk_label_set_xalign (GTK_LABEL (widget), 0.0);
+              gtk_container_add (GTK_CONTAINER (row), widget);
+
+              gtk_list_box_insert (GTK_LIST_BOX (listbox), row, -1);
+              gtk_widget_show_all (row);
+            }
+          gtk_container_add (GTK_CONTAINER (scrolled_window), listbox);
+          gtk_widget_show (listbox);
+
+          g_list_free_full (release_items, g_free);
+        }
 
       hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
       gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]