From 66c74d60913230eb77e3773625f90d7394a27215 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 16 Dec 2021 18:05:31 +0100 Subject: [PATCH] inspector: Add measure graph Generates a graph visualizing calls to gtk_widget_measure(). Generation of the graph can be slow - like when it forces Pango to wrap a huge label 1000s of times. You can dnd the graph to look at it closer or to impress people in gitlab issues. --- gtk/inspector/init.c | 2 + gtk/inspector/measuregraph.c | 250 +++++++++++++++++++++++++++++++++++ gtk/inspector/measuregraph.h | 40 ++++++ gtk/inspector/meson.build | 1 + gtk/inspector/misc-info.c | 62 +++++++++ gtk/inspector/misc-info.ui | 59 +++++++++ 6 files changed, 414 insertions(+) create mode 100644 gtk/inspector/measuregraph.c create mode 100644 gtk/inspector/measuregraph.h diff --git a/gtk/inspector/init.c b/gtk/inspector/init.c index 0e0691a16d..c951fe30fc 100644 --- a/gtk/inspector/init.c +++ b/gtk/inspector/init.c @@ -36,6 +36,7 @@ #include "list-data.h" #include "logs.h" #include "magnifier.h" +#include "measuregraph.h" #include "menu.h" #include "misc-info.h" #include "object-tree.h" @@ -74,6 +75,7 @@ gtk_inspector_init (void) g_type_ensure (GTK_TYPE_INSPECTOR_LOGS); g_type_ensure (GTK_TYPE_MAGNIFIER); g_type_ensure (GTK_TYPE_INSPECTOR_MAGNIFIER); + g_type_ensure (GTK_TYPE_INSPECTOR_MEASURE_GRAPH); g_type_ensure (GTK_TYPE_INSPECTOR_MENU); g_type_ensure (GTK_TYPE_INSPECTOR_MISC_INFO); g_type_ensure (GTK_TYPE_INSPECTOR_OBJECT_TREE); diff --git a/gtk/inspector/measuregraph.c b/gtk/inspector/measuregraph.c new file mode 100644 index 0000000000..d1c93bb0d7 --- /dev/null +++ b/gtk/inspector/measuregraph.c @@ -0,0 +1,250 @@ +/* + * Copyright © 2021 Benjamin Otte + * + * 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.1 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 . + * + * Authors: Benjamin Otte + */ + +#include "config.h" + +#include "measuregraph.h" + +/* gdk_texture_new_for_surface() */ +#include "gdk/gdktextureprivate.h" + +#define MAX_SIZES 2048 + +typedef struct _Size Size; +struct _Size +{ + int min; + int nat; +}; + +struct _GtkInspectorMeasureGraph +{ + GObject parent_instance; + + GdkPaintable *texture; + Size width; + Size height; + Size width_for_height[MAX_SIZES]; + Size height_for_width[MAX_SIZES]; +}; + +struct _GtkInspectorMeasureGraphClass +{ + GObjectClass parent_class; +}; + +static void +gtk_inspector_measure_graph_ensure_texture (GtkInspectorMeasureGraph *self) +{ + int i, width, height; + cairo_surface_t *surface; + cairo_t *cr; + + if (self->texture) + return; + + if (self->width.nat == 0 || self->height.nat == 0) + { + self->texture = gdk_paintable_new_empty (0, 0); + return; + } + + width = self->width.nat; + for (i = 0; i < MAX_SIZES; i++) + width = MAX (width, self->width_for_height[i].nat); + width = MIN (width, MAX_SIZES); + height = self->height.nat; + for (i = 0; i < MAX_SIZES; i++) + height = MAX (height, self->height_for_width[i].nat); + height = MIN (height, MAX_SIZES); + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + cr = cairo_create (surface); + cairo_set_operator (cr, CAIRO_OPERATOR_ADD); + + cairo_set_source_rgba (cr, 0.5, 0, 0, 1); + cairo_rectangle (cr, 0, 0, self->width.min, height); + cairo_fill (cr); + cairo_set_source_rgba (cr, 1, 0, 0, 1); + for (i = self->width.min; i < width; i++) + cairo_rectangle (cr, i, 0, 1, self->height_for_width[i].min); + cairo_fill (cr); + cairo_set_source_rgba (cr, 1, 0, 0, 0.3); + for (i = self->width.min; i < width; i++) + cairo_rectangle (cr, i, self->height_for_width[i].min, 1, self->height_for_width[i].nat - self->height_for_width[i].min); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0, 0, 0.5, 1); + cairo_rectangle (cr, 0, 0, width, self->height.min); + cairo_fill (cr); + cairo_set_source_rgba (cr, 0, 0, 1, 1); + for (i = self->height.min; i < height; i++) + cairo_rectangle (cr, 0, i, self->width_for_height[i].min, 1); + cairo_fill (cr); + cairo_set_source_rgba (cr, 0, 0, 1, 0.3); + for (i = self->height.min; i < height; i++) + cairo_rectangle (cr, self->width_for_height[i].min, i, self->width_for_height[i].nat - self->width_for_height[i].min, 1); + cairo_fill (cr); + + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_set_source_rgba (cr, 0, 0, 0, 1); + cairo_rectangle (cr, self->width.nat, 0, 1, height); + cairo_rectangle (cr, 0, self->height.nat, width, 1); + cairo_fill (cr); + + cairo_destroy (cr); + self->texture = GDK_PAINTABLE (gdk_texture_new_for_surface (surface)); + cairo_surface_destroy (surface); +} + +static void +gtk_inspector_measure_graph_paintable_snapshot (GdkPaintable *paintable, + GdkSnapshot *snapshot, + double width, + double height) +{ + GtkInspectorMeasureGraph *self = GTK_INSPECTOR_MEASURE_GRAPH (paintable); + + gtk_inspector_measure_graph_ensure_texture (self); + + if (self->texture == NULL) + return; + + gdk_paintable_snapshot (self->texture, snapshot, width, height); +} + +static int +gtk_inspector_measure_graph_paintable_get_intrinsic_width (GdkPaintable *paintable) +{ + GtkInspectorMeasureGraph *self = GTK_INSPECTOR_MEASURE_GRAPH (paintable); + + gtk_inspector_measure_graph_ensure_texture (self); + + return gdk_paintable_get_intrinsic_width (self->texture); +} + +static int +gtk_inspector_measure_graph_paintable_get_intrinsic_height (GdkPaintable *paintable) +{ + GtkInspectorMeasureGraph *self = GTK_INSPECTOR_MEASURE_GRAPH (paintable); + + gtk_inspector_measure_graph_ensure_texture (self); + + return gdk_paintable_get_intrinsic_height (self->texture); +} + +static double +gtk_inspector_measure_graph_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable) +{ + GtkInspectorMeasureGraph *self = GTK_INSPECTOR_MEASURE_GRAPH (paintable); + + gtk_inspector_measure_graph_ensure_texture (self); + + return gdk_paintable_get_intrinsic_aspect_ratio (self->texture); +} + +static void +gtk_inspector_measure_graph_paintable_init (GdkPaintableInterface *iface) +{ + iface->snapshot = gtk_inspector_measure_graph_paintable_snapshot; + iface->get_intrinsic_width = gtk_inspector_measure_graph_paintable_get_intrinsic_width; + iface->get_intrinsic_height = gtk_inspector_measure_graph_paintable_get_intrinsic_height; + iface->get_intrinsic_aspect_ratio = gtk_inspector_measure_graph_paintable_get_intrinsic_aspect_ratio; +} + +G_DEFINE_TYPE_EXTENDED (GtkInspectorMeasureGraph, gtk_inspector_measure_graph, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE, + gtk_inspector_measure_graph_paintable_init)) + +static void +gtk_inspector_measure_graph_dispose (GObject *object) +{ + GtkInspectorMeasureGraph *self = GTK_INSPECTOR_MEASURE_GRAPH (object); + + g_clear_object (&self->texture); + + G_OBJECT_CLASS (gtk_inspector_measure_graph_parent_class)->dispose (object); +} + +static void +gtk_inspector_measure_graph_class_init (GtkInspectorMeasureGraphClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->dispose = gtk_inspector_measure_graph_dispose; +} + +static void +gtk_inspector_measure_graph_init (GtkInspectorMeasureGraph *self) +{ +} + +GtkInspectorMeasureGraph * +gtk_inspector_measure_graph_new (void) +{ + return g_object_new (GTK_TYPE_INSPECTOR_MEASURE_GRAPH, NULL); +} + +void +gtk_inspector_measure_graph_clear (GtkInspectorMeasureGraph *self) +{ + g_clear_object (&self->texture); + + memset (&self->width, 0, sizeof (self->width)); + memset (&self->height, 0, sizeof (self->height)); + memset (&self->width_for_height, 0, sizeof (self->width_for_height)); + memset (&self->height_for_width, 0, sizeof (self->height_for_width)); + + gdk_paintable_invalidate_size (GDK_PAINTABLE (self)); + gdk_paintable_invalidate_contents (GDK_PAINTABLE (self)); +} + +void +gtk_inspector_measure_graph_measure (GtkInspectorMeasureGraph *self, + GtkWidget *widget) +{ + int i; + + g_clear_object (&self->texture); + + gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, -1, &self->width.min, &self->width.nat, NULL, NULL); + gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, -1 ,&self->height.min, &self->height.nat, NULL, NULL); + + memset (&self->width_for_height, 0, sizeof (Size) * MIN (self->height.min, MAX_SIZES)); + for (i = self->height.min; i < MAX_SIZES; i++) + gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, i, &self->width_for_height[i].min, &self->width_for_height[i].nat, NULL, NULL); + memset (&self->height_for_width, 0, sizeof (Size) * MIN (self->width.min, MAX_SIZES)); + for (i = self->width.min; i < MAX_SIZES; i++) + gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, i, &self->height_for_width[i].min, &self->height_for_width[i].nat, NULL, NULL); + + gdk_paintable_invalidate_size (GDK_PAINTABLE (self)); + gdk_paintable_invalidate_contents (GDK_PAINTABLE (self)); +} + +GdkTexture * +gtk_inspector_measure_graph_get_texture (GtkInspectorMeasureGraph *self) +{ + gtk_inspector_measure_graph_ensure_texture (self); + + if (!GDK_IS_TEXTURE (self->texture)) + return NULL; + + return GDK_TEXTURE (self->texture); +} + diff --git a/gtk/inspector/measuregraph.h b/gtk/inspector/measuregraph.h new file mode 100644 index 0000000000..e6649ecfe1 --- /dev/null +++ b/gtk/inspector/measuregraph.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2021 Benjamin Otte + * + * 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.1 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 . + * + * Authors: Benjamin Otte + */ + +#ifndef __GTK_INSPECTOR_MEASURE_GRAPH_H__ +#define __GTK_INSPECTOR_MEASURE_GRAPH_H__ + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_INSPECTOR_MEASURE_GRAPH (gtk_inspector_measure_graph_get_type ()) + +G_DECLARE_FINAL_TYPE (GtkInspectorMeasureGraph, gtk_inspector_measure_graph, GTK, INSPECTOR_MEASURE_GRAPH, GObject) + +GtkInspectorMeasureGraph * gtk_inspector_measure_graph_new (void); + +void gtk_inspector_measure_graph_clear (GtkInspectorMeasureGraph *self); +void gtk_inspector_measure_graph_measure (GtkInspectorMeasureGraph *self, + GtkWidget *widget); +GdkTexture * gtk_inspector_measure_graph_get_texture (GtkInspectorMeasureGraph *self); + +G_END_DECLS + +#endif /* __GTK_INSPECTOR_MEASURE_GRAPH_H__ */ diff --git a/gtk/inspector/meson.build b/gtk/inspector/meson.build index 422eab7f24..3b26518e10 100644 --- a/gtk/inspector/meson.build +++ b/gtk/inspector/meson.build @@ -23,6 +23,7 @@ inspector_sources = files( 'layoutoverlay.c', 'logs.c', 'magnifier.c', + 'measuregraph.c', 'menu.c', 'misc-info.c', 'object-tree.c', diff --git a/gtk/inspector/misc-info.c b/gtk/inspector/misc-info.c index df61d1ea00..0908bd9bb0 100644 --- a/gtk/inspector/misc-info.c +++ b/gtk/inspector/misc-info.c @@ -19,6 +19,8 @@ #include #include "misc-info.h" + +#include "measuregraph.h" #include "window.h" #include "type-info.h" @@ -55,6 +57,10 @@ struct _GtkInspectorMiscInfo GtkWidget *mnemonic_label; GtkWidget *request_mode_row; GtkWidget *request_mode; + GtkWidget *measure_row; + GtkWidget *measure_expand_toggle; + GtkWidget *measure_picture; + GdkPaintable *measure_graph; GtkWidget *allocated_size_row; GtkWidget *allocated_size; GtkWidget *baseline_row; @@ -158,6 +164,8 @@ update_allocation (GtkWidget *w, value = g_enum_get_value (class, gtk_widget_get_request_mode (w)); gtk_label_set_label (GTK_LABEL (sl->request_mode), value->value_nick); g_type_class_unref (class); + + gtk_inspector_measure_graph_measure (GTK_INSPECTOR_MEASURE_GRAPH (sl->measure_graph), w); } static void @@ -424,6 +432,50 @@ update_info (gpointer data) return G_SOURCE_CONTINUE; } +static GdkContentProvider * +measure_picture_drag_prepare (GtkDragSource *source, + double x, + double y, + gpointer unused) +{ + GtkWidget *picture; + GdkPaintable *measure_graph; + GdkTexture *texture; + + picture = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (source)); + measure_graph = gtk_picture_get_paintable (GTK_PICTURE (picture)); + if (!GTK_IS_INSPECTOR_MEASURE_GRAPH (measure_graph)) + return NULL; + + texture = gtk_inspector_measure_graph_get_texture (GTK_INSPECTOR_MEASURE_GRAPH (measure_graph)); + if (texture == NULL) + return NULL; + + return gdk_content_provider_new_typed (GDK_TYPE_TEXTURE, texture); +} + +static void +update_measure_picture (GtkPicture *picture, + GtkToggleButton *toggle) +{ + GdkPaintable *paintable = gtk_picture_get_paintable (picture); + + if (gtk_toggle_button_get_active (toggle) || + (gdk_paintable_get_intrinsic_width (paintable) <= 200 && + gdk_paintable_get_intrinsic_height (paintable) <= 100)) + { + gtk_picture_set_can_shrink (picture, FALSE); + gtk_widget_set_size_request (GTK_WIDGET (picture), -1, -1); + } + else + { + gtk_picture_set_can_shrink (picture, TRUE); + gtk_widget_set_size_request (GTK_WIDGET (picture), + -1, + MIN (100, 200 / gdk_paintable_get_intrinsic_aspect_ratio (paintable))); + } +} + void gtk_inspector_misc_info_set_object (GtkInspectorMiscInfo *sl, GObject *object) @@ -448,6 +500,7 @@ gtk_inspector_misc_info_set_object (GtkInspectorMiscInfo *sl, gtk_widget_show (sl->state_row); gtk_widget_show (sl->direction_row); gtk_widget_show (sl->request_mode_row); + gtk_widget_show (sl->measure_row); gtk_widget_show (sl->allocated_size_row); gtk_widget_show (sl->baseline_row); gtk_widget_show (sl->mnemonic_label_row); @@ -462,12 +515,15 @@ gtk_inspector_misc_info_set_object (GtkInspectorMiscInfo *sl, state_flags_changed (GTK_WIDGET (sl->object), 0, sl); update_allocation (GTK_WIDGET (sl->object), sl); + update_measure_picture (GTK_PICTURE (sl->measure_picture), GTK_TOGGLE_BUTTON (sl->measure_expand_toggle)); } else { gtk_widget_hide (sl->state_row); gtk_widget_hide (sl->direction_row); gtk_widget_hide (sl->request_mode_row); + gtk_widget_hide (sl->measure_row); + gtk_inspector_measure_graph_clear (GTK_INSPECTOR_MEASURE_GRAPH (sl->measure_graph)); gtk_widget_hide (sl->mnemonic_label_row); gtk_widget_hide (sl->allocated_size_row); gtk_widget_hide (sl->baseline_row); @@ -572,6 +628,10 @@ gtk_inspector_misc_info_class_init (GtkInspectorMiscInfoClass *klass) gtk_widget_class_bind_template_child (widget_class, GtkInspectorMiscInfo, mnemonic_label); gtk_widget_class_bind_template_child (widget_class, GtkInspectorMiscInfo, request_mode_row); gtk_widget_class_bind_template_child (widget_class, GtkInspectorMiscInfo, request_mode); + gtk_widget_class_bind_template_child (widget_class, GtkInspectorMiscInfo, measure_row); + gtk_widget_class_bind_template_child (widget_class, GtkInspectorMiscInfo, measure_expand_toggle); + gtk_widget_class_bind_template_child (widget_class, GtkInspectorMiscInfo, measure_picture); + gtk_widget_class_bind_template_child (widget_class, GtkInspectorMiscInfo, measure_graph); gtk_widget_class_bind_template_child (widget_class, GtkInspectorMiscInfo, allocated_size_row); gtk_widget_class_bind_template_child (widget_class, GtkInspectorMiscInfo, allocated_size); gtk_widget_class_bind_template_child (widget_class, GtkInspectorMiscInfo, baseline_row); @@ -600,6 +660,8 @@ gtk_inspector_misc_info_class_init (GtkInspectorMiscInfoClass *klass) gtk_widget_class_bind_template_child (widget_class, GtkInspectorMiscInfo, child_visible_row); gtk_widget_class_bind_template_child (widget_class, GtkInspectorMiscInfo, child_visible); + gtk_widget_class_bind_template_callback (widget_class, update_measure_picture); + gtk_widget_class_bind_template_callback (widget_class, measure_picture_drag_prepare); gtk_widget_class_bind_template_callback (widget_class, show_surface); gtk_widget_class_bind_template_callback (widget_class, show_renderer); gtk_widget_class_bind_template_callback (widget_class, show_frame_clock); diff --git a/gtk/inspector/misc-info.ui b/gtk/inspector/misc-info.ui index 8313e5a6e3..1ec347f6fd 100644 --- a/gtk/inspector/misc-info.ui +++ b/gtk/inspector/misc-info.ui @@ -254,6 +254,65 @@ + + + 0 + + + 10 + 10 + 10 + 10 + 40 + + + Measure map + start + baseline + 0 + 1 + + + + + Expand + end + baseline + + + + + + + + + + 0 + + + 10 + 10 + 10 + 10 + 40 + + + + + + 0 + 1 + + + + + + + + + + + 0