inspector: Redo the CSS node page

Showing two lists in a paned was a bit awkward, and space was
getting too limited. Go back to showing just the node list at
first, and make the CSS properties available via a stack. At
the same time, add a right-click context menu to the node list
to make the name and class editing more easily available.
This commit is contained in:
Matthias Clasen 2015-12-06 23:06:33 -05:00
parent 6f1c945010
commit 326f4739ca
2 changed files with 350 additions and 92 deletions

View File

@ -41,6 +41,8 @@
#include "gtktreeview.h"
#include "gtktreeselection.h"
#include "gtktypebuiltins.h"
#include "gtkmodelbutton.h"
#include "gtkstack.h"
enum {
COLUMN_NODE_NAME,
@ -63,6 +65,10 @@ enum
struct _GtkInspectorCssNodeTreePrivate
{
GtkWidget *node_tree;
GtkWidget *stack;
GtkWidget *node_list_button;
GtkWidget *prop_list_button;
GtkWidget *css_node_info;
GtkTreeModel *node_model;
GtkTreeViewColumn *node_name_column;
GtkTreeViewColumn *node_id_column;
@ -77,37 +83,23 @@ struct _GtkInspectorCssNodeTreePrivate
G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorCssNodeTree, gtk_inspector_css_node_tree, GTK_TYPE_BOX)
static void
row_activated (GtkTreeView *tv,
GtkTreePath *path,
GtkTreeViewColumn *col,
GtkInspectorCssNodeTree *cnt)
{
GtkTreeIter iter;
GdkRectangle rect;
GtkWidget *editor;
GtkWidget *popover;
typedef struct {
GtkCssNode *node;
const gchar *prop_name;
GdkRectangle rect;
GtkInspectorCssNodeTree *cnt;
} NodePropEditor;
if (col == cnt->priv->node_name_column)
prop_name = "name";
else if (col == cnt->priv->node_id_column)
prop_name = "id";
else if (col == cnt->priv->node_classes_column)
prop_name = "classes";
else
return;
static void
show_node_prop_editor (NodePropEditor *npe)
{
GtkWidget *popover;
GtkWidget *editor;
gtk_tree_model_get_iter (cnt->priv->node_model, &iter, path);
node = gtk_tree_model_css_node_get_node_from_iter (GTK_TREE_MODEL_CSS_NODE (cnt->priv->node_model), &iter);
gtk_tree_view_get_cell_area (tv, path, col, &rect);
gtk_tree_view_convert_bin_window_to_widget_coords (tv, rect.x, rect.y, &rect.x, &rect.y);
popover = gtk_popover_new (GTK_WIDGET (npe->cnt->priv->node_tree));
gtk_popover_set_pointing_to (GTK_POPOVER (popover), &npe->rect);
popover = gtk_popover_new (GTK_WIDGET (tv));
gtk_popover_set_pointing_to (GTK_POPOVER (popover), &rect);
editor = gtk_inspector_prop_editor_new (G_OBJECT (node), prop_name, FALSE);
editor = gtk_inspector_prop_editor_new (G_OBJECT (npe->node), npe->prop_name, FALSE);
gtk_widget_show (editor);
gtk_container_add (GTK_CONTAINER (popover), editor);
@ -120,12 +112,169 @@ row_activated (GtkTreeView *tv,
g_signal_connect (popover, "unmap", G_CALLBACK (gtk_widget_destroy), NULL);
}
static void populate_properties (GtkInspectorCssNodeTree *cnt);
static void
row_activated (GtkTreeView *tv,
GtkTreePath *path,
GtkTreeViewColumn *col,
GtkInspectorCssNodeTree *cnt)
{
GtkTreeIter iter;
NodePropEditor npe;
npe.cnt = cnt;
if (col == cnt->priv->node_name_column)
npe.prop_name = "name";
else if (col == cnt->priv->node_id_column)
npe.prop_name = "id";
else if (col == cnt->priv->node_classes_column)
npe.prop_name = "classes";
else
return;
gtk_tree_model_get_iter (cnt->priv->node_model, &iter, path);
npe.node = gtk_tree_model_css_node_get_node_from_iter (GTK_TREE_MODEL_CSS_NODE (cnt->priv->node_model), &iter);
gtk_tree_view_get_cell_area (tv, path, col, &npe.rect);
gtk_tree_view_convert_bin_window_to_widget_coords (tv, npe.rect.x, npe.rect.y, &npe.rect.x, &npe.rect.y);
show_node_prop_editor (&npe);
}
static void
switch_to_node_list (GtkInspectorCssNodeTree *cnt)
{
gtk_stack_set_visible_child_name (GTK_STACK (cnt->priv->stack), "node-list");
gtk_widget_show (cnt->priv->prop_list_button);
gtk_widget_hide (cnt->priv->node_list_button);
}
static void
switch_to_prop_list (GtkInspectorCssNodeTree *cnt)
{
gtk_stack_set_visible_child_name (GTK_STACK (cnt->priv->stack), "prop-list");
gtk_widget_show (cnt->priv->node_list_button);
gtk_widget_hide (cnt->priv->prop_list_button);
}
static void
show_node_popover (GtkInspectorCssNodeTree *cnt,
gdouble x,
gdouble y)
{
GtkTreeSelection *selection;
GdkRectangle rect;
GtkWidget *popover;
GtkWidget *box;
GtkWidget *button;
GtkTreeIter iter;
GtkTreePath *path;
NodePropEditor *npe;
GtkCssNode *node;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (cnt->priv->node_tree));
if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
return;
path = gtk_tree_model_get_path (cnt->priv->node_model, &iter);
gtk_tree_view_get_cell_area (GTK_TREE_VIEW (cnt->priv->node_tree), path, NULL, &rect);
gtk_tree_view_convert_bin_window_to_widget_coords (GTK_TREE_VIEW (cnt->priv->node_tree),
rect.x, rect.y, &rect.x, &rect.y);
rect.x = CLAMP (x - 20, 0, gtk_widget_get_allocated_width (cnt->priv->node_tree) - 40);
rect.width = 40;
node = gtk_tree_model_css_node_get_node_from_iter (GTK_TREE_MODEL_CSS_NODE (cnt->priv->node_model), &iter);
npe = g_new0 (NodePropEditor, 1);
npe->node = node;
npe->prop_name = "name";
npe->rect = rect;
npe->cnt = cnt;
popover = gtk_popover_new (GTK_WIDGET (cnt->priv->node_tree));
gtk_popover_set_pointing_to (GTK_POPOVER (popover), &rect);
box = g_object_new (GTK_TYPE_BOX,
"orientation", GTK_ORIENTATION_VERTICAL,
"visible", TRUE,
"margin", 10,
NULL);
gtk_container_add (GTK_CONTAINER (popover), box);
button = g_object_new (GTK_TYPE_MODEL_BUTTON,
"visible", TRUE,
"text", _("Change name"),
NULL);
g_signal_connect_swapped (button, "clicked", G_CALLBACK (show_node_prop_editor), npe);
g_object_set_data_full (G_OBJECT (popover), "prop-name", npe, g_free);
gtk_container_add (GTK_CONTAINER (box), button);
npe = g_new0 (NodePropEditor, 1);
npe->node = node;
npe->prop_name = "classes";
npe->rect = rect;
npe->cnt = cnt;
button = g_object_new (GTK_TYPE_MODEL_BUTTON,
"visible", TRUE,
"text", _("Change classes"),
NULL);
g_signal_connect_swapped (button, "clicked", G_CALLBACK (show_node_prop_editor), npe);
g_object_set_data_full (G_OBJECT (popover), "prop-classes", npe, g_free);
gtk_container_add (GTK_CONTAINER (box), button);
button = g_object_new (GTK_TYPE_MODEL_BUTTON,
"visible", TRUE,
"text", _("CSS properties"),
NULL);
g_signal_connect_swapped (button, "clicked", G_CALLBACK (switch_to_prop_list), cnt);
gtk_container_add (GTK_CONTAINER (box), button);
gtk_tree_path_free (path);
gtk_widget_show (popover);
g_signal_connect (popover, "unmap", G_CALLBACK (gtk_widget_destroy), NULL);
}
static gboolean
button_pressed (GtkWidget *widget,
GdkEventButton *event,
GtkInspectorCssNodeTree *cnt)
{
static gboolean in_press = FALSE;
if (in_press)
return FALSE;
if (!gdk_event_triggers_context_menu ((GdkEvent *)event))
return FALSE;
in_press = TRUE;
gtk_widget_event (cnt->priv->node_tree, (GdkEvent *) event);
in_press = FALSE;
show_node_popover (cnt, event->x, event->y);
return TRUE;
}
static void
gtk_inspector_css_node_tree_set_node (GtkInspectorCssNodeTree *cnt,
GtkCssNode *node);
static void
selection_changed (GtkTreeSelection *selection, GtkInspectorCssNodeTree *cnt)
{
populate_properties (cnt);
GtkTreeIter iter;
GtkCssNode *node;
if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
return;
node = gtk_tree_model_css_node_get_node_from_iter (GTK_TREE_MODEL_CSS_NODE (cnt->priv->node_model), &iter);
gtk_inspector_css_node_tree_set_node (cnt, node);
}
static void
@ -189,9 +338,16 @@ gtk_inspector_css_node_tree_class_init (GtkInspectorCssNodeTreeClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorCssNodeTree, prop_name_column);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorCssNodeTree, prop_model);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorCssNodeTree, prop_name_column);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorCssNodeTree, stack);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorCssNodeTree, prop_list_button);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorCssNodeTree, node_list_button);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorCssNodeTree, css_node_info);
gtk_widget_class_bind_template_callback (widget_class, row_activated);
gtk_widget_class_bind_template_callback (widget_class, selection_changed);
gtk_widget_class_bind_template_callback (widget_class, button_pressed);
gtk_widget_class_bind_template_callback (widget_class, switch_to_node_list);
gtk_widget_class_bind_template_callback (widget_class, switch_to_prop_list);
}
static int
@ -351,10 +507,14 @@ gtk_inspector_css_node_tree_set_object (GtkInspectorCssNodeTree *cnt,
if (!GTK_IS_WIDGET (object))
{
gtk_tree_model_css_node_set_root_node (GTK_TREE_MODEL_CSS_NODE (priv->node_model), NULL);
gtk_widget_hide (GTK_WIDGET (cnt));
return;
}
gtk_widget_show (GTK_WIDGET (cnt));
switch_to_node_list (cnt);
root = node = gtk_widget_get_css_node (GTK_WIDGET (object));
while (gtk_css_node_get_parent (root))
root = gtk_css_node_get_parent (root);
@ -426,6 +586,11 @@ gtk_inspector_css_node_tree_set_node (GtkInspectorCssNodeTree *cnt,
GtkCssNode *node)
{
GtkInspectorCssNodeTreePrivate *priv = cnt->priv;
GString *s;
GType type;
const gchar *name;
gchar **strv;
gint i;
if (priv->node == node)
return;
@ -443,22 +608,37 @@ gtk_inspector_css_node_tree_set_node (GtkInspectorCssNodeTree *cnt,
priv->node = node;
g_signal_connect (node, "style-changed", G_CALLBACK (gtk_inspector_css_node_tree_update_style), cnt);
}
static void
populate_properties (GtkInspectorCssNodeTree *cnt)
{
GtkInspectorCssNodeTreePrivate *priv = cnt->priv;
GtkTreeSelection *selection;
GtkTreeIter titer;
GtkCssNode *node;
s = g_string_new ("");
type = gtk_css_node_get_widget_type (node);
if (type != G_TYPE_NONE && type != G_TYPE_INVALID)
g_string_append (s, g_type_name (type));
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->node_tree));
if (!gtk_tree_selection_get_selected (selection, NULL, &titer))
return;
name = gtk_css_node_get_name (node);
if (name)
{
if (s->len > 0)
g_string_append (s, "");
g_string_append (s, name);
}
node = gtk_tree_model_css_node_get_node_from_iter (GTK_TREE_MODEL_CSS_NODE (priv->node_model), &titer);
gtk_inspector_css_node_tree_set_node (cnt, node);
strv = gtk_css_node_get_classes (node);
if (strv[0] != NULL)
{
strv_sort (strv);
if (s->len > 0)
g_string_append (s, "");
for (i = 0; strv[i]; i++)
{
if (i > 0)
g_string_append (s, " ");
g_string_append (s, strv[i]);
}
g_strfreev (strv);
}
gtk_label_set_label (GTK_LABEL (cnt->priv->css_node_info), s->str);
g_string_free (s, TRUE);
}
// vim: set et sw=2 ts=2:

View File

@ -12,17 +12,65 @@
<template class="GtkInspectorCssNodeTree" parent="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">1</property>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<child type="center">
<object class="GtkLabel" id="object_title">
<property name="visible">1</property>
<property name="valign">center</property>
<property name="margin-top">12</property>
<property name="margin-bottom">12</property>
<property name="margin">12</property>
</object>
</child>
<child>
<object class="GtkPaned">
<object class="GtkButton" id="node_list_button">
<property name="valign">center</property>
<property name="no-show-all">1</property>
<property name="relief">none</property>
<property name="tooltip-text" translatable="yes">Show all CSS nodes</property>
<signal name="clicked" handler="switch_to_node_list" swapped="1"/>
<child>
<object class="GtkImage">
<property name="visible">1</property>
<property name="orientation">vertical</property>
<property name="icon-name">pan-start-symbolic</property>
<property name="icon-size">1</property>
</object>
</child>
<style>
<class name="image-button"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="prop_list_button">
<property name="visible">1</property>
<property name="valign">center</property>
<property name="no-show-all">1</property>
<property name="relief">none</property>
<property name="tooltip-text" translatable="yes">Show CSS properties</property>
<signal name="clicked" handler="switch_to_prop_list" swapped="1"/>
<child>
<object class="GtkImage">
<property name="visible">1</property>
<property name="icon-name">pan-end-symbolic</property>
<property name="icon-size">1</property>
</object>
</child>
<style>
<class name="image-button"/>
</style>
</object>
<packing>
<property name="pack-type">end</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkStack" id="stack">
<property name="visible">1</property>
<property name="transition-type">slide-left-right</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">1</property>
@ -34,9 +82,9 @@
<property name="search-column">0</property>
<property name="enable-search">0</property>
<signal name="row-activated" handler="row_activated"/>
<signal name="button-press-event" handler="button_pressed"/>
<child internal-child="selection">
<object class="GtkTreeSelection">
<property name="mode">single</property>
<signal name="changed" handler="selection_changed"/>
</object>
</child>
@ -108,6 +156,31 @@
</object>
</child>
</object>
<packing>
<property name="name">node-list</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">1</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">1</property>
<property name="orientation">horizontal</property>
<property name="margin">12</property>
<child>
<object class="GtkLabel">
<property name="visible">1</property>
<property name="label" translatable="yes">Node:</property>
</object>
</child>
<child type="center">
<object class="GtkLabel" id="css_node_info">
<property name="visible">1</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
@ -122,7 +195,7 @@
<property name="enable-search">0</property>
<child>
<object class="GtkTreeViewColumn" id="prop_name_column">
<property name="title" translatable="yes">Property</property>
<property name="title" translatable="yes">CSS Property</property>
<property name="resizable">1</property>
<property name="sort-column-id">0</property>
<child>
@ -172,6 +245,11 @@
</object>
</child>
</object>
<packing>
<property name="name">prop-list</property>
</packing>
</child>
</object>
</child>
</template>
</interface>