Pull in workspace isolation and hide dash from d2d

This commit is contained in:
jderose9
2017-01-02 20:46:35 -05:00
parent 5acdd7bb2d
commit a98b3638fb
12 changed files with 311 additions and 62 deletions

View File

@@ -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)

View File

@@ -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 <code>Alt+F2 r Enter</code> 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).

View File

@@ -722,6 +722,50 @@
</child>
</object>
</child>
<child>
<object class="GtkListBoxRow" id="isolate_workspaces_row">
<property name="width_request">100</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkGrid" id="isolate_workspaces_grid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_right">12</property>
<property name="margin_top">12</property>
<property name="margin_bottom">12</property>
<property name="column_spacing">32</property>
<child>
<object class="GtkSwitch" id="isolate_workspaces_switch">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="halign">end</property>
<property name="valign">center</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="isolate_workspaces_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Isolate Workspaces</property>
<property name="use_markup">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child type="label_item">

View File

@@ -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;
}
});

View File

@@ -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;
}

View File

@@ -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"

133
overview.js Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*
* 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);
}
}
});

View File

@@ -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) {

View File

@@ -54,6 +54,11 @@
<summary>Animate Show Applications from the desktop</summary>
<description>Animate Show Applications from the desktop</description>
</key>
<key type="b" name="isolate-workspaces">
<default>false</default>
<summary>Provide workspace isolation</summary>
<description>Dash shows only windows from the currentworkspace</description>
</key>
<key type="b" name="customize-click">
<default>true</default>
<summary>Customize click behaviour</summary>

View File

@@ -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 = "";

View File

@@ -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

View File

@@ -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;
});