From 1ce7de0fb80dbdfdffa56fc1543af12cf0becc41 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 18 Aug 2024 09:54:38 -0400 Subject: [PATCH 1/8] Cosmetics --- gdk/wayland/gdkdisplay-wayland.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c index 63eade8534..add8c05705 100644 --- a/gdk/wayland/gdkdisplay-wayland.c +++ b/gdk/wayland/gdkdisplay-wayland.c @@ -494,7 +494,7 @@ gdk_registry_handle_global (void *data, &server_decoration_listener, display_wayland); } - else if (strcmp(interface, "zxdg_output_manager_v1") == 0) + else if (strcmp (interface, "zxdg_output_manager_v1") == 0) { display_wayland->xdg_output_manager = wl_registry_bind (display_wayland->wl_registry, id, @@ -503,7 +503,7 @@ gdk_registry_handle_global (void *data, gdk_wayland_display_init_xdg_output (display_wayland); _gdk_wayland_display_async_roundtrip (display_wayland); } - else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0) + else if (strcmp (interface, "zwp_idle_inhibit_manager_v1") == 0) { display_wayland->idle_inhibit_manager = wl_registry_bind (display_wayland->wl_registry, id, From 46e7ea8c1e2c41989d24301088f493bb01bc9a19 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 18 Aug 2024 15:43:21 -0400 Subject: [PATCH 2/8] Fix a misleading comment --- gtk/gtkmain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index 81a459b3d4..25a18c4844 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -1926,7 +1926,7 @@ gtk_propagate_event_internal (GtkWidget *widget, i--; } - /* If not yet handled, also propagate down */ + /* If not yet handled, also propagate back up */ if (!handled_event) { /* Propagate event up the widget tree so that From 168bab34f747b87250ead85838d90cee3931f02d Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 18 Aug 2024 14:23:36 -0400 Subject: [PATCH 3/8] inspector: Simplify We can just put the render node on the clipboard directly, no manual serialization required. --- gtk/inspector/recorder.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c index 2a6c595dcb..0d160c42a1 100644 --- a/gtk/inspector/recorder.c +++ b/gtk/inspector/recorder.c @@ -2033,22 +2033,13 @@ render_node_clip (GtkButton *button, { GskRenderNode *node; GdkClipboard *clipboard; - GBytes *bytes; - GdkContentProvider *content; node = get_selected_node (recorder); if (node == NULL) return; - bytes = gsk_render_node_serialize (node); - content = gdk_content_provider_new_for_bytes ("text/plain;charset=utf-8", bytes); - clipboard = gtk_widget_get_clipboard (GTK_WIDGET (recorder)); - - gdk_clipboard_set_content (clipboard, content); - - g_object_unref (content); - g_bytes_unref (bytes); + gdk_clipboard_set (clipboard, GSK_TYPE_RENDER_NODE, node); } static void From e4248503cf8495fd20b6e01287520ab04a68248b Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 18 Aug 2024 09:51:09 -0400 Subject: [PATCH 4/8] inspector: Add event traces When recording events, make it possible to add 'traces' of the events journey, and show those in the recorder. --- gtk/inspector/eventrecording.c | 32 +++++++++++++++++++ gtk/inspector/eventrecording.h | 23 ++++++++++++++ gtk/inspector/recorder.c | 58 ++++++++++++++++++++++++++++++++-- gtk/inspector/recorder.h | 7 ++++ gtk/inspector/window.c | 21 ++++++++++++ gtk/inspector/window.h | 23 +++++++++----- 6 files changed, 153 insertions(+), 11 deletions(-) diff --git a/gtk/inspector/eventrecording.c b/gtk/inspector/eventrecording.c index d24d51455a..7dcb9237d2 100644 --- a/gtk/inspector/eventrecording.c +++ b/gtk/inspector/eventrecording.c @@ -28,6 +28,7 @@ gtk_inspector_event_recording_finalize (GObject *object) GtkInspectorEventRecording *recording = GTK_INSPECTOR_EVENT_RECORDING (object); g_clear_pointer (&recording->event, gdk_event_unref); + g_array_unref (recording->traces); G_OBJECT_CLASS (gtk_inspector_event_recording_parent_class)->finalize (object); } @@ -56,6 +57,7 @@ gtk_inspector_event_recording_new (gint64 timestamp, NULL); recording->event = gdk_event_ref (event); + recording->traces = g_array_new (FALSE, FALSE, sizeof (EventTrace)); return GTK_INSPECTOR_RECORDING (recording); } @@ -66,4 +68,34 @@ gtk_inspector_event_recording_get_event (GtkInspectorEventRecording *recording) return recording->event; } +void +gtk_inspector_event_recording_add_trace (GtkInspectorEventRecording *recording, + GtkPropagationPhase phase, + GtkWidget *widget, + GtkEventController *controller, + GtkWidget *target, + gboolean handled) +{ + EventTrace trace; + + trace.phase = phase; + trace.widget = widget; + trace.widget_type = G_OBJECT_TYPE (widget); + trace.controller_type = G_OBJECT_TYPE (controller); + trace.target_type = G_OBJECT_TYPE (target); + trace.handled = handled; + + g_array_append_val (recording->traces, trace); +} + +EventTrace * +gtk_inspector_event_recording_get_traces (GtkInspectorEventRecording *recording, + gsize *n_traces) +{ + *n_traces = recording->traces->len; + + return (EventTrace *) recording->traces->data; +} + + // vim: set et sw=2 ts=2: diff --git a/gtk/inspector/eventrecording.h b/gtk/inspector/eventrecording.h index 4d6b938443..3646e11994 100644 --- a/gtk/inspector/eventrecording.h +++ b/gtk/inspector/eventrecording.h @@ -20,6 +20,9 @@ #include #include #include "gsk/gskprofilerprivate.h" +#include "gtk/gtkenums.h" +#include "gtk/gtkwidget.h" +#include "gtk/gtkeventcontroller.h" #include "inspector/recording.h" @@ -35,11 +38,22 @@ G_BEGIN_DECLS typedef struct _GtkInspectorEventRecordingPrivate GtkInspectorEventRecordingPrivate; +typedef struct +{ + GtkPropagationPhase phase; + gpointer widget; + GType widget_type; + GType controller_type; + GType target_type; + gboolean handled; +} EventTrace; + typedef struct _GtkInspectorEventRecording { GtkInspectorRecording parent; GdkEvent *event; + GArray *traces; } GtkInspectorEventRecording; typedef struct _GtkInspectorEventRecordingClass @@ -55,6 +69,15 @@ GtkInspectorRecording * GdkEvent * gtk_inspector_event_recording_get_event (GtkInspectorEventRecording *recording); +void gtk_inspector_event_recording_add_trace (GtkInspectorEventRecording *recording, + GtkPropagationPhase phase, + GtkWidget *widget, + GtkEventController *controller, + GtkWidget *target, + gboolean handled); + +EventTrace * gtk_inspector_event_recording_get_traces (GtkInspectorEventRecording *recording, + gsize *n_traces); G_END_DECLS diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c index 0d160c42a1..0335ce532d 100644 --- a/gtk/inspector/recorder.c +++ b/gtk/inspector/recorder.c @@ -214,6 +214,8 @@ struct _GtkInspectorRecorder gboolean highlight_sequences; GdkEventSequence *selected_sequence; + + GtkInspectorEventRecording *last_event_recording; }; typedef struct _GtkInspectorRecorderClass @@ -735,7 +737,9 @@ show_event (GtkInspectorRecorder *recorder, } static void populate_event_properties (GListStore *store, - GdkEvent *event); + GdkEvent *event, + EventTrace *traces, + gsize n_traces); static void recording_selected (GtkSingleSelection *selection, @@ -765,10 +769,13 @@ recording_selected (GtkSingleSelection *selection, else if (GTK_INSPECTOR_IS_EVENT_RECORDING (recording)) { GdkEvent *event; + EventTrace *traces; + gsize n_traces; gtk_stack_set_visible_child_name (GTK_STACK (recorder->recording_data_stack), "event_data"); event = gtk_inspector_event_recording_get_event (GTK_INSPECTOR_EVENT_RECORDING (recording)); + traces = gtk_inspector_event_recording_get_traces (GTK_INSPECTOR_EVENT_RECORDING (recording), &n_traces); for (guint pos = gtk_single_selection_get_selected (selection) - 1; pos > 0; pos--) { @@ -785,7 +792,7 @@ recording_selected (GtkSingleSelection *selection, } } - populate_event_properties (recorder->event_properties, event); + populate_event_properties (recorder->event_properties, event, traces, n_traces); if (recorder->highlight_sequences) selected_sequence = gdk_event_get_event_sequence (event); @@ -1738,7 +1745,9 @@ scroll_unit_name (GdkScrollUnit unit) static void populate_event_properties (GListStore *store, - GdkEvent *event) + GdkEvent *event, + EventTrace *traces, + gsize n_traces) { GdkEventType type; GdkDevice *device; @@ -1909,6 +1918,29 @@ populate_event_properties (GListStore *store, g_free (history); } } + + if (n_traces > 0) + { + GString *s = g_string_new (""); + const char *phase_name[] = { "", "↘", "↙", "⊙" }; + + add_text_row (store, "Target", "%s", g_type_name (traces[0].target_type)); + + for (gsize i = 0; i < n_traces; i++) + { + EventTrace *t = &traces[i]; + + g_string_append_printf (s, "%s %s %s %s\n", + phase_name[t->phase], + g_type_name (t->widget_type), + g_type_name (t->controller_type), + t->handled ? "✓" : ""); + g_string_append_c (s, '\n'); + } + + add_text_row (store, "Trace", "%s", s->str); + g_string_free (s, TRUE); + } } static GskRenderNode * @@ -2523,9 +2555,29 @@ gtk_inspector_recorder_record_event (GtkInspectorRecorder *recorder, recording = gtk_inspector_event_recording_new (frame_time, event); gtk_inspector_recorder_add_recording (recorder, recording); + + recorder->last_event_recording = (GtkInspectorEventRecording *) recording; + g_object_unref (recording); } +void +gtk_inspector_recorder_trace_event (GtkInspectorRecorder *recorder, + GdkEvent *event, + GtkPropagationPhase phase, + GtkWidget *widget, + GtkEventController *controller, + GtkWidget *target, + gboolean handled) +{ + GtkInspectorEventRecording *recording = recorder->last_event_recording; + + if (recording == NULL || recording->event != event) + return; + + gtk_inspector_event_recording_add_trace (recording, phase, widget, controller, target, handled); +} + void gtk_inspector_recorder_set_debug_nodes (GtkInspectorRecorder *recorder, gboolean debug_nodes) diff --git a/gtk/inspector/recorder.h b/gtk/inspector/recorder.h index d7cc720069..eba9b04171 100644 --- a/gtk/inspector/recorder.h +++ b/gtk/inspector/recorder.h @@ -52,6 +52,13 @@ void gtk_inspector_recorder_record_render (GtkInspectorRec void gtk_inspector_recorder_record_event (GtkInspectorRecorder *recorder, GtkWidget *widget, GdkEvent *event); +void gtk_inspector_recorder_trace_event (GtkInspectorRecorder *recorder, + GdkEvent *event, + GtkPropagationPhase phase, + GtkWidget *widget, + GtkEventController *controller, + GtkWidget *target, + gboolean handled); G_END_DECLS diff --git a/gtk/inspector/window.c b/gtk/inspector/window.c index 386aa2633d..8aee9aed8c 100644 --- a/gtk/inspector/window.c +++ b/gtk/inspector/window.c @@ -902,6 +902,27 @@ gtk_inspector_handle_event (GdkEvent *event) return handled; } +void +gtk_inspector_trace_event (GdkEvent *event, + GtkPropagationPhase phase, + GtkWidget *widget, + GtkEventController *controller, + GtkWidget *target, + gboolean handled) +{ + GtkInspectorWindow *iw; + + if (!any_inspector_window_constructed) + return; + + iw = gtk_inspector_window_get_for_display (gdk_event_get_display (event)); + if (iw == NULL) + return; + + gtk_inspector_recorder_trace_event (GTK_INSPECTOR_RECORDER (iw->widget_recorder), + event, phase, widget, controller, target, handled); +} + GdkDisplay * gtk_inspector_window_get_inspected_display (GtkInspectorWindow *iw) { diff --git a/gtk/inspector/window.h b/gtk/inspector/window.h index 283a25bf53..5f2e6b6087 100644 --- a/gtk/inspector/window.h +++ b/gtk/inspector/window.h @@ -149,14 +149,21 @@ void gtk_inspector_window_replace_object (GtkInspectorWindow ChildKind kind, guint position); -gboolean gtk_inspector_is_recording (GtkWidget *widget); -GskRenderNode * gtk_inspector_prepare_render (GtkWidget *widget, - GskRenderer *renderer, - GdkSurface *surface, - const cairo_region_t *region, - GskRenderNode *root, - GskRenderNode *widget_node); -gboolean gtk_inspector_handle_event (GdkEvent *event); +gboolean gtk_inspector_is_recording (GtkWidget *widget); +GskRenderNode * gtk_inspector_prepare_render (GtkWidget *widget, + GskRenderer *renderer, + GdkSurface *surface, + const cairo_region_t *region, + GskRenderNode *root, + GskRenderNode *widget_node); +gboolean gtk_inspector_handle_event (GdkEvent *event); +void gtk_inspector_trace_event (GdkEvent *event, + GtkPropagationPhase phase, + GtkWidget *widget, + GtkEventController *controller, + GtkWidget *target, + gboolean handled); + G_END_DECLS From 7900f91e4632aa60822feba3689c4f6d023bead7 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 18 Aug 2024 09:51:48 -0400 Subject: [PATCH 5/8] widget: Record event traces Use the newly introduced inspector api to add event traces. --- gtk/gtkwidget.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 3491a51bb4..7469691e07 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -4586,6 +4586,8 @@ gtk_widget_run_controllers (GtkWidget *widget, is_gesture = GTK_IS_GESTURE (controller); this_handled = gtk_event_controller_handle_event (controller, event, target, x, y); + gtk_inspector_trace_event (event, phase, widget, controller, target, this_handled); + if (GTK_DEBUG_CHECK (KEYBINDINGS)) { GdkEventType type = gdk_event_get_event_type (event); From 360674f5230c83b80141ed41071574bb0913eedd Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 18 Aug 2024 10:41:59 -0400 Subject: [PATCH 6/8] inspector: Add a snapshot function Add a function that records just a single frame, and puts it on the clipboard. --- gtk/inspector/recorder.c | 39 ++++++++++++++++++++++++++++++++++++++- gtk/inspector/recorder.h | 1 + 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c index 0335ce532d..42e114250f 100644 --- a/gtk/inspector/recorder.c +++ b/gtk/inspector/recorder.c @@ -212,6 +212,8 @@ struct _GtkInspectorRecorder gboolean debug_nodes; gboolean highlight_sequences; + gboolean record_events; + gboolean stop_after_next_frame; GdkEventSequence *selected_sequence; @@ -2473,6 +2475,7 @@ gtk_inspector_recorder_set_recording (GtkInspectorRecorder *recorder, { recorder->recording = gtk_inspector_start_recording_new (); recorder->start_time = 0; + recorder->record_events = TRUE; gtk_inspector_recorder_add_recording (recorder, recorder->recording); } else @@ -2483,12 +2486,31 @@ gtk_inspector_recorder_set_recording (GtkInspectorRecorder *recorder, g_object_notify_by_pspec (G_OBJECT (recorder), props[PROP_RECORDING]); } +void +gtk_inspector_recorder_record_single_frame (GtkInspectorRecorder *recorder) +{ + if (gtk_inspector_recorder_is_recording (recorder)) + return; + + recorder->recording = gtk_inspector_start_recording_new (); + recorder->start_time = 0; + recorder->record_events = FALSE; + recorder->stop_after_next_frame = TRUE; + gtk_inspector_recorder_add_recording (recorder, recorder->recording); +} + gboolean gtk_inspector_recorder_is_recording (GtkInspectorRecorder *recorder) { return recorder->recording != NULL; } +static gboolean +gtk_inspector_recorder_is_recording_events (GtkInspectorRecorder *recorder) +{ + return recorder->recording != NULL && recorder->record_events; +} + void gtk_inspector_recorder_record_render (GtkInspectorRecorder *recorder, GtkWidget *widget, @@ -2526,6 +2548,18 @@ gtk_inspector_recorder_record_render (GtkInspectorRecorder *recorder, node); gtk_inspector_recorder_add_recording (recorder, recording); g_object_unref (recording); + + if (recorder->stop_after_next_frame) + { + GtkSingleSelection *selection; + + recorder->stop_after_next_frame = FALSE; + gtk_inspector_recorder_set_recording (recorder, FALSE); + + selection = GTK_SINGLE_SELECTION (gtk_list_view_get_model (GTK_LIST_VIEW (recorder->recordings_list))); + gtk_single_selection_set_selected (selection, g_list_model_get_n_items (G_LIST_MODEL (selection)) - 1); + render_node_clip (NULL, recorder); + } } void @@ -2537,7 +2571,7 @@ gtk_inspector_recorder_record_event (GtkInspectorRecorder *recorder, GdkFrameClock *frame_clock; gint64 frame_time; - if (!gtk_inspector_recorder_is_recording (recorder)) + if (!gtk_inspector_recorder_is_recording_events (recorder)) return; frame_clock = gtk_widget_get_frame_clock (widget); @@ -2572,6 +2606,9 @@ gtk_inspector_recorder_trace_event (GtkInspectorRecorder *recorder, { GtkInspectorEventRecording *recording = recorder->last_event_recording; + if (!gtk_inspector_recorder_is_recording_events (recorder)) + return; + if (recording == NULL || recording->event != event) return; diff --git a/gtk/inspector/recorder.h b/gtk/inspector/recorder.h index eba9b04171..8c25aaf53f 100644 --- a/gtk/inspector/recorder.h +++ b/gtk/inspector/recorder.h @@ -32,6 +32,7 @@ GType gtk_inspector_recorder_get_type (void); void gtk_inspector_recorder_set_recording (GtkInspectorRecorder *recorder, gboolean record); gboolean gtk_inspector_recorder_is_recording (GtkInspectorRecorder *recorder); +void gtk_inspector_recorder_record_single_frame (GtkInspectorRecorder *recorder); void gtk_inspector_recorder_set_debug_nodes (GtkInspectorRecorder *recorder, gboolean debug_nodes); From 29b6eab0eaf99b7620bfd88afdcdd63ae7351a14 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 18 Aug 2024 10:42:39 -0400 Subject: [PATCH 7/8] inspector: Add some shortcuts While the inspector is open, look for some shortcuts: Super-r to toggle recording Super-c to take a screenshot A screenshot here means just a single-frame recording. For convenience, we put the recorded frame onto the clipboard too. --- gtk/inspector/window.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/gtk/inspector/window.c b/gtk/inspector/window.c index 8aee9aed8c..b4f362baf1 100644 --- a/gtk/inspector/window.c +++ b/gtk/inspector/window.c @@ -893,6 +893,24 @@ gtk_inspector_handle_event (GdkEvent *event) if (iw == NULL) return FALSE; + if (GDK_IS_EVENT_TYPE (event, GDK_KEY_PRESS)) + { + GtkInspectorRecorder *recorder = GTK_INSPECTOR_RECORDER (iw->widget_recorder); + + if (gdk_key_event_matches (event, GDK_KEY_r, GDK_SUPER_MASK) == GDK_KEY_MATCH_EXACT) + { + gboolean recording = gtk_inspector_recorder_is_recording (recorder); + + gtk_inspector_recorder_set_recording (recorder, !recording); + return TRUE; + } + else if (gdk_key_event_matches (event, GDK_KEY_c, GDK_SUPER_MASK) == GDK_KEY_MATCH_EXACT) + { + gtk_inspector_recorder_record_single_frame (recorder); + return TRUE; + } + } + gtk_inspector_recorder_record_event (GTK_INSPECTOR_RECORDER (iw->widget_recorder), gtk_get_event_widget (event), event); From cefaec5d6fdf6f807992b17782b1b77765f63597 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 18 Aug 2024 13:47:28 -0400 Subject: [PATCH 8/8] node-editor: Add Ctrl-Shift-V Make Ctrl-Shift-V trigger a 'paste-as-node' action which replaces the current content with the render node in the clipboard. --- demos/node-editor/node-editor-window.c | 36 +++++++++++++++++++++++++ demos/node-editor/node-editor-window.ui | 4 +++ 2 files changed, 40 insertions(+) diff --git a/demos/node-editor/node-editor-window.c b/demos/node-editor/node-editor-window.c index 02c99c3851..f69491d07e 100644 --- a/demos/node-editor/node-editor-window.c +++ b/demos/node-editor/node-editor-window.c @@ -1205,6 +1205,19 @@ node_editor_window_add_renderer (NodeEditorWindow *self, g_object_unref (paintable); } +static void +update_paste_action (GdkClipboard *clipboard, + GParamSpec *pspec, + gpointer data) +{ + GtkWidget *widget = GTK_WIDGET (data); + gboolean has_node; + + has_node = gdk_content_formats_contain_mime_type (gdk_clipboard_get_formats (clipboard), "application/x-gtk-render-node"); + + gtk_widget_action_set_enabled (widget, "paste-node", has_node); +} + static void node_editor_window_realize (GtkWidget *widget) { @@ -1242,6 +1255,7 @@ node_editor_window_realize (GtkWidget *widget) self->after_paint_handler = g_signal_connect (frameclock, "after-paint", G_CALLBACK (after_paint), self); + g_signal_connect (gtk_widget_get_clipboard (widget), "notify::formats", G_CALLBACK (update_paste_action), widget); } static void @@ -1251,6 +1265,8 @@ node_editor_window_unrealize (GtkWidget *widget) GdkFrameClock *frameclock; guint i; + g_signal_handlers_disconnect_by_func (gtk_widget_get_clipboard (widget), update_paste_action, widget); + frameclock = gtk_widget_get_frame_clock (widget); g_signal_handler_disconnect (frameclock, self->after_paint_handler); self->after_paint_handler = 0; @@ -1615,6 +1631,19 @@ edit_action_cb (GtkWidget *widget, node_editor_window_edit (self, &start); } +static void +paste_node_cb (GtkWidget *widget, + const char *action_name, + GVariant *parameter) +{ + NodeEditorWindow *self = NODE_EDITOR_WINDOW (widget); + GtkTextBuffer *buffer; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->text_view)); + gtk_text_buffer_set_text (buffer, "", 0); + gtk_text_buffer_paste_clipboard (buffer, gtk_widget_get_clipboard (widget), NULL, TRUE); +} + static void node_editor_window_set_property (GObject *object, guint prop_id, @@ -1727,6 +1756,13 @@ node_editor_window_class_init (NodeEditorWindowClass *class) action = gtk_named_action_new ("smart-edit"); shortcut = gtk_shortcut_new (trigger, action); gtk_widget_class_add_shortcut (widget_class, shortcut); + + gtk_widget_class_install_action (widget_class, "paste-node", NULL, paste_node_cb); + + trigger = gtk_keyval_trigger_new (GDK_KEY_v, GDK_CONTROL_MASK | GDK_SHIFT_MASK); + action = gtk_named_action_new ("paste-node"); + shortcut = gtk_shortcut_new (trigger, action); + gtk_widget_class_add_shortcut (widget_class, shortcut); } static GtkWidget * diff --git a/demos/node-editor/node-editor-window.ui b/demos/node-editor/node-editor-window.ui index 68d43ba27e..f1f1b003f8 100644 --- a/demos/node-editor/node-editor-window.ui +++ b/demos/node-editor/node-editor-window.ui @@ -22,6 +22,10 @@
+ + Paste _Node + paste-node + Assisted _Edit smart-edit