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
+
+
+
+
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)