mirror of
https://github.com/morgan9e/dash-to-panel
synced 2026-04-14 00:04:17 +09:00
Refactor AppIcon subclasses into separate file
taskbar.js was getting unwieldy and this will more closely align with dash-to-dock
This commit is contained in:
2
Makefile
2
Makefile
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
UUID = dash-to-panel@jderose9.github.com
|
UUID = dash-to-panel@jderose9.github.com
|
||||||
BASE_MODULES = extension.js stylesheet.css metadata.json COPYING README.md
|
BASE_MODULES = extension.js stylesheet.css metadata.json COPYING README.md
|
||||||
EXTRA_MODULES = convenience.js panel.js panelStyle.js overview.js taskbar.js secondaryMenu.js windowPreview.js prefs.js Settings.ui
|
EXTRA_MODULES = appIcons.js convenience.js panel.js panelStyle.js overview.js taskbar.js secondaryMenu.js windowPreview.js prefs.js Settings.ui
|
||||||
EXTRA_IMAGES = highlight_bg.svg highlight_stacked_bg.svg
|
EXTRA_IMAGES = highlight_bg.svg highlight_stacked_bg.svg
|
||||||
TOLOCALIZE = prefs.js
|
TOLOCALIZE = prefs.js
|
||||||
MSGSRC = $(wildcard po/*.po)
|
MSGSRC = $(wildcard po/*.po)
|
||||||
|
|||||||
900
appIcons.js
Normal file
900
appIcons.js
Normal file
@@ -0,0 +1,900 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
* and code from the Taskbar extension by Zorin OS
|
||||||
|
* Some code was also adapted from the upstream Gnome Shell source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
|
||||||
|
const AppDisplay = imports.ui.appDisplay;
|
||||||
|
const AppFavorites = imports.ui.appFavorites;
|
||||||
|
const Dash = imports.ui.dash;
|
||||||
|
const DND = imports.ui.dnd;
|
||||||
|
const IconGrid = imports.ui.iconGrid;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
const Util = imports.misc.util;
|
||||||
|
const Workspace = imports.ui.workspace;
|
||||||
|
|
||||||
|
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||||
|
const Convenience = Me.imports.convenience;
|
||||||
|
const SecondaryMenu = Me.imports.secondaryMenu;
|
||||||
|
const WindowPreview = Me.imports.windowPreview;
|
||||||
|
const Taskbar = Me.imports.taskbar;
|
||||||
|
|
||||||
|
let DASH_ANIMATION_TIME = Dash.DASH_ANIMATION_TIME;
|
||||||
|
let DASH_ITEM_LABEL_SHOW_TIME = Dash.DASH_ITEM_LABEL_SHOW_TIME;
|
||||||
|
let DASH_ITEM_LABEL_HIDE_TIME = Dash.DASH_ITEM_LABEL_HIDE_TIME;
|
||||||
|
let DASH_ITEM_HOVER_TIMEOUT = Dash.DASH_ITEM_HOVER_TIMEOUT;
|
||||||
|
let LABEL_GAP = 5;
|
||||||
|
|
||||||
|
let DOT_STYLE = {
|
||||||
|
DOTS: "DOTS",
|
||||||
|
SQUARES: "SQUARES",
|
||||||
|
DASHES: "DASHES",
|
||||||
|
SEGMENTED: "SEGMENTED",
|
||||||
|
CILIORA: "CILIORA",
|
||||||
|
METRO: "METRO",
|
||||||
|
SOLID: "SOLID"
|
||||||
|
}
|
||||||
|
|
||||||
|
let DOT_POSITION = {
|
||||||
|
TOP: "TOP",
|
||||||
|
BOTTOM: "BOTTOM"
|
||||||
|
}
|
||||||
|
|
||||||
|
let recentlyClickedAppLoopId = 0;
|
||||||
|
let recentlyClickedApp = null;
|
||||||
|
let recentlyClickedAppWindows = null;
|
||||||
|
let recentlyClickedAppIndex = 0;
|
||||||
|
|
||||||
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend AppIcon
|
||||||
|
*
|
||||||
|
* - Apply a css class based on the number of windows of each application (#N);
|
||||||
|
* - Draw a dot for each window of the application based on the default "dot" style which is hidden (#N);
|
||||||
|
* a class of the form "running#N" is applied to the AppWellIcon actor.
|
||||||
|
* like the original .running one.
|
||||||
|
* - add a .focused style to the focused app
|
||||||
|
* - Customize click actions.
|
||||||
|
* - Update minimization animation target
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
const taskbarAppIcon = new Lang.Class({
|
||||||
|
Name: 'DashToPanel.TaskbarAppIcon',
|
||||||
|
Extends: AppDisplay.AppIcon,
|
||||||
|
|
||||||
|
_init: function(settings, app, iconParams, onActivateOverride) {
|
||||||
|
|
||||||
|
// a prefix is required to avoid conflicting with the parent class variable
|
||||||
|
this._dtpSettings = settings;
|
||||||
|
this._nWindows = 0;
|
||||||
|
|
||||||
|
this.parent(app, iconParams, onActivateOverride);
|
||||||
|
|
||||||
|
this._dot.set_width(0);
|
||||||
|
this._focused = tracker.focus_app == this.app;
|
||||||
|
|
||||||
|
// Monitor windows-changes instead of app state.
|
||||||
|
// Keep using the same Id and function callback (that is extended)
|
||||||
|
if(this._stateChangedId > 0) {
|
||||||
|
this.app.disconnect(this._stateChangedId);
|
||||||
|
this._stateChangedId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._stateChangedId = this.app.connect('windows-changed',
|
||||||
|
Lang.bind(this, this.onWindowsChanged));
|
||||||
|
this._focuseAppChangeId = tracker.connect('notify::focus-app',
|
||||||
|
Lang.bind(this, this._onFocusAppChanged));
|
||||||
|
|
||||||
|
this._focusedDots = null;
|
||||||
|
this._unfocusedDots = null;
|
||||||
|
|
||||||
|
this._showDots();
|
||||||
|
|
||||||
|
this._dtpSettings.connect('changed::dot-position', Lang.bind(this, this._settingsChangeRefresh));
|
||||||
|
this._dtpSettings.connect('changed::dot-size', Lang.bind(this, this._settingsChangeRefresh));
|
||||||
|
this._dtpSettings.connect('changed::dot-style-focused', Lang.bind(this, this._settingsChangeRefresh));
|
||||||
|
this._dtpSettings.connect('changed::dot-style-unfocused', Lang.bind(this, this._settingsChangeRefresh));
|
||||||
|
this._dtpSettings.connect('changed::dot-color-override', Lang.bind(this, this._settingsChangeRefresh));
|
||||||
|
this._dtpSettings.connect('changed::dot-color-1', Lang.bind(this, this._settingsChangeRefresh));
|
||||||
|
this._dtpSettings.connect('changed::dot-color-2', Lang.bind(this, this._settingsChangeRefresh));
|
||||||
|
this._dtpSettings.connect('changed::dot-color-3', Lang.bind(this, this._settingsChangeRefresh));
|
||||||
|
this._dtpSettings.connect('changed::dot-color-4', Lang.bind(this, this._settingsChangeRefresh));
|
||||||
|
this._dtpSettings.connect('changed::dot-color-unfocused-different', Lang.bind(this, this._settingsChangeRefresh));
|
||||||
|
this._dtpSettings.connect('changed::dot-color-unfocused-1', Lang.bind(this, this._settingsChangeRefresh));
|
||||||
|
this._dtpSettings.connect('changed::dot-color-unfocused-2', Lang.bind(this, this._settingsChangeRefresh));
|
||||||
|
this._dtpSettings.connect('changed::dot-color-unfocused-3', Lang.bind(this, this._settingsChangeRefresh));
|
||||||
|
this._dtpSettings.connect('changed::dot-color-unfocused-4', Lang.bind(this, this._settingsChangeRefresh));
|
||||||
|
this._dtpSettings.connect('changed::focus-highlight', Lang.bind(this, this._settingsChangeRefresh));
|
||||||
|
|
||||||
|
this._dtpSettings.connect('changed::appicon-margin', Lang.bind(this, this._setIconStyle));
|
||||||
|
|
||||||
|
// Creating a new menu manager for window previews as adding it to the
|
||||||
|
// using the secondary menu's menu manager (which uses the "ignoreRelease"
|
||||||
|
// function) caused the extension to crash.
|
||||||
|
this.menuManagerWindowPreview = new PopupMenu.PopupMenuManager(this);
|
||||||
|
|
||||||
|
this.windowPreview = new WindowPreview.thumbnailPreviewMenu(this, this._dtpSettings, this.menuManagerWindowPreview);
|
||||||
|
|
||||||
|
this.windowPreview.connect('open-state-changed', Lang.bind(this, function (menu, isPoppedUp) {
|
||||||
|
if (!isPoppedUp)
|
||||||
|
this._onMenuPoppedDown();
|
||||||
|
}));
|
||||||
|
this.menuManagerWindowPreview.addMenu(this.windowPreview);
|
||||||
|
|
||||||
|
// grabHelper.grab() is usually called when the menu is opened. However, there seems to be a bug in the
|
||||||
|
// underlying gnome-shell that causes all window contents to freeze if the grab and ungrab occur
|
||||||
|
// in quick succession in timeouts from the Mainloop (for example, clicking the icon as the preview window is opening)
|
||||||
|
// So, instead wait until the mouse is leaving the icon (and might be moving toward the open window) to trigger the grab
|
||||||
|
// in windowPreview.js
|
||||||
|
let windowPreviewMenuData = this.menuManagerWindowPreview._menus[this.menuManagerWindowPreview._findMenu(this.windowPreview)];
|
||||||
|
this.windowPreview.disconnect(windowPreviewMenuData.openStateChangeId);
|
||||||
|
windowPreviewMenuData.openStateChangeId = this.windowPreview.connect('open-state-changed', Lang.bind(this.menuManagerWindowPreview, function(menu, open) {
|
||||||
|
if (open) {
|
||||||
|
if (this.activeMenu)
|
||||||
|
this.activeMenu.close(BoxPointer.PopupAnimation.FADE);
|
||||||
|
|
||||||
|
// don't grab here, we are grabbing in onLeave in windowPreview.js
|
||||||
|
//this._grabHelper.grab({ actor: menu.actor, focus: menu.sourceActor, onUngrab: Lang.bind(this, this._closeMenu, menu) });
|
||||||
|
} else {
|
||||||
|
this._grabHelper.ungrab({ actor: menu.actor });
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.forcedOverview = false;
|
||||||
|
|
||||||
|
this._numberOverlay();
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldShowTooltip: function() {
|
||||||
|
if (this._dtpSettings.get_boolean("show-window-previews") &&
|
||||||
|
getInterestingWindows(this.app, this._dtpSettings).length > 0) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return this.actor.hover && (!this._menu || !this._menu.isOpen) && (!this.windowPreview || !this.windowPreview.isOpen);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDestroy: function() {
|
||||||
|
this.parent();
|
||||||
|
|
||||||
|
// Disconect global signals
|
||||||
|
// stateChangedId is already handled by parent)
|
||||||
|
if(this._focusAppId>0)
|
||||||
|
tracker.disconnect(this._focusAppId);
|
||||||
|
},
|
||||||
|
|
||||||
|
onWindowsChanged: function() {
|
||||||
|
this._updateCounterClass();
|
||||||
|
this.updateIconGeometry();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Update taraget for minimization animation
|
||||||
|
updateIconGeometry: function() {
|
||||||
|
|
||||||
|
// If (for unknown reason) the actor is not on the stage the reported size
|
||||||
|
// and position are random values, which might exceeds the integer range
|
||||||
|
// resulting in an error when assigned to the a rect. This is a more like
|
||||||
|
// a workaround to prevent flooding the system with errors.
|
||||||
|
if (this.actor.get_stage() == null)
|
||||||
|
return
|
||||||
|
|
||||||
|
let rect = new Meta.Rectangle();
|
||||||
|
|
||||||
|
[rect.x, rect.y] = this.actor.get_transformed_position();
|
||||||
|
[rect.width, rect.height] = this.actor.get_transformed_size();
|
||||||
|
|
||||||
|
let windows = this.app.get_windows();
|
||||||
|
windows.forEach(function(w) {
|
||||||
|
w.set_icon_geometry(rect);
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_showDots: function() {
|
||||||
|
// Just update style if dots already exist
|
||||||
|
if (this._focusedDots && this._unfocusedDots) {
|
||||||
|
this._updateCounterClass();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._focusedDots = new St.DrawingArea({width:1, y_expand: true});
|
||||||
|
this._unfocusedDots = new St.DrawingArea({width:1, y_expand: true});
|
||||||
|
|
||||||
|
this._focusedDots.connect('repaint', Lang.bind(this, function() {
|
||||||
|
if(this._dashItemContainer.animatingIn || this._dashItemContainer.animatingOut) {
|
||||||
|
// don't draw and trigger more animations if the icon is in the middle of
|
||||||
|
// being added to the panel
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._drawRunningIndicator(this._focusedDots, this._dtpSettings.get_string('dot-style-focused'), true);
|
||||||
|
this._displayProperIndicator();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._unfocusedDots.connect('repaint', Lang.bind(this, function() {
|
||||||
|
if(this._dashItemContainer.animatingIn || this._dashItemContainer.animatingOut) {
|
||||||
|
// don't draw and trigger more animations if the icon is in the middle of
|
||||||
|
// being added to the panel
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._drawRunningIndicator(this._unfocusedDots, this._dtpSettings.get_string('dot-style-unfocused'), false);
|
||||||
|
this._displayProperIndicator();
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
this._iconContainer.add_child(this._focusedDots);
|
||||||
|
this._iconContainer.add_child(this._unfocusedDots);
|
||||||
|
|
||||||
|
this._updateCounterClass();
|
||||||
|
},
|
||||||
|
|
||||||
|
_settingsChangeRefresh: function() {
|
||||||
|
this._updateCounterClass();
|
||||||
|
this._focusedDots.queue_repaint();
|
||||||
|
this._unfocusedDots.queue_repaint();
|
||||||
|
this._displayProperIndicator(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
_setIconStyle: function() {
|
||||||
|
let margin = this._dtpSettings.get_int('appicon-margin');
|
||||||
|
let inlineStyle = 'margin: 0 ' + margin + 'px;';
|
||||||
|
|
||||||
|
if(this._dtpSettings.get_boolean('focus-highlight') && tracker.focus_app == this.app && !this._isThemeProvidingIndicator()) {
|
||||||
|
let containerWidth = this._iconContainer.get_width() / St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||||
|
let focusedDotStyle = this._dtpSettings.get_string('dot-style-focused');
|
||||||
|
let isWide = this._isWideDotStyle(focusedDotStyle);
|
||||||
|
let pos = this._dtpSettings.get_string('dot-position');
|
||||||
|
let highlightMargin = isWide ? this._dtpSettings.get_int('dot-size') : 0;
|
||||||
|
|
||||||
|
if(focusedDotStyle == DOT_STYLE.CILIORA || focusedDotStyle == DOT_STYLE.SEGMENTED)
|
||||||
|
highlightMargin += 1;
|
||||||
|
|
||||||
|
inlineStyle += "background-image: url('" +
|
||||||
|
Me.path + "/img/highlight_" +
|
||||||
|
((this._nWindows > 1 && focusedDotStyle == DOT_STYLE.METRO) ? "stacked_" : "") +
|
||||||
|
"bg.svg'); background-position: 0 " +
|
||||||
|
(pos == DOT_POSITION.TOP ? highlightMargin : 0) +
|
||||||
|
"px; background-size: " +
|
||||||
|
containerWidth + "px " +
|
||||||
|
(containerWidth - (pos == DOT_POSITION.BOTTOM ? highlightMargin : 0)) + "px;";
|
||||||
|
}
|
||||||
|
|
||||||
|
// graphical glitches if i dont set this on a timeout
|
||||||
|
if(this.actor.get_style() != inlineStyle)
|
||||||
|
Mainloop.timeout_add(0, Lang.bind(this, function() { this.actor.set_style(inlineStyle); }));
|
||||||
|
},
|
||||||
|
|
||||||
|
popupMenu: function() {
|
||||||
|
this._removeMenuTimeout();
|
||||||
|
this.actor.fake_release();
|
||||||
|
this._draggable.fakeRelease();
|
||||||
|
|
||||||
|
if (!this._menu) {
|
||||||
|
this._menu = new SecondaryMenu.taskbarSecondaryMenu(this, this._dtpSettings);
|
||||||
|
this._menu.connect('activate-window', Lang.bind(this, function (menu, window) {
|
||||||
|
this.activateWindow(window, this._dtpSettings);
|
||||||
|
}));
|
||||||
|
this._menu.connect('open-state-changed', Lang.bind(this, function (menu, isPoppedUp) {
|
||||||
|
if (!isPoppedUp)
|
||||||
|
this._onMenuPoppedDown();
|
||||||
|
}));
|
||||||
|
let id = Main.overview.connect('hiding', Lang.bind(this, function () { this._menu.close(); }));
|
||||||
|
this._menu.actor.connect('destroy', function() {
|
||||||
|
Main.overview.disconnect(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._menuManager.addMenu(this._menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('menu-state-changed', true);
|
||||||
|
|
||||||
|
this.windowPreview.close();
|
||||||
|
|
||||||
|
this.actor.set_hover(true);
|
||||||
|
this._menu.actor.add_style_class_name('dashtopanelSecondaryMenu');
|
||||||
|
this._menu.popup();
|
||||||
|
this._menuManager.ignoreRelease();
|
||||||
|
this.emit('sync-tooltip');
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onFocusAppChanged: function(windowTracker) {
|
||||||
|
this._displayProperIndicator();
|
||||||
|
},
|
||||||
|
|
||||||
|
_displayProperIndicator: function (force) {
|
||||||
|
let containerWidth = this._iconContainer.get_width();
|
||||||
|
let isFocused = (tracker.focus_app == this.app);
|
||||||
|
let focusedDotStyle = this._dtpSettings.get_string('dot-style-focused');
|
||||||
|
let unfocusedDotStyle = this._dtpSettings.get_string('dot-style-unfocused');
|
||||||
|
let focusedIsWide = this._isWideDotStyle(focusedDotStyle);
|
||||||
|
let unfocusedIsWide = this._isWideDotStyle(unfocusedDotStyle);
|
||||||
|
|
||||||
|
this._setIconStyle();
|
||||||
|
|
||||||
|
let newFocusedDotsWidth = 0;
|
||||||
|
let newFocusedDotsOpacity = 0;
|
||||||
|
let newUnfocusedDotsWidth = 0;
|
||||||
|
let newUnfocusedDotsOpacity = 0;
|
||||||
|
|
||||||
|
|
||||||
|
if(isFocused)
|
||||||
|
this.actor.add_style_class_name('focused');
|
||||||
|
else
|
||||||
|
this.actor.remove_style_class_name('focused');
|
||||||
|
|
||||||
|
if(focusedIsWide) {
|
||||||
|
newFocusedDotsWidth = (isFocused && this._nWindows > 0) ? containerWidth : 0;
|
||||||
|
newFocusedDotsOpacity = 255;
|
||||||
|
} else {
|
||||||
|
newFocusedDotsWidth = containerWidth;
|
||||||
|
newFocusedDotsOpacity = (isFocused && this._nWindows > 0) ? 255 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(unfocusedIsWide) {
|
||||||
|
newUnfocusedDotsWidth = (!isFocused && this._nWindows > 0) ? containerWidth : 0;
|
||||||
|
newUnfocusedDotsOpacity = 255;
|
||||||
|
} else {
|
||||||
|
newUnfocusedDotsWidth = containerWidth;
|
||||||
|
newUnfocusedDotsOpacity = (!isFocused && this._nWindows > 0) ? 255 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only animate if...
|
||||||
|
// animation is enabled in settings
|
||||||
|
// AND (going from a wide style to a narrow style indicator or vice-versa
|
||||||
|
// OR going from an open app to a closed app or vice versa)
|
||||||
|
if(this._dtpSettings.get_boolean('animate-app-switch') &&
|
||||||
|
((focusedIsWide != unfocusedIsWide) ||
|
||||||
|
(this._focusedDots.width != newUnfocusedDotsWidth || this._unfocusedDots.width != newFocusedDotsWidth))) {
|
||||||
|
this._animateDotDisplay(this._focusedDots, newFocusedDotsWidth, this._unfocusedDots, newUnfocusedDotsOpacity, force);
|
||||||
|
this._animateDotDisplay(this._unfocusedDots, newUnfocusedDotsWidth, this._focusedDots, newFocusedDotsOpacity, force);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this._focusedDots.opacity = newFocusedDotsOpacity;
|
||||||
|
this._unfocusedDots.opacity = newUnfocusedDotsOpacity;
|
||||||
|
this._focusedDots.width = newFocusedDotsWidth;
|
||||||
|
this._unfocusedDots.width = newUnfocusedDotsWidth;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_animateDotDisplay: function (dots, newWidth, otherDots, newOtherOpacity, force) {
|
||||||
|
if((dots.width != newWidth && dots._tweeningToWidth !== newWidth) || force) {
|
||||||
|
dots._tweeningToWidth = newWidth;
|
||||||
|
Tweener.addTween(dots,
|
||||||
|
{ width: newWidth,
|
||||||
|
time: DASH_ANIMATION_TIME,
|
||||||
|
transition: 'easeInOutCubic',
|
||||||
|
onStart: Lang.bind(this, function() {
|
||||||
|
if(newOtherOpacity == 0)
|
||||||
|
otherDots.opacity = newOtherOpacity;
|
||||||
|
}),
|
||||||
|
onComplete: Lang.bind(this, function() {
|
||||||
|
if(newOtherOpacity > 0)
|
||||||
|
otherDots.opacity = newOtherOpacity;
|
||||||
|
dots._tweeningToWidth = null;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_isWideDotStyle: function(dotStyle) {
|
||||||
|
return dotStyle == DOT_STYLE.SEGMENTED ||
|
||||||
|
dotStyle == DOT_STYLE.CILIORA ||
|
||||||
|
dotStyle == DOT_STYLE.METRO ||
|
||||||
|
dotStyle == DOT_STYLE.SOLID;
|
||||||
|
},
|
||||||
|
|
||||||
|
_isThemeProvidingIndicator: function () {
|
||||||
|
// This is an attempt to determine if the theme is providing their own
|
||||||
|
// running indicator by way of a border image on the icon, for example in
|
||||||
|
// the theme Ciliora
|
||||||
|
return (this.icon.actor.get_stage() &&
|
||||||
|
this.icon.actor.get_theme_node().get_border_image());
|
||||||
|
},
|
||||||
|
|
||||||
|
activate: function(button) {
|
||||||
|
this.windowPreview.requestCloseMenu();
|
||||||
|
|
||||||
|
let event = Clutter.get_current_event();
|
||||||
|
let modifiers = event ? event.get_state() : 0;
|
||||||
|
let focusedApp = tracker.focus_app;
|
||||||
|
|
||||||
|
// Only consider SHIFT and CONTROL as modifiers (exclude SUPER, CAPS-LOCK, etc.)
|
||||||
|
modifiers = modifiers & (Clutter.ModifierType.SHIFT_MASK | Clutter.ModifierType.CONTROL_MASK);
|
||||||
|
|
||||||
|
// We don't change the CTRL-click behaviour: in such case we just chain
|
||||||
|
// up the parent method and return.
|
||||||
|
if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
|
||||||
|
// Keep default behaviour: launch new window
|
||||||
|
// By calling the parent method I make it compatible
|
||||||
|
// with other extensions tweaking ctrl + click
|
||||||
|
this.parent(button);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We check what type of click we have and if the modifier SHIFT is
|
||||||
|
// being used. We then define what buttonAction should be for this
|
||||||
|
// event.
|
||||||
|
let buttonAction = 0;
|
||||||
|
if (button && button == 2 ) {
|
||||||
|
if (modifiers & Clutter.ModifierType.SHIFT_MASK)
|
||||||
|
buttonAction = this._dtpSettings.get_string('shift-middle-click-action');
|
||||||
|
else
|
||||||
|
buttonAction = this._dtpSettings.get_string('middle-click-action');
|
||||||
|
}
|
||||||
|
else if (button && button == 1) {
|
||||||
|
if (modifiers & Clutter.ModifierType.SHIFT_MASK)
|
||||||
|
buttonAction = this._dtpSettings.get_string('shift-click-action');
|
||||||
|
else
|
||||||
|
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 (appIsRunning) {
|
||||||
|
switch (buttonAction) {
|
||||||
|
case "RAISE":
|
||||||
|
activateAllWindows(this.app, this._dtpSettings);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "LAUNCH":
|
||||||
|
if(this._dtpSettings.get_boolean('animate-window-launch'))
|
||||||
|
this.animateLaunch();
|
||||||
|
this.app.open_new_window(-1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "MINIMIZE":
|
||||||
|
// In overview just activate the app, unless the acion is explicitely
|
||||||
|
// requested with a keyboard modifier
|
||||||
|
if (!Main.overview._shown || modifiers){
|
||||||
|
// If we have button=2 or a modifier, allow minimization even if
|
||||||
|
// the app is not focused
|
||||||
|
if (this.app == focusedApp || button == 2 || modifiers & Clutter.ModifierType.SHIFT_MASK) {
|
||||||
|
// minimize all windows on double click and always in the case of primary click without
|
||||||
|
// additional modifiers
|
||||||
|
let click_count = 0;
|
||||||
|
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, this._dtpSettings);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
activateAllWindows(this.app, this._dtpSettings);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.app.activate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "CYCLE":
|
||||||
|
if (!Main.overview._shown){
|
||||||
|
if (this.app == focusedApp)
|
||||||
|
cycleThroughWindows(this.app, this._dtpSettings, false, false);
|
||||||
|
else {
|
||||||
|
activateFirstWindow(this.app, this._dtpSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.app.activate();
|
||||||
|
break;
|
||||||
|
case "CYCLE-MIN":
|
||||||
|
if (!Main.overview._shown){
|
||||||
|
if (this.app == focusedApp ||
|
||||||
|
(recentlyClickedApp == this.app && recentlyClickedAppWindows[recentlyClickedAppIndex % recentlyClickedAppWindows.length] == "MINIMIZE"))
|
||||||
|
cycleThroughWindows(this.app, this._dtpSettings, false, true);
|
||||||
|
else {
|
||||||
|
activateFirstWindow(this.app, this._dtpSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.app.activate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "QUIT":
|
||||||
|
closeAllWindows(this.app, this._dtpSettings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(this._dtpSettings.get_boolean('animate-window-launch'))
|
||||||
|
this.animateLaunch();
|
||||||
|
this.app.open_new_window(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Main.overview.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateCounterClass: function() {
|
||||||
|
let maxN = 4;
|
||||||
|
this._nWindows = Math.min(getInterestingWindows(this.app, this._dtpSettings).length, maxN);
|
||||||
|
|
||||||
|
for (let i = 1; i <= maxN; i++){
|
||||||
|
let className = 'running'+i;
|
||||||
|
if(i != this._nWindows)
|
||||||
|
this.actor.remove_style_class_name(className);
|
||||||
|
else
|
||||||
|
this.actor.add_style_class_name(className);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_drawRunningIndicator: function(area, type, isFocused) {
|
||||||
|
let bodyColor;
|
||||||
|
if(this._dtpSettings.get_boolean('dot-color-override')) {
|
||||||
|
let dotColorSettingPrefix = 'dot-color-';
|
||||||
|
if(!isFocused && this._dtpSettings.get_boolean('dot-color-unfocused-different'))
|
||||||
|
dotColorSettingPrefix = 'dot-color-unfocused-';
|
||||||
|
bodyColor = Clutter.color_from_string(this._dtpSettings.get_string(dotColorSettingPrefix + (this._nWindows > 0 ? this._nWindows : 1)))[1];
|
||||||
|
} else {
|
||||||
|
// Re-use the style - background color, and border width and color -
|
||||||
|
// of the default dot
|
||||||
|
let themeNode = this._dot.get_theme_node();
|
||||||
|
bodyColor = themeNode.get_background_color();
|
||||||
|
if(bodyColor.alpha == 0) // theme didn't provide one, use a default
|
||||||
|
bodyColor = new Clutter.Color({ red: 82, green: 148, blue: 226, alpha: 255 });
|
||||||
|
}
|
||||||
|
|
||||||
|
let [width, height] = area.get_surface_size();
|
||||||
|
let cr = area.get_context();
|
||||||
|
let n = this._nWindows;
|
||||||
|
let size = this._dtpSettings.get_int('dot-size') * St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||||
|
let padding = 0; // distance from the margin
|
||||||
|
let yOffset = this._dtpSettings.get_string('dot-position') == DOT_POSITION.TOP ? 0 : (height - padding - size);
|
||||||
|
|
||||||
|
if(type == DOT_STYLE.DOTS) {
|
||||||
|
// Draw the required numbers of dots
|
||||||
|
let radius = size/2;
|
||||||
|
let spacing = Math.ceil(width/18); // separation between the dots
|
||||||
|
|
||||||
|
cr.translate((width - (2*n)*radius - (n-1)*spacing)/2, yOffset);
|
||||||
|
|
||||||
|
Clutter.cairo_set_source_color(cr, bodyColor);
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
cr.newSubPath();
|
||||||
|
cr.arc((2*i+1)*radius + i*spacing, radius, radius, 0, 2*Math.PI);
|
||||||
|
}
|
||||||
|
cr.fill();
|
||||||
|
} else if(type == DOT_STYLE.SQUARES) {
|
||||||
|
let spacing = Math.ceil(width/18); // separation between the dots
|
||||||
|
|
||||||
|
cr.translate(Math.floor((width - n*size - (n-1)*spacing)/2), yOffset);
|
||||||
|
|
||||||
|
Clutter.cairo_set_source_color(cr, bodyColor);
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
cr.newSubPath();
|
||||||
|
cr.rectangle(i*size + i*spacing, 0, size, size);
|
||||||
|
}
|
||||||
|
cr.fill();
|
||||||
|
} else if(type == DOT_STYLE.DASHES) {
|
||||||
|
let spacing = Math.ceil(width/18); // separation between the dots
|
||||||
|
let dashLength = Math.floor(width/4) - spacing;
|
||||||
|
|
||||||
|
cr.translate(Math.floor((width - n*dashLength - (n-1)*spacing)/2), yOffset);
|
||||||
|
|
||||||
|
Clutter.cairo_set_source_color(cr, bodyColor);
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
cr.newSubPath();
|
||||||
|
cr.rectangle(i*dashLength + i*spacing, 0, dashLength, size);
|
||||||
|
}
|
||||||
|
cr.fill();
|
||||||
|
} else if(type == DOT_STYLE.SEGMENTED) {
|
||||||
|
let spacing = Math.ceil(width/18); // separation between the dots
|
||||||
|
let dashLength = Math.ceil((width - ((n-1)*spacing))/n);
|
||||||
|
|
||||||
|
cr.translate(0, yOffset);
|
||||||
|
|
||||||
|
Clutter.cairo_set_source_color(cr, bodyColor);
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
cr.newSubPath();
|
||||||
|
cr.rectangle(i*dashLength + i*spacing, 0, dashLength, size);
|
||||||
|
}
|
||||||
|
cr.fill();
|
||||||
|
} else if (type == DOT_STYLE.CILIORA) {
|
||||||
|
let spacing = size; // separation between the dots
|
||||||
|
let lineLength = width - (size*(n-1)) - (spacing*(n-1));
|
||||||
|
|
||||||
|
cr.translate(0, yOffset);
|
||||||
|
|
||||||
|
Clutter.cairo_set_source_color(cr, bodyColor);
|
||||||
|
cr.newSubPath();
|
||||||
|
cr.rectangle(0, 0, lineLength, size);
|
||||||
|
for (let i = 1; i < n; i++) {
|
||||||
|
cr.newSubPath();
|
||||||
|
cr.rectangle(lineLength + (i*spacing) + ((i-1)*size), 0, size, size);
|
||||||
|
}
|
||||||
|
cr.fill();
|
||||||
|
} else if (type == DOT_STYLE.METRO) {
|
||||||
|
if(n <= 1) {
|
||||||
|
cr.translate(0, yOffset);
|
||||||
|
Clutter.cairo_set_source_color(cr, bodyColor);
|
||||||
|
cr.newSubPath();
|
||||||
|
cr.rectangle(0, 0, width, size);
|
||||||
|
cr.fill();
|
||||||
|
} else {
|
||||||
|
let blackenedLength = (1/48)*width; // need to scale with the SVG for the stacked highlight
|
||||||
|
let darkenedLength = isFocused ? (2/48)*width : (10/48)*width;
|
||||||
|
let blackenedColor = bodyColor.shade(.3);
|
||||||
|
let darkenedColor = bodyColor.shade(.7);
|
||||||
|
|
||||||
|
cr.translate(0, yOffset);
|
||||||
|
|
||||||
|
Clutter.cairo_set_source_color(cr, bodyColor);
|
||||||
|
cr.newSubPath();
|
||||||
|
cr.rectangle(0, 0, width - darkenedLength - blackenedLength, size);
|
||||||
|
cr.fill();
|
||||||
|
Clutter.cairo_set_source_color(cr, blackenedColor);
|
||||||
|
cr.newSubPath();
|
||||||
|
cr.rectangle(width - darkenedLength - blackenedLength, 0, 1, size);
|
||||||
|
cr.fill();
|
||||||
|
Clutter.cairo_set_source_color(cr, darkenedColor);
|
||||||
|
cr.newSubPath();
|
||||||
|
cr.rectangle(width - darkenedLength, 0, darkenedLength, size);
|
||||||
|
cr.fill();
|
||||||
|
}
|
||||||
|
} else { // solid
|
||||||
|
cr.translate(0, yOffset);
|
||||||
|
Clutter.cairo_set_source_color(cr, bodyColor);
|
||||||
|
cr.newSubPath();
|
||||||
|
cr.rectangle(0, 0, width, size);
|
||||||
|
cr.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
cr.$dispose();
|
||||||
|
},
|
||||||
|
|
||||||
|
_numberOverlay: function() {
|
||||||
|
// Add label for a Hot-Key visual aid
|
||||||
|
this._numberOverlayLabel = new St.Label();
|
||||||
|
this._numberOverlayBin = new St.Bin({
|
||||||
|
child: this._numberOverlayLabel,
|
||||||
|
x_align: St.Align.START, y_align: St.Align.START,
|
||||||
|
x_expand: true, y_expand: true
|
||||||
|
});
|
||||||
|
this._numberOverlayStyle = 'background-color: rgba(0,0,0,0.8);'
|
||||||
|
this._numberOverlayOrder = -1;
|
||||||
|
this._numberOverlayBin.hide();
|
||||||
|
|
||||||
|
this._iconContainer.add_child(this._numberOverlayBin);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
updateNumberOverlay: function() {
|
||||||
|
// Set the font size to something smaller than the whole icon so it is
|
||||||
|
// still visible. The border radius is large to make the shape circular
|
||||||
|
let [minWidth, natWidth] = this._iconContainer.get_preferred_width(-1);
|
||||||
|
let font_size = Math.round(Math.max(12, 0.3*natWidth));
|
||||||
|
let size = Math.round(font_size*1.2);
|
||||||
|
this._numberOverlayLabel.set_style(
|
||||||
|
this._numberOverlayStyle +
|
||||||
|
'font-size: ' + font_size + 'px;' +
|
||||||
|
'text-align: center;' +
|
||||||
|
'border-radius: ' + this.icon.iconSize + 'px;' +
|
||||||
|
'width: ' + size + 'px; height: ' + size +'px;'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
setNumberOverlay: function(number) {
|
||||||
|
this._numberOverlayOrder = number;
|
||||||
|
this._numberOverlayLabel.set_text(number.toString());
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleNumberOverlay: function(activate) {
|
||||||
|
if (activate && this._numberOverlayOrder > -1)
|
||||||
|
this._numberOverlayBin.show();
|
||||||
|
else
|
||||||
|
this._numberOverlayBin.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function minimizeWindow(app, param, settings){
|
||||||
|
// Param true make all app windows minimize
|
||||||
|
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];
|
||||||
|
if (w.get_workspace() == current_workspace && w.showing_on_its_workspace()){
|
||||||
|
w.minimize();
|
||||||
|
// Just minimize one window. By specification it should be the
|
||||||
|
// focused window on the current workspace.
|
||||||
|
if(!param)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* By default only non minimized windows are activated.
|
||||||
|
* This activates all windows in the current workspace.
|
||||||
|
*/
|
||||||
|
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 = getInterestingWindows(app, settings);
|
||||||
|
let w = windows[0];
|
||||||
|
Main.activateWindow(w);
|
||||||
|
let activeWorkspace = global.screen.get_active_workspace_index();
|
||||||
|
|
||||||
|
if (windows.length <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let activatedWindows = 0;
|
||||||
|
|
||||||
|
for (let i = windows.length - 1; i >= 0; i--){
|
||||||
|
if (windows[i].get_workspace().index() == activeWorkspace){
|
||||||
|
Main.activateWindow(windows[i]);
|
||||||
|
activatedWindows++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function activateFirstWindow(app, settings){
|
||||||
|
|
||||||
|
let windows = getInterestingWindows(app, settings);
|
||||||
|
Main.activateWindow(windows[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cycleThroughWindows(app, settings, reversed, shouldMinimize) {
|
||||||
|
// Store for a little amount of time last clicked app and its windows
|
||||||
|
// since the order changes upon window interaction
|
||||||
|
let MEMORY_TIME=3000;
|
||||||
|
|
||||||
|
let app_windows = getInterestingWindows(app, settings);
|
||||||
|
|
||||||
|
if(shouldMinimize)
|
||||||
|
app_windows.push("MINIMIZE");
|
||||||
|
|
||||||
|
if (recentlyClickedAppLoopId > 0)
|
||||||
|
Mainloop.source_remove(recentlyClickedAppLoopId);
|
||||||
|
recentlyClickedAppLoopId = Mainloop.timeout_add(MEMORY_TIME, resetRecentlyClickedApp);
|
||||||
|
|
||||||
|
// If there isn't already a list of windows for the current app,
|
||||||
|
// or the stored list is outdated, use the current windows list.
|
||||||
|
if (!recentlyClickedApp ||
|
||||||
|
recentlyClickedApp.get_id() != app.get_id() ||
|
||||||
|
recentlyClickedAppWindows.length != app_windows.length) {
|
||||||
|
recentlyClickedApp = app;
|
||||||
|
recentlyClickedAppWindows = app_windows;
|
||||||
|
recentlyClickedAppIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reversed) {
|
||||||
|
recentlyClickedAppIndex--;
|
||||||
|
if (recentlyClickedAppIndex < 0) recentlyClickedAppIndex = recentlyClickedAppWindows.length - 1;
|
||||||
|
} else {
|
||||||
|
recentlyClickedAppIndex++;
|
||||||
|
}
|
||||||
|
let index = recentlyClickedAppIndex % recentlyClickedAppWindows.length;
|
||||||
|
|
||||||
|
if(recentlyClickedAppWindows[index] === "MINIMIZE")
|
||||||
|
minimizeWindow(app, true, settings);
|
||||||
|
else
|
||||||
|
Main.activateWindow(recentlyClickedAppWindows[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetRecentlyClickedApp() {
|
||||||
|
if (recentlyClickedAppLoopId > 0)
|
||||||
|
Mainloop.source_remove(recentlyClickedAppLoopId);
|
||||||
|
recentlyClickedAppLoopId=0;
|
||||||
|
recentlyClickedApp =null;
|
||||||
|
recentlyClickedAppWindows = null;
|
||||||
|
recentlyClickedAppIndex = 0;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeAllWindows(app, settings) {
|
||||||
|
let windows = getInterestingWindows(app, settings);
|
||||||
|
for (let i = 0; i < windows.length; i++)
|
||||||
|
windows[i].delete(global.get_current_time());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// define first this function to use it in extendDashItemContainer
|
||||||
|
function ItemShowLabel() {
|
||||||
|
if (!this._labelText)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.label.set_text(this._labelText);
|
||||||
|
this.label.opacity = 0;
|
||||||
|
this.label.show();
|
||||||
|
|
||||||
|
let [stageX, stageY] = this.get_transformed_position();
|
||||||
|
let node = this.label.get_theme_node();
|
||||||
|
|
||||||
|
let itemWidth = this.allocation.x2 - this.allocation.x1;
|
||||||
|
let itemHeight = this.allocation.y2 - this.allocation.y1;
|
||||||
|
|
||||||
|
let labelWidth = this.label.get_width();
|
||||||
|
let labelHeight = this.label.get_height();
|
||||||
|
|
||||||
|
let x, y, xOffset, yOffset;
|
||||||
|
|
||||||
|
let position = Taskbar.getPosition();
|
||||||
|
let labelOffset = node.get_length('-x-offset');
|
||||||
|
|
||||||
|
switch(position) {
|
||||||
|
case St.Side.TOP:
|
||||||
|
y = stageY + labelOffset + itemHeight;
|
||||||
|
xOffset = Math.floor((itemWidth - labelWidth) / 2);
|
||||||
|
x = stageX + xOffset;
|
||||||
|
break;
|
||||||
|
case St.Side.BOTTOM:
|
||||||
|
yOffset = labelOffset;
|
||||||
|
y = stageY - labelHeight - yOffset;
|
||||||
|
xOffset = Math.floor((itemWidth - labelWidth) / 2);
|
||||||
|
x = stageX + xOffset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep the label inside the screen border
|
||||||
|
// Only needed for the x coordinate.
|
||||||
|
|
||||||
|
// Leave a few pixel gap
|
||||||
|
let gap = LABEL_GAP;
|
||||||
|
let monitor = Main.layoutManager.findMonitorForActor(this);
|
||||||
|
if ( x - monitor.x < gap)
|
||||||
|
x += monitor.x - x + labelOffset;
|
||||||
|
else if ( x + labelWidth > monitor.x + monitor.width - gap)
|
||||||
|
x -= x + labelWidth -( monitor.x + monitor.width) + gap;
|
||||||
|
|
||||||
|
this.label.set_position(x, y);
|
||||||
|
Tweener.addTween(this.label,
|
||||||
|
{ opacity: 255,
|
||||||
|
time: DASH_ITEM_LABEL_SHOW_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -31,6 +31,8 @@ const RemoteMenu = imports.ui.remoteMenu;
|
|||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const AppFavorites = imports.ui.appFavorites;
|
const AppFavorites = imports.ui.appFavorites;
|
||||||
|
const Convenience = Me.imports.convenience;
|
||||||
|
const AppIcons = Me.imports.appIcons;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend AppIconMenu
|
* Extend AppIconMenu
|
||||||
@@ -235,7 +237,7 @@ _redisplay: function() {
|
|||||||
|
|
||||||
// quit menu
|
// quit menu
|
||||||
let app = this._source.app;
|
let app = this._source.app;
|
||||||
let count = Taskbar.getInterestingWindows(app, this._dtpSettings).length;
|
let count = AppIcons.getInterestingWindows(app, this._dtpSettings).length;
|
||||||
if ( count > 0) {
|
if ( count > 0) {
|
||||||
this._appendSeparator();
|
this._appendSeparator();
|
||||||
let quitFromTaskbarMenuText = "";
|
let quitFromTaskbarMenuText = "";
|
||||||
|
|||||||
854
taskbar.js
854
taskbar.js
@@ -48,38 +48,17 @@ const Me = imports.misc.extensionUtils.getCurrentExtension();
|
|||||||
const Convenience = Me.imports.convenience;
|
const Convenience = Me.imports.convenience;
|
||||||
const SecondaryMenu = Me.imports.secondaryMenu;
|
const SecondaryMenu = Me.imports.secondaryMenu;
|
||||||
const WindowPreview = Me.imports.windowPreview;
|
const WindowPreview = Me.imports.windowPreview;
|
||||||
|
const AppIcons = Me.imports.appIcons;
|
||||||
|
|
||||||
let DASH_ANIMATION_TIME = Dash.DASH_ANIMATION_TIME;
|
let DASH_ANIMATION_TIME = Dash.DASH_ANIMATION_TIME;
|
||||||
let DASH_ITEM_LABEL_SHOW_TIME = Dash.DASH_ITEM_LABEL_SHOW_TIME;
|
let DASH_ITEM_LABEL_SHOW_TIME = Dash.DASH_ITEM_LABEL_SHOW_TIME;
|
||||||
let DASH_ITEM_LABEL_HIDE_TIME = Dash.DASH_ITEM_LABEL_HIDE_TIME;
|
let DASH_ITEM_LABEL_HIDE_TIME = Dash.DASH_ITEM_LABEL_HIDE_TIME;
|
||||||
let DASH_ITEM_HOVER_TIMEOUT = Dash.DASH_ITEM_HOVER_TIMEOUT;
|
let DASH_ITEM_HOVER_TIMEOUT = Dash.DASH_ITEM_HOVER_TIMEOUT;
|
||||||
let LABEL_GAP = 5;
|
|
||||||
let HFADE_WIDTH = 48;
|
let HFADE_WIDTH = 48;
|
||||||
|
|
||||||
let DOT_STYLE = {
|
|
||||||
DOTS: "DOTS",
|
|
||||||
SQUARES: "SQUARES",
|
|
||||||
DASHES: "DASHES",
|
|
||||||
SEGMENTED: "SEGMENTED",
|
|
||||||
CILIORA: "CILIORA",
|
|
||||||
METRO: "METRO",
|
|
||||||
SOLID: "SOLID"
|
|
||||||
}
|
|
||||||
|
|
||||||
let DOT_POSITION = {
|
|
||||||
TOP: "TOP",
|
|
||||||
BOTTOM: "BOTTOM"
|
|
||||||
}
|
|
||||||
|
|
||||||
let recentlyClickedAppLoopId = 0;
|
|
||||||
let recentlyClickedApp = null;
|
|
||||||
let recentlyClickedAppWindows = null;
|
|
||||||
let recentlyClickedAppIndex = 0;
|
|
||||||
|
|
||||||
function getPosition() {
|
function getPosition() {
|
||||||
return Main.layoutManager.panelBox.anchor_y == 0 ? St.Side.TOP : St.Side.BOTTOM;
|
return Main.layoutManager.panelBox.anchor_y == 0 ? St.Side.TOP : St.Side.BOTTOM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend DashItemContainer
|
* Extend DashItemContainer
|
||||||
*
|
*
|
||||||
@@ -89,64 +68,8 @@ function getPosition() {
|
|||||||
* thus use this ugly pattern.
|
* thus use this ugly pattern.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// define first this function to use it in extendDashItemContainer
|
|
||||||
function ItemShowLabel() {
|
|
||||||
if (!this._labelText)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.label.set_text(this._labelText);
|
|
||||||
this.label.opacity = 0;
|
|
||||||
this.label.show();
|
|
||||||
|
|
||||||
let [stageX, stageY] = this.get_transformed_position();
|
|
||||||
let node = this.label.get_theme_node();
|
|
||||||
|
|
||||||
let itemWidth = this.allocation.x2 - this.allocation.x1;
|
|
||||||
let itemHeight = this.allocation.y2 - this.allocation.y1;
|
|
||||||
|
|
||||||
let labelWidth = this.label.get_width();
|
|
||||||
let labelHeight = this.label.get_height();
|
|
||||||
|
|
||||||
let x, y, xOffset, yOffset;
|
|
||||||
|
|
||||||
let position = getPosition();
|
|
||||||
let labelOffset = node.get_length('-x-offset');
|
|
||||||
|
|
||||||
switch(position) {
|
|
||||||
case St.Side.TOP:
|
|
||||||
y = stageY + labelOffset + itemHeight;
|
|
||||||
xOffset = Math.floor((itemWidth - labelWidth) / 2);
|
|
||||||
x = stageX + xOffset;
|
|
||||||
break;
|
|
||||||
case St.Side.BOTTOM:
|
|
||||||
yOffset = labelOffset;
|
|
||||||
y = stageY - labelHeight - yOffset;
|
|
||||||
xOffset = Math.floor((itemWidth - labelWidth) / 2);
|
|
||||||
x = stageX + xOffset;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep the label inside the screen border
|
|
||||||
// Only needed for the x coordinate.
|
|
||||||
|
|
||||||
// Leave a few pixel gap
|
|
||||||
let gap = LABEL_GAP;
|
|
||||||
let monitor = Main.layoutManager.findMonitorForActor(this);
|
|
||||||
if ( x - monitor.x < gap)
|
|
||||||
x += monitor.x - x + labelOffset;
|
|
||||||
else if ( x + labelWidth > monitor.x + monitor.width - gap)
|
|
||||||
x -= x + labelWidth -( monitor.x + monitor.width) + gap;
|
|
||||||
|
|
||||||
this.label.set_position(x, y);
|
|
||||||
Tweener.addTween(this.label,
|
|
||||||
{ opacity: 255,
|
|
||||||
time: DASH_ITEM_LABEL_SHOW_TIME,
|
|
||||||
transition: 'easeOutQuad',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function extendDashItemContainer(dashItemContainer) {
|
function extendDashItemContainer(dashItemContainer) {
|
||||||
dashItemContainer.showLabel = ItemShowLabel;
|
dashItemContainer.showLabel = AppIcons.ItemShowLabel;
|
||||||
|
|
||||||
// override show so we know when an animation is occurring to suppress indicator animations
|
// override show so we know when an animation is occurring to suppress indicator animations
|
||||||
dashItemContainer.show = Lang.bind(dashItemContainer, function(animate) {
|
dashItemContainer.show = Lang.bind(dashItemContainer, function(animate) {
|
||||||
@@ -295,7 +218,7 @@ const taskbar = new Lang.Class({
|
|||||||
this._scrollView.add_actor(this._box);
|
this._scrollView.add_actor(this._box);
|
||||||
|
|
||||||
this._showAppsIcon = new Dash.ShowAppsIcon();
|
this._showAppsIcon = new Dash.ShowAppsIcon();
|
||||||
this._showAppsIcon.showLabel = ItemShowLabel;
|
this._showAppsIcon.showLabel = AppIcons.ItemShowLabel;
|
||||||
this.showAppsButton = this._showAppsIcon.toggleButton;
|
this.showAppsButton = this._showAppsIcon.toggleButton;
|
||||||
this._showAppsIcon.actor = this.showAppsButton;
|
this._showAppsIcon.actor = this.showAppsButton;
|
||||||
|
|
||||||
@@ -514,7 +437,7 @@ const taskbar = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_createAppItem: function(app) {
|
_createAppItem: function(app) {
|
||||||
let appIcon = new taskbarAppIcon(this._dtpSettings, app,
|
let appIcon = new AppIcons.taskbarAppIcon(this._dtpSettings, app,
|
||||||
{ setSizeManually: true,
|
{ setSizeManually: true,
|
||||||
showLabel: false });
|
showLabel: false });
|
||||||
|
|
||||||
@@ -777,7 +700,7 @@ const taskbar = new Lang.Class({
|
|||||||
// the current workspace
|
// the current workspace
|
||||||
let settings = this._dtpSettings;
|
let settings = this._dtpSettings;
|
||||||
running = running.filter(function(_app) {
|
running = running.filter(function(_app) {
|
||||||
return getInterestingWindows(_app, settings).length != 0;
|
return AppIcons.getInterestingWindows(_app, settings).length != 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1255,756 +1178,6 @@ const taskbar = new Lang.Class({
|
|||||||
|
|
||||||
Signals.addSignalMethods(taskbar.prototype);
|
Signals.addSignalMethods(taskbar.prototype);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extend AppIcon
|
|
||||||
*
|
|
||||||
* - Apply a css class based on the number of windows of each application (#N);
|
|
||||||
* - Draw a dot for each window of the application based on the default "dot" style which is hidden (#N);
|
|
||||||
* a class of the form "running#N" is applied to the AppWellIcon actor.
|
|
||||||
* like the original .running one.
|
|
||||||
* - add a .focused style to the focused app
|
|
||||||
* - Customize click actions.
|
|
||||||
* - Update minimization animation target
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
|
||||||
|
|
||||||
const taskbarAppIcon = new Lang.Class({
|
|
||||||
Name: 'DashToPanel.TaskbarAppIcon',
|
|
||||||
Extends: AppDisplay.AppIcon,
|
|
||||||
|
|
||||||
_init: function(settings, app, iconParams, onActivateOverride) {
|
|
||||||
|
|
||||||
// a prefix is required to avoid conflicting with the parent class variable
|
|
||||||
this._dtpSettings = settings;
|
|
||||||
this._nWindows = 0;
|
|
||||||
|
|
||||||
this.parent(app, iconParams, onActivateOverride);
|
|
||||||
|
|
||||||
this._dot.set_width(0);
|
|
||||||
this._focused = tracker.focus_app == this.app;
|
|
||||||
|
|
||||||
// Monitor windows-changes instead of app state.
|
|
||||||
// Keep using the same Id and function callback (that is extended)
|
|
||||||
if(this._stateChangedId > 0) {
|
|
||||||
this.app.disconnect(this._stateChangedId);
|
|
||||||
this._stateChangedId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._stateChangedId = this.app.connect('windows-changed',
|
|
||||||
Lang.bind(this, this.onWindowsChanged));
|
|
||||||
this._focuseAppChangeId = tracker.connect('notify::focus-app',
|
|
||||||
Lang.bind(this, this._onFocusAppChanged));
|
|
||||||
|
|
||||||
this._focusedDots = null;
|
|
||||||
this._unfocusedDots = null;
|
|
||||||
|
|
||||||
this._showDots();
|
|
||||||
|
|
||||||
this._dtpSettings.connect('changed::dot-position', Lang.bind(this, this._settingsChangeRefresh));
|
|
||||||
this._dtpSettings.connect('changed::dot-size', Lang.bind(this, this._settingsChangeRefresh));
|
|
||||||
this._dtpSettings.connect('changed::dot-style-focused', Lang.bind(this, this._settingsChangeRefresh));
|
|
||||||
this._dtpSettings.connect('changed::dot-style-unfocused', Lang.bind(this, this._settingsChangeRefresh));
|
|
||||||
this._dtpSettings.connect('changed::dot-color-override', Lang.bind(this, this._settingsChangeRefresh));
|
|
||||||
this._dtpSettings.connect('changed::dot-color-1', Lang.bind(this, this._settingsChangeRefresh));
|
|
||||||
this._dtpSettings.connect('changed::dot-color-2', Lang.bind(this, this._settingsChangeRefresh));
|
|
||||||
this._dtpSettings.connect('changed::dot-color-3', Lang.bind(this, this._settingsChangeRefresh));
|
|
||||||
this._dtpSettings.connect('changed::dot-color-4', Lang.bind(this, this._settingsChangeRefresh));
|
|
||||||
this._dtpSettings.connect('changed::dot-color-unfocused-different', Lang.bind(this, this._settingsChangeRefresh));
|
|
||||||
this._dtpSettings.connect('changed::dot-color-unfocused-1', Lang.bind(this, this._settingsChangeRefresh));
|
|
||||||
this._dtpSettings.connect('changed::dot-color-unfocused-2', Lang.bind(this, this._settingsChangeRefresh));
|
|
||||||
this._dtpSettings.connect('changed::dot-color-unfocused-3', Lang.bind(this, this._settingsChangeRefresh));
|
|
||||||
this._dtpSettings.connect('changed::dot-color-unfocused-4', Lang.bind(this, this._settingsChangeRefresh));
|
|
||||||
this._dtpSettings.connect('changed::focus-highlight', Lang.bind(this, this._settingsChangeRefresh));
|
|
||||||
|
|
||||||
this._dtpSettings.connect('changed::appicon-margin', Lang.bind(this, this._setIconStyle));
|
|
||||||
|
|
||||||
// Creating a new menu manager for window previews as adding it to the
|
|
||||||
// using the secondary menu's menu manager (which uses the "ignoreRelease"
|
|
||||||
// function) caused the extension to crash.
|
|
||||||
this.menuManagerWindowPreview = new PopupMenu.PopupMenuManager(this);
|
|
||||||
|
|
||||||
this.windowPreview = new WindowPreview.thumbnailPreviewMenu(this, this._dtpSettings, this.menuManagerWindowPreview);
|
|
||||||
|
|
||||||
this.windowPreview.connect('open-state-changed', Lang.bind(this, function (menu, isPoppedUp) {
|
|
||||||
if (!isPoppedUp)
|
|
||||||
this._onMenuPoppedDown();
|
|
||||||
}));
|
|
||||||
this.menuManagerWindowPreview.addMenu(this.windowPreview);
|
|
||||||
|
|
||||||
// grabHelper.grab() is usually called when the menu is opened. However, there seems to be a bug in the
|
|
||||||
// underlying gnome-shell that causes all window contents to freeze if the grab and ungrab occur
|
|
||||||
// in quick succession in timeouts from the Mainloop (for example, clicking the icon as the preview window is opening)
|
|
||||||
// So, instead wait until the mouse is leaving the icon (and might be moving toward the open window) to trigger the grab
|
|
||||||
// in windowPreview.js
|
|
||||||
let windowPreviewMenuData = this.menuManagerWindowPreview._menus[this.menuManagerWindowPreview._findMenu(this.windowPreview)];
|
|
||||||
this.windowPreview.disconnect(windowPreviewMenuData.openStateChangeId);
|
|
||||||
windowPreviewMenuData.openStateChangeId = this.windowPreview.connect('open-state-changed', Lang.bind(this.menuManagerWindowPreview, function(menu, open) {
|
|
||||||
if (open) {
|
|
||||||
if (this.activeMenu)
|
|
||||||
this.activeMenu.close(BoxPointer.PopupAnimation.FADE);
|
|
||||||
|
|
||||||
// don't grab here, we are grabbing in onLeave in windowPreview.js
|
|
||||||
//this._grabHelper.grab({ actor: menu.actor, focus: menu.sourceActor, onUngrab: Lang.bind(this, this._closeMenu, menu) });
|
|
||||||
} else {
|
|
||||||
this._grabHelper.ungrab({ actor: menu.actor });
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.forcedOverview = false;
|
|
||||||
|
|
||||||
this._numberOverlay();
|
|
||||||
},
|
|
||||||
|
|
||||||
shouldShowTooltip: function() {
|
|
||||||
if (this._dtpSettings.get_boolean("show-window-previews") &&
|
|
||||||
getInterestingWindows(this.app, this._dtpSettings).length > 0) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return this.actor.hover && (!this._menu || !this._menu.isOpen) && (!this.windowPreview || !this.windowPreview.isOpen);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onDestroy: function() {
|
|
||||||
this.parent();
|
|
||||||
|
|
||||||
// Disconect global signals
|
|
||||||
// stateChangedId is already handled by parent)
|
|
||||||
if(this._focusAppId>0)
|
|
||||||
tracker.disconnect(this._focusAppId);
|
|
||||||
},
|
|
||||||
|
|
||||||
onWindowsChanged: function() {
|
|
||||||
this._updateCounterClass();
|
|
||||||
this.updateIconGeometry();
|
|
||||||
},
|
|
||||||
|
|
||||||
// Update taraget for minimization animation
|
|
||||||
updateIconGeometry: function() {
|
|
||||||
|
|
||||||
// If (for unknown reason) the actor is not on the stage the reported size
|
|
||||||
// and position are random values, which might exceeds the integer range
|
|
||||||
// resulting in an error when assigned to the a rect. This is a more like
|
|
||||||
// a workaround to prevent flooding the system with errors.
|
|
||||||
if (this.actor.get_stage() == null)
|
|
||||||
return
|
|
||||||
|
|
||||||
let rect = new Meta.Rectangle();
|
|
||||||
|
|
||||||
[rect.x, rect.y] = this.actor.get_transformed_position();
|
|
||||||
[rect.width, rect.height] = this.actor.get_transformed_size();
|
|
||||||
|
|
||||||
let windows = this.app.get_windows();
|
|
||||||
windows.forEach(function(w) {
|
|
||||||
w.set_icon_geometry(rect);
|
|
||||||
});
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
_showDots: function() {
|
|
||||||
// Just update style if dots already exist
|
|
||||||
if (this._focusedDots && this._unfocusedDots) {
|
|
||||||
this._updateCounterClass();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._focusedDots = new St.DrawingArea({width:1, y_expand: true});
|
|
||||||
this._unfocusedDots = new St.DrawingArea({width:1, y_expand: true});
|
|
||||||
|
|
||||||
this._focusedDots.connect('repaint', Lang.bind(this, function() {
|
|
||||||
if(this._dashItemContainer.animatingIn || this._dashItemContainer.animatingOut) {
|
|
||||||
// don't draw and trigger more animations if the icon is in the middle of
|
|
||||||
// being added to the panel
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._drawRunningIndicator(this._focusedDots, this._dtpSettings.get_string('dot-style-focused'), true);
|
|
||||||
this._displayProperIndicator();
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._unfocusedDots.connect('repaint', Lang.bind(this, function() {
|
|
||||||
if(this._dashItemContainer.animatingIn || this._dashItemContainer.animatingOut) {
|
|
||||||
// don't draw and trigger more animations if the icon is in the middle of
|
|
||||||
// being added to the panel
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._drawRunningIndicator(this._unfocusedDots, this._dtpSettings.get_string('dot-style-unfocused'), false);
|
|
||||||
this._displayProperIndicator();
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
this._iconContainer.add_child(this._focusedDots);
|
|
||||||
this._iconContainer.add_child(this._unfocusedDots);
|
|
||||||
|
|
||||||
this._updateCounterClass();
|
|
||||||
},
|
|
||||||
|
|
||||||
_settingsChangeRefresh: function() {
|
|
||||||
this._updateCounterClass();
|
|
||||||
this._focusedDots.queue_repaint();
|
|
||||||
this._unfocusedDots.queue_repaint();
|
|
||||||
this._displayProperIndicator(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
_setIconStyle: function() {
|
|
||||||
let margin = this._dtpSettings.get_int('appicon-margin');
|
|
||||||
let inlineStyle = 'margin: 0 ' + margin + 'px;';
|
|
||||||
|
|
||||||
if(this._dtpSettings.get_boolean('focus-highlight') && tracker.focus_app == this.app && !this._isThemeProvidingIndicator()) {
|
|
||||||
let containerWidth = this._iconContainer.get_width() / St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
|
||||||
let focusedDotStyle = this._dtpSettings.get_string('dot-style-focused');
|
|
||||||
let isWide = this._isWideDotStyle(focusedDotStyle);
|
|
||||||
let pos = this._dtpSettings.get_string('dot-position');
|
|
||||||
let highlightMargin = isWide ? this._dtpSettings.get_int('dot-size') : 0;
|
|
||||||
|
|
||||||
if(focusedDotStyle == DOT_STYLE.CILIORA || focusedDotStyle == DOT_STYLE.SEGMENTED)
|
|
||||||
highlightMargin += 1;
|
|
||||||
|
|
||||||
inlineStyle += "background-image: url('" +
|
|
||||||
Me.path + "/img/highlight_" +
|
|
||||||
((this._nWindows > 1 && focusedDotStyle == DOT_STYLE.METRO) ? "stacked_" : "") +
|
|
||||||
"bg.svg'); background-position: 0 " +
|
|
||||||
(pos == DOT_POSITION.TOP ? highlightMargin : 0) +
|
|
||||||
"px; background-size: " +
|
|
||||||
containerWidth + "px " +
|
|
||||||
(containerWidth - (pos == DOT_POSITION.BOTTOM ? highlightMargin : 0)) + "px;";
|
|
||||||
}
|
|
||||||
|
|
||||||
// graphical glitches if i dont set this on a timeout
|
|
||||||
if(this.actor.get_style() != inlineStyle)
|
|
||||||
Mainloop.timeout_add(0, Lang.bind(this, function() { this.actor.set_style(inlineStyle); }));
|
|
||||||
},
|
|
||||||
|
|
||||||
popupMenu: function() {
|
|
||||||
this._removeMenuTimeout();
|
|
||||||
this.actor.fake_release();
|
|
||||||
this._draggable.fakeRelease();
|
|
||||||
|
|
||||||
if (!this._menu) {
|
|
||||||
this._menu = new SecondaryMenu.taskbarSecondaryMenu(this, this._dtpSettings);
|
|
||||||
this._menu.connect('activate-window', Lang.bind(this, function (menu, window) {
|
|
||||||
this.activateWindow(window, this._dtpSettings);
|
|
||||||
}));
|
|
||||||
this._menu.connect('open-state-changed', Lang.bind(this, function (menu, isPoppedUp) {
|
|
||||||
if (!isPoppedUp)
|
|
||||||
this._onMenuPoppedDown();
|
|
||||||
}));
|
|
||||||
let id = Main.overview.connect('hiding', Lang.bind(this, function () { this._menu.close(); }));
|
|
||||||
this._menu.actor.connect('destroy', function() {
|
|
||||||
Main.overview.disconnect(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
this._menuManager.addMenu(this._menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.emit('menu-state-changed', true);
|
|
||||||
|
|
||||||
this.windowPreview.close();
|
|
||||||
|
|
||||||
this.actor.set_hover(true);
|
|
||||||
this._menu.actor.add_style_class_name('dashtopanelSecondaryMenu');
|
|
||||||
this._menu.popup();
|
|
||||||
this._menuManager.ignoreRelease();
|
|
||||||
this.emit('sync-tooltip');
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onFocusAppChanged: function(windowTracker) {
|
|
||||||
this._displayProperIndicator();
|
|
||||||
},
|
|
||||||
|
|
||||||
_displayProperIndicator: function (force) {
|
|
||||||
let containerWidth = this._iconContainer.get_width();
|
|
||||||
let isFocused = (tracker.focus_app == this.app);
|
|
||||||
let focusedDotStyle = this._dtpSettings.get_string('dot-style-focused');
|
|
||||||
let unfocusedDotStyle = this._dtpSettings.get_string('dot-style-unfocused');
|
|
||||||
let focusedIsWide = this._isWideDotStyle(focusedDotStyle);
|
|
||||||
let unfocusedIsWide = this._isWideDotStyle(unfocusedDotStyle);
|
|
||||||
|
|
||||||
this._setIconStyle();
|
|
||||||
|
|
||||||
let newFocusedDotsWidth = 0;
|
|
||||||
let newFocusedDotsOpacity = 0;
|
|
||||||
let newUnfocusedDotsWidth = 0;
|
|
||||||
let newUnfocusedDotsOpacity = 0;
|
|
||||||
|
|
||||||
|
|
||||||
if(isFocused)
|
|
||||||
this.actor.add_style_class_name('focused');
|
|
||||||
else
|
|
||||||
this.actor.remove_style_class_name('focused');
|
|
||||||
|
|
||||||
if(focusedIsWide) {
|
|
||||||
newFocusedDotsWidth = (isFocused && this._nWindows > 0) ? containerWidth : 0;
|
|
||||||
newFocusedDotsOpacity = 255;
|
|
||||||
} else {
|
|
||||||
newFocusedDotsWidth = containerWidth;
|
|
||||||
newFocusedDotsOpacity = (isFocused && this._nWindows > 0) ? 255 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(unfocusedIsWide) {
|
|
||||||
newUnfocusedDotsWidth = (!isFocused && this._nWindows > 0) ? containerWidth : 0;
|
|
||||||
newUnfocusedDotsOpacity = 255;
|
|
||||||
} else {
|
|
||||||
newUnfocusedDotsWidth = containerWidth;
|
|
||||||
newUnfocusedDotsOpacity = (!isFocused && this._nWindows > 0) ? 255 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only animate if...
|
|
||||||
// animation is enabled in settings
|
|
||||||
// AND (going from a wide style to a narrow style indicator or vice-versa
|
|
||||||
// OR going from an open app to a closed app or vice versa)
|
|
||||||
if(this._dtpSettings.get_boolean('animate-app-switch') &&
|
|
||||||
((focusedIsWide != unfocusedIsWide) ||
|
|
||||||
(this._focusedDots.width != newUnfocusedDotsWidth || this._unfocusedDots.width != newFocusedDotsWidth))) {
|
|
||||||
this._animateDotDisplay(this._focusedDots, newFocusedDotsWidth, this._unfocusedDots, newUnfocusedDotsOpacity, force);
|
|
||||||
this._animateDotDisplay(this._unfocusedDots, newUnfocusedDotsWidth, this._focusedDots, newFocusedDotsOpacity, force);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this._focusedDots.opacity = newFocusedDotsOpacity;
|
|
||||||
this._unfocusedDots.opacity = newUnfocusedDotsOpacity;
|
|
||||||
this._focusedDots.width = newFocusedDotsWidth;
|
|
||||||
this._unfocusedDots.width = newUnfocusedDotsWidth;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_animateDotDisplay: function (dots, newWidth, otherDots, newOtherOpacity, force) {
|
|
||||||
if((dots.width != newWidth && dots._tweeningToWidth !== newWidth) || force) {
|
|
||||||
dots._tweeningToWidth = newWidth;
|
|
||||||
Tweener.addTween(dots,
|
|
||||||
{ width: newWidth,
|
|
||||||
time: DASH_ANIMATION_TIME,
|
|
||||||
transition: 'easeInOutCubic',
|
|
||||||
onStart: Lang.bind(this, function() {
|
|
||||||
if(newOtherOpacity == 0)
|
|
||||||
otherDots.opacity = newOtherOpacity;
|
|
||||||
}),
|
|
||||||
onComplete: Lang.bind(this, function() {
|
|
||||||
if(newOtherOpacity > 0)
|
|
||||||
otherDots.opacity = newOtherOpacity;
|
|
||||||
dots._tweeningToWidth = null;
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_isWideDotStyle: function(dotStyle) {
|
|
||||||
return dotStyle == DOT_STYLE.SEGMENTED ||
|
|
||||||
dotStyle == DOT_STYLE.CILIORA ||
|
|
||||||
dotStyle == DOT_STYLE.METRO ||
|
|
||||||
dotStyle == DOT_STYLE.SOLID;
|
|
||||||
},
|
|
||||||
|
|
||||||
_isThemeProvidingIndicator: function () {
|
|
||||||
// This is an attempt to determine if the theme is providing their own
|
|
||||||
// running indicator by way of a border image on the icon, for example in
|
|
||||||
// the theme Ciliora
|
|
||||||
return (this.icon.actor.get_stage() &&
|
|
||||||
this.icon.actor.get_theme_node().get_border_image());
|
|
||||||
},
|
|
||||||
|
|
||||||
activate: function(button) {
|
|
||||||
this.windowPreview.requestCloseMenu();
|
|
||||||
|
|
||||||
let event = Clutter.get_current_event();
|
|
||||||
let modifiers = event ? event.get_state() : 0;
|
|
||||||
let focusedApp = tracker.focus_app;
|
|
||||||
|
|
||||||
// Only consider SHIFT and CONTROL as modifiers (exclude SUPER, CAPS-LOCK, etc.)
|
|
||||||
modifiers = modifiers & (Clutter.ModifierType.SHIFT_MASK | Clutter.ModifierType.CONTROL_MASK);
|
|
||||||
|
|
||||||
// We don't change the CTRL-click behaviour: in such case we just chain
|
|
||||||
// up the parent method and return.
|
|
||||||
if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
|
|
||||||
// Keep default behaviour: launch new window
|
|
||||||
// By calling the parent method I make it compatible
|
|
||||||
// with other extensions tweaking ctrl + click
|
|
||||||
this.parent(button);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We check what type of click we have and if the modifier SHIFT is
|
|
||||||
// being used. We then define what buttonAction should be for this
|
|
||||||
// event.
|
|
||||||
let buttonAction = 0;
|
|
||||||
if (button && button == 2 ) {
|
|
||||||
if (modifiers & Clutter.ModifierType.SHIFT_MASK)
|
|
||||||
buttonAction = this._dtpSettings.get_string('shift-middle-click-action');
|
|
||||||
else
|
|
||||||
buttonAction = this._dtpSettings.get_string('middle-click-action');
|
|
||||||
}
|
|
||||||
else if (button && button == 1) {
|
|
||||||
if (modifiers & Clutter.ModifierType.SHIFT_MASK)
|
|
||||||
buttonAction = this._dtpSettings.get_string('shift-click-action');
|
|
||||||
else
|
|
||||||
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 (appIsRunning) {
|
|
||||||
switch (buttonAction) {
|
|
||||||
case "RAISE":
|
|
||||||
activateAllWindows(this.app, this._dtpSettings);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "LAUNCH":
|
|
||||||
if(this._dtpSettings.get_boolean('animate-window-launch'))
|
|
||||||
this.animateLaunch();
|
|
||||||
this.app.open_new_window(-1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "MINIMIZE":
|
|
||||||
// In overview just activate the app, unless the acion is explicitely
|
|
||||||
// requested with a keyboard modifier
|
|
||||||
if (!Main.overview._shown || modifiers){
|
|
||||||
// If we have button=2 or a modifier, allow minimization even if
|
|
||||||
// the app is not focused
|
|
||||||
if (this.app == focusedApp || button == 2 || modifiers & Clutter.ModifierType.SHIFT_MASK) {
|
|
||||||
// minimize all windows on double click and always in the case of primary click without
|
|
||||||
// additional modifiers
|
|
||||||
let click_count = 0;
|
|
||||||
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, this._dtpSettings);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
activateAllWindows(this.app, this._dtpSettings);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
this.app.activate();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "CYCLE":
|
|
||||||
if (!Main.overview._shown){
|
|
||||||
if (this.app == focusedApp)
|
|
||||||
cycleThroughWindows(this.app, this._dtpSettings, false, false);
|
|
||||||
else {
|
|
||||||
activateFirstWindow(this.app, this._dtpSettings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
this.app.activate();
|
|
||||||
break;
|
|
||||||
case "CYCLE-MIN":
|
|
||||||
if (!Main.overview._shown){
|
|
||||||
if (this.app == focusedApp ||
|
|
||||||
(recentlyClickedApp == this.app && recentlyClickedAppWindows[recentlyClickedAppIndex % recentlyClickedAppWindows.length] == "MINIMIZE"))
|
|
||||||
cycleThroughWindows(this.app, this._dtpSettings, false, true);
|
|
||||||
else {
|
|
||||||
activateFirstWindow(this.app, this._dtpSettings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
this.app.activate();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "QUIT":
|
|
||||||
closeAllWindows(this.app, this._dtpSettings);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(this._dtpSettings.get_boolean('animate-window-launch'))
|
|
||||||
this.animateLaunch();
|
|
||||||
this.app.open_new_window(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Main.overview.hide();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateCounterClass: function() {
|
|
||||||
let maxN = 4;
|
|
||||||
this._nWindows = Math.min(getInterestingWindows(this.app, this._dtpSettings).length, maxN);
|
|
||||||
|
|
||||||
for (let i = 1; i <= maxN; i++){
|
|
||||||
let className = 'running'+i;
|
|
||||||
if(i != this._nWindows)
|
|
||||||
this.actor.remove_style_class_name(className);
|
|
||||||
else
|
|
||||||
this.actor.add_style_class_name(className);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_drawRunningIndicator: function(area, type, isFocused) {
|
|
||||||
let bodyColor;
|
|
||||||
if(this._dtpSettings.get_boolean('dot-color-override')) {
|
|
||||||
let dotColorSettingPrefix = 'dot-color-';
|
|
||||||
if(!isFocused && this._dtpSettings.get_boolean('dot-color-unfocused-different'))
|
|
||||||
dotColorSettingPrefix = 'dot-color-unfocused-';
|
|
||||||
bodyColor = Clutter.color_from_string(this._dtpSettings.get_string(dotColorSettingPrefix + (this._nWindows > 0 ? this._nWindows : 1)))[1];
|
|
||||||
} else {
|
|
||||||
// Re-use the style - background color, and border width and color -
|
|
||||||
// of the default dot
|
|
||||||
let themeNode = this._dot.get_theme_node();
|
|
||||||
bodyColor = themeNode.get_background_color();
|
|
||||||
if(bodyColor.alpha == 0) // theme didn't provide one, use a default
|
|
||||||
bodyColor = new Clutter.Color({ red: 82, green: 148, blue: 226, alpha: 255 });
|
|
||||||
}
|
|
||||||
|
|
||||||
let [width, height] = area.get_surface_size();
|
|
||||||
let cr = area.get_context();
|
|
||||||
let n = this._nWindows;
|
|
||||||
let size = this._dtpSettings.get_int('dot-size') * St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
|
||||||
let padding = 0; // distance from the margin
|
|
||||||
let yOffset = this._dtpSettings.get_string('dot-position') == DOT_POSITION.TOP ? 0 : (height - padding - size);
|
|
||||||
|
|
||||||
if(type == DOT_STYLE.DOTS) {
|
|
||||||
// Draw the required numbers of dots
|
|
||||||
let radius = size/2;
|
|
||||||
let spacing = Math.ceil(width/18); // separation between the dots
|
|
||||||
|
|
||||||
cr.translate((width - (2*n)*radius - (n-1)*spacing)/2, yOffset);
|
|
||||||
|
|
||||||
Clutter.cairo_set_source_color(cr, bodyColor);
|
|
||||||
for (let i = 0; i < n; i++) {
|
|
||||||
cr.newSubPath();
|
|
||||||
cr.arc((2*i+1)*radius + i*spacing, radius, radius, 0, 2*Math.PI);
|
|
||||||
}
|
|
||||||
cr.fill();
|
|
||||||
} else if(type == DOT_STYLE.SQUARES) {
|
|
||||||
let spacing = Math.ceil(width/18); // separation between the dots
|
|
||||||
|
|
||||||
cr.translate(Math.floor((width - n*size - (n-1)*spacing)/2), yOffset);
|
|
||||||
|
|
||||||
Clutter.cairo_set_source_color(cr, bodyColor);
|
|
||||||
for (let i = 0; i < n; i++) {
|
|
||||||
cr.newSubPath();
|
|
||||||
cr.rectangle(i*size + i*spacing, 0, size, size);
|
|
||||||
}
|
|
||||||
cr.fill();
|
|
||||||
} else if(type == DOT_STYLE.DASHES) {
|
|
||||||
let spacing = Math.ceil(width/18); // separation between the dots
|
|
||||||
let dashLength = Math.floor(width/4) - spacing;
|
|
||||||
|
|
||||||
cr.translate(Math.floor((width - n*dashLength - (n-1)*spacing)/2), yOffset);
|
|
||||||
|
|
||||||
Clutter.cairo_set_source_color(cr, bodyColor);
|
|
||||||
for (let i = 0; i < n; i++) {
|
|
||||||
cr.newSubPath();
|
|
||||||
cr.rectangle(i*dashLength + i*spacing, 0, dashLength, size);
|
|
||||||
}
|
|
||||||
cr.fill();
|
|
||||||
} else if(type == DOT_STYLE.SEGMENTED) {
|
|
||||||
let spacing = Math.ceil(width/18); // separation between the dots
|
|
||||||
let dashLength = Math.ceil((width - ((n-1)*spacing))/n);
|
|
||||||
|
|
||||||
cr.translate(0, yOffset);
|
|
||||||
|
|
||||||
Clutter.cairo_set_source_color(cr, bodyColor);
|
|
||||||
for (let i = 0; i < n; i++) {
|
|
||||||
cr.newSubPath();
|
|
||||||
cr.rectangle(i*dashLength + i*spacing, 0, dashLength, size);
|
|
||||||
}
|
|
||||||
cr.fill();
|
|
||||||
} else if (type == DOT_STYLE.CILIORA) {
|
|
||||||
let spacing = size; // separation between the dots
|
|
||||||
let lineLength = width - (size*(n-1)) - (spacing*(n-1));
|
|
||||||
|
|
||||||
cr.translate(0, yOffset);
|
|
||||||
|
|
||||||
Clutter.cairo_set_source_color(cr, bodyColor);
|
|
||||||
cr.newSubPath();
|
|
||||||
cr.rectangle(0, 0, lineLength, size);
|
|
||||||
for (let i = 1; i < n; i++) {
|
|
||||||
cr.newSubPath();
|
|
||||||
cr.rectangle(lineLength + (i*spacing) + ((i-1)*size), 0, size, size);
|
|
||||||
}
|
|
||||||
cr.fill();
|
|
||||||
} else if (type == DOT_STYLE.METRO) {
|
|
||||||
if(n <= 1) {
|
|
||||||
cr.translate(0, yOffset);
|
|
||||||
Clutter.cairo_set_source_color(cr, bodyColor);
|
|
||||||
cr.newSubPath();
|
|
||||||
cr.rectangle(0, 0, width, size);
|
|
||||||
cr.fill();
|
|
||||||
} else {
|
|
||||||
let blackenedLength = (1/48)*width; // need to scale with the SVG for the stacked highlight
|
|
||||||
let darkenedLength = isFocused ? (2/48)*width : (10/48)*width;
|
|
||||||
let blackenedColor = bodyColor.shade(.3);
|
|
||||||
let darkenedColor = bodyColor.shade(.7);
|
|
||||||
|
|
||||||
cr.translate(0, yOffset);
|
|
||||||
|
|
||||||
Clutter.cairo_set_source_color(cr, bodyColor);
|
|
||||||
cr.newSubPath();
|
|
||||||
cr.rectangle(0, 0, width - darkenedLength - blackenedLength, size);
|
|
||||||
cr.fill();
|
|
||||||
Clutter.cairo_set_source_color(cr, blackenedColor);
|
|
||||||
cr.newSubPath();
|
|
||||||
cr.rectangle(width - darkenedLength - blackenedLength, 0, 1, size);
|
|
||||||
cr.fill();
|
|
||||||
Clutter.cairo_set_source_color(cr, darkenedColor);
|
|
||||||
cr.newSubPath();
|
|
||||||
cr.rectangle(width - darkenedLength, 0, darkenedLength, size);
|
|
||||||
cr.fill();
|
|
||||||
}
|
|
||||||
} else { // solid
|
|
||||||
cr.translate(0, yOffset);
|
|
||||||
Clutter.cairo_set_source_color(cr, bodyColor);
|
|
||||||
cr.newSubPath();
|
|
||||||
cr.rectangle(0, 0, width, size);
|
|
||||||
cr.fill();
|
|
||||||
}
|
|
||||||
|
|
||||||
cr.$dispose();
|
|
||||||
},
|
|
||||||
|
|
||||||
_numberOverlay: function() {
|
|
||||||
// Add label for a Hot-Key visual aid
|
|
||||||
this._numberOverlayLabel = new St.Label();
|
|
||||||
this._numberOverlayBin = new St.Bin({
|
|
||||||
child: this._numberOverlayLabel,
|
|
||||||
x_align: St.Align.START, y_align: St.Align.START,
|
|
||||||
x_expand: true, y_expand: true
|
|
||||||
});
|
|
||||||
this._numberOverlayStyle = 'background-color: rgba(0,0,0,0.8);'
|
|
||||||
this._numberOverlayOrder = -1;
|
|
||||||
this._numberOverlayBin.hide();
|
|
||||||
|
|
||||||
this._iconContainer.add_child(this._numberOverlayBin);
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
updateNumberOverlay: function() {
|
|
||||||
// Set the font size to something smaller than the whole icon so it is
|
|
||||||
// still visible. The border radius is large to make the shape circular
|
|
||||||
let [minWidth, natWidth] = this._iconContainer.get_preferred_width(-1);
|
|
||||||
let font_size = Math.round(Math.max(12, 0.3*natWidth));
|
|
||||||
let size = Math.round(font_size*1.2);
|
|
||||||
this._numberOverlayLabel.set_style(
|
|
||||||
this._numberOverlayStyle +
|
|
||||||
'font-size: ' + font_size + 'px;' +
|
|
||||||
'text-align: center;' +
|
|
||||||
'border-radius: ' + this.icon.iconSize + 'px;' +
|
|
||||||
'width: ' + size + 'px; height: ' + size +'px;'
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
setNumberOverlay: function(number) {
|
|
||||||
this._numberOverlayOrder = number;
|
|
||||||
this._numberOverlayLabel.set_text(number.toString());
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleNumberOverlay: function(activate) {
|
|
||||||
if (activate && this._numberOverlayOrder > -1)
|
|
||||||
this._numberOverlayBin.show();
|
|
||||||
else
|
|
||||||
this._numberOverlayBin.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
function minimizeWindow(app, param, settings){
|
|
||||||
// Param true make all app windows minimize
|
|
||||||
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];
|
|
||||||
if (w.get_workspace() == current_workspace && w.showing_on_its_workspace()){
|
|
||||||
w.minimize();
|
|
||||||
// Just minimize one window. By specification it should be the
|
|
||||||
// focused window on the current workspace.
|
|
||||||
if(!param)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* By default only non minimized windows are activated.
|
|
||||||
* This activates all windows in the current workspace.
|
|
||||||
*/
|
|
||||||
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 = getInterestingWindows(app, settings);
|
|
||||||
let w = windows[0];
|
|
||||||
Main.activateWindow(w);
|
|
||||||
let activeWorkspace = global.screen.get_active_workspace_index();
|
|
||||||
|
|
||||||
if (windows.length <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let activatedWindows = 0;
|
|
||||||
|
|
||||||
for (let i = windows.length - 1; i >= 0; i--){
|
|
||||||
if (windows[i].get_workspace().index() == activeWorkspace){
|
|
||||||
Main.activateWindow(windows[i]);
|
|
||||||
activatedWindows++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function activateFirstWindow(app, settings){
|
|
||||||
|
|
||||||
let windows = getInterestingWindows(app, settings);
|
|
||||||
Main.activateWindow(windows[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function cycleThroughWindows(app, settings, reversed, shouldMinimize) {
|
|
||||||
// Store for a little amount of time last clicked app and its windows
|
|
||||||
// since the order changes upon window interaction
|
|
||||||
let MEMORY_TIME=3000;
|
|
||||||
|
|
||||||
let app_windows = getInterestingWindows(app, settings);
|
|
||||||
|
|
||||||
if(shouldMinimize)
|
|
||||||
app_windows.push("MINIMIZE");
|
|
||||||
|
|
||||||
if (recentlyClickedAppLoopId > 0)
|
|
||||||
Mainloop.source_remove(recentlyClickedAppLoopId);
|
|
||||||
recentlyClickedAppLoopId = Mainloop.timeout_add(MEMORY_TIME, resetRecentlyClickedApp);
|
|
||||||
|
|
||||||
// If there isn't already a list of windows for the current app,
|
|
||||||
// or the stored list is outdated, use the current windows list.
|
|
||||||
if (!recentlyClickedApp ||
|
|
||||||
recentlyClickedApp.get_id() != app.get_id() ||
|
|
||||||
recentlyClickedAppWindows.length != app_windows.length) {
|
|
||||||
recentlyClickedApp = app;
|
|
||||||
recentlyClickedAppWindows = app_windows;
|
|
||||||
recentlyClickedAppIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reversed) {
|
|
||||||
recentlyClickedAppIndex--;
|
|
||||||
if (recentlyClickedAppIndex < 0) recentlyClickedAppIndex = recentlyClickedAppWindows.length - 1;
|
|
||||||
} else {
|
|
||||||
recentlyClickedAppIndex++;
|
|
||||||
}
|
|
||||||
let index = recentlyClickedAppIndex % recentlyClickedAppWindows.length;
|
|
||||||
|
|
||||||
if(recentlyClickedAppWindows[index] === "MINIMIZE")
|
|
||||||
minimizeWindow(app, true, settings);
|
|
||||||
else
|
|
||||||
Main.activateWindow(recentlyClickedAppWindows[index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetRecentlyClickedApp() {
|
|
||||||
if (recentlyClickedAppLoopId > 0)
|
|
||||||
Mainloop.source_remove(recentlyClickedAppLoopId);
|
|
||||||
recentlyClickedAppLoopId=0;
|
|
||||||
recentlyClickedApp =null;
|
|
||||||
recentlyClickedAppWindows = null;
|
|
||||||
recentlyClickedAppIndex = 0;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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, settings) {
|
function getAppInterestingWindows(app, settings) {
|
||||||
let windows = app.get_windows().filter(function(w) {
|
let windows = app.get_windows().filter(function(w) {
|
||||||
return !w.skip_taskbar;
|
return !w.skip_taskbar;
|
||||||
@@ -2013,23 +1186,6 @@ function getAppInterestingWindows(app, settings) {
|
|||||||
return windows;
|
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
|
* This is a copy of the same function in utils.js, but also adjust horizontal scrolling
|
||||||
* and perform few further cheks on the current value to avoid changing the values when
|
* and perform few further cheks on the current value to avoid changing the values when
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ const Workspace = imports.ui.workspace;
|
|||||||
|
|
||||||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||||
const Taskbar = Me.imports.taskbar;
|
const Taskbar = Me.imports.taskbar;
|
||||||
|
const Convenience = Me.imports.convenience;
|
||||||
|
const AppIcons = Me.imports.appIcons;
|
||||||
|
|
||||||
let DEFAULT_THUMBNAIL_WIDTH = 350;
|
let DEFAULT_THUMBNAIL_WIDTH = 350;
|
||||||
let DEFAULT_THUMBNAIL_HEIGHT = 200;
|
let DEFAULT_THUMBNAIL_HEIGHT = 200;
|
||||||
@@ -96,7 +98,7 @@ const thumbnailPreviewMenu = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
popup: function() {
|
popup: function() {
|
||||||
let windows = Taskbar.getInterestingWindows(this._app, this._dtpSettings);
|
let windows = AppIcons.getInterestingWindows(this._app, this._dtpSettings);
|
||||||
if (windows.length > 0) {
|
if (windows.length > 0) {
|
||||||
this._redisplay();
|
this._redisplay();
|
||||||
this.open();
|
this.open();
|
||||||
@@ -592,7 +594,7 @@ const thumbnailPreviewList = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_redisplay: function () {
|
_redisplay: function () {
|
||||||
let windows = Taskbar.getInterestingWindows(this.app, this._dtpSettings).sort(this.sortWindowsCompareFunction);
|
let windows = AppIcons.getInterestingWindows(this.app, this._dtpSettings).sort(this.sortWindowsCompareFunction);
|
||||||
let children = this.box.get_children().filter(function(actor) {
|
let children = this.box.get_children().filter(function(actor) {
|
||||||
return actor._delegate.window && actor._delegate.preview;
|
return actor._delegate.window && actor._delegate.preview;
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user