[gnome-builder] shortcuts: implement simplified shortcuts generation with GtkBuilder XML



commit cde8ef9c79fa3838c2a4cfa76088662f45a588bc
Author: Christian Hergert <christian hergert me>
Date:   Sun Aug 30 21:27:15 2015 -0700

    shortcuts: implement simplified shortcuts generation with GtkBuilder XML
    
    Quick example:
    
     <views>
      <view>
       <page>
        <column>
         <group>
          <shortcut>
          <gesture>

 data/ui/gb-shortcuts-window.ui        |  223 ++++++++++---------
 src/keybindings/gb-accel-label.c      |   23 +--
 src/shortcuts/gb-shortcuts-dialog.c   |  398 ++++++++++++++++++++++++++++++++-
 src/shortcuts/gb-shortcuts-page.c     |    1 +
 src/shortcuts/gb-shortcuts-shortcut.c |    2 +-
 5 files changed, 522 insertions(+), 125 deletions(-)
---
diff --git a/data/ui/gb-shortcuts-window.ui b/data/ui/gb-shortcuts-window.ui
index e75150e..d276aa5 100644
--- a/data/ui/gb-shortcuts-window.ui
+++ b/data/ui/gb-shortcuts-window.ui
@@ -2,110 +2,127 @@
 <interface>
   <!-- interface-requires gtk+ 3.17 -->
   <template class="GbShortcutsWindow" parent="GbShortcutsDialog">
-    <child>
-      <object class="GbShortcutsView">
-        <property name="visible">true</property>
-        <property name="view-name">editor</property>
+    <views>
+      <view name="editor">
         <property name="title" translatable="yes">Editor Shortcuts</property>
-        <child>
-          <object class="GbShortcutsPage">
-            <property name="visible">true</property>
-            <child>
-              <object class="GbShortcutsColumn">
-                <property name="visible">true</property>
-                <child>
-                  <object class="GbShortcutsGroup">
-                    <property name="title" translatable="yes">General</property>
-                    <property name="visible">true</property>
-                    <child>
-                      <object class="GbShortcutsShortcut">
-                        <property name="accelerator">&lt;ctrl&gt;period</property>
-                        <property name="title" translatable="yes">Global Search</property>
-                        <property name="visible">true</property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GbShortcutsShortcut">
-                        <property name="accelerator">&lt;ctrl&gt;comma</property>
-                        <property name="title" translatable="yes">Preferences</property>
-                        <property name="visible">true</property>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="GbShortcutsGroup">
-                    <property name="title" translatable="yes">Touchpad gestures</property>
-                    <property name="visible">true</property>
-                    <child>
-                      <object class="GbShortcutsGesture">
-                        <property name="icon-name">gesture-two-finger-swipe-right</property>
-                        <property name="title" translatable="yes">Switch to the next document</property>
-                        <property name="subtitle" translatable="yes">Two finger swipe right</property>
-                        <property name="visible">true</property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GbShortcutsGesture">
-                        <property name="icon-name">gesture-two-finger-swipe-left</property>
-                        <property name="title" translatable="yes">Switch to the previous document</property>
-                        <property name="subtitle" translatable="yes">Two finger swipe left</property>
-                        <property name="visible">true</property>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="GbShortcutsColumn">
-                <property name="visible">true</property>
-                <child>
-                  <object class="GbShortcutsGroup">
-                    <property name="title" translatable="yes">Files</property>
-                    <property name="visible">true</property>
-                    <child>
-                      <object class="GbShortcutsShortcut">
-                        <property name="accelerator">&lt;ctrl&gt;n</property>
-                        <property name="title" translatable="yes">Create new document</property>
-                        <property name="visible">true</property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GbShortcutsShortcut">
-                        <property name="accelerator">&lt;ctrl&gt;o</property>
-                        <property name="title" translatable="yes">Open a document</property>
-                        <property name="visible">true</property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GbShortcutsShortcut">
-                        <property name="accelerator">&lt;ctrl&gt;&lt;alt&gt;Page_Down</property>
-                        <property name="title" translatable="yes">Switch to the next document</property>
-                        <property name="visible">true</property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GbShortcutsShortcut">
-                        <property name="accelerator">&lt;ctrl&gt;&lt;alt&gt;Page_Up</property>
-                        <property name="title" translatable="yes">Switch to the previous document</property>
-                        <property name="visible">true</property>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-              </object>
-            </child>
-          </object>
-        </child>
-      </object>
-    </child>
-    <child>
-      <object class="GbShortcutsView">
-        <property name="visible">true</property>
-        <property name="view-name">terminal</property>
+        <page>
+          <column>
+            <group>
+              <property name="title" translatable="yes">General</property>
+              <shortcut>
+                <property name="title" translatable="yes">Global Search</property>
+                <property name="accelerator">&lt;ctrl&gt;period</property>
+              </shortcut>
+              <shortcut>
+                <property name="title" translatable="yes">Preferences</property>
+                <property name="accelerator">&lt;ctrl&gt;comma</property>
+              </shortcut>
+            </group>
+            <group>
+              <property name="title" translatable="yes">Panels</property>
+              <shortcut>
+                <property name="title" translatable="yes">Toggle left panel</property>
+                <property name="accelerator">F9</property>
+              </shortcut>
+              <shortcut>
+                <property name="title" translatable="yes">Toggle right panel</property>
+                <property name="accelerator">&lt;shift&gt;F9</property>
+              </shortcut>
+              <shortcut>
+                <property name="title" translatable="yes">Toggle bottom panel</property>
+                <property name="accelerator">&lt;ctrl&gt;F9</property>
+              </shortcut>
+            </group>
+            <group>
+              <property name="title" translatable="yes">Touchpad gestures</property>
+              <gesture>
+                <property name="title" translatable="yes">Switch to the next document</property>
+                <property name="subtitle" translatable="yes">Two finger swipe right</property>
+                <property name="icon-name">gesture-two-finger-swipe-right</property>
+              </gesture>
+              <gesture>
+                <property name="title" translatable="yes">Switch to the previous document</property>
+                <property name="subtitle" translatable="yes">Two finger swipe left</property>
+                <property name="icon-name">gesture-two-finger-swipe-left</property>
+              </gesture>
+            </group>
+          </column>
+          <column>
+            <group>
+              <property name="title" translatable="yes">Files</property>
+              <shortcut>
+                <property name="accelerator">&lt;ctrl&gt;n</property>
+                <property name="title" translatable="yes">Create new document</property>
+              </shortcut>
+              <shortcut>
+                <property name="accelerator">&lt;ctrl&gt;o</property>
+                <property name="title" translatable="yes">Open a document</property>
+              </shortcut>
+              <shortcut>
+                <property name="accelerator">&lt;ctrl&gt;s</property>
+                <property name="title" translatable="yes">Save the document</property>
+              </shortcut>
+              <shortcut>
+                <property name="accelerator">&lt;ctrl&gt;w</property>
+                <property name="title" translatable="yes">Close the document</property>
+              </shortcut>
+              <shortcut>
+                <property name="accelerator">&lt;ctrl&gt;&lt;alt&gt;Page_Down</property>
+                <property name="title" translatable="yes">Switch to the next document</property>
+              </shortcut>
+              <shortcut>
+                <property name="accelerator">&lt;ctrl&gt;&lt;alt&gt;Page_Up</property>
+                <property name="title" translatable="yes">Switch to the previous document</property>
+              </shortcut>
+            </group>
+            <group>
+              <property name="title" translatable="yes">Find and replace</property>
+              <shortcut>
+                <property name="accelerator">&lt;ctrl&gt;f</property>
+                <property name="title" translatable="yes">Find</property>
+              </shortcut>
+              <shortcut>
+                <property name="accelerator">&lt;ctrl&gt;g</property>
+                <property name="title" translatable="yes">Find the next match</property>
+              </shortcut>
+              <shortcut>
+                <property name="accelerator">&lt;ctrl&gt;&lt;shift&gt;g</property>
+                <property name="title" translatable="yes">Find the previous match</property>
+              </shortcut>
+              <shortcut>
+                <property name="accelerator">&lt;ctrl&gt;h</property>
+                <property name="title" translatable="yes">Find and replace</property>
+              </shortcut>
+              <shortcut>
+                <property name="accelerator">&lt;ctrl&gt;&lt;shift&gt;k</property>
+                <property name="title" translatable="yes">Clear highlight</property>
+              </shortcut>
+              <shortcut>
+                <property name="accelerator">&lt;ctrl&gt;l</property>
+                <property name="title" translatable="yes">Go to line</property>
+              </shortcut>
+            </group>
+          </column>
+        </page>
+      </view>
+      <view name="terminal">
         <property name="title" translatable="yes">Terminal Shortcuts</property>
-      </object>
-    </child>
+        <page>
+          <column>
+            <group>
+              <property name="title" translatable="yes">Copy and Paste</property>
+              <shortcut>
+                <property name="accelerator">&lt;ctrl&gt;&lt;shift&gt;c</property>
+                <property name="title" translatable="yes">Copy selected text to clipboard</property>
+              </shortcut>
+              <shortcut>
+                <property name="accelerator">&lt;ctrl&gt;&lt;shift&gt;v</property>
+                <property name="title" translatable="yes">Paste text from clipboard</property>
+              </shortcut>
+            </group>
+          </column>
+        </page>
+      </view>
+    </views>
   </template>
 </interface>
diff --git a/src/keybindings/gb-accel-label.c b/src/keybindings/gb-accel-label.c
index 89ada5e..fc81a64 100644
--- a/src/keybindings/gb-accel-label.c
+++ b/src/keybindings/gb-accel-label.c
@@ -21,10 +21,8 @@
 
 struct _GbAccelLabel
 {
-  GtkBox        parent_instance;
-
-  gchar        *accelerator;
-  GtkSizeGroup *size_group;
+  GtkBox  parent_instance;
+  gchar  *accelerator;
 };
 
 G_DEFINE_TYPE (GbAccelLabel, gb_accel_label, GTK_TYPE_BOX)
@@ -32,7 +30,6 @@ G_DEFINE_TYPE (GbAccelLabel, gb_accel_label, GTK_TYPE_BOX)
 enum {
   PROP_0,
   PROP_ACCELERATOR,
-  PROP_SIZE_GROUP,
   LAST_PROP
 };
 
@@ -94,8 +91,8 @@ gb_accel_label_rebuild (GbAccelLabel *self)
        * modifiers together. Not always the case, but simple and easy
        * hack.
        */
-      if ((self->size_group != NULL) && (keys [i + 1] != NULL))
-        gtk_size_group_add_widget (self->size_group, GTK_WIDGET (frame));
+      if (keys [i + 1] != NULL)
+        gtk_widget_set_size_request (GTK_WIDGET (frame), 50, -1);
 
       disp = g_object_new (GTK_TYPE_LABEL,
                            "label", keys [i],
@@ -111,7 +108,6 @@ gb_accel_label_finalize (GObject *object)
   GbAccelLabel *self = (GbAccelLabel *)object;
 
   g_clear_pointer (&self->accelerator, g_free);
-  g_clear_object (&self->size_group);
 
   G_OBJECT_CLASS (gb_accel_label_parent_class)->finalize (object);
 }
@@ -149,10 +145,6 @@ gb_accel_label_set_property (GObject      *object,
       gb_accel_label_set_accelerator (self, g_value_get_string (value));
       break;
 
-    case PROP_SIZE_GROUP:
-      self->size_group = g_value_dup_object (value);
-      break;
-
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -174,13 +166,6 @@ gb_accel_label_class_init (GbAccelLabelClass *klass)
                          NULL,
                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
-  gParamSpecs [PROP_SIZE_GROUP] =
-    g_param_spec_object ("size-group",
-                         "Size Group",
-                         "Size Group",
-                         GTK_TYPE_SIZE_GROUP,
-                         (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
-
   g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
 }
 
diff --git a/src/shortcuts/gb-shortcuts-dialog.c b/src/shortcuts/gb-shortcuts-dialog.c
index 68acca8..492a36e 100644
--- a/src/shortcuts/gb-shortcuts-dialog.c
+++ b/src/shortcuts/gb-shortcuts-dialog.c
@@ -41,7 +41,22 @@ typedef struct
   GtkListBox     *list_box;
 } GbShortcutsDialogPrivate;
 
-G_DEFINE_TYPE_WITH_PRIVATE (GbShortcutsDialog, gb_shortcuts_dialog, GTK_TYPE_WINDOW)
+typedef struct
+{
+  GbShortcutsDialog *self;
+  GtkBuilder        *builder;
+  GQueue            *stack;
+  GQueue            *column_image_size_groups;
+  GQueue            *column_desc_size_groups;
+  gchar             *property_name;
+  guint              translatable : 1;
+} ViewsParserData;
+
+static void gtk_buildable_iface_init (GtkBuildableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GbShortcutsDialog, gb_shortcuts_dialog, GTK_TYPE_WINDOW, 0,
+                        G_ADD_PRIVATE (GbShortcutsDialog)
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_buildable_iface_init))
 
 enum {
   CLOSE,
@@ -75,7 +90,7 @@ gb_shortcuts_dialog_add_view (GbShortcutsDialog *self,
   label = g_object_new (GTK_TYPE_LABEL,
                         "margin", 6,
                         "label", title,
-                        "xalign", 0.0f,
+                        "xalign", 0.5f,
                         "visible", TRUE,
                         NULL);
   gtk_container_add (GTK_CONTAINER (row), GTK_WIDGET (label));
@@ -132,6 +147,384 @@ gb_shortcuts_dialog__list_box__row_activated (GbShortcutsDialog *self,
   gtk_widget_hide (GTK_WIDGET (priv->popover));
 }
 
+static gboolean
+check_parent (GMarkupParseContext  *context,
+              const gchar          *element_name,
+              GError              **error)
+{
+  const GSList *stack;
+  const gchar *parent_name;
+  const gchar *our_name;
+
+  stack = g_markup_parse_context_get_element_stack (context);
+  our_name = stack->data;
+  parent_name = stack->next ? stack->next->data : "";
+
+  if (g_strcmp0 (parent_name, element_name) != 0)
+    {
+      gint line;
+      gint col;
+
+      g_markup_parse_context_get_position (context, &line, &col);
+      g_set_error (error,
+                   GTK_BUILDER_ERROR,
+                   GTK_BUILDER_ERROR_INVALID_TAG,
+                   "%d:%d: Element <%s> found in <%s>, expected <%s>.",
+                   line, col, our_name, parent_name, element_name);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+views_parser_start_element (GMarkupParseContext  *context,
+                            const gchar          *element_name,
+                            const gchar         **attribute_names,
+                            const gchar         **attribute_values,
+                            gpointer              user_data,
+                            GError              **error)
+{
+  ViewsParserData *parser_data = user_data;
+  GtkWidget *item;
+
+  g_assert (context != NULL);
+  g_assert (element_name != NULL);
+  g_assert (parser_data != NULL);
+
+  if (g_strcmp0 (element_name, "views") == 0)
+    {
+    }
+  else if (g_strcmp0 (element_name, "view") == 0)
+    {
+      const gchar *name = NULL;
+
+      if (!check_parent (context, "views", error))
+        return;
+
+      if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+                                        G_MARKUP_COLLECT_STRING, "name", &name,
+                                        G_MARKUP_COLLECT_INVALID))
+        return;
+
+      item = g_object_new (GB_TYPE_SHORTCUTS_VIEW,
+                           "view-name", name,
+                           "visible", TRUE,
+                           NULL);
+
+      g_queue_push_head (parser_data->stack, g_object_ref_sink (item));
+    }
+  else if (g_strcmp0 (element_name, "page") == 0)
+    {
+      if (!check_parent (context, "view", error))
+        return;
+
+      item = g_object_new (GB_TYPE_SHORTCUTS_PAGE,
+                           "visible", TRUE,
+                           NULL);
+      g_queue_push_head (parser_data->stack, g_object_ref_sink (item));
+    }
+  else if (g_strcmp0 (element_name, "column") == 0)
+    {
+      GtkSizeGroup *size_group;
+
+      if (!check_parent (context, "page", error))
+        return;
+
+      size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+      g_queue_push_head (parser_data->column_image_size_groups, size_group);
+
+      size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+      g_queue_push_head (parser_data->column_desc_size_groups, size_group);
+
+      item = g_object_new (GB_TYPE_SHORTCUTS_COLUMN,
+                           "visible", TRUE,
+                           NULL);
+      g_queue_push_head (parser_data->stack, g_object_ref_sink (item));
+    }
+  else if (g_strcmp0 (element_name, "group") == 0)
+    {
+      if (!check_parent (context, "column", error))
+        return;
+
+      item = g_object_new (GB_TYPE_SHORTCUTS_GROUP,
+                           "visible", TRUE,
+                           NULL);
+      g_queue_push_head (parser_data->stack, g_object_ref_sink (item));
+    }
+  else if (g_strcmp0 (element_name, "shortcut") == 0)
+    {
+      GtkSizeGroup *accel_size_group;
+      GtkSizeGroup *desc_size_group;
+
+      if (!check_parent (context, "group", error))
+        return;
+
+      accel_size_group = g_queue_peek_head (parser_data->column_image_size_groups);
+      desc_size_group = g_queue_peek_head (parser_data->column_desc_size_groups);
+
+      item = g_object_new (GB_TYPE_SHORTCUTS_SHORTCUT,
+                           "accelerator-size-group", accel_size_group,
+                           "title-size-group", desc_size_group,
+                           "visible", TRUE,
+                           NULL);
+      g_queue_push_head (parser_data->stack, g_object_ref_sink (item));
+    }
+  else if (g_strcmp0 (element_name, "gesture") == 0)
+    {
+      GtkSizeGroup *accel_size_group;
+      GtkSizeGroup *desc_size_group;
+
+      if (!check_parent (context, "group", error))
+        return;
+
+      accel_size_group = g_queue_peek_head (parser_data->column_image_size_groups);
+      desc_size_group = g_queue_peek_head (parser_data->column_desc_size_groups);
+
+      item = g_object_new (GB_TYPE_SHORTCUTS_GESTURE,
+                           "desc-size-group", desc_size_group,
+                           "icon-size-group", accel_size_group,
+                           "visible", TRUE,
+                           NULL);
+      g_queue_push_head (parser_data->stack, g_object_ref_sink (item));
+    }
+  else if (g_strcmp0 (element_name, "property") == 0)
+    {
+      const gchar *name = NULL;
+      const gchar *translatable = NULL;
+
+      item = g_queue_peek_head (parser_data->stack);
+
+      if (item == NULL)
+        {
+          g_set_error (error,
+                       GTK_BUILDER_ERROR,
+                       GTK_BUILDER_ERROR_INVALID_TAG,
+                       "Property called without a parent object");
+          return;
+        }
+
+      if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+                                        G_MARKUP_COLLECT_STRING, "name", &name,
+                                        G_MARKUP_COLLECT_OPTIONAL | G_MARKUP_COLLECT_STRING, "translatable", 
&translatable,
+                                        G_MARKUP_COLLECT_INVALID))
+        return;
+
+      g_free (parser_data->property_name);
+      parser_data->property_name = g_strdup (name);
+      parser_data->translatable = (g_strcmp0 (translatable, "yes") == 0);
+    }
+  else
+    {
+      const GSList *stack;
+      const gchar *parent_name;
+      const gchar *our_name;
+      gint line;
+      gint col;
+
+      stack = g_markup_parse_context_get_element_stack (context);
+      our_name = stack->data;
+      parent_name = stack->next ? stack->next->data : "";
+
+      g_markup_parse_context_get_position (context, &line, &col);
+      g_set_error (error,
+                   GTK_BUILDER_ERROR,
+                   GTK_BUILDER_ERROR_INVALID_TAG,
+                   "%d:%d: Unknown element <%s> found in <%s>.",
+                   line, col, our_name, parent_name);
+    }
+}
+
+static void
+views_parser_end_element (GMarkupParseContext  *context,
+                          const gchar          *element_name,
+                          gpointer              user_data,
+                          GError              **error)
+{
+  ViewsParserData *parser_data = user_data;
+  GtkWidget *item;
+
+  g_assert (context != NULL);
+  g_assert (element_name != NULL);
+  g_assert (parser_data != NULL);
+
+  if (g_strcmp0 (element_name, "view") == 0)
+    {
+      item = g_queue_pop_head (parser_data->stack);
+      gb_shortcuts_dialog_add_view (parser_data->self, GB_SHORTCUTS_VIEW (item));
+      g_object_unref (item);
+    }
+  else if ((g_strcmp0 (element_name, "page") == 0) ||
+           (g_strcmp0 (element_name, "column") == 0) ||
+           (g_strcmp0 (element_name, "group") == 0) ||
+           (g_strcmp0 (element_name, "shortcut") == 0) ||
+           (g_strcmp0 (element_name, "gesture") == 0))
+    {
+      GtkWidget *parent;
+
+      item = g_queue_pop_head (parser_data->stack);
+      parent = g_queue_peek_head (parser_data->stack);
+      if (item != NULL && parent != NULL)
+        gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (item));
+      g_clear_object (&item);
+
+      if (g_strcmp0 (element_name, "column") == 0)
+        {
+          GtkSizeGroup *size_group;
+
+          size_group = g_queue_pop_head (parser_data->column_image_size_groups);
+          g_clear_object (&size_group);
+
+          size_group = g_queue_pop_head (parser_data->column_desc_size_groups);
+          g_clear_object (&size_group);
+        }
+    }
+  else if (g_strcmp0 (element_name, "property") == 0)
+    {
+      g_clear_pointer (&parser_data->property_name, g_free);
+    }
+}
+
+static void
+views_parser_text (GMarkupParseContext  *context,
+                   const gchar          *text,
+                   gsize                 text_len,
+                   gpointer              user_data,
+                   GError              **error)
+{
+  ViewsParserData *parser_data = user_data;
+  GParamSpec *pspec;
+  GtkWidget *item;
+  GValue value = { 0 };
+
+  g_assert (parser_data != NULL);
+
+  if (parser_data->property_name == NULL)
+    return;
+
+  item = g_queue_peek_head (parser_data->stack);
+
+  if (item == NULL)
+    return;
+
+  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (item), parser_data->property_name);
+
+  if (pspec == NULL)
+    {
+      g_set_error (error,
+                   GTK_BUILDER_ERROR,
+                   GTK_BUILDER_ERROR_INVALID_PROPERTY,
+                   "No such property: %s",
+                   parser_data->property_name);
+      return;
+    }
+
+  if (parser_data->translatable)
+    text = _(text);
+
+  if (g_type_is_a (pspec->value_type, G_TYPE_OBJECT))
+    {
+      GObject *relative;
+
+      relative = gtk_builder_get_object (parser_data->builder, text);
+
+      if (relative == NULL)
+        {
+          g_set_error (error,
+                       GTK_BUILDER_ERROR,
+                       GTK_BUILDER_ERROR_INVALID_VALUE,
+                       "Unknown object for property '%s': %s",
+                       parser_data->property_name,
+                       text);
+          return;
+        }
+
+      g_value_init (&value, pspec->value_type);
+      g_value_set_object (&value, relative);
+    }
+  else if (!gtk_builder_value_from_string (parser_data->builder,
+                                           pspec, text, &value, error))
+    return;
+
+  g_object_set_property (G_OBJECT (item), parser_data->property_name, &value);
+  g_value_unset (&value);
+}
+
+static GMarkupParser ViewsParser = {
+  views_parser_start_element,
+  views_parser_end_element,
+  views_parser_text,
+};
+
+static gboolean
+gb_shortcuts_dialog_custom_tag_start (GtkBuildable  *buildable,
+                                      GtkBuilder    *builder,
+                                      GObject       *child,
+                                      const gchar   *tagname,
+                                      GMarkupParser *parser,
+                                      gpointer      *data)
+{
+  GbShortcutsDialog *self = (GbShortcutsDialog *)buildable;
+
+  g_assert (GB_IS_SHORTCUTS_DIALOG (self));
+  g_assert (GTK_IS_BUILDER (builder));
+  g_assert (tagname != NULL);
+  g_assert (parser != NULL);
+  g_assert (data != NULL);
+
+  if (g_strcmp0 (tagname, "views") == 0)
+    {
+      ViewsParserData *parser_data;
+
+      parser_data = g_slice_new0 (ViewsParserData);
+      parser_data->self = g_object_ref (buildable);
+      parser_data->builder = g_object_ref (builder);
+      parser_data->stack = g_queue_new ();
+      parser_data->column_image_size_groups = g_queue_new ();
+      parser_data->column_desc_size_groups = g_queue_new ();
+
+      *parser = ViewsParser;
+      *data = parser_data;
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+gb_shortcuts_dialog_custom_finished (GtkBuildable *buildable,
+                                     GtkBuilder   *builder,
+                                     GObject      *child,
+                                     const gchar  *tagname,
+                                     gpointer      user_data)
+{
+  GbShortcutsDialog *self = (GbShortcutsDialog *)buildable;
+
+  g_assert (GB_IS_SHORTCUTS_DIALOG (self));
+  g_assert (GTK_IS_BUILDER (builder));
+  g_assert (tagname != NULL);
+
+  if (g_strcmp0 (tagname, "views") == 0)
+    {
+      ViewsParserData *parser_data = user_data;
+
+      g_object_unref (parser_data->self);
+      g_object_unref (parser_data->builder);
+      g_queue_free_full (parser_data->stack, (GDestroyNotify)g_object_unref);
+      g_queue_free_full (parser_data->column_image_size_groups, (GDestroyNotify)g_object_unref);
+      g_queue_free_full (parser_data->column_desc_size_groups, (GDestroyNotify)g_object_unref);
+      g_slice_free (ViewsParserData, parser_data);
+    }
+}
+
+static void
+gtk_buildable_iface_init (GtkBuildableIface *iface)
+{
+  iface->custom_tag_start = gb_shortcuts_dialog_custom_tag_start;
+  iface->custom_finished = gb_shortcuts_dialog_custom_finished;
+}
+
 static void
 gb_shortcuts_dialog_class_init (GbShortcutsDialogClass *klass)
 {
@@ -201,6 +594,7 @@ gb_shortcuts_dialog_init (GbShortcutsDialog *self)
   priv->stack = g_object_new (GTK_TYPE_STACK,
                               "expand", TRUE,
                               "homogeneous", TRUE,
+                              "transition-type", GTK_STACK_TRANSITION_TYPE_CROSSFADE,
                               "visible", TRUE,
                               NULL);
   gtk_container_add (GTK_CONTAINER (main_box), GTK_WIDGET (priv->stack));
diff --git a/src/shortcuts/gb-shortcuts-page.c b/src/shortcuts/gb-shortcuts-page.c
index 4e0af7f..6fea635 100644
--- a/src/shortcuts/gb-shortcuts-page.c
+++ b/src/shortcuts/gb-shortcuts-page.c
@@ -34,4 +34,5 @@ static void
 gb_shortcuts_page_init (GbShortcutsPage *self)
 {
   gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_HORIZONTAL);
+  gtk_box_set_spacing (GTK_BOX (self), 22);
 }
diff --git a/src/shortcuts/gb-shortcuts-shortcut.c b/src/shortcuts/gb-shortcuts-shortcut.c
index fc8889e..a7a67a5 100644
--- a/src/shortcuts/gb-shortcuts-shortcut.c
+++ b/src/shortcuts/gb-shortcuts-shortcut.c
@@ -21,7 +21,7 @@
 
 struct _GbShortcutsShortcut
 {
-  GtkBox parent_instance;
+  GtkBox        parent_instance;
 
   GbAccelLabel *accelerator;
   GtkLabel     *title;


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