[gtk/inspector-navigation: 6/6] inspector: Add dom-like navigation controls



commit 1b60c6f2416b41fbce0c282c11cfa00124146088
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon Jul 6 16:53:54 2020 -0400

    inspector: Add dom-like navigation controls
    
    Maintain a stack of objects, and add ˂˅˄˃ buttons
    that navigate this stack, as well as the widget
    tree and list models.

 gtk/inspector/controllers.c |  11 +-
 gtk/inspector/list-data.c   |  13 +-
 gtk/inspector/misc-info.c   |  29 ++--
 gtk/inspector/misc-info.ui  |   2 +-
 gtk/inspector/prop-list.c   |   8 +-
 gtk/inspector/window.c      | 395 +++++++++++++++++++++++++++++++++++++++++++-
 gtk/inspector/window.h      |  32 +++-
 gtk/inspector/window.ui     |  51 ++++++
 8 files changed, 511 insertions(+), 30 deletions(-)
---
diff --git a/gtk/inspector/controllers.c b/gtk/inspector/controllers.c
index 0eae1caafd..d140ac6b82 100644
--- a/gtk/inspector/controllers.c
+++ b/gtk/inspector/controllers.c
@@ -37,6 +37,7 @@
 #include "gtkstack.h"
 #include "gtkstylecontext.h"
 #include "gtkwidgetprivate.h"
+#include "window.h"
 
 struct _GtkInspectorControllers
 {
@@ -66,10 +67,14 @@ row_activated (GtkListBox              *box,
                GtkListBoxRow           *row,
                GtkInspectorControllers *self)
 {
+  GtkInspectorWindow *iw;
   GObject *controller;
-  
+
+  iw = GTK_INSPECTOR_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (self), GTK_TYPE_INSPECTOR_WINDOW));
+
   controller = G_OBJECT (g_object_get_data (G_OBJECT (row), "controller"));
-  gtk_inspector_object_tree_select_object (self->object_tree, controller);
+
+  gtk_inspector_window_push_object (iw, controller, CHILD_KIND_CONTROLLER, 0);
 }
 
 static void
@@ -130,7 +135,7 @@ create_controller_widget (gpointer item,
   const char *phases[5];
 
   row = gtk_list_box_row_new ();
-  gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
+  gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), TRUE);
   box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 40);
   gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), box);
   gtk_widget_set_margin_start (box, 10);
diff --git a/gtk/inspector/list-data.c b/gtk/inspector/list-data.c
index bfa0b7f9a1..f3b8ed5c42 100644
--- a/gtk/inspector/list-data.c
+++ b/gtk/inspector/list-data.c
@@ -31,6 +31,7 @@
 #include "gtknoselection.h"
 #include "gtksignallistitemfactory.h"
 #include "gtklistitem.h"
+#include "window.h"
 
 
 struct _GtkInspectorListData
@@ -167,13 +168,15 @@ static void
 object_properties (GtkWidget   *button,
                    GtkListItem *item)
 {
-  GtkInspectorListData *sl;
-  gpointer obj;
+  GtkInspectorWindow *iw;
+  GObject *obj;
+  guint pos;
+
+  iw = GTK_INSPECTOR_WINDOW (gtk_widget_get_ancestor (button, GTK_TYPE_INSPECTOR_WINDOW));
 
-  sl = GTK_INSPECTOR_LIST_DATA (gtk_widget_get_ancestor (button, GTK_TYPE_INSPECTOR_LIST_DATA));
   obj = gtk_list_item_get_item (item);
-  g_object_set_data (G_OBJECT (sl->object_tree), "next-tab", (gpointer)"properties");
-  gtk_inspector_object_tree_activate_object (sl->object_tree, obj);
+  pos = gtk_list_item_get_position (item);
+  gtk_inspector_window_push_object (iw, obj, CHILD_KIND_LISTITEM, pos);
 }
 
 static void
diff --git a/gtk/inspector/misc-info.c b/gtk/inspector/misc-info.c
index efd9e602bf..e7194be6a1 100644
--- a/gtk/inspector/misc-info.c
+++ b/gtk/inspector/misc-info.c
@@ -179,53 +179,56 @@ disconnect_each_other (gpointer  still_alive,
   g_object_weak_unref (still_alive, disconnect_each_other, for_science);
 }
 
-static void
-show_object (GtkInspectorMiscInfo *sl,
-             GObject              *object,
-             const gchar          *tab)
-{
-  g_object_set_data_full (G_OBJECT (sl->priv->object_tree), "next-tab", g_strdup (tab), g_free);
-  gtk_inspector_object_tree_activate_object (sl->priv->object_tree, object);
-}
-
 static void
 show_mnemonic_label (GtkWidget *button, GtkInspectorMiscInfo *sl)
 {
+  GtkInspectorWindow *iw;
   GtkWidget *widget;
 
+  iw = GTK_INSPECTOR_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (sl), GTK_TYPE_INSPECTOR_WINDOW));
+
   widget = g_object_get_data (G_OBJECT (button), "mnemonic-label");
   if (widget)
-    show_object (sl, G_OBJECT (widget), "properties");
+    gtk_inspector_window_push_object (iw, G_OBJECT (widget), CHILD_KIND_OTHER, 0);
 }
 
 static void
 show_surface (GtkWidget *button, GtkInspectorMiscInfo *sl)
 {
+  GtkInspectorWindow *iw;
   GObject *surface;
 
+  iw = GTK_INSPECTOR_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (sl), GTK_TYPE_INSPECTOR_WINDOW));
+
   surface = (GObject *)gtk_native_get_surface (GTK_NATIVE (sl->priv->object));
   if (surface)
-    show_object (sl, G_OBJECT (surface), "properties");
+    gtk_inspector_window_push_object (iw, surface, CHILD_KIND_OTHER, 0);
 }
 
 static void
 show_renderer (GtkWidget *button, GtkInspectorMiscInfo *sl)
 {
+  GtkInspectorWindow *iw;
   GObject *renderer;
 
+  iw = GTK_INSPECTOR_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (sl), GTK_TYPE_INSPECTOR_WINDOW));
+
   renderer = (GObject *)gtk_native_get_renderer (GTK_NATIVE (sl->priv->object));
   if (renderer)
-    show_object (sl, G_OBJECT (renderer), "properties");
+    gtk_inspector_window_push_object (iw, renderer, CHILD_KIND_OTHER, 0);
 }
 
 static void
 show_frame_clock (GtkWidget *button, GtkInspectorMiscInfo *sl)
 {
+  GtkInspectorWindow *iw;
   GObject *clock;
 
+  iw = GTK_INSPECTOR_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (sl), GTK_TYPE_INSPECTOR_WINDOW));
+
   clock = (GObject *)gtk_widget_get_frame_clock (GTK_WIDGET (sl->priv->object));
   if (clock)
-    show_object (sl, G_OBJECT (clock), "properties");
+    gtk_inspector_window_push_object (iw, clock, CHILD_KIND_OTHER, 0);
 }
 
 static void
diff --git a/gtk/inspector/misc-info.ui b/gtk/inspector/misc-info.ui
index 9761503e23..e19d381eb3 100644
--- a/gtk/inspector/misc-info.ui
+++ b/gtk/inspector/misc-info.ui
@@ -353,7 +353,7 @@
                                 <property name="halign">end</property>
                                 <property name="valign">baseline</property>
                                 <property name="label" translatable="yes">Properties</property>
-                                <signal name="clicked" handler="show_frame_clock"/>
+                                <signal name="clicked" handler="show_renderer"/>
                               </object>
                             </child>
                           </object>
diff --git a/gtk/inspector/prop-list.c b/gtk/inspector/prop-list.c
index 915dccf916..1ac46fcdf5 100644
--- a/gtk/inspector/prop-list.c
+++ b/gtk/inspector/prop-list.c
@@ -45,6 +45,7 @@
 #include "gtkgestureclick.h"
 #include "gtkstylecontext.h"
 #include "prop-holder.h"
+#include "window.h"
 
 enum
 {
@@ -201,9 +202,10 @@ show_object (GtkInspectorPropEditor *editor,
              const gchar            *tab,
              GtkInspectorPropList   *pl)
 {
-  g_object_set_data_full (G_OBJECT (pl->priv->object_tree), "next-tab", g_strdup (tab), g_free);
-  gtk_inspector_object_tree_select_object (pl->priv->object_tree, object);
-  gtk_inspector_object_tree_activate_object (pl->priv->object_tree, object);
+  GtkInspectorWindow *iw;
+
+  iw = GTK_INSPECTOR_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (pl), GTK_TYPE_INSPECTOR_WINDOW));
+  gtk_inspector_window_push_object (iw, object, CHILD_KIND_PROPERTY, 0);
 }
 
 
diff --git a/gtk/inspector/window.c b/gtk/inspector/window.c
index a69daa30e6..a1ef9bb754 100644
--- a/gtk/inspector/window.c
+++ b/gtk/inspector/window.c
@@ -122,8 +122,10 @@ on_object_activated (GtkInspectorObjectTree *wt,
 {
   const gchar *tab;
 
-  if (!set_selected_object (iw, selected))
-    return;
+  if (GTK_IS_WIDGET (selected))
+    gtk_inspector_window_set_object (iw, selected, CHILD_KIND_WIDGET, 0);
+  else
+    gtk_inspector_window_set_object (iw, selected, CHILD_KIND_OTHER, 0);
 
   tab = g_object_get_data (G_OBJECT (wt), "next-tab");
   if (tab)
@@ -180,8 +182,7 @@ open_object_details (GtkWidget *button, GtkInspectorWindow *iw)
 
   selected = gtk_inspector_object_tree_get_selected (GTK_INSPECTOR_OBJECT_TREE (iw->object_tree));
  
-  if (!set_selected_object (iw, selected))
-    return;
+  gtk_inspector_window_set_object (iw, selected, CHILD_KIND_WIDGET, 0);
 
   gtk_stack_set_visible_child_name (GTK_STACK (iw->object_stack), "object-details");
   gtk_stack_set_visible_child_name (GTK_STACK (iw->object_buttons), "details");
@@ -206,12 +207,21 @@ translate_visible_child_name (GBinding     *binding,
   return TRUE;
 }
 
+typedef struct
+{
+  GObject *object;
+  ChildKind kind;
+  guint position;
+} ChildData;
+
 static void
 gtk_inspector_window_init (GtkInspectorWindow *iw)
 {
   GIOExtensionPoint *extension_point;
   GList *l, *extensions;
 
+  iw->objects = g_array_new (FALSE, FALSE, sizeof (ChildData));
+
   gtk_widget_init_template (GTK_WIDGET (iw));
 
   g_object_bind_property_full (iw->object_details, "visible-child-name",
@@ -299,6 +309,7 @@ gtk_inspector_window_dispose (GObject *object)
   g_object_set_data (G_OBJECT (iw->inspected_display), "-gtk-inspector", NULL);
 
   g_clear_object (&iw->flash_overlay);
+  g_clear_pointer (&iw->objects, g_array_unref);
 
   G_OBJECT_CLASS (gtk_inspector_window_parent_class)->dispose (object);
 }
@@ -327,6 +338,201 @@ toggle_sidebar (GtkWidget          *button,
     }
 }
 
+static void
+go_up_cb (GtkWidget          *button,
+          GtkInspectorWindow *iw)
+{
+  if (iw->objects->len > 1)
+    {
+      gtk_inspector_window_pop_object (iw);
+      return;
+    }
+  else if (iw->objects->len > 0)
+    {
+      ChildData *data = &g_array_index (iw->objects, ChildData, 0);
+      GtkWidget *widget = (GtkWidget *)data->object;
+      if (GTK_IS_WIDGET (widget) && gtk_widget_get_parent (widget))
+        {
+          GObject *obj = G_OBJECT (gtk_widget_get_parent (widget));
+          gtk_inspector_window_replace_object (iw, obj, CHILD_KIND_WIDGET, 0);
+          return;
+        }
+    }
+
+  gtk_widget_error_bell (GTK_WIDGET (iw));
+}
+
+static void
+go_down_cb (GtkWidget          *button,
+            GtkInspectorWindow *iw)
+{
+  ChildData *data;
+  GObject *object;
+
+  if (iw->objects->len < 1)
+    {
+      gtk_widget_error_bell (GTK_WIDGET (iw));
+      return;
+    }
+
+  data = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
+  object = data->object;
+
+  if (GTK_IS_WIDGET (object))
+    {
+      GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (object));
+
+      if (child)
+        {
+          gtk_inspector_window_push_object (iw, G_OBJECT (child), CHILD_KIND_WIDGET, 0);
+          return;
+        }
+    }
+  else if (G_IS_LIST_MODEL (object))
+    {
+      GObject *item = g_list_model_get_item (G_LIST_MODEL (object), 0);
+      if (item)
+        {
+          gtk_inspector_window_push_object (iw, item, CHILD_KIND_LISTITEM, 0);
+          g_object_unref (item);
+          return;
+        }
+    }
+
+  gtk_widget_error_bell (GTK_WIDGET (iw));
+}
+
+static void
+go_previous_cb (GtkWidget          *button,
+                GtkInspectorWindow *iw)
+{
+  ChildData *data;
+  GObject *object;
+  GObject *parent;
+
+  if (iw->objects->len < 1)
+    {
+      gtk_widget_error_bell (GTK_WIDGET (iw));
+      return;
+    }
+
+  if (iw->objects->len > 1)
+    {
+      data = &g_array_index (iw->objects, ChildData, iw->objects->len - 2);
+      parent = data->object;
+    }
+  else
+    parent = NULL;
+
+  data = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
+  object = data->object;
+
+  switch (data->kind)
+    {
+    case CHILD_KIND_WIDGET:
+      {
+        GtkWidget *sibling = gtk_widget_get_prev_sibling (GTK_WIDGET (object));
+        if (sibling)
+          {
+            gtk_inspector_window_replace_object (iw, (GObject*)sibling, CHILD_KIND_WIDGET, 0);
+            return;
+          }
+      }
+      break;
+
+    case CHILD_KIND_LISTITEM:
+      {
+        GObject *item;
+
+        if (parent && data->position > 0)
+          item = g_list_model_get_item (G_LIST_MODEL (parent), data->position - 1);
+        else
+          item = NULL;
+
+        if (item)
+          {
+            gtk_inspector_window_replace_object (iw, item, CHILD_KIND_LISTITEM, data->position - 1);
+            g_object_unref (item);
+            return;
+          }
+      }
+      break;
+
+    case CHILD_KIND_CONTROLLER:
+    case CHILD_KIND_PROPERTY:
+    case CHILD_KIND_OTHER:
+    default: ;
+    }
+
+  gtk_widget_error_bell (GTK_WIDGET (iw));
+}
+
+static void
+go_next_cb (GtkWidget          *button,
+            GtkInspectorWindow *iw)
+{
+  ChildData *data;
+  GObject *object;
+  GObject *parent;
+
+  if (iw->objects->len < 1)
+    {
+      gtk_widget_error_bell (GTK_WIDGET (iw));
+      return;
+    }
+
+  if (iw->objects->len > 1)
+    {
+      data = &g_array_index (iw->objects, ChildData, iw->objects->len - 2);
+      parent = data->object;
+    }
+  else
+    parent = NULL;
+
+  data = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
+  object = data->object;
+
+  switch (data->kind)
+    {
+    case CHILD_KIND_WIDGET:
+      {
+        GtkWidget *sibling = gtk_widget_get_next_sibling (GTK_WIDGET (object));
+        if (sibling)
+          {
+            gtk_inspector_window_replace_object (iw, (GObject*)sibling, CHILD_KIND_WIDGET, 0);
+            return;
+          }
+      }
+      break;
+
+    case CHILD_KIND_LISTITEM:
+      {
+        GObject *item;
+
+        if (parent &&
+            data->position + 1 < g_list_model_get_n_items (G_LIST_MODEL (parent)))
+          item = g_list_model_get_item (G_LIST_MODEL (parent), data->position + 1);
+        else
+          item = NULL;
+
+        if (item)
+          {
+            gtk_inspector_window_replace_object (iw, item, CHILD_KIND_LISTITEM, data->position + 1);
+            g_object_unref (item);
+            return;
+          }
+      }
+      break;
+
+    case CHILD_KIND_CONTROLLER:
+    case CHILD_KIND_PROPERTY:
+    case CHILD_KIND_OTHER:
+    default: ;
+    }
+
+  gtk_widget_error_bell (GTK_WIDGET (iw));
+}
+
 static void
 gtk_inspector_window_realize (GtkWidget *widget)
 {
@@ -461,6 +667,12 @@ gtk_inspector_window_class_init (GtkInspectorWindowClass *klass)
   gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, general);
   gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, logs);
 
+  gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, go_up_button);
+  gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, go_down_button);
+  gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, go_previous_button);
+  gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, list_position_label);
+  gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, go_next_button);
+
   gtk_widget_class_bind_template_callback (widget_class, gtk_inspector_on_inspect);
   gtk_widget_class_bind_template_callback (widget_class, on_object_activated);
   gtk_widget_class_bind_template_callback (widget_class, on_object_selected);
@@ -469,6 +681,10 @@ gtk_inspector_window_class_init (GtkInspectorWindowClass *klass)
   gtk_widget_class_bind_template_callback (widget_class, object_details_changed);
   gtk_widget_class_bind_template_callback (widget_class, notify_node);
   gtk_widget_class_bind_template_callback (widget_class, toggle_sidebar);
+  gtk_widget_class_bind_template_callback (widget_class, go_previous_cb);
+  gtk_widget_class_bind_template_callback (widget_class, go_up_cb);
+  gtk_widget_class_bind_template_callback (widget_class, go_down_cb);
+  gtk_widget_class_bind_template_callback (widget_class, go_next_cb);
 }
 
 static GdkDisplay *
@@ -669,5 +885,176 @@ gtk_inspector_window_get_inspected_display (GtkInspectorWindow *iw)
   return iw->inspected_display;
 }
 
+static void
+update_go_button (GtkWidget  *button,
+                  gboolean    enabled,
+                  const char *tooltip)
+{
+  gtk_widget_set_sensitive (button, enabled);
+  gtk_widget_set_tooltip_text (button, tooltip);
+}
+
+static void
+update_go_buttons (GtkInspectorWindow *iw)
+{
+  GObject *parent;
+  GObject *object;
+  ChildKind kind;
+  guint position;
+
+  if (iw->objects->len > 1)
+    {
+      ChildData *data = &g_array_index (iw->objects, ChildData, iw->objects->len - 2);
+      parent = data->object;
+    }
+  else
+    {
+      parent = NULL;
+    }
+
+  if (iw->objects->len > 0)
+    {
+      ChildData *data = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
+      object = data->object;
+      kind = data->kind;
+      position = data->position;
+    }
+  else
+    {
+      object = NULL;
+      kind = CHILD_KIND_OTHER;
+    }
+
+  if (parent)
+    {
+      char *text;
+      text = g_strdup_printf ("Go to %s", G_OBJECT_TYPE_NAME (parent));
+      update_go_button (iw->go_up_button, TRUE, text);
+      g_free (text);
+    }
+  else
+    {
+      update_go_button (iw->go_up_button, GTK_IS_WIDGET (object) && !GTK_IS_ROOT (object), "Parent widget");
+    }
+
+  switch (kind)
+    {
+    case CHILD_KIND_WIDGET:
+      update_go_button (iw->go_down_button, gtk_widget_get_first_child (GTK_WIDGET (object)) != NULL, "First 
child");
+      update_go_button (iw->go_previous_button, gtk_widget_get_prev_sibling (GTK_WIDGET (object)) != NULL, 
"Previous sibling");
+      update_go_button (iw->go_next_button, gtk_widget_get_next_sibling (GTK_WIDGET (object)) != NULL, "Next 
sibling");
+      gtk_widget_hide (iw->list_position_label);
+      break;
+    case CHILD_KIND_LISTITEM:
+      update_go_button (iw->go_down_button, FALSE, NULL);
+      update_go_button (iw->go_previous_button, position > 0, "Previous list item");
+      update_go_button (iw->go_next_button, position + 1 < g_list_model_get_n_items (G_LIST_MODEL (parent)), 
"Next list item");
+      {
+        char *text = g_strdup_printf ("%u", position);
+        gtk_label_set_label (GTK_LABEL (iw->list_position_label), text);
+        g_free (text);
+        gtk_widget_show (iw->list_position_label);
+      }
+      break;
+    case CHILD_KIND_PROPERTY:
+    case CHILD_KIND_CONTROLLER:
+    case CHILD_KIND_OTHER:
+      update_go_button (iw->go_down_button, FALSE, NULL);
+      update_go_button (iw->go_previous_button, FALSE, NULL);
+      update_go_button (iw->go_next_button, FALSE, NULL);
+      gtk_widget_hide (iw->list_position_label);
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+}
+
+static void
+show_object_details (GtkInspectorWindow *iw,
+                     GObject            *object,
+                     const char         *tab)
+{
+  set_selected_object (iw, object);
+
+  if (tab)
+    gtk_stack_set_visible_child_name (GTK_STACK (iw->object_details), tab);
+  if (!gtk_stack_get_visible_child_name (GTK_STACK (iw->object_details)))
+    gtk_stack_set_visible_child_name (GTK_STACK (iw->object_details), "properties");
+
+  gtk_stack_set_visible_child_name (GTK_STACK (iw->object_stack), "object-details");
+  gtk_stack_set_visible_child_name (GTK_STACK (iw->object_buttons), "details");
+}
+
+void
+gtk_inspector_window_push_object (GtkInspectorWindow *iw,
+                                  GObject            *object,
+                                  ChildKind           kind,
+                                  guint               position)
+{
+  ChildData data;
+
+  data.kind = kind;
+  data.object = object;
+  data.position = position;
+  g_array_append_val (iw->objects, data);
+  show_object_details (iw, object, "properties");
+  update_go_buttons (iw);
+}
+
+void
+gtk_inspector_window_pop_object (GtkInspectorWindow *iw)
+{
+  ChildData *data;
+  const char *tabs[] = {
+    "properties",
+    "controllers",
+    "properties",
+    "list-data",
+    "misc",
+  };
+  const char *tab;
+
+  if (iw->objects->len < 2)
+    {
+      gtk_widget_error_bell (GTK_WIDGET (iw));
+      return;
+    }
+
+  data = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
+  tab = tabs[data->kind];
+  g_array_remove_index (iw->objects, iw->objects->len - 1);
+  data = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
+  show_object_details (iw, data->object, tab);
+  update_go_buttons (iw);
+}
+
+void
+gtk_inspector_window_replace_object (GtkInspectorWindow *iw,
+                                     GObject            *object,
+                                     ChildKind           kind,
+                                     guint               position)
+{
+  ChildData *data;
+
+  data = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
+  g_assert (data->kind == kind);
+  data->object = object;
+  data->position = position;
+  show_object_details (iw, object, NULL);
+  update_go_buttons (iw);
+}
+
+void
+gtk_inspector_window_set_object (GtkInspectorWindow *iw,
+                                 GObject            *object,
+                                 ChildKind           kind,
+                                 guint               position)
+{
+  g_array_set_size (iw->objects, 0);
+  gtk_inspector_window_push_object (iw, object, kind, position);
+  update_go_buttons (iw);
+}
+
 // vim: set et sw=2 ts=2:
 
diff --git a/gtk/inspector/window.h b/gtk/inspector/window.h
index d96c495755..928c1c5289 100644
--- a/gtk/inspector/window.h
+++ b/gtk/inspector/window.h
@@ -78,6 +78,12 @@ typedef struct
   GtkWidget *general;
   GtkWidget *logs;
 
+  GtkWidget *go_up_button;
+  GtkWidget *go_down_button;
+  GtkWidget *go_previous_button;
+  GtkWidget *list_position_label;
+  GtkWidget *go_next_button;
+
   GList *extra_pages;
 
   GdkSeat *grab_seat;
@@ -86,6 +92,8 @@ typedef struct
   gint flash_count;
   gint flash_cnx;
 
+  GArray *objects;
+
   GList *overlays;
 
   GdkDisplay *inspected_display;
@@ -117,6 +125,29 @@ void                    gtk_inspector_window_remove_overlay
 void                    gtk_inspector_window_select_widget_under_pointer        (GtkInspectorWindow     *iw);
 GdkDisplay *            gtk_inspector_window_get_inspected_display              (GtkInspectorWindow     *iw);
 
+typedef enum
+{
+  CHILD_KIND_WIDGET,
+  CHILD_KIND_CONTROLLER,
+  CHILD_KIND_PROPERTY,
+  CHILD_KIND_LISTITEM,
+  CHILD_KIND_OTHER
+} ChildKind;
+
+void                    gtk_inspector_window_push_object     (GtkInspectorWindow *iw,
+                                                              GObject            *object,
+                                                              ChildKind           kind,
+                                                              guint               position);
+void                    gtk_inspector_window_pop_object      (GtkInspectorWindow *iw);
+void                    gtk_inspector_window_set_object      (GtkInspectorWindow *iw,
+                                                              GObject            *object,
+                                                              ChildKind           kind,
+                                                              guint               position);
+void                    gtk_inspector_window_replace_object  (GtkInspectorWindow *iw,
+                                                              GObject            *object,
+                                                              ChildKind           kind,
+                                                              guint               position);
+
 gboolean                gtk_inspector_is_recording                              (GtkWidget              
*widget);
 GskRenderNode *         gtk_inspector_prepare_render                            (GtkWidget              
*widget,
                                                                                  GskRenderer            
*renderer,
@@ -124,7 +155,6 @@ GskRenderNode *         gtk_inspector_prepare_render
                                                                                  const cairo_region_t   
*region,
                                                                                  GskRenderNode          
*node);
 gboolean                gtk_inspector_handle_event                              (GdkEvent               
*event);
-                                                                                
 
 G_END_DECLS
 
diff --git a/gtk/inspector/window.ui b/gtk/inspector/window.ui
index 60a20b2573..2baeecda33 100644
--- a/gtk/inspector/window.ui
+++ b/gtk/inspector/window.ui
@@ -339,6 +339,57 @@
                                     </child>
                                   </object>
                                 </child>
+                                <child type="end">
+                                  <object class="GtkBox">
+                                    <property name="margin-start">6</property>
+                                    <property name="margin-end">6</property>
+                                    <child>
+                                      <object class="GtkButton" id="go_up_button">
+                                        <property name="icon-name">go-up-symbolic</property>
+                                        <property name="tooltip-text" translatable="yes">Previous 
object</property>
+                                        <property name="has-frame">0</property>
+                                        <property name="halign">center</property>
+                                        <property name="valign">center</property>
+                                        <signal name="clicked" handler="go_up_cb"/>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkButton" id="go_down_button">
+                                        <property name="icon-name">go-down-symbolic</property>
+                                        <property name="tooltip-text" translatable="yes">Child 
object</property>
+                                        <property name="has-frame">0</property>
+                                        <property name="halign">center</property>
+                                        <property name="valign">center</property>
+                                        <signal name="clicked" handler="go_down_cb"/>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkButton" id="go_previous_button">
+                                        <property name="icon-name">go-previous-symbolic</property>
+                                        <property name="tooltip-text" translatable="yes">Previous 
sibling</property>
+                                        <property name="has-frame">0</property>
+                                        <property name="halign">center</property>
+                                        <property name="valign">center</property>
+                                        <signal name="clicked" handler="go_previous_cb"/>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkLabel" id="list_position_label">
+                                        <property name="tooltip-text" translatable="yes">List 
Position</property>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkButton" id="go_next_button">
+                                        <property name="icon-name">go-next-symbolic</property>
+                                        <property name="tooltip-text" translatable="yes">Next 
sibling</property>
+                                        <property name="has-frame">0</property>
+                                        <property name="halign">center</property>
+                                        <property name="valign">center</property>
+                                        <signal name="clicked" handler="go_next_cb"/>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
                               </object>
                             </child>
                             <child>


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