[gnome-text-editor] page: add overlay to jump to line and optional column
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-text-editor] page: add overlay to jump to line and optional column
- Date: Tue, 5 Oct 2021 20:12:20 +0000 (UTC)
commit 4cd27a03f7db31ef14dd9f7829d5cbc5bf1dd2a2
Author: Christian Hergert <chergert redhat com>
Date: Tue Oct 5 13:03:23 2021 -0700
page: add overlay to jump to line and optional column
Ctrl+I to activate, similar to gedit. Displays an overlay similar to the
search bar which allows inputing line numbers in the form of:
line
line:column
both are 1 index based to match the visual display of columns. If no
column is provided, the cursor is placed on the first non-whitespace
character or the end of line if no non-whitespace character is found.
Also added shortcut to Movements section of help-overlay.ui
If/when https://gitlab.gnome.org/GNOME/gtk/-/issues/4315 is fixed, we can
remove the workaround to attach to the GtkText for suppressing non-integral
characters.
Fixes #168
src/editor-page-actions.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++
src/editor-page-private.h | 2 ++
src/editor-page.c | 61 +++++++++++++++++++++++++++++++++++++++++++++
src/editor-page.ui | 57 ++++++++++++++++++++++++++++++++++++++++++
src/help-overlay.ui | 6 +++++
5 files changed, 189 insertions(+)
---
diff --git a/src/editor-page-actions.c b/src/editor-page-actions.c
index 63d5de8..57bee49 100644
--- a/src/editor-page-actions.c
+++ b/src/editor-page-actions.c
@@ -49,6 +49,7 @@ editor_page_actions_search_hide (GtkWidget *widget,
g_assert (EDITOR_IS_PAGE (self));
+ gtk_revealer_set_reveal_child (self->goto_line_revealer, FALSE);
_editor_page_hide_search (self);
editor_page_grab_focus (self);
}
@@ -110,6 +111,64 @@ editor_page_actions_replace_all (GtkWidget *widget,
}
}
+static void
+editor_page_actions_show_goto_line (GtkWidget *widget,
+ const char *action_name,
+ GVariant *param)
+{
+ EditorPage *self = (EditorPage *)widget;
+ char str[12];
+ guint line, column;
+
+ g_assert (EDITOR_IS_PAGE (self));
+
+ editor_page_get_visual_position (self, &line, &column);
+ g_snprintf (str, sizeof str, "%u", line + 1);
+ gtk_editable_set_text (GTK_EDITABLE (self->goto_line_entry), str);
+
+ gtk_revealer_set_reveal_child (self->goto_line_revealer, TRUE);
+ gtk_widget_grab_focus (GTK_WIDGET (self->goto_line_entry));
+}
+
+static void
+editor_page_actions_goto_line (GtkWidget *widget,
+ const char *action_name,
+ GVariant *param)
+{
+ EditorPage *self = (EditorPage *)widget;
+ const char *str;
+ guint line = 0, column = 0;
+ int count;
+
+ g_assert (EDITOR_IS_PAGE (self));
+
+ str = gtk_editable_get_text (GTK_EDITABLE (self->goto_line_entry));
+ count = sscanf (str, "%u:%u", &line, &column);
+
+ if (count >= 1)
+ {
+ EditorDocument *document = editor_page_get_document (self);
+ GtkTextIter iter;
+
+ if (line > 0)
+ --line;
+
+ if (column > 0)
+ --column;
+
+ gtk_text_buffer_get_iter_at_line_offset (GTK_TEXT_BUFFER (document), &iter, line, column);
+ while (count == 1 &&
+ !gtk_text_iter_is_end (&iter) &&
+ !gtk_text_iter_ends_line (&iter) &&
+ g_unichar_isspace (gtk_text_iter_get_char (&iter)))
+ gtk_text_iter_forward_char (&iter);
+ gtk_text_buffer_select_range (GTK_TEXT_BUFFER (document), &iter, &iter);
+ }
+
+ gtk_revealer_set_reveal_child (self->goto_line_revealer, FALSE);
+ editor_page_grab_focus (self);
+}
+
static void
on_notify_can_move_cb (EditorPage *self,
GParamSpec *pspec,
@@ -147,6 +206,10 @@ _editor_page_class_actions_init (EditorPageClass *klass)
gtk_widget_class_install_action (widget_class, "page.language", NULL,
editor_page_actions_language);
+ gtk_widget_class_install_action (widget_class, "page.show-goto-line", NULL,
+ editor_page_actions_show_goto_line);
+ gtk_widget_class_install_action (widget_class, "page.goto-line", NULL,
+ editor_page_actions_goto_line);
gtk_widget_class_install_action (widget_class, "search.hide", NULL,
editor_page_actions_search_hide);
gtk_widget_class_install_action (widget_class, "search.move-next", NULL,
diff --git a/src/editor-page-private.h b/src/editor-page-private.h
index 4469090..506ab52 100644
--- a/src/editor-page-private.h
+++ b/src/editor-page-private.h
@@ -51,6 +51,8 @@ struct _EditorPage
GtkSourceView *view;
GtkSourceMap *map;
GtkProgressBar *progress_bar;
+ GtkRevealer *goto_line_revealer;
+ GtkEntry *goto_line_entry;
GtkRevealer *search_revealer;
EditorSearchBar *search_bar;
GtkInfoBar *changed_infobar;
diff --git a/src/editor-page.c b/src/editor-page.c
index ca7a4ef..3316600 100644
--- a/src/editor-page.c
+++ b/src/editor-page.c
@@ -358,6 +358,48 @@ editor_page_drop_target_drop (EditorPage *self,
return FALSE;
}
+static void
+goto_line_entry_activate_cb (EditorPage *self,
+ GtkEntry *entry)
+{
+ g_assert (EDITOR_IS_PAGE (self));
+ g_assert (GTK_IS_ENTRY (entry));
+
+ gtk_widget_activate_action (GTK_WIDGET (self), "page.goto-line", NULL);
+}
+
+static void
+goto_line_entry_insert_text_cb (EditorPage *self,
+ const char *new_text,
+ int length,
+ int *position,
+ GtkText *entry)
+{
+ const char *text;
+ GString *str;
+
+ g_assert (EDITOR_IS_PAGE (self));
+ g_assert (position != NULL);
+ g_assert (GTK_IS_TEXT (entry));
+
+ text = gtk_editable_get_text (GTK_EDITABLE (entry));
+ str = g_string_new (text);
+
+ if (position < 0)
+ g_string_insert_len (str, *position, new_text, length);
+
+ for (const char *c = str->str; *c; c = g_utf8_next_char (c))
+ {
+ gunichar ch = g_utf8_get_char (c);
+
+ if (ch == ':' || (ch >= '0' && ch <= '9'))
+ continue;
+
+ g_signal_stop_emission_by_name (entry, "insert-text");
+ return;
+ }
+}
+
static gboolean
get_child_position_cb (GtkOverlay *overlay,
GtkWidget *child,
@@ -554,6 +596,8 @@ editor_page_class_init (EditorPageClass *klass)
gtk_widget_class_set_css_name (widget_class, "page");
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/TextEditor/ui/editor-page.ui");
gtk_widget_class_bind_template_child (widget_class, EditorPage, box);
+ gtk_widget_class_bind_template_child (widget_class, EditorPage, goto_line_entry);
+ gtk_widget_class_bind_template_child (widget_class, EditorPage, goto_line_revealer);
gtk_widget_class_bind_template_child (widget_class, EditorPage, infobar);
gtk_widget_class_bind_template_child (widget_class, EditorPage, map);
gtk_widget_class_bind_template_child (widget_class, EditorPage, overlay);
@@ -563,8 +607,10 @@ editor_page_class_init (EditorPageClass *klass)
gtk_widget_class_bind_template_child (widget_class, EditorPage, search_revealer);
gtk_widget_class_bind_template_child (widget_class, EditorPage, view);
gtk_widget_class_bind_template_callback (widget_class, get_child_position_cb);
+ gtk_widget_class_bind_template_callback (widget_class, goto_line_entry_activate_cb);
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Escape, 0, "search.hide", NULL);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_i, GDK_CONTROL_MASK, "page.show-goto-line",
NULL);
g_type_ensure (EDITOR_TYPE_INFO_BAR);
g_type_ensure (EDITOR_TYPE_SEARCH_BAR);
@@ -578,6 +624,21 @@ editor_page_init (EditorPage *self)
gtk_widget_init_template (GTK_WIDGET (self));
+ /* Work around https://gitlab.gnome.org/GNOME/gtk/-/issues/4315
+ * by connecting to the GtkText to intercept insert-text() emission.
+ */
+ for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self->goto_line_entry));
+ child;
+ child = gtk_widget_get_next_sibling (child))
+ {
+ if (GTK_IS_TEXT (child))
+ g_signal_connect_object (child,
+ "insert-text",
+ G_CALLBACK (goto_line_entry_insert_text_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ }
+
g_object_bind_property (self, "document", self->infobar, "document", 0);
dest = gtk_drop_target_new (G_TYPE_FILE, GDK_ACTION_COPY);
diff --git a/src/editor-page.ui b/src/editor-page.ui
index 41a45cd..f324637 100644
--- a/src/editor-page.ui
+++ b/src/editor-page.ui
@@ -68,6 +68,63 @@
</child>
</object>
</child>
+ <child type="overlay">
+ <object class="GtkRevealer" id="goto_line_revealer">
+ <property name="halign">end</property>
+ <property name="margin-end">6</property>
+ <property name="hexpand">false</property>
+ <property name="valign">start</property>
+ <property name="vexpand">false</property>
+ <property name="transition-duration">300</property>
+ <property name="transition-type">slide-down</property>
+ <property name="reveal-child">false</property>
+ <layout>
+ <property name="measure">true</property>
+ </layout>
+ <child>
+ <object class="GtkBox" id="goto_line_bar">
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <style>
+ <class name="searchbar"/>
+ </style>
+ <child>
+ <object class="GtkBox">
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Go to Line</property>
+ <property name="margin-start">3</property>
+ <property name="margin-end">3</property>
+ <property name="halign">start</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="goto_line_entry">
+ <property name="width-chars">3</property>
+ <property name="max-width-chars">7</property>
+ <signal name="activate" handler="goto_line_entry_activate_cb" swapped="true"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="goto_line_button">
+ <property name="label" translatable="yes">Go</property>
+ <property name="width-request">50</property>
+ <property name="action-name">page.goto-line</property>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
<child type="overlay">
<object class="GtkProgressBar" id="progress_bar">
<style>
diff --git a/src/help-overlay.ui b/src/help-overlay.ui
index 91070ef..e6f56ef 100644
--- a/src/help-overlay.ui
+++ b/src/help-overlay.ui
@@ -266,6 +266,12 @@
<property name="title" translatable="yes" context="shortcut window">Move current or selected
lines down</property>
</object>
</child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="accelerator"><ctrl>i</property>
+ <property name="title" translatable="yes" context="shortcut window">Go to line</property>
+ </object>
+ </child>
</object>
</child>
<child>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]