[gnome-documents] searchbar: deliver overview keyboard events to the entry



commit fa71119a4b81d0005dff073a42ddb7e15d42f706
Author: Cosimo Cecchi <cosimoc gnome org>
Date:   Thu Oct 27 13:13:32 2011 -0400

    searchbar: deliver overview keyboard events to the entry
    
    When the search entry is not shown and something is typed into the
    window, we want to drop in the search bar automatically.
    Borrow code from the typeahead-find entry GtkTreeView implementation to
    handle event delivery and entry focus.

 src/lib/gd-utils.c |   63 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/lib/gd-utils.h |    2 +
 src/mainWindow.js  |   23 +++++++++++-------
 src/searchbar.js   |   54 ++++++++++++++++++++++++++++++++++++++++++--
 src/utils.js       |   14 +++++++++++
 src/windowMode.js  |   16 +++++++++++-
 6 files changed, 158 insertions(+), 14 deletions(-)
---
diff --git a/src/lib/gd-utils.c b/src/lib/gd-utils.c
index 6dc4073..a0aaba1 100644
--- a/src/lib/gd-utils.c
+++ b/src/lib/gd-utils.c
@@ -525,3 +525,66 @@ gd_create_symbolic_icon (const gchar *name,
 
   return retval;
 }
+
+/* taken from gtk/gtktreeview.c */
+static void
+send_focus_change (GtkWidget *widget,
+                   GdkDevice *device,
+		   gboolean   in)
+{
+  GdkDeviceManager *device_manager;
+  GList *devices, *d;
+
+  device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
+  devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+  devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
+  devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_FLOATING));
+
+  for (d = devices; d; d = d->next)
+    {
+      GdkDevice *dev = d->data;
+      GdkEvent *fevent;
+      GdkWindow *window;
+
+      if (gdk_device_get_source (dev) != GDK_SOURCE_KEYBOARD)
+        continue;
+
+      window = gtk_widget_get_window (widget);
+
+      /* Skip non-master keyboards that haven't
+       * selected for events from this window
+       */
+      if (gdk_device_get_device_type (dev) != GDK_DEVICE_TYPE_MASTER &&
+          !gdk_window_get_device_events (window, dev))
+        continue;
+
+      fevent = gdk_event_new (GDK_FOCUS_CHANGE);
+
+      fevent->focus_change.type = GDK_FOCUS_CHANGE;
+      fevent->focus_change.window = g_object_ref (window);
+      fevent->focus_change.in = in;
+      gdk_event_set_device (fevent, device);
+
+      gtk_widget_send_focus_change (widget, fevent);
+
+      gdk_event_free (fevent);
+    }
+
+  g_list_free (devices);
+}
+
+void
+gd_entry_focus_hack (GtkWidget *entry,
+                     GdkDevice *device)
+{
+  GtkWidgetClass *entry_parent_class;
+
+  /* Grab focus will select all the text.  We don't want that to happen, so we
+   * call the parent instance and bypass the selection change.  This is probably
+   * really non-kosher. */
+  entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (entry));
+  (entry_parent_class->grab_focus) (entry);
+
+  /* send focus-in event */
+  send_focus_change (entry, device, TRUE);
+}
diff --git a/src/lib/gd-utils.h b/src/lib/gd-utils.h
index 5eaecd8..ba11267 100644
--- a/src/lib/gd-utils.h
+++ b/src/lib/gd-utils.h
@@ -72,6 +72,8 @@ gboolean gd_time_val_from_iso8601 (const gchar *string,
 
 GIcon *gd_create_symbolic_icon (const gchar *name,
                                 gint base_size);
+void   gd_entry_focus_hack (GtkWidget *entry,
+                            GdkDevice *device);
 
 #endif /* __GD_UTILS_H__ */
                                   
diff --git a/src/mainWindow.js b/src/mainWindow.js
index 85a7047..9b53cd3 100644
--- a/src/mainWindow.js
+++ b/src/mainWindow.js
@@ -32,6 +32,7 @@ const Embed = imports.embed;
 const Global = imports.global;
 const Searchbar = imports.searchbar;
 const Sidebar = imports.sidebar;
+const Utils = imports.utils;
 const WindowMode = imports.windowMode;
 
 const _ = imports.gettext.gettext;
@@ -184,12 +185,14 @@ MainWindow.prototype = {
         }
 
         if (Global.modeController.getWindowMode() == WindowMode.WindowMode.PREVIEW)
-            return this._handleKeyPreview(keyval, state);
+            return this._handleKeyPreview(event);
         else
-            return this._handleKeyOverview(keyval, state);
+            return this._handleKeyOverview(event);
     },
 
-    _handleKeyPreview: function(keyval, state) {
+    _handleKeyPreview: function(event) {
+        let keyval = event.get_keyval()[1];
+        let state = event.get_state()[1];
         let fullscreen = Global.modeController.getFullscreen();
 
         if (keyval == Gdk.KEY_f) {
@@ -211,12 +214,14 @@ MainWindow.prototype = {
         return false;
     },
 
-    _handleKeyOverview: function(keyval, state) {
-        if (((keyval == Gdk.KEY_f) &&
-            ((state & Gdk.ModifierType.CONTROL_MASK) != 0)) ||
-            ((keyval == Gdk.KEY_s) &&
-            ((state & Gdk.ModifierType.CONTROL_MASK) != 0))) {
-            Global.focusController.requestSearch();
+    _handleKeyOverview: function(event) {
+        if (Utils.isSearchEvent(event)) {
+            Global.focusController.toggleSearch();
+            return true;
+        }
+
+        if (!Global.focusController.getSearchVisible()) {
+            Global.focusController.deliverEvent(event);
             return true;
         }
 
diff --git a/src/searchbar.js b/src/searchbar.js
index 2c69b2d..fe97951 100644
--- a/src/searchbar.js
+++ b/src/searchbar.js
@@ -29,6 +29,7 @@ const Mainloop = imports.mainloop;
 
 const Global = imports.global;
 const Tweener = imports.util.tweener;
+const Utils = imports.utils;
 
 const _SEARCH_ENTRY_TIMEOUT = 200;
 
@@ -38,8 +39,10 @@ function Searchbar() {
 
 Searchbar.prototype = {
     _init: function() {
+        this._searchEventId = 0;
         this._searchFocusId = 0;
         this._searchEntryTimeout = 0;
+        this._searchFadingIn = false;
 
         this.widget = new Gtk.Toolbar();
         this.widget.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR);
@@ -104,7 +107,9 @@ Searchbar.prototype = {
         }));
 
         this._searchFocusId =
-            Global.focusController.connect('focus-search', Lang.bind(this, this._moveIn));
+            Global.focusController.connect('toggle-search', Lang.bind(this, this._onToggleSearch));
+        this._searchEventId =
+            Global.focusController.connect('deliver-event', Lang.bind(this, this._onDeliverEvent));
 
         this.widget.insert(item, 0);
         this._searchEntry.set_text(Global.searchFilterController.getFilter());
@@ -117,20 +122,63 @@ Searchbar.prototype = {
             Global.focusController.disconnect(this._searchFocusId);
             this._searchFocusId = 0;
         }
+
+        if (this._searchEventId != 0) {
+            Global.focusController.disconnect(this._searchEventId);
+            this._searchEventId = 0;
+        }
+
+        this.widget.destroy();
+    },
+
+    _onToggleSearch: function() {
+        if (Global.focusController.getSearchVisible())
+            this._moveOut();
+        else
+            this._moveIn(Gtk.get_current_event_device());
     },
 
-    _moveIn: function() {
+    _onDeliverEvent: function(controller, event) {
+        if (!this._searchEntry.get_realized())
+            this._searchEntry.realize();
+
+        let preeditChanged = false;
+        let preeditChangedId =
+            this._searchEntry.connect('preedit-changed', Lang.bind(this,
+                function() {
+                    preeditChanged = true;
+                }));
+
+        let oldText = this._searchEntry.get_text();
+        let res = this._searchEntry.event(event);
+        let newText = this._searchEntry.get_text();
+
+        this._searchEntry.disconnect(preeditChangedId);
+
+        if (((res && (newText != oldText)) || preeditChanged) &&
+            !this._searchFadingIn) {
+            this._moveIn(event.get_device());
+        }
+    },
+
+    _moveIn: function(eventDevice) {
         this._searchEntry.show();
+        this._searchFadingIn = true;
+
         Tweener.addTween(this.actor, { height: this.widget.get_preferred_height()[1],
                                        time: 0.20,
                                        transition: 'easeOutQuad',
                                        onComplete: function() {
-                                           this._searchEntry.grab_focus();
+                                           this._searchFadingIn = false;
+                                           Global.focusController.setSearchVisible(true);
+
+                                           Gd.entry_focus_hack(this._searchEntry, eventDevice);
                                        },
                                        onCompleteScope: this });
     },
 
     _moveOut: function() {
+        Global.focusController.setSearchVisible(false);
         Tweener.addTween(this.actor, { height: 0,
                                        time: 0.20,
                                        transition: 'easeOutQuad',
diff --git a/src/utils.js b/src/utils.js
index 68f42c8..8da8a03 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -19,6 +19,7 @@
  *
  */
 
+const Gdk = imports.gi.Gdk;
 const Gio = imports.gi.Gio;
 const Gtk = imports.gi.Gtk;
 
@@ -60,3 +61,16 @@ function getURNFromPath(path, model) {
 
     return urn;
 }
+
+function isSearchEvent(event) {
+    let keyval = event.get_keyval()[1];
+    let state = event.get_state()[1];
+
+    let retval =
+        (((keyval == Gdk.KEY_f) &&
+          ((state & Gdk.ModifierType.CONTROL_MASK) != 0)) ||
+         ((keyval == Gdk.KEY_s) &&
+          ((state & Gdk.ModifierType.CONTROL_MASK) != 0)));
+
+    return retval;
+}
diff --git a/src/windowMode.js b/src/windowMode.js
index 055dbf9..ac53659 100644
--- a/src/windowMode.js
+++ b/src/windowMode.js
@@ -91,8 +91,20 @@ FocusController.prototype = {
     _init: function() {
     },
 
-    requestSearch: function() {
-        this.emit('focus-search');
+    toggleSearch: function() {
+        this.emit('toggle-search');
+    },
+
+    getSearchVisible: function() {
+        return this._searchVisible;
+    },
+
+    setSearchVisible: function(visible) {
+        this._searchVisible = visible;
+    },
+
+    deliverEvent: function(event) {
+        this.emit('deliver-event', event);
     }
 };
 Signals.addSignalMethods(FocusController.prototype);



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