[tepl] GotoLineBar: implementation and docs



commit 60dd2c410428cc73f0eae12fc35e61bc4ace5cac
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Fri Apr 24 17:10:15 2020 +0200

    GotoLineBar: implementation and docs

 docs/reference/tepl-docs.xml     |   1 +
 docs/reference/tepl-sections.txt |  18 +++
 tepl/tepl-goto-line-bar.c        | 289 ++++++++++++++++++++++++++++++++++++++-
 tepl/tepl-goto-line-bar.h        |  14 +-
 4 files changed, 319 insertions(+), 3 deletions(-)
---
diff --git a/docs/reference/tepl-docs.xml b/docs/reference/tepl-docs.xml
index d036a73..b6e9678 100644
--- a/docs/reference/tepl-docs.xml
+++ b/docs/reference/tepl-docs.xml
@@ -63,6 +63,7 @@
 
     <chapter id="misc">
       <title>Misc</title>
+      <xi:include href="xml/goto-line-bar.xml"/>
       <xi:include href="xml/info-bar.xml"/>
       <xi:include href="xml/iter.xml"/>
       <xi:include href="xml/panel.xml"/>
diff --git a/docs/reference/tepl-sections.txt b/docs/reference/tepl-sections.txt
index 51117de..87d7bea 100644
--- a/docs/reference/tepl-sections.txt
+++ b/docs/reference/tepl-sections.txt
@@ -231,6 +231,24 @@ TEPL_TYPE_FOLD_REGION
 TeplFoldRegionClass
 </SECTION>
 
+<SECTION>
+<FILE>goto-line-bar</FILE>
+TeplGotoLineBar
+tepl_goto_line_bar_new
+tepl_goto_line_bar_set_view
+tepl_goto_line_bar_grab_focus_to_entry
+<SUBSECTION Standard>
+TEPL_GOTO_LINE_BAR
+TEPL_GOTO_LINE_BAR_CLASS
+TEPL_GOTO_LINE_BAR_GET_CLASS
+TEPL_IS_GOTO_LINE_BAR
+TEPL_IS_GOTO_LINE_BAR_CLASS
+TEPL_TYPE_GOTO_LINE_BAR
+TeplGotoLineBarClass
+TeplGotoLineBarPrivate
+tepl_goto_line_bar_get_type
+</SECTION>
+
 <SECTION>
 <FILE>gutter-renderer-folds</FILE>
 TeplGutterRendererFolds
diff --git a/tepl/tepl-goto-line-bar.c b/tepl/tepl-goto-line-bar.c
index acaa680..1af4d0e 100644
--- a/tepl/tepl-goto-line-bar.c
+++ b/tepl/tepl-goto-line-bar.c
@@ -17,11 +17,32 @@
  * along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "config.h"
 #include "tepl-goto-line-bar.h"
+#include <glib/gi18n-lib.h>
+#include "tepl-utils.h"
+
+/**
+ * SECTION:goto-line-bar
+ * @Title: TeplGotoLineBar
+ * @Short_description: Horizontal bar for the 'Go to line' feature
+ *
+ * #TeplGotoLineBar is an horizontal bar containing among other things:
+ * - A #GtkSearchEntry.
+ * - A close button.
+ *
+ * When the #GtkSearchEntry's content changes, tepl_view_goto_line() is called
+ * on the associated #TeplView.
+ */
 
 struct _TeplGotoLineBarPrivate
 {
-       gint something;
+       GtkEntry *entry;
+
+       /* Owns a strong ref. */
+       TeplView *view;
+
+       guint bound_to_gaction_state : 1;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (TeplGotoLineBar, tepl_goto_line_bar, GTK_TYPE_GRID)
@@ -29,6 +50,10 @@ G_DEFINE_TYPE_WITH_PRIVATE (TeplGotoLineBar, tepl_goto_line_bar, GTK_TYPE_GRID)
 static void
 tepl_goto_line_bar_dispose (GObject *object)
 {
+       TeplGotoLineBar *bar = TEPL_GOTO_LINE_BAR (object);
+
+       bar->priv->entry = NULL;
+       g_clear_object (&bar->priv->view);
 
        G_OBJECT_CLASS (tepl_goto_line_bar_parent_class)->dispose (object);
 }
@@ -41,14 +66,276 @@ tepl_goto_line_bar_class_init (TeplGotoLineBarClass *klass)
        object_class->dispose = tepl_goto_line_bar_dispose;
 }
 
+static void
+set_success (GtkEntry *entry,
+            gboolean  success)
+{
+       GtkStyleContext *style_context;
+
+       style_context = gtk_widget_get_style_context (GTK_WIDGET (entry));
+
+       if (success)
+       {
+               gtk_style_context_remove_class (style_context, GTK_STYLE_CLASS_ERROR);
+       }
+       else
+       {
+               gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_ERROR);
+       }
+}
+
+static void
+entry_search_changed_cb (GtkEntry        *entry,
+                        TeplGotoLineBar *bar)
+{
+       const gchar *entry_text;
+       gint64 line = 0;
+       gboolean success = FALSE;
+
+       if (bar->priv->view == NULL)
+       {
+               return;
+       }
+
+       entry_text = gtk_entry_get_text (entry);
+
+       if (entry_text == NULL || entry_text[0] == '\0')
+       {
+               set_success (entry, TRUE);
+               return;
+       }
+
+       if (g_ascii_string_to_signed (entry_text,
+                                     10,
+                                     0, G_MAXINT,
+                                     &line,
+                                     NULL))
+       {
+               /* When typing "0" in the search entry, treat it the same as 1. */
+               success = tepl_view_goto_line (bar->priv->view, MAX (line - 1, 0));
+       }
+
+       set_success (entry, success);
+}
+
+static void
+entry_activate_cb (GtkEntry        *entry,
+                  TeplGotoLineBar *bar)
+{
+       gtk_widget_hide (GTK_WIDGET (bar));
+}
+
+static void
+create_entry (TeplGotoLineBar *bar)
+{
+       g_assert (bar->priv->entry == NULL);
+
+       bar->priv->entry = GTK_ENTRY (gtk_search_entry_new ());
+       gtk_widget_show (GTK_WIDGET (bar->priv->entry));
+       gtk_grid_attach (GTK_GRID (bar), GTK_WIDGET (bar->priv->entry), 1, 0, 1, 1);
+
+       g_signal_connect (bar->priv->entry,
+                         "search-changed",
+                         G_CALLBACK (entry_search_changed_cb),
+                         bar);
+
+       g_signal_connect (bar->priv->entry,
+                         "activate",
+                         G_CALLBACK (entry_activate_cb),
+                         bar);
+}
+
+static void
+close_button_clicked_cb (GtkButton       *close_button,
+                        TeplGotoLineBar *bar)
+{
+       gtk_widget_hide (GTK_WIDGET (bar));
+}
+
+static void
+bar_hide_cb (TeplGotoLineBar *bar,
+            gpointer         user_data)
+{
+       /* A previous implementation for this callback was:
+        * gtk_entry_set_text (bar->priv->entry, "");
+        *
+        * But there was the following situation that was not working well wrt
+        * the styling applied to the GtkEntry:
+        *
+        * GtkEntry in error state (see set_success()) -> hide bar -> set entry
+        * text to "" -> the entry state is set to success but the bar is
+        * immediately hidden -> then some time later the bar is shown again.
+        * --> the error state (in red) is still visible during a short period
+        *  of time when the bar is shown again. The time that it transitions to
+        *  the normal state (the colors fade progressively).
+        *
+        * I haven't found a GtkStyleContext API to fix that problem (for
+        * example to apply the final state immediately).
+        *
+        * So the easiest is to destroy and re-create the widget. That way when
+        * it will be shown again, we are sure that it will have the initial
+        * style.
+        */
+       if (bar->priv->entry != NULL)
+       {
+               gtk_widget_destroy (GTK_WIDGET (bar->priv->entry));
+               bar->priv->entry = NULL;
+       }
+
+       create_entry (bar);
+}
+
 static void
 tepl_goto_line_bar_init (TeplGotoLineBar *bar)
 {
+       GtkWidget *label;
+       GtkWidget *close_button;
+
        bar->priv = tepl_goto_line_bar_get_instance_private (bar);
+
+       gtk_grid_set_column_spacing (GTK_GRID (bar), 6);
+       gtk_widget_set_margin_start (GTK_WIDGET (bar), 6);
+       gtk_widget_set_margin_end (GTK_WIDGET (bar), 4);
+       gtk_widget_set_margin_top (GTK_WIDGET (bar), 3);
+       gtk_widget_set_margin_bottom (GTK_WIDGET (bar), 3);
+
+       label = gtk_label_new (_("Go to line:"));
+       gtk_widget_show (label);
+       gtk_grid_attach (GTK_GRID (bar), label, 0, 0, 1, 1);
+
+       create_entry (bar);
+
+       close_button = tepl_utils_create_close_button ();
+       gtk_widget_show (close_button);
+       gtk_widget_set_tooltip_text (close_button, _("Close"));
+       gtk_widget_set_hexpand (close_button, TRUE);
+       gtk_widget_set_halign (close_button, GTK_ALIGN_END);
+       gtk_grid_attach (GTK_GRID (bar), close_button, 2, 0, 1, 1);
+
+       g_signal_connect (close_button,
+                         "clicked",
+                         G_CALLBACK (close_button_clicked_cb),
+                         bar);
+
+       g_signal_connect (bar,
+                         "hide",
+                         G_CALLBACK (bar_hide_cb),
+                         NULL);
 }
 
+/**
+ * tepl_goto_line_bar_new:
+ *
+ * Returns: (transfer floating): a new #TeplGotoLineBar widget.
+ * Since: 5.0
+ */
 TeplGotoLineBar *
 tepl_goto_line_bar_new (void)
 {
        return g_object_new (TEPL_TYPE_GOTO_LINE_BAR, NULL);
 }
+
+/**
+ * tepl_goto_line_bar_set_view:
+ * @bar: a #TeplGotoLineBar.
+ * @view: a #TeplView.
+ *
+ * Sets the #TeplView. tepl_view_goto_line() will be called on @view when the
+ * user types a line number in the #GtkSearchEntry of @bar.
+ *
+ * Only one #TeplView can be associated per #TeplGotoLineBar.
+ *
+ * Since: 5.0
+ */
+void
+tepl_goto_line_bar_set_view (TeplGotoLineBar *bar,
+                            TeplView        *view)
+{
+       g_return_if_fail (TEPL_IS_GOTO_LINE_BAR (bar));
+       g_return_if_fail (view == NULL || TEPL_IS_VIEW (view));
+
+       if (bar->priv->view != view)
+       {
+               g_clear_object (&bar->priv->view);
+               bar->priv->view = g_object_ref_sink (view);
+       }
+}
+
+/**
+ * tepl_goto_line_bar_grab_focus_to_entry:
+ * @bar: a #TeplGotoLineBar.
+ *
+ * Calls gtk_widget_grab_focus() to the #GtkSearchEntry of @bar.
+ *
+ * Since: 5.0
+ */
+void
+tepl_goto_line_bar_grab_focus_to_entry (TeplGotoLineBar *bar)
+{
+       g_return_if_fail (TEPL_IS_GOTO_LINE_BAR (bar));
+
+       gtk_widget_grab_focus (GTK_WIDGET (bar->priv->entry));
+}
+
+static gboolean
+binding_transform_smart_bool (GBinding     *binding,
+                             const GValue *from_value,
+                             GValue       *to_value,
+                             gpointer      user_data)
+{
+       if (G_VALUE_TYPE (from_value) == G_TYPE_BOOLEAN &&
+           G_VALUE_TYPE (to_value) == G_TYPE_VARIANT)
+       {
+               gboolean bool_value;
+
+               bool_value = g_value_get_boolean (from_value);
+               g_value_set_variant (to_value, g_variant_new_boolean (bool_value));
+
+               return TRUE;
+       }
+       else if (G_VALUE_TYPE (from_value) == G_TYPE_VARIANT &&
+                G_VALUE_TYPE (to_value) == G_TYPE_BOOLEAN)
+       {
+               GVariant *variant_value;
+               gboolean bool_value;
+
+               variant_value = g_value_get_variant (from_value);
+               if (variant_value == NULL)
+               {
+                       return FALSE;
+               }
+
+               if (!g_variant_type_equal (g_variant_get_type (variant_value), G_VARIANT_TYPE_BOOLEAN))
+               {
+                       return FALSE;
+               }
+
+               bool_value = g_variant_get_boolean (variant_value);
+               g_value_set_boolean (to_value, bool_value);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+void
+_tepl_goto_line_bar_bind_to_gaction_state (TeplGotoLineBar *bar,
+                                          GAction         *action)
+{
+       g_return_if_fail (TEPL_IS_GOTO_LINE_BAR (bar));
+       g_return_if_fail (G_IS_ACTION (action));
+
+       if (!bar->priv->bound_to_gaction_state)
+       {
+               g_object_bind_property_full (action, "state",
+                                            bar, "visible",
+                                            G_BINDING_BIDIRECTIONAL |
+                                            G_BINDING_SYNC_CREATE,
+                                            binding_transform_smart_bool,
+                                            binding_transform_smart_bool,
+                                            NULL, NULL);
+
+               bar->priv->bound_to_gaction_state = TRUE;
+       }
+}
diff --git a/tepl/tepl-goto-line-bar.h b/tepl/tepl-goto-line-bar.h
index 5644363..5330e80 100644
--- a/tepl/tepl-goto-line-bar.h
+++ b/tepl/tepl-goto-line-bar.h
@@ -25,6 +25,7 @@
 #endif
 
 #include <gtk/gtk.h>
+#include <tepl/tepl-view.h>
 
 G_BEGIN_DECLS
 
@@ -53,9 +54,18 @@ struct _TeplGotoLineBarClass
        gpointer padding[12];
 };
 
-GType                  tepl_goto_line_bar_get_type     (void);
+GType                  tepl_goto_line_bar_get_type                     (void);
 
-TeplGotoLineBar *      tepl_goto_line_bar_new          (void);
+TeplGotoLineBar *      tepl_goto_line_bar_new                          (void);
+
+void                   tepl_goto_line_bar_set_view                     (TeplGotoLineBar *bar,
+                                                                        TeplView        *view);
+
+void                   tepl_goto_line_bar_grab_focus_to_entry          (TeplGotoLineBar *bar);
+
+G_GNUC_INTERNAL
+void                   _tepl_goto_line_bar_bind_to_gaction_state       (TeplGotoLineBar *bar,
+                                                                        GAction         *action);
 
 G_END_DECLS
 


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