[gnome-builder] libide/editor: port editor search from Text Editor
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] libide/editor: port editor search from Text Editor
- Date: Fri, 15 Jul 2022 20:35:19 +0000 (UTC)
commit 49fca918d327e7adfae690991bbd326abb8d47a2
Author: Christian Hergert <chergert redhat com>
Date: Fri Jul 15 13:35:11 2022 -0700
libide/editor: port editor search from Text Editor
This also revamps it quite a bit to be a different search style that we're
looking to do in Text Editor at some point too. It's not exact to the
designs, but mostly because we'll want a flowbox like layout manager that
can reflow buttons based on allocated width.
src/libide/editor/ide-editor-page-private.h | 3 +
src/libide/editor/ide-editor-page.c | 68 ++
src/libide/editor/ide-editor-page.h | 2 +
src/libide/editor/ide-editor-page.ui | 25 +
src/libide/editor/ide-editor-search-bar-private.h | 78 +++
src/libide/editor/ide-editor-search-bar.c | 798 ++++++++++++++++++++++
src/libide/editor/ide-editor-search-bar.ui | 158 +++++
src/libide/editor/libide-editor.gresource.xml | 1 +
src/libide/editor/meson.build | 28 +-
src/libide/gtk/ide-search-entry.ui | 1 +
src/libide/sourceview/ide-source-view.c | 10 +-
11 files changed, 1157 insertions(+), 15 deletions(-)
---
diff --git a/src/libide/editor/ide-editor-page-private.h b/src/libide/editor/ide-editor-page-private.h
index b9aca73b8..b9233a989 100644
--- a/src/libide/editor/ide-editor-page-private.h
+++ b/src/libide/editor/ide-editor-page-private.h
@@ -23,6 +23,7 @@
#include <libide-plugins.h>
#include "ide-editor-page.h"
+#include "ide-editor-search-bar-private.h"
G_BEGIN_DECLS
@@ -44,6 +45,8 @@ struct _IdeEditorPage
GtkScrolledWindow *scroller;
GtkSourceMap *map;
GtkRevealer *map_revealer;
+ IdeEditorSearchBar *search_bar;
+ GtkRevealer *search_revealer;
guint completion_blocked : 1;
};
diff --git a/src/libide/editor/ide-editor-page.c b/src/libide/editor/ide-editor-page.c
index 1483956e8..7ee6ae3d8 100644
--- a/src/libide/editor/ide-editor-page.c
+++ b/src/libide/editor/ide-editor-page.c
@@ -321,6 +321,56 @@ ide_editor_page_get_file_or_directory (IdePage *page)
IDE_RETURN (ret);
}
+static void
+set_search_visible (IdeEditorPage *self,
+ gboolean search_visible,
+ IdeEditorSearchBarMode mode)
+{
+ g_return_if_fail (IDE_IS_EDITOR_PAGE (self));
+
+ if (search_visible)
+ {
+ _ide_editor_search_bar_set_mode (self->search_bar, mode);
+ _ide_editor_search_bar_attach (self->search_bar, self->buffer);
+ }
+ else
+ {
+ _ide_editor_search_bar_detach (self->search_bar);
+ }
+
+ gtk_revealer_set_reveal_child (self->search_revealer, search_visible);
+
+ if (search_visible)
+ _ide_editor_search_bar_grab_focus (self->search_bar);
+}
+
+static void
+search_hide_action (GtkWidget *widget,
+ const char *action_name,
+ GVariant *param)
+{
+ IdeEditorPage *self = IDE_EDITOR_PAGE (widget);
+
+ set_search_visible (self, FALSE, 0);
+ gtk_widget_grab_focus (GTK_WIDGET (self->view));
+}
+
+static void
+search_begin_find_action (GtkWidget *widget,
+ const char *action_name,
+ GVariant *param)
+{
+ set_search_visible (IDE_EDITOR_PAGE (widget), TRUE, IDE_EDITOR_SEARCH_BAR_MODE_SEARCH);
+}
+
+static void
+search_begin_replace_action (GtkWidget *widget,
+ const char *action_name,
+ GVariant *param)
+{
+ set_search_visible (IDE_EDITOR_PAGE (widget), TRUE, IDE_EDITOR_SEARCH_BAR_MODE_REPLACE);
+}
+
static void
ide_editor_page_dispose (GObject *object)
{
@@ -452,13 +502,23 @@ ide_editor_page_class_init (IdeEditorPageClass *klass)
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-editor/ide-editor-page.ui");
gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, map);
gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, map_revealer);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, search_bar);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, search_revealer);
gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, scroller);
gtk_widget_class_bind_template_child (widget_class, IdeEditorPage, view);
gtk_widget_class_bind_template_callback (widget_class, ide_editor_page_focus_enter_cb);
+ gtk_widget_class_install_action (widget_class, "search.hide", NULL, search_hide_action);
+ gtk_widget_class_install_action (widget_class, "search.begin-find", NULL, search_begin_find_action);
+ gtk_widget_class_install_action (widget_class, "search.begin-replace", NULL, search_begin_replace_action);
+
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_s, GDK_CONTROL_MASK, "page.save", NULL);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_f, GDK_CONTROL_MASK, "search.begin-find", NULL);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_h, GDK_CONTROL_MASK, "search.begin-replace",
NULL);
_ide_editor_page_class_actions_init (klass);
+
+ g_type_ensure (IDE_TYPE_EDITOR_SEARCH_BAR);
}
static void
@@ -859,3 +919,11 @@ ide_editor_page_scroll_to_visual_position (IdeEditorPage *self,
gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (self->view),
gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (self->buffer)));
}
+
+void
+ide_editor_page_scroll_to_insert (IdeEditorPage *self)
+{
+ g_return_if_fail (IDE_IS_EDITOR_PAGE (self));
+
+ ide_source_view_scroll_to_insert (self->view);
+}
diff --git a/src/libide/editor/ide-editor-page.h b/src/libide/editor/ide-editor-page.h
index b4ebf0c3d..c34251659 100644
--- a/src/libide/editor/ide-editor-page.h
+++ b/src/libide/editor/ide-editor-page.h
@@ -68,6 +68,8 @@ gboolean ide_editor_page_save_finish (IdeEditorPage *
GAsyncResult *result,
GError **error);
IDE_AVAILABLE_IN_ALL
+void ide_editor_page_scroll_to_insert (IdeEditorPage *self);
+IDE_AVAILABLE_IN_ALL
void ide_editor_page_scroll_to_visual_position (IdeEditorPage *self,
guint line,
guint column);
diff --git a/src/libide/editor/ide-editor-page.ui b/src/libide/editor/ide-editor-page.ui
index 96949e5ae..72b16ccb6 100644
--- a/src/libide/editor/ide-editor-page.ui
+++ b/src/libide/editor/ide-editor-page.ui
@@ -39,5 +39,30 @@
</child>
</object>
</child>
+ <child type="content">
+ <object class="GtkRevealer" id="search_revealer">
+ <property name="reveal-child">false</property>
+ <property name="transition-type">slide-up</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkSeparator">
+ <property name="orientation">horizontal</property>
+ </object>
+ </child>
+ <child>
+ <object class="IdeEditorSearchBar" id="search_bar">
+ <property name="halign">center</property>
+ <property name="margin-top">6</property>
+ <property name="margin-bottom">6</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
</template>
</interface>
diff --git a/src/libide/editor/ide-editor-search-bar-private.h
b/src/libide/editor/ide-editor-search-bar-private.h
new file mode 100644
index 000000000..3e0a6abff
--- /dev/null
+++ b/src/libide/editor/ide-editor-search-bar-private.h
@@ -0,0 +1,78 @@
+/* ide-search-bar.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-gtk.h>
+#include <libide-sourceview.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_EDITOR_SEARCH_BAR (ide_editor_search_bar_get_type())
+
+typedef enum
+{
+ IDE_EDITOR_SEARCH_BAR_MODE_SEARCH,
+ IDE_EDITOR_SEARCH_BAR_MODE_REPLACE,
+} IdeEditorSearchBarMode;
+
+G_DECLARE_FINAL_TYPE (IdeEditorSearchBar, ide_editor_search_bar, IDE, EDITOR_SEARCH_BAR, GtkWidget)
+
+struct _IdeEditorSearchBar
+{
+ GtkWidget parent_instance;
+
+ GtkSourceSearchContext *context;
+ GtkSourceSearchSettings *settings;
+
+ GtkGrid *grid;
+ IdeSearchEntry *search_entry;
+ GtkEntry *replace_entry;
+ GtkButton *replace_button;
+ GtkButton *replace_all_button;
+ GtkToggleButton *replace_mode_button;
+
+ guint offset_when_shown;
+
+ guint can_move : 1;
+ guint can_replace : 1;
+ guint can_replace_all : 1;
+ guint hide_after_move : 1;
+ guint scroll_to_first_match : 1;
+ guint jump_back_on_hide : 1;
+};
+
+void _ide_editor_search_bar_attach (IdeEditorSearchBar *self,
+ IdeBuffer *buffer);
+void _ide_editor_search_bar_detach (IdeEditorSearchBar *self);
+void _ide_editor_search_bar_set_mode (IdeEditorSearchBar *self,
+ IdeEditorSearchBarMode mode);
+void _ide_editor_search_bar_move_next (IdeEditorSearchBar *self,
+ gboolean hide_after_move);
+void _ide_editor_search_bar_move_previous (IdeEditorSearchBar *self,
+ gboolean hide_after_move);
+gboolean _ide_editor_search_bar_get_can_move (IdeEditorSearchBar *self);
+gboolean _ide_editor_search_bar_get_can_replace (IdeEditorSearchBar *self);
+gboolean _ide_editor_search_bar_get_can_replace_all (IdeEditorSearchBar *self);
+void _ide_editor_search_bar_replace (IdeEditorSearchBar *self);
+void _ide_editor_search_bar_replace_all (IdeEditorSearchBar *self);
+void _ide_editor_search_bar_grab_focus (IdeEditorSearchBar *self);
+
+G_END_DECLS
diff --git a/src/libide/editor/ide-editor-search-bar.c b/src/libide/editor/ide-editor-search-bar.c
new file mode 100644
index 000000000..e2560ef97
--- /dev/null
+++ b/src/libide/editor/ide-editor-search-bar.c
@@ -0,0 +1,798 @@
+/* ide-editor-search-bar.c
+ *
+ * Copyright 2020-2022 Christian Hergert <chergert redhat com>
+ *
+ * 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-editor-search-bar"
+
+#include "config.h"
+
+#include <libide-gui.h>
+
+#include "ide-editor-enums-private.h"
+#include "ide-editor-page-private.h"
+#include "ide-editor-search-bar-private.h"
+
+G_DEFINE_FINAL_TYPE (IdeEditorSearchBar, ide_editor_search_bar, GTK_TYPE_WIDGET)
+
+enum {
+ PROP_0,
+ PROP_CAN_MOVE,
+ PROP_CAN_REPLACE,
+ PROP_CAN_REPLACE_ALL,
+ PROP_CASE_SENSITIVE,
+ PROP_MODE,
+ PROP_USE_REGEX,
+ PROP_WHOLE_WORDS,
+ N_PROPS
+};
+
+enum {
+ MOVE_NEXT_SEARCH,
+ MOVE_PREVIOUS_SEARCH,
+ N_SIGNALS
+};
+
+static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
+
+static void
+update_properties (IdeEditorSearchBar *self)
+{
+ gboolean can_move = _ide_editor_search_bar_get_can_move (self);
+ gboolean can_replace = _ide_editor_search_bar_get_can_replace (self);
+ gboolean can_replace_all = _ide_editor_search_bar_get_can_replace_all (self);
+ int occurrence_position = -1;
+
+ if (can_move != self->can_move)
+ {
+ self->can_move = can_move;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_MOVE]);
+ }
+
+ if (can_replace != self->can_replace)
+ {
+ self->can_replace = can_replace;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_REPLACE]);
+ }
+
+ if (can_replace_all != self->can_replace_all)
+ {
+ self->can_replace_all = can_replace_all;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_REPLACE_ALL]);
+ }
+
+ if (self->context != NULL)
+ {
+ GtkTextBuffer *buffer = GTK_TEXT_BUFFER (gtk_source_search_context_get_buffer (self->context));
+ GtkTextIter begin, end;
+
+ if (gtk_text_buffer_get_selection_bounds (buffer, &begin, &end))
+ occurrence_position = gtk_source_search_context_get_occurrence_position (self->context, &begin,
&end);
+ }
+
+ ide_search_entry_set_occurrence_position (self->search_entry, occurrence_position);
+}
+
+static void
+ide_editor_search_bar_scroll_to_insert (IdeEditorSearchBar *self)
+{
+ GtkWidget *page;
+
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+ if ((page = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_EDITOR_PAGE)))
+ ide_editor_page_scroll_to_insert (IDE_EDITOR_PAGE (page));
+}
+
+static void
+ide_editor_search_bar_move_next_forward_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GtkSourceSearchContext *context = (GtkSourceSearchContext *)object;
+ g_autoptr(IdeEditorSearchBar) self = user_data;
+ g_autoptr(GError) error = NULL;
+ GtkSourceBuffer *buffer;
+ GtkTextIter begin;
+ GtkTextIter end;
+ gboolean has_wrapped = FALSE;
+
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (GTK_SOURCE_IS_SEARCH_CONTEXT (context));
+
+ if (!gtk_source_search_context_forward_finish (context, result, &begin, &end, &has_wrapped, &error))
+ {
+ if (error != NULL)
+ g_debug ("Search forward error: %s", error->message);
+ return;
+ }
+
+ buffer = gtk_source_search_context_get_buffer (context);
+ gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), &begin, &end);
+ ide_editor_search_bar_scroll_to_insert (self);
+
+ if (self->hide_after_move)
+ gtk_widget_activate_action (GTK_WIDGET (self), "search.hide", NULL);
+}
+
+void
+_ide_editor_search_bar_move_next (IdeEditorSearchBar *self,
+ gboolean hide_after_move)
+{
+ GtkSourceBuffer *buffer;
+ GtkTextIter begin;
+ GtkTextIter end;
+
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+ if (self->context == NULL)
+ return;
+
+ self->hide_after_move = !!hide_after_move;
+ self->jump_back_on_hide = FALSE;
+
+ buffer = gtk_source_search_context_get_buffer (self->context);
+ gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer), &begin, &end);
+ gtk_text_iter_order (&begin, &end);
+
+ gtk_source_search_context_forward_async (self->context,
+ &end,
+ NULL,
+ ide_editor_search_bar_move_next_forward_cb,
+ g_object_ref (self));
+}
+
+void
+_ide_editor_search_bar_move_previous (IdeEditorSearchBar *self,
+ gboolean hide_after_move)
+{
+ GtkSourceBuffer *buffer;
+ GtkTextIter begin;
+ GtkTextIter end;
+
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+ if (self->context == NULL)
+ return;
+
+ self->hide_after_move = !!hide_after_move;
+ self->jump_back_on_hide = FALSE;
+
+ buffer = gtk_source_search_context_get_buffer (self->context);
+ gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer), &begin, &end);
+ gtk_text_iter_order (&begin, &end);
+
+ gtk_source_search_context_backward_async (self->context,
+ &begin,
+ NULL,
+ /* XXX: fixme */
+ ide_editor_search_bar_move_next_forward_cb,
+ g_object_ref (self));
+}
+
+static void
+move_next_action (GtkWidget *widget,
+ const char *action_name,
+ GVariant *param)
+{
+ _ide_editor_search_bar_move_next (IDE_EDITOR_SEARCH_BAR (widget),
+ g_variant_get_boolean (param));
+}
+
+static void
+move_previous_action (GtkWidget *widget,
+ const char *action_name,
+ GVariant *param)
+{
+ _ide_editor_search_bar_move_previous (IDE_EDITOR_SEARCH_BAR (widget),
+ g_variant_get_boolean (param));
+}
+
+static gboolean
+text_to_search_text (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ IdeEditorSearchBar *self = user_data;
+ const gchar *str = g_value_get_string (from_value);
+
+ if (!str || gtk_source_search_settings_get_regex_enabled (self->settings))
+ g_value_set_string (to_value, str);
+ else
+ g_value_take_string (to_value, gtk_source_utils_unescape_search_text (str));
+
+ return TRUE;
+}
+
+static gboolean
+search_text_to_text (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ IdeEditorSearchBar *self = user_data;
+ const gchar *str = g_value_get_string (from_value);
+
+ if (str == NULL)
+ str = "";
+
+ if (gtk_source_search_settings_get_regex_enabled (self->settings))
+ g_value_take_string (to_value, gtk_source_utils_escape_search_text (str));
+ else
+ g_value_set_string (to_value, str);
+
+ return TRUE;
+}
+
+static gboolean
+mode_to_boolean (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ if (g_value_get_enum (from_value) == IDE_EDITOR_SEARCH_BAR_MODE_REPLACE)
+ g_value_set_boolean (to_value, TRUE);
+ else
+ g_value_set_boolean (to_value, FALSE);
+ return TRUE;
+}
+
+static gboolean
+boolean_to_mode (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ if (g_value_get_boolean (from_value))
+ g_value_set_enum (to_value, IDE_EDITOR_SEARCH_BAR_MODE_REPLACE);
+ else
+ g_value_set_enum (to_value, IDE_EDITOR_SEARCH_BAR_MODE_SEARCH);
+ return TRUE;
+}
+
+void
+_ide_editor_search_bar_grab_focus (IdeEditorSearchBar *self)
+{
+ g_return_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+ gtk_widget_grab_focus (GTK_WIDGET (self->search_entry));
+}
+
+static void
+on_notify_replace_text_cb (IdeEditorSearchBar *self,
+ GParamSpec *pspec,
+ GtkEntry *entry)
+{
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+ g_assert (GTK_IS_ENTRY (entry));
+
+ update_properties (self);
+}
+
+static void
+on_notify_search_text_cb (IdeEditorSearchBar *self,
+ GParamSpec *pspec,
+ IdeSearchEntry *entry)
+{
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+ g_assert (IDE_IS_SEARCH_ENTRY (entry));
+
+ self->scroll_to_first_match = TRUE;
+}
+
+static gboolean
+on_search_key_pressed_cb (GtkEventControllerKey *key,
+ guint keyval,
+ guint keycode,
+ GdkModifierType state,
+ IdeEditorSearchBar *self)
+{
+ g_assert (GTK_IS_EVENT_CONTROLLER_KEY (key));
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+ if ((state & (GDK_CONTROL_MASK | GDK_ALT_MASK)) == 0)
+ {
+ switch (keyval)
+ {
+ case GDK_KEY_Up:
+ case GDK_KEY_KP_Up:
+ _ide_editor_search_bar_move_previous (self, FALSE);
+ return TRUE;
+
+ case GDK_KEY_Down:
+ case GDK_KEY_KP_Down:
+ _ide_editor_search_bar_move_next (self, FALSE);
+ return TRUE;
+
+ default:
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+on_settings_notify_cb (IdeEditorSearchBar *self,
+ GParamSpec *pspec,
+ GtkSourceSearchSettings *settings)
+{
+ if (g_strcmp0 (pspec->name, "at-word-boundaries") == 0)
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_WHOLE_WORDS]);
+ else if (g_strcmp0 (pspec->name, "regex-enabled") == 0)
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_USE_REGEX]);
+ else if (g_strcmp0 (pspec->name, "case-sensitive") == 0)
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CASE_SENSITIVE]);
+}
+
+static void
+ide_editor_search_bar_dispose (GObject *object)
+{
+ IdeEditorSearchBar *self = (IdeEditorSearchBar *)object;
+
+ g_clear_pointer ((GtkWidget **)&self->grid, gtk_widget_unparent);
+
+ G_OBJECT_CLASS (ide_editor_search_bar_parent_class)->dispose (object);
+}
+
+static void
+ide_editor_search_bar_finalize (GObject *object)
+{
+ IdeEditorSearchBar *self = (IdeEditorSearchBar *)object;
+
+ g_clear_object (&self->context);
+ g_clear_object (&self->settings);
+
+ G_OBJECT_CLASS (ide_editor_search_bar_parent_class)->finalize (object);
+}
+
+static void
+ide_editor_search_bar_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeEditorSearchBar *self = IDE_EDITOR_SEARCH_BAR (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODE:
+ g_value_set_enum (value,
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->replace_mode_button)) ?
+ IDE_EDITOR_SEARCH_BAR_MODE_REPLACE :
+ IDE_EDITOR_SEARCH_BAR_MODE_SEARCH);
+ break;
+
+ case PROP_CAN_MOVE:
+ g_value_set_boolean (value, _ide_editor_search_bar_get_can_move (self));
+ break;
+
+ case PROP_CAN_REPLACE:
+ g_value_set_boolean (value, _ide_editor_search_bar_get_can_replace (self));
+ break;
+
+ case PROP_CAN_REPLACE_ALL:
+ g_value_set_boolean (value, _ide_editor_search_bar_get_can_replace_all (self));
+ break;
+
+ case PROP_CASE_SENSITIVE:
+ g_value_set_boolean (value, gtk_source_search_settings_get_case_sensitive (self->settings));
+ break;
+
+ case PROP_WHOLE_WORDS:
+ g_value_set_boolean (value, gtk_source_search_settings_get_at_word_boundaries (self->settings));
+ break;
+
+ case PROP_USE_REGEX:
+ g_value_set_boolean (value, gtk_source_search_settings_get_regex_enabled (self->settings));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_editor_search_bar_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeEditorSearchBar *self = IDE_EDITOR_SEARCH_BAR (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODE:
+ _ide_editor_search_bar_set_mode (self, g_value_get_enum (value));
+ break;
+
+ case PROP_CASE_SENSITIVE:
+ gtk_source_search_settings_set_case_sensitive (self->settings, g_value_get_boolean (value));
+ break;
+
+ case PROP_WHOLE_WORDS:
+ gtk_source_search_settings_set_at_word_boundaries (self->settings, g_value_get_boolean (value));
+ break;
+
+ case PROP_USE_REGEX:
+ gtk_source_search_settings_set_regex_enabled (self->settings, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_editor_search_bar_class_init (IdeEditorSearchBarClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = ide_editor_search_bar_dispose;
+ object_class->finalize = ide_editor_search_bar_finalize;
+ object_class->get_property = ide_editor_search_bar_get_property;
+ object_class->set_property = ide_editor_search_bar_set_property;
+
+ gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+ gtk_widget_class_set_css_name (widget_class, "searchbar");
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/libide-editor/ide-editor-search-bar.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, grid);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, replace_all_button);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, replace_button);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, replace_entry);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, replace_mode_button);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, search_entry);
+ gtk_widget_class_bind_template_callback (widget_class, on_search_key_pressed_cb);
+
+ signals [MOVE_NEXT_SEARCH] =
+ g_signal_new_class_handler ("move-next-search",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (_ide_editor_search_bar_move_next),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+ signals [MOVE_PREVIOUS_SEARCH] =
+ g_signal_new_class_handler ("move-previous-search",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (_ide_editor_search_bar_move_previous),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+ properties [PROP_MODE] =
+ g_param_spec_enum ("mode",
+ "Mode",
+ "The mode for the search bar",
+ IDE_TYPE_EDITOR_SEARCH_BAR_MODE,
+ IDE_EDITOR_SEARCH_BAR_MODE_SEARCH,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_CAN_MOVE] =
+ g_param_spec_boolean ("can-move",
+ "Can Move",
+ "If there are search results",
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_CAN_REPLACE] =
+ g_param_spec_boolean ("can-replace",
+ "Can Replace",
+ "If search is ready to replace a single result",
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_CAN_REPLACE_ALL] =
+ g_param_spec_boolean ("can-replace-all",
+ "Can Replace All",
+ "If search is ready to replace all results",
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_CASE_SENSITIVE] =
+ g_param_spec_boolean ("case-sensitive", NULL, NULL,
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_USE_REGEX] =
+ g_param_spec_boolean ("use-regex", NULL, NULL,
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_WHOLE_WORDS] =
+ g_param_spec_boolean ("whole-words", NULL, NULL,
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_install_property_action (widget_class, "search.case-sensitive", "case-sensitive");
+ gtk_widget_class_install_property_action (widget_class, "search.whole-words", "whole-words");
+ gtk_widget_class_install_property_action (widget_class, "search.use-regex", "use-regex");
+ gtk_widget_class_install_action (widget_class, "search.move-next", "b", move_next_action);
+ gtk_widget_class_install_action (widget_class, "search.move-previous", "b", move_previous_action);
+
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Escape, 0, "search.hide", NULL);
+
+ g_type_ensure (IDE_TYPE_SEARCH_ENTRY);
+}
+
+static void
+ide_editor_search_bar_init (IdeEditorSearchBar *self)
+{
+ self->settings = gtk_source_search_settings_new ();
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ g_signal_connect_object (self->replace_entry,
+ "notify::text",
+ G_CALLBACK (on_notify_replace_text_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->search_entry,
+ "notify::text",
+ G_CALLBACK (on_notify_search_text_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->settings,
+ "notify",
+ G_CALLBACK (on_settings_notify_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ gtk_source_search_settings_set_wrap_around (self->settings, TRUE);
+
+ g_object_bind_property_full (self->settings, "search-text",
+ self->search_entry, "text",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+ search_text_to_text, text_to_search_text, self, NULL);
+ g_object_bind_property_full (self->replace_mode_button, "active",
+ self, "mode",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+ boolean_to_mode, mode_to_boolean, NULL, NULL);
+}
+
+void
+_ide_editor_search_bar_set_mode (IdeEditorSearchBar *self,
+ IdeEditorSearchBarMode mode)
+{
+ gboolean is_replace;
+
+ g_return_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+ is_replace = mode == IDE_EDITOR_SEARCH_BAR_MODE_REPLACE;
+
+ gtk_widget_set_visible (GTK_WIDGET (self->replace_entry), is_replace);
+ gtk_widget_set_visible (GTK_WIDGET (self->replace_button), is_replace);
+ gtk_widget_set_visible (GTK_WIDGET (self->replace_all_button), is_replace);
+ gtk_toggle_button_set_active (self->replace_mode_button, is_replace);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MODE]);
+}
+
+static void
+scroll_to_first_match (IdeEditorSearchBar *self,
+ GtkSourceSearchContext *context)
+{
+ GtkTextIter iter, match_begin, match_end;
+ GtkTextBuffer *buffer;
+ GtkWidget *page;
+ gboolean wrapped;
+
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+ g_assert (GTK_SOURCE_IS_SEARCH_CONTEXT (context));
+
+ if (!(page = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_EDITOR_PAGE)))
+ return;
+
+ buffer = GTK_TEXT_BUFFER (gtk_source_search_context_get_buffer (context));
+ gtk_text_buffer_get_iter_at_offset (buffer, &iter, self->offset_when_shown);
+ if (gtk_source_search_context_forward (context, &iter, &match_begin, &match_end, &wrapped))
+ {
+ gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (IDE_EDITOR_PAGE (page)->view),
+ &match_begin, 0.25, TRUE, 1.0, 0.5);
+ self->jump_back_on_hide = TRUE;
+ }
+
+ self->scroll_to_first_match = FALSE;
+}
+
+static void
+ide_editor_search_bar_notify_occurrences_count_cb (IdeEditorSearchBar *self,
+ GParamSpec *pspec,
+ GtkSourceSearchContext *context)
+{
+ guint occurrence_count;
+
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+ g_assert (GTK_SOURCE_IS_SEARCH_CONTEXT (context));
+
+ occurrence_count = gtk_source_search_context_get_occurrences_count (context);
+ ide_search_entry_set_occurrence_count (self->search_entry, occurrence_count);
+
+ if (self->scroll_to_first_match && occurrence_count > 0)
+ scroll_to_first_match (self, context);
+
+ gtk_widget_action_set_enabled (GTK_WIDGET (self), "search.move-next", occurrence_count > 0);
+ gtk_widget_action_set_enabled (GTK_WIDGET (self), "search.move-previous", occurrence_count > 0);
+
+ update_properties (self);
+}
+
+static void
+ide_editor_search_bar_cursor_moved_cb (IdeEditorSearchBar *self,
+ IdeBuffer *buffer)
+{
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ update_properties (self);
+}
+
+void
+_ide_editor_search_bar_attach (IdeEditorSearchBar *self,
+ IdeBuffer *buffer)
+{
+ GtkTextIter begin, end, insert;
+
+ g_return_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+ &insert,
+ gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer)));
+ self->offset_when_shown = gtk_text_iter_get_offset (&insert);
+
+ if (gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer), &begin, &end))
+ {
+ g_autofree gchar *text = gtk_text_iter_get_slice (&begin, &end);
+ gtk_editable_set_text (GTK_EDITABLE (self->search_entry), text);
+ }
+
+ if (self->context != NULL)
+ return;
+
+ self->context = gtk_source_search_context_new (GTK_SOURCE_BUFFER (buffer), self->settings);
+
+ g_signal_connect_object (self->context,
+ "notify::occurrences-count",
+ G_CALLBACK (ide_editor_search_bar_notify_occurrences_count_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (buffer,
+ "cursor-moved",
+ G_CALLBACK (ide_editor_search_bar_cursor_moved_cb),
+ self,
+ G_CONNECT_SWAPPED);
+}
+
+void
+_ide_editor_search_bar_detach (IdeEditorSearchBar *self)
+{
+ g_return_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+ if (self->context != NULL)
+ {
+ IdeBuffer *buffer = IDE_BUFFER (gtk_source_search_context_get_buffer (self->context));
+
+ if (self->jump_back_on_hide)
+ ide_editor_search_bar_scroll_to_insert (self);
+
+ g_signal_handlers_disconnect_by_func (self->context,
+ G_CALLBACK (ide_editor_search_bar_notify_occurrences_count_cb),
+ self);
+ g_signal_handlers_disconnect_by_func (buffer,
+ G_CALLBACK (ide_editor_search_bar_cursor_moved_cb),
+ self);
+
+ g_clear_object (&self->context);
+ }
+
+ self->hide_after_move = FALSE;
+ self->jump_back_on_hide = FALSE;
+}
+
+gboolean
+_ide_editor_search_bar_get_can_move (IdeEditorSearchBar *self)
+{
+ g_return_val_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self), FALSE);
+
+ return self->context != NULL &&
+ gtk_source_search_context_get_occurrences_count (self->context) > 0;
+}
+
+gboolean
+_ide_editor_search_bar_get_can_replace (IdeEditorSearchBar *self)
+{
+ GtkTextIter begin, end;
+ GtkTextBuffer *buffer;
+
+ g_return_val_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self), FALSE);
+
+ if (self->context == NULL)
+ return FALSE;
+
+ buffer = GTK_TEXT_BUFFER (gtk_source_search_context_get_buffer (self->context));
+
+ return _ide_editor_search_bar_get_can_move (self) &&
+ gtk_text_buffer_get_selection_bounds (buffer, &begin, &end) &&
+ gtk_source_search_context_get_occurrence_position (self->context, &begin, &end) > 0;
+}
+
+gboolean
+_ide_editor_search_bar_get_can_replace_all (IdeEditorSearchBar *self)
+{
+ g_return_val_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self), FALSE);
+
+ return _ide_editor_search_bar_get_can_move (self);
+}
+
+void
+_ide_editor_search_bar_replace (IdeEditorSearchBar *self)
+{
+ g_autoptr(GError) error = NULL;
+ GtkSourceBuffer *buffer;
+ GtkTextIter begin, end;
+ const char *replace;
+
+ g_return_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+ if (!_ide_editor_search_bar_get_can_replace (self))
+ return;
+
+ buffer = gtk_source_search_context_get_buffer (self->context);
+ replace = gtk_editable_get_text (GTK_EDITABLE (self->replace_entry));
+
+ gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer), &begin, &end);
+
+ if (!gtk_source_search_context_replace (self->context, &begin, &end, replace, -1, &error))
+ {
+ g_warning ("Failed to replace match: %s", error->message);
+ return;
+ }
+
+ gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), &end, &end);
+ _ide_editor_search_bar_move_next (self, FALSE);
+}
+
+void
+_ide_editor_search_bar_replace_all (IdeEditorSearchBar *self)
+{
+ g_autoptr(GError) error = NULL;
+ g_autofree char *unescaped = NULL;
+ const char *replace;
+
+ g_return_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+ if (!_ide_editor_search_bar_get_can_replace_all (self))
+ return;
+
+ replace = gtk_editable_get_text (GTK_EDITABLE (self->replace_entry));
+ unescaped = gtk_source_utils_unescape_search_text (replace);
+
+ if (!gtk_source_search_context_replace_all (self->context, unescaped, -1, &error))
+ g_warning ("Failed to replace all matches: %s", error->message);
+}
diff --git a/src/libide/editor/ide-editor-search-bar.ui b/src/libide/editor/ide-editor-search-bar.ui
new file mode 100644
index 000000000..e075f30a6
--- /dev/null
+++ b/src/libide/editor/ide-editor-search-bar.ui
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk" version="4.0"/>
+ <template class="IdeEditorSearchBar" parent="GtkWidget">
+ <child>
+ <object class="GtkGrid" id="grid">
+ <property name="column-spacing">6</property>
+ <property name="row-spacing">6</property>
+ <child>
+ <object class="IdeSearchEntry" id="search_entry">
+ <property name="hexpand">true</property>
+ <layout>
+ <property name="row">0</property>
+ <property name="column">0</property>
+ </layout>
+ <child>
+ <object class="GtkEventControllerKey">
+ <property name="propagation-phase">capture</property>
+ <signal name="key-pressed" handler="on_search_key_pressed_cb"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="replace_entry">
+ <property name="primary-icon-name">edit-find-replace-symbolic</property>
+ <property name="max-width-chars">20</property>
+ <property name="hexpand">true</property>
+ <property name="visible">false</property>
+ <property name="placeholder-text" translatable="yes">Replace</property>
+ <layout>
+ <property name="row">1</property>
+ <property name="column">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="homogeneous">true</property>
+ <property name="orientation">horizontal</property>
+ <property name="spacing">3</property>
+ <child>
+ <object class="GtkButton">
+ <property name="action-name">search.move-previous</property>
+ <property name="action-target">false</property>
+ <property name="tooltip-text" translatable="yes">Move to previous match
(Ctrl+Shift+G)</property>
+ <property name="icon-name">go-up-symbolic</property>
+ <style>
+ <class name="flat"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="action-name">search.move-next</property>
+ <property name="action-target">false</property>
+ <property name="tooltip-text" translatable="yes">Move to next match (Ctrl+G)</property>
+ <property name="icon-name">go-down-symbolic</property>
+ <style>
+ <class name="flat"/>
+ </style>
+ </object>
+ </child>
+ <layout>
+ <property name="row">0</property>
+ <property name="column">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="replace_mode_button">
+ <property name="tooltip-text" translatable="yes">Search & Replace (Ctrl+H)</property>
+ <property name="icon-name">edit-find-replace-symbolic</property>
+ <layout>
+ <property name="row">0</property>
+ <property name="column">2</property>
+ </layout>
+ <style>
+ <class name="flat"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuButton">
+ <property name="direction">up</property>
+ <property name="icon-name">view-more-symbolic</property>
+ <property name="menu-model">options_menu</property>
+ <layout>
+ <property name="row">0</property>
+ <property name="column">3</property>
+ </layout>
+ <style>
+ <class name="flat"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="close_button">
+ <property name="margin-end">3</property>
+ <property name="action-name">search.hide</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="tooltip-text" translatable="yes">Close search</property>
+ <property name="icon-name">window-close-symbolic</property>
+ <style>
+ <class name="flat"/>
+ <class name="small-button"/>
+ <class name="circular"/>
+ </style>
+ <layout>
+ <property name="row">0</property>
+ <property name="column">4</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="replace_button">
+ <property name="action-name">search.replace-one</property>
+ <property name="use-underline">true</property>
+ <property name="label" translatable="yes">_Replace</property>
+ <property name="visible">false</property>
+ <layout>
+ <property name="row">1</property>
+ <property name="column">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="replace_all_button">
+ <property name="action-name">search.replace-all</property>
+ <property name="use-underline">true</property>
+ <property name="label" translatable="yes">Replace _All</property>
+ <property name="visible">false</property>
+ <layout>
+ <property name="row">1</property>
+ <property name="column">2</property>
+ <property name="column-span">3</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <menu id="options_menu">
+ <item>
+ <attribute name="label" translatable="yes">Use Re_gular Expressions</attribute>
+ <attribute name="action">search.use-regex</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">_Case Sensitive</attribute>
+ <attribute name="action">search.case-sensitive</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">Match Whole Words</attribute>
+ <attribute name="action">search.whole-words</attribute>
+ </item>
+ </menu>
+</interface>
diff --git a/src/libide/editor/libide-editor.gresource.xml b/src/libide/editor/libide-editor.gresource.xml
index 8ceb752e9..d9d0869bd 100644
--- a/src/libide/editor/libide-editor.gresource.xml
+++ b/src/libide/editor/libide-editor.gresource.xml
@@ -2,6 +2,7 @@
<gresources>
<gresource prefix="/org/gnome/libide-editor/">
<file preprocess="xml-stripblanks">ide-editor-page.ui</file>
+ <file preprocess="xml-stripblanks">ide-editor-search-bar.ui</file>
<file preprocess="xml-stripblanks">ide-editor-workspace.ui</file>
<file preprocess="xml-stripblanks">gtk/menus.ui</file>
<file>style.css</file>
diff --git a/src/libide/editor/meson.build b/src/libide/editor/meson.build
index c6eb702b8..c1105c696 100644
--- a/src/libide/editor/meson.build
+++ b/src/libide/editor/meson.build
@@ -42,11 +42,9 @@ libide_editor_private_sources = [
'ide-editor-init.c',
'ide-editor-page-actions.c',
'ide-editor-page-settings.c',
+ 'ide-editor-search-bar.c',
]
-libide_editor_sources += libide_editor_public_sources
-libide_editor_sources += libide_editor_private_sources
-
#
# Generated Resource Files
#
@@ -59,6 +57,17 @@ libide_editor_resources = gnome.compile_resources(
libide_editor_generated_headers += [libide_editor_resources[1]]
libide_editor_sources += libide_editor_resources
+#
+# Enums Generation
+#
+libide_editor_private_enums_headers = [
+ 'ide-editor-search-bar-private.h',
+]
+libide_editor_private_enums = gnome.mkenums_simple('ide-editor-enums-private',
+ body_prefix: '#include "config.h"',
+ sources: libide_editor_private_enums_headers,
+)
+
#
# Dependencies
#
@@ -78,16 +87,17 @@ libide_editor_deps = [
libide_gui_dep,
]
-libide_editor_internal_deps = [
-]
-
#
# Library Definitions
#
-libide_editor = static_library('ide-editor-' + libide_api_version, libide_editor_sources,
- dependencies: libide_editor_deps + libide_editor_internal_deps,
- c_args: libide_args + release_args + ['-DIDE_EDITOR_COMPILATION'],
+libide_editor_sources += libide_editor_public_sources
+libide_editor_sources += libide_editor_private_sources
+
+libide_editor = static_library('ide-editor-' + libide_api_version,
+ libide_editor_sources + libide_editor_private_enums,
+ dependencies: libide_editor_deps,
+ c_args: libide_args + release_args + ['-DIDE_EDITOR_COMPILATION'],
)
libide_editor_dep = declare_dependency(
diff --git a/src/libide/gtk/ide-search-entry.ui b/src/libide/gtk/ide-search-entry.ui
index 7bcb1c30c..e751faee1 100644
--- a/src/libide/gtk/ide-search-entry.ui
+++ b/src/libide/gtk/ide-search-entry.ui
@@ -14,6 +14,7 @@
<property name="vexpand">true</property>
<property name="width-chars">12</property>
<property name="max-width-chars">12</property>
+ <property name="placeholder-text" translatable="yes">Search</property>
<signal name="notify" handler="on_text_notify_cb" swapped="true"/>
<signal name="activate" handler="on_text_activate_cb" swapped="true"/>
</object>
diff --git a/src/libide/sourceview/ide-source-view.c b/src/libide/sourceview/ide-source-view.c
index 1442fa952..17348ce0e 100644
--- a/src/libide/sourceview/ide-source-view.c
+++ b/src/libide/sourceview/ide-source-view.c
@@ -855,17 +855,15 @@ void
ide_source_view_scroll_to_insert (IdeSourceView *self)
{
GtkTextBuffer *buffer;
- GtkTextView *view;
GtkTextMark *mark;
+ GtkTextIter iter;
g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
- view = GTK_TEXT_VIEW (self);
- buffer = gtk_text_view_get_buffer (view);
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
mark = gtk_text_buffer_get_insert (buffer);
-
- /* TODO: use margin to implement "scroll offset" */
- gtk_text_view_scroll_to_mark (view, mark, .25, FALSE, .0, .0);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+ ide_source_view_jump_to_iter (GTK_TEXT_VIEW (self), &iter, .25, TRUE, 1.0, 0.5);
}
void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]