Files
dash-to-panel/windowPreview.js

945 lines
32 KiB
JavaScript
Raw Normal View History

2016-09-21 00:18:00 +00:00
/*
2016-12-23 11:08:39 -05:00
* This file is part of the Dash-To-Panel extension for Gnome 3
2016-09-21 00:18:00 +00:00
*
* 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
2019-05-11 21:40:48 -04:00
* along with this program. If not, see <http://www.gnu.org/licenses/>.
2016-09-21 00:18:00 +00:00
*/
2019-05-14 17:08:08 -04:00
const Clutter = imports.gi.Clutter;
2019-05-19 10:42:22 -04:00
const Config = imports.misc.config;
2019-05-14 17:08:08 -04:00
const Gtk = imports.gi.Gtk;
2016-09-21 00:18:00 +00:00
const Main = imports.ui.main;
const Mainloop = imports.mainloop;
2019-05-19 10:42:22 -04:00
const Meta = imports.gi.Meta;
2019-05-25 11:08:46 -04:00
const PopupMenu = imports.ui.popupMenu;
2016-09-21 00:18:00 +00:00
const Signals = imports.signals;
const Shell = imports.gi.Shell;
2016-09-21 00:18:00 +00:00
const St = imports.gi.St;
const Tweener = imports.ui.tweener;
2019-05-22 21:37:53 -04:00
const WindowManager = imports.ui.windowManager;
2019-05-19 10:42:22 -04:00
const Workspace = imports.ui.workspace;
2016-09-21 00:18:00 +00:00
const Me = imports.misc.extensionUtils.getCurrentExtension();
const Taskbar = Me.imports.taskbar;
const Utils = Me.imports.utils;
2016-09-21 00:18:00 +00:00
2019-05-11 21:40:48 -04:00
//timeout names
const T1 = 'openMenuTimeout';
const T2 = 'closeMenuTimeout';
const T3 = 'peekTimeout';
const MAX_TRANSLATION = 40;
2019-05-20 00:25:22 -04:00
const HEADER_HEIGHT = 38;
const DEFAULT_RATIO = { w: 160, h: 90 };
2019-05-20 00:25:22 -04:00
const FOCUSED_COLOR_OFFSET = 24;
const HEADER_COLOR_OFFSET = -12;
2019-05-22 21:37:53 -04:00
const PEEK_INDEX_PROP = '_dtpPeekInitialIndex';
2019-05-19 10:42:22 -04:00
var headerHeight = 0;
var isLeftButtons = false;
var isTopHeader = true;
var scaleFactor = 1;
var animationTime = 0;
2019-05-19 10:42:22 -04:00
2019-05-11 21:40:48 -04:00
var PreviewMenu = Utils.defineClass({
Name: 'DashToPanel-PreviewMenu',
2019-05-11 21:40:48 -04:00
Extends: St.Widget,
Signals: { 'open-state-changed': {} },
2016-09-21 00:18:00 +00:00
_init: function(dtpSettings, panelWrapper) {
this.callParent('_init', { layout_manager: new Clutter.BinLayout() });
this._dtpSettings = dtpSettings;
this._panelWrapper = panelWrapper;
2019-05-20 00:25:22 -04:00
this.currentAppIcon = null;
2019-05-20 10:56:22 -04:00
this._focusedPreview = null;
this._peekedWindow = null;
2019-05-25 11:08:46 -04:00
this.peekInitialWorkspaceIndex = -1;
this.opened = false;
this._position = Taskbar.getPosition();
let isLeftOrRight = this._checkIfLeftOrRight();
2019-05-14 17:08:08 -04:00
this._translationProp = 'translation_' + (isLeftOrRight ? 'x' : 'y');
this._translationOffset = Math.min(this._dtpSettings.get_int('panel-size'), MAX_TRANSLATION) *
(this._position == St.Side.TOP || this._position == St.Side.LEFT ? -1 : 1);
2019-05-14 17:08:08 -04:00
this.menu = new St.Widget({ name: 'preview-menu', layout_manager: new Clutter.BinLayout(), reactive: true, track_hover: true });
this._box = new St.BoxLayout({ vertical: isLeftOrRight });
2019-05-14 17:08:08 -04:00
this._scrollView = new St.ScrollView({
name: 'dashtopanelPreviewScrollview',
hscrollbar_policy: Gtk.PolicyType.NEVER,
vscrollbar_policy: Gtk.PolicyType.NEVER,
enable_mouse_scrolling: true
});
this._scrollView.add_actor(this._box);
this.menu.add_child(this._scrollView);
this.add_child(this.menu);
},
2019-05-11 21:40:48 -04:00
enable: function() {
this._timeoutsHandler = new Utils.TimeoutsHandler();
2019-05-14 17:08:08 -04:00
this._signalsHandler = new Utils.GlobalSignalsHandler();
2019-05-20 10:56:22 -04:00
Main.layoutManager.addChrome(this, { trackFullscreen: true, affectsInputRegion: false });
Main.layoutManager.trackChrome(this.menu, { affectsInputRegion: true });
2019-05-14 17:08:08 -04:00
this._resetHiddenState();
2019-05-19 10:42:22 -04:00
this._refreshGlobals();
this._updateClip();
2019-05-20 10:56:22 -04:00
this.menu.set_position(1, 1);
2019-05-15 17:02:18 -04:00
2019-05-14 17:08:08 -04:00
this._signalsHandler.add(
[
this.menu,
2019-05-14 17:08:08 -04:00
'notify::hover',
() => this._onHoverChanged()
],
2019-05-15 17:02:18 -04:00
[
this._scrollView,
'scroll-event',
this._onScrollEvent.bind(this)
],
[
this._dtpSettings,
[
'changed::panel-size',
'changed::window-preview-size',
2019-05-20 00:25:22 -04:00
'changed::window-preview-padding',
'changed::window-preview-show-title'
],
2019-05-20 00:25:22 -04:00
() => {
this._refreshGlobals();
this._updateClip();
}
2019-05-15 17:02:18 -04:00
]
2019-05-14 17:08:08 -04:00
);
},
2019-05-11 21:40:48 -04:00
disable: function() {
this._timeoutsHandler.destroy();
2019-05-14 17:08:08 -04:00
this._signalsHandler.destroy();
2017-01-09 17:54:03 -05:00
this.close(true);
2019-05-14 17:08:08 -04:00
Main.layoutManager._untrackActor(this);
2019-05-11 21:40:48 -04:00
Main.uiGroup.remove_child(this);
this.destroy();
2016-09-21 00:18:00 +00:00
},
requestOpen: function(appIcon) {
this._endOpenCloseTimeouts();
this._timeoutsHandler.add([T1, this._dtpSettings.get_int('show-window-previews-timeout'), () => this.open(appIcon)]);
2016-09-21 00:18:00 +00:00
},
requestClose: function() {
this._endOpenCloseTimeouts();
2019-05-14 17:08:08 -04:00
this._addCloseTimeout();
},
open: function(appIcon) {
2019-05-20 00:25:22 -04:00
if (this.currentAppIcon != appIcon) {
this.currentAppIcon = appIcon;
if (!this.opened) {
this.menu.set_style('background: ' + this._panelWrapper.dynamicTransparency.currentBackgroundColor);
this.menu.show();
2019-05-19 10:42:22 -04:00
this._refreshGlobals();
this.grab_key_focus();
}
2019-05-19 10:42:22 -04:00
this._mergeWindows(appIcon);
this._updatePosition();
2019-05-15 17:02:18 -04:00
this._animateOpenOrClose(true);
2019-05-19 10:42:22 -04:00
this.menu.reactive = true;
this._setOpenedState(true);
2019-05-15 17:02:18 -04:00
}
},
2016-09-21 00:18:00 +00:00
close: function(immediate) {
2019-05-19 17:51:07 -04:00
this._endOpenCloseTimeouts();
2019-05-21 14:31:55 -04:00
this._removeFocus();
this._endPeek();
2019-05-14 17:08:08 -04:00
if (immediate) {
this._resetHiddenState();
} else {
this._animateOpenOrClose(false, () => this._resetHiddenState());
}
2019-05-21 14:31:55 -04:00
Utils.DisplayWrapper.getScreen().focus_default_window(1);
this._box.get_children().forEach(c => c.reactive = false);
this.menu.reactive = false;
2019-05-21 14:31:55 -04:00
this.currentAppIcon = null;
2016-09-21 00:18:00 +00:00
},
2019-05-19 10:42:22 -04:00
update: function(appIcon, windows) {
2019-05-20 00:25:22 -04:00
if (this.currentAppIcon == appIcon) {
2019-05-19 10:42:22 -04:00
if (windows && !windows.length) {
this.close();
} else {
this._addAndRemoveWindows(windows);
this._updatePosition();
2019-05-14 17:08:08 -04:00
}
}
},
2016-09-21 00:18:00 +00:00
updatePosition: function() {
this._updatePosition();
},
2019-05-20 10:56:22 -04:00
focusNext: function() {
let previews = this._box.get_children();
let currentIndex = this._focusedPreview ? previews.indexOf(this._focusedPreview) : -1;
let nextIndex = currentIndex + 1;
nextIndex = previews[nextIndex] ? nextIndex : 0;
2019-05-21 14:31:55 -04:00
if (previews[nextIndex]) {
this._removeFocus();
previews[nextIndex].setFocus(true);
this._focusedPreview = previews[nextIndex];
}
2019-05-20 10:56:22 -04:00
return nextIndex;
},
activateFocused: function() {
if (this.opened && this._focusedPreview) {
this._focusedPreview.activate();
}
},
requestPeek: function(window) {
2019-05-21 14:31:55 -04:00
this._timeoutsHandler.remove(T3);
if (this._dtpSettings.get_boolean('peek-mode')) {
2019-05-25 11:08:46 -04:00
if (this.peekInitialWorkspaceIndex < 0) {
2019-05-21 14:31:55 -04:00
this._timeoutsHandler.add([T3, this._dtpSettings.get_int('enter-peek-mode-timeout'), () => this._peek(window)]);
} else {
this._peek(window);
}
}
},
endPeekHere: function() {
this._endPeek(true);
},
_setOpenedState: function(opened) {
this.opened = opened;
this.emit('open-state-changed');
},
2019-05-21 14:31:55 -04:00
_removeFocus: function() {
2019-05-20 10:56:22 -04:00
if (this._focusedPreview) {
this._focusedPreview.setFocus(false);
this._focusedPreview = null;
}
},
2019-05-19 10:42:22 -04:00
_mergeWindows: function(appIcon, windows) {
windows = windows || (appIcon.window ? [appIcon.window] : appIcon.getAppIconInterestingWindows());
windows.sort(Taskbar.sortWindowsCompareFunction);
let currentPreviews = this._box.get_children();
let l = Math.max(windows.length, currentPreviews.length);
for (let i = 0; i < l; ++i) {
if (currentPreviews[i] && windows[i] && windows[i] != currentPreviews[i].window) {
currentPreviews[i].assignWindow(windows[i], this.opened);
} else if (!currentPreviews[i]) {
this._addNewPreview(windows[i]);
} else if (!windows[i]) {
currentPreviews[i][!this.opened ? 'destroy' : 'animateOut']();
}
}
},
_addAndRemoveWindows: function(windows) {
let currentPreviews = this._box.get_children();
windows.sort(Taskbar.sortWindowsCompareFunction);
for (let i = 0, l = windows.length; i < l; ++i) {
let currentIndex = Utils.findIndex(currentPreviews, c => c.window == windows[i]);
if (currentIndex < 0) {
this._addNewPreview(windows[i]);
} else {
2019-05-25 11:08:46 -04:00
currentPreviews[currentIndex].cancelAnimateOut();
currentPreviews[currentIndex].assignWindow(windows[i]);
2019-05-19 10:42:22 -04:00
currentPreviews.splice(currentIndex, 1);
2019-05-25 11:08:46 -04:00
if (this._peekedWindow && this._peekedWindow == windows[i]) {
this.requestPeek(windows[i]);
}
2019-05-19 10:42:22 -04:00
}
}
currentPreviews.forEach(c => c.animateOut());
},
_addNewPreview: function(window) {
let preview = new Preview(this._panelWrapper, this);
this._box.add_child(preview);
2019-05-20 00:25:22 -04:00
preview.adjustOnStage();
2019-05-19 10:42:22 -04:00
preview.assignWindow(window, this.opened);
},
getCurrentAppIcon: function() {
2019-05-20 00:25:22 -04:00
return this.currentAppIcon;
2016-09-21 00:18:00 +00:00
},
2019-05-14 17:08:08 -04:00
_addCloseTimeout: function() {
this._timeoutsHandler.add([T2, this._dtpSettings.get_int('leave-timeout'), () => this.close()]);
},
_onHoverChanged: function() {
this._endOpenCloseTimeouts();
2019-05-20 00:25:22 -04:00
if (this.currentAppIcon && !this.menu.hover) {
2019-05-14 17:08:08 -04:00
this._addCloseTimeout();
}
2016-09-21 00:18:00 +00:00
},
2019-05-15 17:02:18 -04:00
_onScrollEvent: function(actor, event) {
if (!event.is_pointer_emulated()) {
let vOrh = this._checkIfLeftOrRight() ? 'v' : 'h';
let adjustment = this._scrollView['get_' + vOrh + 'scroll_bar']().get_adjustment();
2019-05-15 17:02:18 -04:00
let increment = adjustment.step_increment;
let delta = increment;
2019-05-15 17:02:18 -04:00
switch (event.get_scroll_direction()) {
case Clutter.ScrollDirection.UP:
2019-05-19 11:39:15 -04:00
delta = -increment;
2019-05-15 17:02:18 -04:00
break;
case Clutter.ScrollDirection.SMOOTH:
2019-05-19 11:39:15 -04:00
let [dx, dy] = event.get_scroll_delta();
delta = dy * increment;
delta += dx * increment;
2019-05-15 17:02:18 -04:00
break;
}
adjustment.set_value(adjustment.get_value() + delta);
}
return Clutter.EVENT_STOP;
},
_endOpenCloseTimeouts: function() {
this._timeoutsHandler.remove(T1);
this._timeoutsHandler.remove(T2);
2016-09-21 00:18:00 +00:00
},
2019-05-19 10:42:22 -04:00
_refreshGlobals: function() {
isLeftButtons = Meta.prefs_get_button_layout().left_buttons.indexOf(Meta.ButtonFunction.CLOSE) >= 0;
isTopHeader = this._dtpSettings.get_string('window-preview-title-position') == 'TOP';
scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
headerHeight = this._dtpSettings.get_boolean('window-preview-show-title') ? HEADER_HEIGHT * scaleFactor : 0;
animationTime = this._dtpSettings.get_int('window-preview-animation-time') * .001;
2019-05-19 10:42:22 -04:00
},
2019-05-14 17:08:08 -04:00
_resetHiddenState: function() {
this.menu.hide();
this._setOpenedState(false);
this.menu.opacity = 0;
this.menu[this._translationProp] = this._translationOffset;
2019-05-14 20:13:01 -04:00
this._box.get_children().forEach(c => c.destroy());
2019-05-14 17:08:08 -04:00
},
_updateClip: function() {
let x, y, w, h;
let panelSize = this._dtpSettings.get_int('panel-size') * scaleFactor;
let previewSize = (this._dtpSettings.get_int('window-preview-size') +
this._dtpSettings.get_int('window-preview-padding') * 2) * scaleFactor;
if (this._checkIfLeftOrRight()) {
w = previewSize;
h = this._panelWrapper.monitor.height;
y = this._panelWrapper.monitor.y;
} else {
w = this._panelWrapper.monitor.width;
h = (previewSize + headerHeight);
x = this._panelWrapper.monitor.x;
}
if (this._position == St.Side.LEFT) {
x = this._panelWrapper.monitor.x + panelSize;
} else if (this._position == St.Side.RIGHT) {
x = this._panelWrapper.monitor.x + this._panelWrapper.monitor.width - (panelSize + previewSize) ;
} else if (this._position == St.Side.TOP) {
y = this._panelWrapper.monitor.y + panelSize;
} else { //St.Side.BOTTOM
y = this._panelWrapper.monitor.y + this._panelWrapper.monitor.height - (panelSize + previewSize + headerHeight);
}
this.set_clip(0, 0, w, h);
this.set_position(x, y);
this.set_size(w, h);
},
_updatePosition: function() {
2019-05-20 00:25:22 -04:00
let sourceNode = this.currentAppIcon.actor.get_theme_node();
let sourceContentBox = sourceNode.get_content_box(this.currentAppIcon.actor.get_allocation_box());
let sourceAllocation = Shell.util_get_transformed_allocation(this.currentAppIcon.actor);
let [previewsWidth, previewsHeight] = this._getPreviewsSize();
let x = 0, y = 0;
2016-09-21 00:18:00 +00:00
previewsWidth = Math.min(previewsWidth, this._panelWrapper.monitor.width);
previewsHeight = Math.min(previewsHeight, this._panelWrapper.monitor.height);
2019-05-15 17:02:18 -04:00
if (this._checkIfLeftOrRight()) {
y = sourceAllocation.y1 - this._panelWrapper.monitor.y + (sourceContentBox.y2 - sourceContentBox.y1 - previewsHeight) * .5;
y = Math.max(y, 0);
y = Math.min(y, this._panelWrapper.monitor.height - previewsHeight);
} else {
x = sourceAllocation.x1 - this._panelWrapper.monitor.x + (sourceContentBox.x2 - sourceContentBox.x1 - previewsWidth) * .5;
x = Math.max(x, 0);
x = Math.min(x, this._panelWrapper.monitor.width - previewsWidth);
2016-09-21 00:18:00 +00:00
}
if (!this.opened) {
this.menu.set_position(x, y);
} else {
Tweener.addTween(this.menu, getTweenOpts({ x: x, y: y }));
}
2016-09-21 00:18:00 +00:00
},
_getPreviewsSize: function() {
2019-05-15 17:02:18 -04:00
let previewsWidth = 0;
let previewsHeight = 0;
this._box.get_children().forEach(c => {
if (!c.animatingOut) {
let [width, height] = c.getSize();
if (this._checkIfLeftOrRight()) {
2019-05-17 01:25:39 -04:00
previewsWidth = Math.max(width, previewsWidth);
2019-05-15 17:02:18 -04:00
previewsHeight += height;
} else {
previewsWidth += width;
2019-05-17 01:25:39 -04:00
previewsHeight = Math.max(height, previewsHeight);
2019-05-15 17:02:18 -04:00
}
}
});
return [previewsWidth, previewsHeight];
},
2019-05-14 17:08:08 -04:00
_animateOpenOrClose: function(show, onComplete) {
let isTranslationAnimation = this.menu[this._translationProp] != 0;
2019-05-11 21:40:48 -04:00
let tweenOpts = {
opacity: show ? 255 : 0,
2019-05-14 17:08:08 -04:00
transition: show ? 'easeInOutQuad' : 'easeInCubic',
onComplete: () => {
if (isTranslationAnimation) {
Main.layoutManager._queueUpdateRegions();
}
(onComplete || (() => {}))();
}
2019-05-11 21:40:48 -04:00
};
2019-05-14 17:08:08 -04:00
tweenOpts[this._translationProp] = show ? 0 : this._translationOffset;
Tweener.addTween(this.menu, getTweenOpts(tweenOpts));
2019-05-14 17:08:08 -04:00
},
_checkIfLeftOrRight: function() {
return this._position == St.Side.LEFT || this._position == St.Side.RIGHT;
2019-05-21 14:31:55 -04:00
},
_peek: function(window) {
2019-05-25 11:08:46 -04:00
let currentWorkspace = Utils.getCurrentWorkspace();
2019-05-21 14:31:55 -04:00
let windowWorkspace = window.get_workspace();
let focusWindow = () => this._focusMetaWindow(this._dtpSettings.get_int('peek-mode-opacity'), window);
this._restorePeekedWindowStack();
2019-05-25 11:08:46 -04:00
this._peekedWindow = window;
2019-05-22 21:37:53 -04:00
if (currentWorkspace != windowWorkspace) {
2019-05-25 11:08:46 -04:00
this._switchToWorkspaceImmediate(windowWorkspace.index());
this._timeoutsHandler.add([T3, 100, focusWindow]);
2019-05-22 21:37:53 -04:00
} else {
focusWindow();
2019-05-21 14:31:55 -04:00
}
2019-05-25 11:08:46 -04:00
if (this.peekInitialWorkspaceIndex < 0) {
this.peekInitialWorkspaceIndex = currentWorkspace.index();
2019-05-21 14:31:55 -04:00
}
},
_endPeek: function(stayHere) {
this._timeoutsHandler.remove(T3);
if (this._peekedWindow) {
this._restorePeekedWindowStack();
2019-05-21 14:31:55 -04:00
this._focusMetaWindow(255);
this._peekedWindow = null;
2019-05-25 23:38:09 -04:00
if (!stayHere) {
this._switchToWorkspaceImmediate(this.peekInitialWorkspaceIndex);
}
2019-05-26 00:01:22 -04:00
this.peekInitialWorkspaceIndex = -1;
2019-05-21 14:31:55 -04:00
}
},
2019-05-21 14:31:55 -04:00
2019-05-25 11:08:46 -04:00
_switchToWorkspaceImmediate: function(workspaceIndex) {
let workspace = Utils.getWorkspaceByIndex(workspaceIndex);
if (!workspace || (!workspace.list_windows().length &&
workspaceIndex < Utils.getWorkspaceCount() -1)) {
workspace = Utils.getCurrentWorkspace();
}
2019-05-25 11:08:46 -04:00
Main.wm._blockAnimations = true;
workspace.activate(1);
Main.wm._blockAnimations = false;
this.grab_key_focus();
2019-05-21 14:31:55 -04:00
},
2019-05-25 11:08:46 -04:00
_focusMetaWindow: function(dimOpacity, window) {
2019-05-22 21:37:53 -04:00
if (Main.overview.visibleTarget) {
return;
}
global.get_window_actors().forEach(wa => {
let mw = wa.meta_window;
2019-05-25 11:08:46 -04:00
let isFocused = mw == window;
2019-05-22 21:37:53 -04:00
if (mw) {
if (isFocused) {
mw[PEEK_INDEX_PROP] = wa.get_parent().get_children().indexOf(wa);
wa.get_parent().set_child_above_sibling(wa, null);
}
2019-05-22 21:37:53 -04:00
if (isFocused && mw.minimized) {
wa.show();
2019-05-22 21:37:53 -04:00
}
Tweener.addTween(wa, getTweenOpts({ opacity: isFocused ? 255 : dimOpacity }));
2019-05-22 21:37:53 -04:00
}
});
},
_restorePeekedWindowStack: function() {
let windowActor = this._peekedWindow ? this._peekedWindow.get_compositor_private() : null;
2019-05-22 21:37:53 -04:00
if (windowActor) {
if (this._peekedWindow.hasOwnProperty(PEEK_INDEX_PROP)) {
windowActor.get_parent().set_child_at_index(windowActor, this._peekedWindow[PEEK_INDEX_PROP]);
delete this._peekedWindow[PEEK_INDEX_PROP];
2019-05-22 21:37:53 -04:00
}
if(this._peekedWindow.minimized) {
windowActor.hide();
2019-05-21 14:31:55 -04:00
}
}
2019-05-21 14:31:55 -04:00
},
2019-05-14 17:08:08 -04:00
});
var Preview = Utils.defineClass({
Name: 'DashToPanel-Preview',
2019-05-14 17:08:08 -04:00
Extends: St.Widget,
2019-05-17 01:25:39 -04:00
_init: function(panelWrapper, previewMenu) {
this.callParent('_init', {
style_class: 'preview-container',
2019-05-17 01:25:39 -04:00
reactive: true,
2019-05-19 10:42:22 -04:00
track_hover: true,
layout_manager: new Clutter.BinLayout()
2019-05-17 01:25:39 -04:00
});
2019-05-14 17:08:08 -04:00
2019-05-25 11:08:46 -04:00
this.window = null;
this.cloneWidth = this.cloneHeight = 0;
2019-05-14 20:13:01 -04:00
this._panelWrapper = panelWrapper;
this._previewMenu = previewMenu;
this._padding = previewMenu._dtpSettings.get_int('window-preview-padding') * scaleFactor;
this._previewDimensions = this._getPreviewDimensions();
2019-05-14 20:13:01 -04:00
this.animatingOut = false;
2019-05-20 00:25:22 -04:00
let [previewBinWidth, previewBinHeight] = this._getBinSize();
2019-05-19 10:42:22 -04:00
let closeButton = new St.Button({ style_class: 'window-close', accessible_name: 'Close window' });
2019-05-20 00:25:22 -04:00
this._previewBin = new St.Widget({
layout_manager: new Clutter.BinLayout(),
y_align: Clutter.ActorAlign[!isTopHeader ? 'START' : 'END'],
y_expand: true,
style: 'padding: ' + this._padding + 'px;'
});
this._previewBin.set_size(previewBinWidth, previewBinHeight);
2019-05-20 00:25:22 -04:00
if (Config.PACKAGE_VERSION >= '3.31.9') {
closeButton.add_actor(new St.Icon({ icon_name: 'window-close-symbolic' }));
}
2019-05-19 10:42:22 -04:00
this._closeButtonBin = new St.Widget({
layout_manager: new Clutter.BinLayout(),
opacity: 0,
x_expand: true, y_expand: true,
x_align: Clutter.ActorAlign[isLeftButtons ? 'START' : 'END'],
y_align: Clutter.ActorAlign[isTopHeader ? 'START' : 'END']
});
2019-05-19 10:42:22 -04:00
this._closeButtonBin.add_child(closeButton);
2019-05-20 00:25:22 -04:00
if (headerHeight) {
let headerBox = new St.Widget({
layout_manager: new Clutter.BoxLayout(),
y_align: Clutter.ActorAlign[isTopHeader ? 'START' : 'END'],
2019-05-20 00:25:22 -04:00
y_expand: true,
2019-05-20 10:56:22 -04:00
style: this._getBackgroundColor(HEADER_COLOR_OFFSET, .8)
2019-05-20 00:25:22 -04:00
});
this._workspaceIndicator = new St.Label({ y_align: Clutter.ActorAlign.CENTER });
2019-05-20 00:25:22 -04:00
this._windowTitle = new St.Label({ y_align: Clutter.ActorAlign.CENTER, x_expand: true });
2019-05-14 17:08:08 -04:00
2019-05-20 00:25:22 -04:00
this._iconBin = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this._iconBin.set_size(headerHeight, headerHeight);
2019-05-19 10:42:22 -04:00
2019-05-20 00:25:22 -04:00
headerBox.add_child(this._iconBin);
headerBox.insert_child_at_index(this._workspaceIndicator, isLeftButtons ? 0 : 1);
headerBox.insert_child_at_index(this._windowTitle, isLeftButtons ? 1 : 2);
2019-05-14 17:08:08 -04:00
2019-05-20 00:25:22 -04:00
this.add_child(headerBox);
}
2019-05-19 10:42:22 -04:00
closeButton.connect('clicked', () => this._onCloseBtnClick());
this.connect('notify::hover', () => this._onHoverChanged());
2019-05-19 11:39:15 -04:00
this.connect('button-release-event', (actor, e) => this._onButtonReleaseEvent(e));
2019-05-20 00:25:22 -04:00
this.connect('destroy', () => this._onDestroy());
this.add_child(this._previewBin);
2019-05-19 10:42:22 -04:00
this.add_child(this._closeButtonBin);
2019-05-20 00:25:22 -04:00
this.set_size(-1, previewBinHeight + headerHeight);
},
adjustOnStage: function() {
let closeButtonPadding = headerHeight ? Math.round((headerHeight - this._closeButtonBin.height) * .5 / scaleFactor) : 4;
2019-05-20 10:56:22 -04:00
let closeButtonBorderRadius = '';
if (!headerHeight) {
closeButtonBorderRadius = 'border-radius: ';
if (isTopHeader) {
closeButtonBorderRadius += (isLeftButtons ? '0 0 4px 0;' : '0 0 0 4px;');
} else {
closeButtonBorderRadius += (isLeftButtons ? '0 4px 0 0;' : '4px 0 0 0;');
}
2019-05-20 10:56:22 -04:00
}
2019-05-20 00:25:22 -04:00
this._closeButtonBin.set_style(
'padding: ' + closeButtonPadding + 'px; ' +
this._getBackgroundColor(Math.abs(HEADER_COLOR_OFFSET), .8) +
2019-05-20 10:56:22 -04:00
closeButtonBorderRadius
2019-05-20 00:25:22 -04:00
);
2019-05-15 17:02:18 -04:00
},
2019-05-14 20:13:01 -04:00
assignWindow: function(window, animateSize) {
2019-05-25 11:08:46 -04:00
if (this.window != window) {
let _assignWindowClone = () => {
if (window.get_compositor_private()) {
let clone = this._getWindowClone(window);
this._resizeClone(clone);
this._addClone(clone, animateSize);
this._previewMenu.updatePosition();
} else {
Mainloop.idle_add(() => _assignWindowClone());
}
};
_assignWindowClone();
}
2019-05-14 20:13:01 -04:00
2019-05-20 00:25:22 -04:00
this._removeWindowSignals();
2019-05-19 10:42:22 -04:00
this.window = window;
2019-05-25 11:08:46 -04:00
this._updateHeader();
2019-05-14 20:13:01 -04:00
},
animateOut: function() {
2019-05-25 11:08:46 -04:00
if (!this.animatingOut) {
let tweenOpts = getTweenOpts({ opacity: 0, onComplete: () => this.destroy() });
2019-05-14 20:13:01 -04:00
2019-05-25 11:08:46 -04:00
tweenOpts[this._previewMenu._checkIfLeftOrRight() ? 'height' : 'width'] = 0;
this.animatingOut = true;
2019-05-14 20:13:01 -04:00
2019-05-25 11:08:46 -04:00
Tweener.removeTweens(this);
Tweener.addTween(this, tweenOpts);
}
},
cancelAnimateOut: function() {
if (this.animatingOut) {
this.animatingOut = false;
Tweener.removeTweens(this);
Tweener.addTween(this, getTweenOpts({ opacity: 255 }));
}
2019-05-14 20:13:01 -04:00
},
2019-05-15 17:02:18 -04:00
getSize: function() {
let [binWidth, binHeight] = this._getBinSize();
2019-05-17 01:25:39 -04:00
binWidth = Math.max(binWidth, this.cloneWidth + this._padding * 2);
binHeight = Math.max(binHeight, this.cloneHeight + this._padding * 2);
2019-05-15 17:02:18 -04:00
return [binWidth, binHeight];
},
2019-05-20 10:56:22 -04:00
setFocus: function(focused) {
this._hideOrShowCloseButton(!focused);
this.set_style(this._getBackgroundColor(FOCUSED_COLOR_OFFSET, focused ? '-' : 0));
2019-05-21 14:31:55 -04:00
if (focused) {
this._previewMenu.requestPeek(this.window);
}
2019-05-20 10:56:22 -04:00
},
activate: function() {
2019-05-21 14:31:55 -04:00
this._previewMenu.endPeekHere();
2019-05-20 10:56:22 -04:00
this._previewMenu.close();
2019-05-22 21:37:53 -04:00
Main.activateWindow(this.window);
2019-05-20 10:56:22 -04:00
},
2019-05-20 00:25:22 -04:00
_onDestroy: function() {
this._removeWindowSignals();
},
2019-05-19 10:42:22 -04:00
_onHoverChanged: function() {
2019-05-20 10:56:22 -04:00
this.setFocus(this.hover);
2019-05-19 10:42:22 -04:00
},
_onCloseBtnClick: function() {
this.window.delete(global.get_current_time());
2019-05-19 17:51:07 -04:00
this._hideOrShowCloseButton(true);
2019-05-22 21:37:53 -04:00
this.reactive = false;
2019-05-20 00:25:22 -04:00
if (!this._previewMenu._dtpSettings.get_boolean('group-apps')) {
this._previewMenu.close();
}
2019-05-19 10:42:22 -04:00
},
2019-05-19 11:39:15 -04:00
_onButtonReleaseEvent: function(e) {
switch (e.get_button()) {
case 1: // Left click
2019-05-20 10:56:22 -04:00
this.activate();
2019-05-19 11:39:15 -04:00
break;
case 2: // Middle click
if (this._previewMenu._dtpSettings.get_boolean('preview-middle-click-close')) {
this._onCloseBtnClick();
}
break;
2019-05-25 11:08:46 -04:00
case 3: // Right click
this._showContextMenu(e);
break;
2019-05-19 11:39:15 -04:00
}
return Clutter.EVENT_STOP;
},
2019-05-25 11:08:46 -04:00
_showContextMenu: function(e) {
let coords = e.get_coords();
let currentWorkspace = this._previewMenu.peekInitialWorkspaceIndex < 0 ?
Utils.getCurrentWorkspace() :
Utils.getWorkspaceByIndex(this._previewMenu.peekInitialWorkspaceIndex);
Main.wm._showWindowMenu(null, this.window, Meta.WindowMenuType.WM, {
x: coords[0],
y: coords[1],
width: 0,
height: 0
});
let ctxMenuData = Main.wm._windowMenuManager._manager._menus[0];
ctxMenuData.menu.connect('open-state-changed', () => this._previewMenu.menu.sync_hover());
if (this.window.get_workspace() != currentWorkspace) {
let menuItem = new PopupMenu.PopupMenuItem(_('Move to current Workspace') + ' [' + (currentWorkspace.index() + 1) + ']');
let menuItems = ctxMenuData.menu.box.get_children();
let insertIndex = Utils.findIndex(menuItems, c => c._delegate instanceof PopupMenu.PopupSeparatorMenuItem);
insertIndex = insertIndex >= 0 ? insertIndex : menuItems.length - 1;
ctxMenuData.menu.addMenuItem(menuItem, insertIndex);
menuItem.connect('activate', () => this.window.change_workspace(currentWorkspace));
}
},
2019-05-20 00:25:22 -04:00
_removeWindowSignals: function() {
if (this._titleWindowChangeId) {
this.window.disconnect(this._titleWindowChangeId);
this._titleWindowChangeId = 0;
}
},
_updateHeader: function() {
if (headerHeight) {
let iconTextureSize = headerHeight / scaleFactor * .6;
let icon = this._previewMenu.getCurrentAppIcon().app.create_icon_texture(iconTextureSize);
2019-05-23 20:08:49 -04:00
let workspaceIndex = '';
let workspaceStyle = null;
let commonTitleStyles = 'color: ' + this._previewMenu._dtpSettings.get_string('window-preview-title-font-color') + ';' +
'font-size: ' + this._previewMenu._dtpSettings.get_int('window-preview-title-font-size') + 'px;' +
'font-weight: ' + this._previewMenu._dtpSettings.get_string('window-preview-title-font-weight') + ';';
2019-05-20 00:25:22 -04:00
this._iconBin.destroy_all_children();
this._iconBin.add_child(icon);
2019-05-23 20:08:49 -04:00
if (!this._previewMenu._dtpSettings.get_boolean('isolate-workspaces')) {
workspaceIndex = (this.window.get_workspace().index() + 1).toString();
workspaceStyle = 'margin: 0 4px 0 ' + (isLeftButtons ? Math.round((headerHeight - icon.width) * .5) + 'px' : '0') + '; padding: 0 4px;' +
'border: 2px solid ' + this._getRgbaColor(FOCUSED_COLOR_OFFSET, .8) + 'border-radius: 2px;' + commonTitleStyles;
2019-05-20 00:25:22 -04:00
}
2019-05-23 20:08:49 -04:00
this._workspaceIndicator.text = workspaceIndex;
this._workspaceIndicator.set_style(workspaceStyle);
2019-05-20 00:25:22 -04:00
this._titleWindowChangeId = this.window.connect('notify::title', () => this._updateWindowTitle());
2019-05-23 20:08:49 -04:00
this._windowTitle.set_style('max-width: 0px; padding-right: 4px;' + commonTitleStyles);
2019-05-20 00:25:22 -04:00
this._updateWindowTitle();
}
},
_updateWindowTitle: function() {
this._windowTitle.text = this.window.title;
2019-05-20 00:25:22 -04:00
},
2019-05-19 17:51:07 -04:00
_hideOrShowCloseButton: function(hide) {
Tweener.addTween(this._closeButtonBin, getTweenOpts({ opacity: hide ? 0 : 255 }));
},
2019-05-20 00:25:22 -04:00
_getBackgroundColor: function(offset, alpha) {
return 'background-color: ' + this._getRgbaColor(offset, alpha) +
'transition-duration:' + this._panelWrapper.dynamicTransparency.animationDuration;
},
_getRgbaColor: function(offset, alpha) {
2019-05-20 00:25:22 -04:00
alpha = Math.abs(alpha);
if (isNaN(alpha)) {
alpha = this._panelWrapper.dynamicTransparency.alpha;
}
return Utils.getrgbaColor(this._panelWrapper.dynamicTransparency.backgroundColorRgb, alpha, offset);
2019-05-19 17:51:07 -04:00
},
_addClone: function(newClone, animateSize) {
2019-05-17 01:25:39 -04:00
let currentClones = this._previewBin.get_children();
let newCloneOpts = getTweenOpts({ opacity: 255 });
if (currentClones.length) {
let currentClone = currentClones.pop();
let currentCloneOpts = getTweenOpts({ opacity: 0, onComplete: () => currentClone.destroy() });
if (newClone.width > currentClone.width) {
newCloneOpts.width = newClone.width;
newClone.width = currentClone.width;
} else {
currentCloneOpts.width = newClone.width;
}
if (newClone.height > currentClone.height) {
newCloneOpts.height = newClone.height;
newClone.height = currentClone.height;
} else {
currentCloneOpts.height = newClone.height;
}
currentClones.forEach(c => c.destroy());
Tweener.addTween(currentClone, currentCloneOpts);
} else if (animateSize) {
if (this._previewMenu._checkIfLeftOrRight()) {
2019-05-17 01:25:39 -04:00
newClone.height = 0;
newCloneOpts.height = this.cloneHeight;
} else {
newClone.width = 0;
newCloneOpts.width = this.cloneWidth;
}
}
this._previewBin.add_child(newClone);
Tweener.addTween(newClone, newCloneOpts);
2019-05-15 17:02:18 -04:00
},
2019-05-14 20:13:01 -04:00
2019-05-15 17:02:18 -04:00
_getWindowClone: function(window) {
return new Clutter.Clone({
source: window.get_compositor_private(),
2019-05-17 01:25:39 -04:00
opacity: 0,
2019-05-15 17:02:18 -04:00
y_align: Clutter.ActorAlign.CENTER,
x_align: Clutter.ActorAlign.CENTER
});
},
_getBinSize: function() {
let [width, height] = this._previewDimensions;
2019-05-15 17:02:18 -04:00
width += this._padding * 2;
height += this._padding * 2;
if (this._previewMenu._checkIfLeftOrRight()) {
height = -1;
} else {
width = -1;
}
2019-05-15 17:02:18 -04:00
return [width, height];
2019-05-14 17:08:08 -04:00
},
2019-05-15 17:02:18 -04:00
_resizeClone: function(clone) {
2019-05-14 17:08:08 -04:00
let [width, height] = clone.get_source().get_size();
let [maxWidth, maxHeight] = this._previewDimensions;
2019-05-15 17:02:18 -04:00
let ratio = Math.min(maxWidth / width, maxHeight / height);
ratio = ratio < 1 ? ratio : 1;
2019-05-14 17:08:08 -04:00
2019-05-17 01:25:39 -04:00
this.cloneWidth = Math.floor(width * ratio);
this.cloneHeight = Math.floor(height * ratio);
clone.set_size(this.cloneWidth, this.cloneHeight);
},
2019-05-15 17:02:18 -04:00
_getPreviewDimensions: function() {
let size = this._previewMenu._dtpSettings.get_int('window-preview-size') * scaleFactor;
let w, h;
if (this._previewMenu._checkIfLeftOrRight()) {
w = Math.max(DEFAULT_RATIO.w, size);
h = w * DEFAULT_RATIO.h / DEFAULT_RATIO.w;
} else {
h = Math.max(DEFAULT_RATIO.h, size);
w = h * DEFAULT_RATIO.w / DEFAULT_RATIO.h;
}
return [w, h];
2019-05-15 17:02:18 -04:00
}
2019-05-14 20:13:01 -04:00
});
function getTweenOpts(opts) {
let defaults = {
time: animationTime,
2019-05-14 20:13:01 -04:00
transition: 'easeInOutQuad'
};
return Utils.mergeObjects(opts || {}, defaults);
}