diff --git a/Makefile b/Makefile
index c7658f2..2c4be07 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
UUID = dash-to-panel@jderose9.github.com
BASE_MODULES = extension.js stylesheet.css metadata.json COPYING README.md
-EXTRA_MODULES = convenience.js panel.js taskbar.js secondaryMenu.js windowPreview.js prefs.js Settings.ui
+EXTRA_MODULES = convenience.js panel.js overview.js taskbar.js secondaryMenu.js windowPreview.js prefs.js Settings.ui
#EXTRA_MEDIA = logo.svg
TOLOCALIZE = prefs.js
MSGSRC = $(wildcard po/*.po)
diff --git a/README.md b/README.md
index cc1812c..89a612e 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# Dash to Panel

-## An icon-only taskbar for the GNOME Shell
-An icon-only taskbar for the Gnome Shell. This extension moves the dash into the gnome main panel (top bar) to behave as an icon-only task manager similar to that found in KDE Plasma and Windows 7+.
+## An icon taskbar for the GNOME Shell
+An icon taskbar for the Gnome Shell. This extension moves the dash into the gnome main panel (top bar) to behave as an icon-only task manager similar to that found in KDE Plasma and Windows 7+.
## Installation from source
@@ -16,18 +16,6 @@ make install
to install the extension in your home directory. A Shell reload is required Alt+F2 r Enter and the extension has to be enabled with *gnome-tweak-tool* or with *dconf*.
-## TODO
-- configure tray item spacing
-- isolate workspaces
-- add "show desktop" button
-- replace activities (overview) button text with an icon
-- reorder "activities" and open apps view" buttons when both visible
-- allow moving overview hotspot to bottom left
-- allow moving notifications popup
-- disable built-in dash
-
-
-
## Bug Reporting
Bugs should be reported to the Github bug tracker [https://github.com/jderose9/dash-to-panel/issues](https://github.com/jderose9/dash-to-panel/issues).
diff --git a/Settings.ui b/Settings.ui
index a8d4bc3..b9d05dd 100644
--- a/Settings.ui
+++ b/Settings.ui
@@ -722,6 +722,50 @@
+
+
+
diff --git a/convenience.js b/convenience.js
index 501c21c..bb3e704 100644
--- a/convenience.js
+++ b/convenience.js
@@ -166,3 +166,29 @@ const GlobalSignalsHandler = new Lang.Class({
item[0].disconnect(item[1]);
}
});
+
+/**
+ * Manage function injection: both instances and prototype can be overridden
+ * and restored
+ */
+const InjectionsHandler = new Lang.Class({
+ Name: 'Taskbar.InjectionsHandler',
+ Extends: BasicHandler,
+
+ _create: function(item) {
+ let object = item[0];
+ let name = item[1];
+ let injectedFunction = item[2];
+ let original = object[name];
+
+ object[name] = injectedFunction;
+ return [object, name, injectedFunction, original];
+ },
+
+ _remove: function(item) {
+ let object = item[0];
+ let name = item[1];
+ let original = item[3];
+ object[name] = original;
+ }
+});
diff --git a/extension.js b/extension.js
index b19c665..cb79666 100644
--- a/extension.js
+++ b/extension.js
@@ -21,8 +21,10 @@
const Me = imports.misc.extensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;
const Panel = Me.imports.panel;
+const Overview = Me.imports.overview;
let panel;
+let overview;
let settings;
function init() {
@@ -32,12 +34,16 @@ function enable() {
settings = Convenience.getSettings('org.gnome.shell.extensions.dash-to-panel');
panel = new Panel.taskbarPanel(settings);
panel.enable();
+ overview = new Overview.taskbarOverview(settings);
+ overview.enable(panel.taskbar);
}
function disable() {
+ overview.disable();
panel.disable();
settings.run_dispose();
settings = null;
+ overview = null;
panel = null;
}
diff --git a/metadata.json b/metadata.json
index 50315fe..7060294 100644
--- a/metadata.json
+++ b/metadata.json
@@ -2,7 +2,7 @@
"extension-id": "dash-to-panel",
"uuid": "dash-to-panel@jderose9.github.com",
"name": "Dash to Panel",
-"description": "An icon-only taskbar for the Gnome Shell. This extension moves the dash into the gnome main panel (top bar) to behave as an icon-only task manager similar to that found in KDE Plasma and Windows 7+.",
+"description": "An icon taskbar for the Gnome Shell. This extension moves the dash into the gnome main panel (top bar) to behave as an icon-only task manager similar to that found in KDE Plasma and Windows 7+.",
"shell-version": [ "3.20", "3.22" ],
"url": "https://github.com/jderose9/dash-to-panel",
"gettext-domain": "dash-to-panel"
diff --git a/overview.js b/overview.js
new file mode 100644
index 0000000..91265d0
--- /dev/null
+++ b/overview.js
@@ -0,0 +1,133 @@
+/*
+ * This file is part of the Dash-To-Panel extension for Gnome 3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Credits:
+ * This file is based on code from the Dash to Dock extension by micheleg
+ *
+ * Some code was also adapted from the upstream Gnome Shell source code.
+ */
+
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const Convenience = Me.imports.convenience;
+const Lang = imports.lang;
+const Main = imports.ui.main;
+const Shell = imports.gi.Shell;
+
+const taskbarOverview = new Lang.Class({
+ Name: 'TaskBar.Overview',
+
+ _init: function(settings) {
+ this._dtpSettings = settings;
+ },
+
+ enable : function(taskbar) {
+ this.taskbar = taskbar;
+
+ this._injectionsHandler = new Convenience.InjectionsHandler();
+ this._signalsHandler = new Convenience.GlobalSignalsHandler();
+
+ // Hide usual Dash
+ Main.overview._controls.dash.actor.hide();
+
+ // Also set dash width to 1, so it's almost not taken into account by code
+ // calculaing the reserved space in the overview. The reason to keep it at 1 is
+ // to allow its visibility change to trigger an allocaion of the appGrid which
+ // in turn is triggergin the appsIcon spring animation, required when no other
+ // actors has this effect, i.e in horizontal mode and without the workspaceThumnails
+ // 1 static workspace only)
+ Main.overview._controls.dash.actor.set_width(1);
+
+ this._optionalWorkspaceIsolation();
+ this._bindSettingsChanges();
+ },
+
+ disable: function () {
+ Main.overview._controls.dash.actor.show();
+ Main.overview._controls.dash.actor.set_width(-1); //reset default dash size
+ // This force the recalculation of the icon size
+ Main.overview._controls.dash._maxHeight = -1;
+
+ // reset stored icon size to the default dash
+ Main.overview.dashIconSize = Main.overview._controls.dash.iconSize;
+ },
+
+ _bindSettingsChanges: function() {
+ },
+
+ /**
+ * Isolate overview to open new windows for inactive apps
+ */
+ _optionalWorkspaceIsolation: function() {
+
+ let label = 'optionalWorkspaceIsolation';
+
+ this._dtpSettings.connect('changed::isolate-workspaces', Lang.bind(this, function() {
+ this.taskbar.resetAppIcons();
+ if (this._dtpSettings.get_boolean('isolate-workspaces'))
+ Lang.bind(this, enable)();
+ else
+ Lang.bind(this, disable)();
+ }));
+
+ if (this._dtpSettings.get_boolean('isolate-workspaces'))
+ Lang.bind(this, enable)();
+
+ function enable() {
+ this._injectionsHandler.removeWithLabel(label);
+
+ this._injectionsHandler.addWithLabel(label, [
+ Shell.App.prototype,
+ 'activate',
+ IsolatedOverview
+ ]);
+
+ this._signalsHandler.removeWithLabel(label);
+
+ this._signalsHandler.addWithLabel(label, [
+ global.screen,
+ 'restacked',
+ Lang.bind(this.taskbar, this.taskbar._queueRedisplay)
+ ]);
+ this._signalsHandler.addWithLabel(label, [
+ global.window_manager,
+ 'switch-workspace',
+ Lang.bind(this.taskbar, this.taskbar._queueRedisplay)
+ ]);
+ }
+
+ function disable() {
+ this._signalsHandler.removeWithLabel(label);
+ this._injectionsHandler.removeWithLabel(label);
+
+ this._signalsHandler.destroy();
+ this._injectionsHandler.destroy();
+ }
+
+ function IsolatedOverview() {
+ // These lines take care of Nautilus for icons on Desktop
+ let windows = this.get_windows().filter(function(w) {
+ return w.get_workspace().index() == global.screen.get_active_workspace_index();
+ });
+ if (windows.length == 1)
+ if (windows[0].skip_taskbar)
+ return this.open_new_window(-1);
+
+ if (this.is_on_workspace(global.screen.get_active_workspace()))
+ return Main.activateWindow(windows[0]);
+ return this.open_new_window(-1);
+ }
+ }
+});
\ No newline at end of file
diff --git a/prefs.js b/prefs.js
index 2e5d555..a177238 100644
--- a/prefs.js
+++ b/prefs.js
@@ -139,6 +139,10 @@ const Settings = new Lang.Class({
this._builder.get_object('application_button_animation_button'),
'sensitive',
Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('isolate-workspaces',
+ this._builder.get_object('isolate_workspaces_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
this._builder.get_object('click_action_combo').set_active_id(this._settings.get_string('click-action'));
this._builder.get_object('click_action_combo').connect('changed', Lang.bind (this, function(widget) {
diff --git a/schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml b/schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml
index b79e2e1..2ae100c 100644
--- a/schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml
+++ b/schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml
@@ -54,6 +54,11 @@
Animate Show Applications from the desktopAnimate Show Applications from the desktop
+
+ false
+ Provide workspace isolation
+ Dash shows only windows from the currentworkspace
+ trueCustomize click behaviour
@@ -91,16 +96,16 @@
0Tray content sizeSet the size of the tray items. (0 for default)
-
-
- 0
- Left content size
- Set the size of the leftBox items. (0 for default)
-
-
- 8
- App icon margin
- Set the margin for application icons in the embedded dash.
-
+
+
+ 0
+ Left content size
+ Set the size of the leftBox items. (0 for default)
+
+
+ 8
+ App icon margin
+ Set the margin for application icons in the embedded dash.
+
diff --git a/secondaryMenu.js b/secondaryMenu.js
index df6e1d9..dfa2b67 100644
--- a/secondaryMenu.js
+++ b/secondaryMenu.js
@@ -40,7 +40,9 @@ const taskbarSecondaryMenu = new Lang.Class({
Name: 'taskbarSecondaryMenu',
Extends: AppDisplay.AppIconMenu,
- _init: function(source) {
+ _init: function(source, settings) {
+
+ this._dtpSettings = settings;
let side = Taskbar.getPosition();
@@ -65,7 +67,7 @@ const taskbarSecondaryMenu = new Lang.Class({
// quit menu
let app = this._source.app;
- let count = Taskbar.getAppInterestingWindows(app).length;
+ let count = Taskbar.getInterestingWindows(app, this._dtpSettings).length;
if ( count > 0) {
this._appendSeparator();
let quitFromTaskbarMenuText = "";
diff --git a/taskbar.js b/taskbar.js
index 44f1826..6b1f0aa 100644
--- a/taskbar.js
+++ b/taskbar.js
@@ -709,6 +709,14 @@ const taskbar = new Lang.Class({
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
let running = this._appSystem.get_running().sort(this.sortAppsCompareFunction);
+ if (this._dtpSettings.get_boolean('isolate-workspaces')) {
+ // When using isolation, we filter out apps that have no windows in
+ // the current workspace
+ let settings = this._dtpSettings;
+ running = running.filter(function(_app) {
+ return getInterestingWindows(_app, settings).length != 0;
+ });
+ }
let children = this._box.get_children().filter(function(actor) {
return actor.child &&
@@ -1192,7 +1200,7 @@ const taskbarAppIcon = new Lang.Class({
// function) caused the extension to crash.
this._menuManagerWindowPreview = new PopupMenu.PopupMenuManager(this);
- this._windowPreview = new WindowPreview.thumbnailPreviewMenu(this);
+ this._windowPreview = new WindowPreview.thumbnailPreviewMenu(this, this._dtpSettings);
this._windowPreview.connect('open-state-changed', Lang.bind(this, function (menu, isPoppedUp) {
if (!isPoppedUp)
this._onMenuPoppedDown();
@@ -1203,7 +1211,7 @@ const taskbarAppIcon = new Lang.Class({
},
shouldShowTooltip: function() {
- let windows = getAppInterestingWindows(this.app);
+ let windows = getInterestingWindows(this.app, this._dtpSettings);
if (windows.length > 0) {
return false;
} else {
@@ -1272,6 +1280,16 @@ const taskbarAppIcon = new Lang.Class({
},
_updateRunningStyle: function() {
+ // When using workspace isolation, we need to hide the dots of apps with
+ // no windows in the current workspace
+ if (this._dtpSettings.get_boolean('isolate-workspaces')) {
+ if (this.app.state != Shell.AppState.STOPPED
+ && getInterestingWindows(this.app, this._dtpSettings).length != 0)
+ this._dot.show();
+ else
+ this._dot.hide();
+ }
+
this._updateCounterClass();
},
@@ -1281,9 +1299,9 @@ const taskbarAppIcon = new Lang.Class({
this._draggable.fakeRelease();
if (!this._menu) {
- this._menu = new SecondaryMenu.taskbarSecondaryMenu(this);
+ this._menu = new SecondaryMenu.taskbarSecondaryMenu(this, this._dtpSettings);
this._menu.connect('activate-window', Lang.bind(this, function (menu, window) {
- this.activateWindow(window);
+ this.activateWindow(window, this._dtpSettings);
}));
this._menu.connect('open-state-changed', Lang.bind(this, function (menu, isPoppedUp) {
if (!isPoppedUp)
@@ -1379,11 +1397,16 @@ const taskbarAppIcon = new Lang.Class({
buttonAction = this._dtpSettings.get_string('click-action');
}
+ // We check if the app is running, and that the # of windows is > 0 in
+ // case we use workspace isolation,
+ let appIsRunning = this.app.state == Shell.AppState.RUNNING
+ && getInterestingWindows(this.app, this._dtpSettings).length > 0
+
// We customize the action only when the application is already running
- if (this.app.state == Shell.AppState.RUNNING) {
+ if (appIsRunning) {
switch (buttonAction) {
case "RAISE":
- activateAllWindows(this.app);
+ activateAllWindows(this.app, this._dtpSettings);
break;
case "LAUNCH":
@@ -1404,10 +1427,10 @@ const taskbarAppIcon = new Lang.Class({
if (Clutter.EventType.CLUTTER_BUTTON_PRESS)
click_count = event.get_click_count();
let all_windows = (button == 1 && ! modifiers) || click_count > 1;
- minimizeWindow(this.app, all_windows);
+ minimizeWindow(this.app, all_windows, this._dtpSettings);
}
else
- activateAllWindows(this.app);
+ activateAllWindows(this.app, this._dtpSettings);
}
else
this.app.activate();
@@ -1416,9 +1439,9 @@ const taskbarAppIcon = new Lang.Class({
case "CYCLE":
if (!Main.overview._shown){
if (this.app == focusedApp)
- activateNextWindow(this.app, false);
+ activateNextWindow(this.app, false, this._dtpSettings);
else {
- activateFirstWindow(this.app);
+ activateFirstWindow(this.app, this._dtpSettings);
}
}
else
@@ -1427,9 +1450,9 @@ const taskbarAppIcon = new Lang.Class({
case "CYCLE-MIN":
if (!Main.overview._shown){
if (this.app == focusedApp)
- activateNextWindow(this.app, true);
+ activateNextWindow(this.app, true, this._dtpSettings);
else {
- activateFirstWindow(this.app);
+ activateFirstWindow(this.app, this._dtpSettings);
}
}
else
@@ -1437,7 +1460,7 @@ const taskbarAppIcon = new Lang.Class({
break;
case "QUIT":
- closeAllWindows(this.app);
+ closeAllWindows(this.app, this._dtpSettings);
break;
}
}
@@ -1457,7 +1480,7 @@ const taskbarAppIcon = new Lang.Class({
this._dot.set_y_align(Clutter.ActorAlign.END);
let maxN = 4;
- this._nWindows = Math.min(getAppInterestingWindows(this.app).length, maxN);
+ this._nWindows = Math.min(getInterestingWindows(this.app, this._dtpSettings).length, maxN);
for (let i = 1; i <= maxN; i++){
let className = 'running'+i;
@@ -1500,9 +1523,9 @@ const taskbarAppIcon = new Lang.Class({
});
-function minimizeWindow(app, param){
+function minimizeWindow(app, param, settings){
// Param true make all app windows minimize
- let windows = getAppInterestingWindows(app);
+ let windows = getInterestingWindows(app, settings);
let current_workspace = global.screen.get_active_workspace();
for (let i = 0; i < windows.length; i++) {
let w = windows[i];
@@ -1520,11 +1543,11 @@ function minimizeWindow(app, param){
* By default only non minimized windows are activated.
* This activates all windows in the current workspace.
*/
-function activateAllWindows(app){
+function activateAllWindows(app, settings){
// First activate first window so workspace is switched if needed,
// then activate all other app windows in the current workspace.
- let windows = getAppInterestingWindows(app);
+ let windows = getInterestingWindows(app, settings);
let w = windows[0];
Main.activateWindow(w);
let activeWorkspace = global.screen.get_active_workspace_index();
@@ -1542,9 +1565,9 @@ function activateAllWindows(app){
}
}
-function activateFirstWindow(app){
+function activateFirstWindow(app, settings){
- let windows = getAppInterestingWindows(app).sort(function(windowA, windowB) {
+ let windows = getInterestingWindows(app, settings).sort(function(windowA, windowB) {
return windowA.get_stable_sequence() > windowB.get_stable_sequence();
});
@@ -1554,9 +1577,9 @@ function activateFirstWindow(app){
/*
* Activate the next running window for the current application
*/
-function activateNextWindow(app, shouldMinimize){
+function activateNextWindow(app, shouldMinimize, settings){
- let windows = getAppInterestingWindows(app).sort(function(windowA, windowB) {
+ let windows = getInterestingWindows(app, settings).sort(function(windowA, windowB) {
return windowA.get_stable_sequence() > windowB.get_stable_sequence();
});
@@ -1567,22 +1590,20 @@ function activateNextWindow(app, shouldMinimize){
if(i < windows.length - 1)
Main.activateWindow(windows[i + 1]);
else
- shouldMinimize ? minimizeWindow(app, true) : Main.activateWindow(windows[0]);
+ shouldMinimize ? minimizeWindow(app, true, settings) : Main.activateWindow(windows[0]);
break;
}
}
}
-function closeAllWindows(app) {
- let windows = getAppInterestingWindows(app);
+function closeAllWindows(app, settings) {
+ let windows = getInterestingWindows(app, settings);
for (let i = 0; i < windows.length; i++)
windows[i].delete(global.get_current_time());
}
-function getAppInterestingWindows(app) {
- // Filter out unnecessary windows, for instance
- // nautilus desktop window.
+function getAppInterestingWindows(app, settings) {
let windows = app.get_windows().filter(function(w) {
return !w.skip_taskbar;
});
@@ -1590,6 +1611,22 @@ function getAppInterestingWindows(app) {
return windows;
}
+// Filter out unnecessary windows, for instance
+// nautilus desktop window.
+function getInterestingWindows(app, settings) {
+ let windows = app.get_windows().filter(function(w) {
+ return !w.skip_taskbar;
+ });
+
+ // When using workspace isolation, we filter out windows
+ // that are not in the current workspace
+ if (settings.get_boolean('isolate-workspaces'))
+ windows = windows.filter(function(w) {
+ return w.get_workspace().index() == global.screen.get_active_workspace_index();
+ });
+
+ return windows;
+}
/*
* This is a copy of the same function in utils.js, but also adjust horizontal scrolling
diff --git a/windowPreview.js b/windowPreview.js
index 5a85176..f3f849c 100644
--- a/windowPreview.js
+++ b/windowPreview.js
@@ -44,7 +44,9 @@ const thumbnailPreviewMenu = new Lang.Class({
Name: 'thumbnailPreviewMenu',
Extends: PopupMenu.PopupMenu,
- _init: function(source) {
+ _init: function(source, settings) {
+
+ this._dtpSettings = settings;
let side = Taskbar.getPosition();
@@ -82,7 +84,7 @@ const thumbnailPreviewMenu = new Lang.Class({
this._boxPointer._arrowSide = side;
this._boxPointer._userArrowSide = side;
- this._previewBox = new thumbnailPreviewList(this._app, THUMBNAIL_HEIGHT);
+ this._previewBox = new thumbnailPreviewList(this._app, this._dtpSettings);
this.addMenuItem(this._previewBox);
},
@@ -97,7 +99,7 @@ const thumbnailPreviewMenu = new Lang.Class({
},
popup: function() {
- let windows = Taskbar.getAppInterestingWindows(this._app);
+ let windows = Taskbar.getInterestingWindows(this._app, this._dtpSettings);
if (windows.length > 0) {
this._redisplay();
this.open();
@@ -408,7 +410,9 @@ const thumbnailPreviewList = new Lang.Class({
Name: 'thumbnailPreviewList',
Extends: PopupMenu.PopupMenuSection,
- _init: function(app) {
+ _init: function(app, settings) {
+ this._dtpSettings = settings;
+
this.parent();
this._ensurePreviewVisibilityTimeoutId = 0;
@@ -561,7 +565,7 @@ const thumbnailPreviewList = new Lang.Class({
},
_redisplay: function () {
- let windows = Taskbar.getAppInterestingWindows(this.app).sort(this.sortWindowsCompareFunction);
+ let windows = Taskbar.getInterestingWindows(this.app, this._dtpSettings).sort(this.sortWindowsCompareFunction);
let children = this.box.get_children().filter(function(actor) {
return actor._delegate.window && actor._delegate.preview;
});