There's no need to show the workspace indicator at the right corner of the window-list if there's just a single workspace AND the workspace creation is static. This saves us a bit more of space. https://bugzilla.gnome.org/show_bug.cgi?id=777504
1298 lines
46 KiB
JavaScript
1298 lines
46 KiB
JavaScript
const Clutter = imports.gi.Clutter;
|
|
const GLib = imports.gi.GLib;
|
|
const Gio = imports.gi.Gio;
|
|
const Gtk = imports.gi.Gtk;
|
|
const Meta = imports.gi.Meta;
|
|
const Shell = imports.gi.Shell;
|
|
const St = imports.gi.St;
|
|
|
|
const DND = imports.ui.dnd;
|
|
const Lang = imports.lang;
|
|
const Main = imports.ui.main;
|
|
const PanelMenu = imports.ui.panelMenu;
|
|
const PopupMenu = imports.ui.popupMenu;
|
|
|
|
const ExtensionUtils = imports.misc.extensionUtils;
|
|
const Me = ExtensionUtils.getCurrentExtension();
|
|
const Convenience = Me.imports.convenience;
|
|
|
|
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
|
const _ = Gettext.gettext;
|
|
|
|
const ICON_TEXTURE_SIZE = 24;
|
|
const DND_ACTIVATE_TIMEOUT = 500;
|
|
|
|
const GroupingMode = {
|
|
NEVER: 0,
|
|
AUTO: 1,
|
|
ALWAYS: 2
|
|
};
|
|
|
|
|
|
function _minimizeOrActivateWindow(window) {
|
|
let focusWindow = global.display.focus_window;
|
|
if (focusWindow == window ||
|
|
focusWindow && focusWindow.get_transient_for() == window)
|
|
window.minimize();
|
|
else
|
|
window.activate(global.get_current_time());
|
|
}
|
|
|
|
function _openMenu(menu) {
|
|
menu.open();
|
|
|
|
let event = Clutter.get_current_event();
|
|
if (event && event.type() == Clutter.EventType.KEY_RELEASE)
|
|
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
|
}
|
|
|
|
function _onMenuStateChanged(menu, isOpen) {
|
|
if (isOpen)
|
|
return;
|
|
|
|
let [x, y,] = global.get_pointer();
|
|
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
|
if (Me.stateObj.someWindowListContains(actor))
|
|
actor.sync_hover();
|
|
}
|
|
|
|
function _getAppStableSequence(app) {
|
|
let windows = app.get_windows().filter(function(w) { return !w.skip_taskbar; });
|
|
return windows.reduce(function(prev, cur) {
|
|
return Math.min(prev, cur.get_stable_sequence());
|
|
}, Infinity);
|
|
}
|
|
|
|
|
|
const WindowContextMenu = new Lang.Class({
|
|
Name: 'WindowContextMenu',
|
|
Extends: PopupMenu.PopupMenu,
|
|
|
|
_init: function(source, metaWindow) {
|
|
this.parent(source, 0.5, St.Side.BOTTOM);
|
|
|
|
this._metaWindow = metaWindow;
|
|
|
|
this._minimizeItem = new PopupMenu.PopupMenuItem('');
|
|
this._minimizeItem.connect('activate', Lang.bind(this, function() {
|
|
if (this._metaWindow.minimized)
|
|
this._metaWindow.unminimize();
|
|
else
|
|
this._metaWindow.minimize();
|
|
}));
|
|
this.addMenuItem(this._minimizeItem);
|
|
|
|
this._notifyMinimizedId =
|
|
this._metaWindow.connect('notify::minimized',
|
|
Lang.bind(this, this._updateMinimizeItem));
|
|
this._updateMinimizeItem();
|
|
|
|
this._maximizeItem = new PopupMenu.PopupMenuItem('');
|
|
this._maximizeItem.connect('activate', Lang.bind(this, function() {
|
|
if (this._metaWindow.maximized_vertically &&
|
|
this._metaWindow.maximized_horizontally)
|
|
this._metaWindow.unmaximize(Meta.MaximizeFlags.HORIZONTAL |
|
|
Meta.MaximizeFlags.VERTICAL);
|
|
else
|
|
this._metaWindow.maximize(Meta.MaximizeFlags.HORIZONTAL |
|
|
Meta.MaximizeFlags.VERTICAL);
|
|
}));
|
|
this.addMenuItem(this._maximizeItem);
|
|
|
|
this._notifyMaximizedHId =
|
|
this._metaWindow.connect('notify::maximized-horizontally',
|
|
Lang.bind(this, this._updateMaximizeItem));
|
|
this._notifyMaximizedVId =
|
|
this._metaWindow.connect('notify::maximized-vertically',
|
|
Lang.bind(this, this._updateMaximizeItem));
|
|
this._updateMaximizeItem();
|
|
|
|
let item = new PopupMenu.PopupMenuItem(_("Close"));
|
|
item.connect('activate', Lang.bind(this, function() {
|
|
this._metaWindow.delete(global.get_current_time());
|
|
}));
|
|
this.addMenuItem(item);
|
|
|
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
|
},
|
|
|
|
_updateMinimizeItem: function() {
|
|
this._minimizeItem.label.text = this._metaWindow.minimized ? _("Unminimize")
|
|
: _("Minimize");
|
|
},
|
|
|
|
_updateMaximizeItem: function() {
|
|
let maximized = this._metaWindow.maximized_vertically &&
|
|
this._metaWindow.maximized_horizontally;
|
|
this._maximizeItem.label.text = maximized ? _("Unmaximize")
|
|
: _("Maximize");
|
|
},
|
|
|
|
_onDestroy: function() {
|
|
this._metaWindow.disconnect(this._notifyMinimizedId);
|
|
this._metaWindow.disconnect(this._notifyMaximizedHId);
|
|
this._metaWindow.disconnect(this._notifyMaximizedVId);
|
|
}
|
|
});
|
|
|
|
const WindowTitle = new Lang.Class({
|
|
Name: 'WindowTitle',
|
|
|
|
_init: function(metaWindow) {
|
|
this._metaWindow = metaWindow;
|
|
this.actor = new St.BoxLayout({ style_class: 'window-button-box',
|
|
x_expand: true, y_expand: true });
|
|
|
|
this._icon = new St.Bin({ style_class: 'window-button-icon' });
|
|
this.actor.add(this._icon);
|
|
this.label_actor = new St.Label({ y_align: Clutter.ActorAlign.CENTER });
|
|
this.actor.add(this.label_actor);
|
|
|
|
this._textureCache = St.TextureCache.get_default();
|
|
this._iconThemeChangedId =
|
|
this._textureCache.connect('icon-theme-changed',
|
|
Lang.bind(this, this._updateIcon));
|
|
this._notifyWmClass =
|
|
this._metaWindow.connect('notify::wm-class',
|
|
Lang.bind(this, this._updateIcon));
|
|
this._notifyAppId =
|
|
this._metaWindow.connect('notify::gtk-application-id',
|
|
Lang.bind(this, this._updateIcon));
|
|
this._updateIcon();
|
|
|
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
|
|
|
this._notifyTitleId =
|
|
this._metaWindow.connect('notify::title',
|
|
Lang.bind(this, this._updateTitle));
|
|
this._notifyMinimizedId =
|
|
this._metaWindow.connect('notify::minimized',
|
|
Lang.bind(this, this._minimizedChanged));
|
|
this._minimizedChanged();
|
|
},
|
|
|
|
_minimizedChanged: function() {
|
|
this._icon.opacity = this._metaWindow.minimized ? 128 : 255;
|
|
this._updateTitle();
|
|
},
|
|
|
|
_updateTitle: function() {
|
|
if (!this._metaWindow.title)
|
|
return;
|
|
|
|
if (this._metaWindow.minimized)
|
|
this.label_actor.text = '[%s]'.format(this._metaWindow.title);
|
|
else
|
|
this.label_actor.text = this._metaWindow.title;
|
|
},
|
|
|
|
_updateIcon: function() {
|
|
let app = Shell.WindowTracker.get_default().get_window_app(this._metaWindow);
|
|
if (app)
|
|
this._icon.child = app.create_icon_texture(ICON_TEXTURE_SIZE);
|
|
else
|
|
this._icon.child = new St.Icon({ icon_name: 'icon-missing',
|
|
icon_size: ICON_TEXTURE_SIZE });
|
|
},
|
|
|
|
_onDestroy: function() {
|
|
this._textureCache.disconnect(this._iconThemeChangedId);
|
|
this._metaWindow.disconnect(this._notifyTitleId);
|
|
this._metaWindow.disconnect(this._notifyMinimizedId);
|
|
this._metaWindow.disconnect(this._notifyWmClass);
|
|
this._metaWindow.disconnect(this._notifyAppId);
|
|
}
|
|
});
|
|
|
|
|
|
const BaseButton = new Lang.Class({
|
|
Name: 'BaseButton',
|
|
Abstract: true,
|
|
|
|
_init: function(perMonitor, monitorIndex) {
|
|
this._perMonitor = perMonitor;
|
|
this._monitorIndex = monitorIndex;
|
|
|
|
this.actor = new St.Button({ style_class: 'window-button',
|
|
x_fill: true,
|
|
y_fill: true,
|
|
can_focus: true,
|
|
button_mask: St.ButtonMask.ONE |
|
|
St.ButtonMask.THREE });
|
|
this.actor._delegate = this;
|
|
|
|
this.actor.connect('allocation-changed',
|
|
Lang.bind(this, this._updateIconGeometry));
|
|
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
|
this.actor.connect('popup-menu', Lang.bind(this, this._onPopupMenu));
|
|
|
|
this._contextMenuManager = new PopupMenu.PopupMenuManager(this);
|
|
|
|
this._switchWorkspaceId =
|
|
global.window_manager.connect('switch-workspace',
|
|
Lang.bind(this, this._updateVisibility));
|
|
|
|
if (this._perMonitor) {
|
|
this._windowEnteredMonitorId =
|
|
global.screen.connect('window-entered-monitor',
|
|
Lang.bind(this, this._windowEnteredOrLeftMonitor));
|
|
this._windowLeftMonitorId =
|
|
global.screen.connect('window-left-monitor',
|
|
Lang.bind(this, this._windowEnteredOrLeftMonitor));
|
|
}
|
|
},
|
|
|
|
get active() {
|
|
return this.actor.has_style_class_name('focused');
|
|
},
|
|
|
|
activate: function() {
|
|
if (this.active)
|
|
return;
|
|
|
|
this._onClicked(this.actor, 1);
|
|
},
|
|
|
|
_onClicked: function(actor, button) {
|
|
throw new Error('Not implemented');
|
|
},
|
|
|
|
_canOpenPopupMenu: function() {
|
|
return true;
|
|
},
|
|
|
|
_onPopupMenu: function(actor) {
|
|
if (!this._canOpenPopupMenu() || this._contextMenu.isOpen)
|
|
return;
|
|
_openMenu(this._contextMenu);
|
|
},
|
|
|
|
_isFocused: function() {
|
|
throw new Error('Not implemented');
|
|
},
|
|
|
|
_updateStyle: function() {
|
|
if (this._isFocused())
|
|
this.actor.add_style_class_name('focused');
|
|
else
|
|
this.actor.remove_style_class_name('focused');
|
|
},
|
|
|
|
_windowEnteredOrLeftMonitor: function(metaScreen, monitorIndex, metaWindow) {
|
|
throw new Error('Not implemented');
|
|
},
|
|
|
|
_isWindowVisible: function(window) {
|
|
let workspace = global.screen.get_active_workspace();
|
|
|
|
return !window.skip_taskbar &&
|
|
window.located_on_workspace(workspace) &&
|
|
(!this._perMonitor || window.get_monitor() == this._monitorIndex);
|
|
},
|
|
|
|
_updateVisibility: function() {
|
|
throw new Error('Not implemented');
|
|
},
|
|
|
|
_getIconGeometry: function() {
|
|
let rect = new Meta.Rectangle();
|
|
|
|
[rect.x, rect.y] = this.actor.get_transformed_position();
|
|
[rect.width, rect.height] = this.actor.get_transformed_size();
|
|
|
|
return rect;
|
|
},
|
|
|
|
_updateIconGeometry: function() {
|
|
throw new Error('Not implemented');
|
|
},
|
|
|
|
_onDestroy: function() {
|
|
global.window_manager.disconnect(this._switchWorkspaceId);
|
|
|
|
if (this._windowEnteredMonitorId)
|
|
global.screen.disconnect(this._windowEnteredMonitorId);
|
|
this._windowEnteredMonitorId = 0;
|
|
|
|
if (this._windowLeftMonitorId)
|
|
global.screen.disconnect(this._windowLeftMonitorId);
|
|
this._windowLeftMonitorId = 0;
|
|
}
|
|
});
|
|
|
|
|
|
const WindowButton = new Lang.Class({
|
|
Name: 'WindowButton',
|
|
Extends: BaseButton,
|
|
|
|
_init: function(metaWindow, perMonitor, monitorIndex) {
|
|
this.parent(perMonitor, monitorIndex);
|
|
|
|
this.metaWindow = metaWindow;
|
|
this._updateVisibility();
|
|
|
|
this._windowTitle = new WindowTitle(this.metaWindow);
|
|
this.actor.set_child(this._windowTitle.actor);
|
|
this.actor.label_actor = this._windowTitle.label_actor;
|
|
|
|
this._contextMenu = new WindowContextMenu(this.actor, this.metaWindow);
|
|
this._contextMenu.connect('open-state-changed', _onMenuStateChanged);
|
|
this._contextMenu.actor.hide();
|
|
this._contextMenuManager.addMenu(this._contextMenu);
|
|
Main.uiGroup.add_actor(this._contextMenu.actor);
|
|
|
|
this._workspaceChangedId =
|
|
this.metaWindow.connect('workspace-changed',
|
|
Lang.bind(this, this._updateVisibility));
|
|
|
|
this._notifyFocusId =
|
|
global.display.connect('notify::focus-window',
|
|
Lang.bind(this, this._updateStyle));
|
|
this._updateStyle();
|
|
},
|
|
|
|
_onClicked: function(actor, button) {
|
|
if (this._contextMenu.isOpen) {
|
|
this._contextMenu.close();
|
|
return;
|
|
}
|
|
|
|
if (button == 1)
|
|
_minimizeOrActivateWindow(this.metaWindow);
|
|
else
|
|
_openMenu(this._contextMenu);
|
|
},
|
|
|
|
_isFocused: function() {
|
|
return global.display.focus_window == this.metaWindow;
|
|
},
|
|
|
|
_updateStyle: function() {
|
|
this.parent();
|
|
|
|
if (this.metaWindow.minimized)
|
|
this.actor.add_style_class_name('minimized');
|
|
else
|
|
this.actor.remove_style_class_name('minimized');
|
|
},
|
|
|
|
_windowEnteredOrLeftMonitor: function(metaScreen, monitorIndex, metaWindow) {
|
|
if (monitorIndex == this._monitorIndex && metaWindow == this.metaWindow)
|
|
this._updateVisibility();
|
|
},
|
|
|
|
_updateVisibility: function() {
|
|
this.actor.visible = this._isWindowVisible(this.metaWindow);
|
|
},
|
|
|
|
_updateIconGeometry: function() {
|
|
this.metaWindow.set_icon_geometry(this._getIconGeometry());
|
|
},
|
|
|
|
_onDestroy: function() {
|
|
this.parent();
|
|
this.metaWindow.disconnect(this._workspaceChangedId);
|
|
global.display.disconnect(this._notifyFocusId);
|
|
this._contextMenu.destroy();
|
|
}
|
|
});
|
|
|
|
|
|
const AppContextMenu = new Lang.Class({
|
|
Name: 'AppContextMenu',
|
|
Extends: PopupMenu.PopupMenu,
|
|
|
|
_init: function(source, appButton) {
|
|
this.parent(source, 0.5, St.Side.BOTTOM);
|
|
|
|
this._appButton = appButton;
|
|
|
|
this._minimizeItem = new PopupMenu.PopupMenuItem(_("Minimize all"));
|
|
this._minimizeItem.connect('activate', Lang.bind(this, function() {
|
|
this._appButton.getWindowList().forEach(function(w) {
|
|
w.minimize();
|
|
});
|
|
}));
|
|
this.addMenuItem(this._minimizeItem);
|
|
|
|
this._unminimizeItem = new PopupMenu.PopupMenuItem(_("Unminimize all"));
|
|
this._unminimizeItem.connect('activate', Lang.bind(this, function() {
|
|
this._appButton.getWindowList().forEach(function(w) {
|
|
w.unminimize();
|
|
});
|
|
}));
|
|
this.addMenuItem(this._unminimizeItem);
|
|
|
|
this._maximizeItem = new PopupMenu.PopupMenuItem(_("Maximize all"));
|
|
this._maximizeItem.connect('activate', Lang.bind(this, function() {
|
|
this._appButton.getWindowList().forEach(function(w) {
|
|
w.maximize(Meta.MaximizeFlags.HORIZONTAL |
|
|
Meta.MaximizeFlags.VERTICAL);
|
|
});
|
|
}));
|
|
this.addMenuItem(this._maximizeItem);
|
|
|
|
this._unmaximizeItem = new PopupMenu.PopupMenuItem(_("Unmaximize all"));
|
|
this._unmaximizeItem.connect('activate', Lang.bind(this, function() {
|
|
this._appButton.getWindowList().forEach(function(w) {
|
|
w.unmaximize(Meta.MaximizeFlags.HORIZONTAL |
|
|
Meta.MaximizeFlags.VERTICAL);
|
|
});
|
|
}));
|
|
this.addMenuItem(this._unmaximizeItem);
|
|
|
|
let item = new PopupMenu.PopupMenuItem(_("Close all"));
|
|
item.connect('activate', Lang.bind(this, function() {
|
|
this._appButton.getWindowList().forEach(function(w) {
|
|
w.delete(global.get_current_time());
|
|
});
|
|
}));
|
|
this.addMenuItem(item);
|
|
},
|
|
|
|
open: function(animate) {
|
|
let windows = this._appButton.getWindowList();
|
|
this._minimizeItem.actor.visible = windows.some(function(w) {
|
|
return !w.minimized;
|
|
});
|
|
this._unminimizeItem.actor.visible = windows.some(function(w) {
|
|
return w.minimized;
|
|
});
|
|
this._maximizeItem.actor.visible = windows.some(function(w) {
|
|
return !(w.maximized_horizontally && w.maximized_vertically);
|
|
});
|
|
this._unmaximizeItem.actor.visible = windows.some(function(w) {
|
|
return w.maximized_horizontally && w.maximized_vertically;
|
|
});
|
|
|
|
this.parent(animate);
|
|
}
|
|
});
|
|
|
|
const AppButton = new Lang.Class({
|
|
Name: 'AppButton',
|
|
Extends: BaseButton,
|
|
|
|
_init: function(app, perMonitor, monitorIndex) {
|
|
this.parent(perMonitor, monitorIndex);
|
|
|
|
this.app = app;
|
|
this._updateVisibility();
|
|
|
|
let stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
|
this.actor.set_child(stack);
|
|
|
|
this._singleWindowTitle = new St.Bin({ x_expand: true,
|
|
y_fill: true,
|
|
x_align: St.Align.START });
|
|
stack.add_actor(this._singleWindowTitle);
|
|
|
|
this._multiWindowTitle = new St.BoxLayout({ style_class: 'window-button-box',
|
|
x_expand: true });
|
|
stack.add_actor(this._multiWindowTitle);
|
|
|
|
this._icon = new St.Bin({ style_class: 'window-button-icon',
|
|
child: app.create_icon_texture(ICON_TEXTURE_SIZE) });
|
|
this._multiWindowTitle.add(this._icon);
|
|
|
|
let label = new St.Label({ text: app.get_name(),
|
|
y_align: Clutter.ActorAlign.CENTER });
|
|
this._multiWindowTitle.add(label);
|
|
this._multiWindowTitle.label_actor = label;
|
|
|
|
this._menuManager = new PopupMenu.PopupMenuManager(this);
|
|
this._menu = new PopupMenu.PopupMenu(this.actor, 0.5, St.Side.BOTTOM);
|
|
this._menu.connect('open-state-changed', _onMenuStateChanged);
|
|
this._menu.actor.hide();
|
|
this._menu.connect('activate', Lang.bind(this, this._onMenuActivate));
|
|
this._menuManager.addMenu(this._menu);
|
|
Main.uiGroup.add_actor(this._menu.actor);
|
|
|
|
this._appContextMenu = new AppContextMenu(this.actor, this);
|
|
this._appContextMenu.connect('open-state-changed', _onMenuStateChanged);
|
|
this._appContextMenu.actor.hide();
|
|
Main.uiGroup.add_actor(this._appContextMenu.actor);
|
|
|
|
this._textureCache = St.TextureCache.get_default();
|
|
this._iconThemeChangedId =
|
|
this._textureCache.connect('icon-theme-changed', Lang.bind(this,
|
|
function() {
|
|
this._icon.child = app.create_icon_texture(ICON_TEXTURE_SIZE);
|
|
}));
|
|
|
|
this._windowsChangedId =
|
|
this.app.connect('windows-changed',
|
|
Lang.bind(this, this._windowsChanged));
|
|
this._windowsChanged();
|
|
|
|
this._windowTracker = Shell.WindowTracker.get_default();
|
|
this._notifyFocusId =
|
|
this._windowTracker.connect('notify::focus-app',
|
|
Lang.bind(this, this._updateStyle));
|
|
this._updateStyle();
|
|
},
|
|
|
|
_windowEnteredOrLeftMonitor: function(metaScreen, monitorIndex, metaWindow) {
|
|
if (this._windowTracker.get_window_app(metaWindow) == this.app &&
|
|
monitorIndex == this._monitorIndex) {
|
|
this._updateVisibility();
|
|
this._windowsChanged();
|
|
}
|
|
},
|
|
|
|
_updateVisibility: function() {
|
|
if (!this._perMonitor) {
|
|
// fast path: use ShellApp API to avoid iterating over all windows.
|
|
let workspace = global.screen.get_active_workspace();
|
|
this.actor.visible = this.app.is_on_workspace(workspace);
|
|
} else {
|
|
this.actor.visible = this.getWindowList().length >= 1;
|
|
}
|
|
},
|
|
|
|
_isFocused: function() {
|
|
return this._windowTracker.focus_app == this.app;
|
|
},
|
|
|
|
_updateIconGeometry: function() {
|
|
let rect = this._getIconGeometry();
|
|
|
|
let windows = this.app.get_windows();
|
|
windows.forEach(function(w) {
|
|
w.set_icon_geometry(rect);
|
|
});
|
|
},
|
|
|
|
|
|
getWindowList: function() {
|
|
return this.app.get_windows().filter(Lang.bind(this, function(win) {
|
|
return this._isWindowVisible(win);
|
|
}));
|
|
},
|
|
|
|
_windowsChanged: function() {
|
|
let windows = this.getWindowList();
|
|
this._singleWindowTitle.visible = windows.length == 1;
|
|
this._multiWindowTitle.visible = !this._singleWindowTitle.visible;
|
|
|
|
if (this._singleWindowTitle.visible) {
|
|
if (!this._windowTitle) {
|
|
this.metaWindow = windows[0];
|
|
this._windowTitle = new WindowTitle(this.metaWindow);
|
|
this._singleWindowTitle.child = this._windowTitle.actor;
|
|
this._windowContextMenu = new WindowContextMenu(this.actor, this.metaWindow);
|
|
this._windowContextMenu.connect('open-state-changed',
|
|
_onMenuStateChanged);
|
|
Main.uiGroup.add_actor(this._windowContextMenu.actor);
|
|
this._windowContextMenu.actor.hide();
|
|
this._contextMenuManager.addMenu(this._windowContextMenu);
|
|
}
|
|
this._contextMenuManager.removeMenu(this._appContextMenu);
|
|
this._contextMenu = this._windowContextMenu;
|
|
this.actor.label_actor = this._windowTitle.label_actor;
|
|
} else {
|
|
if (this._windowTitle) {
|
|
this.metaWindow = null;
|
|
this._singleWindowTitle.child = null;
|
|
this._windowTitle = null;
|
|
this._windowContextMenu.destroy();
|
|
this._windowContextMenu = null;
|
|
}
|
|
this._contextMenu = this._appContextMenu;
|
|
this._contextMenuManager.addMenu(this._appContextMenu);
|
|
this.actor.label_actor = this._multiWindowTitle.label_actor;
|
|
}
|
|
|
|
},
|
|
|
|
_onClicked: function(actor, button) {
|
|
let menuWasOpen = this._menu.isOpen;
|
|
if (menuWasOpen)
|
|
this._menu.close();
|
|
|
|
let contextMenuWasOpen = this._contextMenu.isOpen;
|
|
if (contextMenuWasOpen)
|
|
this._contextMenu.close();
|
|
|
|
if (button == 1) {
|
|
if (menuWasOpen)
|
|
return;
|
|
|
|
let windows = this.getWindowList();
|
|
if (windows.length == 1) {
|
|
if (contextMenuWasOpen)
|
|
return;
|
|
_minimizeOrActivateWindow(windows[0]);
|
|
} else {
|
|
this._menu.removeAll();
|
|
|
|
for (let i = 0; i < windows.length; i++) {
|
|
let windowTitle = new WindowTitle(windows[i]);
|
|
let item = new PopupMenu.PopupBaseMenuItem();
|
|
item.actor.add_actor(windowTitle.actor);
|
|
item._window = windows[i];
|
|
this._menu.addMenuItem(item);
|
|
}
|
|
_openMenu(this._menu);
|
|
}
|
|
} else {
|
|
if (contextMenuWasOpen)
|
|
return;
|
|
_openMenu(this._contextMenu);
|
|
}
|
|
},
|
|
|
|
_canOpenPopupMenu: function() {
|
|
return !this._menu.isOpen;
|
|
},
|
|
|
|
_onMenuActivate: function(menu, child) {
|
|
child._window.activate(global.get_current_time());
|
|
},
|
|
|
|
_onDestroy: function() {
|
|
this.parent();
|
|
this._textureCache.disconnect(this._iconThemeChangedId);
|
|
this._windowTracker.disconnect(this._notifyFocusId);
|
|
this.app.disconnect(this._windowsChangedId);
|
|
this._menu.destroy();
|
|
}
|
|
});
|
|
|
|
|
|
const WorkspaceIndicator = new Lang.Class({
|
|
Name: 'WindowList.WorkspaceIndicator',
|
|
Extends: PanelMenu.Button,
|
|
|
|
_init: function(){
|
|
this.parent(0.0, _("Workspace Indicator"), true);
|
|
this.setMenu(new PopupMenu.PopupMenu(this.actor, 0.0, St.Side.BOTTOM));
|
|
this.actor.add_style_class_name('window-list-workspace-indicator');
|
|
this.menu.actor.remove_style_class_name('panel-menu');
|
|
|
|
let container = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
|
x_expand: true, y_expand: true });
|
|
this.actor.add_actor(container);
|
|
|
|
this._currentWorkspace = global.screen.get_active_workspace().index();
|
|
this.statusLabel = new St.Label({ text: this._getStatusText(),
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
y_align: Clutter.ActorAlign.CENTER });
|
|
container.add_actor(this.statusLabel);
|
|
|
|
this.workspacesItems = [];
|
|
|
|
this._screenSignals = [];
|
|
this._screenSignals.push(global.screen.connect('notify::n-workspaces', Lang.bind(this,this._updateMenu)));
|
|
this._screenSignals.push(global.screen.connect_after('workspace-switched', Lang.bind(this,this._updateIndicator)));
|
|
|
|
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
|
this._updateMenu();
|
|
|
|
this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.wm.preferences' });
|
|
this._settingsChangedId = this._settings.connect('changed::workspace-names', Lang.bind(this, this._updateMenu));
|
|
},
|
|
|
|
destroy: function() {
|
|
for (let i = 0; i < this._screenSignals.length; i++)
|
|
global.screen.disconnect(this._screenSignals[i]);
|
|
|
|
if (this._settingsChangedId) {
|
|
this._settings.disconnect(this._settingsChangedId);
|
|
this._settingsChangedId = 0;
|
|
}
|
|
|
|
this.parent();
|
|
},
|
|
|
|
_updateIndicator: function() {
|
|
this.workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE);
|
|
this._currentWorkspace = global.screen.get_active_workspace().index();
|
|
this.workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.DOT);
|
|
|
|
this.statusLabel.set_text(this._getStatusText());
|
|
},
|
|
|
|
_getStatusText: function() {
|
|
let current = global.screen.get_active_workspace().index();
|
|
let total = global.screen.n_workspaces;
|
|
|
|
return '%d / %d'.format(current + 1, total);
|
|
},
|
|
|
|
_updateMenu: function() {
|
|
this.menu.removeAll();
|
|
this.workspacesItems = [];
|
|
this._currentWorkspace = global.screen.get_active_workspace().index();
|
|
|
|
for(let i = 0; i < global.screen.n_workspaces; i++) {
|
|
let name = Meta.prefs_get_workspace_name(i);
|
|
let item = new PopupMenu.PopupMenuItem(name);
|
|
item.workspaceId = i;
|
|
|
|
item.connect('activate', Lang.bind(this, function(item, event) {
|
|
this._activate(item.workspaceId);
|
|
}));
|
|
|
|
if (i == this._currentWorkspace)
|
|
item.setOrnament(PopupMenu.Ornament.DOT);
|
|
|
|
this.menu.addMenuItem(item);
|
|
this.workspacesItems[i] = item;
|
|
}
|
|
|
|
this.statusLabel.set_text(this._getStatusText());
|
|
},
|
|
|
|
_activate: function(index) {
|
|
if(index >= 0 && index < global.screen.n_workspaces) {
|
|
let metaWorkspace = global.screen.get_workspace_by_index(index);
|
|
metaWorkspace.activate(global.get_current_time());
|
|
}
|
|
},
|
|
|
|
_onScrollEvent: function(actor, event) {
|
|
let direction = event.get_scroll_direction();
|
|
let diff = 0;
|
|
if (direction == Clutter.ScrollDirection.DOWN) {
|
|
diff = 1;
|
|
} else if (direction == Clutter.ScrollDirection.UP) {
|
|
diff = -1;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
let newIndex = this._currentWorkspace + diff;
|
|
this._activate(newIndex);
|
|
},
|
|
|
|
_allocate: function(actor, box, flags) {
|
|
if (actor.get_n_children() > 0)
|
|
actor.get_first_child().allocate(box, flags);
|
|
}
|
|
});
|
|
|
|
const WindowList = new Lang.Class({
|
|
Name: 'WindowList',
|
|
|
|
_init: function(perMonitor, monitor) {
|
|
this._perMonitor = perMonitor;
|
|
this._monitor = monitor;
|
|
|
|
this.actor = new St.Widget({ name: 'panel',
|
|
style_class: 'bottom-panel',
|
|
reactive: true,
|
|
track_hover: true,
|
|
layout_manager: new Clutter.BinLayout()});
|
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
|
|
|
let box = new St.BoxLayout({ x_expand: true, y_expand: true });
|
|
this.actor.add_actor(box);
|
|
|
|
let layout = new Clutter.BoxLayout({ homogeneous: true });
|
|
this._windowList = new St.Widget({ style_class: 'window-list',
|
|
reactive: true,
|
|
layout_manager: layout,
|
|
x_align: Clutter.ActorAlign.START,
|
|
x_expand: true,
|
|
y_expand: true });
|
|
box.add(this._windowList, { expand: true });
|
|
|
|
this._windowList.connect('style-changed', Lang.bind(this,
|
|
function() {
|
|
let node = this._windowList.get_theme_node();
|
|
let spacing = node.get_length('spacing');
|
|
this._windowList.layout_manager.spacing = spacing;
|
|
}));
|
|
this._windowList.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
|
|
|
let indicatorsBox = new St.BoxLayout({ x_align: Clutter.ActorAlign.END });
|
|
box.add(indicatorsBox);
|
|
|
|
this._workspaceIndicator = new WorkspaceIndicator();
|
|
indicatorsBox.add(this._workspaceIndicator.container, { expand: false, y_fill: true });
|
|
|
|
this._mutterSettings = new Gio.Settings({ schema_id: 'org.gnome.mutter' });
|
|
this._workspaceSettings = this._getWorkspaceSettings();
|
|
this._workspacesOnlyOnPrimaryChangedId =
|
|
this._workspaceSettings.connect('changed::workspaces-only-on-primary',
|
|
Lang.bind(this, this._updateWorkspaceIndicatorVisibility));
|
|
this._dynamicWorkspacesSettings = this._getDynamicWorkspacesSettings();
|
|
this._dynamicWorkspacesChangedId =
|
|
this._dynamicWorkspacesSettings.connect('changed::dynamic-workspaces',
|
|
Lang.bind(this, this._updateWorkspaceIndicatorVisibility));
|
|
this._updateWorkspaceIndicatorVisibility();
|
|
|
|
this._menuManager = new PopupMenu.PopupMenuManager(this);
|
|
this._menuManager.addMenu(this._workspaceIndicator.menu);
|
|
|
|
Main.layoutManager.addChrome(this.actor, { affectsStruts: true,
|
|
trackFullscreen: true });
|
|
Main.uiGroup.set_child_above_sibling(this.actor, Main.layoutManager.panelBox);
|
|
Main.ctrlAltTabManager.addGroup(this.actor, _("Window List"), 'start-here-symbolic');
|
|
|
|
this.actor.width = this._monitor.width;
|
|
this.actor.connect('notify::height', Lang.bind(this, this._updatePosition));
|
|
this._updatePosition();
|
|
|
|
this._appSystem = Shell.AppSystem.get_default();
|
|
this._appStateChangedId =
|
|
this._appSystem.connect('app-state-changed',
|
|
Lang.bind(this, this._onAppStateChanged));
|
|
|
|
this._keyboardVisiblechangedId =
|
|
Main.layoutManager.connect('keyboard-visible-changed',
|
|
Lang.bind(this, function(o, state) {
|
|
Main.layoutManager.keyboardBox.visible = state;
|
|
let keyboardBox = Main.layoutManager.keyboardBox;
|
|
keyboardBox.visible = state;
|
|
if (state)
|
|
Main.uiGroup.set_child_above_sibling(this.actor, keyboardBox);
|
|
else
|
|
Main.uiGroup.set_child_above_sibling(this.actor,
|
|
Main.layoutManager.panelBox);
|
|
this._updateKeyboardAnchor();
|
|
}));
|
|
|
|
this._workspaceSignals = new Map();
|
|
this._nWorkspacesChangedId =
|
|
global.screen.connect('notify::n-workspaces',
|
|
Lang.bind(this, this._onWorkspacesChanged));
|
|
this._onWorkspacesChanged();
|
|
|
|
this._switchWorkspaceId =
|
|
global.window_manager.connect('switch-workspace',
|
|
Lang.bind(this, this._checkGrouping));
|
|
|
|
this._overviewShowingId =
|
|
Main.overview.connect('showing', Lang.bind(this, function() {
|
|
this.actor.hide();
|
|
this._updateKeyboardAnchor();
|
|
}));
|
|
|
|
this._overviewHidingId =
|
|
Main.overview.connect('hiding', Lang.bind(this, function() {
|
|
this.actor.visible = !Main.layoutManager.primaryMonitor.inFullscreen;
|
|
this._updateKeyboardAnchor();
|
|
}));
|
|
|
|
this._fullscreenChangedId =
|
|
global.screen.connect('in-fullscreen-changed', Lang.bind(this, function() {
|
|
this._updateKeyboardAnchor();
|
|
}));
|
|
|
|
this._dragBeginId =
|
|
Main.xdndHandler.connect('drag-begin',
|
|
Lang.bind(this, this._onDragBegin));
|
|
this._dragEndId =
|
|
Main.xdndHandler.connect('drag-end',
|
|
Lang.bind(this, this._onDragEnd));
|
|
this._dragMonitor = {
|
|
dragMotion: Lang.bind(this, this._onDragMotion)
|
|
};
|
|
|
|
this._dndTimeoutId = 0;
|
|
this._dndWindow = null;
|
|
|
|
this._settings = Convenience.getSettings();
|
|
this._groupingModeChangedId =
|
|
this._settings.connect('changed::grouping-mode',
|
|
Lang.bind(this, this._groupingModeChanged));
|
|
this._grouped = undefined;
|
|
this._groupingModeChanged();
|
|
},
|
|
|
|
_getDynamicWorkspacesSettings: function() {
|
|
if (this._workspaceSettings.list_keys().indexOf('dynamic-workspaces') > -1)
|
|
return this._workspaceSettings;
|
|
return this._mutterSettings;
|
|
},
|
|
|
|
_getWorkspaceSettings: function() {
|
|
let settings = global.get_overrides_settings();
|
|
if (settings.list_keys().indexOf('workspaces-only-on-primary') > -1)
|
|
return settings;
|
|
return this._mutterSettings;
|
|
},
|
|
|
|
_onScrollEvent: function(actor, event) {
|
|
let direction = event.get_scroll_direction();
|
|
let diff = 0;
|
|
if (direction == Clutter.ScrollDirection.DOWN)
|
|
diff = 1;
|
|
else if (direction == Clutter.ScrollDirection.UP)
|
|
diff = -1;
|
|
else
|
|
return;
|
|
|
|
let children = this._windowList.get_children().map(function(actor) {
|
|
return actor._delegate;
|
|
});
|
|
let active = 0;
|
|
for (let i = 0; i < children.length; i++) {
|
|
if (children[i].active) {
|
|
active = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
active = Math.max(0, Math.min(active + diff, children.length-1));
|
|
children[active].activate();
|
|
},
|
|
|
|
_updatePosition: function() {
|
|
this.actor.set_position(this._monitor.x,
|
|
this._monitor.y + this._monitor.height - this.actor.height);
|
|
},
|
|
|
|
_updateWorkspaceIndicatorVisibility: function() {
|
|
let hasWorkspaces = this._dynamicWorkspacesSettings.get_boolean('dynamic-workspaces') ||
|
|
global.screen.n_workspaces > 1;
|
|
let workspacesOnMonitor = this._monitor == Main.layoutManager.primaryMonitor ||
|
|
!this._workspaceSettings.get_boolean('workspaces-only-on-primary');
|
|
|
|
this._workspaceIndicator.actor.visible = hasWorkspaces && workspacesOnMonitor;
|
|
},
|
|
|
|
_getPreferredUngroupedWindowListWidth: function() {
|
|
if (this._windowList.get_n_children() == 0)
|
|
return this._windowList.get_preferred_width(-1)[1];
|
|
|
|
let children = this._windowList.get_children();
|
|
let [, childWidth] = children[0].get_preferred_width(-1);
|
|
let spacing = this._windowList.layout_manager.spacing;
|
|
|
|
let workspace = global.screen.get_active_workspace();
|
|
let windows = global.display.get_tab_list(Meta.TabList.NORMAL, workspace);
|
|
if (this._perMonitor) {
|
|
windows = windows.filter(Lang.bind(this, function(window) {
|
|
return window.get_monitor() == this._monitor.index;
|
|
}));
|
|
}
|
|
let nWindows = windows.length;
|
|
if (nWindows == 0)
|
|
return this._windowList.get_preferred_width(-1)[1];
|
|
|
|
return nWindows * childWidth + (nWindows - 1) * spacing;
|
|
},
|
|
|
|
_getMaxWindowListWidth: function() {
|
|
let indicatorsBox = this._workspaceIndicator.actor.get_parent();
|
|
return this.actor.width - indicatorsBox.get_preferred_width(-1)[1];
|
|
},
|
|
|
|
_groupingModeChanged: function() {
|
|
this._groupingMode = this._settings.get_enum('grouping-mode');
|
|
|
|
if (this._groupingMode == GroupingMode.AUTO) {
|
|
this._checkGrouping();
|
|
} else {
|
|
this._grouped = this._groupingMode == GroupingMode.ALWAYS;
|
|
this._populateWindowList();
|
|
}
|
|
},
|
|
|
|
_checkGrouping: function() {
|
|
if (this._groupingMode != GroupingMode.AUTO)
|
|
return;
|
|
|
|
let maxWidth = this._getMaxWindowListWidth();
|
|
let natWidth = this._getPreferredUngroupedWindowListWidth();
|
|
|
|
let grouped = (maxWidth < natWidth);
|
|
if (this._grouped !== grouped) {
|
|
this._grouped = grouped;
|
|
this._populateWindowList();
|
|
}
|
|
},
|
|
|
|
_populateWindowList: function() {
|
|
this._windowList.destroy_all_children();
|
|
|
|
if (!this._grouped) {
|
|
let windows = global.get_window_actors().sort(
|
|
function(w1, w2) {
|
|
return w1.metaWindow.get_stable_sequence() -
|
|
w2.metaWindow.get_stable_sequence();
|
|
});
|
|
for (let i = 0; i < windows.length; i++)
|
|
this._onWindowAdded(null, windows[i].metaWindow);
|
|
} else {
|
|
let apps = this._appSystem.get_running().sort(
|
|
function(a1, a2) {
|
|
return _getAppStableSequence(a1) -
|
|
_getAppStableSequence(a2);
|
|
});
|
|
for (let i = 0; i < apps.length; i++)
|
|
this._addApp(apps[i]);
|
|
}
|
|
},
|
|
|
|
_updateKeyboardAnchor: function() {
|
|
if (!Main.keyboard.actor)
|
|
return;
|
|
|
|
let anchorY = Main.overview.visible ? 0 : this.actor.height;
|
|
Main.keyboard.actor.anchor_y = anchorY;
|
|
},
|
|
|
|
_onAppStateChanged: function(appSys, app) {
|
|
if (!this._grouped)
|
|
return;
|
|
|
|
if (app.state == Shell.AppState.RUNNING)
|
|
this._addApp(app);
|
|
else if (app.state == Shell.AppState.STOPPED)
|
|
this._removeApp(app);
|
|
},
|
|
|
|
_addApp: function(app) {
|
|
let button = new AppButton(app, this._perMonitor, this._monitor.index);
|
|
this._windowList.layout_manager.pack(button.actor,
|
|
true, true, true,
|
|
Clutter.BoxAlignment.START,
|
|
Clutter.BoxAlignment.START);
|
|
},
|
|
|
|
_removeApp: function(app) {
|
|
let children = this._windowList.get_children();
|
|
for (let i = 0; i < children.length; i++) {
|
|
if (children[i]._delegate.app == app) {
|
|
children[i].destroy();
|
|
return;
|
|
}
|
|
}
|
|
},
|
|
|
|
_onWindowAdded: function(ws, win) {
|
|
if (win.skip_taskbar)
|
|
return;
|
|
|
|
if (!this._grouped)
|
|
this._checkGrouping();
|
|
|
|
if (this._grouped)
|
|
return;
|
|
|
|
let children = this._windowList.get_children();
|
|
for (let i = 0; i < children.length; i++) {
|
|
if (children[i]._delegate.metaWindow == win)
|
|
return;
|
|
}
|
|
|
|
let button = new WindowButton(win, this._perMonitor, this._monitor.index);
|
|
this._windowList.layout_manager.pack(button.actor,
|
|
true, true, true,
|
|
Clutter.BoxAlignment.START,
|
|
Clutter.BoxAlignment.START);
|
|
},
|
|
|
|
_onWindowRemoved: function(ws, win) {
|
|
if (this._grouped)
|
|
this._checkGrouping();
|
|
|
|
if (this._grouped)
|
|
return;
|
|
|
|
if (win.get_compositor_private())
|
|
return; // not actually removed, just moved to another workspace
|
|
|
|
let children = this._windowList.get_children();
|
|
for (let i = 0; i < children.length; i++) {
|
|
if (children[i]._delegate.metaWindow == win) {
|
|
children[i].destroy();
|
|
return;
|
|
}
|
|
}
|
|
},
|
|
|
|
_onWorkspacesChanged: function() {
|
|
let numWorkspaces = global.screen.n_workspaces;
|
|
for (let i = 0; i < numWorkspaces; i++) {
|
|
let workspace = global.screen.get_workspace_by_index(i);
|
|
if (this._workspaceSignals.has(workspace))
|
|
continue;
|
|
|
|
let signals = { windowAddedId: 0, windowRemovedId: 0 };
|
|
signals._windowAddedId =
|
|
workspace.connect_after('window-added',
|
|
Lang.bind(this, this._onWindowAdded));
|
|
signals._windowRemovedId =
|
|
workspace.connect('window-removed',
|
|
Lang.bind(this, this._onWindowRemoved));
|
|
this._workspaceSignals.set(workspace, signals);
|
|
}
|
|
|
|
this._updateWorkspaceIndicatorVisibility();
|
|
},
|
|
|
|
_disconnectWorkspaceSignals: function() {
|
|
let numWorkspaces = global.screen.n_workspaces;
|
|
for (let i = 0; i < numWorkspaces; i++) {
|
|
let workspace = global.screen.get_workspace_by_index(i);
|
|
let signals = this._workspaceSignals.get(workspace);
|
|
this._workspaceSignals.delete(workspace);
|
|
workspace.disconnect(signals._windowAddedId);
|
|
workspace.disconnect(signals._windowRemovedId);
|
|
}
|
|
},
|
|
|
|
_onDragBegin: function() {
|
|
DND.addDragMonitor(this._dragMonitor);
|
|
},
|
|
|
|
_onDragEnd: function() {
|
|
DND.removeDragMonitor(this._dragMonitor);
|
|
this._removeActivateTimeout();
|
|
},
|
|
|
|
_onDragMotion: function(dragEvent) {
|
|
if (Main.overview.visible ||
|
|
!this.actor.contains(dragEvent.targetActor)) {
|
|
this._removeActivateTimeout();
|
|
return DND.DragMotionResult.CONTINUE;
|
|
}
|
|
|
|
let hoveredWindow = null;
|
|
if (dragEvent.targetActor._delegate)
|
|
hoveredWindow = dragEvent.targetActor._delegate.metaWindow;
|
|
|
|
if (!hoveredWindow ||
|
|
this._dndWindow == hoveredWindow)
|
|
return DND.DragMotionResult.CONTINUE;
|
|
|
|
this._removeActivateTimeout();
|
|
|
|
this._dndWindow = hoveredWindow;
|
|
this._dndTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
|
DND_ACTIVATE_TIMEOUT,
|
|
Lang.bind(this, this._activateWindow));
|
|
|
|
return DND.DragMotionResult.CONTINUE;
|
|
},
|
|
|
|
_removeActivateTimeout: function() {
|
|
if (this._dndTimeoutId)
|
|
GLib.source_remove (this._dndTimeoutId);
|
|
this._dndTimeoutId = 0;
|
|
this._dndWindow = null;
|
|
},
|
|
|
|
_activateWindow: function() {
|
|
let [x, y] = global.get_pointer();
|
|
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
|
|
|
|
if (this._dndWindow && this.actor.contains(pickedActor))
|
|
this._dndWindow.activate(global.get_current_time());
|
|
this._dndWindow = null;
|
|
this._dndTimeoutId = 0;
|
|
|
|
return false;
|
|
},
|
|
|
|
_onDestroy: function() {
|
|
this._workspaceSettings.disconnect(this._workspacesOnlyOnPrimaryChangedId);
|
|
this._dynamicWorkspacesSettings.disconnect(this._dynamicWorkspacesChangedId);
|
|
|
|
this._workspaceIndicator.destroy();
|
|
|
|
Main.ctrlAltTabManager.removeGroup(this.actor);
|
|
|
|
this._appSystem.disconnect(this._appStateChangedId);
|
|
this._appStateChangedId = 0;
|
|
|
|
Main.layoutManager.disconnect(this._keyboardVisiblechangedId);
|
|
this._keyboardVisiblechangedId = 0;
|
|
|
|
Main.layoutManager.hideKeyboard();
|
|
|
|
this._disconnectWorkspaceSignals();
|
|
global.screen.disconnect(this._nWorkspacesChangedId);
|
|
this._nWorkspacesChangedId = 0;
|
|
|
|
global.window_manager.disconnect(this._switchWorkspaceId);
|
|
this._switchWorkspaceId = 0;
|
|
|
|
|
|
Main.overview.disconnect(this._overviewShowingId);
|
|
Main.overview.disconnect(this._overviewHidingId);
|
|
|
|
global.screen.disconnect(this._fullscreenChangedId);
|
|
|
|
Main.xdndHandler.disconnect(this._dragBeginId);
|
|
Main.xdndHandler.disconnect(this._dragEndId);
|
|
|
|
this._settings.disconnect(this._groupingModeChangedId);
|
|
|
|
let windows = global.get_window_actors();
|
|
for (let i = 0; i < windows.length; i++)
|
|
windows[i].metaWindow.set_icon_geometry(null);
|
|
}
|
|
});
|
|
|
|
const Extension = new Lang.Class({
|
|
Name: 'Extension',
|
|
|
|
_init: function() {
|
|
this._windowLists = null;
|
|
this._injections = {};
|
|
},
|
|
|
|
enable: function() {
|
|
this._windowLists = [];
|
|
|
|
this._settings = Convenience.getSettings();
|
|
this._showOnAllMonitorsChangedId =
|
|
this._settings.connect('changed::show-on-all-monitors',
|
|
Lang.bind(this, this._buildWindowLists));
|
|
|
|
this._monitorsChangedId =
|
|
Main.layoutManager.connect('monitors-changed',
|
|
Lang.bind(this, this._buildWindowLists));
|
|
|
|
this._buildWindowLists();
|
|
},
|
|
|
|
_buildWindowLists: function() {
|
|
this._windowLists.forEach(function(windowList) {
|
|
windowList.actor.destroy();
|
|
});
|
|
this._windowLists = [];
|
|
|
|
let showOnAllMonitors = this._settings.get_boolean('show-on-all-monitors');
|
|
|
|
Main.layoutManager.monitors.forEach(Lang.bind(this, function(monitor) {
|
|
if (showOnAllMonitors || monitor == Main.layoutManager.primaryMonitor)
|
|
this._windowLists.push(new WindowList(showOnAllMonitors, monitor));
|
|
}));
|
|
},
|
|
|
|
disable: function() {
|
|
if (!this._windowLists)
|
|
return;
|
|
|
|
this._settings.disconnect(this._showOnAllMonitorsChangedId);
|
|
this._showOnAllMonitorsChangedId = 0;
|
|
|
|
Main.layoutManager.disconnect(this._monitorsChangedId);
|
|
this._monitorsChangedId = 0;
|
|
|
|
this._windowLists.forEach(function(windowList) {
|
|
windowList.actor.hide();
|
|
windowList.actor.destroy();
|
|
});
|
|
this._windowLists = null;
|
|
},
|
|
|
|
someWindowListContains: function(actor) {
|
|
return this._windowLists.some(function(windowList) {
|
|
return windowList.actor.contains(actor);
|
|
});
|
|
}
|
|
});
|
|
|
|
function init() {
|
|
return new Extension();
|
|
}
|