[gnome-shell] workspace: Implement key navigation on the workspaces page



commit 47a20756b915850051858dea2b80e24b0f40096f
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Mon Nov 4 17:23:44 2013 -0500

    workspace: Implement key navigation on the workspaces page
    
    Simply use St's existing key navigation system by making all the window
    clones StWidgets, and making the WorkspacesView a focus group.
    
    Since the workspace view is effectively "fake", we need to add a focus
    delegator so that when key focus is assigned to the fake workspaces page,
    we can keynav inside it properly.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=644306

 js/ui/workspace.js      |   35 +++++++++++++++++++++++++++--------
 js/ui/workspacesView.js |   17 ++++++++++++++++-
 2 files changed, 43 insertions(+), 9 deletions(-)
---
diff --git a/js/ui/workspace.js b/js/ui/workspace.js
index 4f3958d..d1abc22 100644
--- a/js/ui/workspace.js
+++ b/js/ui/workspace.js
@@ -63,11 +63,12 @@ const WindowClone = new Lang.Class({
         // the invisible border; this is inconvenient; rather than trying
         // to compensate all over the place we insert a ClutterActor into
         // the hierarchy that is sized to only the visible portion.
-        this.actor = new Clutter.Actor({ reactive: true,
-                                         x: this.origX,
-                                         y: this.origY,
-                                         width: outerRect.width,
-                                         height: outerRect.height });
+        this.actor = new St.Widget({ reactive: true,
+                                     can_focus: true,
+                                     x: this.origX,
+                                     y: this.origY,
+                                     width: outerRect.width,
+                                     height: outerRect.height });
 
         this.actor.add_child(this._windowClone);
 
@@ -85,10 +86,9 @@ const WindowClone = new Lang.Class({
         let clickAction = new Clutter.ClickAction();
         clickAction.connect('clicked', Lang.bind(this, this._onClicked));
         clickAction.connect('long-press', Lang.bind(this, this._onLongPress));
-
         this.actor.add_action(clickAction);
-
         this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+        this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPress));
 
         this._draggable = DND.makeDraggable(this.actor,
                                             { restoreOnSuccess: true,
@@ -197,11 +197,26 @@ const WindowClone = new Lang.Class({
         this.disconnectAll();
     },
 
-    _onClicked: function(action, actor) {
+    _activate: function() {
         this._selected = true;
         this.emit('selected', global.get_current_time());
     },
 
+    _onKeyPress: function(actor, event) {
+        let symbol = event.get_key_symbol();
+        let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter);
+        if (isEnter) {
+            this._activate();
+            return true;
+        }
+
+        return false;
+    },
+
+    _onClicked: function(action, actor) {
+        this._activate();
+    },
+
     _onLongPress: function(action, actor, state) {
         // Take advantage of the Clutter policy to consider
         // a long-press canceled when the pointer movement
@@ -301,6 +316,10 @@ const WindowOverlay = new Lang.Class({
                                   Lang.bind(this, this._onEnter));
         windowClone.actor.connect('leave-event',
                                   Lang.bind(this, this._onLeave));
+        windowClone.actor.connect('key-focus-in',
+                                  Lang.bind(this, this._onEnter));
+        windowClone.actor.connect('key-focus-out',
+                                  Lang.bind(this, this._onLeave));
 
         this._windowAddedId = 0;
 
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index eaca4f0..2af8d05 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -30,6 +30,7 @@ const WorkspacesViewBase = new Lang.Class({
         this.actor = new St.Widget({ style_class: 'workspaces-view',
                                      reactive: true });
         this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+        global.focus_manager.add_group(this.actor);
 
         // The actor itself isn't a drop target, so we don't want to pick on its area
         this.actor.set_size(0, 0);
@@ -371,11 +372,21 @@ const ExtraWorkspaceView = new Lang.Class({
     },
 });
 
+const DelegateFocusNavigator = new Lang.Class({
+    Name: 'DelegateFocusNavigator',
+    Extends: St.Widget,
+
+    vfunc_navigate_focus: function(from, direction) {
+        return this._delegate.navigateFocus(from, direction);
+    },
+});
+
 const WorkspacesDisplay = new Lang.Class({
     Name: 'WorkspacesDisplay',
 
     _init: function() {
-        this.actor = new St.Widget({ clip_to_allocation: true });
+        this.actor = new DelegateFocusNavigator({ clip_to_allocation: true });
+        this.actor._delegate = this;
         this.actor.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesActualGeometry));
         this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
 
@@ -437,6 +448,10 @@ const WorkspacesDisplay = new Lang.Class({
         return false;
     },
 
+    navigateFocus: function(from, direction) {
+        return this._getPrimaryView().actor.navigate_focus(from, direction, false);
+    },
+
     show: function() {
         this._updateWorkspacesViews();
         for (let i = 0; i < this._workspacesViews.length; i++)


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