viewSelector: add Applications pane and search entry to Ctrl-Alt-Tab
Add Ctrl-Alt-Tab support to ViewTab, and fix the Applications pane to scroll to track the keyboard focus. The Windows pane can be switched to, but navigation within the pane is not yet implemented. https://bugzilla.gnome.org/show_bug.cgi?id=618887
This commit is contained in:
parent
7aa326a836
commit
0cccf1d4cc
@ -576,6 +576,10 @@ StTooltip StLabel {
|
|||||||
background-position: 10px 10px;
|
background-position: 10px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-filter:focus {
|
||||||
|
outline: 1px solid #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
.dash-item-container > .app-well-app {
|
.dash-item-container > .app-well-app {
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ const Workspace = imports.ui.workspace;
|
|||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
const MENU_POPUP_TIMEOUT = 600;
|
const MENU_POPUP_TIMEOUT = 600;
|
||||||
|
const SCROLL_TIME = 0.1;
|
||||||
|
|
||||||
function AlphabeticalView() {
|
function AlphabeticalView() {
|
||||||
this._init();
|
this._init();
|
||||||
@ -67,6 +68,7 @@ AlphabeticalView.prototype = {
|
|||||||
let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
|
let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
|
||||||
|
|
||||||
this._grid.addItem(appIcon.actor);
|
this._grid.addItem(appIcon.actor);
|
||||||
|
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
|
||||||
|
|
||||||
appIcon._appInfo = appInfo;
|
appIcon._appInfo = appInfo;
|
||||||
if (this._filterApp && !this._filterApp(appInfo))
|
if (this._filterApp && !this._filterApp(appInfo))
|
||||||
@ -75,6 +77,28 @@ AlphabeticalView.prototype = {
|
|||||||
this._apps.push(appIcon);
|
this._apps.push(appIcon);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_ensureIconVisible: function(icon) {
|
||||||
|
let adjustment = this.actor.vscroll.adjustment;
|
||||||
|
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
|
||||||
|
|
||||||
|
let offset = 0;
|
||||||
|
let vfade = this.actor.get_effect("vfade");
|
||||||
|
if (vfade)
|
||||||
|
offset = vfade.fade_offset;
|
||||||
|
|
||||||
|
if (icon.y < value + offset)
|
||||||
|
value = Math.max(0, icon.y - offset);
|
||||||
|
else if (icon.y + icon.height > value + pageSize - offset)
|
||||||
|
value = Math.min(upper, icon.y + icon.height + offset - pageSize);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
Tweener.addTween(adjustment,
|
||||||
|
{ value: value,
|
||||||
|
time: SCROLL_TIME,
|
||||||
|
transition: 'easeOutQuad' });
|
||||||
|
},
|
||||||
|
|
||||||
setFilter: function(filter) {
|
setFilter: function(filter) {
|
||||||
this._filterApp = filter;
|
this._filterApp = filter;
|
||||||
for (let i = 0; i < this._apps.length; i++)
|
for (let i = 0; i < this._apps.length; i++)
|
||||||
@ -128,6 +152,12 @@ ViewByCategories.prototype = {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
this._sections = [];
|
this._sections = [];
|
||||||
|
|
||||||
|
// We need a dummy actor to catch the keyboard focus if the
|
||||||
|
// user Ctrl-Alt-Tabs here before the deferred work creates
|
||||||
|
// our real contents
|
||||||
|
this._focusDummy = new St.Bin({ can_focus: true });
|
||||||
|
this.actor.add(this._focusDummy);
|
||||||
},
|
},
|
||||||
|
|
||||||
_scrollFilter: function(actor, event) {
|
_scrollFilter: function(actor, event) {
|
||||||
@ -166,7 +196,8 @@ ViewByCategories.prototype = {
|
|||||||
_addFilter: function(name, num) {
|
_addFilter: function(name, num) {
|
||||||
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
|
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
|
||||||
style_class: 'app-filter',
|
style_class: 'app-filter',
|
||||||
x_align: St.Align.START });
|
x_align: St.Align.START,
|
||||||
|
can_focus: true });
|
||||||
this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
|
this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
|
||||||
button.connect('clicked', Lang.bind(this, function() {
|
button.connect('clicked', Lang.bind(this, function() {
|
||||||
this._selectCategory(num);
|
this._selectCategory(num);
|
||||||
@ -201,6 +232,14 @@ ViewByCategories.prototype = {
|
|||||||
this._addFilter(sections[i], i);
|
this._addFilter(sections[i], i);
|
||||||
|
|
||||||
this._selectCategory(-1);
|
this._selectCategory(-1);
|
||||||
|
|
||||||
|
if (this._focusDummy) {
|
||||||
|
let focused = this._focusDummy.has_key_focus();
|
||||||
|
this._focusDummy.destroy();
|
||||||
|
this._focusDummy = null;
|
||||||
|
if (focused)
|
||||||
|
this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -180,10 +180,10 @@ Overview.prototype = {
|
|||||||
this._group.add_actor(this.viewSelector.actor);
|
this._group.add_actor(this.viewSelector.actor);
|
||||||
|
|
||||||
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
|
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
|
||||||
this.viewSelector.addViewTab(_("Windows"), this._workspacesDisplay.actor);
|
this.viewSelector.addViewTab(_("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
|
||||||
|
|
||||||
let appView = new AppDisplay.AllAppDisplay();
|
let appView = new AppDisplay.AllAppDisplay();
|
||||||
this.viewSelector.addViewTab(_("Applications"), appView.actor);
|
this.viewSelector.addViewTab(_("Applications"), appView.actor, 'system-run');
|
||||||
|
|
||||||
// Default search providers
|
// Default search providers
|
||||||
this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
|
this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
@ -15,12 +16,12 @@ const Search = imports.ui.search;
|
|||||||
const SearchDisplay = imports.ui.searchDisplay;
|
const SearchDisplay = imports.ui.searchDisplay;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
function BaseTab(titleActor, pageActor) {
|
function BaseTab(titleActor, pageActor, name, a11yIcon) {
|
||||||
this._init(titleActor, pageActor);
|
this._init(titleActor, pageActor, name, a11yIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseTab.prototype = {
|
BaseTab.prototype = {
|
||||||
_init: function(titleActor, pageActor) {
|
_init: function(titleActor, pageActor, name, a11yIcon) {
|
||||||
this.title = titleActor;
|
this.title = titleActor;
|
||||||
this.page = new St.Bin({ child: pageActor,
|
this.page = new St.Bin({ child: pageActor,
|
||||||
x_align: St.Align.START,
|
x_align: St.Align.START,
|
||||||
@ -29,6 +30,14 @@ BaseTab.prototype = {
|
|||||||
y_fill: true,
|
y_fill: true,
|
||||||
style_class: 'view-tab-page' });
|
style_class: 'view-tab-page' });
|
||||||
|
|
||||||
|
if (this.title.can_focus) {
|
||||||
|
Main.ctrlAltTabManager.addGroup(this.title, name, a11yIcon);
|
||||||
|
} else {
|
||||||
|
Main.ctrlAltTabManager.addGroup(this.page, name, a11yIcon,
|
||||||
|
{ proxy: this.title,
|
||||||
|
focusCallback: Lang.bind(this, this._a11yFocus) });
|
||||||
|
}
|
||||||
|
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -56,6 +65,11 @@ BaseTab.prototype = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_a11yFocus: function() {
|
||||||
|
this._activate();
|
||||||
|
this.page.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||||
|
},
|
||||||
|
|
||||||
_activate: function() {
|
_activate: function() {
|
||||||
this.emit('activated');
|
this.emit('activated');
|
||||||
}
|
}
|
||||||
@ -63,19 +77,19 @@ BaseTab.prototype = {
|
|||||||
Signals.addSignalMethods(BaseTab.prototype);
|
Signals.addSignalMethods(BaseTab.prototype);
|
||||||
|
|
||||||
|
|
||||||
function ViewTab(label, pageActor) {
|
function ViewTab(label, pageActor, a11yIcon) {
|
||||||
this._init(label, pageActor);
|
this._init(label, pageActor, a11yIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewTab.prototype = {
|
ViewTab.prototype = {
|
||||||
__proto__: BaseTab.prototype,
|
__proto__: BaseTab.prototype,
|
||||||
|
|
||||||
_init: function(label, pageActor) {
|
_init: function(label, pageActor, a11yIcon) {
|
||||||
let titleActor = new St.Button({ label: label,
|
let titleActor = new St.Button({ label: label,
|
||||||
style_class: 'view-tab-title' });
|
style_class: 'view-tab-title' });
|
||||||
titleActor.connect('clicked', Lang.bind(this, this._activate));
|
titleActor.connect('clicked', Lang.bind(this, this._activate));
|
||||||
|
|
||||||
BaseTab.prototype._init.call(this, titleActor, pageActor);
|
BaseTab.prototype._init.call(this, titleActor, pageActor, label, a11yIcon);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -101,7 +115,8 @@ SearchTab.prototype = {
|
|||||||
active; it should not exceed ~30
|
active; it should not exceed ~30
|
||||||
characters. */
|
characters. */
|
||||||
hint_text: _("Type to search..."),
|
hint_text: _("Type to search..."),
|
||||||
track_hover: true });
|
track_hover: true,
|
||||||
|
can_focus: true });
|
||||||
this._text = this._entry.clutter_text;
|
this._text = this._entry.clutter_text;
|
||||||
this._text.connect('key-press-event', Lang.bind(this, this._onKeyPress));
|
this._text.connect('key-press-event', Lang.bind(this, this._onKeyPress));
|
||||||
|
|
||||||
@ -118,7 +133,9 @@ SearchTab.prototype = {
|
|||||||
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem, this._openSearchSystem);
|
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem, this._openSearchSystem);
|
||||||
BaseTab.prototype._init.call(this,
|
BaseTab.prototype._init.call(this,
|
||||||
this._entry,
|
this._entry,
|
||||||
this._searchResults.actor);
|
this._searchResults.actor,
|
||||||
|
_("Search"),
|
||||||
|
'edit-find');
|
||||||
|
|
||||||
this._text.connect('text-changed', Lang.bind(this, this._onTextChanged));
|
this._text.connect('text-changed', Lang.bind(this, this._onTextChanged));
|
||||||
this._text.connect('activate', Lang.bind(this, function (se) {
|
this._text.connect('activate', Lang.bind(this, function (se) {
|
||||||
@ -366,8 +383,8 @@ ViewSelector.prototype = {
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
addViewTab: function(title, pageActor) {
|
addViewTab: function(title, pageActor, a11yIcon) {
|
||||||
let viewTab = new ViewTab(title, pageActor);
|
let viewTab = new ViewTab(title, pageActor, a11yIcon);
|
||||||
this._tabs.push(viewTab);
|
this._tabs.push(viewTab);
|
||||||
this._tabBox.add(viewTab.title);
|
this._tabBox.add(viewTab.title);
|
||||||
this._addTab(viewTab);
|
this._addTab(viewTab);
|
||||||
|
@ -176,7 +176,8 @@ st_scroll_view_set_vfade (StScrollView *self,
|
|||||||
if (priv->vfade_effect == NULL)
|
if (priv->vfade_effect == NULL)
|
||||||
priv->vfade_effect = g_object_new (ST_TYPE_SCROLL_VIEW_FADE, NULL);
|
priv->vfade_effect = g_object_new (ST_TYPE_SCROLL_VIEW_FADE, NULL);
|
||||||
|
|
||||||
clutter_actor_add_effect (CLUTTER_ACTOR (self), CLUTTER_EFFECT (priv->vfade_effect));
|
clutter_actor_add_effect_with_name (CLUTTER_ACTOR (self), "vfade",
|
||||||
|
CLUTTER_EFFECT (priv->vfade_effect));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user