mirror of
https://github.com/morgan9e/dash-to-panel
synced 2026-04-14 00:04:17 +09:00
1228 lines
48 KiB
JavaScript
1228 lines
48 KiB
JavaScript
/*
|
||
* 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 Workspace = imports.ui.workspace;
|
||
|
||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||
const Utils = Me.imports.utils;
|
||
const WindowPreview = Me.imports.windowPreview;
|
||
const AppIcons = Me.imports.appIcons;
|
||
|
||
var DASH_ANIMATION_TIME = Dash.DASH_ANIMATION_TIME / (Dash.DASH_ANIMATION_TIME > 1 ? 1000 : 1);
|
||
var DASH_ITEM_HOVER_TIMEOUT = Dash.DASH_ITEM_HOVER_TIMEOUT;
|
||
var MIN_ICON_SIZE = 4;
|
||
var dtpSettings;
|
||
|
||
function getPosition() {
|
||
let position = dtpSettings.get_string('panel-position');
|
||
|
||
if (position == 'TOP') {
|
||
return St.Side.TOP;
|
||
} else if (position == 'RIGHT') {
|
||
return St.Side.RIGHT;
|
||
} else if (position == 'BOTTOM') {
|
||
return St.Side.BOTTOM;
|
||
}
|
||
|
||
return St.Side.LEFT;
|
||
}
|
||
/**
|
||
* Extend DashItemContainer
|
||
*
|
||
* - set label position based on taskbar orientation
|
||
*
|
||
* I can't subclass the original object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973.
|
||
* thus use this ugly pattern.
|
||
*/
|
||
|
||
function extendDashItemContainer(dashItemContainer) {
|
||
dashItemContainer.showLabel = AppIcons.ItemShowLabel;
|
||
};
|
||
|
||
/* This class is a fork of the upstream DashActor class (ui.dash.js)
|
||
*
|
||
* Summary of changes:
|
||
* - modified chldBox calculations for when 'show-apps-at-top' option is checked
|
||
* - handle horizontal dash
|
||
*/
|
||
var taskbarActor = Utils.defineClass({
|
||
Name: 'DashToPanel-TaskbarActor',
|
||
Extends: St.Widget,
|
||
|
||
_init: function(delegate) {
|
||
this._delegate = delegate;
|
||
this._currentBackgroundColor = 0;
|
||
this.callParent('_init', { name: 'dashtopanelTaskbar',
|
||
layout_manager: new Clutter.BoxLayout({ orientation: Clutter.Orientation.HORIZONTAL }),
|
||
clip_to_allocation: true });
|
||
},
|
||
|
||
vfunc_allocate: function(box, flags) {
|
||
this.set_allocation(box, flags);
|
||
|
||
let availHeight = box.y2 - box.y1;
|
||
let [, showAppsButton, scrollview, leftFade, rightFade] = this.get_children();
|
||
let [, showAppsNatWidth] = showAppsButton.get_preferred_width(availHeight);
|
||
let childBox = new Clutter.ActorBox();
|
||
|
||
childBox.x1 = box.x1;
|
||
childBox.x2 = box.x1 + showAppsNatWidth;
|
||
childBox.y1 = box.y1;
|
||
childBox.y2 = box.y2;
|
||
showAppsButton.allocate(childBox, flags);
|
||
|
||
childBox.x1 = box.x1 + showAppsNatWidth;
|
||
childBox.x2 = box.x2;
|
||
scrollview.allocate(childBox, flags);
|
||
|
||
let [hvalue, , hupper, , , hpageSize] = scrollview.hscroll.adjustment.get_values();
|
||
hupper = Math.floor(hupper);
|
||
scrollview._dtpFadeSize = hupper > hpageSize ? this._delegate.iconSize : 0;
|
||
|
||
if (this._delegate.panelWrapper.dynamicTransparency &&
|
||
this._currentBackgroundColor !== this._delegate.panelWrapper.dynamicTransparency.currentBackgroundColor) {
|
||
this._currentBackgroundColor = this._delegate.panelWrapper.dynamicTransparency.currentBackgroundColor;
|
||
let gradientStart = 'background-gradient-start: ' + this._currentBackgroundColor;
|
||
leftFade.set_style(gradientStart);
|
||
rightFade.set_style(gradientStart);
|
||
}
|
||
|
||
childBox.x1 = box.x1 + showAppsNatWidth;
|
||
childBox.x2 = childBox.x1 + (hvalue > 0 ? scrollview._dtpFadeSize : 0);
|
||
leftFade.allocate(childBox, flags);
|
||
|
||
childBox.x1 = box.x2 - (hvalue + hpageSize < hupper ? scrollview._dtpFadeSize : 0);
|
||
childBox.x2 = box.x2;
|
||
rightFade.allocate(childBox, flags);
|
||
},
|
||
|
||
vfunc_get_preferred_width: function(forHeight) {
|
||
// We want to request the natural width of all our children
|
||
// as our natural width, so we chain up to StWidget (which
|
||
// then calls BoxLayout)
|
||
let [, natWidth] = St.Widget.prototype.vfunc_get_preferred_width.call(this, forHeight);
|
||
|
||
return [0, natWidth];
|
||
},
|
||
});
|
||
|
||
/* This class is a fork of the upstream dash class (ui.dash.js)
|
||
*
|
||
* Summary of changes:
|
||
* - disconnect global signals adding a destroy method;
|
||
* - play animations even when not in overview mode
|
||
* - set a maximum icon size
|
||
* - show running and/or favorite applications
|
||
* - emit a custom signal when an app icon is added
|
||
* - Add scrollview
|
||
* Ensure actor is visible on keyfocus inside the scrollview
|
||
* - add 128px icon size, might be useful for hidpi display
|
||
* - Sync minimization application target position.
|
||
*/
|
||
|
||
var taskbar = Utils.defineClass({
|
||
Name: 'DashToPanel.Taskbar',
|
||
|
||
_init : function(panelWrapper) {
|
||
this.panelWrapper = panelWrapper;
|
||
|
||
// start at smallest size due to running indicator drawing area expanding but not shrinking
|
||
this.iconSize = 16;
|
||
|
||
this._shownInitially = false;
|
||
|
||
this._position = getPosition();
|
||
this._signalsHandler = new Utils.GlobalSignalsHandler();
|
||
|
||
this._showLabelTimeoutId = 0;
|
||
this._resetHoverTimeoutId = 0;
|
||
this._ensureAppIconVisibilityTimeoutId = 0;
|
||
this._labelShowing = false;
|
||
|
||
this._box = new St.BoxLayout({ vertical: false,
|
||
clip_to_allocation: false,
|
||
x_align: Clutter.ActorAlign.START,
|
||
y_align: Clutter.ActorAlign.START });
|
||
|
||
this._container = new taskbarActor(this);
|
||
this._scrollView = new St.ScrollView({ name: 'dashtopanelScrollview',
|
||
hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||
vscrollbar_policy: Gtk.PolicyType.NEVER,
|
||
enable_mouse_scrolling: true });
|
||
|
||
this._scrollView.connect('scroll-event', Lang.bind(this, this._onScrollEvent ));
|
||
this._scrollView.add_actor(this._box);
|
||
|
||
// Create a wrapper around the real showAppsIcon in order to add a popupMenu.
|
||
this._showAppsIconWrapper = new AppIcons.ShowAppsIconWrapper(dtpSettings);
|
||
this._showAppsIconWrapper.connect('menu-state-changed', Lang.bind(this, function(showAppsIconWrapper, opened) {
|
||
this._itemMenuStateChanged(showAppsIconWrapper, opened);
|
||
}));
|
||
// an instance of the showAppsIcon class is encapsulated in the wrapper
|
||
this._showAppsIcon = this._showAppsIconWrapper.realShowAppsIcon;
|
||
this.showAppsButton = this._showAppsIcon.toggleButton;
|
||
|
||
this.showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled));
|
||
this.showAppsButton.checked = Main.overview.viewSelector._showAppsButton.checked;
|
||
|
||
this._showAppsIcon.childScale = 1;
|
||
this._showAppsIcon.childOpacity = 255;
|
||
this._showAppsIcon.icon.setIconSize(this.iconSize);
|
||
this._hookUpLabel(this._showAppsIcon, this._showAppsIconWrapper);
|
||
|
||
this._container.add_child(new St.Widget({ width: 0, reactive: false }));
|
||
this._container.add_actor(this._showAppsIcon);
|
||
this._container.add_actor(this._scrollView);
|
||
this._container.add_actor(new St.Widget({ style_class: 'scrollview-fade', reactive: false }));
|
||
this._container.add_actor(new St.Widget({ style_class: 'scrollview-fade',
|
||
reactive: false,
|
||
pivot_point: new Clutter.Point({ x: .5, y: .5 }),
|
||
rotation_angle_z: 180 }));
|
||
|
||
this.showAppsButton.add_constraint(new Clutter.BindConstraint({
|
||
source: this._container,
|
||
coordinate: Clutter.BindCoordinate.HEIGHT
|
||
}));
|
||
|
||
this.previewMenu = new WindowPreview.PreviewMenu(dtpSettings, panelWrapper);
|
||
this.previewMenu.enable();
|
||
|
||
if (!dtpSettings.get_boolean('show-show-apps-button'))
|
||
this.hideShowAppsButton();
|
||
|
||
let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
|
||
this.actor = new St.Bin({ child: this._container,
|
||
y_align: St.Align.START, x_align:rtl?St.Align.END:St.Align.START
|
||
});
|
||
|
||
// Update minimization animation target position on allocation of the
|
||
// container and on scrollview change.
|
||
this._box.connect('notify::allocation', Lang.bind(this, this._updateAppIcons));
|
||
let scrollViewAdjustment = this._scrollView.hscroll.adjustment;
|
||
scrollViewAdjustment.connect('notify::value', Lang.bind(this, this._updateAppIcons));
|
||
|
||
this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay));
|
||
|
||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' });
|
||
|
||
this._appSystem = Shell.AppSystem.get_default();
|
||
|
||
this._signalsHandler.add(
|
||
[
|
||
this.panelWrapper.panel.actor,
|
||
'notify::height',
|
||
() => this._queueRedisplay()
|
||
],
|
||
[
|
||
this.panelWrapper.panel.actor,
|
||
'notify::width',
|
||
() => this._queueRedisplay()
|
||
],
|
||
[
|
||
this._appSystem,
|
||
'installed-changed',
|
||
Lang.bind(this, function() {
|
||
AppFavorites.getAppFavorites().reload();
|
||
this._queueRedisplay();
|
||
})
|
||
],
|
||
[
|
||
this._appSystem,
|
||
'app-state-changed',
|
||
Lang.bind(this, this._queueRedisplay)
|
||
],
|
||
[
|
||
AppFavorites.getAppFavorites(),
|
||
'changed',
|
||
Lang.bind(this, this._queueRedisplay)
|
||
],
|
||
[
|
||
global.window_manager,
|
||
'switch-workspace',
|
||
() => this._connectWorkspaceSignals()
|
||
],
|
||
[
|
||
Utils.DisplayWrapper.getScreen(),
|
||
[
|
||
'window-entered-monitor',
|
||
'window-left-monitor'
|
||
],
|
||
() => {
|
||
if (dtpSettings.get_boolean('isolate-monitors')) {
|
||
this._queueRedisplay();
|
||
}
|
||
}
|
||
],
|
||
[
|
||
Main.overview,
|
||
'item-drag-begin',
|
||
Lang.bind(this, this._onDragBegin)
|
||
],
|
||
[
|
||
Main.overview,
|
||
'item-drag-end',
|
||
Lang.bind(this, this._onDragEnd)
|
||
],
|
||
[
|
||
Main.overview,
|
||
'item-drag-cancelled',
|
||
Lang.bind(this, this._onDragCancelled)
|
||
],
|
||
[
|
||
// Ensure the ShowAppsButton status is kept in sync
|
||
Main.overview.viewSelector._showAppsButton,
|
||
'notify::checked',
|
||
Lang.bind(this, this._syncShowAppsButtonToggled)
|
||
],
|
||
[
|
||
dtpSettings,
|
||
'changed::show-show-apps-button',
|
||
Lang.bind(this, function() {
|
||
if (dtpSettings.get_boolean('show-show-apps-button'))
|
||
this.showShowAppsButton();
|
||
else
|
||
this.hideShowAppsButton();
|
||
})
|
||
],
|
||
[
|
||
dtpSettings,
|
||
[
|
||
'changed::dot-size',
|
||
'changed::show-favorites',
|
||
'changed::show-running-apps',
|
||
'changed::show-favorites-all-monitors'
|
||
],
|
||
Lang.bind(this, this._redisplay)
|
||
],
|
||
[
|
||
dtpSettings,
|
||
'changed::group-apps',
|
||
Lang.bind(this, function() {
|
||
this.isGroupApps = dtpSettings.get_boolean('group-apps');
|
||
this._connectWorkspaceSignals();
|
||
this.resetAppIcons();
|
||
})
|
||
],
|
||
[
|
||
dtpSettings,
|
||
[
|
||
'changed::group-apps-use-launchers',
|
||
'changed::taskbar-locked'
|
||
],
|
||
() => this.resetAppIcons()
|
||
]
|
||
);
|
||
|
||
this.isGroupApps = dtpSettings.get_boolean('group-apps');
|
||
|
||
this._connectWorkspaceSignals();
|
||
},
|
||
|
||
destroy: function() {
|
||
this._signalsHandler.destroy();
|
||
this._signalsHandler = 0;
|
||
this._showAppsIconWrapper.destroy();
|
||
|
||
this._container.destroy();
|
||
|
||
this.previewMenu.disable();
|
||
this.previewMenu.destroy();
|
||
|
||
this._disconnectWorkspaceSignals();
|
||
},
|
||
|
||
_onScrollEvent: function(actor, event) {
|
||
|
||
// Event coordinates are relative to the stage but can be transformed
|
||
// as the actor will only receive events within his bounds.
|
||
let stage_x, stage_y, ok, event_x, event_y, actor_w, actor_h;
|
||
[stage_x, stage_y] = event.get_coords();
|
||
[ok, event_x, event_y] = actor.transform_stage_point(stage_x, stage_y);
|
||
[actor_w, actor_h] = actor.get_size();
|
||
|
||
// reset timeout to avid conflicts with the mousehover event
|
||
if (this._ensureAppIconVisibilityTimeoutId>0) {
|
||
Mainloop.source_remove(this._ensureAppIconVisibilityTimeoutId);
|
||
this._ensureAppIconVisibilityTimeoutId = 0;
|
||
}
|
||
|
||
// Skip to avoid double events mouse
|
||
if (event.is_pointer_emulated())
|
||
return Clutter.EVENT_STOP;
|
||
|
||
let adjustment, delta;
|
||
|
||
adjustment = this._scrollView.get_hscroll_bar().get_adjustment();
|
||
|
||
let increment = adjustment.step_increment;
|
||
|
||
switch ( event.get_scroll_direction() ) {
|
||
case Clutter.ScrollDirection.UP:
|
||
delta = -increment;
|
||
break;
|
||
case Clutter.ScrollDirection.DOWN:
|
||
delta = +increment;
|
||
break;
|
||
case Clutter.ScrollDirection.SMOOTH:
|
||
let [dx, dy] = event.get_scroll_delta();
|
||
delta = dy*increment;
|
||
delta += dx*increment;
|
||
break;
|
||
|
||
}
|
||
|
||
adjustment.set_value(adjustment.get_value() + delta);
|
||
|
||
return Clutter.EVENT_STOP;
|
||
|
||
},
|
||
|
||
_onDragBegin: function() {
|
||
this._dragCancelled = false;
|
||
this._dragMonitor = {
|
||
dragMotion: Lang.bind(this, this._onDragMotion)
|
||
};
|
||
DND.addDragMonitor(this._dragMonitor);
|
||
|
||
if (this._box.get_n_children() == 0) {
|
||
this._emptyDropTarget = new Dash.EmptyDropTargetItem();
|
||
this._box.insert_child_at_index(this._emptyDropTarget, 0);
|
||
this._emptyDropTarget.show(true);
|
||
}
|
||
|
||
this._toggleFavortieHighlight(true);
|
||
},
|
||
|
||
_onDragCancelled: function() {
|
||
this._dragCancelled = true;
|
||
|
||
if (this._dragInfo) {
|
||
this._box.set_child_at_index(this._dragInfo[1]._dashItemContainer, this._dragInfo[0]);
|
||
}
|
||
|
||
this._endDrag();
|
||
},
|
||
|
||
_onDragEnd: function() {
|
||
if (this._dragCancelled)
|
||
return;
|
||
|
||
this._endDrag();
|
||
},
|
||
|
||
_endDrag: function() {
|
||
if (this._dragInfo && this._dragInfo[1]._dashItemContainer instanceof DragPlaceholderItem) {
|
||
this._box.remove_child(this._dragInfo[1]._dashItemContainer);
|
||
this._dragInfo[1]._dashItemContainer.destroy();
|
||
delete this._dragInfo[1]._dashItemContainer;
|
||
}
|
||
|
||
this._dragInfo = null;
|
||
this._clearEmptyDropTarget();
|
||
this._showAppsIcon.setDragApp(null);
|
||
DND.removeDragMonitor(this._dragMonitor);
|
||
|
||
this._toggleFavortieHighlight();
|
||
},
|
||
|
||
_onDragMotion: function(dragEvent) {
|
||
let app = Dash.getAppFromSource(dragEvent.source);
|
||
if (app == null)
|
||
return DND.DragMotionResult.CONTINUE;
|
||
|
||
let showAppsHovered = this._showAppsIcon.contains(dragEvent.targetActor);
|
||
|
||
if (showAppsHovered)
|
||
this._showAppsIcon.setDragApp(app);
|
||
else
|
||
this._showAppsIcon.setDragApp(null);
|
||
|
||
return DND.DragMotionResult.CONTINUE;
|
||
},
|
||
|
||
_toggleFavortieHighlight: function(show) {
|
||
let appFavorites = AppFavorites.getAppFavorites();
|
||
let cssFuncName = (show ? 'add' : 'remove') + '_style_class_name';
|
||
|
||
this._getAppIcons().filter(appIcon => appFavorites.isFavorite(appIcon.app.get_id()))
|
||
.forEach(fav => fav._container[cssFuncName]('favorite'));
|
||
},
|
||
|
||
handleIsolatedWorkspaceSwitch: function() {
|
||
this._shownInitially = this.isGroupApps;
|
||
this._queueRedisplay();
|
||
},
|
||
|
||
_connectWorkspaceSignals: function() {
|
||
this._disconnectWorkspaceSignals();
|
||
|
||
this._lastWorkspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace();
|
||
|
||
this._workspaceWindowAddedId = this._lastWorkspace.connect('window-added', () => this._queueRedisplay());
|
||
this._workspaceWindowRemovedId = this._lastWorkspace.connect('window-removed', () => this._queueRedisplay());
|
||
},
|
||
|
||
_disconnectWorkspaceSignals: function() {
|
||
if (this._lastWorkspace) {
|
||
this._lastWorkspace.disconnect(this._workspaceWindowAddedId);
|
||
this._lastWorkspace.disconnect(this._workspaceWindowRemovedId);
|
||
|
||
this._lastWorkspace = null;
|
||
}
|
||
},
|
||
|
||
_queueRedisplay: function () {
|
||
Main.queueDeferredWork(this._workId);
|
||
},
|
||
|
||
_hookUpLabel: function(item, syncHandler) {
|
||
item.child.connect('notify::hover', Lang.bind(this, function() {
|
||
this._syncLabel(item, syncHandler);
|
||
}));
|
||
|
||
syncHandler.connect('sync-tooltip', Lang.bind(this, function() {
|
||
this._syncLabel(item, syncHandler);
|
||
}));
|
||
},
|
||
|
||
_createAppItem: function(app, window, isLauncher) {
|
||
let appIcon = new AppIcons.taskbarAppIcon(
|
||
dtpSettings,
|
||
{
|
||
app: app,
|
||
window: window,
|
||
isLauncher: isLauncher
|
||
},
|
||
this.panelWrapper,
|
||
{
|
||
setSizeManually: true,
|
||
showLabel: false,
|
||
isDraggable: !dtpSettings.get_boolean('taskbar-locked'),
|
||
},
|
||
this.previewMenu
|
||
);
|
||
|
||
if (appIcon._draggable) {
|
||
appIcon._draggable.connect('drag-begin',
|
||
Lang.bind(this, function() {
|
||
appIcon.actor.opacity = 50;
|
||
}));
|
||
appIcon._draggable.connect('drag-end',
|
||
Lang.bind(this, function() {
|
||
appIcon.actor.opacity = 255;
|
||
}));
|
||
}
|
||
|
||
appIcon.connect('menu-state-changed',
|
||
Lang.bind(this, function(appIcon, opened) {
|
||
this._itemMenuStateChanged(item, opened);
|
||
}));
|
||
|
||
let item = new Dash.DashItemContainer();
|
||
|
||
extendDashItemContainer(item);
|
||
|
||
item.setChild(appIcon.actor);
|
||
appIcon._dashItemContainer = item;
|
||
|
||
appIcon.actor.connect('notify::hover', Lang.bind(this, function() {
|
||
if (appIcon.actor.hover){
|
||
this._ensureAppIconVisibilityTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function(){
|
||
Utils.ensureActorVisibleInScrollView(this._scrollView, appIcon.actor, this._scrollView._dtpFadeSize);
|
||
this._ensureAppIconVisibilityTimeoutId = 0;
|
||
return GLib.SOURCE_REMOVE;
|
||
}));
|
||
} else {
|
||
if (this._ensureAppIconVisibilityTimeoutId>0) {
|
||
Mainloop.source_remove(this._ensureAppIconVisibilityTimeoutId);
|
||
this._ensureAppIconVisibilityTimeoutId = 0;
|
||
}
|
||
}
|
||
}));
|
||
|
||
appIcon.actor.connect('clicked',
|
||
Lang.bind(this, function(actor) {
|
||
Utils.ensureActorVisibleInScrollView(this._scrollView, actor, this._scrollView._dtpFadeSize);
|
||
}));
|
||
|
||
appIcon.actor.connect('key-focus-in', Lang.bind(this, function(actor) {
|
||
let [x_shift, y_shift] = Utils.ensureActorVisibleInScrollView(this._scrollView, actor, this._scrollView._dtpFadeSize);
|
||
|
||
// This signal is triggered also by mouse click. The popup menu is opened at the original
|
||
// coordinates. Thus correct for the shift which is going to be applied to the scrollview.
|
||
if (appIcon._menu) {
|
||
appIcon._menu._boxPointer.xOffset = -x_shift;
|
||
appIcon._menu._boxPointer.yOffset = -y_shift;
|
||
}
|
||
}));
|
||
|
||
// Override default AppIcon label_actor, now the
|
||
// accessible_name is set at DashItemContainer.setLabelText
|
||
appIcon.actor.label_actor = null;
|
||
item.setLabelText(app.get_name());
|
||
|
||
appIcon.icon.setIconSize(this.iconSize);
|
||
this._hookUpLabel(item, appIcon);
|
||
|
||
return item;
|
||
},
|
||
|
||
// Return an array with the "proper" appIcons currently in the taskbar
|
||
_getAppIcons: function() {
|
||
// Only consider children which are "proper" icons and which are not
|
||
// animating out (which means they will be destroyed at the end of
|
||
// the animation)
|
||
return this._getTaskbarIcons().map(function(actor){
|
||
return actor.child._delegate;
|
||
});
|
||
},
|
||
|
||
_getTaskbarIcons: function(includeAnimated) {
|
||
return this._box.get_children().filter(function(actor) {
|
||
return actor.child &&
|
||
actor.child._delegate &&
|
||
actor.child._delegate.icon &&
|
||
(includeAnimated || !actor.animatingOut);
|
||
});
|
||
},
|
||
|
||
_updateAppIcons: function() {
|
||
let appIcons = this._getAppIcons();
|
||
|
||
appIcons.filter(icon => icon.constructor === AppIcons.taskbarAppIcon).forEach(icon => {
|
||
icon.updateIcon();
|
||
});
|
||
},
|
||
|
||
_itemMenuStateChanged: function(item, opened) {
|
||
// When the menu closes, it calls sync_hover, which means
|
||
// that the notify::hover handler does everything we need to.
|
||
if (opened) {
|
||
if (this._showLabelTimeoutId > 0) {
|
||
Mainloop.source_remove(this._showLabelTimeoutId);
|
||
this._showLabelTimeoutId = 0;
|
||
}
|
||
|
||
item.hideLabel();
|
||
} else {
|
||
// I want to listen from outside when a menu is closed. I used to
|
||
// add a custom signal to the appIcon, since gnome 3.8 the signal
|
||
// calling this callback was added upstream.
|
||
this.emit('menu-closed');
|
||
}
|
||
},
|
||
|
||
_syncLabel: function (item, syncHandler) {
|
||
let shouldShow = syncHandler ? syncHandler.shouldShowTooltip() : item.child.get_hover();
|
||
|
||
if (shouldShow) {
|
||
if (this._showLabelTimeoutId == 0) {
|
||
let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
|
||
this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
|
||
Lang.bind(this, function() {
|
||
this._labelShowing = true;
|
||
item.showLabel();
|
||
this._showLabelTimeoutId = 0;
|
||
return GLib.SOURCE_REMOVE;
|
||
}));
|
||
GLib.Source.set_name_by_id(this._showLabelTimeoutId, '[gnome-shell] item.showLabel');
|
||
if (this._resetHoverTimeoutId > 0) {
|
||
Mainloop.source_remove(this._resetHoverTimeoutId);
|
||
this._resetHoverTimeoutId = 0;
|
||
}
|
||
}
|
||
} else {
|
||
if (this._showLabelTimeoutId > 0)
|
||
Mainloop.source_remove(this._showLabelTimeoutId);
|
||
this._showLabelTimeoutId = 0;
|
||
item.hideLabel();
|
||
if (this._labelShowing) {
|
||
this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT,
|
||
Lang.bind(this, function() {
|
||
this._labelShowing = false;
|
||
this._resetHoverTimeoutId = 0;
|
||
return GLib.SOURCE_REMOVE;
|
||
}));
|
||
GLib.Source.set_name_by_id(this._resetHoverTimeoutId, '[gnome-shell] this._labelShowing');
|
||
}
|
||
}
|
||
},
|
||
|
||
_adjustIconSize: function() {
|
||
let panelSize = dtpSettings.get_int('panel-size');
|
||
let availSize = panelSize - dtpSettings.get_int('appicon-padding') * 2;
|
||
let minIconSize = MIN_ICON_SIZE + panelSize % 2;
|
||
|
||
if (availSize == this.iconSize)
|
||
return;
|
||
|
||
if (availSize < minIconSize) {
|
||
availSize = minIconSize;
|
||
}
|
||
|
||
// For the icon size, we only consider children which are "proper"
|
||
// icons and which are not animating out (which means they will be
|
||
// destroyed at the end of the animation)
|
||
let iconChildren = this._getTaskbarIcons().concat([this._showAppsIcon]);
|
||
let scale = this.iconSize / availSize;
|
||
|
||
this.iconSize = availSize;
|
||
this.emit('icon-size-changed');
|
||
|
||
for (let i = 0; i < iconChildren.length; i++) {
|
||
let icon = iconChildren[i].child._delegate.icon;
|
||
|
||
// Set the new size immediately, to keep the icons' sizes
|
||
// in sync with this.iconSize
|
||
icon.setIconSize(this.iconSize);
|
||
|
||
// Don't animate the icon size change when the overview
|
||
// is transitioning, or when initially filling
|
||
// the taskbar
|
||
if (Main.overview.animationInProgress ||
|
||
!this._shownInitially)
|
||
continue;
|
||
|
||
let [targetWidth, targetHeight] = icon.icon.get_size();
|
||
|
||
// Scale the icon's texture to the previous size and
|
||
// tween to the new size
|
||
icon.icon.set_size(icon.icon.width * scale, icon.icon.height * scale);
|
||
|
||
Tweener.addTween(icon.icon,
|
||
{ width: targetWidth,
|
||
height: targetHeight,
|
||
time: DASH_ANIMATION_TIME,
|
||
transition: 'easeOutQuad',
|
||
});
|
||
}
|
||
},
|
||
|
||
sortAppsCompareFunction: function(appA, appB) {
|
||
return getAppStableSequence(appA, dtpSettings, this.panelWrapper.monitor) -
|
||
getAppStableSequence(appB, dtpSettings, this.panelWrapper.monitor);
|
||
},
|
||
|
||
getAppInfos: function() {
|
||
//get the user's favorite apps
|
||
let favoriteApps = this._checkIfShowingFavorites() ? AppFavorites.getAppFavorites().getFavorites() : [];
|
||
|
||
//find the apps that should be in the taskbar: the favorites first, then add the running apps
|
||
// When using isolation, we filter out apps that have no windows in
|
||
// the current workspace (this check is done in AppIcons.getInterestingWindows)
|
||
let runningApps = this._checkIfShowingRunningApps() ? this._getRunningApps().sort(this.sortAppsCompareFunction.bind(this)) : [];
|
||
|
||
if (!this.isGroupApps && dtpSettings.get_boolean('group-apps-use-launchers')) {
|
||
return this._createAppInfos(favoriteApps, [], true)
|
||
.concat(this._createAppInfos(runningApps)
|
||
.filter(appInfo => appInfo.windows.length));
|
||
} else {
|
||
return this._createAppInfos(favoriteApps.concat(runningApps.filter(app => favoriteApps.indexOf(app) < 0)))
|
||
.filter(appInfo => appInfo.windows.length || favoriteApps.indexOf(appInfo.app) >= 0);
|
||
}
|
||
},
|
||
|
||
_redisplay: function () {
|
||
if (!this._signalsHandler) {
|
||
return;
|
||
}
|
||
|
||
//get the currently displayed appIcons
|
||
let currentAppIcons = this._getTaskbarIcons();
|
||
let expectedAppInfos = this.getAppInfos();
|
||
|
||
//remove the appIcons which are not in the expected apps list
|
||
for (let i = currentAppIcons.length - 1; i > -1; --i) {
|
||
let appIcon = currentAppIcons[i].child._delegate;
|
||
let appIndex = Utils.findIndex(expectedAppInfos, appInfo => appInfo.app == appIcon.app &&
|
||
appInfo.isLauncher == appIcon.isLauncher);
|
||
|
||
if (appIndex < 0 ||
|
||
(appIcon.window && (this.isGroupApps || expectedAppInfos[appIndex].windows.indexOf(appIcon.window) < 0)) ||
|
||
(!appIcon.window && !appIcon.isLauncher &&
|
||
!this.isGroupApps && expectedAppInfos[appIndex].windows.length)) {
|
||
currentAppIcons[i][this._shownInitially ? 'animateOutAndDestroy' : 'destroy']();
|
||
currentAppIcons.splice(i, 1);
|
||
}
|
||
}
|
||
|
||
//if needed, reorder the existing appIcons and create the missing ones
|
||
let currentPosition = 0;
|
||
for (let i = 0, l = expectedAppInfos.length; i < l; ++i) {
|
||
let neededAppIcons = this.isGroupApps || !expectedAppInfos[i].windows.length ?
|
||
[{ app: expectedAppInfos[i].app, window: null, isLauncher: expectedAppInfos[i].isLauncher }] :
|
||
expectedAppInfos[i].windows.map(window => ({ app: expectedAppInfos[i].app, window: window, isLauncher: false }));
|
||
|
||
for (let j = 0, ll = neededAppIcons.length; j < ll; ++j) {
|
||
//check if the icon already exists
|
||
let matchingAppIconIndex = Utils.findIndex(currentAppIcons, appIcon => appIcon.child._delegate.app == neededAppIcons[j].app &&
|
||
appIcon.child._delegate.window == neededAppIcons[j].window);
|
||
|
||
if (matchingAppIconIndex > 0 && matchingAppIconIndex != currentPosition) {
|
||
//moved icon, reposition it
|
||
this._box.remove_child(currentAppIcons[matchingAppIconIndex]);
|
||
this._box.insert_child_at_index(currentAppIcons[matchingAppIconIndex], currentPosition);
|
||
} else if (matchingAppIconIndex < 0) {
|
||
//the icon doesn't exist yet, create a new one
|
||
let newAppIcon = this._createAppItem(neededAppIcons[j].app, neededAppIcons[j].window, neededAppIcons[j].isLauncher);
|
||
|
||
this._box.insert_child_at_index(newAppIcon, currentPosition);
|
||
currentAppIcons.splice(currentPosition, 0, newAppIcon);
|
||
|
||
// Skip animations on first run when adding the initial set
|
||
// of items, to avoid all items zooming in at once
|
||
newAppIcon.show(this._shownInitially);
|
||
}
|
||
|
||
++currentPosition;
|
||
}
|
||
}
|
||
|
||
this._adjustIconSize();
|
||
|
||
// Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
|
||
// Without it, StBoxLayout may use a stale size cache
|
||
this._box.queue_relayout();
|
||
|
||
// This is required for icon reordering when the scrollview is used.
|
||
this._updateAppIcons();
|
||
|
||
// This will update the size, and the corresponding number for each icon on the primary panel
|
||
if (!this.panelWrapper.isSecondary) {
|
||
this._updateNumberOverlay();
|
||
}
|
||
|
||
this._shownInitially = true;
|
||
},
|
||
|
||
_checkIfShowingRunningApps: function() {
|
||
return dtpSettings.get_boolean('show-running-apps');
|
||
},
|
||
|
||
_checkIfShowingFavorites: function() {
|
||
return dtpSettings.get_boolean('show-favorites') &&
|
||
(!this.panelWrapper.isSecondary || dtpSettings.get_boolean('show-favorites-all-monitors'));
|
||
},
|
||
|
||
_getRunningApps: function() {
|
||
let tracker = Shell.WindowTracker.get_default();
|
||
let windows = global.get_window_actors();
|
||
let apps = [];
|
||
|
||
for (let i = 0, l = windows.length; i < l; ++i) {
|
||
let app = tracker.get_window_app(windows[i].metaWindow);
|
||
|
||
if (app && apps.indexOf(app) < 0) {
|
||
apps.push(app);
|
||
}
|
||
}
|
||
|
||
return apps;
|
||
},
|
||
|
||
_createAppInfos: function(apps, defaultWindows, defaultIsLauncher) {
|
||
return apps.map(app => ({
|
||
app: app,
|
||
isLauncher: defaultIsLauncher || false,
|
||
windows: defaultWindows || AppIcons.getInterestingWindows(app, dtpSettings, this.panelWrapper.monitor)
|
||
.sort(sortWindowsCompareFunction)
|
||
}));
|
||
},
|
||
|
||
// Reset the displayed apps icon to mantain the correct order
|
||
resetAppIcons : function() {
|
||
let children = this._getTaskbarIcons(true);
|
||
|
||
for (let i = 0; i < children.length; i++) {
|
||
let item = children[i];
|
||
item.destroy();
|
||
}
|
||
|
||
// to avoid ugly animations, just suppress them like when taskbar is first loaded.
|
||
this._shownInitially = false;
|
||
this._redisplay();
|
||
|
||
},
|
||
|
||
_updateNumberOverlay: function() {
|
||
let seenApps = {};
|
||
let counter = 0;
|
||
|
||
this._getAppIcons().forEach(function(icon) {
|
||
if (!seenApps[icon.app]) {
|
||
seenApps[icon.app] = 1;
|
||
counter++;
|
||
}
|
||
|
||
if (counter <= 10) {
|
||
icon.setNumberOverlay(counter == 10 ? 0 : counter);
|
||
} else {
|
||
// No overlay after 10
|
||
icon.setNumberOverlay(-1);
|
||
}
|
||
|
||
icon.updateNumberOverlay();
|
||
});
|
||
|
||
if (dtpSettings.get_boolean('hot-keys') &&
|
||
dtpSettings.get_string('hotkeys-overlay-combo') === 'ALWAYS')
|
||
this.toggleNumberOverlay(true);
|
||
},
|
||
|
||
toggleNumberOverlay: function(activate) {
|
||
let appIcons = this._getAppIcons();
|
||
appIcons.forEach(function(icon) {
|
||
icon.toggleNumberOverlay(activate);
|
||
});
|
||
},
|
||
|
||
_clearEmptyDropTarget: function() {
|
||
if (this._emptyDropTarget) {
|
||
this._emptyDropTarget.animateOutAndDestroy();
|
||
this._emptyDropTarget = null;
|
||
}
|
||
},
|
||
|
||
handleDragOver: function(source, actor, x, y, time) {
|
||
if (source == Main.xdndHandler)
|
||
return DND.DragMotionResult.CONTINUE;
|
||
|
||
// Don't allow favoriting of transient apps
|
||
if (source.app == null || source.app.is_window_backed())
|
||
return DND.DragMotionResult.NO_DROP;
|
||
|
||
if (!this._settings.is_writable('favorite-apps'))
|
||
return DND.DragMotionResult.NO_DROP;
|
||
|
||
if (!this._box.contains(source.actor) && !source._dashItemContainer) {
|
||
//not an appIcon of the taskbar, probably from the applications view
|
||
source._dashItemContainer = new DragPlaceholderItem(source, this.iconSize);
|
||
this._box.insert_child_above(source._dashItemContainer, null);
|
||
}
|
||
|
||
x -= this.showAppsButton.width;
|
||
|
||
let currentAppIcons = this._getAppIcons();
|
||
let sourceIndex = currentAppIcons.indexOf(source);
|
||
let hoveredIndex = Utils.findIndex(currentAppIcons,
|
||
appIcon => x >= appIcon._dashItemContainer.x &&
|
||
x <= (appIcon._dashItemContainer.x + appIcon._dashItemContainer.width));
|
||
|
||
if (!this._dragInfo) {
|
||
this._dragInfo = [sourceIndex, source];
|
||
}
|
||
|
||
if (hoveredIndex >= 0) {
|
||
let isLeft = x < currentAppIcons[hoveredIndex]._dashItemContainer.x + currentAppIcons[hoveredIndex]._dashItemContainer.width * .5;
|
||
|
||
// Don't allow positioning before or after self and between icons of same app
|
||
if (!(hoveredIndex === sourceIndex ||
|
||
(isLeft && hoveredIndex - 1 == sourceIndex) ||
|
||
(isLeft && hoveredIndex - 1 >= 0 && source.app != currentAppIcons[hoveredIndex - 1].app &&
|
||
currentAppIcons[hoveredIndex - 1].app == currentAppIcons[hoveredIndex].app) ||
|
||
(!isLeft && hoveredIndex + 1 == sourceIndex) ||
|
||
(!isLeft && hoveredIndex + 1 < currentAppIcons.length && source.app != currentAppIcons[hoveredIndex + 1].app &&
|
||
currentAppIcons[hoveredIndex + 1].app == currentAppIcons[hoveredIndex].app))) {
|
||
this._box.set_child_at_index(source._dashItemContainer, hoveredIndex);
|
||
|
||
// Ensure the next and previous icon are visible when moving the icon
|
||
// (I assume there's room for both of them)
|
||
if (hoveredIndex > 1)
|
||
Utils.ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[hoveredIndex-1], this._scrollView._dtpFadeSize);
|
||
if (hoveredIndex < this._box.get_children().length-1)
|
||
Utils.ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[hoveredIndex+1], this._scrollView._dtpFadeSize);
|
||
}
|
||
}
|
||
|
||
return this._dragInfo[0] !== sourceIndex ? DND.DragMotionResult.MOVE_DROP : DND.DragMotionResult.CONTINUE;
|
||
},
|
||
|
||
// Draggable target interface
|
||
acceptDrop : function(source, actor, x, y, time) {
|
||
// Don't allow favoriting of transient apps
|
||
if (!source.app || source.app.is_window_backed() || !this._settings.is_writable('favorite-apps')) {
|
||
return false;
|
||
}
|
||
|
||
let appIcons = this._getAppIcons();
|
||
let sourceIndex = appIcons.indexOf(source);
|
||
let usingLaunchers = !this.isGroupApps && dtpSettings.get_boolean('group-apps-use-launchers');
|
||
|
||
// dragging the icon to its original position
|
||
if (this._dragInfo[0] === sourceIndex) {
|
||
return true;
|
||
}
|
||
|
||
let appFavorites = AppFavorites.getAppFavorites();
|
||
let sourceAppId = source.app.get_id();
|
||
let appIsFavorite = appFavorites.isFavorite(sourceAppId);
|
||
let replacingIndex = sourceIndex + (sourceIndex > this._dragInfo[0] ? -1 : 1);
|
||
let favoriteIndex = replacingIndex >= 0 ? appFavorites.getFavorites().indexOf(appIcons[replacingIndex].app) : 0;
|
||
let sameApps = appIcons.filter(a => a != source && a.app == source.app);
|
||
let showingFavorites = this._checkIfShowingFavorites();
|
||
let favoritesCount = 0;
|
||
let position = 0;
|
||
let interestingWindows = {};
|
||
let getAppWindows = app => {
|
||
if (!interestingWindows[app]) {
|
||
interestingWindows[app] = AppIcons.getInterestingWindows(app, dtpSettings, this.panelWrapper.monitor);
|
||
}
|
||
|
||
let appWindows = interestingWindows[app]; //prevents "reference to undefined property Symbol.toPrimitive" warning
|
||
return appWindows;
|
||
};
|
||
|
||
if (sameApps.length &&
|
||
((!appIcons[sourceIndex - 1] || appIcons[sourceIndex - 1].app !== source.app) &&
|
||
(!appIcons[sourceIndex + 1] || appIcons[sourceIndex + 1].app !== source.app))) {
|
||
appIcons.splice(appIcons.indexOf(sameApps[0]), sameApps.length);
|
||
Array.prototype.splice.apply(appIcons, [sourceIndex + 1, 0].concat(sameApps));
|
||
}
|
||
|
||
for (let i = 0, l = appIcons.length; i < l; ++i) {
|
||
let windows = [];
|
||
|
||
if (!usingLaunchers || (!source.isLauncher && !appIcons[i].isLauncher)) {
|
||
windows = appIcons[i].window ? [appIcons[i].window] : getAppWindows(appIcons[i].app);
|
||
}
|
||
|
||
windows.forEach(w => w._dtpPosition = position++);
|
||
|
||
if (showingFavorites &&
|
||
((usingLaunchers && appIcons[i].isLauncher) ||
|
||
(!usingLaunchers && appFavorites.isFavorite(appIcons[i].app.get_id())))) {
|
||
++favoritesCount;
|
||
}
|
||
}
|
||
|
||
if (sourceIndex < favoritesCount) {
|
||
if (appIsFavorite) {
|
||
appFavorites.moveFavoriteToPos(sourceAppId, favoriteIndex);
|
||
} else {
|
||
appFavorites.addFavoriteAtPos(sourceAppId, favoriteIndex);
|
||
}
|
||
} else if (appIsFavorite && showingFavorites && (!usingLaunchers || source.isLauncher)) {
|
||
appFavorites.removeFavorite(sourceAppId);
|
||
}
|
||
|
||
appFavorites.emit('changed');
|
||
|
||
return true;
|
||
},
|
||
|
||
_onShowAppsButtonToggled: function() {
|
||
// Sync the status of the default appButtons. Only if the two statuses are
|
||
// different, that means the user interacted with the extension provided
|
||
// application button, cutomize the behaviour. Otherwise the shell has changed the
|
||
// status (due to the _syncShowAppsButtonToggled function below) and it
|
||
// has already performed the desired action.
|
||
|
||
let animate = dtpSettings.get_boolean('animate-show-apps');
|
||
let selector = Main.overview.viewSelector;
|
||
|
||
if (selector._showAppsButton.checked !== this.showAppsButton.checked) {
|
||
// find visible view
|
||
let visibleView;
|
||
Main.overview.viewSelector.appDisplay._views.every(function(v, index) {
|
||
if (v.view.actor.visible) {
|
||
visibleView = index;
|
||
return false;
|
||
}
|
||
else
|
||
return true;
|
||
});
|
||
|
||
if (this.showAppsButton.checked) {
|
||
// force spring animation triggering.By default the animation only
|
||
// runs if we are already inside the overview.
|
||
if (!Main.overview._shown) {
|
||
this.forcedOverview = true;
|
||
let view = Main.overview.viewSelector.appDisplay._views[visibleView].view;
|
||
let grid = view._grid;
|
||
if (animate) {
|
||
// Animate in the the appview, hide the appGrid to avoiud flashing
|
||
// Go to the appView before entering the overview, skipping the workspaces.
|
||
// Do this manually avoiding opacity in transitions so that the setting of the opacity
|
||
// to 0 doesn't get overwritten.
|
||
Main.overview.viewSelector._activePage.opacity = 0;
|
||
Main.overview.viewSelector._activePage.hide();
|
||
Main.overview.viewSelector._activePage = Main.overview.viewSelector._appsPage;
|
||
Main.overview.viewSelector._activePage.show();
|
||
grid.actor.opacity = 0;
|
||
|
||
// The animation has to be trigered manually because the AppDisplay.animate
|
||
// method is waiting for an allocation not happening, as we skip the workspace view
|
||
// and the appgrid could already be allocated from previous shown.
|
||
// It has to be triggered after the overview is shown as wrong coordinates are obtained
|
||
// otherwise.
|
||
let overviewShownId = Main.overview.connect('shown', Lang.bind(this, function() {
|
||
Main.overview.disconnect(overviewShownId);
|
||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||
grid.actor.opacity = 255;
|
||
grid.animateSpring(IconGrid.AnimationDirection.IN, this.showAppsButton);
|
||
}));
|
||
}));
|
||
} else {
|
||
Main.overview.viewSelector._activePage = Main.overview.viewSelector._appsPage;
|
||
Main.overview.viewSelector._activePage.show();
|
||
grid.actor.opacity = 255;
|
||
|
||
}
|
||
}
|
||
|
||
//temporarily use as primary the monitor on which the showapps btn was clicked
|
||
this.panelWrapper.panelManager.setFocusedMonitor(this.panelWrapper.monitor);
|
||
|
||
//reset the primary monitor when exiting the overview
|
||
let overviewHiddenId = Main.overview.connect('hidden', () => {
|
||
Main.overview.disconnect(overviewHiddenId);
|
||
this.panelWrapper.panelManager.setFocusedMonitor(this.panelWrapper.panelManager.primaryPanel.monitor, true);
|
||
});
|
||
|
||
// Finally show the overview
|
||
selector._showAppsButton.checked = true;
|
||
Main.overview.show();
|
||
}
|
||
else {
|
||
if (this.forcedOverview) {
|
||
// force exiting overview if needed
|
||
|
||
if (animate) {
|
||
// Manually trigger springout animation without activating the
|
||
// workspaceView to avoid the zoomout animation. Hide the appPage
|
||
// onComplete to avoid ugly flashing of original icons.
|
||
let view = Main.overview.viewSelector.appDisplay._views[visibleView].view;
|
||
view.animate(IconGrid.AnimationDirection.OUT, Lang.bind(this, function() {
|
||
Main.overview.viewSelector._appsPage.hide();
|
||
Main.overview.hide();
|
||
selector._showAppsButton.checked = false;
|
||
this.forcedOverview = false;
|
||
}));
|
||
}
|
||
else {
|
||
Main.overview.hide();
|
||
this.forcedOverview = false;
|
||
}
|
||
}
|
||
else {
|
||
selector._showAppsButton.checked = false;
|
||
this.forcedOverview = false;
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
_syncShowAppsButtonToggled: function() {
|
||
let status = Main.overview.viewSelector._showAppsButton.checked;
|
||
if (this.showAppsButton.checked !== status)
|
||
this.showAppsButton.checked = status;
|
||
},
|
||
|
||
showShowAppsButton: function() {
|
||
this.showAppsButton.visible = true;
|
||
this.showAppsButton.set_width(-1);
|
||
this.showAppsButton.set_height(-1);
|
||
},
|
||
|
||
hideShowAppsButton: function() {
|
||
this.showAppsButton.hide();
|
||
this.showAppsButton.set_width(0);
|
||
this.showAppsButton.set_height(0);
|
||
},
|
||
|
||
popupFocusedAppSecondaryMenu: function() {
|
||
let appIcons = this._getAppIcons();
|
||
for(let i in appIcons) {
|
||
if(appIcons[i].app == tracker.focus_app) {
|
||
let appIcon = appIcons[i];
|
||
if(appIcon._menu && appIcon._menu.isOpen)
|
||
appIcon._menu.close();
|
||
else
|
||
appIcons[i].popupMenu();
|
||
break;
|
||
}
|
||
}
|
||
},
|
||
});
|
||
|
||
Signals.addSignalMethods(taskbar.prototype);
|
||
|
||
var DragPlaceholderItem = Utils.defineClass({
|
||
Name: 'DashToPanel-DragPlaceholderItem',
|
||
Extends: St.Widget,
|
||
|
||
_init: function(appIcon, iconSize) {
|
||
this.callParent('_init', { style_class: 'dtp-icon-container', layout_manager: new Clutter.BinLayout() });
|
||
|
||
this.child = { _delegate: appIcon };
|
||
|
||
this._clone = new Clutter.Clone({
|
||
source: appIcon.icon._iconBin,
|
||
width: iconSize,
|
||
height: iconSize
|
||
});
|
||
|
||
this.add_actor(this._clone);
|
||
},
|
||
|
||
destroy: function() {
|
||
this._clone.destroy();
|
||
this.callParent('destroy');
|
||
},
|
||
});
|
||
|
||
function getAppStableSequence(app, dtpSettings, monitor) {
|
||
let windows = AppIcons.getInterestingWindows(app, dtpSettings, monitor);
|
||
|
||
return windows.reduce((prevWindow, window) => {
|
||
return Math.min(prevWindow, getWindowStableSequence(window));
|
||
}, Infinity);
|
||
}
|
||
|
||
function sortWindowsCompareFunction(windowA, windowB) {
|
||
return getWindowStableSequence(windowA) - getWindowStableSequence(windowB);
|
||
}
|
||
|
||
function getWindowStableSequence(window) {
|
||
return ('_dtpPosition' in window ? window._dtpPosition : window.get_stable_sequence());
|
||
} |