[gnome-builder/wip/gtk4-port: 1334/1774] libide/webkit: various style and function improvements




commit 7f9ee15ba81a81f5142e4304d5e39843f8c416ec
Author: Christian Hergert <chergert redhat com>
Date:   Fri Jun 3 14:29:07 2022 -0700

    libide/webkit: various style and function improvements
    
     * Text cursor when hovering the display label
     * Track loading and progress changes with .osd style progress bar
     * Animate progress when possible
     * Try to somewhat make clicking on the url entry feel a bit nicer.
       Unfortunately we cannot duplicate GtkText click to focus word behavior
       as the internals are just too complex to duplicate. Although I'd really
       like to be able to have 1,2,3 clicks do 1) full selection 2) word
       selection 3) full selection again so that most use cases feel natural.

 src/libide/webkit/ide-url-bar.c  | 177 +++++++++++++++++++++++++++++++++++----
 src/libide/webkit/ide-url-bar.ui | 106 +++++++++++++++--------
 2 files changed, 230 insertions(+), 53 deletions(-)
---
diff --git a/src/libide/webkit/ide-url-bar.c b/src/libide/webkit/ide-url-bar.c
index a10a2336c..ed1734e38 100644
--- a/src/libide/webkit/ide-url-bar.c
+++ b/src/libide/webkit/ide-url-bar.c
@@ -20,7 +20,13 @@
 
 #define G_LOG_DOMAIN "ide-url-bar"
 
+#include "config.h"
+
+#include <libide-io.h>
+#include <libide-gtk.h>
+
 #include "ide-url-bar.h"
+#include "ide-webkit-util.h"
 
 struct _IdeUrlBar
 {
@@ -29,11 +35,17 @@ struct _IdeUrlBar
   /* Owned references */
   WebKitWebView *web_view;
   GBindingGroup *web_view_bindings;
+  GSignalGroup  *web_view_signals;
+
+  /* Weak References */
+  IdeAnimation   *animation;
 
   /* Template references */
-  GtkStack      *stack;
-  GtkLabel      *url_display;
-  GtkText       *url_editable;
+  GtkOverlay     *overlay;
+  GtkStack       *stack;
+  GtkLabel       *url_display;
+  GtkText        *url_editable;
+  GtkProgressBar *load_progress;
 };
 
 enum {
@@ -50,17 +62,9 @@ static void
 on_editable_focus_enter_cb (IdeUrlBar               *self,
                             GtkEventControllerFocus *focus)
 {
-  const char *uri;
-
   g_assert (IDE_IS_URL_BAR (self));
   g_assert (GTK_IS_EVENT_CONTROLLER_FOCUS (focus));
 
-  if (self->web_view == NULL)
-    return;
-
-  uri = webkit_web_view_get_uri (self->web_view);
-  gtk_editable_set_text (GTK_EDITABLE (self->url_editable), uri);
-  gtk_editable_select_region (GTK_EDITABLE (self->url_editable), 0, -1);
 }
 
 static void
@@ -70,21 +74,34 @@ on_editable_focus_leave_cb (IdeUrlBar               *self,
   g_assert (IDE_IS_URL_BAR (self));
   g_assert (GTK_IS_EVENT_CONTROLLER_FOCUS (focus));
 
-  gtk_stack_set_visible_child_name (self->stack, "display");
 }
 
 static void
 on_editable_activate_cb (IdeUrlBar   *self,
                          GtkEditable *editable)
 {
+  g_autofree char *expanded = NULL;
+  g_autofree char *normalized = NULL;
+  const char *uri;
+
   g_assert (IDE_IS_URL_BAR (self));
   g_assert (GTK_IS_EDITABLE (editable));
 
   if (self->web_view == NULL)
     return;
 
-  webkit_web_view_load_uri (self->web_view,
-                            gtk_editable_get_text (editable));
+  uri = gtk_editable_get_text (editable);
+  if (uri == NULL || uri[0] == 0)
+    return;
+
+  /* Expand ~/ access to home directory first */
+  if (g_str_has_prefix (uri, "~/"))
+    uri = expanded = ide_path_expand (uri);
+
+  normalized = ide_webkit_util_normalize_address (uri);
+
+  webkit_web_view_load_uri (self->web_view, normalized);
+  gtk_stack_set_visible_child_name (self->stack, "display");
   gtk_widget_grab_focus (GTK_WIDGET (self->web_view));
 }
 
@@ -104,12 +121,101 @@ on_click_gesture_pressed_cb (IdeUrlBar       *self,
     return;
 
   name = gtk_stack_get_visible_child_name (self->stack);
-  if (g_strcmp0 (name, "edit") == 0)
-    return;
 
-  gtk_gesture_set_state (GTK_GESTURE (click), GTK_EVENT_SEQUENCE_CLAIMED);
+  /* On first click, just change to the text field immediately so that
+   * we can propagate the event to that widget instead of the label.
+   */
+  if (n_presses == 1)
+    {
+      if (g_strcmp0 (name, "edit") != 0)
+        {
+          const char *uri = webkit_web_view_get_uri (self->web_view);
+
+          gtk_editable_set_text (GTK_EDITABLE (self->url_editable), uri ? uri : "");
+          gtk_stack_set_visible_child_name (self->stack, "edit");
+          gtk_widget_grab_focus (GTK_WIDGET (self->url_editable));
+          gtk_editable_select_region (GTK_EDITABLE (self->url_editable), 0, -1);
+          gtk_gesture_set_state (GTK_GESTURE (click), GTK_EVENT_SEQUENCE_CLAIMED);
+          return;
+        }
+    }
+
+  gtk_gesture_set_state (GTK_GESTURE (click), GTK_EVENT_SEQUENCE_DENIED);
+}
+
+static void
+on_web_view_notify_is_loading_cb (IdeUrlBar     *self,
+                                  GParamSpec    *pspec,
+                                  WebKitWebView *web_view)
+{
+  g_assert (IDE_IS_URL_BAR (self));
+  g_assert (WEBKIT_IS_WEB_VIEW (web_view));
+
+  if (webkit_web_view_is_loading (web_view))
+    {
+      gtk_progress_bar_set_fraction (self->load_progress, 0);
+      gtk_widget_show (GTK_WIDGET (self->load_progress));
+    }
+  else
+    {
+      ide_gtk_widget_hide_with_fade (GTK_WIDGET (self->load_progress));
+    }
+}
+
+static void
+on_web_view_notify_estimated_load_progress_cb (IdeUrlBar     *self,
+                                               GParamSpec    *pspec,
+                                               WebKitWebView *web_view)
+{
+  IdeAnimation *anim;
+  double progress;
+
+  g_assert (IDE_IS_URL_BAR (self));
+  g_assert (WEBKIT_IS_WEB_VIEW (web_view));
+
+  progress = webkit_web_view_get_estimated_load_progress (web_view);
+
+  /* First cancel any previous animation */
+  if ((anim = self->animation))
+    {
+      g_clear_weak_pointer (&self->animation);
+      ide_animation_stop (anim);
+    }
+
+  /* Short-circuit if we're not actively loading or we are jumping
+   * backwards in progress instead of forwards.
+   */
+  if (!webkit_web_view_is_loading (web_view) ||
+      progress < gtk_progress_bar_get_fraction (self->load_progress))
+    {
+      gtk_progress_bar_set_fraction (self->load_progress, progress);
+      return;
+    }
+
+  anim = ide_object_animate (self->load_progress,
+                             IDE_ANIMATION_LINEAR,
+                             200,
+                             NULL,
+                             "fraction", progress,
+                             NULL);
+  g_set_weak_pointer (&self->animation, anim);
+}
+
+static gboolean
+ide_url_bar_grab_focus (GtkWidget *widget)
+{
+  IdeUrlBar *self = (IdeUrlBar *)widget;
+
+  g_assert (IDE_IS_URL_BAR (self));
+
+  if (self->web_view == NULL)
+    return FALSE;
+
   gtk_stack_set_visible_child_name (self->stack, "edit");
   gtk_widget_grab_focus (GTK_WIDGET (self->url_editable));
+  gtk_editable_select_region (GTK_EDITABLE (self->url_editable), 0, -1);
+
+  return TRUE;
 }
 
 static void
@@ -118,9 +224,10 @@ ide_url_bar_dispose (GObject *object)
   IdeUrlBar *self = (IdeUrlBar *)object;
 
   g_clear_object (&self->web_view_bindings);
+  g_clear_object (&self->web_view_signals);
   g_clear_object (&self->web_view);
 
-  g_clear_pointer ((GtkWidget **)&self->stack, gtk_widget_unparent);
+  g_clear_pointer ((GtkWidget **)&self->overlay, gtk_widget_unparent);
 
   G_OBJECT_CLASS (ide_url_bar_parent_class)->dispose (object);
 }
@@ -173,6 +280,8 @@ ide_url_bar_class_init (IdeUrlBarClass *klass)
   object_class->get_property = ide_url_bar_get_property;
   object_class->set_property = ide_url_bar_set_property;
 
+  widget_class->grab_focus = ide_url_bar_grab_focus;
+
   properties [PROP_WEB_VIEW] =
     g_param_spec_object ("web-view", NULL, NULL,
                          WEBKIT_TYPE_WEB_VIEW,
@@ -185,6 +294,8 @@ ide_url_bar_class_init (IdeUrlBarClass *klass)
   gtk_widget_class_set_template_from_resource (widget_class, "/plugins/webkit/ide-url-bar.ui");
   gtk_widget_class_bind_template_child (widget_class, IdeUrlBar, url_display);
   gtk_widget_class_bind_template_child (widget_class, IdeUrlBar, url_editable);
+  gtk_widget_class_bind_template_child (widget_class, IdeUrlBar, load_progress);
+  gtk_widget_class_bind_template_child (widget_class, IdeUrlBar, overlay);
   gtk_widget_class_bind_template_child (widget_class, IdeUrlBar, stack);
   gtk_widget_class_bind_template_callback (widget_class, on_click_gesture_pressed_cb);
   gtk_widget_class_bind_template_callback (widget_class, on_editable_focus_enter_cb);
@@ -197,10 +308,24 @@ ide_url_bar_init (IdeUrlBar *self)
 {
   gtk_widget_init_template (GTK_WIDGET (self));
 
+  self->web_view_signals = g_signal_group_new (WEBKIT_TYPE_WEB_VIEW);
+  g_signal_group_connect_object (self->web_view_signals,
+                                 "notify::estimated-load-progress",
+                                 G_CALLBACK (on_web_view_notify_estimated_load_progress_cb),
+                                 self,
+                                 G_CONNECT_SWAPPED);
+  g_signal_group_connect_object (self->web_view_signals,
+                                 "notify::is-loading",
+                                 G_CALLBACK (on_web_view_notify_is_loading_cb),
+                                 self,
+                                 G_CONNECT_SWAPPED);
+
   self->web_view_bindings = g_binding_group_new ();
   g_binding_group_bind (self->web_view_bindings, "uri",
                         self->url_display, "label",
                         G_BINDING_SYNC_CREATE);
+
+  gtk_widget_set_cursor_from_name (GTK_WIDGET (self->url_display), "text");
 }
 
 WebKitWebView *
@@ -221,7 +346,23 @@ ide_url_bar_set_web_view (IdeUrlBar     *self,
   if (g_set_object (&self->web_view, web_view))
     {
       g_binding_group_set_source (self->web_view_bindings, web_view);
+      g_signal_group_set_target (self->web_view_signals, web_view);
+
+      gtk_widget_hide (GTK_WIDGET (self->load_progress));
       gtk_widget_set_can_focus (GTK_WIDGET (self), web_view != NULL);
+
+      if (self->web_view != NULL)
+        {
+          const char *uri = webkit_web_view_get_uri (self->web_view);
+
+          gtk_editable_set_text (GTK_EDITABLE (self->url_editable), uri ? uri : "");
+
+          if (gtk_widget_has_focus (GTK_WIDGET (self->url_editable)))
+            gtk_editable_select_region (GTK_EDITABLE (self->url_editable), 0, -1);
+
+          on_web_view_notify_estimated_load_progress_cb (self, NULL, self->web_view);
+        }
+
       g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_WEB_VIEW]);
     }
 }
diff --git a/src/libide/webkit/ide-url-bar.ui b/src/libide/webkit/ide-url-bar.ui
index 7a4e0984a..1b5538c39 100644
--- a/src/libide/webkit/ide-url-bar.ui
+++ b/src/libide/webkit/ide-url-bar.ui
@@ -8,52 +8,88 @@
       </object>
     </child>
     <child>
-      <object class="GtkStack" id="stack">
-        <property name="hexpand">true</property>
-        <child>
-          <object class="GtkStackPage">
-            <property name="name">display</property>
-            <property name="child">
-              <object class="GtkBox">
-                <child>
-                  <object class="GtkImage">
-                    <property name="margin-start">6</property>
-                    <property name="margin-end">6</property>
-                    <property name="icon-name">lock-small-open-symbolic</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="url_display">
-                    <property name="hexpand">true</property>
-                    <property name="xalign">0</property>
-                  </object>
-                </child>
-              </object>
-            </property>
+      <object class="GtkOverlay" id="overlay">
+        <child type="overlay">
+          <object class="GtkProgressBar" id="load_progress">
+            <property name="hexpand">true</property>
+            <property name="valign">end</property>
+            <property name="margin-start">6</property>
+            <property name="margin-end">6</property>
+            <property name="visible">false</property>
+            <style>
+              <class name="osd"/>
+            </style>
           </object>
         </child>
         <child>
-          <object class="GtkStackPage">
-            <property name="name">edit</property>
-            <property name="child">
-              <object class="GtkBox">
-                <child>
-                  <object class="GtkText" id="url_editable">
-                    <property name="hexpand">true</property>
-                    <signal name="activate" handler="on_editable_activate_cb" swapped="true" 
object="IdeUrlBar"/>
+          <object class="GtkStack" id="stack">
+            <property name="hexpand">true</property>
+            <property name="transition-type">none</property>
+            <property name="hhomogeneous">true</property>
+            <child>
+              <object class="GtkStackPage">
+                <property name="name">display</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <child>
+                      <object class="GtkBox" id="display_controls">
+                        <property name="margin-start">6</property>
+                        <property name="margin-end">6</property>
+                        <property name="spacing">3</property>
+                        <child>
+                          <object class="GtkImage">
+                            <property name="icon-name">lock-small-open-symbolic</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="url_display">
+                        <property name="hexpand">true</property>
+                        <property name="xalign">0</property>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkStackPage">
+                <property name="name">edit</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <child>
+                      <object class="GtkBox" id="edit_controls">
+                      </object>
+                    </child>
                     <child>
-                      <object class="GtkEventControllerFocus">
-                        <signal name="enter" handler="on_editable_focus_enter_cb" swapped="true" 
object="IdeUrlBar"/>
-                        <signal name="leave" handler="on_editable_focus_leave_cb" swapped="true" 
object="IdeUrlBar"/>
+                      <object class="GtkText" id="url_editable">
+                        <property name="hexpand">true</property>
+                        <property name="enable-emoji-completion">false</property>
+                        <property name="input-purpose">url</property>
+                        <signal name="activate" handler="on_editable_activate_cb" swapped="true" 
object="IdeUrlBar"/>
+                        <child>
+                          <object class="GtkEventControllerFocus">
+                            <signal name="enter" handler="on_editable_focus_enter_cb" swapped="true" 
object="IdeUrlBar"/>
+                            <signal name="leave" handler="on_editable_focus_leave_cb" swapped="true" 
object="IdeUrlBar"/>
+                          </object>
+                        </child>
                       </object>
                     </child>
                   </object>
-                </child>
+                </property>
               </object>
-            </property>
+            </child>
           </object>
         </child>
       </object>
     </child>
   </template>
+  <object class="GtkSizeGroup">
+    <property name="mode">horizontal</property>
+    <widgets>
+      <widget name="edit_controls"/>
+      <widget name="display_controls"/>
+    </widgets>
+  </object>
 </interface>


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