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
This commit is contained in:
Jasper St. Pierre 2013-11-04 17:23:44 -05:00
parent a7aba1d585
commit 47a20756b9
2 changed files with 43 additions and 9 deletions

View File

@ -63,11 +63,12 @@ const WindowClone = new Lang.Class({
// the invisible border; this is inconvenient; rather than trying // the invisible border; this is inconvenient; rather than trying
// to compensate all over the place we insert a ClutterActor into // to compensate all over the place we insert a ClutterActor into
// the hierarchy that is sized to only the visible portion. // the hierarchy that is sized to only the visible portion.
this.actor = new Clutter.Actor({ reactive: true, this.actor = new St.Widget({ reactive: true,
x: this.origX, can_focus: true,
y: this.origY, x: this.origX,
width: outerRect.width, y: this.origY,
height: outerRect.height }); width: outerRect.width,
height: outerRect.height });
this.actor.add_child(this._windowClone); this.actor.add_child(this._windowClone);
@ -85,10 +86,9 @@ const WindowClone = new Lang.Class({
let clickAction = new Clutter.ClickAction(); let clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', Lang.bind(this, this._onClicked)); clickAction.connect('clicked', Lang.bind(this, this._onClicked));
clickAction.connect('long-press', Lang.bind(this, this._onLongPress)); clickAction.connect('long-press', Lang.bind(this, this._onLongPress));
this.actor.add_action(clickAction); this.actor.add_action(clickAction);
this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); 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, this._draggable = DND.makeDraggable(this.actor,
{ restoreOnSuccess: true, { restoreOnSuccess: true,
@ -197,11 +197,26 @@ const WindowClone = new Lang.Class({
this.disconnectAll(); this.disconnectAll();
}, },
_onClicked: function(action, actor) { _activate: function() {
this._selected = true; this._selected = true;
this.emit('selected', global.get_current_time()); 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) { _onLongPress: function(action, actor, state) {
// Take advantage of the Clutter policy to consider // Take advantage of the Clutter policy to consider
// a long-press canceled when the pointer movement // a long-press canceled when the pointer movement
@ -301,6 +316,10 @@ const WindowOverlay = new Lang.Class({
Lang.bind(this, this._onEnter)); Lang.bind(this, this._onEnter));
windowClone.actor.connect('leave-event', windowClone.actor.connect('leave-event',
Lang.bind(this, this._onLeave)); 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; this._windowAddedId = 0;

View File

@ -30,6 +30,7 @@ const WorkspacesViewBase = new Lang.Class({
this.actor = new St.Widget({ style_class: 'workspaces-view', this.actor = new St.Widget({ style_class: 'workspaces-view',
reactive: true }); reactive: true });
this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); 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 // The actor itself isn't a drop target, so we don't want to pick on its area
this.actor.set_size(0, 0); 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({ const WorkspacesDisplay = new Lang.Class({
Name: 'WorkspacesDisplay', Name: 'WorkspacesDisplay',
_init: function() { _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('notify::allocation', Lang.bind(this, this._updateWorkspacesActualGeometry));
this.actor.connect('parent-set', Lang.bind(this, this._parentSet)); this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
@ -437,6 +448,10 @@ const WorkspacesDisplay = new Lang.Class({
return false; return false;
}, },
navigateFocus: function(from, direction) {
return this._getPrimaryView().actor.navigate_focus(from, direction, false);
},
show: function() { show: function() {
this._updateWorkspacesViews(); this._updateWorkspacesViews();
for (let i = 0; i < this._workspacesViews.length; i++) for (let i = 0; i < this._workspacesViews.length; i++)