From 4f751aa53d76003565adae0f68fc37be675614d4 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 15 Dec 2021 21:56:53 -0500 Subject: [PATCH] inspector: Highlight event sequences Optionally, highlight rows of events whose event sequences match the selected event. --- .../actions/function-linear-symbolic.svg | 2 + gtk/inspector/inspector.css | 4 + gtk/inspector/meson.build | 1 + gtk/inspector/recorder.c | 116 +++++++++++++-- gtk/inspector/recorder.h | 6 + gtk/inspector/recorder.ui | 8 + gtk/inspector/recorderrow.c | 138 ++++++++++++++++++ gtk/inspector/recorderrow.h | 24 +++ 8 files changed, 287 insertions(+), 12 deletions(-) create mode 100644 gtk/icons/scalable/actions/function-linear-symbolic.svg create mode 100644 gtk/inspector/recorderrow.c create mode 100644 gtk/inspector/recorderrow.h diff --git a/gtk/icons/scalable/actions/function-linear-symbolic.svg b/gtk/icons/scalable/actions/function-linear-symbolic.svg new file mode 100644 index 0000000000..0d3dfd5440 --- /dev/null +++ b/gtk/icons/scalable/actions/function-linear-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/gtk/inspector/inspector.css b/gtk/inspector/inspector.css index 702dc59690..33cad8a691 100644 --- a/gtk/inspector/inspector.css +++ b/gtk/inspector/inspector.css @@ -55,3 +55,7 @@ picture.light { min-width: 10px; min-height: 10px; } + +row:not(:selected) > .highlight { + background-color: rgba(135, 206, 250, 0.4); +} diff --git a/gtk/inspector/meson.build b/gtk/inspector/meson.build index 08031b2c9a..fb9817fb41 100644 --- a/gtk/inspector/meson.build +++ b/gtk/inspector/meson.build @@ -31,6 +31,7 @@ inspector_sources = files( 'prop-holder.c', 'prop-list.c', 'recorder.c', + 'recorderrow.c', 'recording.c', 'renderrecording.c', 'resource-holder.c', diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c index 09114206e4..94930cba9e 100644 --- a/gtk/inspector/recorder.c +++ b/gtk/inspector/recorder.c @@ -55,6 +55,7 @@ #include "renderrecording.h" #include "startrecording.h" #include "eventrecording.h" +#include "recorderrow.h" struct _GtkInspectorRecorder { @@ -82,6 +83,9 @@ struct _GtkInspectorRecorder gint64 start_time; gboolean debug_nodes; + gboolean highlight_sequences; + + GdkEventSequence *selected_sequence; }; typedef struct _GtkInspectorRecorderClass @@ -95,6 +99,8 @@ enum PROP_0, PROP_RECORDING, PROP_DEBUG_NODES, + PROP_HIGHLIGHT_SEQUENCES, + PROP_SELECTED_SEQUENCE, LAST_PROP }; @@ -506,6 +512,7 @@ recording_selected (GtkSingleSelection *selection, GtkInspectorRecorder *recorder) { GtkInspectorRecording *recording; + GdkEventSequence *selected_sequence = NULL; if (recorder->recordings == NULL) { @@ -548,6 +555,9 @@ recording_selected (GtkSingleSelection *selection, } populate_event_properties (GTK_LIST_STORE (recorder->event_properties), event); + + if (recorder->highlight_sequences) + selected_sequence = gdk_event_get_event_sequence (event); } else { @@ -556,6 +566,8 @@ recording_selected (GtkSingleSelection *selection, gtk_picture_set_paintable (GTK_PICTURE (recorder->render_node_view), NULL); g_list_store_remove_all (recorder->render_node_root_model); } + + gtk_inspector_recorder_set_selected_sequence (recorder, selected_sequence); } static GdkTexture * @@ -1428,6 +1440,12 @@ populate_event_properties (GtkListStore *store, type = gdk_event_get_event_type (event); add_text_row (store, "Type", event_type_name (type)); + if (gdk_event_get_event_sequence (event) != NULL) + { + tmp = g_strdup_printf ("%p", gdk_event_get_event_sequence (event)); + add_text_row (store, "Sequence", tmp); + g_free (tmp); + } add_int_row (store, "Timestamp", gdk_event_get_time (event)); device = gdk_event_get_device (event); @@ -1747,23 +1765,27 @@ setup_widget_for_recording (GtkListItemFactory *factory, GtkListItem *item, gpointer data) { - GtkWidget *widget, *label; + GtkWidget *row, *box, *label; - widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + row = g_object_new (GTK_TYPE_INSPECTOR_RECORDER_ROW, NULL); + + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new (""); gtk_label_set_xalign (GTK_LABEL (label), 0.0f); gtk_widget_set_hexpand (label, TRUE); - gtk_box_append (GTK_BOX (widget), label); + gtk_box_append (GTK_BOX (box), label); label = gtk_label_new (""); - gtk_box_append (GTK_BOX (widget), label); + gtk_box_append (GTK_BOX (box), label); - 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); + gtk_widget_set_margin_start (box, 6); + gtk_widget_set_margin_end (box, 6); + gtk_widget_set_margin_top (box, 6); + gtk_widget_set_margin_bottom (box, 6); - gtk_list_item_set_child (item, widget); + gtk_widget_set_parent (box, row); + + gtk_list_item_set_child (item, row); } static char * @@ -1841,14 +1863,19 @@ bind_widget_for_recording (GtkListItemFactory *factory, GtkListItem *item, gpointer data) { + GtkInspectorRecorder *recorder = GTK_INSPECTOR_RECORDER (data); GtkInspectorRecording *recording = gtk_list_item_get_item (item); - GtkWidget *widget, *label, *label2; + GtkWidget *row, *box, *label, *label2; char *text; - widget = gtk_list_item_get_child (item); - label = gtk_widget_get_first_child (widget); + row = gtk_list_item_get_child (item); + box = gtk_widget_get_first_child (row); + label = gtk_widget_get_first_child (box); label2 = gtk_widget_get_next_sibling (label); + g_object_set (row, "sequence", NULL, NULL); + g_object_bind_property (recorder, "selected-sequence", row, "match-sequence", G_BINDING_SYNC_CREATE); + gtk_label_set_use_markup (GTK_LABEL (label), FALSE); if (GTK_INSPECTOR_IS_RENDER_RECORDING (recording)) @@ -1864,6 +1891,8 @@ bind_widget_for_recording (GtkListItemFactory *factory, { GdkEvent *event = gtk_inspector_event_recording_get_event (GTK_INSPECTOR_EVENT_RECORDING (recording)); + g_object_set (row, "sequence", gdk_event_get_event_sequence (event), NULL); + text = get_event_summary (event); gtk_label_set_label (GTK_LABEL (label), text); g_free (text); @@ -1939,6 +1968,14 @@ gtk_inspector_recorder_get_property (GObject *object, g_value_set_boolean (value, recorder->debug_nodes); break; + case PROP_HIGHLIGHT_SEQUENCES: + g_value_set_boolean (value, recorder->highlight_sequences); + break; + + case PROP_SELECTED_SEQUENCE: + g_value_set_pointer (value, recorder->selected_sequence); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -1963,6 +2000,14 @@ gtk_inspector_recorder_set_property (GObject *object, gtk_inspector_recorder_set_debug_nodes (recorder, g_value_get_boolean (value)); break; + case PROP_HIGHLIGHT_SEQUENCES: + gtk_inspector_recorder_set_highlight_sequences (recorder, g_value_get_boolean (value)); + break; + + case PROP_SELECTED_SEQUENCE: + recorder->selected_sequence = g_value_get_pointer (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -2005,6 +2050,9 @@ gtk_inspector_recorder_class_init (GtkInspectorRecorderClass *klass) FALSE, G_PARAM_READWRITE); + props[PROP_HIGHLIGHT_SEQUENCES] = g_param_spec_boolean ("highlight-sequences", "", "", FALSE, G_PARAM_READWRITE); + props[PROP_SELECTED_SEQUENCE] = g_param_spec_pointer ("selected-sequence", "", "", 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"); @@ -2197,4 +2245,48 @@ gtk_inspector_recorder_set_debug_nodes (GtkInspectorRecorder *recorder, g_object_notify_by_pspec (G_OBJECT (recorder), props[PROP_DEBUG_NODES]); } +void +gtk_inspector_recorder_set_highlight_sequences (GtkInspectorRecorder *recorder, + gboolean highlight_sequences) +{ + GdkEventSequence *sequence = NULL; + + if (recorder->highlight_sequences == highlight_sequences) + return; + + recorder->highlight_sequences = highlight_sequences; + + if (highlight_sequences) + { + GtkSingleSelection *selection; + GtkInspectorRecording *recording; + GdkEvent *event; + + selection = GTK_SINGLE_SELECTION (gtk_list_view_get_model (GTK_LIST_VIEW (recorder->recordings_list))); + recording = gtk_single_selection_get_selected_item (selection); + + if (GTK_INSPECTOR_IS_EVENT_RECORDING (recording)) + { + event = gtk_inspector_event_recording_get_event (GTK_INSPECTOR_EVENT_RECORDING (recording)); + sequence = gdk_event_get_event_sequence (event); + } + } + + gtk_inspector_recorder_set_selected_sequence (recorder, sequence); + + g_object_notify_by_pspec (G_OBJECT (recorder), props[PROP_HIGHLIGHT_SEQUENCES]); +} + +void +gtk_inspector_recorder_set_selected_sequence (GtkInspectorRecorder *recorder, + GdkEventSequence *sequence) +{ + if (recorder->selected_sequence == sequence) + return; + + recorder->selected_sequence = sequence; + + g_object_notify_by_pspec (G_OBJECT (recorder), props[PROP_SELECTED_SEQUENCE]); +} + diff --git a/gtk/inspector/recorder.h b/gtk/inspector/recorder.h index be5260879b..1ce908036a 100644 --- a/gtk/inspector/recorder.h +++ b/gtk/inspector/recorder.h @@ -37,6 +37,12 @@ gboolean gtk_inspector_recorder_is_recording (GtkInspectorRec void gtk_inspector_recorder_set_debug_nodes (GtkInspectorRecorder *recorder, gboolean debug_nodes); +void gtk_inspector_recorder_set_highlight_sequences (GtkInspectorRecorder *recorder, + gboolean highlight_sequences); + +void gtk_inspector_recorder_set_selected_sequence (GtkInspectorRecorder *recorder, + GdkEventSequence *sequence); + void gtk_inspector_recorder_record_render (GtkInspectorRecorder *recorder, GtkWidget *widget, GskRenderer *renderer, diff --git a/gtk/inspector/recorder.ui b/gtk/inspector/recorder.ui index 4accd7fa44..d3bee18312 100644 --- a/gtk/inspector/recorder.ui +++ b/gtk/inspector/recorder.ui @@ -34,6 +34,14 @@ Add debug nodes start + + + + + function-linear-symbolic + Highlight event sequences + + start 1 diff --git a/gtk/inspector/recorderrow.c b/gtk/inspector/recorderrow.c new file mode 100644 index 0000000000..37ceab7e3f --- /dev/null +++ b/gtk/inspector/recorderrow.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021 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 . + */ + +#include "config.h" +#include + +#include "recorderrow.h" + +#include + +/* This is a minimal widget whose purpose it is to compare the event sequence + * of its row in the recordings list with the event sequence of the selected + * row, and highlight itself if they match. + */ + +struct _GtkInspectorRecorderRow +{ + GtkWidget parent; + + gpointer sequence; + gpointer match_sequence; +}; + +enum { + PROP_SEQUENCE = 1, + PROP_MATCH_SEQUENCE, + LAST_PROP +}; + +static GParamSpec *props[LAST_PROP] = { NULL, }; + +G_DEFINE_TYPE (GtkInspectorRecorderRow, gtk_inspector_recorder_row, GTK_TYPE_WIDGET) + +static void +gtk_inspector_recorder_row_init (GtkInspectorRecorderRow *self) +{ +} + +static void +dispose (GObject *object) +{ + GtkInspectorRecorderRow *self = GTK_INSPECTOR_RECORDER_ROW (object); + + gtk_widget_unparent (gtk_widget_get_first_child (GTK_WIDGET (self))); + + G_OBJECT_CLASS (gtk_inspector_recorder_row_parent_class)->dispose (object); +} + +static void +update_style (GtkInspectorRecorderRow *self) +{ + if (self->sequence == self->match_sequence && self->sequence != NULL) + gtk_widget_add_css_class (GTK_WIDGET (self), "highlight"); + else + gtk_widget_remove_css_class (GTK_WIDGET (self), "highlight"); +} + +static void +gtk_inspector_recorder_row_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GtkInspectorRecorderRow *self = GTK_INSPECTOR_RECORDER_ROW (object); + + switch (param_id) + { + case PROP_SEQUENCE: + g_value_set_pointer (value, self->sequence); + break; + + case PROP_MATCH_SEQUENCE: + g_value_set_pointer (value, self->match_sequence); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +gtk_inspector_recorder_row_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkInspectorRecorderRow *self = GTK_INSPECTOR_RECORDER_ROW (object); + + switch (param_id) + { + case PROP_SEQUENCE: + self->sequence = g_value_get_pointer (value); + update_style (self); + break; + + case PROP_MATCH_SEQUENCE: + self->match_sequence = g_value_get_pointer (value); + update_style (self); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +gtk_inspector_recorder_row_class_init (GtkInspectorRecorderRowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = dispose; + object_class->set_property = gtk_inspector_recorder_row_set_property; + object_class->get_property = gtk_inspector_recorder_row_get_property; + + props[PROP_SEQUENCE] = g_param_spec_pointer ("sequence", "", "", G_PARAM_READWRITE); + props[PROP_MATCH_SEQUENCE] = g_param_spec_pointer ("match-sequence", "", "", G_PARAM_READWRITE); + + g_object_class_install_properties (object_class, LAST_PROP, props); + + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); +} diff --git a/gtk/inspector/recorderrow.h b/gtk/inspector/recorderrow.h new file mode 100644 index 0000000000..6aa835107b --- /dev/null +++ b/gtk/inspector/recorderrow.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 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 . + */ + +#pragma once + +#include + +#define GTK_TYPE_INSPECTOR_RECORDER_ROW (gtk_inspector_recorder_row_get_type ()) + +G_DECLARE_FINAL_TYPE (GtkInspectorRecorderRow, gtk_inspector_recorder_row, GTK, INSPECTOR_RECORDER_ROW, GtkWidget)