forked from pool/gnome-shell-extensions
Dominique Leuenberger
f2b6886886
- Update gse-sle-classic-ext.patch, make the bottom panel background of SLE-Classic "black" in the Overview mode (bsc#1064377). OBS-URL: https://build.opensuse.org/request/show/537511 OBS-URL: https://build.opensuse.org/package/show/GNOME:Factory/gnome-shell-extensions?expand=0&rev=147
954 lines
38 KiB
Diff
954 lines
38 KiB
Diff
diff --git a/data/gnome-classic.css b/data/gnome-classic.css
|
|
index 5331df9..dcda0db 100644
|
|
--- a/data/gnome-classic.css
|
|
+++ b/data/gnome-classic.css
|
|
@@ -500,7 +500,12 @@ StScrollBar {
|
|
color: rgba(46, 52, 54, 0.5); }
|
|
.popup-menu.panel-menu {
|
|
-boxpointer-gap: 4px;
|
|
- margin-bottom: 1.75em; }
|
|
+ /* TODO was 1.75em, no idea of its use */
|
|
+ /* NOTE: the following creates an ugly gap between menu and its source actor
|
|
+when the PanelMenu's source actor is at the bottom. Preferrably for bottom menu,
|
|
+`margin-top` might be a better choice. However, since we have no idea about its
|
|
+use so reset to 0 for now. */
|
|
+ margin-bottom: 0em; }
|
|
|
|
.popup-menu-ornament {
|
|
text-align: right;
|
|
@@ -691,8 +696,7 @@ StScrollBar {
|
|
margin-right: 4px; }
|
|
#panel .panel-button .system-status-icon,
|
|
#panel .panel-button .app-menu-icon > StIcon,
|
|
- #panel .panel-button .popup-menu-arrow {
|
|
- icon-shadow: 0px 1px 2px rgba(0, 0, 0, 0.9); }
|
|
+ #panel .panel-button .popup-menu-arrow {}
|
|
#panel .panel-button:hover {
|
|
color: #454f52;
|
|
text-shadow: 0px 1px 6px black; }
|
|
@@ -901,6 +905,7 @@ StScrollBar {
|
|
width: 31.5em; }
|
|
|
|
.message-list-clear-button.button {
|
|
+ color: black;
|
|
background-color: transparent;
|
|
margin: 1.5em 1.5em 0; }
|
|
.message-list-clear-button.button:hover, .message-list-clear-button.button:focus {
|
|
@@ -924,7 +929,6 @@ StScrollBar {
|
|
padding: 0.68em 0.68em 0.68em 0.2em; }
|
|
|
|
.message-icon-bin > StIcon {
|
|
- color: black;
|
|
icon-size: 1.09em;
|
|
-st-icon-style: symbolic; }
|
|
|
|
@@ -941,11 +945,9 @@ StScrollBar {
|
|
.message-secondary-bin > StIcon {
|
|
icon-size: 1.09em; }
|
|
|
|
-.message-title {
|
|
- color: #222728; }
|
|
+.message-title {}
|
|
|
|
.message-content {
|
|
- color: black;
|
|
padding: 10px; }
|
|
|
|
.message-media-control {
|
|
@@ -1882,8 +1884,6 @@ StScrollBar {
|
|
#panel {
|
|
font-weight: normal;
|
|
background-color: #ededed !important;
|
|
- background-gradient-direction: vertical;
|
|
- background-gradient-end: #e0e0e0;
|
|
border-top-color: #666;
|
|
/* we don't support non-uniform border-colors and
|
|
use the top border color for any border, so we
|
|
@@ -1892,12 +1892,12 @@ StScrollBar {
|
|
border-bottom: 1px solid #666;
|
|
app-icon-bottom-clip: 0px; }
|
|
#panel:overview {
|
|
- background-color: #000;
|
|
- background-gradient-end: #000;
|
|
- border-top-color: #000;
|
|
- border-bottom: 1px solid #000; }
|
|
+ background-color: #000 !important;
|
|
+ background-gradient-end: #000 !important;
|
|
+ border-top-color: #000 !important;
|
|
+ border-bottom: 1px solid #000 !important; }
|
|
#panel:overview .panel-button {
|
|
- color: #fff; }
|
|
+ color: #fff !important; }
|
|
#panel .panel-button {
|
|
-natural-hpadding: 8px;
|
|
-minimum-hpadding: 4px;
|
|
@@ -1912,8 +1912,6 @@ StScrollBar {
|
|
icon-shadow: none; }
|
|
#panel .panel-button:hover {
|
|
text-shadow: none; }
|
|
- #panel .panel-button:hover .system-status-icon {
|
|
- icon-shadow: none; }
|
|
#panel .panel-button .app-menu-icon {
|
|
width: 0;
|
|
height: 0;
|
|
@@ -1924,12 +1922,10 @@ StScrollBar {
|
|
#panel .panel-corner:focus {
|
|
-panel-corner-radius: 0; }
|
|
#panel.lock-screen, #panel.unlock-screen, #panel.login-screen {
|
|
- background-color: rgba(46, 52, 54, 0.5);
|
|
- background-gradient-start: rgba(46, 52, 54, 0.5);
|
|
- background-gradient-end: rgba(46, 52, 54, 0.5);
|
|
+ background-color: transparent !important;
|
|
border-bottom: none; }
|
|
#panel.lock-screen .panel-button, #panel.unlock-screen .panel-button, #panel.login-screen .panel-button {
|
|
- color: #eeeeec; }
|
|
+ color: #eeeeec !important; }
|
|
#panel .popup-menu-arrow {
|
|
width: 0;
|
|
height: 0; }
|
|
diff --git a/extensions/window-list/classic.css b/extensions/window-list/classic.css
|
|
index f3c44a3..8096d27 100644
|
|
--- a/extensions/window-list/classic.css
|
|
+++ b/extensions/window-list/classic.css
|
|
@@ -6,7 +6,7 @@
|
|
height: 2.25em ;
|
|
}
|
|
|
|
- .bottom-panel .window-button > StWidget {
|
|
+ .window-button > StWidget {
|
|
background-gradient-drection: vertical;
|
|
background-color: #fff;
|
|
background-gradient-start: #fff;
|
|
@@ -22,25 +22,25 @@
|
|
text-shadow: 0 0 transparent;
|
|
}
|
|
|
|
- .bottom-panel .window-button:hover > StWidget {
|
|
+ .window-button:hover > StWidget {
|
|
background-color: #f9f9f9;
|
|
}
|
|
|
|
- .bottom-panel .window-button:active > StWidget,
|
|
- .bottom-panel .window-button:focus > StWidget {
|
|
+ .window-button:active > StWidget,
|
|
+ .window-button:focus > StWidget {
|
|
box-shadow: inset 1px 1px 2px rgba(0,0,0,0.5);
|
|
}
|
|
|
|
- .bottom-panel .window-button.focused > StWidget {
|
|
+ .window-button.focused > StWidget {
|
|
background-color: #ddd;
|
|
box-shadow: inset 1px 1px 1px rgba(0,0,0,0.5);
|
|
}
|
|
|
|
- .bottom-panel .window-button.focused:hover > StWidget {
|
|
+ .window-button.focused:hover > StWidget {
|
|
background-color: #e9e9e9;
|
|
}
|
|
|
|
- .bottom-panel .window-button.minimized > StWidget {
|
|
+ .window-button.minimized > StWidget {
|
|
color: #888;
|
|
box-shadow: inset -1px -1px 1px rgba(0,0,0,0.5);
|
|
}
|
|
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
|
|
index f01b872..6bd8709 100644
|
|
--- a/extensions/window-list/extension.js
|
|
+++ b/extensions/window-list/extension.js
|
|
@@ -22,6 +22,8 @@ const _ = Gettext.gettext;
|
|
const ICON_TEXTURE_SIZE = 24;
|
|
const DND_ACTIVATE_TIMEOUT = 500;
|
|
|
|
+const SLEClassicExt = imports.ui.SLEClassicExt;
|
|
+
|
|
const GroupingMode = {
|
|
NEVER: 0,
|
|
AUTO: 1,
|
|
@@ -64,6 +66,9 @@ function _getAppStableSequence(app) {
|
|
}
|
|
|
|
|
|
+// TODO: not critical, but with `gnome-shell -r` there are `St-CRITICAL` errors
|
|
+// shown in stdout, 3 warnings for each existing window. Wrong initialization
|
|
+// order?
|
|
const WindowContextMenu = new Lang.Class({
|
|
Name: 'WindowContextMenu',
|
|
Extends: PopupMenu.PopupMenu,
|
|
@@ -113,6 +118,9 @@ const WindowContextMenu = new Lang.Class({
|
|
}));
|
|
this.addMenuItem(this._closeItem);
|
|
|
|
+ // NOTE add `-boxpointer-gap` to the menu, to align with `PanelMenu`,
|
|
+ // totally optional.
|
|
+ this.actor.add_style_class_name('bottom-panel-menu');
|
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
|
|
|
this.connect('open-state-changed', () => {
|
|
@@ -678,6 +686,8 @@ const WorkspaceIndicator = new Lang.Class({
|
|
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');
|
|
+ // NOTE: add `-boxpointer-gap`, totally optional
|
|
+ this.menu.actor.add_style_class_name('bottom-panel-menu');
|
|
this.menu.actor.remove_style_class_name('panel-menu');
|
|
|
|
let container = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
|
@@ -782,88 +792,52 @@ const WorkspaceIndicator = new Lang.Class({
|
|
}
|
|
});
|
|
|
|
-const WindowList = new Lang.Class({
|
|
- Name: 'WindowList',
|
|
+// NOTE: call `initializeWindowList` explicitly to finish initialization.
|
|
+const PureWinList = new Lang.Class({
|
|
+ Name: 'PureWinList',
|
|
|
|
- _init: function(perMonitor, monitor) {
|
|
+ _init: function(perMonitor, monitor, maxWidthFunc) {
|
|
+ // NOTE: in SLE Classic `PureWinList` will NOT use any multiple monitor
|
|
+ // support, the following is kept for use in GNOME Classic as we try to
|
|
+ // unify code for two sides.
|
|
this._perMonitor = perMonitor;
|
|
this._monitor = monitor;
|
|
+ // NOTE: callback function given by the employer of this PureWinList.
|
|
+ // Since PureWinList can be used various widgets hierarchy, we have to
|
|
+ // leave the calculation of max available width to the employer.
|
|
+ this._getMaxWindowListWidth = maxWidthFunc;
|
|
|
|
- this.actor = new St.Widget({ name: 'panel',
|
|
- style_class: 'bottom-panel solid',
|
|
+ let layout = new Clutter.BoxLayout({ homogeneous: true });
|
|
+ this.actor = new St.Widget({ style_class: 'window-list',
|
|
reactive: true,
|
|
- track_hover: true,
|
|
- layout_manager: new Clutter.BinLayout()});
|
|
+ layout_manager: layout,
|
|
+ x_align: Clutter.ActorAlign.START,
|
|
+ x_expand: true,
|
|
+ y_expand: true });
|
|
+
|
|
+ this.actor.connect('style-changed', Lang.bind(this, function() {
|
|
+ let node = this.actor.get_theme_node();
|
|
+ let spacing = node.get_length('spacing');
|
|
+ this.actor.layout_manager.spacing = spacing;
|
|
+ }));
|
|
+ this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
|
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._settings = Convenience.getSettings();
|
|
+
|
|
+ // Grouping
|
|
+ this._groupingModeChangedId =
|
|
+ this._settings.connect('changed::grouping-mode',
|
|
+ Lang.bind(this, this._groupingModeChanged));
|
|
+ this._grouped = undefined;
|
|
+ // NOTE: do NOT `_checkGrouping` here
|
|
|
|
+ // workspace related
|
|
this._workspaceSignals = new Map();
|
|
this._nWorkspacesChangedId =
|
|
global.screen.connect('notify::n-workspaces',
|
|
@@ -874,42 +848,18 @@ const WindowList = new Lang.Class({
|
|
global.window_manager.connect('switch-workspace',
|
|
Lang.bind(this, this._checkGrouping));
|
|
|
|
+ // Hide and Show with Overview
|
|
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.actor.show();
|
|
}));
|
|
|
|
- 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();
|
|
+ this._groupingMode = this._settings.get_enum('grouping-mode');
|
|
},
|
|
|
|
_getDynamicWorkspacesSettings: function() {
|
|
@@ -918,77 +868,77 @@ const WindowList = new Lang.Class({
|
|
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;
|
|
+ // NOTE: an API for parent panel to refresh the window list. This is
|
|
+ // necessary as window/app buttons require its parents having allocation and
|
|
+ // positioning *completed* before being properly allocated and positioned
|
|
+ initializeWindowList: function() {
|
|
+ this._groupingModeChanged();
|
|
},
|
|
|
|
+ // NOTE: support scrolling in window list s.t. scrolling activate window
|
|
+ // buttons sequentially.
|
|
+ //
|
|
+ // *Code is rewritten*.
|
|
_onScrollEvent: function(actor, event) {
|
|
let direction = event.get_scroll_direction();
|
|
let diff = 0;
|
|
- if (direction == Clutter.ScrollDirection.DOWN)
|
|
+ if (direction === Clutter.ScrollDirection.DOWN)
|
|
diff = 1;
|
|
- else if (direction == Clutter.ScrollDirection.UP)
|
|
+ else if (direction === Clutter.ScrollDirection.UP)
|
|
diff = -1;
|
|
else
|
|
return;
|
|
|
|
- let children = this._windowList.get_children().map(function(actor) {
|
|
+ let buttons = this.actor.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;
|
|
+ let totalBtnNum = buttons.length;
|
|
+
|
|
+ let activeBtnIdx = 0
|
|
+ for (let i = 0; i < totalBtnNum; i++) {
|
|
+ if (buttons[i].active) {
|
|
+ activeBtnIdx = i
|
|
break;
|
|
}
|
|
}
|
|
|
|
- active = Math.max(0, Math.min(active + diff, children.length-1));
|
|
- children[active].activate();
|
|
- },
|
|
+ // NOTE: bound by `0` and `totalBtnNum - 1`, no wrapping for
|
|
+ // scrolling.
|
|
+ activeBtnIdx = activeBtnIdx + diff;
|
|
+ let noScrollActionNeeded = ( (activeBtnIdx < 0) || (activeBtnIdx >= totalBtnNum) );
|
|
+ if (noScrollActionNeeded)
|
|
+ return;
|
|
|
|
- _updatePosition: function() {
|
|
- this.actor.set_position(this._monitor.x,
|
|
- this._monitor.y + this._monitor.height - this.actor.height);
|
|
+ // TODO: no need to call `deactivate` for old `active button` ?
|
|
+ buttons[activeBtnIdx].activate();
|
|
},
|
|
|
|
- _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');
|
|
+ _onAppStateChanged: function(appSys, app) {
|
|
+ if (!this._grouped)
|
|
+ return;
|
|
|
|
- this._workspaceIndicator.actor.visible = hasWorkspaces && workspacesOnMonitor;
|
|
+ if (app.state == Shell.AppState.RUNNING)
|
|
+ this._addApp(app);
|
|
+ else if (app.state == Shell.AppState.STOPPED)
|
|
+ this._removeApp(app);
|
|
},
|
|
|
|
- _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;
|
|
+ _addApp: function(app) {
|
|
+ let button = new AppButton(app, this._perMonitor, this._monitor.index);
|
|
+ this.actor.layout_manager.pack(button.actor,
|
|
+ true, true, true,
|
|
+ Clutter.BoxAlignment.START,
|
|
+ Clutter.BoxAlignment.START)
|
|
},
|
|
|
|
- _getMaxWindowListWidth: function() {
|
|
- let indicatorsBox = this._workspaceIndicator.actor.get_parent();
|
|
- return this.actor.width - indicatorsBox.get_preferred_width(-1)[1];
|
|
+ _removeApp: function(app) {
|
|
+ let children = this.actor.get_children();
|
|
+ for (let i = 0; i < children.length; i++) {
|
|
+ if (children[i]._delegate.app === app) {
|
|
+ children[i].destroy();
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
},
|
|
|
|
_groupingModeChanged: function() {
|
|
@@ -997,7 +947,7 @@ const WindowList = new Lang.Class({
|
|
if (this._groupingMode == GroupingMode.AUTO) {
|
|
this._checkGrouping();
|
|
} else {
|
|
- this._grouped = this._groupingMode == GroupingMode.ALWAYS;
|
|
+ this._grouped = ( this._groupingMode === GroupingMode.ALWAYS );
|
|
this._populateWindowList();
|
|
}
|
|
},
|
|
@@ -1006,6 +956,26 @@ const WindowList = new Lang.Class({
|
|
if (this._groupingMode != GroupingMode.AUTO)
|
|
return;
|
|
|
|
+ // TODO `_getMaxWindowListWidth` is known to depend on parent
|
|
+ // conditions. However the following call seems to get the right parent
|
|
+ // value. So an option to avoid timing issue is to use the following
|
|
+ // callback.
|
|
+ //
|
|
+ // this.actor.connect('allocation-changed', () => {
|
|
+ // log('parent width: ' + this.actor.get_parent().width);
|
|
+ // });
|
|
+ //
|
|
+ // The legitimacy can be explained in the (guessed) algorithm of
|
|
+ // allocation: Bubble up then propagate down (like DOM Events?). In
|
|
+ // details: changes that would alter the geometric properties of a
|
|
+ // widget would trigger a re-allocation to its parent AFTER some initial
|
|
+ // allocation calculation of its own (for queries like
|
|
+ // `_get_preferred_width` work for its parents). The `re-allocation`
|
|
+ // would bubble up the widget hierarchy till one widget stops it (e.g. a
|
|
+ // widget that has fixed size and absolute positioning and thus it does
|
|
+ // not need to send re-allocation request up.). Then the re-allocation
|
|
+ // signal is sent down to its origin. (downward propagation is necessary
|
|
+ // as much of the positioning and allocation depends on one's parent)
|
|
let maxWidth = this._getMaxWindowListWidth();
|
|
let natWidth = this._getPreferredUngroupedWindowListWidth();
|
|
|
|
@@ -1017,7 +987,7 @@ const WindowList = new Lang.Class({
|
|
},
|
|
|
|
_populateWindowList: function() {
|
|
- this._windowList.destroy_all_children();
|
|
+ this.actor.destroy_all_children();
|
|
|
|
if (!this._grouped) {
|
|
let windows = global.get_window_actors().sort(
|
|
@@ -1038,42 +1008,8 @@ const WindowList = new Lang.Class({
|
|
}
|
|
},
|
|
|
|
- _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;
|
|
- }
|
|
- }
|
|
- },
|
|
-
|
|
+ // NOTE the `ws` params in the following two are not used (necessarily be
|
|
+ // here as the event handler Interface dictates).
|
|
_onWindowAdded: function(ws, win) {
|
|
if (win.skip_taskbar)
|
|
return;
|
|
@@ -1084,30 +1020,32 @@ const WindowList = new Lang.Class({
|
|
if (this._grouped)
|
|
return;
|
|
|
|
- let children = this._windowList.get_children();
|
|
+ let children = this.actor.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);
|
|
+ this.actor.layout_manager.pack(button.actor,
|
|
+ true, true, true,
|
|
+ Clutter.BoxAlignment.START,
|
|
+ Clutter.BoxAlignment.START);
|
|
},
|
|
|
|
_onWindowRemoved: function(ws, win) {
|
|
if (this._grouped)
|
|
this._checkGrouping();
|
|
|
|
+ // NOTE: if it's still grouped after `checking`, do nothing, window
|
|
+ // removal is managed by `AppButton` anyway.
|
|
if (this._grouped)
|
|
return;
|
|
|
|
if (win.get_compositor_private())
|
|
return; // not actually removed, just moved to another workspace
|
|
|
|
- let children = this._windowList.get_children();
|
|
+ let children = this.actor.get_children();
|
|
for (let i = 0; i < children.length; i++) {
|
|
if (children[i]._delegate.metaWindow == win) {
|
|
children[i].destroy();
|
|
@@ -1116,6 +1054,28 @@ const WindowList = new Lang.Class({
|
|
}
|
|
},
|
|
|
|
+ _getPreferredUngroupedWindowListWidth: function() {
|
|
+ if (this.actor.get_n_children() == 0)
|
|
+ return this.actor.get_preferred_width(-1)[1];
|
|
+
|
|
+ let children = this.actor.get_children();
|
|
+ let [, childWidth] = children[0].get_preferred_width(-1);
|
|
+ let spacing = this.actor.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.actor.get_preferred_width(-1)[1];
|
|
+
|
|
+ return nWindows * childWidth + (nWindows - 1) * spacing;
|
|
+ },
|
|
+
|
|
_onWorkspacesChanged: function() {
|
|
let numWorkspaces = global.screen.n_workspaces;
|
|
for (let i = 0; i < numWorkspaces; i++) {
|
|
@@ -1132,8 +1092,6 @@ const WindowList = new Lang.Class({
|
|
Lang.bind(this, this._onWindowRemoved));
|
|
this._workspaceSignals.set(workspace, signals);
|
|
}
|
|
-
|
|
- this._updateWorkspaceIndicatorVisibility();
|
|
},
|
|
|
|
_disconnectWorkspaceSignals: function() {
|
|
@@ -1147,6 +1105,157 @@ const WindowList = new Lang.Class({
|
|
}
|
|
},
|
|
|
|
+ _onDestroy: function() {
|
|
+ Main.overview.disconnect(this._overviewHidingId);
|
|
+ this._overviewHidingId = 0;
|
|
+ Main.overview.disconnect(this._overviewShowingId);
|
|
+ this._overviewShowingId = 0;
|
|
+
|
|
+ global.screen.disconnect(this._nWorkspacesChangedId);
|
|
+ this._nWorkspacesChangedId = 0;
|
|
+ global.window_manager.disconnect(this._switchWorkspaceId);
|
|
+ this._switchWorkspaceId = 0;
|
|
+ this._disconnectWorkspaceSignals();
|
|
+
|
|
+ this._settings.disconnect(this._groupingModeChangedId);
|
|
+ this._groupingModeChangedId = 0;
|
|
+
|
|
+ this._appSystem.disconnect(this._appStateChangedId);
|
|
+ this._appStateChangedId = 0;
|
|
+
|
|
+ let windows = global.get_window_actors();
|
|
+ for (let i = 0; i < windows.length; i++)
|
|
+ windows[i].metaWindow.set_icon_geometry(null);
|
|
+ }
|
|
+});
|
|
+
|
|
+// NOTE: the following so-called `WindowList` is actually a bottom panel. The
|
|
+// "list of windows" part is going to be factored out into `PureWinList`,
|
|
+// specially designed to adapt this extension for use with `SLE-Classic` mode.
|
|
+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 maxWinListWidthFunc = () => {
|
|
+ let indicatorsBox = this._workspaceIndicator.actor.get_parent();
|
|
+ return this.actor.width - indicatorsBox.get_preferred_width(-1)[1];
|
|
+ };
|
|
+ this._windowList = new PureWinList(perMonitor, monitor, maxWinListWidthFunc);
|
|
+ box.add(this._windowList.actor, { expand: true });
|
|
+ let _windowListInitId = this.actor.connect('allocation-changed',
|
|
+ () => {
|
|
+ this._windowList.initializeWindowList();
|
|
+ this.actor.disconnect(_windowListInitId);
|
|
+ });
|
|
+
|
|
+ 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._workspaceSettings = this._getWorkspaceSettings();
|
|
+ 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);
|
|
+
|
|
+ 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._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._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;
|
|
+ },
|
|
+
|
|
+ _getWorkspaceSettings: function() {
|
|
+ let settings = global.get_overrides_settings();
|
|
+ if (settings.list_keys().indexOf('workspaces-only-on-primary') > -1)
|
|
+ return settings;
|
|
+ return new Gio.Settings({ schema_id: 'org.gnome.mutter' });
|
|
+ },
|
|
+
|
|
+ _updatePosition: function() {
|
|
+ this.actor.set_position(this._monitor.x,
|
|
+ this._monitor.y + this._monitor.height - this.actor.height);
|
|
+ },
|
|
+
|
|
+ _updateWorkspaceIndicatorVisibility: function() {
|
|
+ this._workspaceIndicator.actor.visible =
|
|
+ this._monitor == Main.layoutManager.primaryMonitor ||
|
|
+ !this._workspaceSettings.get_boolean('workspaces-only-on-primary');
|
|
+ },
|
|
+
|
|
+
|
|
+ _updateKeyboardAnchor: function() {
|
|
+ if (!Main.keyboard.actor)
|
|
+ return;
|
|
+
|
|
+ let anchorY = Main.overview.visible ? 0 : this.actor.height;
|
|
+ Main.keyboard.actor.anchor_y = anchorY;
|
|
+ },
|
|
+
|
|
_onDragBegin: function() {
|
|
DND.addDragMonitor(this._dragMonitor);
|
|
},
|
|
@@ -1202,28 +1311,16 @@ const WindowList = new Lang.Class({
|
|
|
|
_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);
|
|
|
|
@@ -1231,12 +1328,6 @@ const WindowList = new Lang.Class({
|
|
|
|
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);
|
|
}
|
|
});
|
|
|
|
@@ -1245,7 +1336,6 @@ const Extension = new Lang.Class({
|
|
|
|
_init: function() {
|
|
this._windowLists = null;
|
|
- this._injections = {};
|
|
},
|
|
|
|
enable: function() {
|
|
@@ -1301,6 +1391,73 @@ const Extension = new Lang.Class({
|
|
}
|
|
});
|
|
|
|
+const SCExtension = new Lang.Class({
|
|
+ Name: 'SCExtension',
|
|
+ _init: function() {
|
|
+ this._pureWinList = null;
|
|
+ },
|
|
+
|
|
+ enable: function() {
|
|
+ // NOTE For SLE Classic, a window list is shown on Main panel ONLY
|
|
+ let showOnAllMonitors = false;
|
|
+ // NOTE Use a guessed value passed to `PureWinList` as `checkGrouping`
|
|
+ // is run at a time the allocation of the panel boxes might not complete
|
|
+ // yet (and thus we get almost random width value). The other options
|
|
+ // are to duplicate the centerbox width calculation or change the order
|
|
+ // of grouping check code (way more complicated).
|
|
+ //
|
|
+ // This value is guessed *conservatively*. Further this value is used by
|
|
+ // AUTO grouping only.
|
|
+ //
|
|
+ // NOTE: no Promise is available
|
|
+ let panelCenterBoxWidth = Main.panel.actor.width * 0.8;
|
|
+
|
|
+ this._pureWinList = new PureWinList(showOnAllMonitors,
|
|
+ Main.layoutManager.primaryMonitor,
|
|
+ () => panelCenterBoxWidth );
|
|
+ Main.panel._centerBox.add(this._pureWinList.actor, {expand: true});
|
|
+ let _winListRefreshId = Main.panel._centerBox.connect(
|
|
+ 'allocation-changed',
|
|
+ () => {
|
|
+ if (this._pureWinList == null)
|
|
+ return;
|
|
+
|
|
+ this._pureWinList.initializeWindowList();
|
|
+ Main.panel._centerBox.disconnect(_winListRefreshId);
|
|
+ });
|
|
+ // NOTE: IMO, no need to rebuild `_pureWinList` when monitors changed.
|
|
+ // No need for `showOnAllMonitors` change either even this option
|
|
+ // changes.
|
|
+
|
|
+ this._heightChangeId =
|
|
+ Main.panel.actor.connect('notify::height', Lang.bind(this, function() {
|
|
+ Main.layoutManager.panelBox.set_position(this._pureWinList._monitor.x,
|
|
+ this._pureWinList._monitor.y + this._pureWinList._monitor.height - Main.panel.actor.height);
|
|
+ }));
|
|
+ },
|
|
+
|
|
+ disable: function() {
|
|
+ if (!this._pureWinList)
|
|
+ return;
|
|
+
|
|
+ Main.panel.actor.disconnect(this._heightChangeId);
|
|
+ this._pureWinList.actor.hide();
|
|
+ this._pureWinList.actor.destroy();
|
|
+
|
|
+ this._pureWinList = null;
|
|
+ },
|
|
+
|
|
+ // NOTE: this function is used for multiple window list situations, invalid for SCExtension case, let's return false.
|
|
+ someWindowListContains: function(actor) {
|
|
+ return false;
|
|
+ }
|
|
+});
|
|
+
|
|
function init() {
|
|
- return new Extension();
|
|
+ if ( SLEClassicExt.isSLEClassicMode() ){
|
|
+ return new SCExtension();
|
|
+ }
|
|
+ else {
|
|
+ return new Extension();
|
|
+ }
|
|
}
|
|
diff --git a/extensions/window-list/stylesheet.css b/extensions/window-list/stylesheet.css
|
|
index f5285cb..c207078 100644
|
|
--- a/extensions/window-list/stylesheet.css
|
|
+++ b/extensions/window-list/stylesheet.css
|
|
@@ -79,6 +79,10 @@
|
|
border: 1px solid #cccccc;
|
|
}
|
|
|
|
+.bottom-panel-menu {
|
|
+ -boxpointer-gap: 4px;
|
|
+}
|
|
+
|
|
.notification {
|
|
font-weight: normal;
|
|
}
|