2b45a01517
We have made good progress on object literals as well, although there are still a lot that use the old style, given how ubiquitous object literals are. But the needed reindentation isn't overly intrusive, as changes are limited to the object literals themselves (i.e. they don't affect surrounding code). And given that object literals account for quite a bit of the remaining differences between regular and legacy rules, doing the transition now is still worthwhile. Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2200>
204 lines
6.3 KiB
JavaScript
204 lines
6.3 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
/* exported CtrlAltTabManager */
|
|
|
|
const { Clutter, GObject, Meta, Shell, St } = imports.gi;
|
|
|
|
const Main = imports.ui.main;
|
|
const SwitcherPopup = imports.ui.switcherPopup;
|
|
const Params = imports.misc.params;
|
|
|
|
var POPUP_APPICON_SIZE = 96;
|
|
|
|
var SortGroup = {
|
|
TOP: 0,
|
|
MIDDLE: 1,
|
|
BOTTOM: 2,
|
|
};
|
|
|
|
var CtrlAltTabManager = class CtrlAltTabManager {
|
|
constructor() {
|
|
this._items = [];
|
|
this.addGroup(global.window_group,
|
|
_('Windows'),
|
|
'focus-windows-symbolic', {
|
|
sortGroup: SortGroup.TOP,
|
|
focusCallback: this._focusWindows.bind(this),
|
|
});
|
|
}
|
|
|
|
addGroup(root, name, icon, params) {
|
|
const item = Params.parse(params, {
|
|
sortGroup: SortGroup.MIDDLE,
|
|
proxy: root,
|
|
focusCallback: null,
|
|
});
|
|
|
|
item.root = root;
|
|
item.name = name;
|
|
item.iconName = icon;
|
|
|
|
this._items.push(item);
|
|
root.connect('destroy', () => this.removeGroup(root));
|
|
if (root instanceof St.Widget)
|
|
global.focus_manager.add_group(root);
|
|
}
|
|
|
|
removeGroup(root) {
|
|
if (root instanceof St.Widget)
|
|
global.focus_manager.remove_group(root);
|
|
for (let i = 0; i < this._items.length; i++) {
|
|
if (this._items[i].root == root) {
|
|
this._items.splice(i, 1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
focusGroup(item, timestamp) {
|
|
if (item.focusCallback)
|
|
item.focusCallback(timestamp);
|
|
else
|
|
item.root.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
|
|
}
|
|
|
|
// Sort the items into a consistent order; panel first, tray last,
|
|
// and everything else in between, sorted by X coordinate, so that
|
|
// they will have the same left-to-right ordering in the
|
|
// Ctrl-Alt-Tab dialog as they do onscreen.
|
|
_sortItems(a, b) {
|
|
if (a.sortGroup != b.sortGroup)
|
|
return a.sortGroup - b.sortGroup;
|
|
|
|
let [ax] = a.proxy.get_transformed_position();
|
|
let [bx] = b.proxy.get_transformed_position();
|
|
|
|
return ax - bx;
|
|
}
|
|
|
|
popup(backward, binding, mask) {
|
|
// Start with the set of focus groups that are currently mapped
|
|
let items = this._items.filter(item => item.proxy.mapped);
|
|
|
|
// And add the windows metacity would show in its Ctrl-Alt-Tab list
|
|
if (Main.sessionMode.hasWindows && !Main.overview.visible) {
|
|
let display = global.display;
|
|
let workspaceManager = global.workspace_manager;
|
|
let activeWorkspace = workspaceManager.get_active_workspace();
|
|
let windows = display.get_tab_list(Meta.TabList.DOCKS,
|
|
activeWorkspace);
|
|
let windowTracker = Shell.WindowTracker.get_default();
|
|
let textureCache = St.TextureCache.get_default();
|
|
for (let i = 0; i < windows.length; i++) {
|
|
let icon = null;
|
|
let iconName = null;
|
|
if (windows[i].get_window_type() == Meta.WindowType.DESKTOP) {
|
|
iconName = 'video-display-symbolic';
|
|
} else {
|
|
let app = windowTracker.get_window_app(windows[i]);
|
|
if (app) {
|
|
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
|
|
} else {
|
|
icon = new St.Icon({
|
|
gicon: textureCache.bind_cairo_surface_property(windows[i], 'icon'),
|
|
icon_size: POPUP_APPICON_SIZE,
|
|
});
|
|
}
|
|
}
|
|
|
|
items.push({
|
|
name: windows[i].title,
|
|
proxy: windows[i].get_compositor_private(),
|
|
focusCallback: timestamp => {
|
|
Main.activateWindow(windows[i], timestamp);
|
|
},
|
|
iconActor: icon,
|
|
iconName,
|
|
sortGroup: SortGroup.MIDDLE,
|
|
});
|
|
}
|
|
}
|
|
|
|
if (!items.length)
|
|
return;
|
|
|
|
items.sort(this._sortItems.bind(this));
|
|
|
|
if (!this._popup) {
|
|
this._popup = new CtrlAltTabPopup(items);
|
|
this._popup.show(backward, binding, mask);
|
|
|
|
this._popup.connect('destroy',
|
|
() => {
|
|
this._popup = null;
|
|
});
|
|
}
|
|
}
|
|
|
|
_focusWindows(timestamp) {
|
|
global.display.focus_default_window(timestamp);
|
|
}
|
|
};
|
|
|
|
var CtrlAltTabPopup = GObject.registerClass(
|
|
class CtrlAltTabPopup extends SwitcherPopup.SwitcherPopup {
|
|
_init(items) {
|
|
super._init(items);
|
|
|
|
this._switcherList = new CtrlAltTabSwitcher(this._items);
|
|
}
|
|
|
|
_keyPressHandler(keysym, action) {
|
|
if (action == Meta.KeyBindingAction.SWITCH_PANELS)
|
|
this._select(this._next());
|
|
else if (action == Meta.KeyBindingAction.SWITCH_PANELS_BACKWARD)
|
|
this._select(this._previous());
|
|
else if (keysym == Clutter.KEY_Left)
|
|
this._select(this._previous());
|
|
else if (keysym == Clutter.KEY_Right)
|
|
this._select(this._next());
|
|
else
|
|
return Clutter.EVENT_PROPAGATE;
|
|
|
|
return Clutter.EVENT_STOP;
|
|
}
|
|
|
|
_finish(time) {
|
|
super._finish(time);
|
|
Main.ctrlAltTabManager.focusGroup(this._items[this._selectedIndex], time);
|
|
}
|
|
});
|
|
|
|
var CtrlAltTabSwitcher = GObject.registerClass(
|
|
class CtrlAltTabSwitcher extends SwitcherPopup.SwitcherList {
|
|
_init(items) {
|
|
super._init(true);
|
|
|
|
for (let i = 0; i < items.length; i++)
|
|
this._addIcon(items[i]);
|
|
}
|
|
|
|
_addIcon(item) {
|
|
const box = new St.BoxLayout({
|
|
style_class: 'alt-tab-app',
|
|
vertical: true,
|
|
});
|
|
|
|
let icon = item.iconActor;
|
|
if (!icon) {
|
|
icon = new St.Icon({
|
|
icon_name: item.iconName,
|
|
icon_size: POPUP_APPICON_SIZE,
|
|
});
|
|
}
|
|
box.add_child(icon);
|
|
|
|
let text = new St.Label({
|
|
text: item.name,
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
});
|
|
box.add_child(text);
|
|
|
|
this.addItem(box, text);
|
|
}
|
|
});
|