[gtk/wip/otte/listmodel: 9/9] wip: inspector object tree as ListBox



commit d00307a02dc058dd5a2ef0478aa43cbc5b9e85cc
Author: Benjamin Otte <otte redhat com>
Date:   Wed Aug 29 16:19:37 2018 +0200

    wip: inspector object tree as ListBox

 gtk/inspector/object-tree.c  | 498 ++++++++++++++++++++++++++++++++++++++++++-
 gtk/inspector/object-tree.ui | 145 +++++++------
 2 files changed, 582 insertions(+), 61 deletions(-)
---
diff --git a/gtk/inspector/object-tree.c b/gtk/inspector/object-tree.c
index 08f3aa85af..b00be8bf93 100644
--- a/gtk/inspector/object-tree.c
+++ b/gtk/inspector/object-tree.c
@@ -34,11 +34,18 @@
 #include "gtkbutton.h"
 #include "gtkcelllayout.h"
 #include "gtkcomboboxprivate.h"
+#include "gtkfilterlistmodel.h"
+#include "gtkflattenlistmodel.h"
+#include "gtkiconprivate.h"
 #include "gtkiconview.h"
 #include "gtklabel.h"
+#include "gtklistbox.h"
 #include "gtkmenuitem.h"
 #include "gtksettings.h"
+#include "gtksizegroup.h"
 #include "gtktextview.h"
+#include "gtktogglebutton.h"
+#include "gtktreelistmodel.h"
 #include "gtktreeview.h"
 #include "gtktreeselection.h"
 #include "gtktreestore.h"
@@ -74,6 +81,8 @@ struct _GtkInspectorObjectTreePrivate
 {
   GtkTreeView *tree;
   GtkTreeStore *model;
+  GtkListBox *list;
+  GtkTreeListModel *list_model;
   gulong map_hook;
   gulong unmap_hook;
   GtkTreeViewColumn *object_column;
@@ -81,6 +90,9 @@ struct _GtkInspectorObjectTreePrivate
   GtkWidget *search_entry;
   GtkTreeWalk *walk;
   gint search_length;
+  GtkSizeGroup *type_size_group;
+  GtkSizeGroup *name_size_group;
+  GtkSizeGroup *label_size_group;
 };
 
 typedef struct _ObjectTreeClassFuncs ObjectTreeClassFuncs;
@@ -91,6 +103,7 @@ typedef void (* ObjectTreeForallFunc) (GObject    *object,
 struct _ObjectTreeClassFuncs {
   GType         (* get_type)            (void);
   GObject *     (* get_parent)          (GObject                *object);
+  GListModel *  (* get_children)        (GObject                *object);
   void          (* forall)              (GObject                *object,
                                          ObjectTreeForallFunc    forall_func,
                                          gpointer                forall_data);
@@ -107,6 +120,12 @@ object_tree_get_parent_default (GObject *object)
   return g_object_get_data (object, "inspector-object-tree-parent");
 }
 
+static GListModel *
+object_tree_get_children_default (GObject *object)
+{
+  return NULL;
+}
+
 static void
 object_tree_forall_default (GObject              *object,
                             ObjectTreeForallFunc  forall_func,
@@ -140,6 +159,30 @@ object_tree_widget_get_sensitive (GObject *object)
   return gtk_widget_get_mapped (GTK_WIDGET (object));
 }
 
+static GListModel *
+object_tree_widget_get_children (GObject *object)
+{
+  GtkWidget *widget = GTK_WIDGET (object);
+  GtkFlattenListModel *flatten;
+  GListStore *list;
+  GListModel *sublist;
+
+  list = g_list_store_new (G_TYPE_LIST_MODEL);
+
+  sublist = gtk_widget_observe_children (widget);
+  g_list_store_append (list, sublist);
+  g_object_unref (sublist);
+
+  sublist = gtk_widget_observe_controllers (widget);
+  g_list_store_append (list, sublist);
+  g_object_unref (sublist);
+
+  flatten = gtk_flatten_list_model_new (G_TYPE_OBJECT, G_LIST_MODEL (list));
+  g_object_unref (list);
+
+  return G_LIST_MODEL (flatten);
+}
+
 typedef struct {
   ObjectTreeForallFunc forall_func;
   gpointer             forall_data;
@@ -192,6 +235,17 @@ object_tree_widget_forall (GObject              *object,
      }
 }
 
+static GListModel *
+object_tree_tree_model_sort_get_children (GObject *object)
+{
+  GListStore *store;
+
+  store = g_list_store_new (G_TYPE_OBJECT);
+  g_list_store_append (store, gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (object)));
+
+  return G_LIST_MODEL (store);
+}
+
 static void
 object_tree_tree_model_sort_forall (GObject              *object,
                                     ObjectTreeForallFunc  forall_func,
@@ -203,6 +257,17 @@ object_tree_tree_model_sort_forall (GObject              *object,
     forall_func (child, "model", forall_data);
 }
 
+static GListModel *
+object_tree_tree_model_filter_get_children (GObject *object)
+{
+  GListStore *store;
+
+  store = g_list_store_new (G_TYPE_OBJECT);
+  g_list_store_append (store, gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (object)));
+
+  return G_LIST_MODEL (store);
+}
+
 static void
 object_tree_tree_model_filter_forall (GObject              *object,
                                       ObjectTreeForallFunc  forall_func,
@@ -214,6 +279,83 @@ object_tree_tree_model_filter_forall (GObject              *object,
     forall_func (child, "model", forall_data);
 }
 
+static void
+update_list_store (GListStore *store,
+                   GObject    *object,
+                   const char *property)
+{
+  gpointer value;
+
+  g_object_get (object, property, &value, NULL);
+  if (value)
+    {
+      g_list_store_splice (store,
+                           0,
+                           g_list_model_get_n_items (G_LIST_MODEL (store)),
+                           &value,
+                           1);
+    }
+  else
+    {
+      g_list_store_remove_all (store);
+    }
+}
+
+static void
+list_model_for_property_notify_cb (GObject    *object,
+                                   GParamSpec *pspec,
+                                   GListStore *store)
+{
+  update_list_store (store, object, pspec->name);
+}
+
+static GListModel *
+list_model_for_property (GObject    *object,
+                         const char *property)
+{
+  GListStore *store = g_list_store_new (G_TYPE_OBJECT);
+
+  /* g_signal_connect_object ("notify::property") */
+  g_signal_connect_closure_by_id (object,
+                                  g_signal_lookup (property, G_OBJECT_TYPE (object)),
+                                  g_quark_from_static_string (property),
+                                  g_cclosure_new_object (G_CALLBACK (list_model_for_property_notify_cb), 
G_OBJECT (store)),
+                                  FALSE);
+  update_list_store (store, object, property);
+
+  return G_LIST_MODEL (store);
+}
+
+static GListModel *
+list_model_for_properties (GObject     *object,
+                           const char **props)
+{
+  GListStore *concat;
+  GListModel *result;
+  guint i;
+
+  if (props[1] == NULL)
+    return list_model_for_property (object, props[0]);
+
+  concat = g_list_store_new (G_TYPE_LIST_MODEL);
+  for (i = 0; props[i]; i++)
+    {
+      GListModel *tmp = list_model_for_property (object, props[i]);
+      g_list_store_append (concat, tmp);
+      g_object_unref (tmp);
+    }
+
+  result = G_LIST_MODEL (gtk_flatten_list_model_new (G_TYPE_OBJECT, G_LIST_MODEL (concat)));
+  g_object_unref (concat);
+  return result;
+}
+
+static GListModel *
+object_tree_menu_item_get_children (GObject *object)
+{
+  return list_model_for_properties (object, (const char *[2]) { "submenu", NULL });
+}
+
 static void
 object_tree_menu_item_forall (GObject              *object,
                               ObjectTreeForallFunc  forall_func,
@@ -226,6 +368,12 @@ object_tree_menu_item_forall (GObject              *object,
     forall_func (G_OBJECT (submenu), "submenu", forall_data);
 }
 
+static GListModel *
+object_tree_combo_box_get_children (GObject *object)
+{
+  return list_model_for_properties (object, (const char *[3]) { "model", "popup", NULL });
+}
+
 static void
 object_tree_combo_box_forall (GObject              *object,
                               ObjectTreeForallFunc  forall_func,
@@ -243,6 +391,12 @@ object_tree_combo_box_forall (GObject              *object,
     forall_func (child, "model", forall_data);
 }
 
+static GListModel *
+object_tree_tree_view_get_children (GObject *object)
+{
+  return NULL;
+}
+
 static void
 object_tree_tree_view_forall (GObject              *object,
                               ObjectTreeForallFunc  forall_func,
@@ -267,6 +421,12 @@ object_tree_tree_view_forall (GObject              *object,
     }
 }
 
+static GListModel *
+object_tree_icon_view_get_children (GObject *object)
+{
+  return list_model_for_properties (object, (const char *[2]) { "model", NULL });
+}
+
 static void
 object_tree_icon_view_forall (GObject              *object,
                               ObjectTreeForallFunc  forall_func,
@@ -299,6 +459,12 @@ cell_callback (GtkCellRenderer *renderer,
   return FALSE;
 }
 
+static GListModel *
+object_tree_cell_area_get_children (GObject *object)
+{
+  return NULL;
+}
+
 static void
 object_tree_cell_area_forall (GObject              *object,
                               ObjectTreeForallFunc  forall_func,
@@ -313,6 +479,12 @@ object_tree_cell_area_forall (GObject              *object,
   gtk_cell_area_foreach (GTK_CELL_AREA (object), cell_callback, &data);
 }
 
+static GListModel *
+object_tree_cell_layout_get_children (GObject *object)
+{
+  return NULL;
+}
+
 static void
 object_tree_cell_layout_forall (GObject              *object,
                                 ObjectTreeForallFunc  forall_func,
@@ -332,6 +504,12 @@ object_tree_cell_layout_forall (GObject              *object,
   forall_func (G_OBJECT (area), "cell-area", forall_data);
 }
 
+static GListModel *
+object_tree_text_view_get_children (GObject *object)
+{
+  return list_model_for_properties (object, (const char *[2]) { "buffer", NULL });
+}
+
 static void
 object_tree_text_view_forall (GObject              *object,
                               ObjectTreeForallFunc  forall_func,
@@ -343,6 +521,12 @@ object_tree_text_view_forall (GObject              *object,
   forall_func (G_OBJECT (buffer), "buffer", forall_data);
 }
 
+static GListModel *
+object_tree_text_buffer_get_children (GObject *object)
+{
+  return list_model_for_properties (object, (const char *[2]) { "tag-table", NULL });
+}
+
 static void
 object_tree_text_buffer_forall (GObject              *object,
                                 ObjectTreeForallFunc  forall_func,
@@ -366,6 +550,12 @@ tag_callback (GtkTextTag *tag,
   g_free (name);
 }
 
+static GListModel *
+object_tree_text_tag_table_get_children (GObject *object)
+{
+  return NULL;
+}
+
 static void
 object_tree_text_tag_table_forall (GObject              *object,
                                    ObjectTreeForallFunc  forall_func,
@@ -379,6 +569,12 @@ object_tree_text_tag_table_forall (GObject              *object,
   gtk_text_tag_table_foreach (GTK_TEXT_TAG_TABLE (object), tag_callback, &data);
 }
 
+static GListModel *
+object_tree_application_get_children (GObject *object)
+{
+  return list_model_for_properties (object, (const char *[3]) { "app-menu", "menubar", NULL });
+}
+
 static void
 object_tree_application_forall (GObject              *object,
                                 ObjectTreeForallFunc  forall_func,
@@ -404,90 +600,105 @@ static const ObjectTreeClassFuncs object_tree_class_funcs[] = {
   {
     gtk_application_get_type,
     object_tree_get_parent_default,
+    object_tree_application_get_children,
     object_tree_application_forall,
     object_tree_get_sensitive_default
   },
   {
     gtk_text_tag_table_get_type,
     object_tree_get_parent_default,
+    object_tree_text_tag_table_get_children,
     object_tree_text_tag_table_forall,
     object_tree_get_sensitive_default
   },
   {
     gtk_text_buffer_get_type,
     object_tree_get_parent_default,
+    object_tree_text_buffer_get_children,
     object_tree_text_buffer_forall,
     object_tree_get_sensitive_default
   },
   {
     gtk_text_view_get_type,
     object_tree_widget_get_parent,
+    object_tree_text_view_get_children,
     object_tree_text_view_forall,
     object_tree_widget_get_sensitive
   },
   {
     gtk_icon_view_get_type,
     object_tree_widget_get_parent,
+    object_tree_icon_view_get_children,
     object_tree_icon_view_forall,
     object_tree_widget_get_sensitive
   },
   {
     gtk_tree_view_get_type,
     object_tree_widget_get_parent,
+    object_tree_tree_view_get_children,
     object_tree_tree_view_forall,
     object_tree_widget_get_sensitive
   },
   {
     gtk_combo_box_get_type,
     object_tree_widget_get_parent,
+    object_tree_combo_box_get_children,
     object_tree_combo_box_forall,
     object_tree_widget_get_sensitive
   },
   {
     gtk_menu_item_get_type,
     object_tree_widget_get_parent,
+    object_tree_menu_item_get_children,
     object_tree_menu_item_forall,
     object_tree_widget_get_sensitive
   },
   {
     gtk_menu_get_type,
     object_tree_menu_get_parent,
+    object_tree_widget_get_children,
     object_tree_widget_forall,
     object_tree_widget_get_sensitive
   },
   {
     gtk_widget_get_type,
     object_tree_widget_get_parent,
+    object_tree_widget_get_children,
     object_tree_widget_forall,
     object_tree_widget_get_sensitive
   },
   {
     gtk_tree_model_filter_get_type,
     object_tree_get_parent_default,
+    object_tree_tree_model_filter_get_children,
     object_tree_tree_model_filter_forall,
     object_tree_get_sensitive_default
   },
   {
     gtk_tree_model_sort_get_type,
     object_tree_get_parent_default,
+    object_tree_tree_model_sort_get_children,
     object_tree_tree_model_sort_forall,
     object_tree_get_sensitive_default
   },
   {
     gtk_cell_area_get_type,
     object_tree_get_parent_default,
+    object_tree_cell_area_get_children,
     object_tree_cell_area_forall,
     object_tree_get_sensitive_default
   },
   {
     gtk_cell_layout_get_type,
     object_tree_get_parent_default,
+    object_tree_cell_layout_get_children,
     object_tree_cell_layout_forall,
     object_tree_get_sensitive_default
   },
   {
     g_object_get_type,
     object_tree_get_parent_default,
+    object_tree_get_children_default,
     object_tree_forall_default,
     object_tree_get_sensitive_default
   },
@@ -522,6 +733,55 @@ object_get_parent (GObject *object)
   return funcs->get_parent (object);
 }
 
+static GListModel *
+object_get_children (GObject *object)
+{
+  GType object_type;
+  GListModel *result, *children;
+  GListStore *result_list;
+  guint i;
+
+  object_type = G_OBJECT_TYPE (object);
+  result = NULL;
+  result_list = NULL;
+
+  for (i = 0; i < G_N_ELEMENTS (object_tree_class_funcs); i++)
+    {
+      if (!g_type_is_a (object_type, object_tree_class_funcs[i].get_type ()))
+        continue;
+
+      children = object_tree_class_funcs[i].get_children (object);
+      if (children == NULL)
+        continue;
+
+      if (result_list)
+        {
+          g_list_store_append (result_list, children);
+          g_object_unref (result_list);
+        }
+      else if (result == NULL)
+        {
+          result = children;
+        }
+      else
+        {
+          result_list = g_list_store_new (G_TYPE_LIST_MODEL);
+          g_list_store_append (result_list, result);
+          g_object_unref (result);
+          g_list_store_append (result_list, children);
+          g_object_unref (children);
+        }
+    }
+
+  if (result_list)
+    {
+      result = G_LIST_MODEL (gtk_flatten_list_model_new (G_TYPE_OBJECT, G_LIST_MODEL (result_list)));
+      g_object_unref (result_list);
+    }
+
+  return result;
+}
+
 static void
 object_forall (GObject              *object,
                ObjectTreeForallFunc  forall_func,
@@ -936,9 +1196,151 @@ stop_search (GtkWidget              *entry,
   gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (wt->priv->search_bar), FALSE);
 }
 
+static GtkWidget *
+gtk_inspector_object_tree_create_list_widget (gpointer row_item,
+                                              gpointer user_data)
+{
+  GtkInspectorObjectTree *wt = user_data;
+  gpointer item;
+  const gchar *name;
+  GtkWidget *row, *box, *column, *child;
+  guint depth;
+
+  row = gtk_list_box_row_new ();
+  g_object_set_data_full (G_OBJECT (row), "make-sure-its-not-unreffed", g_object_ref (row_item), 
g_object_unref);
+
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  gtk_container_add (GTK_CONTAINER (row), box);
+
+  column = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  gtk_size_group_add_widget (wt->priv->type_size_group, column);
+  gtk_container_add (GTK_CONTAINER (box), column);
+
+  /* expander */
+  depth = gtk_tree_list_row_get_depth (row_item);
+  if (depth > 0)
+    {
+      child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+      gtk_widget_set_size_request (child, 16 * depth, 0);
+      gtk_container_add (GTK_CONTAINER (column), child);
+    }
+  if (gtk_tree_list_row_is_expandable (row_item))
+    {
+      GtkWidget *title, *arrow;
+
+      child = g_object_new (GTK_TYPE_BOX, "css-name", "expander", NULL);
+      
+      title = g_object_new (GTK_TYPE_TOGGLE_BUTTON, "css-name", "title", NULL);
+      gtk_button_set_relief (GTK_BUTTON (title), GTK_RELIEF_NONE);
+      g_object_bind_property (row_item, "expanded", title, "active", G_BINDING_BIDIRECTIONAL | 
G_BINDING_SYNC_CREATE);
+      gtk_container_add (GTK_CONTAINER (child), title);
+
+      arrow = gtk_icon_new ("arrow");
+      gtk_container_add (GTK_CONTAINER (title), arrow);
+    }
+  else
+    {
+      child = gtk_image_new (); /* empty whatever */
+    }
+  gtk_container_add (GTK_CONTAINER (column), child);
+
+  item = gtk_tree_list_row_get_item (row_item);
+
+  /* 1st column: type name */
+  child = gtk_label_new (G_OBJECT_TYPE_NAME (item));
+  gtk_label_set_width_chars (GTK_LABEL (child), 30);
+  gtk_label_set_xalign (GTK_LABEL (child), 0.0);
+  gtk_container_add (GTK_CONTAINER (column), child);
+
+  /* 2nd column: name */
+  name = NULL;
+
+  if (GTK_IS_WIDGET (item))
+    {
+      const gchar *id;
+
+      id = gtk_widget_get_name (item);
+      if (name == NULL && id != NULL && g_strcmp0 (id, G_OBJECT_TYPE_NAME (item)) != 0)
+        name = id;
+    }
+
+  if (GTK_IS_BUILDABLE (item))
+    {
+      const gchar *id;
+      id = gtk_buildable_get_name (item);
+      if (name == NULL && id != NULL && !g_str_has_prefix (id, "___object_"))
+        name = id;
+    }
+
+  child = gtk_label_new (name);
+  gtk_label_set_width_chars (GTK_LABEL (child), 15);
+  gtk_label_set_xalign (GTK_LABEL (child), 0.0);
+  gtk_size_group_add_widget (wt->priv->name_size_group, child);
+  gtk_container_add (GTK_CONTAINER (box), child);
+
+  /* 3rd column: label */
+  child = gtk_label_new (NULL);
+  if (GTK_IS_LABEL (item))
+    g_object_bind_property (item, "label", child, "label", G_BINDING_SYNC_CREATE);
+  else if (GTK_IS_BUTTON (item))
+    g_object_bind_property (item, "label", child, "label", G_BINDING_SYNC_CREATE);
+  else if (GTK_IS_WINDOW (item))
+    g_object_bind_property (item, "title", child, "label", G_BINDING_SYNC_CREATE);
+  else if (GTK_IS_TREE_VIEW_COLUMN (item))
+    g_object_bind_property (item, "title", child, "label", G_BINDING_SYNC_CREATE);
+  gtk_label_set_width_chars (GTK_LABEL (child), 15);
+  gtk_label_set_xalign (GTK_LABEL (child), 0.0);
+  gtk_size_group_add_widget (wt->priv->label_size_group, child);
+  gtk_container_add (GTK_CONTAINER (box), child);
+
+  g_object_unref (item);
+
+  return row;
+}
+
+static GListModel *
+create_model_for_object (gpointer object,
+                         gpointer user_data)
+{
+  return object_get_children (object);
+}
+
+static gboolean
+toplevel_filter_func (gpointer item,
+                      gpointer data)
+{
+  GdkDisplay *display = data;
+
+  if (!GTK_IS_WINDOW (item))
+    return FALSE;
+
+  if (g_str_equal (G_OBJECT_TYPE_NAME (item), "GtkInspectorWindow"))
+    return FALSE;
+
+  return gtk_window_get_window_type (item) == GTK_WINDOW_TOPLEVEL &&
+         gtk_widget_get_display (item) == display;
+}
+
+static GListModel *
+create_root_model (void)
+{
+  GtkFilterListModel *filter;
+
+  filter = gtk_filter_list_model_new_for_type (G_TYPE_OBJECT);
+
+  gtk_filter_list_model_set_filter_func (filter, 
+                                         toplevel_filter_func,
+                                         g_object_ref (gdk_display_get_default ()),
+                                         g_object_unref);
+  gtk_filter_list_model_set_model (filter, gtk_window_get_toplevels ());
+
+  return G_LIST_MODEL (filter);
+}
+
 static void
 gtk_inspector_object_tree_init (GtkInspectorObjectTree *wt)
 {
+  GListModel *root_model;
   guint signal_id;
 
   wt->priv = gtk_inspector_object_tree_get_instance_private (wt);
@@ -959,6 +1361,21 @@ gtk_inspector_object_tree_init (GtkInspectorObjectTree *wt)
                                                    map_or_unmap, wt, NULL);
 
   gtk_inspector_object_tree_append_object (wt, G_OBJECT (gtk_settings_get_default ()), NULL, NULL);
+
+  root_model = create_root_model ();
+  wt->priv->list_model = gtk_tree_list_model_new (FALSE,
+                                                  root_model,
+                                                  FALSE,
+                                                  create_model_for_object,
+                                                  NULL,
+                                                  NULL);
+  g_object_unref (root_model);
+
+  gtk_list_box_bind_model (wt->priv->list,
+                           G_LIST_MODEL (wt->priv->list_model),
+                           gtk_inspector_object_tree_create_list_widget,
+                           wt,
+                           NULL);
 }
 
 static void
@@ -968,6 +1385,8 @@ gtk_inspector_object_tree_dispose (GObject *object)
 
   clear_store (wt);
 
+  g_clear_object (&wt->priv->list_model);
+
   G_OBJECT_CLASS (gtk_inspector_object_tree_parent_class)->dispose (object);
 }
 
@@ -1017,9 +1436,13 @@ gtk_inspector_object_tree_class_init (GtkInspectorObjectTreeClass *klass)
   gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/object-tree.ui");
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorObjectTree, model);
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorObjectTree, tree);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorObjectTree, list);
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorObjectTree, object_column);
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorObjectTree, search_bar);
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorObjectTree, search_entry);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorObjectTree, type_size_group);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorObjectTree, name_size_group);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorObjectTree, label_size_group);
   gtk_widget_class_bind_template_callback (widget_class, on_selection_changed);
   gtk_widget_class_bind_template_callback (widget_class, on_row_activated);
   gtk_widget_class_bind_template_callback (widget_class, on_hierarchy_changed);
@@ -1201,11 +1624,84 @@ select_object_internal (GtkInspectorObjectTree *wt,
   return FALSE;
 }
 
+static guint
+model_get_item_index (GListModel *model,
+                      gpointer    item)
+{
+  gpointer cmp;
+  guint i;
+
+  for (i = 0; (cmp = g_list_model_get_item (model, i)); i++)
+    {
+      if (cmp == item)
+        {
+          g_object_unref (cmp);
+          return i;
+        }
+      g_object_unref (cmp);
+    }
+
+  return G_MAXUINT;
+}
+
+static GtkTreeListRow *
+find_and_expand_object (GtkTreeListModel *model,
+                        GObject          *object)
+{
+  GtkTreeListRow *result;
+  GObject *parent;
+  guint pos;
+
+  parent = object_get_parent (object);
+  if (parent)
+    {
+      GtkTreeListRow *parent_row = find_and_expand_object (model, parent);
+      if (parent_row == NULL)
+        return NULL;
+
+      gtk_tree_list_row_set_expanded (parent_row, TRUE);
+      pos = model_get_item_index (gtk_tree_list_row_get_children (parent_row), object);
+      result = gtk_tree_list_row_get_child (parent_row, pos);
+      g_object_unref (parent_row);
+    }
+  else
+    {
+      pos = model_get_item_index (gtk_tree_list_model_get_model (model), object);
+      result = gtk_tree_list_model_get_child (model, pos);
+    }
+  
+  return result;
+}
+
+static void
+select_object_in_listbox (GtkInspectorObjectTree *wt,
+                          GObject                *object)
+{
+  GtkTreeListRow *row_item;
+  GtkListBoxRow *row_widget;
+
+  row_item = find_and_expand_object (wt->priv->list_model, object);
+  if (row_item == NULL)
+    return;
+
+  row_widget = gtk_list_box_get_row_at_index (wt->priv->list,
+                                              gtk_tree_list_row_get_position (row_item));
+  g_return_if_fail (row_widget != NULL);
+  gtk_list_box_select_row (wt->priv->list, row_widget);
+  g_object_unref (row_item);
+}
+
 gboolean
 gtk_inspector_object_tree_select_object (GtkInspectorObjectTree *wt,
                                          GObject                *object)
 {
-  return select_object_internal (wt, object, TRUE);
+  gboolean result;
+
+  result = select_object_internal (wt, object, TRUE);
+
+  select_object_in_listbox (wt, object);
+
+  return result;
 }
 
 void
diff --git a/gtk/inspector/object-tree.ui b/gtk/inspector/object-tree.ui
index 07d85d276d..9b8b4ec1c5 100644
--- a/gtk/inspector/object-tree.ui
+++ b/gtk/inspector/object-tree.ui
@@ -48,85 +48,110 @@
       </object>
     </child>
     <child>
-      <object class="GtkScrolledWindow">
-        <property name="hscrollbar-policy">never</property>
-        <property name="expand">1</property>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <property name="homogeneous">true</property>
         <child>
-          <object class="GtkTreeView" id="tree">
-            <property name="model">model</property>
-            <property name="enable-search">0</property>
-            <property name="enable-grid-lines">vertical</property>
-            <signal name="row-activated" handler="on_row_activated"/>
-            <child internal-child="selection">
-              <object class="GtkTreeSelection">
-                <signal name="changed" handler="on_selection_changed"/>
-              </object>
-            </child>
+          <object class="GtkScrolledWindow">
+            <property name="hscrollbar-policy">never</property>
+            <property name="expand">1</property>
             <child>
-              <object class="GtkTreeViewColumn" id="object_column">
-                <property name="title" translatable="yes">Object</property>
-                <property name="resizable">1</property>
+              <object class="GtkTreeView" id="tree">
+                <property name="model">model</property>
+                <property name="enable-search">0</property>
+                <property name="enable-grid-lines">vertical</property>
+                <signal name="row-activated" handler="on_row_activated"/>
+                <child internal-child="selection">
+                  <object class="GtkTreeSelection">
+                    <signal name="changed" handler="on_selection_changed"/>
+                  </object>
+                </child>
                 <child>
-                  <object class="GtkCellRendererText">
-                    <property name="scale">0.8</property>
+                  <object class="GtkTreeViewColumn" id="object_column">
+                    <property name="title" translatable="yes">Object</property>
+                    <property name="resizable">1</property>
+                    <child>
+                      <object class="GtkCellRendererText">
+                        <property name="scale">0.8</property>
+                      </object>
+                      <attributes>
+                        <attribute name="text">1</attribute>
+                        <attribute name="sensitive">5</attribute>
+                      </attributes>
+                    </child>
                   </object>
-                  <attributes>
-                    <attribute name="text">1</attribute>
-                    <attribute name="sensitive">5</attribute>
-                  </attributes>
                 </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkTreeViewColumn">
-                <property name="title" translatable="yes">Name</property>
-                <property name="resizable">1</property>
                 <child>
-                  <object class="GtkCellRendererText">
-                    <property name="scale">0.8</property>
+                  <object class="GtkTreeViewColumn">
+                    <property name="title" translatable="yes">Name</property>
+                    <property name="resizable">1</property>
+                    <child>
+                      <object class="GtkCellRendererText">
+                        <property name="scale">0.8</property>
+                      </object>
+                      <attributes>
+                        <attribute name="text">2</attribute>
+                        <attribute name="sensitive">5</attribute>
+                      </attributes>
+                    </child>
                   </object>
-                  <attributes>
-                    <attribute name="text">2</attribute>
-                    <attribute name="sensitive">5</attribute>
-                  </attributes>
                 </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkTreeViewColumn">
-                <property name="title" translatable="yes">Label</property>
-                <property name="resizable">1</property>
                 <child>
-                  <object class="GtkCellRendererText">
-                    <property name="scale">0.8</property>
-                    <property name="ellipsize">end</property>
-                    <property name="max-width-chars">30</property>
+                  <object class="GtkTreeViewColumn">
+                    <property name="title" translatable="yes">Label</property>
+                    <property name="resizable">1</property>
+                    <child>
+                      <object class="GtkCellRendererText">
+                        <property name="scale">0.8</property>
+                        <property name="ellipsize">end</property>
+                        <property name="max-width-chars">30</property>
+                      </object>
+                      <attributes>
+                        <attribute name="text">3</attribute>
+                        <attribute name="sensitive">5</attribute>
+                      </attributes>
+                    </child>
                   </object>
-                  <attributes>
-                    <attribute name="text">3</attribute>
-                    <attribute name="sensitive">5</attribute>
-                  </attributes>
                 </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkTreeViewColumn">
-                <property name="title" translatable="yes">Style Classes</property>
-                <property name="resizable">1</property>
                 <child>
-                  <object class="GtkCellRendererText">
-                    <property name="scale">0.8</property>
+                  <object class="GtkTreeViewColumn">
+                    <property name="title" translatable="yes">Style Classes</property>
+                    <property name="resizable">1</property>
+                    <child>
+                      <object class="GtkCellRendererText">
+                        <property name="scale">0.8</property>
+                      </object>
+                      <attributes>
+                        <attribute name="text">4</attribute>
+                        <attribute name="sensitive">5</attribute>
+                      </attributes>
+                    </child>
                   </object>
-                  <attributes>
-                    <attribute name="text">4</attribute>
-                    <attribute name="sensitive">5</attribute>
-                  </attributes>
                 </child>
               </object>
             </child>
           </object>
         </child>
+        <child>
+          <object class="GtkScrolledWindow">
+            <property name="hscrollbar-policy">never</property>
+            <property name="expand">1</property>
+            <child>
+              <object class="GtkListBox" id="list">
+              </object>
+            </child>
+          </object>
+        </child>
       </object>
     </child>
   </template>
+  <object class="GtkSizeGroup" id="type_size_group">
+    <property name="mode">both</property>
+  </object>
+  <object class="GtkSizeGroup" id="name_size_group">
+    <property name="mode">both</property>
+  </object>
+  <object class="GtkSizeGroup" id="label_size_group">
+    <property name="mode">both</property>
+  </object>
 </interface>


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