1458 lines
50 KiB
JavaScript
1458 lines
50 KiB
JavaScript
|
|
const Graphene = imports.gi.Graphene;
|
|
const GLib = imports.gi.GLib;
|
|
const Gio = imports.gi.Gio;
|
|
const GObject = imports.gi.GObject;
|
|
const Gtk = imports.gi.Gtk;
|
|
const Clutter = imports.gi.Clutter;
|
|
const St = imports.gi.St;
|
|
const Shell = imports.gi.Shell;
|
|
const Meta = imports.gi.Meta;
|
|
const Lang = imports.lang;
|
|
const Signals = imports.signals;
|
|
const Params = imports.misc.params;
|
|
const Config = imports.misc.config;
|
|
const GnomeSession = imports.misc.gnomeSession;
|
|
const AppDisplay = imports.ui.appDisplay;
|
|
const AppFavorites = imports.ui.appFavorites;
|
|
const Layout = imports.ui.layout;
|
|
const Main = imports.ui.main;
|
|
const PanelMenu = imports.ui.panelMenu;
|
|
const PopupMenu = imports.ui.popupMenu;
|
|
const DND = imports.ui.dnd;
|
|
const IconGrid = imports.ui.iconGrid;
|
|
|
|
const Util = imports.misc.util;
|
|
const ExtensionUtils = imports.misc.extensionUtils;
|
|
const Me = ExtensionUtils.getCurrentExtension();
|
|
|
|
const PlaceDisplay = Me.imports.placeDisplay;
|
|
const Convenience = Me.imports.convenience;
|
|
|
|
const MENU_POPUP_TIMEOUT = 600;
|
|
|
|
const ApplicationType = {
|
|
APPLICATION: 0,
|
|
PLACE: 1,
|
|
RECENT: 2,
|
|
APPSBUTTON: 3
|
|
};
|
|
|
|
const ShortcutsPanelOrientation = {
|
|
OUTSIDE: 0,
|
|
INSIDE: 1
|
|
};
|
|
|
|
let recentlyClickedAppLoopId = 0;
|
|
let recentlyClickedApp = null;
|
|
let recentlyClickedAppWindows = null;
|
|
let recentlyClickedAppIndex = 0;
|
|
|
|
// Filter out unnecessary windows, for instance nautilus desktop window.
|
|
function getInterestingWindows(app) {
|
|
return app.get_windows().filter((w) => {
|
|
return !w.skip_taskbar;
|
|
});
|
|
}
|
|
|
|
/* Return the actual position reverseing left and right in rtl */
|
|
function getPosition(settings) {
|
|
let position = settings.get_enum('dock-position');
|
|
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
|
|
if (position == St.Side.LEFT)
|
|
position = St.Side.RIGHT;
|
|
else if (position == St.Side.RIGHT)
|
|
position = St.Side.LEFT;
|
|
}
|
|
return position;
|
|
}
|
|
|
|
function getAppFromSource(source) {
|
|
if (source instanceof AppDisplay.AppIcon) {
|
|
return source.app;
|
|
} else if (source instanceof ShortcutButton) {
|
|
if (source._type == ApplicationType.APPLICATION)
|
|
return source.app;
|
|
else
|
|
return null;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function _getViewFromIcon(icon) {
|
|
for (let parent = icon.get_parent(); parent; parent = parent.get_parent()) {
|
|
if (parent instanceof ShortcutsPanel)
|
|
return parent;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
var MyDragPlaceholderItem = GObject.registerClass(
|
|
class WorkspacesToDock_MyDragPlaceholderItem extends St.Widget {
|
|
_init() {
|
|
super._init({ style_class: 'placeholder',
|
|
opacity: 0,
|
|
x_expand: true,
|
|
y_expand: true,
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
y_align: Clutter.ActorAlign.CENTER });
|
|
|
|
this._settings = Convenience.getSettings('org.gnome.shell.extensions.workspaces-to-dock');
|
|
let iconSize = this._settings.get_double('shortcuts-panel-icon-size');
|
|
this.set_size(iconSize, iconSize);
|
|
}
|
|
});
|
|
|
|
var ShortcutButton = GObject.registerClass({
|
|
Signals: {
|
|
'menu-state-changed': { param_types: [GObject.TYPE_BOOLEAN] },
|
|
'sync-tooltip': {},
|
|
},
|
|
}, class WorkspacesToDock_ShortcutButton extends St.Button {
|
|
_init(app, appType, panel) {
|
|
super._init({
|
|
style_class: 'app-well-app workspacestodock-shortcut-button',
|
|
pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
|
|
reactive: true,
|
|
button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
|
|
can_focus: true,
|
|
});
|
|
|
|
this.app = app;
|
|
//this.id = app.get_id();
|
|
//this.name = app.get_name();
|
|
this._type = appType;
|
|
this._panel = panel;
|
|
|
|
this._stateChangedId = 0;
|
|
this._countChangedId = 0;
|
|
this._maxN = 4;
|
|
this._settings = Convenience.getSettings('org.gnome.shell.extensions.workspaces-to-dock');
|
|
this._gDesktopInterfaceSettings = Convenience.getSettings('org.gnome.desktop.interface');
|
|
|
|
this._iconContainer = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
|
x_expand: true, y_expand: true });
|
|
|
|
this.set_child(this._iconContainer);
|
|
|
|
this._delegate = this;
|
|
|
|
this._hasDndHover = false;
|
|
this._folderPreviewId = 0;
|
|
|
|
let iconParams = {
|
|
setSizeManually: true,
|
|
showLabel: false
|
|
};
|
|
iconParams['createIcon'] = this._createIcon.bind(this);
|
|
|
|
this.icon = new IconGrid.BaseIcon(null, iconParams);
|
|
this._iconSize = this._settings.get_double('shortcuts-panel-icon-size');
|
|
this.icon.setIconSize(this._iconSize);
|
|
this.icon.add_style_class_name('workspacestodock-shortcut-button-icon');
|
|
if (this._type == ApplicationType.PLACE) {
|
|
this.icon.add_style_class_name('workspacestodock-shortcut-button-symbolic-icon');
|
|
}
|
|
this._iconContainer.add_child(this.icon);
|
|
|
|
this._dot = new St.Widget({
|
|
style_class: 'app-well-app-running-dot',
|
|
layout_manager: new Clutter.BinLayout(),
|
|
x_expand: true,
|
|
y_expand: true,
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
y_align: Clutter.ActorAlign.END,
|
|
});
|
|
this._iconContainer.add_child(this._dot);
|
|
|
|
this.label_actor = this.icon.label;
|
|
|
|
this._menu = null;
|
|
this._menuManager = new PopupMenu.PopupMenuManager(this);
|
|
|
|
if (this._type != ApplicationType.APPSBUTTON) {
|
|
this._draggable = DND.makeDraggable(this);
|
|
this._draggable.connect('drag-begin', () => {
|
|
this._dragging = true;
|
|
this.scaleAndFade();
|
|
this._removeMenuTimeout();
|
|
Main.overview.beginItemDrag(this);
|
|
});
|
|
this._draggable.connect('drag-cancelled', () => {
|
|
this._dragging = false;
|
|
Main.overview.cancelledItemDrag(this);
|
|
});
|
|
this._draggable.connect('drag-end', () => {
|
|
this._dragging = false;
|
|
this.undoScaleAndFade();
|
|
Main.overview.endItemDrag(this);
|
|
});
|
|
}
|
|
|
|
this._dragMonitor = null;
|
|
this._itemDragBeginId = Main.overview.connect(
|
|
'item-drag-begin', this._onDragBegin.bind(this));
|
|
this._itemDragEndId = Main.overview.connect(
|
|
'item-drag-end', this._onDragEnd.bind(this));
|
|
|
|
this._menuTimeoutId = 0;
|
|
this._stateChangedId = 0;
|
|
if (this._type == ApplicationType.APPSBUTTON) {
|
|
this._stateChangedId = Main.overview.viewSelector._showAppsButton.connect('notify::checked', () => {
|
|
this._onStateChanged.bind(this);
|
|
});
|
|
} else if (this._type == ApplicationType.APPLICATION) {
|
|
this._stateChangedId = this.app.connect('notify::state', () => {
|
|
this._onStateChanged();
|
|
});
|
|
this._countChangedId = this.app.connect('windows-changed', () => {
|
|
this._onCountChanged.bind(this);
|
|
});
|
|
}
|
|
|
|
this._dot.opacity = 0;
|
|
this._onStateChanged();
|
|
|
|
this.connect('destroy', this._onDestroy.bind(this));
|
|
}
|
|
|
|
_onDestroy() {
|
|
Main.overview.disconnect(this._itemDragBeginId);
|
|
Main.overview.disconnect(this._itemDragEndId);
|
|
|
|
if (this._stateChangedId > 0) {
|
|
if (this._type == ApplicationType.APPSBUTTON) {
|
|
Main.overview.viewSelector._showAppsButton.disconnect(this._stateChangedId);
|
|
} else {
|
|
this.app.disconnect(this._stateChangedId);
|
|
}
|
|
}
|
|
this._stateChangedId = 0;
|
|
|
|
if (this._countChangedId > 0) {
|
|
if (this._type == ApplicationType.APPLICATION) {
|
|
this.app.disconnect(this._countChangedId);
|
|
}
|
|
}
|
|
this._countChangedId = 0;
|
|
|
|
if (this._dragMonitor) {
|
|
DND.removeDragMonitor(this._dragMonitor);
|
|
this._dragMonitor = null;
|
|
}
|
|
|
|
if (this._draggable) {
|
|
if (this._dragging)
|
|
Main.overview.endItemDrag(this);
|
|
this._draggable = null;
|
|
}
|
|
|
|
this._removeMenuTimeout();
|
|
}
|
|
|
|
_createIcon(iconSize) {
|
|
if (this._type == ApplicationType.APPLICATION) {
|
|
return this.app.create_icon_texture(iconSize);
|
|
} else if (this._type == ApplicationType.PLACE) {
|
|
// Adjust 'places' symbolic icons by reducing their size
|
|
// and setting a special class for button padding
|
|
this._iconSize -= 4;
|
|
this.actor.add_style_class_name('workspacestodock-shortcut-button-symbolic');
|
|
return new St.Icon({gicon: this.app.icon, icon_size: iconSize});
|
|
} else if (this._type == ApplicationType.RECENT) {
|
|
let gicon = Gio.content_type_get_icon(this.app.mime);
|
|
return new St.Icon({gicon: gicon, icon_size: iconSize});
|
|
} else if (this._type == ApplicationType.APPSBUTTON) {
|
|
return new St.Icon({icon_name: 'view-app-grid-symbolic', icon_size: iconSize});
|
|
}
|
|
}
|
|
|
|
_removeMenuTimeout() {
|
|
if (this._menuTimeoutId > 0) {
|
|
GLib.source_remove(this._menuTimeoutId);
|
|
this._menuTimeoutId = 0;
|
|
}
|
|
}
|
|
|
|
_onStateChanged() {
|
|
if (this._type == ApplicationType.APPSBUTTON) {
|
|
if (Main.overview.viewSelector._showAppsButton.checked) {
|
|
this.add_style_pseudo_class('checked');
|
|
} else {
|
|
this.remove_style_pseudo_class('checked');
|
|
}
|
|
} else if (this._type == ApplicationType.APPLICATION) {
|
|
if (this.app.state != Shell.AppState.STOPPED) {
|
|
if (!this._settings.get_boolean('shortcuts-panel-show-window-count-indicators')) {
|
|
this._dot.opacity = 255;
|
|
}
|
|
this._onCountChanged();
|
|
} else {
|
|
this._dot.opacity = 0;
|
|
this._onCountChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
_onCountChanged() {
|
|
if (!this._settings.get_boolean('shortcuts-panel-show-window-count-indicators'))
|
|
return;
|
|
|
|
let appWindows = this.app.get_windows().filter((w) => {
|
|
return !w.skip_taskbar;
|
|
});
|
|
|
|
let n = appWindows.length;
|
|
if (n > this._maxN)
|
|
n = this._maxN;
|
|
|
|
for (let i = 1; i <= this._maxN; i++) {
|
|
let className = 'workspacestodock-shortcut-button-windowcount-image-'+i;
|
|
if (i != n) {
|
|
this.remove_style_class_name(className);
|
|
} else {
|
|
this.add_style_class_name(className);
|
|
}
|
|
}
|
|
}
|
|
|
|
_setPopupTimeout() {
|
|
this._removeMenuTimeout();
|
|
this._menuTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, MENU_POPUP_TIMEOUT, () => {
|
|
this._menuTimeoutId = 0;
|
|
this.popupMenu();
|
|
return GLib.SOURCE_REMOVE;
|
|
});
|
|
GLib.Source.set_name_by_id(this._menuTimeoutId, '[gnome-shell] this.popupMenu');
|
|
}
|
|
|
|
vfunc_leave_event(crossingEvent) {
|
|
let ret = super.vfunc_leave_event(crossingEvent);
|
|
|
|
this.fake_release();
|
|
this._removeMenuTimeout();
|
|
return ret;
|
|
}
|
|
|
|
vfunc_button_press_event(buttonEvent) {
|
|
super.vfunc_button_press_event(buttonEvent);
|
|
if (this._type == ApplicationType.APPSBUTTON || this._type == ApplicationType.APPLICATION) {
|
|
if (buttonEvent.button == 1) {
|
|
this._setPopupTimeout();
|
|
} else if (buttonEvent.button == 3) {
|
|
this.popupMenu();
|
|
return Clutter.EVENT_STOP;
|
|
}
|
|
}
|
|
return Clutter.EVENT_PROPAGATE;
|
|
}
|
|
|
|
vfunc_touch_event(touchEvent) {
|
|
super.vfunc_touch_event(touchEvent);
|
|
if (touchEvent.type == Clutter.EventType.TOUCH_BEGIN)
|
|
this._setPopupTimeout();
|
|
|
|
return Clutter.EVENT_PROPAGATE;
|
|
}
|
|
|
|
vfunc_clicked(button) {
|
|
this._removeMenuTimeout();
|
|
this.activate(button);
|
|
}
|
|
|
|
_onKeyboardPopupMenu() {
|
|
this.popupMenu();
|
|
this._menu.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
|
|
}
|
|
|
|
getId() {
|
|
return this.app.get_id();
|
|
}
|
|
|
|
popupMenu() {
|
|
if (this._type != ApplicationType.APPSBUTTON && this._type != ApplicationType.APPLICATION)
|
|
return false;
|
|
|
|
this._removeMenuTimeout();
|
|
this.fake_release();
|
|
|
|
if (this._draggable)
|
|
this._draggable.fakeRelease();
|
|
|
|
if (!this._menu) {
|
|
this._menu = new ShortcutButtonMenu(this);
|
|
this._menu.connect('activate-window', (menu, window) => {
|
|
this.activateWindow(window);
|
|
});
|
|
this._menu.connect('open-state-changed', (menu, isPoppedUp) => {
|
|
if (!isPoppedUp)
|
|
this._onMenuPoppedDown();
|
|
});
|
|
let id = Main.overview.connect('hiding', () => {
|
|
this._menu.close();
|
|
});
|
|
this.connect('destroy', () => {
|
|
Main.overview.disconnect(id);
|
|
});
|
|
|
|
this._menuManager.addMenu(this._menu);
|
|
}
|
|
|
|
this.emit('menu-state-changed', true);
|
|
|
|
this._panel.setPopupMenuFlag(true);
|
|
this._panel.hideThumbnails();
|
|
|
|
this.set_hover(true);
|
|
this._menu.popup();
|
|
this._menuManager.ignoreRelease();
|
|
this.emit('sync-tooltip');
|
|
|
|
return false;
|
|
}
|
|
|
|
activateWindow(metaWindow) {
|
|
if (metaWindow)
|
|
Main.activateWindow(metaWindow);
|
|
else
|
|
Main.overview.hide();
|
|
}
|
|
|
|
_onMenuPoppedDown() {
|
|
this.sync_hover();
|
|
this.emit('menu-state-changed', false);
|
|
|
|
this._panel.setPopupMenuFlag(false);
|
|
this._panel.showThumbnails();
|
|
}
|
|
|
|
activate(button) {
|
|
let event = Clutter.get_current_event();
|
|
let modifiers = event ? event.get_state() : 0;
|
|
let isPrimaryButton = button && button == Clutter.BUTTON_PRIMARY;
|
|
let isMiddleButton = button && button == Clutter.BUTTON_MIDDLE;
|
|
let isCtrlPressed = (modifiers & Clutter.ModifierType.CONTROL_MASK) != 0;
|
|
|
|
let openNewWindow = false;
|
|
if (this._type == ApplicationType.APPLICATION) {
|
|
openNewWindow = this.app.can_open_new_window() &&
|
|
this.app.state == Shell.AppState.RUNNING &&
|
|
(isCtrlPressed || isMiddleButton);
|
|
}
|
|
|
|
if (isPrimaryButton) {
|
|
if (this._type == ApplicationType.APPLICATION) {
|
|
// let tracker = Shell.WindowTracker.get_default();
|
|
// if (this.app.state == Shell.AppState.RUNNING) {
|
|
// if (this.app == tracker.focus_app && !Main.overview._shown) {
|
|
// this._cycleThroughWindows();
|
|
// } else {
|
|
// // If we activate the app (this.app.activate), all app
|
|
// // windows will come to the foreground. We only want to
|
|
// // activate one window at a time
|
|
// let windows = getInterestingWindows(this.app);
|
|
// let w = windows[0];
|
|
// Main.activateWindow(w);
|
|
// }
|
|
// } else {
|
|
// this.app.open_new_window(-1);
|
|
// }
|
|
if (this.app.state == Shell.AppState.STOPPED || openNewWindow)
|
|
this.animateLaunch();
|
|
|
|
if (openNewWindow)
|
|
this.app.open_new_window(-1);
|
|
else
|
|
this.app.activate();
|
|
|
|
Main.overview.hide();
|
|
} else if (this._type == ApplicationType.PLACE) {
|
|
this.app.launch(global.get_current_time());
|
|
} else if (this._type == ApplicationType.RECENT) {
|
|
Gio.app_info_launch_default_for_uri(this.app.uri, global.create_app_launch_context());
|
|
} else if (this._type == ApplicationType.APPSBUTTON) {
|
|
if (Main.overview.visible) {
|
|
if (Main.overview.viewSelector._showAppsButton.checked) {
|
|
Main.overview.hide();
|
|
Main.overview.viewSelector._showAppsButton.checked = false;
|
|
} else {
|
|
Main.overview.viewSelector._showAppsButton.checked = true;
|
|
}
|
|
} else {
|
|
// passingthru67: ISSUES #49 & #50
|
|
// Workaround issue by detecting animation status
|
|
// Showing the overview after checking the showAppsButton fails
|
|
// to animate when Gnome animations are enabled. On the other hand,
|
|
// showing the overview before checking the showAppsButton fails
|
|
// to scroll when Gnome animations are disabled.
|
|
if (this._gDesktopInterfaceSettings.get_boolean('enable-animations')) {
|
|
Main.overview.show();
|
|
Main.overview.viewSelector._showAppsButton.checked = true;
|
|
} else {
|
|
Main.overview.viewSelector._showAppsButton.checked = true;
|
|
Main.overview.show();
|
|
}
|
|
}
|
|
}
|
|
} else if (isMiddleButton) {
|
|
if (this._type == ApplicationType.APPLICATION) {
|
|
this.app.open_new_window(-1);
|
|
}
|
|
}
|
|
return Clutter.EVENT_PROPAGATE;
|
|
}
|
|
|
|
_cycleThroughWindows() {
|
|
// Store for a little amount of time last time app was clicked
|
|
// since the order changes upon window interaction
|
|
let MEMORY_TIME = 3000;
|
|
|
|
let appWindows = getInterestingWindows(this.app);
|
|
|
|
if(recentlyClickedAppLoopId>0)
|
|
GLib.source_remove(recentlyClickedAppLoopId);
|
|
|
|
recentlyClickedAppLoopId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, MEMORY_TIME, this._resetClickedApp);
|
|
|
|
// 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() != this.app.get_id() ||
|
|
recentlyClickedAppWindows.length != appWindows.length
|
|
) {
|
|
|
|
recentlyClickedApp = this.app;
|
|
recentlyClickedAppWindows = appWindows;
|
|
recentlyClickedAppIndex = 0;
|
|
}
|
|
|
|
recentlyClickedAppIndex ++;
|
|
let index = recentlyClickedAppIndex % recentlyClickedAppWindows.length;
|
|
let window = recentlyClickedAppWindows[index];
|
|
Main.activateWindow(window);
|
|
}
|
|
|
|
_resetClickedApp() {
|
|
if(recentlyClickedAppLoopId>0)
|
|
GLib.source_remove(recentlyClickedAppLoopId);
|
|
|
|
recentlyClickedAppLoopId=0;
|
|
recentlyClickedApp =null;
|
|
recentlyClickedAppWindows = null;
|
|
recentlyClickedAppIndex = 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
animateLaunch() {
|
|
this.icon.animateZoomOut();
|
|
}
|
|
|
|
animateLaunchAtPos(x, y) {
|
|
this.icon.animateZoomOutAtPos(x, y);
|
|
}
|
|
|
|
scaleIn() {
|
|
this.scale_x = 0;
|
|
this.scale_y = 0;
|
|
|
|
this.ease({
|
|
scale_x: 1,
|
|
scale_y: 1,
|
|
duration: APP_ICON_SCALE_IN_TIME,
|
|
delay: APP_ICON_SCALE_IN_DELAY,
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUINT,
|
|
});
|
|
}
|
|
|
|
shellWorkspaceLaunch(params) {
|
|
let { stack } = new Error();
|
|
log('shellWorkspaceLaunch is deprecated, use app.open_new_window() instead\n%s'.format(stack));
|
|
|
|
params = Params.parse(params, { workspace: -1,
|
|
timestamp: 0 });
|
|
|
|
if (this._type == ApplicationType.APPLICATION) {
|
|
this.app.open_new_window(params.workspace);
|
|
} else if (this._type == ApplicationType.PLACE) {
|
|
this.app.launch(global.get_current_time(), params.workspace);
|
|
} else if (this._type == ApplicationType.RECENT) {
|
|
Gio.app_info_launch_default_for_uri(this.app.uri, global.create_app_launch_context());
|
|
} else if (this._type == ApplicationType.APPSBUTTON) {
|
|
if (Main.overview.visible) {
|
|
if (Main.overview.viewSelector._showAppsButton.checked) {
|
|
Main.overview.hide();
|
|
Main.overview.viewSelector._showAppsButton.checked = false;
|
|
} else {
|
|
Main.overview.viewSelector._showAppsButton.checked = true;
|
|
}
|
|
} else {
|
|
Main.overview.show();
|
|
Main.overview.viewSelector._showAppsButton.checked = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
getDragActor() {
|
|
let appIcon;
|
|
if (this._type == ApplicationType.APPLICATION) {
|
|
appIcon = this.app.create_icon_texture(this._iconSize);
|
|
} else if (this._type == ApplicationType.PLACE) {
|
|
appIcon = new St.Icon({gicon: this.app.icon, icon_size: this._iconSize});
|
|
} else if (this._type == ApplicationType.RECENT) {
|
|
let gicon = Gio.content_type_get_icon(this.app.mime);
|
|
appIcon = new St.Icon({gicon: gicon, icon_size: this._iconSize});
|
|
} else if (this._type == ApplicationType.APPSBUTTON) {
|
|
appIcon = new St.Icon({icon_name: 'view-app-grid-symbolic', icon_size: iconSize});
|
|
}
|
|
return appIcon;
|
|
}
|
|
|
|
// Returns the original actor that should align with the actor
|
|
// we show as the item is being dragged.
|
|
getDragActorSource() {
|
|
return this.icon.icon;
|
|
}
|
|
|
|
shouldShowTooltip() {
|
|
return this.hover && (!this._menu || !this._menu.isOpen);
|
|
}
|
|
|
|
scaleAndFade() {
|
|
this.reactive = false;
|
|
this.ease({
|
|
scale_x: 0.75,
|
|
scale_y: 0.75,
|
|
opacity: 128,
|
|
});
|
|
}
|
|
|
|
undoScaleAndFade() {
|
|
this.reactive = true;
|
|
this.ease({
|
|
scale_x: 1.0,
|
|
scale_y: 1.0,
|
|
opacity: 255,
|
|
});
|
|
}
|
|
|
|
_canAccept(source) {
|
|
return false;
|
|
|
|
// let view = _getViewFromIcon(source);
|
|
//
|
|
// return source != this &&
|
|
// (source instanceof this.constructor) &&
|
|
// (view instanceof AllView);
|
|
}
|
|
|
|
_setHoveringByDnd(hovering) {
|
|
// if (hovering) {
|
|
// if (this._folderPreviewId > 0)
|
|
// return;
|
|
//
|
|
// this._folderPreviewId =
|
|
// GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
|
|
// this.add_style_pseudo_class('drop');
|
|
// this._showFolderPreview();
|
|
// this._folderPreviewId = 0;
|
|
// return GLib.SOURCE_REMOVE;
|
|
// });
|
|
// } else {
|
|
// if (this._folderPreviewId > 0) {
|
|
// GLib.source_remove(this._folderPreviewId);
|
|
// this._folderPreviewId = 0;
|
|
// }
|
|
// this._hideFolderPreview();
|
|
// this.remove_style_pseudo_class('drop');
|
|
// }
|
|
}
|
|
|
|
_onDragBegin() {
|
|
this._dragMonitor = {
|
|
dragMotion: this._onDragMotion.bind(this),
|
|
};
|
|
DND.addDragMonitor(this._dragMonitor);
|
|
}
|
|
|
|
_onDragMotion(dragEvent) {
|
|
let target = dragEvent.targetActor;
|
|
let isHovering = target == this || this.contains(target);
|
|
let canDrop = this._canAccept(dragEvent.source);
|
|
let hasDndHover = isHovering && canDrop;
|
|
|
|
if (this._hasDndHover != hasDndHover) {
|
|
// this._setHoveringByDnd(hasDndHover);
|
|
this._hasDndHover = hasDndHover;
|
|
}
|
|
|
|
return DND.DragMotionResult.CONTINUE;
|
|
}
|
|
|
|
_onDragEnd() {
|
|
this.remove_style_pseudo_class('drop');
|
|
DND.removeDragMonitor(this._dragMonitor);
|
|
}
|
|
|
|
handleDragOver(source) {
|
|
if (source == this)
|
|
return DND.DragMotionResult.NO_DROP;
|
|
|
|
if (!this._canAccept(source))
|
|
return DND.DragMotionResult.CONTINUE;
|
|
|
|
return DND.DragMotionResult.MOVE_DROP;
|
|
}
|
|
|
|
acceptDrop(source) {
|
|
// this._setHoveringByDnd(false);
|
|
|
|
if (!this._canAccept(source))
|
|
return false;
|
|
|
|
let view = _getViewFromIcon(this);
|
|
let apps = [this.id, source.id];
|
|
|
|
return view.createFolder(apps);
|
|
}
|
|
});
|
|
|
|
var ShortcutButtonMenu = class WorkspacesToDock_ShortcutButtonMenu extends PopupMenu.PopupMenu {
|
|
constructor(source) {
|
|
let settings = Convenience.getSettings('org.gnome.shell.extensions.workspaces-to-dock');
|
|
let side = getPosition(settings);
|
|
|
|
super(source, 0.5, side);
|
|
this._settings = settings;
|
|
|
|
// We want to keep the item hovered while the menu is up
|
|
this.blockSourceEvents = true;
|
|
|
|
this._source = source;
|
|
|
|
this.actor.add_style_class_name('app-well-menu');
|
|
|
|
// Chain our visibility and lifecycle to that of the source
|
|
this._sourceMappedId = source.connect('notify::mapped', () => {
|
|
if (!source.mapped)
|
|
this.close();
|
|
});
|
|
source.connect('destroy', () => {
|
|
source.disconnect(this._sourceMappedId);
|
|
this.destroy();
|
|
});
|
|
|
|
Main.uiGroup.add_actor(this.actor);
|
|
}
|
|
|
|
_redisplay() {
|
|
this.removeAll();
|
|
|
|
// passingthru67: appsbutton menu to show extension preferences
|
|
if (this._source._type == ApplicationType.APPSBUTTON) {
|
|
let item = this._appendMenuItem(_("Extension Preferences"));
|
|
item.connect('activate', () => {
|
|
if (typeof ExtensionUtils.openPrefs === 'function') {
|
|
ExtensionUtils.openPrefs();
|
|
} else {
|
|
Util.spawn(["gnome-shell-extension-prefs", Me.metadata.uuid]);
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
let windows = this._source.app.get_windows().filter(
|
|
w => !w.skip_taskbar
|
|
);
|
|
|
|
if (windows.length > 0) {
|
|
this.addMenuItem(
|
|
/* Translators: This is the heading of a list of open windows */
|
|
new PopupMenu.PopupSeparatorMenuItem(_("Open Windows"))
|
|
);
|
|
}
|
|
|
|
windows.forEach(window => {
|
|
let title = window.title
|
|
? window.title : this._source.app.get_name();
|
|
let item = this._appendMenuItem(title);
|
|
item.connect('activate', () => {
|
|
this.emit('activate-window', window);
|
|
});
|
|
});
|
|
|
|
if (!this._source.app.is_window_backed()) {
|
|
this._appendSeparator();
|
|
|
|
let appInfo = this._source.app.get_app_info();
|
|
let actions = appInfo.list_actions();
|
|
if (this._source.app.can_open_new_window() &&
|
|
!actions.includes('new-window')) {
|
|
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
|
|
this._newWindowMenuItem.connect('activate', () => {
|
|
this._source.animateLaunch();
|
|
this._source.app.open_new_window(-1);
|
|
this.emit('activate-window', null);
|
|
});
|
|
this._appendSeparator();
|
|
}
|
|
|
|
// if (discreteGpuAvailable &&
|
|
// this._source.app.state == Shell.AppState.STOPPED) {
|
|
// this._onDiscreteGpuMenuItem = this._appendMenuItem(_("Launch using Dedicated Graphics Card"));
|
|
// this._onDiscreteGpuMenuItem.connect('activate', () => {
|
|
// this._source.animateLaunch();
|
|
// this._source.app.launch(0, -1, true);
|
|
// this.emit('activate-window', null);
|
|
// });
|
|
// }
|
|
|
|
for (let i = 0; i < actions.length; i++) {
|
|
let action = actions[i];
|
|
let item = this._appendMenuItem(appInfo.get_action_name(action));
|
|
item.connect('activate', (emitter, event) => {
|
|
if (action == 'new-window')
|
|
this._source.animateLaunch();
|
|
|
|
this._source.app.launch_action(action, event.get_time(), -1);
|
|
this.emit('activate-window', null);
|
|
});
|
|
}
|
|
|
|
let canFavorite = global.settings.is_writable('favorite-apps');
|
|
|
|
if (canFavorite) {
|
|
this._appendSeparator();
|
|
|
|
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
|
|
|
|
if (isFavorite) {
|
|
let item = this._appendMenuItem(_("Remove from Favorites"));
|
|
item.connect('activate', () => {
|
|
let favs = AppFavorites.getAppFavorites();
|
|
favs.removeFavorite(this._source.app.get_id());
|
|
});
|
|
} else {
|
|
let item = this._appendMenuItem(_("Add to Favorites"));
|
|
item.connect('activate', () => {
|
|
let favs = AppFavorites.getAppFavorites();
|
|
favs.addFavorite(this._source.app.get_id());
|
|
});
|
|
}
|
|
}
|
|
|
|
if (Shell.AppSystem.get_default().lookup_app('org.gnome.Software.desktop')) {
|
|
this._appendSeparator();
|
|
let item = this._appendMenuItem(_("Show Details"));
|
|
item.connect('activate', () => {
|
|
let id = this._source.app.get_id();
|
|
let args = GLib.Variant.new('(ss)', [id, '']);
|
|
Gio.DBus.get(Gio.BusType.SESSION, null, (o, res) => {
|
|
let bus = Gio.DBus.get_finish(res);
|
|
bus.call('org.gnome.Software',
|
|
'/org/gnome/Software',
|
|
'org.gtk.Actions', 'Activate',
|
|
GLib.Variant.new('(sava{sv})',
|
|
['details', [args], null]),
|
|
null, 0, -1, null, null);
|
|
Main.overview.hide();
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
_appendSeparator() {
|
|
let separator = new PopupMenu.PopupSeparatorMenuItem();
|
|
this.addMenuItem(separator);
|
|
}
|
|
|
|
_appendMenuItem(labelText) {
|
|
// FIXME: app-well-menu-item style
|
|
let item = new PopupMenu.PopupMenuItem(labelText);
|
|
this.addMenuItem(item);
|
|
return item;
|
|
}
|
|
|
|
popup(_activatingButton) {
|
|
this._redisplay();
|
|
|
|
if (this._settings.get_boolean('shortcuts-panel-popupmenu-arrow-at-top')) {
|
|
this._arrowAlignment = 0.0;
|
|
} else {
|
|
this._arrowAlignment = 0.5;
|
|
}
|
|
|
|
this.open();
|
|
}
|
|
};
|
|
Signals.addSignalMethods(ShortcutButtonMenu.prototype);
|
|
|
|
var ShortcutsPanel = class WorkspacesToDock_ShortcutsPanel {
|
|
constructor(dock) {
|
|
this._dock = dock;
|
|
this._settings = Convenience.getSettings('org.gnome.shell.extensions.workspaces-to-dock');
|
|
this._position = getPosition(this._settings);
|
|
this._isHorizontal = (this._position == St.Side.TOP ||
|
|
this._position == St.Side.BOTTOM);
|
|
|
|
let packVertical = true;
|
|
if (this._isHorizontal)
|
|
packVertical = false;
|
|
|
|
// Set _centerContainer property
|
|
if (this._settings.get_boolean('customize-height') && this._settings.get_boolean('center-thumbnails-on-dock')) {
|
|
this._centerContainer = true;
|
|
} else {
|
|
this._centerContainer = false;
|
|
}
|
|
|
|
// Set _centerPanelsIndependently property
|
|
if (this._centerContainer && this._settings.get_int('center-thumbnails-option') == 0) {
|
|
this._centerPanelsIndependently = true;
|
|
} else {
|
|
this._centerPanelsIndependently = false;
|
|
}
|
|
|
|
this.actor = new St.BoxLayout({
|
|
style_class: 'workspace-thumbnails workspacestodock-shortcuts-panel',
|
|
vertical: packVertical,
|
|
clip_to_allocation: true,
|
|
x_align: (this._centerContainer && this._centerPanelsIndependently) ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.START,
|
|
y_align: (this._centerContainer && this._centerPanelsIndependently) ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.START
|
|
});
|
|
this.actor._delegate = this;
|
|
|
|
this._appSystem = Shell.AppSystem.get_default();
|
|
this._appFavorites = AppFavorites.getAppFavorites();
|
|
|
|
this._installedChangedId = this._appSystem.connect('installed-changed', () => {
|
|
this._appFavorites.reload();
|
|
this.refresh();
|
|
});
|
|
|
|
// Connect to AppSystem and listen for app state changes
|
|
this._appStateChangedId = this._appSystem.connect('app-state-changed', this._updateRunningApps.bind(this));
|
|
|
|
// Connect to AppFavorites and listen for favorites changes
|
|
this._favoritesChangedId = this._appFavorites.connect('changed', this._queueUpdateFavoriteApps.bind(this));
|
|
|
|
// Connect to item drag signals
|
|
this._dragPlaceholder = null;
|
|
this._dragPlaceholderPos = -1;
|
|
this._itemDragBeginId = Main.overview.connect('item-drag-begin', this._onDragBegin.bind(this));
|
|
this._itemDragEndId = Main.overview.connect('item-drag-end', this._onDragEnd.bind(this));
|
|
this._itemDragCancelId = Main.overview.connect('item-drag-cancelled', this._onDragCancelled.bind(this));
|
|
|
|
// Bind Preference Settings
|
|
this._bindSettingsChanges();
|
|
|
|
// Populate panel
|
|
this._populate();
|
|
}
|
|
|
|
destroy() {
|
|
|
|
// Disconnect global signals
|
|
if (this._itemDragBeginId > 0) Main.overview.disconnect(this._itemDragBeginId);
|
|
if (this._itemDragEndId > 0) Main.overview.disconnect(this._itemDragEndId);
|
|
if (this._itemDragCancelId > 0) Main.overview.disconnect(this._itemDragCancelId);
|
|
if (this._installedChangedId > 0) this._appSystem.disconnect(this._installedChangedId);
|
|
if (this._appStateChangedId > 0) this._appSystem.disconnect(this._appStateChangedId);
|
|
if (this._favoritesChangedId > 0) this._appFavorites.disconnect(this._favoritesChangedId);
|
|
|
|
// Destroy main clutter actor
|
|
this.actor.destroy_all_children();
|
|
this.actor.destroy();
|
|
|
|
// Disconnect GSettings signals
|
|
this._settings.run_dispose();
|
|
}
|
|
|
|
_bindSettingsChanges() {
|
|
this._settings.connect('changed::shortcuts-panel-show-running', () => {
|
|
this.refresh();
|
|
});
|
|
this._settings.connect('changed::shortcuts-panel-show-places', () => {
|
|
this.refresh();
|
|
});
|
|
this._settings.connect('changed::shortcuts-panel-show-window-count-indicators', () => {
|
|
this.refresh();
|
|
});
|
|
this._settings.connect('changed::shortcuts-panel-appsbutton-at-bottom', () => {
|
|
this.refresh();
|
|
});
|
|
}
|
|
|
|
_onDragBegin() {
|
|
this._dragCancelled = false;
|
|
this._dragMonitor = {
|
|
dragMotion: this._onDragMotion.bind(this)
|
|
};
|
|
DND.addDragMonitor(this._dragMonitor);
|
|
}
|
|
|
|
_onDragCancelled() {
|
|
this._dragCancelled = true;
|
|
this._endDrag();
|
|
}
|
|
|
|
_onDragEnd() {
|
|
if (this._dragCancelled)
|
|
return;
|
|
|
|
this._endDrag();
|
|
}
|
|
|
|
_endDrag() {
|
|
this._clearDragPlaceholder();
|
|
DND.removeDragMonitor(this._dragMonitor);
|
|
}
|
|
|
|
_onDragMotion(dragEvent) {
|
|
let app = getAppFromSource(dragEvent.source);
|
|
if (app == null)
|
|
return DND.DragMotionResult.CONTINUE;
|
|
|
|
if (!this.actor.contains(dragEvent.targetActor))
|
|
this._clearDragPlaceholder();
|
|
|
|
return DND.DragMotionResult.CONTINUE;
|
|
}
|
|
|
|
_clearDragPlaceholder() {
|
|
if (this._dragPlaceholder) {
|
|
this._dragPlaceholder.destroy();
|
|
this._dragPlaceholder = null;
|
|
}
|
|
this._dragPlaceholderPos = -1;
|
|
}
|
|
|
|
handleDragOver(source, actor, x, y, time) {
|
|
let app = getAppFromSource(source);
|
|
|
|
// Don't allow favoriting of transient apps
|
|
if (app == null || app.is_window_backed())
|
|
return DND.DragMotionResult.NO_DROP;
|
|
|
|
if (!global.settings.is_writable('favorite-apps'))
|
|
return DND.DragMotionResult.NO_DROP;
|
|
|
|
let favorites = AppFavorites.getAppFavorites().getFavorites();
|
|
let numFavorites = favorites.length;
|
|
|
|
let favPos = favorites.indexOf(app);
|
|
|
|
let children = this._favoriteAppsBox.get_children();
|
|
let numChildren = children.length;
|
|
let boxH;
|
|
let boxY;
|
|
if (this._isHorizontal) {
|
|
boxY = this._favoriteAppsBox.x;
|
|
boxH = this._favoriteAppsBox.width;
|
|
} else {
|
|
boxY = this._favoriteAppsBox.y;
|
|
boxH = this._favoriteAppsBox.height;
|
|
}
|
|
|
|
// Keep the placeholder out of the index calculation; assuming that
|
|
// the remove target has the same size as "normal" items, we don't
|
|
// need to do the same adjustment there.
|
|
if (this._dragPlaceholder) {
|
|
if (this._isHorizontal) {
|
|
boxH -= this._dragPlaceholder.width;
|
|
} else {
|
|
boxH -= this._dragPlaceholder.height;
|
|
}
|
|
numChildren--;
|
|
}
|
|
|
|
let pos;
|
|
let posY;
|
|
if (this._isHorizontal) {
|
|
posY = x - boxY;
|
|
} else {
|
|
posY = y - boxY;
|
|
}
|
|
pos = Math.floor(posY * numChildren / boxH);
|
|
|
|
if (pos != this._dragPlaceholderPos && pos <= numFavorites) {
|
|
this._dragPlaceholderPos = pos;
|
|
|
|
// Don't allow positioning before or after self
|
|
if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
|
|
this._clearDragPlaceholder();
|
|
return DND.DragMotionResult.CONTINUE;
|
|
}
|
|
|
|
// If the placeholder already exists, we just move
|
|
// it, but if we are adding it, expand its size in
|
|
// an animation
|
|
// Passingthru67 TODO:
|
|
// Need to convert the dragPlaceholder to an St.Widget so that
|
|
// we can use the fadein animation
|
|
let fadeIn;
|
|
if (this._dragPlaceholder) {
|
|
this._dragPlaceholder.destroy();
|
|
fadeIn = false;
|
|
} else {
|
|
fadeIn = true;
|
|
}
|
|
|
|
this._dragPlaceholder = new MyDragPlaceholderItem();
|
|
this._favoriteAppsBox.insert_child_at_index(this._dragPlaceholder,
|
|
this._dragPlaceholderPos);
|
|
// this._dragPlaceholder.show(fadeIn);
|
|
this._dragPlaceholder.show();
|
|
}
|
|
|
|
// Remove the drag placeholder if we are not in the
|
|
// "favorites zone"
|
|
if (pos > numFavorites)
|
|
this._clearDragPlaceholder();
|
|
|
|
if (!this._dragPlaceholder)
|
|
return DND.DragMotionResult.NO_DROP;
|
|
|
|
let srcIsFavorite = (favPos != -1);
|
|
if (srcIsFavorite) {
|
|
return DND.DragMotionResult.MOVE_DROP;
|
|
}
|
|
|
|
return DND.DragMotionResult.COPY_DROP;
|
|
}
|
|
|
|
// Draggable target interface
|
|
acceptDrop(source, actor, x, y, time) {
|
|
let app = getAppFromSource(source);
|
|
|
|
// Don't allow favoriting of transient apps
|
|
if (app == null || app.is_window_backed()) {
|
|
return false;
|
|
}
|
|
|
|
if (!global.settings.is_writable('favorite-apps'))
|
|
return false;
|
|
|
|
let id = app.get_id();
|
|
|
|
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
|
|
|
let srcIsFavorite = (id in favorites);
|
|
|
|
let favPos = 0;
|
|
let children = this._favoriteAppsBox.get_children();
|
|
for (let i = 0; i < this._dragPlaceholderPos; i++) {
|
|
if (this._dragPlaceholder &&
|
|
children[i] == this._dragPlaceholder)
|
|
continue;
|
|
|
|
let childId = children[i]._delegate.app.get_id();
|
|
if (childId == id) {
|
|
continue;
|
|
}
|
|
if (childId in favorites) {
|
|
favPos++;
|
|
}
|
|
}
|
|
|
|
// No drag placeholder means we don't wan't to favorite the app
|
|
// and we are dragging it to its original position
|
|
if (!this._dragPlaceholder)
|
|
return true;
|
|
|
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
|
|
let appFavorites = AppFavorites.getAppFavorites();
|
|
if (srcIsFavorite)
|
|
appFavorites.moveFavoriteToPos(id, favPos);
|
|
else
|
|
appFavorites.addFavoriteAtPos(id, favPos);
|
|
return false;
|
|
});
|
|
|
|
this._clearDragPlaceholder();
|
|
return true;
|
|
}
|
|
|
|
setPopupMenuFlag(showing) {
|
|
this._dock.setPopupMenuFlag(showing);
|
|
}
|
|
|
|
hideThumbnails() {
|
|
if (this._settings.get_boolean('shortcuts-panel-popupmenu-hide-thumbnails')) {
|
|
if (this._settings.get_enum('shortcuts-panel-orientation') == ShortcutsPanelOrientation.OUTSIDE) {
|
|
this._dock._thumbnailsBox.actor.opacity = 0;
|
|
this.actor.remove_style_class_name('workspacestodock-shortcuts-panel');
|
|
this.actor.add_style_class_name('workspacestodock-shortcuts-panel-popupmenu');
|
|
// for (let i = 0; i < this._dock._thumbnailsBox._thumbnails.length; i++) {
|
|
// this._dock._thumbnailsBox._thumbnails[i].actor.opacity = 0;
|
|
// }
|
|
// this._dock._thumbnailsBox._indicator.opacity = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
showThumbnails() {
|
|
if (this._settings.get_boolean('shortcuts-panel-popupmenu-hide-thumbnails')) {
|
|
this._dock._thumbnailsBox.actor.opacity = 255;
|
|
this.actor.remove_style_class_name('workspacestodock-shortcuts-panel-popupmenu');
|
|
this.actor.add_style_class_name('workspacestodock-shortcuts-panel');
|
|
// for (let i = 0; i < this._dock._thumbnailsBox._thumbnails.length; i++) {
|
|
// this._dock._thumbnailsBox._thumbnails[i].actor.opacity = 255;
|
|
// }
|
|
// this._dock._thumbnailsBox._indicator.opacity = 255;
|
|
}
|
|
}
|
|
|
|
setReactiveState(state) {
|
|
if (state == null)
|
|
return;
|
|
|
|
// Deactive Apps button
|
|
this._appsButton.reactive = state;
|
|
|
|
// Deactivate favorites
|
|
if (this._favoriteAppsBox) {
|
|
let children = this._favoriteAppsBox.get_children();
|
|
for (let i = 0; i < children.length; i++) {
|
|
children[i].reactive = state;
|
|
}
|
|
}
|
|
|
|
// Deactivate running apps
|
|
if (this._runningAppsBox) {
|
|
let children = this._runningAppsBox.get_children();
|
|
for (let i = 0; i < children.length; i++) {
|
|
children[i].reactive = state;
|
|
}
|
|
}
|
|
|
|
// Deactivate places
|
|
if (this._placesBox) {
|
|
let children = this._placesBox.get_children();
|
|
for (let i = 0; i < children.length; i++) {
|
|
children[i].reactive = state;
|
|
}
|
|
}
|
|
}
|
|
|
|
refresh() {
|
|
this._clear();
|
|
this._populate();
|
|
}
|
|
|
|
_clear() {
|
|
this.actor.destroy_all_children();
|
|
}
|
|
|
|
_populate() {
|
|
let packVertical = true;
|
|
if (this._isHorizontal)
|
|
packVertical = false;
|
|
|
|
// Add Favorite Apps Box
|
|
this._favoriteAppsBox = new St.BoxLayout({
|
|
vertical: packVertical,
|
|
style_class: 'workspacestodock-shortcuts-panel workspacestodock-shortcuts-panel-favorites'
|
|
});
|
|
this.actor.add_actor(this._favoriteAppsBox);
|
|
this._favoriteAppsWorkId = Main.initializeDeferredWork(this._favoriteAppsBox, this._updateFavoriteApps.bind(this));
|
|
|
|
// Add Running Apps Box
|
|
if (this._settings.get_boolean('shortcuts-panel-show-running')) {
|
|
this._runningAppsBox = new St.BoxLayout({
|
|
vertical: packVertical,
|
|
style_class: 'workspacestodock-shortcuts-panel workspacestodock-shortcuts-panel-running'
|
|
});
|
|
this.actor.add_actor(this._runningAppsBox);
|
|
this._updateRunningApps();
|
|
}
|
|
|
|
if (this._settings.get_boolean('shortcuts-panel-show-places')) {
|
|
this._placesBox = new St.BoxLayout({
|
|
vertical: packVertical,
|
|
style_class: 'workspacestodock-shortcuts-panel workspacestodock-shortcuts-panel-places'
|
|
});
|
|
this.actor.add_actor(this._placesBox);
|
|
|
|
// Get places
|
|
let placesManager = new PlaceDisplay.PlacesManager();
|
|
let special = placesManager.get('special');
|
|
|
|
let allPlaces = [];
|
|
allPlaces = allPlaces.concat(special);
|
|
|
|
// Add places to Places Box
|
|
for (let i = 0; i < allPlaces.length; ++i) {
|
|
let app = allPlaces[i];
|
|
let shortcutButton = new ShortcutButton(app, ApplicationType.PLACE);
|
|
this._placesBox.add_actor(shortcutButton);
|
|
}
|
|
}
|
|
|
|
// Add Apps Button to top or bottom of shortcuts panel
|
|
this._appsButton = new ShortcutButton(null, ApplicationType.APPSBUTTON, this);
|
|
if (this._settings.get_boolean('shortcuts-panel-appsbutton-at-bottom')) {
|
|
let filler = new St.Widget({
|
|
style_class: 'popup-separator-menu-item workspacestodock-shortcut-panel-filler',
|
|
x_expand: true,
|
|
y_expand: true
|
|
});
|
|
this.actor.add_actor(filler);
|
|
this.actor.add_actor(this._appsButton);
|
|
} else {
|
|
this.actor.insert_child_at_index(this._appsButton, 0);
|
|
}
|
|
}
|
|
|
|
_queueUpdateFavoriteApps () {
|
|
Main.queueDeferredWork(this._favoriteAppsWorkId);
|
|
}
|
|
|
|
_updateFavoriteApps() {
|
|
if (!this._favoriteAppsBox)
|
|
return;
|
|
|
|
// Clear favorite apps box
|
|
this._favoriteAppsBox.destroy_all_children();
|
|
|
|
// Apps supposed to be in the favorite apps box
|
|
let newApps = [];
|
|
|
|
// Get favorites
|
|
let favorites = this._appFavorites.getFavoriteMap();
|
|
for (let id in favorites) {
|
|
newApps.push(favorites[id]);
|
|
}
|
|
|
|
// Populate shortcuts panel with favorites
|
|
for (let i = 0; i < newApps.length; ++i) {
|
|
let app = newApps[i];
|
|
let shortcutButton = new ShortcutButton(app, ApplicationType.APPLICATION, this);
|
|
this._favoriteAppsBox.add_actor(shortcutButton);
|
|
}
|
|
|
|
this.emit('update-favorite-apps');
|
|
this._updateRunningApps();
|
|
}
|
|
|
|
_updateRunningApps() {
|
|
if (!this._runningAppsBox)
|
|
return;
|
|
|
|
let children = this._runningAppsBox.get_children().filter((actor) => {
|
|
return actor &&
|
|
actor._delegate &&
|
|
actor._delegate.app && actor._delegate._type == ApplicationType.APPLICATION;
|
|
});
|
|
|
|
// Apps currently in running apps box
|
|
let oldApps = children.map(function(actor) {
|
|
return actor._delegate.app;
|
|
});
|
|
|
|
// Apps supposed to be in the running apps box
|
|
let newApps = [];
|
|
|
|
// Get favorites
|
|
let favorites = this._appFavorites.getFavoriteMap();
|
|
|
|
// Get running apps
|
|
let running = this._appSystem.get_running();
|
|
for (let i = 0; i < running.length; i++) {
|
|
let app = running[i];
|
|
if (app.get_id() in favorites)
|
|
continue;
|
|
newApps.push(app);
|
|
}
|
|
|
|
// Figure out the actual changes to the list of items; we iterate
|
|
// over both the list of items currently in the dash and the list
|
|
// of items expected there, and collect additions and removals.
|
|
// Moves are both an addition and a removal, where the order of
|
|
// the operations depends on whether we encounter the position
|
|
// where the item has been added first or the one from where it
|
|
// was removed.
|
|
// There is an assumption that only one item is moved at a given
|
|
// time; when moving several items at once, everything will still
|
|
// end up at the right position, but there might be additional
|
|
// additions/removals (e.g. it might remove all the launchers
|
|
// and add them back in the new order even if a smaller set of
|
|
// additions and removals is possible).
|
|
// If above assumptions turns out to be a problem, we might need
|
|
// to use a more sophisticated algorithm, e.g. Longest Common
|
|
// Subsequence as used by diff.
|
|
let addedItems = [];
|
|
let removedActors = [];
|
|
|
|
let newIndex = 0;
|
|
let oldIndex = 0;
|
|
while (newIndex < newApps.length || oldIndex < oldApps.length) {
|
|
let oldApp = oldApps.length > oldIndex ? oldApps[oldIndex] : null;
|
|
let newApp = newApps.length > newIndex ? newApps[newIndex] : null;
|
|
|
|
// No change at oldIndex/newIndex
|
|
if (oldApp == newApp) {
|
|
oldIndex++;
|
|
newIndex++;
|
|
continue;
|
|
}
|
|
|
|
// App removed at oldIndex
|
|
if (oldApp && newApps.indexOf(oldApp) == -1) {
|
|
removedActors.push(children[oldIndex]);
|
|
oldIndex++;
|
|
continue;
|
|
}
|
|
|
|
// App added at newIndex
|
|
if (newApp && oldApps.indexOf(newApp) == -1) {
|
|
addedItems.push({ app: newApp,
|
|
item: this._createShortcutButton(newApp, ApplicationType.APPLICATION),
|
|
pos: newIndex });
|
|
newIndex++;
|
|
continue;
|
|
}
|
|
|
|
// App moved
|
|
let nextApp = newApps.length > newIndex + 1 ? newApps[newIndex + 1]
|
|
: null;
|
|
let insertHere = nextApp && nextApp == oldApp;
|
|
let alreadyRemoved = removedActors.reduce((result, actor) => {
|
|
let removedApp = actor.child._delegate.app;
|
|
return result || removedApp == newApp;
|
|
}, false);
|
|
|
|
if (insertHere || alreadyRemoved) {
|
|
let newItem = this._createShortcutButton(newApp, shortcutType);
|
|
addedItems.push({ app: newApp,
|
|
item: newItem,
|
|
pos: newIndex + removedActors.length });
|
|
newIndex++;
|
|
} else {
|
|
removedActors.push(children[oldIndex]);
|
|
oldIndex++;
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < addedItems.length; i++)
|
|
this._runningAppsBox.insert_child_at_index(addedItems[i].item,
|
|
addedItems[i].pos);
|
|
|
|
for (let i = 0; i < removedActors.length; i++) {
|
|
let item = removedActors[i];
|
|
item.destroy();
|
|
}
|
|
|
|
this.emit('update-running-apps');
|
|
}
|
|
|
|
_createShortcutButton(app, appType) {
|
|
let shortcutType = app ? appType : ApplicationType.APPSBUTTON;
|
|
let shortcutButton = new ShortcutButton(app, shortcutType, this);
|
|
return shortcutButton;
|
|
}
|
|
};
|
|
Signals.addSignalMethods(ShortcutsPanel.prototype);
|