From a98b3638fb75d647c80d537aa664caf00b9862f1 Mon Sep 17 00:00:00 2001 From: jderose9 Date: Mon, 2 Jan 2017 20:46:35 -0500 Subject: [PATCH] Pull in workspace isolation and hide dash from d2d --- Makefile | 2 +- README.md | 16 +-- Settings.ui | 44 ++++++ convenience.js | 26 ++++ extension.js | 6 + metadata.json | 2 +- overview.js | 133 ++++++++++++++++++ prefs.js | 4 + ...shell.extensions.dash-to-panel.gschema.xml | 27 ++-- secondaryMenu.js | 6 +- taskbar.js | 93 ++++++++---- windowPreview.js | 14 +- 12 files changed, 311 insertions(+), 62 deletions(-) create mode 100644 overview.js 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 ![screenshot](https://github.com/jderose9/dash-to-panel/raw/master/media/screenshot.png) -## 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 @@ + + + 100 + True + True + + + True + False + 12 + 12 + 12 + 12 + 32 + + + True + True + end + center + + + 1 + 0 + + + + + True + False + True + Isolate Workspaces + True + 0 + + + 0 + 0 + + + + + + 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 desktop Animate Show Applications from the desktop + + false + Provide workspace isolation + Dash shows only windows from the currentworkspace + true Customize click behaviour @@ -91,16 +96,16 @@ 0 Tray content size Set 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; });