diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
index 23ea3cb4..06777775 100644
--- a/extensions/window-list/extension.js
+++ b/extensions/window-list/extension.js
@@ -53,7 +53,7 @@ function _onMenuStateChanged(menu, isOpen) {
let [x, y,] = global.get_pointer();
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
- if (Me.stateObj.windowListContains(actor))
+ if (Me.stateObj.someWindowListContains(actor))
actor.sync_hover();
}
@@ -190,7 +190,10 @@ const BaseButton = new Lang.Class({
Name: 'BaseButton',
Abstract: true,
- _init: function() {
+ _init: function(perMonitor, monitorIndex) {
+ this._perMonitor = perMonitor;
+ this._monitorIndex = monitorIndex;
+
this.actor = new St.Button({ style_class: 'window-button',
x_fill: true,
can_focus: true,
@@ -209,6 +212,15 @@ const BaseButton = new Lang.Class({
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() {
@@ -247,10 +259,16 @@ const BaseButton = new Lang.Class({
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);
+ return !window.skip_taskbar &&
+ window.located_on_workspace(workspace) &&
+ (!this._perMonitor || window.get_monitor() == this._monitorIndex);
},
_updateVisibility: function() {
@@ -272,6 +290,14 @@ const BaseButton = new Lang.Class({
_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;
}
});
@@ -280,8 +306,8 @@ const WindowButton = new Lang.Class({
Name: 'WindowButton',
Extends: BaseButton,
- _init: function(metaWindow) {
- this.parent();
+ _init: function(metaWindow, perMonitor, monitorIndex) {
+ this.parent(perMonitor, monitorIndex);
this.metaWindow = metaWindow;
this._updateVisibility();
@@ -330,6 +356,11 @@ const WindowButton = new Lang.Class({
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);
},
@@ -422,8 +453,8 @@ const AppButton = new Lang.Class({
Name: 'AppButton',
Extends: BaseButton,
- _init: function(app) {
- this.parent();
+ _init: function(app, perMonitor, monitorIndex) {
+ this.parent(perMonitor, monitorIndex);
this.app = app;
this._updateVisibility();
@@ -476,9 +507,22 @@ const AppButton = new Lang.Class({
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() {
- let workspace = global.screen.get_active_workspace();
- this.actor.visible = this.app.is_on_workspace(workspace);
+ 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() {
@@ -767,7 +811,10 @@ const WorkspaceIndicator = new Lang.Class({
const WindowList = new Lang.Class({
Name: 'WindowList',
- _init: function() {
+ _init: function(perMonitor, monitor) {
+ this._perMonitor = perMonitor;
+ this._monitor = monitor;
+
this.actor = new St.Widget({ name: 'panel',
style_class: 'bottom-panel',
reactive: true,
@@ -801,26 +848,35 @@ const WindowList = new Lang.Class({
this._workspaceIndicator = new WorkspaceIndicator();
indicatorsBox.add(this._workspaceIndicator.container, { expand: false, y_fill: true });
+ this._workspaceSettings = new Gio.Settings({ schema_id: 'org.gnome.shell.overrides' });
+ this._workspacesOnlyOnPrimaryChangedId =
+ this._workspaceSettings.connect('changed::workspaces-only-on-primary',
+ Lang.bind(this, this._updateWorkspaceIndicatorVisibility));
+ this._updateWorkspaceIndicatorVisibility();
+
this._menuManager = new PopupMenu.PopupMenuManager(this);
this._menuManager.addMenu(this._workspaceIndicator.menu);
- this._trayButton = new TrayButton();
- indicatorsBox.add(this._trayButton.actor, { expand: false });
+ this._isOnBottomMonitor = this._monitor == Main.layoutManager.bottomMonitor;
+
+ if (this._isOnBottomMonitor) {
+ this._trayButton = new TrayButton();
+ indicatorsBox.add(this._trayButton.actor, { expand: false });
+ }
Main.layoutManager.addChrome(this.actor, { affectsStruts: true,
trackFullscreen: true });
Main.ctrlAltTabManager.addGroup(this.actor, _("Window List"), 'start-here-symbolic');
+ this.actor.width = this._monitor.width;
+ this.actor.set_position(this._monitor.x,
+ this._monitor.y + this._monitor.height - this.actor.height);
+
this._appSystem = Shell.AppSystem.get_default();
this._appStateChangedId =
this._appSystem.connect('app-state-changed',
Lang.bind(this, this._onAppStateChanged));
- this._monitorsChangedId =
- Main.layoutManager.connect('monitors-changed',
- Lang.bind(this, this._updatePosition));
- this._updatePosition();
-
this._keyboardVisiblechangedId =
Main.layoutManager.connect('keyboard-visible-changed',
Lang.bind(this, function(o, state) {
@@ -854,8 +910,6 @@ const WindowList = new Lang.Class({
this._updateMessageTrayAnchor();
}));
- this._isOnBottomMonitor = Main.layoutManager.primaryIndex == Main.layoutManager.bottomIndex;
-
if (this._isOnBottomMonitor) {
let actor = this.actor;
this._bottomHoverChangedId =
@@ -925,6 +979,12 @@ const WindowList = new Lang.Class({
children[active].activate();
},
+ _updateWorkspaceIndicatorVisibility: function() {
+ this._workspaceIndicator.actor.visible =
+ this._monitor == Main.layoutManager.primaryMonitor ||
+ !this._workspaceSettings.get_boolean('workspaces-only-on-primary');
+ },
+
_getPreferredUngroupedWindowListWidth: function() {
if (this._windowList.get_n_children() == 0)
return this._windowList.get_preferred_width(-1)[1];
@@ -934,13 +994,21 @@ const WindowList = new Lang.Class({
let spacing = this._windowList.layout_manager.spacing;
let workspace = global.screen.get_active_workspace();
- let nWindows = global.display.get_tab_list(Meta.TabList.NORMAL, workspace).length;
+ 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._trayButton.actor.get_parent();
+ let indicatorsBox = this._workspaceIndicator.actor.get_parent();
return this.actor.width - indicatorsBox.get_preferred_width(-1)[1];
},
@@ -991,12 +1059,6 @@ const WindowList = new Lang.Class({
}
},
- _updatePosition: function() {
- let monitor = Main.layoutManager.primaryMonitor;
- this.actor.width = monitor.width;
- this.actor.set_position(monitor.x, monitor.y + monitor.height - this.actor.height);
- },
-
_updateKeyboardAnchor: function() {
if (!Main.keyboard.actor)
return;
@@ -1026,7 +1088,7 @@ const WindowList = new Lang.Class({
},
_addApp: function(app) {
- let button = new AppButton(app);
+ let button = new AppButton(app, this._perMonitor, this._monitor.index);
this._windowList.layout_manager.pack(button.actor,
true, true, true,
Clutter.BoxAlignment.START,
@@ -1059,7 +1121,7 @@ const WindowList = new Lang.Class({
return;
}
- let button = new WindowButton(win);
+ let button = new WindowButton(win, this._perMonitor, this._monitor.index);
this._windowList.layout_manager.pack(button.actor,
true, true, true,
Clutter.BoxAlignment.START,
@@ -1168,6 +1230,8 @@ const WindowList = new Lang.Class({
},
_onDestroy: function() {
+ this._workspaceSettings.disconnect(this._workspacesOnlyOnPrimaryChangedId);
+
this._workspaceIndicator.destroy();
Main.ctrlAltTabManager.removeGroup(this.actor);
@@ -1175,9 +1239,6 @@ const WindowList = new Lang.Class({
this._appSystem.disconnect(this._appStateChangedId);
this._appStateChangedId = 0;
- Main.layoutManager.disconnect(this._monitorsChangedId);
- this._monitorsChangedId = 0;
-
Main.layoutManager.disconnect(this._keyboardVisiblechangedId);
this._keyboardVisiblechangedId = 0;
@@ -1224,34 +1285,69 @@ const Extension = new Lang.Class({
Name: 'Extension',
_init: function() {
- this._windowList = null;
+ this._windowLists = null;
this._injections = {};
},
enable: function() {
- this._windowList = new WindowList();
+ this._windowLists = [];
this._injections['_trayDwellTimeout'] =
MessageTray.MessageTray.prototype._trayDwellTimeout;
MessageTray.MessageTray.prototype._trayDwellTimeout = function() {
return false;
};
+
+ 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._windowList)
+ if (!this._windowLists)
return;
- this._windowList.actor.hide();
- this._windowList.actor.destroy();
- this._windowList = null;
+ 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;
for (let prop in this._injections)
MessageTray.MessageTray.prototype[prop] = this._injections[prop];
},
- windowListContains: function(actor) {
- return this._windowList.actor.contains(actor);
+ someWindowListContains: function(actor) {
+ return this._windowLists.some(function(windowList) {
+ return windowList.actor.contains(actor);
+ });
}
});
diff --git a/extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in b/extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in
index d5bbdf4e..f736e363 100644
--- a/extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in
+++ b/extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in
@@ -15,5 +15,13 @@
window list. Possible values are "never", "auto" and "always".
+
+ false
+ <_summary>Show the window list on all monitors
+ <_description>
+ Whether to show the window list on all connected monitors or
+ only on the primary one.
+
+
diff --git a/extensions/window-list/prefs.js b/extensions/window-list/prefs.js
index 3db09c30..dc200656 100644
--- a/extensions/window-list/prefs.js
+++ b/extensions/window-list/prefs.js
@@ -1,5 +1,6 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
+const Gio = imports.gi.Gio;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
@@ -19,25 +20,25 @@ function init() {
const WindowListPrefsWidget = new GObject.Class({
Name: 'WindowList.Prefs.Widget',
GTypeName: 'WindowListPrefsWidget',
- Extends: Gtk.Frame,
+ Extends: Gtk.Grid,
_init: function(params) {
this.parent(params);
- this.shadow_type = Gtk.ShadowType.NONE;
this.margin = 24;
+ this.row_spacing = 6;
+ this.orientation = Gtk.Orientation.VERTICAL;
- let title = '' + _("Window Grouping") + '';
- let titleLabel = new Gtk.Label({ use_markup: true, label: title });
- this.set_label_widget(titleLabel);
+ let groupingLabel = '' + _("Window Grouping") + '';
+ this.add(new Gtk.Label({ label: groupingLabel, use_markup: true,
+ halign: Gtk.Align.START }));
let align = new Gtk.Alignment({ left_padding: 12 });
this.add(align);
let grid = new Gtk.Grid({ orientation: Gtk.Orientation.VERTICAL,
row_spacing: 6,
- column_spacing: 6,
- margin_top: 6 });
+ column_spacing: 6 });
align.add(grid);
this._settings = Convenience.getSettings();
@@ -70,6 +71,11 @@ const WindowListPrefsWidget = new GObject.Class({
this._settings.set_string('grouping-mode', mode);
}));
}
+
+ let check = new Gtk.CheckButton({ label: _("Show on all monitors"),
+ margin_top: 6 });
+ this._settings.bind('show-on-all-monitors', check, 'active', Gio.SettingsBindFlags.DEFAULT);
+ this.add(check);
}
});