[gtk/inspector-navigation: 2/2] inspector: Add dom-like navigation controls
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/inspector-navigation: 2/2] inspector: Add dom-like navigation controls
- Date: Mon, 6 Jul 2020 20:56:59 +0000 (UTC)
commit 5e1c83e79e24567bb299798f3abdbe5e80593e31
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 | 9 +-
gtk/inspector/list-data.c | 10 +-
gtk/inspector/prop-list.c | 8 +-
gtk/inspector/window.c | 352 +++++++++++++++++++++++++++++++++++++++++++-
gtk/inspector/window.h | 26 +++-
gtk/inspector/window.ui | 46 ++++++
6 files changed, 435 insertions(+), 16 deletions(-)
---
diff --git a/gtk/inspector/controllers.c b/gtk/inspector/controllers.c
index 0eae1caafd..f4c7ac075d 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, CHILD_KIND_CONTROLLER, controller);
}
static void
diff --git a/gtk/inspector/list-data.c b/gtk/inspector/list-data.c
index bfa0b7f9a1..294e7978ee 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,10 @@ static void
object_properties (GtkWidget *button,
GtkListItem *item)
{
- GtkInspectorListData *sl;
- gpointer obj;
+ GtkInspectorWindow *iw;
- 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);
+ iw = GTK_INSPECTOR_WINDOW (gtk_widget_get_ancestor (button, GTK_TYPE_INSPECTOR_WINDOW));
+ gtk_inspector_window_push_object (iw, CHILD_KIND_LISTITEM, gtk_list_item_get_item (item));
}
static void
diff --git a/gtk/inspector/prop-list.c b/gtk/inspector/prop-list.c
index 915dccf916..c9a07b8981 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, CHILD_KIND_PROPERTY, object);
}
diff --git a/gtk/inspector/window.c b/gtk/inspector/window.c
index a69daa30e6..7210884d02 100644
--- a/gtk/inspector/window.c
+++ b/gtk/inspector/window.c
@@ -122,8 +122,7 @@ on_object_activated (GtkInspectorObjectTree *wt,
{
const gchar *tab;
- if (!set_selected_object (iw, selected))
- return;
+ gtk_inspector_window_set_object (iw, CHILD_KIND_WIDGET, selected);
tab = g_object_get_data (G_OBJECT (wt), "next-tab");
if (tab)
@@ -180,8 +179,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, CHILD_KIND_WIDGET, selected);
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 +204,20 @@ translate_visible_child_name (GBinding *binding,
return TRUE;
}
+typedef struct
+{
+ GObject *object;
+ ChildKind kind;
+} 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 +305,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 +334,202 @@ 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))
+ {
+ gtk_inspector_window_replace_object (iw, (GObject*)gtk_widget_get_parent (widget));
+ 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, CHILD_KIND_WIDGET, G_OBJECT (child));
+ 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, CHILD_KIND_LISTITEM, item);
+ g_object_unref (item);
+ return;
+ }
+ }
+
+ gtk_widget_error_bell (GTK_WIDGET (iw));
+}
+
+static guint
+list_model_find_object (GListModel *model,
+ gpointer object)
+{
+
+ guint i, n;
+
+ n = g_list_model_get_n_items (model);
+ for (i = 0; i < n; i++)
+ {
+ gpointer item = g_list_model_get_item (model, i);
+
+ if (item == object)
+ {
+ g_object_unref (item);
+ return i;
+ }
+
+ g_object_unref (item);
+ }
+
+ return GTK_INVALID_LIST_POSITION;
+}
+
+static gpointer
+list_model_get_previous (GListModel *model,
+ gpointer item)
+{
+ guint position = list_model_find_object (model, item);
+
+ if (position == GTK_INVALID_LIST_POSITION || position == 0)
+ return NULL;
+
+ return g_list_model_get_item (model, position - 1);
+}
+
+static gpointer
+list_model_get_next (GListModel *model,
+ gpointer item)
+{
+ guint position = list_model_find_object (model, item);
+
+ return g_list_model_get_item (model, position + 1);
+}
+
+static void
+go_previous_cb (GtkWidget *button,
+ GtkInspectorWindow *iw)
+{
+ ChildData *odata;
+ ChildData *pdata;
+ GObject *object;
+ GObject *parent;
+
+ if (iw->objects->len < 2)
+ {
+ gtk_widget_error_bell (GTK_WIDGET (iw));
+ return;
+ }
+
+ odata = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
+ pdata = &g_array_index (iw->objects, ChildData, iw->objects->len - 2);
+
+ object = odata->object;
+ parent = pdata->object;
+
+ if (GTK_IS_WIDGET (object) && gtk_widget_get_parent (GTK_WIDGET (object)) == (GtkWidget*)parent)
+ {
+ GtkWidget *sibling = gtk_widget_get_prev_sibling (GTK_WIDGET (object));
+ if (sibling)
+ {
+ gtk_inspector_window_replace_object (iw, (GObject*)sibling);
+ return;
+ }
+ }
+ else if (G_IS_LIST_MODEL (parent))
+ {
+ GObject *item = list_model_get_previous (G_LIST_MODEL (parent), object);
+ if (item)
+ {
+ gtk_inspector_window_replace_object (iw, item);
+ g_object_unref (item);
+ return;
+ }
+ }
+
+ gtk_widget_error_bell (GTK_WIDGET (iw));
+}
+
+static void
+go_next_cb (GtkWidget *button,
+ GtkInspectorWindow *iw)
+{
+ ChildData *odata;
+ ChildData *pdata;
+ GObject *object;
+ GObject *parent;
+
+ if (iw->objects->len < 2)
+ {
+ gtk_widget_error_bell (GTK_WIDGET (iw));
+ return;
+ }
+
+ odata = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
+ pdata = &g_array_index (iw->objects, ChildData, iw->objects->len - 2);
+
+ object = odata->object;
+ parent = pdata->object;
+
+ if (GTK_IS_WIDGET (object) && gtk_widget_get_parent (GTK_WIDGET (object)) == (GtkWidget*)parent)
+ {
+ GtkWidget *sibling = gtk_widget_get_next_sibling (GTK_WIDGET (object));
+ if (sibling)
+ {
+ gtk_inspector_window_replace_object (iw, (GObject*)sibling);
+ return;
+ }
+ }
+ else if (G_IS_LIST_MODEL (parent))
+ {
+ GObject *item = list_model_get_next (G_LIST_MODEL (parent), object);
+ if (item)
+ {
+ gtk_inspector_window_replace_object (iw, item);
+ g_object_unref (item);
+ return;
+ }
+ }
+
+ gtk_widget_error_bell (GTK_WIDGET (iw));
+}
+
static void
gtk_inspector_window_realize (GtkWidget *widget)
{
@@ -461,6 +664,11 @@ 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_previous_button);
+ 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_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 +677,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 +881,137 @@ gtk_inspector_window_get_inspected_display (GtkInspectorWindow *iw)
return iw->inspected_display;
}
+static void
+update_go_buttons (GtkInspectorWindow *iw)
+{
+ ChildData *pdata;
+ ChildData *odata;
+ GObject *parent;
+ GObject *object;
+
+ if (iw->objects->len > 1)
+ {
+ pdata = &g_array_index (iw->objects, ChildData, iw->objects->len - 2);
+ parent = pdata->object;
+ }
+ else
+ {
+ pdata = NULL;
+ parent = NULL;
+ }
+
+ if (iw->objects->len > 0)
+ {
+ odata = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
+ object = odata->object;
+ }
+ else
+ {
+ odata = NULL;
+ object = NULL;
+ }
+
+ gtk_widget_set_sensitive (iw->go_up_button, parent != NULL || GTK_IS_WIDGET (object));
+
+ if (parent)
+ {
+ char *text;
+ text = g_strdup_printf ("Go to %s", G_OBJECT_TYPE_NAME (parent));
+ gtk_widget_set_tooltip_text (iw->go_up_button, text);
+ g_free (text);
+ }
+ else
+ {
+ gtk_widget_set_tooltip_text (iw->go_up_button, NULL);
+ }
+
+ switch (odata->kind)
+ {
+ case CHILD_KIND_WIDGET:
+ gtk_widget_set_sensitive (iw->go_down_button, TRUE);
+ gtk_widget_set_sensitive (iw->go_previous_button, TRUE);
+ gtk_widget_set_sensitive (iw->go_next_button, TRUE);
+ break;
+ case CHILD_KIND_LISTITEM:
+ gtk_widget_set_sensitive (iw->go_down_button, FALSE);
+ gtk_widget_set_sensitive (iw->go_previous_button, TRUE);
+ gtk_widget_set_sensitive (iw->go_next_button, TRUE);
+ break;
+ case CHILD_KIND_PROPERTY:
+ case CHILD_KIND_CONTROLLER:
+ gtk_widget_set_sensitive (iw->go_down_button, FALSE);
+ gtk_widget_set_sensitive (iw->go_previous_button, FALSE);
+ gtk_widget_set_sensitive (iw->go_next_button, FALSE);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+show_object_details (GtkInspectorWindow *iw,
+ GObject *object)
+{
+ set_selected_object (iw, object);
+
+ gtk_stack_set_visible_child_name (GTK_STACK (iw->object_details), "misc");
+ 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,
+ ChildKind kind,
+ GObject *object)
+{
+ ChildData data;
+
+ data.kind = kind;
+ data.object = object;
+ g_array_append_val (iw->objects, data);
+ show_object_details (iw, object);
+ update_go_buttons (iw);
+}
+
+void
+gtk_inspector_window_pop_object (GtkInspectorWindow *iw)
+{
+ ChildData *data;
+
+ if (iw->objects->len < 2)
+ {
+ gtk_widget_error_bell (GTK_WIDGET (iw));
+ return;
+ }
+
+ 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);
+ update_go_buttons (iw);
+}
+
+void
+gtk_inspector_window_replace_object (GtkInspectorWindow *iw,
+ GObject *object)
+{
+ ChildData *data;
+
+ data = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
+ data->object = object;
+ show_object_details (iw, object);
+ update_go_buttons (iw);
+}
+
+void
+gtk_inspector_window_set_object (GtkInspectorWindow *iw,
+ ChildKind kind,
+ GObject *object)
+{
+ g_array_set_size (iw->objects, 0);
+ gtk_inspector_window_push_object (iw, kind, object);
+ 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..02b5c9b3fa 100644
--- a/gtk/inspector/window.h
+++ b/gtk/inspector/window.h
@@ -78,6 +78,11 @@ typedef struct
GtkWidget *general;
GtkWidget *logs;
+ GtkWidget *go_previous_button;
+ GtkWidget *go_up_button;
+ GtkWidget *go_down_button;
+ GtkWidget *go_next_button;
+
GList *extra_pages;
GdkSeat *grab_seat;
@@ -86,6 +91,8 @@ typedef struct
gint flash_count;
gint flash_cnx;
+ GArray *objects;
+
GList *overlays;
GdkDisplay *inspected_display;
@@ -117,6 +124,24 @@ 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
+} ChildKind;
+
+void gtk_inspector_window_push_object (GtkInspectorWindow *iw,
+ ChildKind kind,
+ GObject *object);
+void gtk_inspector_window_pop_object (GtkInspectorWindow *iw);
+void gtk_inspector_window_set_object (GtkInspectorWindow *iw,
+ ChildKind kind,
+ GObject *object);
+void gtk_inspector_window_replace_object (GtkInspectorWindow *iw,
+ GObject *object);
+
gboolean gtk_inspector_is_recording (GtkWidget
*widget);
GskRenderNode * gtk_inspector_prepare_render (GtkWidget
*widget,
GskRenderer
*renderer,
@@ -124,7 +149,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..fd6e3148ec 100644
--- a/gtk/inspector/window.ui
+++ b/gtk/inspector/window.ui
@@ -339,6 +339,52 @@
</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_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="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_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]