[gnome-text-editor] searchbar: use custom entry for search entry



commit 3d373666b86d253a765e6e9c70696962a9f72d5b
Author: Christian Hergert <chergert redhat com>
Date:   Wed Oct 6 16:35:10 2021 -0700

    searchbar: use custom entry for search entry
    
    This gives us basically what we used to have in GTK 3 with the
    GdTaggedEntry but specific for the search bar pattern within Text Editor.
    
    Fixes #147

 src/editor-search-bar.c                |  39 ++++--
 src/editor-search-bar.ui               |   5 +-
 src/editor-search-entry-private.h      |  37 ++++++
 src/editor-search-entry.c              | 236 +++++++++++++++++++++++++++++++++
 src/editor-search-entry.ui             |  29 ++++
 src/meson.build                        |   1 +
 src/org.gnome.TextEditor.gresource.xml |   1 +
 7 files changed, 334 insertions(+), 14 deletions(-)
---
diff --git a/src/editor-search-bar.c b/src/editor-search-bar.c
index 893eff9..0844459 100644
--- a/src/editor-search-bar.c
+++ b/src/editor-search-bar.c
@@ -25,6 +25,7 @@
 #include "editor-enums.h"
 #include "editor-page-private.h"
 #include "editor-search-bar-private.h"
+#include "editor-search-entry-private.h"
 #include "editor-utils-private.h"
 
 struct _EditorSearchBar
@@ -36,7 +37,7 @@ struct _EditorSearchBar
   GCancellable            *cancellable;
 
   GtkGrid                 *grid;
-  GtkEntry                *search_entry;
+  EditorSearchEntry       *search_entry;
   GtkEntry                *replace_entry;
   GtkButton               *replace_button;
   GtkButton               *replace_all_button;
@@ -84,6 +85,7 @@ update_properties (EditorSearchBar *self)
   gboolean can_move = _editor_search_bar_get_can_move (self);
   gboolean can_replace = _editor_search_bar_get_can_replace (self);
   gboolean can_replace_all = _editor_search_bar_get_can_replace_all (self);
+  int occurrence_position = -1;
 
   if (can_move != self->can_move)
     {
@@ -102,6 +104,17 @@ update_properties (EditorSearchBar *self)
       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);
+    }
+
+  editor_search_entry_set_occurrence_position (self->search_entry, occurrence_position);
 }
 
 static void
@@ -116,11 +129,11 @@ editor_search_bar_scroll_to_insert (EditorSearchBar *self)
 }
 
 static void
-editor_search_bar_search_activate_cb (EditorSearchBar *self,
-                                      GtkEntry        *entry)
+editor_search_bar_search_activate_cb (EditorSearchBar   *self,
+                                      EditorSearchEntry *entry)
 {
   g_assert (EDITOR_IS_SEARCH_BAR (self));
-  g_assert (GTK_IS_ENTRY (entry));
+  g_assert (EDITOR_IS_SEARCH_ENTRY (entry));
 
   _editor_search_bar_move_next (self, TRUE);
 }
@@ -303,12 +316,12 @@ on_notify_replace_text_cb (EditorSearchBar *self,
 }
 
 static void
-on_notify_search_text_cb (EditorSearchBar *self,
-                          GParamSpec      *pspec,
-                          GtkEntry        *entry)
+on_notify_search_text_cb (EditorSearchBar   *self,
+                          GParamSpec        *pspec,
+                          EditorSearchEntry *entry)
 {
   g_assert (EDITOR_IS_SEARCH_BAR (self));
-  g_assert (GTK_IS_ENTRY (entry));
+  g_assert (EDITOR_IS_SEARCH_ENTRY (entry));
 
   self->scroll_to_first_match = TRUE;
 }
@@ -515,6 +528,8 @@ editor_search_bar_class_init (EditorSearchBarClass *klass)
                           (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  g_type_ensure (EDITOR_TYPE_SEARCH_ENTRY);
 }
 
 static void
@@ -611,11 +626,15 @@ editor_search_bar_notify_occurrences_count_cb (EditorSearchBar        *self,
                                                GParamSpec             *pspec,
                                                GtkSourceSearchContext *context)
 {
+  guint occurrence_count;
+
   g_assert (EDITOR_IS_SEARCH_BAR (self));
   g_assert (GTK_SOURCE_IS_SEARCH_CONTEXT (context));
 
-  if (self->scroll_to_first_match &&
-      gtk_source_search_context_get_occurrences_count (context) > 0)
+  occurrence_count = gtk_source_search_context_get_occurrences_count (context);
+  editor_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);
 
   update_properties (self);
diff --git a/src/editor-search-bar.ui b/src/editor-search-bar.ui
index 08ead7a..8f03f23 100644
--- a/src/editor-search-bar.ui
+++ b/src/editor-search-bar.ui
@@ -10,11 +10,8 @@
         <property name="column-spacing">8</property>
         <property name="row-spacing">8</property>
         <child>
-          <object class="GtkEntry" id="search_entry">
-            <property name="primary-icon-name">edit-find-symbolic</property>
-            <property name="max-width-chars">20</property>
+          <object class="EditorSearchEntry" id="search_entry">
             <property name="hexpand">true</property>
-            <property name="placeholder-text" translatable="yes">Search</property>
             <signal name="activate" handler="editor_search_bar_search_activate_cb" swapped="true" 
object="EditorSearchBar"/>
             <layout>
               <property name="row">0</property>
diff --git a/src/editor-search-entry-private.h b/src/editor-search-entry-private.h
new file mode 100644
index 0000000..cd2bf2c
--- /dev/null
+++ b/src/editor-search-entry-private.h
@@ -0,0 +1,37 @@
+/* editor-search-entry-private.h
+ *
+ * Copyright 2021 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 <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EDITOR_TYPE_SEARCH_ENTRY (editor_search_entry_get_type())
+
+G_DECLARE_FINAL_TYPE (EditorSearchEntry, editor_search_entry, EDITOR, SEARCH_ENTRY, GtkWidget)
+
+GtkWidget *editor_search_entry_new                     (void);
+void       editor_search_entry_set_occurrence_count    (EditorSearchEntry *self,
+                                                        guint              occurrence_count);
+void       editor_search_entry_set_occurrence_position (EditorSearchEntry *self,
+                                                        int                occurrence_position);
+
+G_END_DECLS
diff --git a/src/editor-search-entry.c b/src/editor-search-entry.c
new file mode 100644
index 0000000..d821de4
--- /dev/null
+++ b/src/editor-search-entry.c
@@ -0,0 +1,236 @@
+/* editor-search-entry.c
+ *
+ * Copyright 2021 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
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "editor-search-entry-private.h"
+
+struct _EditorSearchEntry
+{
+  GtkWidget  parent_instance;
+
+  GtkText   *text;
+  GtkLabel  *info;
+
+  guint      occurrence_count;
+  int        occurrence_position;
+};
+
+static void editable_iface_init (GtkEditableInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EditorSearchEntry, editor_search_entry, GTK_TYPE_WIDGET,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE, editable_iface_init))
+
+enum {
+  ACTIVATE,
+  N_SIGNALS
+};
+
+static guint signals[N_SIGNALS];
+
+GtkWidget *
+editor_search_entry_new (void)
+{
+  return g_object_new (EDITOR_TYPE_SEARCH_ENTRY, NULL);
+}
+
+static gboolean
+editor_search_entry_grab_focus (GtkWidget *widget)
+{
+  return gtk_widget_grab_focus (GTK_WIDGET (EDITOR_SEARCH_ENTRY (widget)->text));
+}
+
+static void
+on_text_activate_cb (EditorSearchEntry *self,
+                     GtkText           *text)
+{
+  g_assert (EDITOR_IS_SEARCH_ENTRY (self));
+  g_assert (GTK_IS_TEXT (text));
+
+  g_signal_emit (self, signals [ACTIVATE], 0);
+}
+
+static void
+on_text_notify_cb (EditorSearchEntry *self,
+                   GParamSpec        *pspec,
+                   GtkText           *text)
+{
+  GObjectClass *klass;
+
+  g_assert (EDITOR_IS_SEARCH_ENTRY (self));
+  g_assert (GTK_IS_TEXT (text));
+
+  klass = G_OBJECT_GET_CLASS (self);
+  pspec = g_object_class_find_property (klass, pspec->name);
+
+  if (pspec != NULL)
+    g_object_notify_by_pspec (G_OBJECT (self), pspec);
+}
+
+static void
+editor_search_entry_dispose (GObject *object)
+{
+  EditorSearchEntry *self = (EditorSearchEntry *)object;
+  GtkWidget *child;
+
+  self->text = NULL;
+  self->info = NULL;
+
+  while ((child = gtk_widget_get_first_child (GTK_WIDGET (self))))
+    gtk_widget_unparent (child);
+
+  G_OBJECT_CLASS (editor_search_entry_parent_class)->dispose (object);
+}
+
+static void
+editor_search_entry_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  if (gtk_editable_delegate_get_property (object, prop_id, value, pspec))
+    return;
+
+  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+editor_search_entry_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  if (gtk_editable_delegate_set_property (object, prop_id, value, pspec))
+    return;
+
+  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+editor_search_entry_class_init (EditorSearchEntryClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->dispose = editor_search_entry_dispose;
+  object_class->get_property = editor_search_entry_get_property;
+  object_class->set_property = editor_search_entry_set_property;
+
+  widget_class->grab_focus = editor_search_entry_grab_focus;
+
+  gtk_editable_install_properties (object_class, 1);
+
+  signals[ACTIVATE] =
+    g_signal_new ("activate",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL,
+                  NULL,
+                  G_TYPE_NONE, 0);
+
+  gtk_widget_class_set_activate_signal (widget_class, signals[ACTIVATE]);
+  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
+  gtk_widget_class_set_css_name (widget_class, "entry");
+  gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_TEXT_BOX);
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/TextEditor/ui/editor-search-entry.ui");
+  gtk_widget_class_bind_template_child (widget_class, EditorSearchEntry, info);
+  gtk_widget_class_bind_template_child (widget_class, EditorSearchEntry, text);
+  gtk_widget_class_bind_template_callback (widget_class, on_text_activate_cb);
+  gtk_widget_class_bind_template_callback (widget_class, on_text_notify_cb);
+}
+
+static void
+editor_search_entry_init (EditorSearchEntry *self)
+{
+  cairo_font_options_t *options;
+
+  self->occurrence_position = -1;
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  options = cairo_font_options_create ();
+  cairo_font_options_set_variations (options, "tnum");
+  gtk_widget_set_font_options (GTK_WIDGET (self->info), options);
+  cairo_font_options_destroy (options);
+}
+
+static GtkEditable *
+editor_search_entry_get_delegate (GtkEditable *editable)
+{
+  return GTK_EDITABLE (EDITOR_SEARCH_ENTRY (editable)->text);
+}
+
+static void
+editable_iface_init (GtkEditableInterface *iface)
+{
+  iface->get_delegate = editor_search_entry_get_delegate;
+}
+
+static void
+editor_search_entry_update_position (EditorSearchEntry *self)
+{
+  const char *text;
+
+  g_assert (EDITOR_IS_SEARCH_ENTRY (self));
+
+  text = gtk_editable_get_text (GTK_EDITABLE (self->text));
+
+  if (self->occurrence_count == 0)
+    {
+      gtk_label_set_label (self->info, NULL);
+    }
+  else
+    {
+      /* translators: the first %u is replaced with the current position, the second with the number of 
search results */
+      g_autofree char *str = g_strdup_printf (_("%u of %u"), MAX (0, self->occurrence_position), 
self->occurrence_count);
+      gtk_label_set_label (self->info, str);
+    }
+}
+
+void
+editor_search_entry_set_occurrence_count (EditorSearchEntry *self,
+                                          guint              occurrence_count)
+{
+  g_assert (EDITOR_IS_SEARCH_ENTRY (self));
+
+  if (self->occurrence_count != occurrence_count)
+    {
+      self->occurrence_count = occurrence_count;
+      editor_search_entry_update_position (self);
+    }
+}
+
+void
+editor_search_entry_set_occurrence_position (EditorSearchEntry *self,
+                                             int                occurrence_position)
+{
+  g_assert (EDITOR_IS_SEARCH_ENTRY (self));
+
+  occurrence_position = MAX (-1, occurrence_position);
+
+  if (self->occurrence_position != occurrence_position)
+    {
+      self->occurrence_position = occurrence_position;
+      editor_search_entry_update_position (self);
+    }
+}
diff --git a/src/editor-search-entry.ui b/src/editor-search-entry.ui
new file mode 100644
index 0000000..1ad300a
--- /dev/null
+++ b/src/editor-search-entry.ui
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="EditorSearchEntry" parent="GtkWidget">
+    <property name="width-request">225</property>
+    <child>
+      <object class="GtkImage">
+        <property name="icon-name">edit-find-symbolic</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkText" id="text">
+        <property name="hexpand">true</property>
+        <property name="vexpand">true</property>
+        <property name="width-chars">12</property>
+        <property name="max-width-chars">12</property>
+        <signal name="notify" handler="on_text_notify_cb" swapped="true"/>
+        <signal name="activate" handler="on_text_activate_cb" swapped="true"/>
+      </object>
+    </child>
+    <child>
+      <object class="GtkLabel" id="info">
+        <property name="xalign">1</property>
+        <attributes>
+          <attribute name="foreground-alpha" value="33000"/>
+        </attributes>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/meson.build b/src/meson.build
index 9959ecf..8cd3f8f 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -26,6 +26,7 @@ editor_sources = [
   'editor-print-operation.c',
   'editor-save-changes-dialog.c',
   'editor-search-bar.c',
+  'editor-search-entry.c',
   'editor-session.c',
   'editor-sidebar-item.c',
   'editor-sidebar-model.c',
diff --git a/src/org.gnome.TextEditor.gresource.xml b/src/org.gnome.TextEditor.gresource.xml
index 4c5240f..8974c37 100644
--- a/src/org.gnome.TextEditor.gresource.xml
+++ b/src/org.gnome.TextEditor.gresource.xml
@@ -8,6 +8,7 @@
     <file preprocess="xml-stripblanks">editor-page.ui</file>
     <file preprocess="xml-stripblanks">editor-position-label.ui</file>
     <file preprocess="xml-stripblanks">editor-search-bar.ui</file>
+    <file preprocess="xml-stripblanks">editor-search-entry.ui</file>
     <file preprocess="xml-stripblanks">editor-sidebar-row.ui</file>
     <file preprocess="xml-stripblanks">editor-sidebar.ui</file>
     <file preprocess="xml-stripblanks">editor-theme-selector.ui</file>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]