Files
dash-to-panel/windowPreview.js

1153 lines
40 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
*/
import GObject from 'gi://GObject';
import Clutter from 'gi://Clutter';
import GLib from 'gi://GLib';
import Graphene from 'gi://Graphene';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import Meta from 'gi://Meta';
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
import St from 'gi://St';
2016-09-21 00:18:00 +00:00
import * as Taskbar from './taskbar.js';
import * as Utils from './utils.js';
2023-08-13 22:58:15 +02:00
import {SETTINGS, DESKTOPSETTINGS} from './extension.js';
2023-09-21 19:22:29 -07:00
import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
2016-09-21 00:18:00 +00:00
2019-06-07 20:02:51 -04:00
//timeout intervals
const ENSURE_VISIBLE_MS = 200;
2019-05-11 21:40:48 -04:00
//timeout names
const T1 = 'openMenuTimeout';
const T2 = 'closeMenuTimeout';
const T3 = 'peekTimeout';
2019-06-07 20:02:51 -04:00
const T4 = 'ensureVisibleTimeout';
const MAX_TRANSLATION = 40;
2019-05-20 00:25:22 -04:00
const HEADER_HEIGHT = 38;
const MAX_CLOSE_BUTTON_SIZE = 30;
2019-05-27 23:57:25 -04:00
const MIN_DIMENSION = 100;
2019-05-20 00:25:22 -04:00
const FOCUSED_COLOR_OFFSET = 24;
const HEADER_COLOR_OFFSET = -12;
const FADE_SIZE = 36;
2019-05-22 21:37:53 -04:00
const PEEK_INDEX_PROP = '_dtpPeekInitialIndex';
2019-05-29 21:24:25 -04:00
let headerHeight = 0;
let alphaBg = 0;
2019-05-29 21:24:25 -04:00
let isLeftButtons = false;
let isTopHeader = true;
let isManualStyling = false;
2019-05-29 21:24:25 -04:00
let scaleFactor = 1;
let animationTime = 0;
let aspectRatio = {};
2019-05-19 10:42:22 -04:00
export const PreviewMenu = GObject.registerClass({
2022-04-01 22:32:15 -04:00
Signals: { 'open-state-changed': {} }
}, class PreviewMenu extends St.Widget {
2016-09-21 00:18:00 +00:00
2022-04-01 22:32:15 -04:00
_init(panel) {
super._init({ layout_manager: new Clutter.BinLayout() });
2019-09-06 17:47:34 -04:00
let geom = panel.geom;
this.panel = panel;
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;
2022-04-08 18:50:50 -04:00
this.allowCloseWindow = true;
2019-05-25 11:08:46 -04:00
this.peekInitialWorkspaceIndex = -1;
this.opened = false;
2019-09-04 21:55:26 -04:00
this.isVertical = geom.position == St.Side.LEFT || geom.position == St.Side.RIGHT;
this._translationProp = 'translation_' + (this.isVertical ? 'x' : 'y');
this._translationDirection = (geom.position == St.Side.TOP || geom.position == St.Side.LEFT ? -1 : 1);
2020-06-13 07:40:33 -04:00
this._translationOffset = Math.min(panel.dtpSize, MAX_TRANSLATION) * this._translationDirection;
2019-05-14 17:08:08 -04:00
2019-05-26 13:43:42 -04:00
this.menu = new St.Widget({
name: 'preview-menu',
layout_manager: new Clutter.BinLayout(),
reactive: true,
track_hover: true,
2019-09-03 21:46:10 -04:00
x_expand: true,
2019-05-26 13:43:42 -04:00
y_expand: true,
2019-09-04 21:55:26 -04:00
x_align: Clutter.ActorAlign[geom.position != St.Side.RIGHT ? 'START' : 'END'],
y_align: Clutter.ActorAlign[geom.position != St.Side.BOTTOM ? 'START' : 'END']
2019-05-26 13:43:42 -04:00
});
2019-09-04 21:55:26 -04:00
this._box = new St.BoxLayout({ vertical: this.isVertical });
2019-05-14 17:08:08 -04:00
this._scrollView = new St.ScrollView({
name: 'dashtopanelPreviewScrollview',
hscrollbar_policy: St.PolicyType.NEVER,
vscrollbar_policy: St.PolicyType.NEVER,
enable_mouse_scrolling: true,
2019-09-04 21:55:26 -04:00
y_expand: !this.isVertical
2019-05-14 17:08:08 -04:00
});
2024-02-03 20:19:22 +01:00
this._scrollView.add_child(this._box);
this.menu.add_child(this._scrollView);
this.add_child(this.menu);
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
enable() {
2019-05-11 21:40:48 -04:00
this._timeoutsHandler = new Utils.TimeoutsHandler();
2019-05-14 17:08:08 -04:00
this._signalsHandler = new Utils.GlobalSignalsHandler();
Main.layoutManager.addChrome(this, { affectsInputRegion: false });
Main.layoutManager.trackChrome(this.menu, { affectsInputRegion: true });
2019-09-26 09:39:10 -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)
],
[
2019-09-06 17:47:34 -04:00
this.panel.panelBox,
'style-changed',
() => this._updateClip()
],
2019-09-26 12:44:56 -04:00
[
Utils.DisplayWrapper.getScreen(),
2019-09-26 12:44:56 -04:00
'in-fullscreen-changed',
() => {
if (global.display.focus_window && global.display.focus_window.is_fullscreen()) {
this.close(true);
}
}
],
[
2023-08-13 22:58:15 +02:00
SETTINGS,
[
Configure panel thickness, length, and anchor per-monitor Settings.ui: - Move thickness, length, and anchor from Style tab to Position tab. Group together with position in the same frame. schema: - Remove unpublished panel-length and panel-anchor settings, replacing them with panel-lengths and panel-anchors JSON objects (like panel-positions). - Remove unpublished anchor enum, since panel-anchors is being managed by the extension in JSON, not typed by the schema. - Deprecate panel-size in favour of new panel-sizes JSON, storing per-monitor panel sizes. - Mention that panel-position is deprecated. Introduce panelSettings.js: - Functions to fetch or set panel settings that are stored as JSON. Grown from now-removed getSettingsPositions() in panelPositions.js. prefs.js: - Group together the different UI widget label and value refreshing into method _updateWidgetSettingsForMonitor(). - Change multi-panel behaviour of _setAnchorLabels(). Previously, all panels shared the same anchor setting, and so setAnchorLabels considered all monitors. Now, panels are configured either independently, or sometimes all together; set labels according to orientation(s) of panel(s) being configured. - Omitting preventTop handling in refactored _setPanelPosition() method. Previously, it was written to set the first monitor's panel to Pos.BOTTOM if the user clicked the Top position radio button, if stockgs-keep-top-panel was also set. But the user can't activate the Top button anyway if stockgs is set (likely implemented later). panelManager.js: - Removing panelPositions, as it is not needed any more.
2020-10-27 16:04:19 -06:00
'changed::panel-sizes',
'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
);
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
disable() {
2019-05-11 21:40:48 -04:00
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
2019-08-08 17:54:38 -04:00
Main.layoutManager.untrackChrome(this.menu);
Main.layoutManager.removeChrome(this);
2022-04-01 22:32:15 -04:00
}
2016-09-21 00:18:00 +00:00
2022-04-01 22:32:15 -04:00
requestOpen(appIcon) {
2023-08-13 22:58:15 +02:00
let timeout = SETTINGS.get_int('show-window-previews-timeout');
if (this.opened) {
timeout = Math.min(100, timeout);
}
this._endOpenCloseTimeouts();
this._timeoutsHandler.add([T1, timeout, () => this.open(appIcon)]);
2022-04-01 22:32:15 -04:00
}
2016-09-21 00:18:00 +00:00
2022-04-01 22:32:15 -04:00
requestClose() {
this._endOpenCloseTimeouts();
2019-05-14 17:08:08 -04:00
this._addCloseTimeout();
2022-04-01 22:32:15 -04:00
}
2022-04-08 18:50:50 -04:00
open(appIcon, preventCloseWindow) {
2019-05-20 00:25:22 -04:00
if (this.currentAppIcon != appIcon) {
this.currentAppIcon = appIcon;
2022-04-08 18:50:50 -04:00
this.allowCloseWindow = !preventCloseWindow;
if (!this.opened) {
2019-05-19 10:42:22 -04:00
this._refreshGlobals();
2020-06-13 07:40:33 -04:00
this.set_height(this.clipHeight);
2025-01-19 11:42:34 -05:00
this.show();
setStyle(this.menu, 'background: ' + Utils.getrgbaColor(this.panel.dynamicTransparency.backgroundColorRgb, alphaBg));
}
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._setReactive(true);
this._setOpenedState(true);
2019-05-15 17:02:18 -04:00
}
2022-04-01 22:32:15 -04:00
}
2016-09-21 00:18:00 +00:00
2022-04-01 22:32:15 -04:00
close(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) {
Utils.stopAnimations(this.menu);
2019-05-14 17:08:08 -04:00
this._resetHiddenState();
} else {
this._animateOpenOrClose(false, () => this._resetHiddenState());
}
2019-05-21 14:31:55 -04:00
this._setReactive(false);
2019-05-21 14:31:55 -04:00
this.currentAppIcon = null;
2022-04-01 22:32:15 -04:00
}
2016-09-21 00:18:00 +00:00
2022-04-01 22:32:15 -04:00
update(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
}
}
2022-04-01 22:32:15 -04:00
}
2016-09-21 00:18:00 +00:00
2022-04-01 22:32:15 -04:00
updatePosition() {
this._updatePosition();
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
focusNext() {
2019-05-20 10:56:22 -04:00
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;
2022-04-01 22:32:15 -04:00
}
2019-05-20 10:56:22 -04:00
2022-04-01 22:32:15 -04:00
activateFocused() {
2019-05-20 10:56:22 -04:00
if (this.opened && this._focusedPreview) {
this._focusedPreview.activate();
}
2022-04-01 22:32:15 -04:00
}
2019-05-20 10:56:22 -04:00
2022-04-01 22:32:15 -04:00
requestPeek(window) {
2019-05-21 14:31:55 -04:00
this._timeoutsHandler.remove(T3);
2023-08-13 22:58:15 +02:00
if (SETTINGS.get_boolean('peek-mode')) {
2019-05-25 11:08:46 -04:00
if (this.peekInitialWorkspaceIndex < 0) {
2023-08-13 22:58:15 +02:00
this._timeoutsHandler.add([T3, SETTINGS.get_int('enter-peek-mode-timeout'), () => this._peek(window)]);
2019-05-21 14:31:55 -04:00
} else {
this._peek(window);
}
}
2022-04-01 22:32:15 -04:00
}
2019-05-21 14:31:55 -04:00
2022-04-01 22:32:15 -04:00
endPeekHere() {
2019-05-21 14:31:55 -04:00
this._endPeek(true);
2022-04-01 22:32:15 -04:00
}
2019-05-21 14:31:55 -04:00
2022-04-01 22:32:15 -04:00
ensureVisible(preview) {
let [ , upper, pageSize] = this._getScrollAdjustmentValues();
2019-06-07 20:02:51 -04:00
if (upper > pageSize) {
this._timeoutsHandler.add([
T4,
ENSURE_VISIBLE_MS,
() => Utils.ensureActorVisibleInScrollView(this._scrollView, preview, MIN_DIMENSION, () => this._updateScrollFade())
2019-06-07 20:02:51 -04:00
]);
}
2022-04-01 22:32:15 -04:00
}
2019-06-07 20:02:51 -04:00
2022-04-01 22:32:15 -04:00
getCurrentAppIcon() {
2019-09-26 12:44:56 -04:00
return this.currentAppIcon;
2022-04-01 22:32:15 -04:00
}
2019-09-26 12:44:56 -04:00
2022-04-01 22:32:15 -04:00
_setReactive(reactive) {
this._box.get_children().forEach(c => c.reactive = reactive);
this.menu.reactive = reactive;
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
_setOpenedState(opened) {
this.opened = opened;
this.emit('open-state-changed');
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
_resetHiddenState() {
2025-01-19 11:42:34 -05:00
this.hide();
this.set_height(0);
2019-09-26 12:44:56 -04:00
this._setOpenedState(false);
this.menu.opacity = 0;
this.menu[this._translationProp] = this._translationOffset;
this._box.get_children().forEach(c => c.destroy());
2022-04-01 22:32:15 -04:00
}
2019-09-26 12:44:56 -04:00
2022-04-01 22:32:15 -04:00
_removeFocus() {
2019-05-20 10:56:22 -04:00
if (this._focusedPreview) {
this._focusedPreview.setFocus(false);
this._focusedPreview = null;
}
2022-04-01 22:32:15 -04:00
}
2019-05-20 10:56:22 -04:00
2022-04-01 22:32:15 -04:00
_mergeWindows(appIcon, windows) {
2019-05-19 10:42:22 -04:00
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]) {
2019-05-19 10:42:22 -04:00
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']();
}
}
2022-04-01 22:32:15 -04:00
}
2019-05-19 10:42:22 -04:00
2022-04-01 22:32:15 -04:00
_addAndRemoveWindows(windows) {
2019-05-19 10:42:22 -04:00
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].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());
2022-04-01 22:32:15 -04:00
}
2019-05-19 10:42:22 -04:00
2022-04-01 22:32:15 -04:00
_addNewPreview(window) {
let preview = new Preview(this);
2019-05-19 10:42:22 -04:00
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);
2022-04-01 22:32:15 -04:00
}
2019-05-19 10:42:22 -04:00
2022-04-01 22:32:15 -04:00
_addCloseTimeout() {
2023-08-13 22:58:15 +02:00
this._timeoutsHandler.add([T2, SETTINGS.get_int('leave-timeout'), () => this.close()]);
2022-04-01 22:32:15 -04:00
}
2019-05-14 17:08:08 -04:00
2022-04-01 22:32:15 -04:00
_onHoverChanged() {
2019-05-14 17:08:08 -04:00
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();
this._endPeek();
2019-05-14 17:08:08 -04:00
}
2022-04-01 22:32:15 -04:00
}
2016-09-21 00:18:00 +00:00
2022-04-01 22:32:15 -04:00
_onScrollEvent(actor, event) {
2019-05-15 17:02:18 -04:00
if (!event.is_pointer_emulated()) {
2019-09-04 21:55:26 -04:00
let vOrh = this.isVertical ? '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);
this._updateScrollFade();
2019-05-15 17:02:18 -04:00
}
return Clutter.EVENT_STOP;
2022-04-01 22:32:15 -04:00
}
2019-05-15 17:02:18 -04:00
2022-04-01 22:32:15 -04:00
_endOpenCloseTimeouts() {
this._timeoutsHandler.remove(T1);
this._timeoutsHandler.remove(T2);
2019-06-07 20:02:51 -04:00
this._timeoutsHandler.remove(T4);
2022-04-01 22:32:15 -04:00
}
2016-09-21 00:18:00 +00:00
2022-04-01 22:32:15 -04:00
_refreshGlobals() {
2019-05-19 10:42:22 -04:00
isLeftButtons = Meta.prefs_get_button_layout().left_buttons.indexOf(Meta.ButtonFunction.CLOSE) >= 0;
2023-08-13 22:58:15 +02:00
isTopHeader = SETTINGS.get_string('window-preview-title-position') == 'TOP';
isManualStyling = SETTINGS.get_boolean('window-preview-manual-styling');
2020-05-03 21:27:40 -04:00
scaleFactor = Utils.getScaleFactor();
2023-08-13 22:58:15 +02:00
headerHeight = SETTINGS.get_boolean('window-preview-show-title') ? HEADER_HEIGHT * scaleFactor : 0;
animationTime = SETTINGS.get_int('window-preview-animation-time') * .001;
2019-05-29 21:24:25 -04:00
aspectRatio.x = {
2023-08-13 22:58:15 +02:00
size: SETTINGS.get_int('window-preview-aspect-ratio-x'),
fixed: SETTINGS.get_boolean('window-preview-fixed-x')
2019-05-29 21:24:25 -04:00
};
aspectRatio.y = {
2023-08-13 22:58:15 +02:00
size: SETTINGS.get_int('window-preview-aspect-ratio-y'),
fixed: SETTINGS.get_boolean('window-preview-fixed-y')
2019-05-29 21:24:25 -04:00
};
2023-08-13 22:58:15 +02:00
alphaBg = SETTINGS.get_boolean('preview-use-custom-opacity') ?
SETTINGS.get_int('preview-custom-opacity') * .01 :
2020-04-13 12:53:48 -04:00
this.panel.dynamicTransparency.alpha;
2022-04-01 22:32:15 -04:00
}
2019-05-19 10:42:22 -04:00
2022-04-01 22:32:15 -04:00
_updateClip() {
let x, y, w;
let geom = this.panel.getGeometry();
2019-09-06 17:47:34 -04:00
let panelBoxTheme = this.panel.panelBox.get_theme_node();
2023-08-13 22:58:15 +02:00
let previewSize = (SETTINGS.get_int('window-preview-size') +
SETTINGS.get_int('window-preview-padding') * 2) * scaleFactor;
2019-09-04 21:55:26 -04:00
if (this.isVertical) {
w = previewSize;
2020-06-13 07:40:33 -04:00
this.clipHeight = this.panel.monitor.height;
2019-09-06 17:47:34 -04:00
y = this.panel.monitor.y;
} else {
2019-09-06 17:47:34 -04:00
w = this.panel.monitor.width;
2020-06-13 07:40:33 -04:00
this.clipHeight = (previewSize + headerHeight);
2019-09-06 17:47:34 -04:00
x = this.panel.monitor.x;
}
2019-09-04 21:55:26 -04:00
if (geom.position == St.Side.LEFT) {
2020-06-13 07:40:33 -04:00
x = this.panel.monitor.x + this.panel.dtpSize + panelBoxTheme.get_padding(St.Side.LEFT);
2019-09-04 21:55:26 -04:00
} else if (geom.position == St.Side.RIGHT) {
2020-06-13 07:40:33 -04:00
x = this.panel.monitor.x + this.panel.monitor.width - (this.panel.dtpSize + previewSize) - panelBoxTheme.get_padding(St.Side.RIGHT);
2019-09-04 21:55:26 -04:00
} else if (geom.position == St.Side.TOP) {
2020-06-13 07:40:33 -04:00
y = this.panel.monitor.y + this.panel.dtpSize + panelBoxTheme.get_padding(St.Side.TOP);
} else { //St.Side.BOTTOM
2020-06-13 07:40:33 -04:00
y = this.panel.monitor.y + this.panel.monitor.height - (this.panel.dtpSize + panelBoxTheme.get_padding(St.Side.BOTTOM) + previewSize + headerHeight);
}
2020-06-13 07:40:33 -04:00
Utils.setClip(this, x, y, w, this.clipHeight);
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
_updatePosition() {
2022-04-13 22:29:57 -04:00
let sourceNode = this.currentAppIcon.get_theme_node();
let sourceContentBox = sourceNode.get_content_box(this.currentAppIcon.get_allocation_box());
let sourceAllocation = Utils.getTransformedAllocation(this.currentAppIcon);
let [previewsWidth, previewsHeight] = this._getPreviewsSize();
2023-08-13 22:58:15 +02:00
let appIconMargin = SETTINGS.get_int('appicon-margin') / scaleFactor;
let x = 0, y = 0;
2016-09-21 00:18:00 +00:00
2019-09-06 17:47:34 -04:00
previewsWidth = Math.min(previewsWidth, this.panel.monitor.width);
previewsHeight = Math.min(previewsHeight, this.panel.monitor.height);
this._updateScrollFade(previewsWidth < this.panel.monitor.width && previewsHeight < this.panel.monitor.height);
2019-05-15 17:02:18 -04:00
2019-09-04 21:55:26 -04:00
if (this.isVertical) {
2019-09-06 17:47:34 -04:00
y = sourceAllocation.y1 + appIconMargin - this.panel.monitor.y + (sourceContentBox.y2 - sourceContentBox.y1 - previewsHeight) * .5;
y = Math.max(y, 0);
2019-09-06 17:47:34 -04:00
y = Math.min(y, this.panel.monitor.height - previewsHeight);
} else {
2019-09-06 17:47:34 -04:00
x = sourceAllocation.x1 + appIconMargin - this.panel.monitor.x + (sourceContentBox.x2 - sourceContentBox.x1 - previewsWidth) * .5;
x = Math.max(x, 0);
2019-09-06 17:47:34 -04:00
x = Math.min(x, this.panel.monitor.width - previewsWidth);
2016-09-21 00:18:00 +00:00
}
if (!this.opened) {
this.menu.set_position(x, y);
2019-05-26 13:43:42 -04:00
this.menu.set_size(previewsWidth, previewsHeight);
} else {
Utils.animate(this.menu, getTweenOpts({ x: x, y: y, width: previewsWidth, height: previewsHeight }));
}
2022-04-01 22:32:15 -04:00
}
2016-09-21 00:18:00 +00:00
2022-04-01 22:32:15 -04:00
_updateScrollFade(remove) {
let [value, upper, pageSize] = this._getScrollAdjustmentValues();
2019-09-13 18:50:57 -04:00
let needsFade = Math.round(upper) > Math.round(pageSize);
let fadeWidgets = this.menu.get_children().filter(c => c != this._scrollView);
if (!remove && needsFade) {
if (!fadeWidgets.length) {
fadeWidgets.push(this._getFadeWidget());
fadeWidgets.push(this._getFadeWidget(true));
this.menu.add_child(fadeWidgets[0]);
this.menu.add_child(fadeWidgets[1]);
}
fadeWidgets[0].visible = value > 0;
fadeWidgets[1].visible = value + pageSize < upper;
} else if (remove || (!needsFade && fadeWidgets.length)) {
fadeWidgets.forEach(fw => fw.destroy());
}
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
_getScrollAdjustmentValues() {
let [value , , upper, , , pageSize] = this._scrollView[(this.isVertical ? 'v' : 'h') + 'adjustment'].get_values();
return [value, upper, pageSize];
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
_getFadeWidget(end) {
let x = 0, y = 0;
2019-09-06 17:47:34 -04:00
let startBg = Utils.getrgbaColor(this.panel.dynamicTransparency.backgroundColorRgb, Math.min(alphaBg + .1, 1));
let endBg = Utils.getrgbaColor(this.panel.dynamicTransparency.backgroundColorRgb, 0)
2019-08-31 16:37:16 -04:00
let fadeStyle = 'background-gradient-start:' + startBg +
'background-gradient-end:' + endBg +
2020-06-13 07:40:33 -04:00
'background-gradient-direction:' + this.panel.getOrientation();
2019-09-04 21:55:26 -04:00
if (this.isVertical) {
2019-09-06 17:47:34 -04:00
y = end ? this.panel.monitor.height - FADE_SIZE : 0;
} else {
2019-09-06 17:47:34 -04:00
x = end ? this.panel.monitor.width - FADE_SIZE : 0;
}
let fadeWidget = new St.Widget({
reactive: false,
2022-04-13 22:29:57 -04:00
pivot_point: new Graphene.Point({ x: .5, y: .5 }),
2019-09-06 17:47:34 -04:00
rotation_angle_z: end ? 180 : 0,
style: fadeStyle,
x: x, y: y,
2019-09-06 17:47:34 -04:00
width: this.isVertical ? this.width : FADE_SIZE,
height: this.isVertical ? FADE_SIZE : this.height
});
return fadeWidget;
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
_getPreviewsSize() {
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();
2019-09-04 21:55:26 -04:00
if (this.isVertical) {
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];
2022-04-01 22:32:15 -04:00
}
2019-05-15 17:02:18 -04:00
2022-04-01 22:32:15 -04:00
_animateOpenOrClose(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
};
tweenOpts[this._translationProp] = show ? this._translationDirection : this._translationOffset;
2019-05-14 17:08:08 -04:00
Utils.animate(this.menu, getTweenOpts(tweenOpts));
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
_peek(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();
2023-08-13 22:58:15 +02:00
let focusWindow = () => this._focusMetaWindow(SETTINGS.get_int('peek-mode-opacity'), window);
this._restorePeekedWindowStack();
if (this._peekedWindow && windowWorkspace != currentWorkspace) {
currentWorkspace.list_windows().forEach(mw => this.animateWindowOpacity(mw, null, 255))
}
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
}
2022-04-01 22:32:15 -04:00
}
2019-05-21 14:31:55 -04:00
2022-04-01 22:32:15 -04:00
_endPeek(stayHere) {
2019-05-21 14:31:55 -04:00
this._timeoutsHandler.remove(T3);
if (this._peekedWindow) {
2020-04-30 00:30:52 -04:00
let immediate = !stayHere && this.peekInitialWorkspaceIndex != Utils.getCurrentWorkspace().index();
2019-05-21 14:31:55 -04:00
2020-04-30 00:30:52 -04:00
this._restorePeekedWindowStack();
this._focusMetaWindow(255, this._peekedWindow, immediate, true);
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
}
2022-04-01 22:32:15 -04:00
}
2019-05-21 14:31:55 -04:00
2022-04-01 22:32:15 -04:00
_switchToWorkspaceImmediate(workspaceIndex) {
2019-05-25 11:08:46 -04:00
let workspace = Utils.getWorkspaceByIndex(workspaceIndex);
2020-04-29 17:36:52 -04:00
let shouldAnimate = Main.wm._shouldAnimate;
2019-05-25 11:08:46 -04:00
if (!workspace || (!workspace.list_windows().length &&
2020-04-30 00:30:52 -04:00
workspaceIndex < Utils.getWorkspaceCount() - 1)) {
2019-05-25 11:08:46 -04:00
workspace = Utils.getCurrentWorkspace();
}
2019-05-25 11:08:46 -04:00
2020-04-29 17:36:52 -04:00
Main.wm._shouldAnimate = () => false;
workspace.activate(global.display.get_current_time_roundtrip());
2020-04-29 17:36:52 -04:00
Main.wm._shouldAnimate = shouldAnimate;
2022-04-01 22:32:15 -04:00
}
2019-05-21 14:31:55 -04:00
2022-04-01 22:32:15 -04:00
_focusMetaWindow(dimOpacity, window, immediate, ignoreFocus) {
2020-04-30 00:30:52 -04:00
window.get_workspace().list_windows().forEach(mw => {
let wa = mw.get_compositor_private();
let isFocused = !ignoreFocus && mw == window;
2019-05-22 21:37:53 -04:00
2020-04-30 00:30:52 -04:00
if (wa) {
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
}
this.animateWindowOpacity(mw, wa, isFocused ? 255 : dimOpacity, immediate)
2019-05-22 21:37:53 -04:00
}
});
2022-04-01 22:32:15 -04:00
}
animateWindowOpacity(metaWindow, windowActor, opacity, immediate) {
windowActor = windowActor || metaWindow.get_compositor_private();
if (windowActor && !metaWindow.minimized) {
let tweenOpts = getTweenOpts({ opacity });
if (immediate && !metaWindow.is_on_all_workspaces()) {
tweenOpts.time = 0;
}
Utils.animateWindowOpacity(windowActor, tweenOpts);
}
}
2022-04-01 22:32:15 -04:00
_restorePeekedWindowStack() {
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
}
}
2022-04-01 22:32:15 -04:00
}
2019-05-14 17:08:08 -04:00
});
export const Preview = GObject.registerClass({
2022-04-01 22:32:15 -04:00
}, class Preview extends St.Widget {
2019-05-14 17:08:08 -04:00
2022-04-01 22:32:15 -04:00
_init(previewMenu) {
super._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._waitWindowId = 0;
this._needsCloseButton = true;
this.cloneWidth = this.cloneHeight = 0;
2019-05-14 20:13:01 -04:00
this._previewMenu = previewMenu;
2023-08-13 22:58:15 +02:00
this._padding = SETTINGS.get_int('window-preview-padding') * scaleFactor;
this._previewDimensions = this._getPreviewDimensions();
2019-05-14 20:13:01 -04:00
this.animatingOut = false;
2020-08-22 10:18:31 -04:00
let box = new St.Widget({ layout_manager: new Clutter.BoxLayout({ orientation: Clutter.Orientation.VERTICAL }), y_expand: true });
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
2024-02-03 20:19:22 +01:00
closeButton.add_child(new St.Icon({ icon_name: 'window-close-symbolic' }));
2019-05-20 00:25:22 -04:00
2019-05-19 10:42:22 -04:00
this._closeButtonBin = new St.Widget({
style_class: 'preview-close-btn-container',
2019-05-19 10:42:22 -04:00
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-26 13:43:42 -04:00
this._previewBin = new St.Widget({
layout_manager: new Clutter.BinLayout(),
x_expand: true, y_expand: true,
2019-05-27 23:57:25 -04:00
style: 'padding: ' + this._padding / scaleFactor + 'px;'
2019-05-26 13:43:42 -04:00
});
this._previewBin.set_size(previewBinWidth, previewBinHeight);
box.add_child(this._previewBin);
2019-05-20 00:25:22 -04:00
if (headerHeight) {
let headerBox = new St.Widget({
style_class: 'preview-header-box',
2019-05-20 00:25:22 -04:00
layout_manager: new Clutter.BoxLayout(),
2019-05-26 13:43:42 -04:00
x_expand: true,
y_align: Clutter.ActorAlign[isTopHeader ? 'START' : 'END']
2019-05-20 00:25:22 -04:00
});
setStyle(headerBox, this._getBackgroundColor(HEADER_COLOR_OFFSET, 1));
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-26 13:43:42 -04:00
box.insert_child_at_index(headerBox, isTopHeader ? 0 : 1);
2019-05-20 00:25:22 -04:00
}
2019-05-19 10:42:22 -04:00
2019-05-26 13:43:42 -04:00
this.add_child(box);
this.add_child(this._closeButtonBin);
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());
2022-04-01 22:32:15 -04:00
}
2019-05-20 00:25:22 -04:00
2022-04-01 22:32:15 -04:00
adjustOnStage() {
let closeButton = this._closeButtonBin.get_first_child();
let closeButtonHeight = closeButton.height;
let maxCloseButtonSize = MAX_CLOSE_BUTTON_SIZE * scaleFactor;
2019-05-20 10:56:22 -04:00
let closeButtonBorderRadius = '';
if (closeButtonHeight > maxCloseButtonSize) {
closeButtonHeight = maxCloseButtonSize;
closeButton.set_size(closeButtonHeight, closeButtonHeight);
}
2019-05-20 10:56:22 -04:00
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
}
setStyle(
this._closeButtonBin,
'padding: ' + (headerHeight ? Math.round((headerHeight - closeButtonHeight) * .5 / scaleFactor) : 4) + 'px;' +
this._getBackgroundColor(HEADER_COLOR_OFFSET, headerHeight ? 1 : .6) +
2019-05-20 10:56:22 -04:00
closeButtonBorderRadius
2019-05-20 00:25:22 -04:00
);
2022-04-01 22:32:15 -04:00
}
2019-05-14 20:13:01 -04:00
2022-04-01 22:32:15 -04:00
assignWindow(window, animateSize) {
2019-05-25 11:08:46 -04:00
if (this.window != window) {
let _assignWindowClone = () => {
if (window.get_compositor_private()) {
2019-05-27 23:57:25 -04:00
let cloneBin = this._getWindowCloneBin(window);
2019-05-25 11:08:46 -04:00
this._resizeClone(cloneBin, window);
2019-05-27 23:57:25 -04:00
this._addClone(cloneBin, animateSize);
2019-05-25 11:08:46 -04:00
this._previewMenu.updatePosition();
} else if (!this._waitWindowId) {
2023-09-21 19:22:31 -07:00
this._waitWindowId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
this._waitWindowId = 0;
2020-04-29 17:36:52 -04:00
if (this._previewMenu.opened) {
_assignWindowClone();
}
2023-09-21 19:22:31 -07:00
return GLib.SOURCE_REMOVE;
});
2019-05-25 11:08:46 -04:00
}
};
_assignWindowClone();
}
2019-05-14 20:13:01 -04:00
this._cancelAnimateOut();
2019-05-20 00:25:22 -04:00
this._removeWindowSignals();
2019-05-19 10:42:22 -04:00
this.window = window;
2022-04-08 18:50:50 -04:00
this._needsCloseButton = this._previewMenu.allowCloseWindow && window.can_close() && !Utils.checkIfWindowHasTransient(window);
this._updateHeader();
2022-04-01 22:32:15 -04:00
}
2019-05-14 20:13:01 -04:00
2022-04-01 22:32:15 -04:00
animateOut() {
2019-05-25 11:08:46 -04:00
if (!this.animatingOut) {
2019-05-26 13:43:42 -04:00
let tweenOpts = getTweenOpts({ opacity: 0, width: 0, height: 0, onComplete: () => this.destroy() });
2019-05-14 20:13:01 -04:00
2019-05-25 11:08:46 -04:00
this.animatingOut = true;
2019-05-14 20:13:01 -04:00
Utils.stopAnimations(this);
Utils.animate(this, tweenOpts);
2019-05-25 11:08:46 -04:00
}
2022-04-01 22:32:15 -04:00
}
2019-05-25 11:08:46 -04:00
2022-04-01 22:32:15 -04:00
getSize() {
2019-05-15 17:02:18 -04:00
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) + headerHeight;
2019-05-15 17:02:18 -04:00
return [binWidth, binHeight];
2022-04-01 22:32:15 -04:00
}
2019-05-15 17:02:18 -04:00
2022-04-01 22:32:15 -04:00
setFocus(focused) {
2019-05-20 10:56:22 -04:00
this._hideOrShowCloseButton(!focused);
setStyle(this, this._getBackgroundColor(FOCUSED_COLOR_OFFSET, focused ? '-' : 0));
2019-05-21 14:31:55 -04:00
if (focused) {
2019-06-07 20:02:51 -04:00
this._previewMenu.ensureVisible(this);
2019-05-21 14:31:55 -04:00
this._previewMenu.requestPeek(this.window);
}
2022-04-01 22:32:15 -04:00
}
2019-05-20 10:56:22 -04:00
2022-04-01 22:32:15 -04:00
activate() {
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);
2022-04-01 22:32:15 -04:00
}
2019-05-20 10:56:22 -04:00
2022-04-01 22:32:15 -04:00
_onDestroy() {
if (this._waitWindowId) {
GLib.source_remove(this._waitWindowId);
this._waitWindowId = 0;
}
2019-05-20 00:25:22 -04:00
this._removeWindowSignals();
2022-04-01 22:32:15 -04:00
}
2019-05-20 00:25:22 -04:00
2022-04-01 22:32:15 -04:00
_onHoverChanged() {
2019-05-20 10:56:22 -04:00
this.setFocus(this.hover);
2022-04-01 22:32:15 -04:00
}
2019-05-19 10:42:22 -04:00
2022-04-01 22:32:15 -04:00
_onCloseBtnClick() {
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
2023-08-13 22:58:15 +02:00
if (!SETTINGS.get_boolean('group-apps')) {
2019-05-20 00:25:22 -04:00
this._previewMenu.close();
2020-09-04 07:39:01 -04:00
} else {
this._previewMenu.endPeekHere();
2019-05-20 00:25:22 -04:00
}
2020-09-04 07:39:01 -04:00
this.window.delete(global.get_current_time());
2022-04-01 22:32:15 -04:00
}
2019-05-19 10:42:22 -04:00
2022-04-01 22:32:15 -04:00
_onButtonReleaseEvent(e) {
2019-05-19 11:39:15 -04:00
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
2023-08-13 22:58:15 +02:00
if (SETTINGS.get_boolean('preview-middle-click-close')) {
2019-05-19 11:39:15 -04:00
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;
2022-04-01 22:32:15 -04:00
}
2019-05-19 11:39:15 -04:00
2022-04-01 22:32:15 -04:00
_cancelAnimateOut() {
if (this.animatingOut) {
this.animatingOut = false;
Utils.stopAnimations(this);
Utils.animate(this, getTweenOpts({ opacity: 255, width: this.cloneWidth, height: this.cloneHeight }));
}
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
_showContextMenu(e) {
2019-05-25 11:08:46 -04:00
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 menu = Main.wm._windowMenuManager._manager._menus[0];
2019-05-25 11:08:46 -04:00
menu.connect('open-state-changed', () => this._previewMenu.menu.sync_hover());
this._previewMenu.menu.sync_hover();
2019-05-25 11:08:46 -04:00
if (this.window.get_workspace() != currentWorkspace) {
let menuItem = new PopupMenu.PopupMenuItem(_('Move to current Workspace') + ' [' + (currentWorkspace.index() + 1) + ']');
let menuItems = menu.box.get_children();
2019-05-25 11:08:46 -04:00
let insertIndex = Utils.findIndex(menuItems, c => c._delegate instanceof PopupMenu.PopupSeparatorMenuItem);
insertIndex = insertIndex >= 0 ? insertIndex : menuItems.length - 1;
menu.addMenuItem(menuItem, insertIndex);
2019-05-25 11:08:46 -04:00
menuItem.connect('activate', () => this.window.change_workspace(currentWorkspace));
}
2022-04-01 22:32:15 -04:00
}
2019-05-25 11:08:46 -04:00
2022-04-01 22:32:15 -04:00
_removeWindowSignals() {
2019-05-20 00:25:22 -04:00
if (this._titleWindowChangeId) {
this.window.disconnect(this._titleWindowChangeId);
this._titleWindowChangeId = 0;
}
2022-04-01 22:32:15 -04:00
}
2019-05-20 00:25:22 -04:00
2022-04-01 22:32:15 -04:00
_updateHeader() {
2019-05-20 00:25:22 -04:00
if (headerHeight) {
2023-08-13 22:58:15 +02:00
let iconTextureSize = SETTINGS.get_boolean('window-preview-use-custom-icon-size') ?
SETTINGS.get_int('window-preview-custom-icon-size') :
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;
2023-08-13 22:58:15 +02:00
let fontScale = DESKTOPSETTINGS.get_double('text-scaling-factor');
let commonTitleStyles = 'color: ' + SETTINGS.get_string('window-preview-title-font-color') + ';' +
'font-size: ' + SETTINGS.get_int('window-preview-title-font-size') * fontScale + 'px;' +
'font-weight: ' + SETTINGS.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);
2023-08-13 22:58:15 +02:00
if (!SETTINGS.get_boolean('isolate-workspaces')) {
2019-05-23 20:08:49 -04:00
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;
setStyle(this._workspaceIndicator, workspaceStyle);
2019-05-20 00:25:22 -04:00
this._titleWindowChangeId = this.window.connect('notify::title', () => this._updateWindowTitle());
setStyle(this._windowTitle, 'max-width: 0px; padding-right: 4px;' + commonTitleStyles);
2019-05-20 00:25:22 -04:00
this._updateWindowTitle();
}
2022-04-01 22:32:15 -04:00
}
2019-05-20 00:25:22 -04:00
2022-04-01 22:32:15 -04:00
_updateWindowTitle() {
this._windowTitle.text = this.window.title;
2022-04-01 22:32:15 -04:00
}
2019-05-20 00:25:22 -04:00
2022-04-01 22:32:15 -04:00
_hideOrShowCloseButton(hide) {
if (this._needsCloseButton) {
Utils.animate(this._closeButtonBin, getTweenOpts({ opacity: hide ? 0 : 255 }));
}
2022-04-01 22:32:15 -04:00
}
2019-05-19 17:51:07 -04:00
2022-04-01 22:32:15 -04:00
_getBackgroundColor(offset, alpha) {
return 'background-color: ' + this._getRgbaColor(offset, alpha) +
2019-09-06 17:47:34 -04:00
'transition-duration:' + this._previewMenu.panel.dynamicTransparency.animationDuration;
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
_getRgbaColor(offset, alpha) {
2019-05-20 00:25:22 -04:00
alpha = Math.abs(alpha);
if (isNaN(alpha)) {
alpha = alphaBg;
2019-05-20 00:25:22 -04:00
}
2019-09-06 17:47:34 -04:00
return Utils.getrgbaColor(this._previewMenu.panel.dynamicTransparency.backgroundColorRgb, alpha, offset);
2022-04-01 22:32:15 -04:00
}
2019-05-19 17:51:07 -04:00
2022-04-01 22:32:15 -04:00
_addClone(newCloneBin, animateSize) {
2019-05-17 01:25:39 -04:00
let currentClones = this._previewBin.get_children();
let newCloneOpts = getTweenOpts({ opacity: 255 });
2019-05-27 23:57:25 -04:00
this._previewBin.add_child(newCloneBin);
2019-05-17 01:25:39 -04:00
if (currentClones.length) {
2019-05-27 23:57:25 -04:00
let currentCloneBin = currentClones.pop();
let currentCloneOpts = getTweenOpts({ opacity: 0, onComplete: () => currentCloneBin.destroy() });
2019-05-17 01:25:39 -04:00
2019-05-27 23:57:25 -04:00
if (newCloneBin.width > currentCloneBin.width) {
newCloneOpts.width = newCloneBin.width;
newCloneBin.width = currentCloneBin.width;
2019-05-17 01:25:39 -04:00
} else {
2019-05-27 23:57:25 -04:00
currentCloneOpts.width = newCloneBin.width;
2019-05-17 01:25:39 -04:00
}
2019-05-27 23:57:25 -04:00
if (newCloneBin.height > currentCloneBin.height) {
newCloneOpts.height = newCloneBin.height;
newCloneBin.height = currentCloneBin.height;
2019-05-17 01:25:39 -04:00
} else {
2019-05-27 23:57:25 -04:00
currentCloneOpts.height = newCloneBin.height;
2019-05-17 01:25:39 -04:00
}
currentClones.forEach(c => c.destroy());
Utils.animate(currentCloneBin, currentCloneOpts);
} else if (animateSize) {
2019-05-27 23:57:25 -04:00
newCloneBin.width = 0;
newCloneBin.height = 0;
2019-05-26 13:43:42 -04:00
newCloneOpts.width = this.cloneWidth;
newCloneOpts.height = this.cloneHeight;
2019-05-17 01:25:39 -04:00
}
Utils.animate(newCloneBin, newCloneOpts);
2022-04-01 22:32:15 -04:00
}
2019-05-14 20:13:01 -04:00
2022-04-01 22:32:15 -04:00
_getWindowCloneBin(window) {
2019-06-10 06:40:47 -04:00
let frameRect = window.get_frame_rect();
let bufferRect = window.get_buffer_rect();
let clone = new Clutter.Clone({ source: window.get_compositor_private() });
let cloneBin = new St.Widget({
2019-05-27 23:57:25 -04:00
opacity: 0,
2019-06-10 06:40:47 -04:00
layout_manager: frameRect.width != bufferRect.width ||
frameRect.height != bufferRect.height ?
new WindowCloneLayout(frameRect, bufferRect) :
new Clutter.BinLayout()
2019-05-15 17:02:18 -04:00
});
cloneBin.add_child(clone);
return cloneBin;
2022-04-01 22:32:15 -04:00
}
2019-05-15 17:02:18 -04:00
2022-04-01 22:32:15 -04:00
_getBinSize() {
2019-05-29 21:24:25 -04:00
let [fixedWidth, fixedHeight] = this._previewDimensions;
2019-05-29 21:24:25 -04:00
return [
aspectRatio.x.fixed ? fixedWidth + this._padding * 2 : -1,
aspectRatio.y.fixed ? fixedHeight + this._padding * 2 : -1
];
2022-04-01 22:32:15 -04:00
}
2019-05-14 17:08:08 -04:00
2022-04-01 22:32:15 -04:00
_resizeClone(cloneBin, window) {
2019-06-10 06:40:47 -04:00
let frameRect = cloneBin.layout_manager.frameRect || window.get_frame_rect();
let [fixedWidth, fixedHeight] = this._previewDimensions;
let ratio = Math.min(fixedWidth / frameRect.width, fixedHeight / frameRect.height, 1);
let cloneWidth = frameRect.width * ratio;
let cloneHeight = frameRect.height * ratio;
2019-05-27 23:57:25 -04:00
let clonePaddingTB = cloneHeight < MIN_DIMENSION ? MIN_DIMENSION - cloneHeight : 0;
let clonePaddingLR = cloneWidth < MIN_DIMENSION ? MIN_DIMENSION - cloneWidth : 0;
let clonePaddingTop = clonePaddingTB * .5;
let clonePaddingLeft = clonePaddingLR * .5;
2019-05-15 17:02:18 -04:00
2019-05-27 23:57:25 -04:00
this.cloneWidth = cloneWidth + clonePaddingLR * scaleFactor;
this.cloneHeight = cloneHeight + clonePaddingTB * scaleFactor;
2019-05-17 01:25:39 -04:00
cloneBin.set_style('padding: ' + clonePaddingTop + 'px ' + clonePaddingLeft + 'px;');
cloneBin.layout_manager.ratio = ratio;
cloneBin.layout_manager.padding = [clonePaddingLeft * scaleFactor, clonePaddingTop * scaleFactor];
cloneBin.get_first_child().set_size(cloneWidth, cloneHeight);
2022-04-01 22:32:15 -04:00
}
2019-05-15 17:02:18 -04:00
2022-04-01 22:32:15 -04:00
_getPreviewDimensions() {
2023-08-13 22:58:15 +02:00
let size = SETTINGS.get_int('window-preview-size') * scaleFactor;
let w, h;
2019-09-04 21:55:26 -04:00
if (this._previewMenu.isVertical) {
w = size;
2019-05-29 21:24:25 -04:00
h = w * aspectRatio.y.size / aspectRatio.x.size;
} else {
h = size;
2019-05-29 21:24:25 -04:00
w = h * aspectRatio.x.size / aspectRatio.y.size;
}
return [w, h];
2019-05-15 17:02:18 -04:00
}
2019-05-14 20:13:01 -04:00
});
export const WindowCloneLayout = GObject.registerClass({
2022-04-01 22:32:15 -04:00
}, class WindowCloneLayout extends Clutter.BinLayout {
2022-04-01 22:32:15 -04:00
_init(frameRect, bufferRect) {
super._init();
2019-06-10 06:40:47 -04:00
//the buffer_rect contains the transparent padding that must be removed
this.frameRect = frameRect;
this.bufferRect = bufferRect;
2022-04-01 22:32:15 -04:00
}
2022-04-13 22:29:57 -04:00
vfunc_allocate(actor, box) {
let [width, height] = box.get_size();
box.set_origin(
(this.bufferRect.x - this.frameRect.x) * this.ratio + this.padding[0],
(this.bufferRect.y - this.frameRect.y) * this.ratio + this.padding[1]
);
box.set_size(
width + (this.bufferRect.width - this.frameRect.width) * this.ratio,
height + (this.bufferRect.height - this.frameRect.height) * this.ratio
);
2022-04-13 22:29:57 -04:00
actor.get_first_child().allocate(box);
}
});
export function setStyle(actor, style) {
if (!isManualStyling) {
actor.set_style(style);
}
}
export function getTweenOpts(opts) {
2019-05-14 20:13:01 -04:00
let defaults = {
time: animationTime,
2019-05-14 20:13:01 -04:00
transition: 'easeInOutQuad'
};
return Utils.mergeObjects(opts || {}, defaults);
}