forked from AuroraMiddleware/gtk
8d79a32c50
Change the apis in GtkListView, GtkColumnView and GtkGridView to be explicitly about GtkSelectionModel, to make it obvious that the widgets handle selection. Update all users.
1363 lines
45 KiB
C
1363 lines
45 KiB
C
/*
|
|
* Copyright (c) 2016 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "recorder.h"
|
|
|
|
#include <gtk/gtkbinlayout.h>
|
|
#include <gtk/gtkbox.h>
|
|
#include <gtk/gtkfilechooserdialog.h>
|
|
#include <gtk/gtksignallistitemfactory.h>
|
|
#include <gtk/gtklabel.h>
|
|
#include <gtk/gtklistbox.h>
|
|
#include <gtk/gtklistitem.h>
|
|
#include <gtk/gtklistview.h>
|
|
#include <gtk/gtkliststore.h>
|
|
#include <gtk/gtkmessagedialog.h>
|
|
#include <gtk/gtkpicture.h>
|
|
#include <gtk/gtkpopover.h>
|
|
#include <gtk/gtksingleselection.h>
|
|
#include <gtk/gtktogglebutton.h>
|
|
#include <gtk/gtktreeexpander.h>
|
|
#include <gtk/gtktreelistmodel.h>
|
|
#include <gtk/gtktreemodel.h>
|
|
#include <gtk/gtktreeview.h>
|
|
#include <gsk/gskrendererprivate.h>
|
|
#include <gsk/gskrendernodeprivate.h>
|
|
#include <gsk/gskroundedrectprivate.h>
|
|
#include <gsk/gsktransformprivate.h>
|
|
|
|
#include <glib/gi18n-lib.h>
|
|
#include <gdk/gdktextureprivate.h>
|
|
#include "gtk/gtkdebug.h"
|
|
#include "gtk/gtkbuiltiniconprivate.h"
|
|
#include "gtk/gtkrendernodepaintableprivate.h"
|
|
|
|
#include "recording.h"
|
|
#include "renderrecording.h"
|
|
#include "startrecording.h"
|
|
|
|
struct _GtkInspectorRecorder
|
|
{
|
|
GtkWidget parent;
|
|
|
|
GListModel *recordings;
|
|
GtkTreeListModel *render_node_model;
|
|
GListStore *render_node_root_model;
|
|
GtkSingleSelection *render_node_selection;
|
|
|
|
GtkWidget *box;
|
|
GtkWidget *recordings_list;
|
|
GtkWidget *render_node_view;
|
|
GtkWidget *render_node_list;
|
|
GtkWidget *render_node_save_button;
|
|
GtkWidget *node_property_tree;
|
|
GtkTreeModel *render_node_properties;
|
|
|
|
GtkInspectorRecording *recording; /* start recording if recording or NULL if not */
|
|
|
|
gboolean debug_nodes;
|
|
};
|
|
|
|
typedef struct _GtkInspectorRecorderClass
|
|
{
|
|
GtkWidgetClass parent;
|
|
} GtkInspectorRecorderClass;
|
|
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_RECORDING,
|
|
PROP_DEBUG_NODES,
|
|
LAST_PROP
|
|
};
|
|
|
|
static GParamSpec *props[LAST_PROP] = { NULL, };
|
|
|
|
G_DEFINE_TYPE (GtkInspectorRecorder, gtk_inspector_recorder, GTK_TYPE_WIDGET)
|
|
|
|
static GListModel *
|
|
create_render_node_list_model (GskRenderNode **nodes,
|
|
guint n_nodes)
|
|
{
|
|
GListStore *store;
|
|
guint i;
|
|
|
|
/* can't put render nodes into list models - they're not GObjects */
|
|
store = g_list_store_new (GDK_TYPE_PAINTABLE);
|
|
|
|
for (i = 0; i < n_nodes; i++)
|
|
{
|
|
graphene_rect_t bounds;
|
|
|
|
gsk_render_node_get_bounds (nodes[i], &bounds);
|
|
GdkPaintable *paintable = gtk_render_node_paintable_new (nodes[i], &bounds);
|
|
g_list_store_append (store, paintable);
|
|
g_object_unref (paintable);
|
|
}
|
|
|
|
return G_LIST_MODEL (store);
|
|
}
|
|
|
|
static GListModel *
|
|
create_list_model_for_render_node (GskRenderNode *node)
|
|
{
|
|
switch (gsk_render_node_get_node_type (node))
|
|
{
|
|
default:
|
|
case GSK_NOT_A_RENDER_NODE:
|
|
g_assert_not_reached ();
|
|
return NULL;
|
|
|
|
case GSK_CAIRO_NODE:
|
|
case GSK_TEXT_NODE:
|
|
case GSK_TEXTURE_NODE:
|
|
case GSK_COLOR_NODE:
|
|
case GSK_LINEAR_GRADIENT_NODE:
|
|
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
|
|
case GSK_BORDER_NODE:
|
|
case GSK_INSET_SHADOW_NODE:
|
|
case GSK_OUTSET_SHADOW_NODE:
|
|
/* no children */
|
|
return NULL;
|
|
|
|
case GSK_TRANSFORM_NODE:
|
|
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_transform_node_get_child (node) }, 1);
|
|
|
|
case GSK_OPACITY_NODE:
|
|
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_opacity_node_get_child (node) }, 1);
|
|
|
|
case GSK_COLOR_MATRIX_NODE:
|
|
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_color_matrix_node_get_child (node) }, 1);
|
|
|
|
case GSK_BLUR_NODE:
|
|
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_blur_node_get_child (node) }, 1);
|
|
|
|
case GSK_REPEAT_NODE:
|
|
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_repeat_node_get_child (node) }, 1);
|
|
|
|
case GSK_CLIP_NODE:
|
|
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_clip_node_get_child (node) }, 1);
|
|
|
|
case GSK_ROUNDED_CLIP_NODE:
|
|
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_rounded_clip_node_get_child (node) }, 1);
|
|
|
|
case GSK_SHADOW_NODE:
|
|
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_shadow_node_get_child (node) }, 1);
|
|
|
|
case GSK_BLEND_NODE:
|
|
return create_render_node_list_model ((GskRenderNode *[2]) { gsk_blend_node_get_bottom_child (node),
|
|
gsk_blend_node_get_top_child (node) }, 2);
|
|
|
|
case GSK_CROSS_FADE_NODE:
|
|
return create_render_node_list_model ((GskRenderNode *[2]) { gsk_cross_fade_node_get_start_child (node),
|
|
gsk_cross_fade_node_get_end_child (node) }, 2);
|
|
|
|
case GSK_CONTAINER_NODE:
|
|
{
|
|
GListStore *store;
|
|
guint i;
|
|
|
|
/* can't put render nodes into list models - they're not GObjects */
|
|
store = g_list_store_new (GDK_TYPE_PAINTABLE);
|
|
|
|
for (i = 0; i < gsk_container_node_get_n_children (node); i++)
|
|
{
|
|
GskRenderNode *child = gsk_container_node_get_child (node, i);
|
|
graphene_rect_t bounds;
|
|
GdkPaintable *paintable;
|
|
|
|
gsk_render_node_get_bounds (child, &bounds);
|
|
paintable = gtk_render_node_paintable_new (child, &bounds);
|
|
g_list_store_append (store, paintable);
|
|
g_object_unref (paintable);
|
|
}
|
|
|
|
return G_LIST_MODEL (store);
|
|
}
|
|
|
|
case GSK_DEBUG_NODE:
|
|
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_debug_node_get_child (node) }, 1);
|
|
}
|
|
}
|
|
|
|
static GListModel *
|
|
create_list_model_for_render_node_paintable (gpointer paintable,
|
|
gpointer unused)
|
|
{
|
|
GskRenderNode *node = gtk_render_node_paintable_get_render_node (paintable);
|
|
|
|
return create_list_model_for_render_node (node);
|
|
}
|
|
|
|
static void
|
|
recordings_clear_all (GtkButton *button,
|
|
GtkInspectorRecorder *recorder)
|
|
{
|
|
g_list_store_remove_all (G_LIST_STORE (recorder->recordings));
|
|
}
|
|
|
|
static const char *
|
|
node_type_name (GskRenderNodeType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case GSK_NOT_A_RENDER_NODE:
|
|
default:
|
|
g_assert_not_reached ();
|
|
return "Unknown";
|
|
case GSK_CONTAINER_NODE:
|
|
return "Container";
|
|
case GSK_DEBUG_NODE:
|
|
return "Debug";
|
|
case GSK_CAIRO_NODE:
|
|
return "Cairo";
|
|
case GSK_COLOR_NODE:
|
|
return "Color";
|
|
case GSK_LINEAR_GRADIENT_NODE:
|
|
return "Linear Gradient";
|
|
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
|
|
return "Repeating Linear Gradient";
|
|
case GSK_BORDER_NODE:
|
|
return "Border";
|
|
case GSK_TEXTURE_NODE:
|
|
return "Texture";
|
|
case GSK_INSET_SHADOW_NODE:
|
|
return "Inset Shadow";
|
|
case GSK_OUTSET_SHADOW_NODE:
|
|
return "Outset Shadow";
|
|
case GSK_TRANSFORM_NODE:
|
|
return "Transform";
|
|
case GSK_OPACITY_NODE:
|
|
return "Opacity";
|
|
case GSK_COLOR_MATRIX_NODE:
|
|
return "Color Matrix";
|
|
case GSK_REPEAT_NODE:
|
|
return "Repeat";
|
|
case GSK_CLIP_NODE:
|
|
return "Clip";
|
|
case GSK_ROUNDED_CLIP_NODE:
|
|
return "Rounded Clip";
|
|
case GSK_SHADOW_NODE:
|
|
return "Shadow";
|
|
case GSK_BLEND_NODE:
|
|
return "Blend";
|
|
case GSK_CROSS_FADE_NODE:
|
|
return "CrossFade";
|
|
case GSK_TEXT_NODE:
|
|
return "Text";
|
|
case GSK_BLUR_NODE:
|
|
return "Blur";
|
|
}
|
|
}
|
|
|
|
static char *
|
|
node_name (GskRenderNode *node)
|
|
{
|
|
switch (gsk_render_node_get_node_type (node))
|
|
{
|
|
case GSK_NOT_A_RENDER_NODE:
|
|
default:
|
|
g_assert_not_reached ();
|
|
case GSK_CONTAINER_NODE:
|
|
case GSK_CAIRO_NODE:
|
|
case GSK_LINEAR_GRADIENT_NODE:
|
|
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
|
|
case GSK_BORDER_NODE:
|
|
case GSK_INSET_SHADOW_NODE:
|
|
case GSK_OUTSET_SHADOW_NODE:
|
|
case GSK_TRANSFORM_NODE:
|
|
case GSK_OPACITY_NODE:
|
|
case GSK_COLOR_MATRIX_NODE:
|
|
case GSK_REPEAT_NODE:
|
|
case GSK_CLIP_NODE:
|
|
case GSK_ROUNDED_CLIP_NODE:
|
|
case GSK_SHADOW_NODE:
|
|
case GSK_BLEND_NODE:
|
|
case GSK_CROSS_FADE_NODE:
|
|
case GSK_TEXT_NODE:
|
|
case GSK_BLUR_NODE:
|
|
return g_strdup (node_type_name (gsk_render_node_get_node_type (node)));
|
|
|
|
case GSK_DEBUG_NODE:
|
|
return g_strdup (gsk_debug_node_get_message (node));
|
|
|
|
case GSK_COLOR_NODE:
|
|
return gdk_rgba_to_string (gsk_color_node_peek_color (node));
|
|
|
|
case GSK_TEXTURE_NODE:
|
|
{
|
|
GdkTexture *texture = gsk_texture_node_get_texture (node);
|
|
return g_strdup_printf ("%dx%d Texture", gdk_texture_get_width (texture), gdk_texture_get_height (texture));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
setup_widget_for_render_node (GtkSignalListItemFactory *factory,
|
|
GtkListItem *list_item)
|
|
{
|
|
GtkWidget *expander, *box, *child;
|
|
|
|
/* expander */
|
|
expander = gtk_tree_expander_new ();
|
|
gtk_list_item_set_child (list_item, expander);
|
|
|
|
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
|
|
gtk_tree_expander_set_child (GTK_TREE_EXPANDER (expander), box);
|
|
|
|
/* icon */
|
|
child = gtk_image_new ();
|
|
gtk_box_append (GTK_BOX (box), child);
|
|
|
|
/* name */
|
|
child = gtk_label_new (NULL);
|
|
gtk_box_append (GTK_BOX (box), child);
|
|
}
|
|
|
|
static void
|
|
bind_widget_for_render_node (GtkSignalListItemFactory *factory,
|
|
GtkListItem *list_item)
|
|
{
|
|
GdkPaintable *paintable;
|
|
GskRenderNode *node;
|
|
GtkTreeListRow *row_item;
|
|
GtkWidget *expander, *box, *child;
|
|
char *name;
|
|
|
|
row_item = gtk_list_item_get_item (list_item);
|
|
paintable = gtk_tree_list_row_get_item (row_item);
|
|
node = gtk_render_node_paintable_get_render_node (GTK_RENDER_NODE_PAINTABLE (paintable));
|
|
|
|
/* expander */
|
|
expander = gtk_list_item_get_child (list_item);
|
|
gtk_tree_expander_set_list_row (GTK_TREE_EXPANDER (expander), row_item);
|
|
box = gtk_tree_expander_get_child (GTK_TREE_EXPANDER (expander));
|
|
|
|
/* icon */
|
|
child = gtk_widget_get_first_child (box);
|
|
gtk_image_set_from_paintable (GTK_IMAGE (child), paintable);
|
|
|
|
/* name */
|
|
name = node_name (node);
|
|
child = gtk_widget_get_last_child (box);
|
|
gtk_label_set_label (GTK_LABEL (child), name);
|
|
g_free (name);
|
|
|
|
g_object_unref (paintable);
|
|
}
|
|
|
|
static void
|
|
recordings_list_row_selected (GtkListBox *box,
|
|
GtkListBoxRow *row,
|
|
GtkInspectorRecorder *recorder)
|
|
{
|
|
GtkInspectorRecording *recording;
|
|
|
|
if (row)
|
|
recording = g_list_model_get_item (recorder->recordings, gtk_list_box_row_get_index (row));
|
|
else
|
|
recording = NULL;
|
|
|
|
if (GTK_INSPECTOR_IS_RENDER_RECORDING (recording))
|
|
{
|
|
graphene_rect_t bounds;
|
|
GskRenderNode *node;
|
|
GdkPaintable *paintable;
|
|
|
|
node = gtk_inspector_render_recording_get_node (GTK_INSPECTOR_RENDER_RECORDING (recording));
|
|
gsk_render_node_get_bounds (node, &bounds);
|
|
paintable = gtk_render_node_paintable_new (node, &bounds);
|
|
gtk_picture_set_paintable (GTK_PICTURE (recorder->render_node_view), paintable);
|
|
|
|
g_list_store_splice (recorder->render_node_root_model,
|
|
0, g_list_model_get_n_items (G_LIST_MODEL (recorder->render_node_root_model)),
|
|
(gpointer[1]) { paintable },
|
|
1);
|
|
g_object_unref (paintable);
|
|
}
|
|
else
|
|
{
|
|
gtk_picture_set_paintable (GTK_PICTURE (recorder->render_node_view), NULL);
|
|
g_list_store_remove_all (recorder->render_node_root_model);
|
|
}
|
|
|
|
if (recording)
|
|
g_object_unref (recording);
|
|
}
|
|
|
|
static GdkTexture *
|
|
get_color_texture (const GdkRGBA *color)
|
|
{
|
|
GdkTexture *texture;
|
|
guchar pixel[4];
|
|
guchar *data;
|
|
GBytes *bytes;
|
|
int width = 30;
|
|
int height = 30;
|
|
int i;
|
|
|
|
pixel[0] = round (color->red * 255);
|
|
pixel[1] = round (color->green * 255);
|
|
pixel[2] = round (color->blue * 255);
|
|
pixel[3] = round (color->alpha * 255);
|
|
|
|
data = g_malloc (4 * width * height);
|
|
for (i = 0; i < width * height; i++)
|
|
{
|
|
memcpy (data + 4 * i, pixel, 4);
|
|
}
|
|
|
|
bytes = g_bytes_new_take (data, 4 * width * height);
|
|
texture = gdk_memory_texture_new (width,
|
|
height,
|
|
GDK_MEMORY_R8G8B8A8,
|
|
bytes,
|
|
width * 4);
|
|
g_bytes_unref (bytes);
|
|
|
|
return texture;
|
|
}
|
|
|
|
static GdkTexture *
|
|
get_linear_gradient_texture (gsize n_stops, const GskColorStop *stops)
|
|
{
|
|
cairo_surface_t *surface;
|
|
cairo_t *cr;
|
|
cairo_pattern_t *pattern;
|
|
GdkTexture *texture;
|
|
int i;
|
|
|
|
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 90, 30);
|
|
cr = cairo_create (surface);
|
|
|
|
pattern = cairo_pattern_create_linear (0, 0, 90, 0);
|
|
for (i = 0; i < n_stops; i++)
|
|
{
|
|
cairo_pattern_add_color_stop_rgba (pattern,
|
|
stops[i].offset,
|
|
stops[i].color.red,
|
|
stops[i].color.green,
|
|
stops[i].color.blue,
|
|
stops[i].color.alpha);
|
|
}
|
|
|
|
cairo_set_source (cr, pattern);
|
|
cairo_pattern_destroy (pattern);
|
|
cairo_rectangle (cr, 0, 0, 90, 30);
|
|
cairo_fill (cr);
|
|
cairo_destroy (cr);
|
|
|
|
texture = gdk_texture_new_for_surface (surface);
|
|
cairo_surface_destroy (surface);
|
|
|
|
return texture;
|
|
}
|
|
|
|
static void
|
|
add_text_row (GtkListStore *store,
|
|
const char *name,
|
|
const char *text)
|
|
{
|
|
gtk_list_store_insert_with_values (store, NULL, -1,
|
|
0, name,
|
|
1, text,
|
|
2, FALSE,
|
|
3, NULL,
|
|
-1);
|
|
}
|
|
|
|
static void
|
|
add_color_row (GtkListStore *store,
|
|
const char *name,
|
|
const GdkRGBA *color)
|
|
{
|
|
char *text;
|
|
GdkTexture *texture;
|
|
|
|
text = gdk_rgba_to_string (color);
|
|
texture = get_color_texture (color);
|
|
gtk_list_store_insert_with_values (store, NULL, -1,
|
|
0, name,
|
|
1, text,
|
|
2, TRUE,
|
|
3, texture,
|
|
-1);
|
|
g_free (text);
|
|
g_object_unref (texture);
|
|
}
|
|
|
|
static void
|
|
add_float_row (GtkListStore *store,
|
|
const char *name,
|
|
float value)
|
|
{
|
|
char *text = g_strdup_printf ("%.2f", value);
|
|
add_text_row (store, name, text);
|
|
g_free (text);
|
|
}
|
|
|
|
static void
|
|
populate_render_node_properties (GtkListStore *store,
|
|
GskRenderNode *node)
|
|
{
|
|
graphene_rect_t bounds;
|
|
char *tmp;
|
|
|
|
gtk_list_store_clear (store);
|
|
|
|
gsk_render_node_get_bounds (node, &bounds);
|
|
|
|
add_text_row (store, "Type", node_type_name (gsk_render_node_get_node_type (node)));
|
|
|
|
tmp = g_strdup_printf ("%.2f x %.2f + %.2f + %.2f",
|
|
bounds.size.width,
|
|
bounds.size.height,
|
|
bounds.origin.x,
|
|
bounds.origin.y);
|
|
add_text_row (store, "Bounds", tmp);
|
|
g_free (tmp);
|
|
|
|
switch (gsk_render_node_get_node_type (node))
|
|
{
|
|
case GSK_CAIRO_NODE:
|
|
{
|
|
GdkTexture *texture;
|
|
cairo_surface_t *drawn_surface;
|
|
cairo_t *cr;
|
|
gboolean show_inline;
|
|
|
|
drawn_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
|
ceilf (node->bounds.size.width),
|
|
ceilf (node->bounds.size.height));
|
|
cr = cairo_create (drawn_surface);
|
|
cairo_save (cr);
|
|
cairo_translate (cr, -node->bounds.origin.x, -node->bounds.origin.y);
|
|
gsk_render_node_draw (node, cr);
|
|
cairo_restore (cr);
|
|
|
|
cairo_destroy (cr);
|
|
|
|
texture = gdk_texture_new_for_surface (drawn_surface);
|
|
cairo_surface_destroy (drawn_surface);
|
|
|
|
show_inline = gdk_texture_get_height (texture) <= 40 &&
|
|
gdk_texture_get_width (texture) <= 100;
|
|
|
|
gtk_list_store_insert_with_values (store, NULL, -1,
|
|
0, "Surface",
|
|
1, show_inline ? "" : "Yes (click to show)",
|
|
2, show_inline,
|
|
3, texture,
|
|
-1);
|
|
}
|
|
break;
|
|
|
|
case GSK_TEXTURE_NODE:
|
|
{
|
|
GdkTexture *texture = g_object_ref (gsk_texture_node_get_texture (node));
|
|
gboolean show_inline;
|
|
|
|
show_inline = gdk_texture_get_height (texture) <= 40 &&
|
|
gdk_texture_get_width (texture) <= 100;
|
|
|
|
gtk_list_store_insert_with_values (store, NULL, -1,
|
|
0, "Texture",
|
|
1, show_inline ? "" : "Yes (click to show)",
|
|
2, show_inline,
|
|
3, texture,
|
|
-1);
|
|
}
|
|
break;
|
|
|
|
case GSK_COLOR_NODE:
|
|
add_color_row (store, "Color", gsk_color_node_peek_color (node));
|
|
break;
|
|
|
|
case GSK_LINEAR_GRADIENT_NODE:
|
|
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
|
|
{
|
|
const graphene_point_t *start = gsk_linear_gradient_node_peek_start (node);
|
|
const graphene_point_t *end = gsk_linear_gradient_node_peek_end (node);
|
|
const gsize n_stops = gsk_linear_gradient_node_get_n_color_stops (node);
|
|
const GskColorStop *stops = gsk_linear_gradient_node_peek_color_stops (node, NULL);
|
|
int i;
|
|
GString *s;
|
|
GdkTexture *texture;
|
|
|
|
tmp = g_strdup_printf ("%.2f %.2f ⟶ %.2f %.2f", start->x, start->y, end->x, end->y);
|
|
add_text_row (store, "Direction", tmp);
|
|
g_free (tmp);
|
|
|
|
s = g_string_new ("");
|
|
for (i = 0; i < n_stops; i++)
|
|
{
|
|
tmp = gdk_rgba_to_string (&stops[i].color);
|
|
g_string_append_printf (s, "%.2f, %s\n", stops[i].offset, tmp);
|
|
g_free (tmp);
|
|
}
|
|
|
|
texture = get_linear_gradient_texture (n_stops, stops);
|
|
gtk_list_store_insert_with_values (store, NULL, -1,
|
|
0, "Color Stops",
|
|
1, s->str,
|
|
2, TRUE,
|
|
3, texture,
|
|
-1);
|
|
g_string_free (s, TRUE);
|
|
g_object_unref (texture);
|
|
}
|
|
break;
|
|
|
|
case GSK_TEXT_NODE:
|
|
{
|
|
const PangoFont *font = gsk_text_node_peek_font (node);
|
|
const PangoGlyphInfo *glyphs = gsk_text_node_peek_glyphs (node, NULL);
|
|
const GdkRGBA *color = gsk_text_node_peek_color (node);
|
|
guint num_glyphs = gsk_text_node_get_num_glyphs (node);
|
|
const graphene_point_t *offset = gsk_text_node_get_offset (node);
|
|
PangoFontDescription *desc;
|
|
GString *s;
|
|
int i;
|
|
|
|
desc = pango_font_describe ((PangoFont *)font);
|
|
tmp = pango_font_description_to_string (desc);
|
|
add_text_row (store, "Font", tmp);
|
|
g_free (tmp);
|
|
pango_font_description_free (desc);
|
|
|
|
s = g_string_sized_new (6 * num_glyphs);
|
|
for (i = 0; i < num_glyphs; i++)
|
|
g_string_append_printf (s, "%x ", glyphs[i].glyph);
|
|
add_text_row (store, "Glyphs", s->str);
|
|
g_string_free (s, TRUE);
|
|
|
|
tmp = g_strdup_printf ("%.2f %.2f", offset->x, offset->y);
|
|
add_text_row (store, "Position", tmp);
|
|
g_free (tmp);
|
|
|
|
add_color_row (store, "Color", color);
|
|
}
|
|
break;
|
|
|
|
case GSK_BORDER_NODE:
|
|
{
|
|
const char *name[4] = { "Top", "Right", "Bottom", "Left" };
|
|
const float *widths = gsk_border_node_peek_widths (node);
|
|
const GdkRGBA *colors = gsk_border_node_peek_colors (node);
|
|
int i;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
GdkTexture *texture;
|
|
char *text;
|
|
|
|
texture = get_color_texture (&colors[i]);
|
|
text = gdk_rgba_to_string (&colors[i]);
|
|
tmp = g_strdup_printf ("%.2f, %s", widths[i], text);
|
|
gtk_list_store_insert_with_values (store, NULL, -1,
|
|
0, name[i],
|
|
1, tmp,
|
|
2, TRUE,
|
|
3, texture,
|
|
-1);
|
|
g_free (text);
|
|
g_free (tmp);
|
|
g_object_unref (texture);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GSK_OPACITY_NODE:
|
|
add_float_row (store, "Opacity", gsk_opacity_node_get_opacity (node));
|
|
break;
|
|
|
|
case GSK_CROSS_FADE_NODE:
|
|
add_float_row (store, "Progress", gsk_cross_fade_node_get_progress (node));
|
|
break;
|
|
|
|
case GSK_BLEND_NODE:
|
|
{
|
|
GskBlendMode mode = gsk_blend_node_get_blend_mode (node);
|
|
tmp = g_enum_to_string (GSK_TYPE_BLEND_MODE, mode);
|
|
add_text_row (store, "Blendmode", tmp);
|
|
g_free (tmp);
|
|
}
|
|
break;
|
|
|
|
case GSK_BLUR_NODE:
|
|
add_float_row (store, "Radius", gsk_blur_node_get_radius (node));
|
|
break;
|
|
|
|
case GSK_INSET_SHADOW_NODE:
|
|
{
|
|
const GdkRGBA *color = gsk_inset_shadow_node_peek_color (node);
|
|
float dx = gsk_inset_shadow_node_get_dx (node);
|
|
float dy = gsk_inset_shadow_node_get_dy (node);
|
|
float spread = gsk_inset_shadow_node_get_spread (node);
|
|
float radius = gsk_inset_shadow_node_get_blur_radius (node);
|
|
|
|
add_color_row (store, "Color", color);
|
|
|
|
tmp = g_strdup_printf ("%.2f %.2f", dx, dy);
|
|
add_text_row (store, "Offset", tmp);
|
|
g_free (tmp);
|
|
|
|
add_float_row (store, "Spread", spread);
|
|
add_float_row (store, "Radius", radius);
|
|
}
|
|
break;
|
|
|
|
case GSK_OUTSET_SHADOW_NODE:
|
|
{
|
|
const GskRoundedRect *outline = gsk_outset_shadow_node_peek_outline (node);
|
|
const GdkRGBA *color = gsk_outset_shadow_node_peek_color (node);
|
|
float dx = gsk_outset_shadow_node_get_dx (node);
|
|
float dy = gsk_outset_shadow_node_get_dy (node);
|
|
float spread = gsk_outset_shadow_node_get_spread (node);
|
|
float radius = gsk_outset_shadow_node_get_blur_radius (node);
|
|
float rect[12];
|
|
|
|
gsk_rounded_rect_to_float (outline, rect);
|
|
tmp = g_strdup_printf ("%.2f x %.2f + %.2f + %.2f",
|
|
rect[2], rect[3], rect[0], rect[1]);
|
|
add_text_row (store, "Outline", tmp);
|
|
g_free (tmp);
|
|
|
|
add_color_row (store, "Color", color);
|
|
|
|
tmp = g_strdup_printf ("%.2f %.2f", dx, dy);
|
|
add_text_row (store, "Offset", tmp);
|
|
g_free (tmp);
|
|
|
|
add_float_row (store, "Spread", spread);
|
|
add_float_row (store, "Radius", radius);
|
|
}
|
|
break;
|
|
|
|
case GSK_REPEAT_NODE:
|
|
{
|
|
const graphene_rect_t *child_bounds = gsk_repeat_node_peek_child_bounds (node);
|
|
|
|
tmp = g_strdup_printf ("%.2f x %.2f + %.2f + %.2f",
|
|
child_bounds->size.width,
|
|
child_bounds->size.height,
|
|
child_bounds->origin.x,
|
|
child_bounds->origin.y);
|
|
add_text_row (store, "Child Bounds", tmp);
|
|
g_free (tmp);
|
|
}
|
|
break;
|
|
|
|
case GSK_COLOR_MATRIX_NODE:
|
|
{
|
|
const graphene_matrix_t *matrix = gsk_color_matrix_node_peek_color_matrix (node);
|
|
const graphene_vec4_t *offset = gsk_color_matrix_node_peek_color_offset (node);
|
|
|
|
tmp = g_strdup_printf ("% .2f % .2f % .2f % .2f\n"
|
|
"% .2f % .2f % .2f % .2f\n"
|
|
"% .2f % .2f % .2f % .2f\n"
|
|
"% .2f % .2f % .2f % .2f",
|
|
graphene_matrix_get_value (matrix, 0, 0),
|
|
graphene_matrix_get_value (matrix, 0, 1),
|
|
graphene_matrix_get_value (matrix, 0, 2),
|
|
graphene_matrix_get_value (matrix, 0, 3),
|
|
graphene_matrix_get_value (matrix, 1, 0),
|
|
graphene_matrix_get_value (matrix, 1, 1),
|
|
graphene_matrix_get_value (matrix, 1, 2),
|
|
graphene_matrix_get_value (matrix, 1, 3),
|
|
graphene_matrix_get_value (matrix, 2, 0),
|
|
graphene_matrix_get_value (matrix, 2, 1),
|
|
graphene_matrix_get_value (matrix, 2, 2),
|
|
graphene_matrix_get_value (matrix, 2, 3),
|
|
graphene_matrix_get_value (matrix, 3, 0),
|
|
graphene_matrix_get_value (matrix, 3, 1),
|
|
graphene_matrix_get_value (matrix, 3, 2),
|
|
graphene_matrix_get_value (matrix, 3, 3));
|
|
add_text_row (store, "Matrix", tmp);
|
|
g_free (tmp);
|
|
tmp = g_strdup_printf ("%.2f %.2f %.2f %.2f",
|
|
graphene_vec4_get_x (offset),
|
|
graphene_vec4_get_y (offset),
|
|
graphene_vec4_get_z (offset),
|
|
graphene_vec4_get_w (offset));
|
|
add_text_row (store, "Offset", tmp);
|
|
g_free (tmp);
|
|
}
|
|
break;
|
|
|
|
case GSK_CLIP_NODE:
|
|
{
|
|
const graphene_rect_t *clip = gsk_clip_node_peek_clip (node);
|
|
tmp = g_strdup_printf ("%.2f x %.2f + %.2f + %.2f",
|
|
clip->size.width,
|
|
clip->size.height,
|
|
clip->origin.x,
|
|
clip->origin.y);
|
|
add_text_row (store, "Clip", tmp);
|
|
g_free (tmp);
|
|
}
|
|
break;
|
|
|
|
case GSK_ROUNDED_CLIP_NODE:
|
|
{
|
|
const GskRoundedRect *clip = gsk_rounded_clip_node_peek_clip (node);
|
|
tmp = g_strdup_printf ("%.2f x %.2f + %.2f + %.2f",
|
|
clip->bounds.size.width,
|
|
clip->bounds.size.height,
|
|
clip->bounds.origin.x,
|
|
clip->bounds.origin.y);
|
|
add_text_row (store, "Clip", tmp);
|
|
g_free (tmp);
|
|
|
|
tmp = g_strdup_printf ("%.2f x %.2f", clip->corner[0].width, clip->corner[0].height);
|
|
add_text_row (store, "Top Left Corner Size", tmp);
|
|
g_free (tmp);
|
|
|
|
tmp = g_strdup_printf ("%.2f x %.2f", clip->corner[1].width, clip->corner[1].height);
|
|
add_text_row (store, "Top Right Corner Size", tmp);
|
|
g_free (tmp);
|
|
|
|
tmp = g_strdup_printf ("%.2f x %.2f", clip->corner[2].width, clip->corner[2].height);
|
|
add_text_row (store, "Bottom Right Corner Size", tmp);
|
|
g_free (tmp);
|
|
|
|
tmp = g_strdup_printf ("%.2f x %.2f", clip->corner[3].width, clip->corner[3].height);
|
|
add_text_row (store, "Bottom Left Corner Size", tmp);
|
|
g_free (tmp);
|
|
}
|
|
break;
|
|
|
|
case GSK_CONTAINER_NODE:
|
|
tmp = g_strdup_printf ("%d", gsk_container_node_get_n_children (node));
|
|
add_text_row (store, "Children", tmp);
|
|
g_free (tmp);
|
|
break;
|
|
|
|
case GSK_DEBUG_NODE:
|
|
add_text_row (store, "Message", gsk_debug_node_get_message (node));
|
|
break;
|
|
|
|
case GSK_SHADOW_NODE:
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < gsk_shadow_node_get_n_shadows (node); i++)
|
|
{
|
|
char *label;
|
|
char *value;
|
|
const GskShadow *shadow = gsk_shadow_node_peek_shadow (node, i);
|
|
|
|
label = g_strdup_printf ("Color %d", i);
|
|
add_color_row (store, label, &shadow->color);
|
|
g_free (label);
|
|
|
|
label = g_strdup_printf ("Offset %d", i);
|
|
value = g_strdup_printf ("%.2f %.2f", shadow->dx, shadow->dy);
|
|
add_text_row (store, label, value);
|
|
g_free (value);
|
|
g_free (label);
|
|
|
|
label = g_strdup_printf ("Radius %d", i);
|
|
add_float_row (store, label, shadow->radius);
|
|
g_free (label);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GSK_TRANSFORM_NODE:
|
|
{
|
|
static const char * category_names[] = {
|
|
[GSK_TRANSFORM_CATEGORY_UNKNOWN] = "unknown",
|
|
[GSK_TRANSFORM_CATEGORY_ANY] = "any",
|
|
[GSK_TRANSFORM_CATEGORY_3D] = "3D",
|
|
[GSK_TRANSFORM_CATEGORY_2D] = "2D",
|
|
[GSK_TRANSFORM_CATEGORY_2D_AFFINE] = "2D affine",
|
|
[GSK_TRANSFORM_CATEGORY_2D_TRANSLATE] = "2D translate",
|
|
[GSK_TRANSFORM_CATEGORY_IDENTITY] = "identity"
|
|
};
|
|
GskTransform *transform;
|
|
char *s;
|
|
|
|
transform = gsk_transform_node_get_transform (node);
|
|
s = gsk_transform_to_string (transform);
|
|
add_text_row (store, "Matrix", s);
|
|
g_free (s);
|
|
add_text_row (store, "Category", category_names[gsk_transform_get_category (transform)]);
|
|
}
|
|
break;
|
|
|
|
case GSK_NOT_A_RENDER_NODE:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static GskRenderNode *
|
|
get_selected_node (GtkInspectorRecorder *recorder)
|
|
{
|
|
GtkTreeListRow *row_item;
|
|
GdkPaintable *paintable;
|
|
GskRenderNode *node;
|
|
|
|
row_item = gtk_single_selection_get_selected_item (recorder->render_node_selection);
|
|
if (row_item == NULL)
|
|
return NULL;
|
|
|
|
paintable = gtk_tree_list_row_get_item (row_item);
|
|
node = gtk_render_node_paintable_get_render_node (GTK_RENDER_NODE_PAINTABLE (paintable));
|
|
g_object_unref (paintable);
|
|
|
|
return node;
|
|
}
|
|
|
|
static void
|
|
render_node_list_selection_changed (GtkListBox *list,
|
|
GtkListBoxRow *row,
|
|
GtkInspectorRecorder *recorder)
|
|
{
|
|
GskRenderNode *node;
|
|
GdkPaintable *paintable;
|
|
GtkTreeListRow *row_item;
|
|
|
|
row_item = gtk_single_selection_get_selected_item (recorder->render_node_selection);
|
|
if (row_item == NULL)
|
|
return;
|
|
|
|
paintable = gtk_tree_list_row_get_item (row_item);
|
|
|
|
gtk_widget_set_sensitive (recorder->render_node_save_button, TRUE);
|
|
gtk_picture_set_paintable (GTK_PICTURE (recorder->render_node_view), paintable);
|
|
node = gtk_render_node_paintable_get_render_node (GTK_RENDER_NODE_PAINTABLE (paintable));
|
|
populate_render_node_properties (GTK_LIST_STORE (recorder->render_node_properties), node);
|
|
|
|
g_object_unref (paintable);
|
|
}
|
|
|
|
static void
|
|
render_node_save_response (GtkWidget *dialog,
|
|
int response,
|
|
GskRenderNode *node)
|
|
{
|
|
gtk_widget_hide (dialog);
|
|
|
|
if (response == GTK_RESPONSE_ACCEPT)
|
|
{
|
|
GBytes *bytes = gsk_render_node_serialize (node);
|
|
GError *error = NULL;
|
|
|
|
if (!g_file_replace_contents (gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)),
|
|
g_bytes_get_data (bytes, NULL),
|
|
g_bytes_get_size (bytes),
|
|
NULL,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&error))
|
|
{
|
|
GtkWidget *message_dialog;
|
|
|
|
message_dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_window_get_transient_for (GTK_WINDOW (dialog))),
|
|
GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
GTK_MESSAGE_INFO,
|
|
GTK_BUTTONS_OK,
|
|
_("Saving RenderNode failed"));
|
|
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (message_dialog),
|
|
"%s", error->message);
|
|
g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
|
gtk_widget_show (message_dialog);
|
|
g_error_free (error);
|
|
}
|
|
|
|
g_bytes_unref (bytes);
|
|
}
|
|
|
|
gtk_window_destroy (GTK_WINDOW (dialog));
|
|
}
|
|
|
|
static void
|
|
render_node_save (GtkButton *button,
|
|
GtkInspectorRecorder *recorder)
|
|
{
|
|
GskRenderNode *node;
|
|
GtkWidget *dialog;
|
|
char *filename, *nodename;
|
|
|
|
node = get_selected_node (recorder);
|
|
if (node == NULL)
|
|
return;
|
|
|
|
dialog = gtk_file_chooser_dialog_new ("",
|
|
GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (recorder))),
|
|
GTK_FILE_CHOOSER_ACTION_SAVE,
|
|
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
|
_("_Save"), GTK_RESPONSE_ACCEPT,
|
|
NULL);
|
|
nodename = node_name (node);
|
|
filename = g_strdup_printf ("%s.node", nodename);
|
|
g_free (nodename);
|
|
gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), filename);
|
|
g_free (filename);
|
|
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
|
|
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
|
|
g_signal_connect (dialog, "response", G_CALLBACK (render_node_save_response), node);
|
|
gtk_widget_show (dialog);
|
|
}
|
|
|
|
static void
|
|
toggle_dark_mode (GtkToggleButton *button,
|
|
GParamSpec *pspec,
|
|
gpointer data)
|
|
{
|
|
GtkWidget *picture = data;
|
|
|
|
if (gtk_toggle_button_get_active (button))
|
|
{
|
|
gtk_widget_add_css_class (picture, "dark");
|
|
gtk_widget_remove_css_class (picture, "light");
|
|
}
|
|
else
|
|
{
|
|
gtk_widget_remove_css_class (picture, "dark");
|
|
gtk_widget_add_css_class (picture, "light");
|
|
}
|
|
}
|
|
|
|
static GtkWidget *
|
|
gtk_inspector_recorder_recordings_list_create_widget (gpointer item,
|
|
gpointer user_data)
|
|
{
|
|
GtkInspectorRecording *recording = GTK_INSPECTOR_RECORDING (item);
|
|
GtkWidget *widget;
|
|
|
|
if (GTK_INSPECTOR_IS_RENDER_RECORDING (recording))
|
|
{
|
|
cairo_region_t *region;
|
|
GtkWidget *hbox, *label, *button;
|
|
|
|
widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
|
gtk_box_append (GTK_BOX (widget), hbox);
|
|
|
|
region = cairo_region_create_rectangle (
|
|
gtk_inspector_render_recording_get_area (GTK_INSPECTOR_RENDER_RECORDING (recording)));
|
|
cairo_region_subtract (region,
|
|
gtk_inspector_render_recording_get_clip_region (GTK_INSPECTOR_RENDER_RECORDING (recording)));
|
|
cairo_region_destroy (region);
|
|
|
|
label = gtk_label_new ("<b>Frame</b>");
|
|
gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
|
|
gtk_widget_set_hexpand (label, TRUE);
|
|
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
|
|
gtk_box_append (GTK_BOX (hbox), label);
|
|
|
|
button = gtk_toggle_button_new ();
|
|
gtk_button_set_has_frame (GTK_BUTTON (button), FALSE);
|
|
gtk_button_set_icon_name (GTK_BUTTON (button), "view-more-symbolic");
|
|
|
|
gtk_box_append (GTK_BOX (hbox), button);
|
|
|
|
label = gtk_label_new (gtk_inspector_render_recording_get_profiler_info (GTK_INSPECTOR_RENDER_RECORDING (recording)));
|
|
gtk_widget_hide (label);
|
|
gtk_box_append (GTK_BOX (widget), label);
|
|
g_object_bind_property (button, "active", label, "visible", 0);
|
|
}
|
|
else
|
|
{
|
|
widget = gtk_label_new ("<b>Start of Recording</b>");
|
|
gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
|
|
}
|
|
|
|
gtk_widget_set_margin_start (widget, 6);
|
|
gtk_widget_set_margin_end (widget, 6);
|
|
gtk_widget_set_margin_top (widget, 6);
|
|
gtk_widget_set_margin_bottom (widget, 6);
|
|
|
|
return widget;
|
|
}
|
|
|
|
static void
|
|
node_property_activated (GtkTreeView *tv,
|
|
GtkTreePath *path,
|
|
GtkTreeViewColumn *col,
|
|
GtkInspectorRecorder *recorder)
|
|
{
|
|
GtkTreeIter iter;
|
|
GdkRectangle rect;
|
|
GdkTexture *texture;
|
|
gboolean visible;
|
|
GtkWidget *popover;
|
|
GtkWidget *image;
|
|
|
|
gtk_tree_model_get_iter (GTK_TREE_MODEL (recorder->render_node_properties), &iter, path);
|
|
gtk_tree_model_get (GTK_TREE_MODEL (recorder->render_node_properties), &iter,
|
|
2, &visible,
|
|
3, &texture,
|
|
-1);
|
|
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);
|
|
|
|
if (texture == NULL || visible)
|
|
return;
|
|
|
|
popover = gtk_popover_new ();
|
|
gtk_widget_set_parent (popover, GTK_WIDGET (tv));
|
|
gtk_popover_set_pointing_to (GTK_POPOVER (popover), &rect);
|
|
|
|
image = gtk_image_new_from_paintable (GDK_PAINTABLE (texture));
|
|
gtk_widget_set_margin_start (image, 20);
|
|
gtk_widget_set_margin_end (image, 20);
|
|
gtk_widget_set_margin_top (image, 20);
|
|
gtk_widget_set_margin_bottom (image, 20);
|
|
gtk_popover_set_child (GTK_POPOVER (popover), image);
|
|
gtk_popover_popup (GTK_POPOVER (popover));
|
|
|
|
g_signal_connect (popover, "unmap", G_CALLBACK (gtk_widget_unparent), NULL);
|
|
|
|
g_object_unref (texture);
|
|
}
|
|
|
|
static void
|
|
gtk_inspector_recorder_get_property (GObject *object,
|
|
guint param_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkInspectorRecorder *recorder = GTK_INSPECTOR_RECORDER (object);
|
|
|
|
switch (param_id)
|
|
{
|
|
case PROP_RECORDING:
|
|
g_value_set_boolean (value, recorder->recording != NULL);
|
|
break;
|
|
|
|
case PROP_DEBUG_NODES:
|
|
g_value_set_boolean (value, recorder->debug_nodes);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_inspector_recorder_set_property (GObject *object,
|
|
guint param_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkInspectorRecorder *recorder = GTK_INSPECTOR_RECORDER (object);
|
|
|
|
switch (param_id)
|
|
{
|
|
case PROP_RECORDING:
|
|
gtk_inspector_recorder_set_recording (recorder, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_DEBUG_NODES:
|
|
gtk_inspector_recorder_set_debug_nodes (recorder, g_value_get_boolean (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_inspector_recorder_dispose (GObject *object)
|
|
{
|
|
GtkInspectorRecorder *recorder = GTK_INSPECTOR_RECORDER (object);
|
|
|
|
g_clear_pointer (&recorder->box, gtk_widget_unparent);
|
|
g_clear_object (&recorder->render_node_model);
|
|
g_clear_object (&recorder->render_node_root_model);
|
|
g_clear_object (&recorder->render_node_selection);
|
|
|
|
G_OBJECT_CLASS (gtk_inspector_recorder_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gtk_inspector_recorder_class_init (GtkInspectorRecorderClass *klass)
|
|
{
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->get_property = gtk_inspector_recorder_get_property;
|
|
object_class->set_property = gtk_inspector_recorder_set_property;
|
|
object_class->dispose = gtk_inspector_recorder_dispose;
|
|
|
|
props[PROP_RECORDING] =
|
|
g_param_spec_boolean ("recording",
|
|
"Recording",
|
|
"Whether the recorder is currently recording",
|
|
FALSE,
|
|
G_PARAM_READWRITE);
|
|
props[PROP_DEBUG_NODES] =
|
|
g_param_spec_boolean ("debug-nodes",
|
|
"Debug nodes",
|
|
"Whether to insert extra debug nodes in the tree",
|
|
FALSE,
|
|
G_PARAM_READWRITE);
|
|
|
|
g_object_class_install_properties (object_class, LAST_PROP, props);
|
|
|
|
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/recorder.ui");
|
|
|
|
gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, box);
|
|
gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, recordings);
|
|
gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, recordings_list);
|
|
gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, render_node_view);
|
|
gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, render_node_list);
|
|
gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, render_node_save_button);
|
|
gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, node_property_tree);
|
|
|
|
gtk_widget_class_bind_template_callback (widget_class, recordings_clear_all);
|
|
gtk_widget_class_bind_template_callback (widget_class, recordings_list_row_selected);
|
|
gtk_widget_class_bind_template_callback (widget_class, render_node_save);
|
|
gtk_widget_class_bind_template_callback (widget_class, node_property_activated);
|
|
gtk_widget_class_bind_template_callback (widget_class, toggle_dark_mode);
|
|
|
|
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
|
|
}
|
|
|
|
static void
|
|
gtk_inspector_recorder_init (GtkInspectorRecorder *recorder)
|
|
{
|
|
GtkListItemFactory *factory;
|
|
|
|
gtk_widget_init_template (GTK_WIDGET (recorder));
|
|
|
|
gtk_list_box_bind_model (GTK_LIST_BOX (recorder->recordings_list),
|
|
recorder->recordings,
|
|
gtk_inspector_recorder_recordings_list_create_widget,
|
|
recorder,
|
|
NULL);
|
|
|
|
recorder->render_node_root_model = g_list_store_new (GDK_TYPE_PAINTABLE);
|
|
recorder->render_node_model = gtk_tree_list_model_new (g_object_ref (G_LIST_MODEL (recorder->render_node_root_model)),
|
|
FALSE,
|
|
TRUE,
|
|
create_list_model_for_render_node_paintable,
|
|
NULL, NULL);
|
|
recorder->render_node_selection = gtk_single_selection_new (g_object_ref (G_LIST_MODEL (recorder->render_node_model)));
|
|
g_signal_connect (recorder->render_node_selection, "notify::selected-item", G_CALLBACK (render_node_list_selection_changed), recorder);
|
|
|
|
factory = gtk_signal_list_item_factory_new ();
|
|
g_signal_connect (factory, "setup", G_CALLBACK (setup_widget_for_render_node), NULL);
|
|
g_signal_connect (factory, "bind", G_CALLBACK (bind_widget_for_render_node), NULL);
|
|
|
|
gtk_list_view_set_factory (GTK_LIST_VIEW (recorder->render_node_list), factory);
|
|
g_object_unref (factory);
|
|
gtk_list_view_set_model (GTK_LIST_VIEW (recorder->render_node_list),
|
|
GTK_SELECTION_MODEL (recorder->render_node_selection));
|
|
|
|
recorder->render_node_properties = GTK_TREE_MODEL (gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, GDK_TYPE_TEXTURE));
|
|
gtk_tree_view_set_model (GTK_TREE_VIEW (recorder->node_property_tree), recorder->render_node_properties);
|
|
g_object_unref (recorder->render_node_properties);
|
|
}
|
|
|
|
static void
|
|
gtk_inspector_recorder_add_recording (GtkInspectorRecorder *recorder,
|
|
GtkInspectorRecording *recording)
|
|
{
|
|
g_list_store_append (G_LIST_STORE (recorder->recordings), recording);
|
|
}
|
|
|
|
void
|
|
gtk_inspector_recorder_set_recording (GtkInspectorRecorder *recorder,
|
|
gboolean recording)
|
|
{
|
|
if (gtk_inspector_recorder_is_recording (recorder) == recording)
|
|
return;
|
|
|
|
if (recording)
|
|
{
|
|
recorder->recording = gtk_inspector_start_recording_new ();
|
|
gtk_inspector_recorder_add_recording (recorder, recorder->recording);
|
|
}
|
|
else
|
|
{
|
|
g_clear_object (&recorder->recording);
|
|
}
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (recorder), props[PROP_RECORDING]);
|
|
}
|
|
|
|
gboolean
|
|
gtk_inspector_recorder_is_recording (GtkInspectorRecorder *recorder)
|
|
{
|
|
return recorder->recording != NULL;
|
|
}
|
|
|
|
void
|
|
gtk_inspector_recorder_record_render (GtkInspectorRecorder *recorder,
|
|
GtkWidget *widget,
|
|
GskRenderer *renderer,
|
|
GdkSurface *surface,
|
|
const cairo_region_t *region,
|
|
GskRenderNode *node)
|
|
{
|
|
GtkInspectorRecording *recording;
|
|
GdkFrameClock *frame_clock;
|
|
|
|
if (!gtk_inspector_recorder_is_recording (recorder))
|
|
return;
|
|
|
|
frame_clock = gtk_widget_get_frame_clock (widget);
|
|
|
|
recording = gtk_inspector_render_recording_new (gdk_frame_clock_get_frame_time (frame_clock),
|
|
gsk_renderer_get_profiler (renderer),
|
|
&(GdkRectangle) { 0, 0,
|
|
gdk_surface_get_width (surface),
|
|
gdk_surface_get_height (surface) },
|
|
region,
|
|
node);
|
|
gtk_inspector_recorder_add_recording (recorder, recording);
|
|
g_object_unref (recording);
|
|
}
|
|
|
|
void
|
|
gtk_inspector_recorder_set_debug_nodes (GtkInspectorRecorder *recorder,
|
|
gboolean debug_nodes)
|
|
{
|
|
guint flags;
|
|
|
|
if (recorder->debug_nodes == debug_nodes)
|
|
return;
|
|
|
|
recorder->debug_nodes = debug_nodes;
|
|
|
|
flags = gtk_get_debug_flags ();
|
|
|
|
if (debug_nodes)
|
|
flags |= GTK_DEBUG_SNAPSHOT;
|
|
else
|
|
flags &= ~GTK_DEBUG_SNAPSHOT;
|
|
|
|
gtk_set_debug_flags (flags);
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (recorder), props[PROP_DEBUG_NODES]);
|
|
}
|
|
|
|
// vim: set et sw=2 ts=2:
|