[gnome-builder] gb-source-vim: Add support for vim scrolloff
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] gb-source-vim: Add support for vim scrolloff
- Date: Sun, 11 Jan 2015 04:07:50 +0000 (UTC)
commit c4f71f75acb8681847ce5116d03c413cf9997b7e
Author: Carlos Soriano <carlos soriano89 gmail com>
Date: Tue Jan 6 00:01:02 2015 +0100
gb-source-vim: Add support for vim scrolloff
Vim scrolloff allow the user to set a margin of a number of lines
while moving through the buffer.
Add support to it and add a setting to let the user adjust the number
of lines.
One downside of the implementation is that movement commands scroll
horizontally not just the needed movement but also scrolls making the
word position the left as possible. This is noticed when the scroll view
has horizontal scrolling and the user moves backward in word motions in
the words that are normally outside the view when the view is scrolled
maximum to the left.
As a detail, a scrolloff greater than half of the number of lines in the
buffer keeps the current line always in the middle.
https://bugzilla.gnome.org/show_bug.cgi?id=742442
configure.ac | 1 +
data/gsettings.mk | 1 +
data/org.gnome.builder.editor.vim.gschema.xml.in | 8 +
src/gnome-builder.mk | 2 +
src/preferences/gb-preferences-page-vim.c | 94 +++++++
src/preferences/gb-preferences-page-vim.h | 55 ++++
src/preferences/gb-preferences-page.c | 22 ++
src/preferences/gb-preferences-page.h | 17 +-
src/preferences/gb-preferences-window.c | 38 +++-
src/resources/gnome-builder.gresource.xml | 1 +
src/resources/ui/gb-preferences-page-vim.ui | 90 +++++++
src/resources/ui/gb-preferences-window.ui | 9 +
src/vim/gb-source-vim.c | 296 +++++++++++++++++-----
13 files changed, 559 insertions(+), 75 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b4e0b7f..23eb9ee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -51,6 +51,7 @@ AC_CONFIG_FILES([
data/icons/Makefile
data/org.gnome.builder.editor.gschema.xml
data/org.gnome.builder.editor.language.gschema.xml
+ data/org.gnome.builder.editor.vim.gschema.xml
])
# Last change to configure things and write configuration files.
diff --git a/data/gsettings.mk b/data/gsettings.mk
index e15f2b6..fbd3e0b 100644
--- a/data/gsettings.mk
+++ b/data/gsettings.mk
@@ -1,5 +1,6 @@
gsettingsschema_in_files = \
data/org.gnome.builder.editor.gschema.xml.in \
+ data/org.gnome.builder.editor.vim.gschema.xml.in \
data/org.gnome.builder.editor.language.gschema.xml.in
gsettings_SCHEMAS = $(gsettingsschema_in_files:.xml.in=.xml)
diff --git a/data/org.gnome.builder.editor.vim.gschema.xml.in
b/data/org.gnome.builder.editor.vim.gschema.xml.in
new file mode 100644
index 0000000..9f2a829
--- /dev/null
+++ b/data/org.gnome.builder.editor.vim.gschema.xml.in
@@ -0,0 +1,8 @@
+<schemalist>
+ <schema id="org.gnome.builder.editor.vim" path="/org/gnome/builder/editor/vim/"
gettext-domain="gnome-builder">
+ <key name="scroll-off" type="i">
+ <range min="0" max="999"/>
+ <default>0</default>
+ </key>
+ </schema>
+</schemalist>
diff --git a/src/gnome-builder.mk b/src/gnome-builder.mk
index bcdc5f9..6c877ac 100644
--- a/src/gnome-builder.mk
+++ b/src/gnome-builder.mk
@@ -141,6 +141,8 @@ libgnome_builder_la_SOURCES = \
src/preferences/gb-preferences-page-git.h \
src/preferences/gb-preferences-page-language.c \
src/preferences/gb-preferences-page-language.h \
+ src/preferences/gb-preferences-page-vim.c \
+ src/preferences/gb-preferences-page-vim.h \
src/preferences/gb-preferences-page.c \
src/preferences/gb-preferences-page.h \
src/preferences/gb-preferences-window.c \
diff --git a/src/preferences/gb-preferences-page-vim.c b/src/preferences/gb-preferences-page-vim.c
new file mode 100644
index 0000000..deb642d
--- /dev/null
+++ b/src/preferences/gb-preferences-page-vim.c
@@ -0,0 +1,94 @@
+/* gb-preferences-page-vim.c
+ *
+ * Copyright (C) 2015 Carlos Soriano <csoriano gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "prefs-page-editor"
+
+#include <glib/gi18n.h>
+
+#include "gb-preferences-page-vim.h"
+
+struct _GbPreferencesPageVimPrivate
+{
+ GSettings *settings;
+
+ /* Widgets owned by Template */
+ GtkSpinButton *scroll_off_spin;
+
+ /* Template widgets used for filtering */
+ GtkWidget *scroll_off_container;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GbPreferencesPageVim, gb_preferences_page_vim,
+ GB_TYPE_PREFERENCES_PAGE)
+
+static void
+gb_preferences_page_vim_constructed (GObject *object)
+{
+ GbPreferencesPageVimPrivate *priv;
+ GbPreferencesPageVim *vim = (GbPreferencesPageVim *)object;
+
+ g_return_if_fail (GB_IS_PREFERENCES_PAGE_VIM (vim));
+
+ priv = vim->priv;
+
+ priv->settings = g_settings_new ("org.gnome.builder.editor.vim");
+
+ g_settings_bind (priv->settings, "scroll-off", priv->scroll_off_spin, "value",
+ G_SETTINGS_BIND_DEFAULT);
+}
+
+static void
+gb_preferences_page_vim_finalize (GObject *object)
+{
+ GbPreferencesPageVimPrivate *priv = GB_PREFERENCES_PAGE_VIM (object)->priv;
+
+ g_clear_object (&priv->settings);
+
+ G_OBJECT_CLASS (gb_preferences_page_vim_parent_class)->finalize (object);
+}
+
+static void
+gb_preferences_page_vim_class_init (GbPreferencesPageVimClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gb_preferences_page_vim_finalize;
+ object_class->constructed = gb_preferences_page_vim_constructed;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gnome/builder/ui/gb-preferences-page-vim.ui");
+
+ gtk_widget_class_bind_template_child_private (widget_class, GbPreferencesPageVim, scroll_off_spin);
+
+ gtk_widget_class_bind_template_child_private (widget_class, GbPreferencesPageVim, scroll_off_container);
+}
+
+static void
+gb_preferences_page_vim_init (GbPreferencesPageVim *self)
+{
+ self->priv = gb_preferences_page_vim_get_instance_private (self);
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
+ _("lines margin scrolloff scroll off"),
+ self->priv->scroll_off_container,
+ self->priv->scroll_off_spin,
+ NULL);
+}
diff --git a/src/preferences/gb-preferences-page-vim.h b/src/preferences/gb-preferences-page-vim.h
new file mode 100644
index 0000000..f3a7b0e
--- /dev/null
+++ b/src/preferences/gb-preferences-page-vim.h
@@ -0,0 +1,55 @@
+/* gb-preferences-page-vim.h
+ *
+ * Copyright (C) 2015 Carlos Soriano <csoriano gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GB_PREFERENCES_PAGE_VIM_H
+#define GB_PREFERENCES_PAGE_VIM_H
+
+#include "gb-preferences-page.h"
+
+G_BEGIN_DECLS
+
+#define GB_TYPE_PREFERENCES_PAGE_VIM (gb_preferences_page_vim_get_type())
+#define GB_PREFERENCES_PAGE_VIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GB_TYPE_PREFERENCES_PAGE_VIM, GbPreferencesPageVim))
+#define GB_PREFERENCES_PAGE_VIM_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GB_TYPE_PREFERENCES_PAGE_VIM, GbPreferencesPageVim const))
+#define GB_PREFERENCES_PAGE_VIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
GB_TYPE_PREFERENCES_PAGE_VIM, GbPreferencesPageVimClass))
+#define GB_IS_PREFERENCES_PAGE_VIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GB_TYPE_PREFERENCES_PAGE_VIM))
+#define GB_IS_PREFERENCES_PAGE_VIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
GB_TYPE_PREFERENCES_PAGE_VIM))
+#define GB_PREFERENCES_PAGE_VIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
GB_TYPE_PREFERENCES_PAGE_VIM, GbPreferencesPageVimClass))
+
+typedef struct _GbPreferencesPageVim GbPreferencesPageVim;
+typedef struct _GbPreferencesPageVimClass GbPreferencesPageVimClass;
+typedef struct _GbPreferencesPageVimPrivate GbPreferencesPageVimPrivate;
+
+struct _GbPreferencesPageVim
+{
+ GbPreferencesPage parent;
+
+ /*< private >*/
+ GbPreferencesPageVimPrivate *priv;
+};
+
+struct _GbPreferencesPageVimClass
+{
+ GbPreferencesPageClass parent;
+};
+
+GType gb_preferences_page_vim_get_type (void);
+
+G_END_DECLS
+
+#endif /* GB_PREFERENCES_PAGE_VIM_H */
diff --git a/src/preferences/gb-preferences-page.c b/src/preferences/gb-preferences-page.c
index aef24e5..fbc07d1 100644
--- a/src/preferences/gb-preferences-page.c
+++ b/src/preferences/gb-preferences-page.c
@@ -28,6 +28,7 @@ struct _GbPreferencesPagePrivate
{
GHashTable *widgets;
gchar *title;
+ gboolean active;
};
G_DEFINE_TYPE_WITH_PRIVATE (GbPreferencesPage, gb_preferences_page,
@@ -155,6 +156,23 @@ gb_preferences_page_set_title (GbPreferencesPage *page,
}
}
+gboolean
+gb_preferences_page_get_active (GbPreferencesPage *page)
+{
+ g_return_val_if_fail (GB_IS_PREFERENCES_PAGE (page), FALSE);
+
+ return page->priv->active;
+}
+
+void
+gb_preferences_page_set_active (GbPreferencesPage *page,
+ gboolean active)
+{
+ g_return_if_fail (GB_IS_PREFERENCES_PAGE (page));
+
+ page->priv->active = active;
+}
+
static void
gb_preferences_page_finalize (GObject *object)
{
@@ -230,4 +248,8 @@ gb_preferences_page_init (GbPreferencesPage *self)
self->priv = gb_preferences_page_get_instance_private (self);
self->priv->widgets = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, NULL);
+ /* Make it active by default. If some page has to be disabled
+ * let the preferences window make it disabled.
+ */
+ self->priv->active = TRUE;
}
diff --git a/src/preferences/gb-preferences-page.h b/src/preferences/gb-preferences-page.h
index e475859..5d4f93a 100644
--- a/src/preferences/gb-preferences-page.h
+++ b/src/preferences/gb-preferences-page.h
@@ -48,13 +48,16 @@ struct _GbPreferencesPageClass
GtkBinClass parent;
};
-GType gb_preferences_page_get_type (void);
-guint gb_preferences_page_set_keywords (GbPreferencesPage *page,
- const gchar * const *keywords);
-void gb_preferences_page_set_keywords_for_widget (GbPreferencesPage *page,
- const gchar *keywords,
- GtkWidget *first_widget,
- ...) G_GNUC_NULL_TERMINATED;
+GType gb_preferences_page_get_type (void);
+guint gb_preferences_page_set_keywords (GbPreferencesPage *page,
+ const gchar * const *keywords);
+void gb_preferences_page_set_keywords_for_widget (GbPreferencesPage *page,
+ const gchar *keywords,
+ GtkWidget *first_widget,
+ ...) G_GNUC_NULL_TERMINATED;
+gboolean gb_preferences_page_get_active (GbPreferencesPage *page);
+void gb_preferences_page_set_active (GbPreferencesPage *page,
+ gboolean active);
G_END_DECLS
diff --git a/src/preferences/gb-preferences-window.c b/src/preferences/gb-preferences-window.c
index d97a276..b91ed79 100644
--- a/src/preferences/gb-preferences-window.c
+++ b/src/preferences/gb-preferences-window.c
@@ -27,6 +27,8 @@
#include "gb-preferences-page-editor.h"
#include "gb-preferences-page-git.h"
#include "gb-preferences-page-language.h"
+#include "gb-preferences-page-vim.h"
+#include "gb-preferences-page.h"
#include "gb-preferences-window.h"
#include "gb-widget.h"
@@ -36,6 +38,10 @@ struct _GbPreferencesWindowPrivate
GtkSearchEntry *search_entry;
GtkSearchBar *search_bar;
GtkStack *stack;
+
+ GtkWidget *vim_page;
+
+ GSettings *editor_settings;
};
G_DEFINE_TYPE_WITH_PRIVATE (GbPreferencesWindow, gb_preferences_window,
@@ -111,7 +117,8 @@ gb_preferences_window_search_changed (GbPreferencesWindow *window,
if (0 == gb_preferences_page_set_keywords (page, (const gchar * const *)keywords))
gtk_widget_set_visible (GTK_WIDGET (page), FALSE);
else
- gtk_widget_set_visible (GTK_WIDGET (page), TRUE);
+ gtk_widget_set_visible (GTK_WIDGET (page),
+ gb_preferences_page_get_active (page));
}
g_list_free (pages);
@@ -247,6 +254,10 @@ gb_preferences_window_constructed (GObject *object)
static void
gb_preferences_window_finalize (GObject *object)
{
+ GbPreferencesWindowPrivate *priv = GB_PREFERENCES_WINDOW (object)->priv;
+
+ g_clear_object (&priv->editor_settings);
+
G_OBJECT_CLASS (gb_preferences_window_parent_class)->finalize (object);
}
@@ -277,6 +288,22 @@ gb_preferences_window_set_property (GObject *object,
}
static void
+gb_prefereces_window_vim_mode_changed (GbPreferencesWindow *window,
+ const gchar *key,
+ GSettings *settings)
+{
+ gboolean active;
+
+ g_return_if_fail (GB_IS_PREFERENCES_WINDOW (window));
+ g_return_if_fail (G_IS_SETTINGS (settings));
+
+ active = g_settings_get_boolean (settings, "vim-mode");
+ gtk_widget_set_visible (window->priv->vim_page, active);
+ gb_preferences_page_set_active (GB_PREFERENCES_PAGE (window->priv->vim_page),
+ active);
+}
+
+static void
gb_preferences_window_class_init (GbPreferencesWindowClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -311,6 +338,7 @@ gb_preferences_window_class_init (GbPreferencesWindowClass *klass)
g_type_ensure (GB_TYPE_PREFERENCES_PAGE_GIT);
g_type_ensure (GB_TYPE_PREFERENCES_PAGE_EDITOR);
g_type_ensure (GB_TYPE_PREFERENCES_PAGE_LANGUAGE);
+ g_type_ensure (GB_TYPE_PREFERENCES_PAGE_VIM);
}
static void
@@ -319,4 +347,12 @@ gb_preferences_window_init (GbPreferencesWindow *self)
self->priv = gb_preferences_window_get_instance_private (self);
gtk_widget_init_template (GTK_WIDGET (self));
+
+ self->priv->editor_settings = g_settings_new ("org.gnome.builder.editor");
+ g_signal_connect_object (self->priv->editor_settings,
+ "changed::vim-mode",
+ G_CALLBACK (gb_prefereces_window_vim_mode_changed),
+ self,
+ G_CONNECT_SWAPPED);
+ gb_prefereces_window_vim_mode_changed (self, NULL, self->priv->editor_settings);
}
diff --git a/src/resources/gnome-builder.gresource.xml b/src/resources/gnome-builder.gresource.xml
index eb7d01e..5bbd5a9 100644
--- a/src/resources/gnome-builder.gresource.xml
+++ b/src/resources/gnome-builder.gresource.xml
@@ -44,6 +44,7 @@
<file>ui/gb-preferences-page-editor.ui</file>
<file>ui/gb-preferences-page-git.ui</file>
<file>ui/gb-preferences-page-language.ui</file>
+ <file>ui/gb-preferences-page-vim.ui</file>
<file>ui/gb-search-box.ui</file>
<file>ui/gb-search-display.ui</file>
<file>ui/gb-workbench.ui</file>
diff --git a/src/resources/ui/gb-preferences-page-vim.ui b/src/resources/ui/gb-preferences-page-vim.ui
new file mode 100644
index 0000000..e55f692
--- /dev/null
+++ b/src/resources/ui/gb-preferences-page-vim.ui
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.8 -->
+ <template class="GbPreferencesPageVim" parent="GbPreferencesPage">
+ <child>
+ <object class="GtkBox">
+ <property name="visible">true</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">12</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkSpinButton" id="scroll_off_spin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="valign">center</property>
+ <property name="adjustment">scroll_off_adjustment</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="scroll_off_container">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes"><b>Scroll Off</b></property>
+ <property name="use_markup">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Margin in lines for cursor movement.</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkAdjustment" id="scroll_off_adjustment">
+ <property name="lower">0</property>
+ <property name="upper">999</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">1</property>
+ <property name="value">0</property>
+ </object>
+</interface>
diff --git a/src/resources/ui/gb-preferences-window.ui b/src/resources/ui/gb-preferences-window.ui
index d2e38d6..1859c80 100644
--- a/src/resources/ui/gb-preferences-window.ui
+++ b/src/resources/ui/gb-preferences-window.ui
@@ -132,6 +132,15 @@
<property name="title" translatable="yes">Git</property>
</packing>
</child>
+ <child>
+ <object class="GbPreferencesPageVim" id="vim_page">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="name">vim</property>
+ <property name="title" translatable="yes">Vim</property>
+ </packing>
+ </child>
</object>
</child>
</object>
diff --git a/src/vim/gb-source-vim.c b/src/vim/gb-source-vim.c
index f17db3d..11631e6 100644
--- a/src/vim/gb-source-vim.c
+++ b/src/vim/gb-source-vim.c
@@ -17,7 +17,6 @@
*/
#define G_LOG_DOMAIN "vim"
-#define SCROLL_OFF 3
#include <errno.h>
#include <glib/gi18n.h>
@@ -81,7 +80,9 @@ struct _GbSourceVimPrivate
GtkSourceSearchSettings *search_settings;
GPtrArray *captured_events;
GbSourceVimMode mode;
+ GSettings *vim_settings;
gulong key_press_event_handler;
+ gulong event_after_handler;
gulong key_release_event_handler;
gulong focus_in_event_handler;
gulong mark_set_handler;
@@ -90,6 +91,7 @@ struct _GbSourceVimPrivate
guint stash_line;
guint stash_line_offset;
guint anim_timeout;
+ guint scroll_off;
gchar recording_trigger;
gchar recording_modifier;
guint enabled : 1;
@@ -108,6 +110,20 @@ typedef enum
typedef enum
{
+ GB_SOURCE_VIM_ALIGNMENT_NONE,
+ GB_SOURCE_VIM_ALIGNMENT_KEEP,
+ GB_SOURCE_VIM_ALIGNMENT_TOP,
+ GB_SOURCE_VIM_ALIGNMENT_BOTTOM
+} GbSourceVimAlignment;
+
+typedef enum
+{
+ GB_SOURCE_VIM_ITER_BOUND_START,
+ GB_SOURCE_VIM_ITER_BOUND_END
+} GbSourceVimIterBound;
+
+typedef enum
+{
GB_SOURCE_VIM_COMMAND_NOOP,
GB_SOURCE_VIM_COMMAND_MOVEMENT,
GB_SOURCE_VIM_COMMAND_CHANGE,
@@ -139,6 +155,12 @@ typedef struct
GbSourceVimCommandFlags flags;
} GbSourceVimCommand;
+typedef struct
+{
+ gfloat yalign;
+ gint line;
+} GbSourceVimAdjustedScroll;
+
typedef enum
{
GB_SOURCE_VIM_PHRASE_FAILED,
@@ -209,6 +231,13 @@ static void gb_source_vim_cmd_delete_to_end (GbSourceVim *vim,
static void gb_source_vim_cmd_insert_before_line (GbSourceVim *vim,
guint count,
gchar modifier);
+static GbSourceVimAdjustedScroll*
+gb_source_vim_adjust_scroll (GbSourceVim *vim,
+ gint line,
+ GbSourceVimAlignment aligment);
+static void
+gb_source_vim_ensure_scroll_off (GbSourceVim *vim,
+ GbSourceVimIterBound iter_bound);
GbSourceVim *
gb_source_vim_new (GtkTextView *text_view)
@@ -545,7 +574,7 @@ gb_source_vim_clear_selection (GbSourceVim *vim)
vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
}
GbSourceVimMode
@@ -778,6 +807,34 @@ gb_source_vim_select_range (GbSourceVim *vim,
}
static void
+gb_source_vim_ensure_scroll_off (GbSourceVim *vim,
+ GbSourceVimIterBound iter_bound)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter iter_end;
+ GtkTextIter iter_start;
+ GtkTextIter iter;
+ guint line;
+ GbSourceVimAdjustedScroll *scroll;
+
+ g_return_if_fail (GB_IS_SOURCE_VIM (vim));
+
+ buffer = gtk_text_view_get_buffer (vim->priv->text_view);
+ gtk_text_buffer_get_selection_bounds (buffer, &iter_start, &iter_end);
+ iter = iter_bound == GB_SOURCE_VIM_ITER_BOUND_START ? iter_start : iter_end;
+ line = gtk_text_iter_get_line (&iter);
+ scroll = gb_source_vim_adjust_scroll (vim, line, GB_SOURCE_VIM_ALIGNMENT_NONE);
+
+ /* Only adjust scroll if necesary. In this way we avoid
+ * odd jumpings because of yalign imprecision.
+ */
+ if (scroll->yalign >= 0)
+ gtk_text_view_scroll_to_iter (vim->priv->text_view, &iter, 0.0,
+ TRUE, 1.0, scroll->yalign);
+ g_free (scroll);
+}
+
+static void
gb_source_vim_move_line0 (GbSourceVim *vim)
{
GtkTextBuffer *buffer;
@@ -887,7 +944,7 @@ gb_source_vim_move_line_end (GbSourceVim *vim)
vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);;
}
static void
@@ -1006,7 +1063,6 @@ gb_source_vim_move_backward_word (GbSourceVim *vim)
GtkTextBuffer *buffer;
GtkTextIter iter;
GtkTextIter selection;
- GtkTextMark *insert;
gboolean has_selection;
g_assert (GB_IS_SOURCE_VIM (vim));
@@ -1029,8 +1085,7 @@ gb_source_vim_move_backward_word (GbSourceVim *vim)
vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
}
static void
@@ -1163,7 +1218,6 @@ static void
gb_source_vim_move_forward_word (GbSourceVim *vim)
{
GtkTextBuffer *buffer;
- GtkTextMark *insert;
GtkTextIter iter;
GtkTextIter selection;
gboolean has_selection;
@@ -1192,15 +1246,13 @@ gb_source_vim_move_forward_word (GbSourceVim *vim)
vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
}
static void
gb_source_vim_move_forward_word_end (GbSourceVim *vim)
{
GtkTextBuffer *buffer;
- GtkTextMark *insert;
GtkTextIter iter;
GtkTextIter selection;
gboolean has_selection;
@@ -1225,8 +1277,7 @@ gb_source_vim_move_forward_word_end (GbSourceVim *vim)
vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
}
static gboolean
@@ -1248,7 +1299,6 @@ gb_source_vim_move_matching_bracket (GbSourceVim *vim)
{
MatchingBracketState state;
GtkTextBuffer *buffer;
- GtkTextMark *insert;
GtkTextIter iter;
GtkTextIter selection;
gboolean has_selection;
@@ -1316,8 +1366,7 @@ gb_source_vim_move_matching_bracket (GbSourceVim *vim)
else
gtk_text_buffer_select_range (buffer, &iter, &iter);
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
}
}
@@ -1372,7 +1421,6 @@ static void
gb_source_vim_move_forward_paragraph (GbSourceVim *vim)
{
GtkTextBuffer *buffer;
- GtkTextMark *insert;
GtkTextIter iter, selection;
gboolean has_selection;
@@ -1403,15 +1451,13 @@ gb_source_vim_move_forward_paragraph (GbSourceVim *vim)
vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
}
static void
gb_source_vim_move_backward_paragraph (GbSourceVim *vim)
{
GtkTextBuffer *buffer;
- GtkTextMark *insert;
GtkTextIter iter, selection;
gboolean has_selection;
@@ -1444,8 +1490,7 @@ gb_source_vim_move_backward_paragraph (GbSourceVim *vim)
vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
}
static void
@@ -1453,10 +1498,10 @@ gb_source_vim_move_down (GbSourceVim *vim)
{
GbSourceVimPrivate *priv;
GtkTextBuffer *buffer;
- GtkTextMark *insert;
GtkTextIter iter;
GtkTextIter selection;
gboolean has_selection;
+ GbSourceVimIterBound iter_bound;
guint line;
guint offset;
@@ -1468,6 +1513,11 @@ gb_source_vim_move_down (GbSourceVim *vim)
has_selection = gb_source_vim_get_selection_bounds (vim, &iter, &selection);
line = gtk_text_iter_get_line (&iter);
offset = vim->priv->target_line_offset;
+ iter_bound = GB_SOURCE_VIM_ITER_BOUND_END;
+
+ /* The cursor is at the start of the selection */
+ if (gtk_text_iter_compare (&iter, &selection) < 0)
+ iter_bound = GB_SOURCE_VIM_ITER_BOUND_START;
/*
* If we have a whole line selected (from say `V`), then we need to swap
@@ -1528,8 +1578,7 @@ select_to_end:
}
move_mark:
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, iter_bound);
}
static void
@@ -1537,10 +1586,10 @@ gb_source_vim_move_up (GbSourceVim *vim)
{
GbSourceVimPrivate *priv;
GtkTextBuffer *buffer;
- GtkTextMark *insert;
GtkTextIter iter;
GtkTextIter selection;
gboolean has_selection;
+ GbSourceVimIterBound iter_bound;
guint line;
guint offset;
@@ -1552,10 +1601,21 @@ gb_source_vim_move_up (GbSourceVim *vim)
has_selection = gb_source_vim_get_selection_bounds (vim, &iter, &selection);
line = gtk_text_iter_get_line (&iter);
offset = vim->priv->target_line_offset;
+ iter_bound = GB_SOURCE_VIM_ITER_BOUND_START;
if (line == 0)
return;
+ /* The cursor is at the end of the selection */
+ if (gtk_text_iter_compare (&iter, &selection) > 0)
+ iter_bound = GB_SOURCE_VIM_ITER_BOUND_END;
+
+ /*
+ * If we have a whole line selected (from say `V`), then we need to swap
+ * the cursor and selection. This feels to me like a slight bit of a hack.
+ * There may be cause to actually have a selection mode and know the type
+ * of selection (line vs individual characters).
+ */
if (is_single_line_selection (&iter, &selection))
{
if (gtk_text_iter_compare (&iter, &selection) > 0)
@@ -1596,8 +1656,7 @@ gb_source_vim_move_up (GbSourceVim *vim)
}
move_mark:
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, iter_bound);
}
static void
@@ -1649,7 +1708,6 @@ static void
gb_source_vim_delete_selection (GbSourceVim *vim)
{
GtkTextBuffer *buffer;
- GtkTextMark *insert;
GtkTextIter begin;
GtkTextIter end;
GtkClipboard *clipboard;
@@ -1701,8 +1759,7 @@ gb_source_vim_delete_selection (GbSourceVim *vim)
vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
}
static void
@@ -1857,7 +1914,6 @@ gb_source_vim_undo (GbSourceVim *vim)
{
GtkSourceUndoManager *undo;
GtkTextBuffer *buffer;
- GtkTextMark *insert;
GtkTextIter iter;
g_assert (GB_IS_SOURCE_VIM (vim));
@@ -1882,8 +1938,7 @@ gb_source_vim_undo (GbSourceVim *vim)
vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
}
static void
@@ -1917,14 +1972,13 @@ gb_source_vim_redo (GbSourceVim *vim)
vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
}
static void
gb_source_vim_join (GbSourceVim *vim)
{
GtkTextBuffer *buffer;
- GtkTextMark *insert;
GtkTextIter iter;
GtkTextIter selection;
gboolean has_selection;
@@ -1985,8 +2039,7 @@ gb_source_vim_join (GbSourceVim *vim)
vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
g_strfreev (parts);
g_free (slice);
@@ -2031,7 +2084,7 @@ gb_source_vim_insert_nl_before (GbSourceVim *vim)
vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
}
static void
@@ -2069,7 +2122,7 @@ gb_source_vim_insert_nl_after (GbSourceVim *vim,
vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
}
static void
@@ -2294,7 +2347,6 @@ static void
gb_source_vim_yank (GbSourceVim *vim)
{
GtkTextBuffer *buffer;
- GtkTextMark *insert;
GtkTextIter begin;
GtkTextIter end;
gchar *text;
@@ -2348,8 +2400,7 @@ gb_source_vim_yank (GbSourceVim *vim)
*/
gtk_text_buffer_select_range (buffer, &begin, &begin);
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
}
@@ -2533,14 +2584,11 @@ static void
gb_source_vim_move_to_line_n (GbSourceVim *vim,
guint line)
{
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
GtkTextIter iter, selection;
gboolean has_selection;
g_assert (GB_IS_SOURCE_VIM (vim));
- buffer = gtk_text_view_get_buffer (vim->priv->text_view);
has_selection = gb_source_vim_get_selection_bounds (vim, &iter, &selection);
if (is_single_line_selection (&iter, &selection))
@@ -2565,8 +2613,7 @@ gb_source_vim_move_to_line_n (GbSourceVim *vim,
vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
}
static gboolean
@@ -2626,6 +2673,88 @@ gb_source_vim_move_to_iter (GbSourceVim *vim,
TRUE, 0.5, yalign);
}
+static GbSourceVimAdjustedScroll*
+gb_source_vim_adjust_scroll (GbSourceVim *vim,
+ gint line,
+ GbSourceVimAlignment alignment)
+{
+ GdkRectangle rect;
+ gint line_top;
+ gint line_bottom;
+ gint line_current;
+ gint page_lines;
+ GtkTextIter iter_top;
+ GtkTextIter iter_bottom;
+ GtkTextIter iter_current;
+
+ GtkTextBuffer *buffer;
+ gfloat min_yalign, max_yalign, yalign;
+ GbSourceVimAdjustedScroll *result;
+
+ g_assert (GB_IS_SOURCE_VIM (vim));
+
+ gtk_text_view_get_visible_rect (vim->priv->text_view, &rect);
+ gtk_text_view_get_iter_at_location (vim->priv->text_view, &iter_top,
+ rect.x, rect.y);
+ gtk_text_view_get_iter_at_location (vim->priv->text_view, &iter_bottom,
+ rect.x, rect.y + rect.height);
+
+ buffer = gtk_text_view_get_buffer (vim->priv->text_view);
+ gtk_text_buffer_get_selection_bounds (buffer, &iter_current, NULL);
+
+ result = malloc (sizeof (*result));
+ line_top = gtk_text_iter_get_line (&iter_top);
+ line_bottom = gtk_text_iter_get_line (&iter_bottom);
+ line_current = gtk_text_iter_get_line (&iter_current);
+ page_lines = line_bottom - line_top;
+
+ if (page_lines == 0) {
+ result->line = 0;
+ result->yalign = 0.;
+
+ return result;
+ }
+
+ min_yalign = MIN (vim->priv->scroll_off / (float) page_lines, 0.5);
+ max_yalign = 1.0 - min_yalign;
+ yalign = (line - line_top) / (float) page_lines;
+
+ switch (alignment)
+ {
+ case GB_SOURCE_VIM_ALIGNMENT_NONE:
+ /* Only change yalign if necesary, if not, indicate to the caller that
+ * is not necesary to adjust scroll
+ */
+ if (min_yalign > yalign || yalign > max_yalign)
+ result->yalign = CLAMP (yalign, min_yalign, max_yalign);
+ else
+ result->yalign = -1;
+ result->line = line;
+ break;
+
+ case GB_SOURCE_VIM_ALIGNMENT_KEEP:
+ result->yalign = MAX (0.0, (float)(line_current - line_top) /
+ (float)(line_bottom - line_top));
+ result->line = line;
+ break;
+
+ case GB_SOURCE_VIM_ALIGNMENT_TOP:
+ result->yalign = CLAMP (0.0, min_yalign, max_yalign);
+ result->line = line + result->yalign * page_lines;
+ break;
+
+ case GB_SOURCE_VIM_ALIGNMENT_BOTTOM:
+ result->yalign = CLAMP (1.0, min_yalign, max_yalign);
+ result->line = line - (1.0 - result->yalign) * page_lines;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ return result;
+}
+
static void
gb_source_vim_move_page (GbSourceVim *vim,
GbSourceVimPageDirectionType direction)
@@ -2635,7 +2764,7 @@ gb_source_vim_move_page (GbSourceVim *vim,
guint offset;
gint line, line_top, line_bottom, line_current;
GtkTextBuffer *buffer;
- gfloat yalign = 0.0;
+ GbSourceVimAdjustedScroll *adjusted_scroll;
g_assert (GB_IS_SOURCE_VIM (vim));
@@ -2652,47 +2781,38 @@ gb_source_vim_move_page (GbSourceVim *vim,
line_bottom = gtk_text_iter_get_line (&iter_bottom);
line_current = gtk_text_iter_get_line (&iter_current);
- if (direction == GB_SOURCE_VIM_HALF_PAGE_UP ||
- direction == GB_SOURCE_VIM_HALF_PAGE_DOWN)
- {
- /* keep current yalign */
- if (line_bottom != line_top)
- yalign = MAX (0.0, (float)(line_current - line_top) /
- (float)(line_bottom - line_top));
- }
-
switch (direction)
{
case GB_SOURCE_VIM_HALF_PAGE_UP:
line = line_current - (line_bottom - line_top) / 2;
+ adjusted_scroll = gb_source_vim_adjust_scroll (vim, line, GB_SOURCE_VIM_ALIGNMENT_KEEP);
break;
case GB_SOURCE_VIM_HALF_PAGE_DOWN:
line = line_current + (line_bottom - line_top) / 2;
+ adjusted_scroll = gb_source_vim_adjust_scroll (vim, line, GB_SOURCE_VIM_ALIGNMENT_KEEP);
break;
case GB_SOURCE_VIM_PAGE_UP:
- yalign = 1.0;
- line = gtk_text_iter_get_line (&iter_top) + SCROLL_OFF;
+ line = gtk_text_iter_get_line (&iter_top);
+ adjusted_scroll = gb_source_vim_adjust_scroll (vim, line, GB_SOURCE_VIM_ALIGNMENT_BOTTOM);
break;
case GB_SOURCE_VIM_PAGE_DOWN:
- yalign = 0.0;
- /*
- * rect.y + rect.height is the next line after the end of the buffer so
- * now we have to decrease one more.
- */
- line = MAX (0, gtk_text_iter_get_line (&iter_bottom) - SCROLL_OFF - 1);
+ line = MAX (0, gtk_text_iter_get_line (&iter_bottom));
+ adjusted_scroll = gb_source_vim_adjust_scroll (vim, line, GB_SOURCE_VIM_ALIGNMENT_TOP);
break;
default:
g_assert_not_reached();
}
- gtk_text_iter_set_line (&iter_current, line);
+ gtk_text_iter_set_line (&iter_current, adjusted_scroll->line);
for (offset = vim->priv->target_line_offset; offset; offset--)
if (gtk_text_iter_ends_line (&iter_current) ||
!gtk_text_iter_forward_char (&iter_current))
break;
- gb_source_vim_move_to_iter (vim, &iter_current, yalign);
+ gb_source_vim_move_to_iter (vim, &iter_current, adjusted_scroll->yalign);
+
+ g_free (adjusted_scroll);
}
static void
@@ -3160,6 +3280,15 @@ gb_source_vim_handle_command (GbSourceVim *vim,
return TRUE;
}
+static void
+gb_source_vim_event_after_cb (GtkTextView *text_view,
+ GdkEventKey *event,
+ GbSourceVim *vim)
+{
+ if (vim->priv->mode == GB_SOURCE_VIM_INSERT)
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
+}
+
static gboolean
gb_source_vim_key_press_event_cb (GtkTextView *text_view,
GdkEventKey *event,
@@ -3391,6 +3520,13 @@ gb_source_vim_connect (GbSourceVim *vim)
vim,
0);
+ vim->priv->event_after_handler =
+ g_signal_connect_object (vim->priv->text_view,
+ "event-after",
+ G_CALLBACK (gb_source_vim_event_after_cb),
+ vim,
+ 0);
+
vim->priv->key_release_event_handler =
g_signal_connect_object (vim->priv->text_view,
"key-release-event",
@@ -3443,6 +3579,10 @@ gb_source_vim_disconnect (GbSourceVim *vim)
vim->priv->key_press_event_handler = 0;
g_signal_handler_disconnect (vim->priv->text_view,
+ vim->priv->event_after_handler);
+ vim->priv->event_after_handler = 0;
+
+ g_signal_handler_disconnect (vim->priv->text_view,
vim->priv->key_release_event_handler);
vim->priv->key_release_event_handler = 0;
@@ -4000,6 +4140,7 @@ gb_source_vim_finalize (GObject *object)
}
g_clear_object (&priv->search_settings);
+ g_clear_object (&priv->vim_settings);
g_string_free (priv->phrase, TRUE);
priv->phrase = NULL;
@@ -5297,6 +5438,19 @@ gb_source_vim_class_init (GbSourceVimClass *klass)
}
static void
+gb_source_vim_scroll_off_changed (GbSourceVim *vim,
+ const gchar *key,
+ GSettings *settings)
+{
+ g_return_if_fail (GB_IS_SOURCE_VIM (vim));
+ g_return_if_fail (G_IS_SETTINGS (settings));
+
+ vim->priv->scroll_off = g_settings_get_int (settings, "scroll-off");
+ if (vim->priv->text_view != NULL)
+ gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
+}
+
+static void
gb_source_vim_init (GbSourceVim *vim)
{
vim->priv = gb_source_vim_get_instance_private (vim);
@@ -5306,6 +5460,14 @@ gb_source_vim_init (GbSourceVim *vim)
vim->priv->search_settings = gtk_source_search_settings_new ();
vim->priv->captured_events =
g_ptr_array_new_with_free_func ((GDestroyNotify)gdk_event_free);
+
+ vim->priv->vim_settings = g_settings_new ("org.gnome.builder.editor.vim");
+ g_signal_connect_object (vim->priv->vim_settings,
+ "changed::scroll-off",
+ G_CALLBACK (gb_source_vim_scroll_off_changed),
+ vim,
+ G_CONNECT_SWAPPED);
+ gb_source_vim_scroll_off_changed (vim, NULL, vim->priv->vim_settings);
}
GType
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]