diff options
author | Matthias Clasen <mclasen@redhat.com> | 2023-05-06 22:31:58 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2023-05-07 09:19:44 -0400 |
commit | 87332920d51a48335570ed35013f0f7b50a26d02 (patch) | |
tree | 2bdc0dcf4d7172e0a6a7778f037f3b05ebab0ac8 /demos | |
parent | d2a358206c64d9d80f8f9fd826b2d7aba2cbc267 (diff) | |
download | gtk+-87332920d51a48335570ed35013f0f7b50a26d02.tar.gz |
node-editor: Add some editor smarts
Allow control-clicks on some fields to bring up
a more specific UI. This functionality is also
available via Ctrl-E and the context menu.
At this point, it can edit colors, fonts and
files in some places, as well as a few enums.
Diffstat (limited to 'demos')
-rw-r--r-- | demos/node-editor/node-editor-window.c | 364 | ||||
-rw-r--r-- | demos/node-editor/node-editor-window.ui | 15 |
2 files changed, 379 insertions, 0 deletions
diff --git a/demos/node-editor/node-editor-window.c b/demos/node-editor/node-editor-window.c index 86b5297347..40f03ae487 100644 --- a/demos/node-editor/node-editor-window.c +++ b/demos/node-editor/node-editor-window.c @@ -1190,11 +1190,367 @@ node_editor_window_unrealize (GtkWidget *widget) GTK_WIDGET_CLASS (node_editor_window_parent_class)->unrealize (widget); } +typedef struct +{ + NodeEditorWindow *self; + GtkTextIter start, end; +} Selection; + +static void +color_cb (GObject *source, + GAsyncResult *result, + gpointer data) +{ + GtkColorDialog *dialog = GTK_COLOR_DIALOG (source); + Selection *selection = data; + NodeEditorWindow *self = selection->self; + GdkRGBA *color; + char *text; + GError *error = NULL; + GtkTextBuffer *buffer; + + color = gtk_color_dialog_choose_rgba_finish (dialog, result, &error); + if (!color) + { + g_print ("%s\n", error->message); + g_error_free (error); + g_free (selection); + return; + } + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->text_view)); + + text = gdk_rgba_to_string (color); + gtk_text_buffer_delete (buffer, &selection->start, &selection->end); + gtk_text_buffer_insert (buffer, &selection->start, text, -1); + + g_free (text); + gdk_rgba_free (color); + g_free (selection); +} + +static void +font_cb (GObject *source, + GAsyncResult *result, + gpointer data) +{ + GtkFontDialog *dialog = GTK_FONT_DIALOG (source); + Selection *selection = data; + NodeEditorWindow *self = selection->self; + GError *error = NULL; + PangoFontDescription *desc; + GtkTextBuffer *buffer; + char *text; + + desc = gtk_font_dialog_choose_font_finish (dialog, result, &error); + if (!desc) + { + g_print ("%s\n", error->message); + g_error_free (error); + g_free (selection); + return; + } + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->text_view)); + + text = pango_font_description_to_string (desc); + gtk_text_buffer_delete (buffer, &selection->start, &selection->end); + gtk_text_buffer_insert (buffer, &selection->start, text, -1); + + g_free (text); + pango_font_description_free (desc); + g_free (selection); +} + +static void +file_cb (GObject *source, + GAsyncResult *result, + gpointer data) +{ + GtkFileDialog *dialog = GTK_FILE_DIALOG (source); + Selection *selection = data; + NodeEditorWindow *self = selection->self; + GError *error = NULL; + GFile *file; + GtkTextBuffer *buffer; + char *text; + + file = gtk_file_dialog_open_finish (dialog, result, &error); + if (!file) + { + g_print ("%s\n", error->message); + g_error_free (error); + g_free (selection); + return; + } + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->text_view)); + + text = g_file_get_uri (file); + gtk_text_buffer_delete (buffer, &selection->start, &selection->end); + gtk_text_buffer_insert (buffer, &selection->start, text, -1); + + g_free (text); + g_object_unref (file); + g_free (selection); +} + +static void +key_pressed (GtkEventControllerKey *controller, + unsigned int keyval, + unsigned int keycode, + GdkModifierType state, + gpointer data) +{ + GtkWidget *dd = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (controller)); + Selection *selection = data; + NodeEditorWindow *self = selection->self; + unsigned int selected; + GtkStringList *strings; + GtkTextBuffer *buffer; + const char *text; + + if (keyval != GDK_KEY_Escape) + return; + + strings = GTK_STRING_LIST (gtk_drop_down_get_model (GTK_DROP_DOWN (dd))); + selected = gtk_drop_down_get_selected (GTK_DROP_DOWN (dd)); + text = gtk_string_list_get_string (strings, selected); + + gtk_text_view_remove (GTK_TEXT_VIEW (self->text_view), dd); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->text_view)); + gtk_text_iter_backward_search (&selection->start, "mode:", 0, NULL, &selection->start, NULL); + gtk_text_iter_forward_search (&selection->start, ";", 0, &selection->end, NULL, NULL); + gtk_text_buffer_delete (buffer, &selection->start, &selection->end); + gtk_text_buffer_insert (buffer, &selection->start, " ", -1); + gtk_text_buffer_insert (buffer, &selection->start, text, -1); +} + +static void +node_editor_window_edit (NodeEditorWindow *self, + GtkTextIter *iter) +{ + GtkTextIter start, end; + GtkTextBuffer *buffer; + Selection *selection; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->text_view)); + + gtk_text_iter_set_line_offset (iter, 0); + + if (gtk_text_iter_forward_search (iter, ";", 0, &end, NULL, NULL) && + gtk_text_iter_forward_search (iter, "color:", 0, NULL, &start, &end)) + { + GtkColorDialog *dialog; + GdkRGBA color; + char *text; + + while (g_unichar_isspace (gtk_text_iter_get_char (&start))) + gtk_text_iter_forward_char (&start); + + gtk_text_buffer_select_range (buffer, &start, &end); + text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE); + gdk_rgba_parse (&color, text); + g_free (text); + + selection = g_new0 (Selection, 1); + selection->self = self; + selection->start = start; + selection->end = end; + + dialog = gtk_color_dialog_new (); + gtk_color_dialog_choose_rgba (dialog, GTK_WINDOW (self), &color, NULL, color_cb, selection); + } + else if (gtk_text_iter_forward_search (iter, ";", 0, &end, NULL, NULL) && + gtk_text_iter_forward_search (iter, "font:", 0, NULL, &start, &end)) + { + GtkFontDialog *dialog; + PangoFontDescription *desc; + char *text; + + while (g_unichar_isspace (gtk_text_iter_get_char (&start))) + gtk_text_iter_forward_char (&start); + + /* Skip the quotes */ + gtk_text_iter_forward_char (&start); + gtk_text_iter_backward_char (&end); + + gtk_text_buffer_select_range (buffer, &start, &end); + + text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE); + desc = pango_font_description_from_string (text); + g_free (text); + + selection = g_new0 (Selection, 1); + selection->self = self; + selection->start = start; + selection->end = end; + + dialog = gtk_font_dialog_new (); + gtk_font_dialog_choose_font (dialog, GTK_WINDOW (self), desc, NULL, font_cb, selection); + pango_font_description_free (desc); + } + else if (gtk_text_iter_forward_search (iter, ";", 0, &end, NULL, NULL) && + gtk_text_iter_forward_search (iter, "mode:", 0, NULL, &start, &end)) + { + /* Assume we have a blend node, for now */ + GEnumClass *class; + GtkStringList *strings; + GtkWidget *dd; + GtkTextChildAnchor *anchor; + unsigned int selected = 0; + GtkEventController *key_controller; + gboolean is_blend_mode = FALSE; + char *text; + + while (g_unichar_isspace (gtk_text_iter_get_char (&start))) + gtk_text_iter_forward_char (&start); + + text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE); + + strings = gtk_string_list_new (NULL); + class = g_type_class_ref (GSK_TYPE_BLEND_MODE); + for (unsigned int i = 0; i < class->n_values; i++) + { + if (strcmp (class->values[i].value_nick, text) == 0) + is_blend_mode = TRUE; + } + g_type_class_unref (class); + + if (is_blend_mode) + class = g_type_class_ref (GSK_TYPE_BLEND_MODE); + else + class = g_type_class_ref (GSK_TYPE_MASK_MODE); + + for (unsigned int i = 0; i < class->n_values; i++) + { + if (i == 0 && is_blend_mode) + gtk_string_list_append (strings, "normal"); + else + gtk_string_list_append (strings, class->values[i].value_nick); + + if (strcmp (class->values[i].value_nick, text) == 0) + selected = i; + } + g_type_class_unref (class); + + gtk_text_buffer_delete (buffer, &start, &end); + + anchor = gtk_text_buffer_create_child_anchor (buffer, &start); + dd = gtk_drop_down_new (G_LIST_MODEL (strings), NULL); + gtk_drop_down_set_selected (GTK_DROP_DOWN (dd), selected); + gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (self->text_view), dd, anchor); + + selection = g_new0 (Selection, 1); + selection->self = self; + selection->start = start; + selection->end = end; + + key_controller = gtk_event_controller_key_new (); + g_signal_connect (key_controller, "key-pressed", G_CALLBACK (key_pressed), selection); + gtk_widget_add_controller (dd, key_controller); + } + else if (gtk_text_iter_forward_search (iter, ";", 0, &end, NULL, NULL) && + gtk_text_iter_forward_search (iter, "texture:", 0, NULL, &start, &end)) + { + GtkFileDialog *dialog; + GtkTextIter skip; + char *text; + GFile *file; + + while (g_unichar_isspace (gtk_text_iter_get_char (&start))) + gtk_text_iter_forward_char (&start); + + skip = start; + gtk_text_iter_forward_chars (&skip, strlen ("url(\"")); + text = gtk_text_iter_get_text (&start, &skip); + if (strcmp (text, "url(\"") != 0) + { + g_free (text); + return; + } + g_free (text); + start = skip; + + skip = end; + gtk_text_iter_backward_chars (&skip, strlen ("\")")); + text = gtk_text_iter_get_text (&skip, &end); + if (strcmp (text, "\")") != 0) + { + g_free (text); + return; + } + g_free (text); + end = skip; + + gtk_text_buffer_select_range (buffer, &start, &end); + + text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE); + file = g_file_new_for_uri (text); + g_free (text); + + selection = g_new0 (Selection, 1); + selection->self = self; + selection->start = start; + selection->end = end; + + dialog = gtk_file_dialog_new (); + gtk_file_dialog_set_initial_file (dialog, file); + gtk_file_dialog_open (dialog, GTK_WINDOW (self), NULL, file_cb, selection); + g_object_unref (file); + } +} + +static void +click_gesture_pressed (GtkGestureClick *gesture, + int n_press, + double x, + double y, + NodeEditorWindow *self) +{ + GtkTextIter iter; + int bx, by, trailing; + GdkModifierType state; + + state = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture)); + if ((state & GDK_CONTROL_MASK) == 0) + return; + + gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (self->text_view), GTK_TEXT_WINDOW_TEXT, x, y, &bx, &by); + gtk_text_view_get_iter_at_position (GTK_TEXT_VIEW (self->text_view), &iter, &trailing, bx, by); + + node_editor_window_edit (self, &iter); +} + +static void +edit_action_cb (GtkWidget *widget, + const char *action_name, + GVariant *parameter) +{ + NodeEditorWindow *self = NODE_EDITOR_WINDOW (widget); + GtkTextBuffer *buffer; + GtkTextIter start, end; + +#if 0 + if (gtk_window_get_focus (GTK_WINDOW (self)) != self->text_view) + return; +#endif + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->text_view)); + gtk_text_buffer_get_selection_bounds (buffer, &start, &end); + + node_editor_window_edit (self, &start); +} + static void node_editor_window_class_init (NodeEditorWindowClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + GtkShortcutTrigger *trigger; + GtkShortcutAction *action; + GtkShortcut *shortcut; object_class->dispose = node_editor_window_dispose; object_class->finalize = node_editor_window_finalize; @@ -1225,6 +1581,14 @@ node_editor_window_class_init (NodeEditorWindowClass *class) gtk_widget_class_bind_template_callback (widget_class, dark_mode_cb); gtk_widget_class_bind_template_callback (widget_class, on_picture_drag_prepare_cb); gtk_widget_class_bind_template_callback (widget_class, on_picture_drop_cb); + gtk_widget_class_bind_template_callback (widget_class, click_gesture_pressed); + + gtk_widget_class_install_action (widget_class, "smart-edit", NULL, edit_action_cb); + + trigger = gtk_keyval_trigger_new (GDK_KEY_e, GDK_CONTROL_MASK); + action = gtk_named_action_new ("smart-edit"); + 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 45a37cd952..deeeb98de3 100644 --- a/demos/node-editor/node-editor-window.ui +++ b/demos/node-editor/node-editor-window.ui @@ -16,6 +16,14 @@ </item> </section> </menu> + <menu id="extra_menu"> + <section> + <item> + <attribute name="label" translatable="yes">Assisted _Edit</attribute> + <attribute name="action">smart-edit</attribute> + </item> + </section> + </menu> <object class="GtkPopover" id="testcase_popover"> <child> @@ -195,10 +203,17 @@ <property name="right-margin">6</property> <property name="bottom-margin">6</property> <property name="has-tooltip">1</property> + <property name="extra-menu">extra_menu</property> <signal name="query-tooltip" handler="text_view_query_tooltip_cb"/> <style> <class name="editor" /> </style> + <child> + <object class="GtkGestureClick"> + <property name="button">1</property> + <signal name="pressed" handler="click_gesture_pressed"/> + </object> + </child> </object> </child> </object> |