summaryrefslogtreecommitdiffstats
path: root/extensions/45/vertical-workspaces/lib/workspaceThumbnail.js
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/45/vertical-workspaces/lib/workspaceThumbnail.js')
-rw-r--r--extensions/45/vertical-workspaces/lib/workspaceThumbnail.js1236
1 files changed, 0 insertions, 1236 deletions
diff --git a/extensions/45/vertical-workspaces/lib/workspaceThumbnail.js b/extensions/45/vertical-workspaces/lib/workspaceThumbnail.js
deleted file mode 100644
index 75e5250..0000000
--- a/extensions/45/vertical-workspaces/lib/workspaceThumbnail.js
+++ /dev/null
@@ -1,1236 +0,0 @@
-/**
- * V-Shell (Vertical Workspaces)
- * workspaceThumbnail.js
- *
- * @author GdH <G-dH@github.com>
- * @copyright 2022 - 2023
- * @license GPL-3.0
- *
- */
-
-'use strict';
-
-import GLib from 'gi://GLib';
-import Clutter from 'gi://Clutter';
-import St from 'gi://St';
-import Meta from 'gi://Meta';
-import Shell from 'gi://Shell';
-
-import * as Main from 'resource:///org/gnome/shell/ui/main.js';
-import * as DND from 'resource:///org/gnome/shell/ui/dnd.js';
-import * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js';
-import * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js';
-import * as WorkspaceThumbnail from 'resource:///org/gnome/shell/ui/workspaceThumbnail.js';
-import * as Background from 'resource:///org/gnome/shell/ui/background.js';
-
-let Me;
-let opt;
-
-const ThumbnailState = {
- NEW: 0,
- EXPANDING: 1,
- EXPANDED: 2,
- ANIMATING_IN: 3,
- NORMAL: 4,
- REMOVING: 5,
- ANIMATING_OUT: 6,
- ANIMATED_OUT: 7,
- COLLAPSING: 8,
- DESTROYED: 9,
-};
-
-const ControlsState = OverviewControls.ControlsState;
-
-const WORKSPACE_CUT_SIZE = 10;
-const WORKSPACE_KEEP_ALIVE_TIME = 100;
-
-export const WorkspaceThumbnailModule = class {
- constructor(me) {
- Me = me;
- opt = Me.opt;
-
- this._firstActivation = true;
- this.moduleEnabled = false;
- this._overrides = null;
- }
-
- cleanGlobals() {
- Me = null;
- opt = null;
- }
-
- update(reset) {
- this.moduleEnabled = true;
- const conflict = false;
-
- reset = reset || !this.moduleEnabled || conflict;
-
- // don't touch the original code if module disabled
- if (reset && !this._firstActivation) {
- this._disableModule();
- } else if (!reset) {
- this._firstActivation = false;
- this._activateModule();
- }
- if (reset && this._firstActivation)
- console.debug(' WorkspaceThumbnailModule - Keeping untouched');
- }
-
- _activateModule() {
- if (!this._overrides)
- this._overrides = new Me.Util.Overrides();
-
- // don't limit max thumbnail scale for other clients than overview, specifically AATWS.
- // this variable is not yet implemented in 45.beta.1
-
- this._overrides.addOverride('WorkspaceThumbnail', WorkspaceThumbnail.WorkspaceThumbnail.prototype, WorkspaceThumbnailCommon);
- this._overrides.addOverride('ThumbnailsBoxCommon', WorkspaceThumbnail.ThumbnailsBox.prototype, ThumbnailsBoxCommon);
-
- // replacing opt.ORIENTATION local constant with boxOrientation internal variable allows external customers such as the AATWS extension to control the box orientation.
- Main.overview._overview.controls._thumbnailsBox._boxOrientation = opt.ORIENTATION;
-
- console.debug(' WorkspaceThumbnailModule - Activated');
- }
-
- _disableModule() {
- if (this._overrides)
- this._overrides.removeAll();
- this._overrides = null;
-
- console.debug(' WorkspaceThumbnailModule - Disabled');
- }
-};
-
-const WorkspaceThumbnailCommon = {
- // injection to _init()
- after__init() {
- // layout manager allows aligning widget children
- this.layout_manager = new Clutter.BinLayout();
- // adding layout manager to tmb widget breaks wallpaper background aligning and rounded corners
- // unless border is removed
- if (opt.SHOW_WS_TMB_BG)
- this.add_style_class_name('ws-tmb-labeled');
- else
- this.add_style_class_name('ws-tmb-transparent');
-
- // add workspace thumbnails labels if enabled
- if (opt.SHOW_WST_LABELS) { // 0 - disable
- const getLabel = function () {
- const wsIndex = this.metaWorkspace.index();
- let label = `${wsIndex + 1}`;
- if (opt.SHOW_WST_LABELS === 2) { // 2 - index + workspace name
- const settings = Me.getSettings('org.gnome.desktop.wm.preferences');
- const wsLabels = settings.get_strv('workspace-names');
- if (wsLabels.length > wsIndex && wsLabels[wsIndex])
- label += `: ${wsLabels[wsIndex]}`;
- } else if (opt.SHOW_WST_LABELS === 3) { // 3- index + app name
- // global.display.get_tab_list offers workspace filtering using the second argument, but...
- // ... it sometimes includes windows from other workspaces, like minimized VBox machines, after Shell restarts
- const metaWin = global.display.get_tab_list(0, null).filter(
- w => w.get_monitor() === this.monitorIndex && w.get_workspace().index() === wsIndex)[0];
-
- if (metaWin) {
- const tracker = Shell.WindowTracker.get_default();
- const app = tracker.get_window_app(metaWin);
- label += `: ${app ? app.get_name() : ''}`;
- }
- } else if (opt.SHOW_WST_LABELS === 4) {
- const metaWin = global.display.get_tab_list(0, null).filter(
- w => w.get_monitor() === this.monitorIndex && w.get_workspace().index() === wsIndex)[0];
-
- if (metaWin)
- label += `: ${metaWin.title}`;
- }
- return label;
- }.bind(this);
-
- const label = getLabel();
-
- this._wsLabel = new St.Label({
- text: label,
- style_class: 'ws-tmb-label',
- x_align: Clutter.ActorAlign.FILL,
- y_align: Clutter.ActorAlign.END,
- x_expand: true,
- y_expand: true,
- });
-
- this._wsLabel._maxOpacity = 255;
- this._wsLabel.opacity = this._wsLabel._maxOpacity;
-
- this.add_child(this._wsLabel);
- this.set_child_above_sibling(this._wsLabel, null);
-
- this._wsIndexConId = this.metaWorkspace.connect('notify::workspace-index', () => {
- const newLabel = getLabel();
- this._wsLabel.text = newLabel;
- // avoid possibility of accessing non existing ws
- if (this._updateLabelTimeout) {
- GLib.source_remove(this._updateLabelTimeout);
- this._updateLabelTimeout = 0;
- }
- });
- this._nWindowsConId = this.metaWorkspace.connect('notify::n-windows', () => {
- if (this._updateLabelTimeout)
- return;
- // wait for new data
- this._updateLabelTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, () => {
- const newLabel = getLabel();
- this._wsLabel.text = newLabel;
- this._updateLabelTimeout = 0;
- return GLib.SOURCE_REMOVE;
- });
- });
- }
-
- if (opt.CLOSE_WS_BUTTON_MODE) {
- const closeButton = new St.Icon({
- style_class: 'workspace-close-button',
- icon_name: 'window-close-symbolic',
- x_align: Clutter.ActorAlign.END,
- y_align: Clutter.ActorAlign.START,
- x_expand: true,
- y_expand: true,
- reactive: true,
- opacity: 0,
- });
-
- closeButton.connect('button-release-event', () => {
- if (opt.CLOSE_WS_BUTTON_MODE) {
- this._closeWorkspace();
- return Clutter.EVENT_STOP;
- } else {
- return Clutter.EVENT_PROPAGATE;
- }
- });
-
- closeButton.connect('button-press-event', () => {
- return Clutter.EVENT_STOP;
- });
-
- closeButton.connect('enter-event', () => {
- closeButton.opacity = 255;
- if (!Meta.prefs_get_dynamic_workspaces() || (Meta.prefs_get_dynamic_workspaces() && global.workspace_manager.get_n_workspaces() - 1 !== this.metaWorkspace.index())) {
- // color the button red if ready to react on clicks
- if (opt.CLOSE_WS_BUTTON_MODE < 3 || (opt.CLOSE_WS_BUTTON_MODE === 3 && Me.Util.isCtrlPressed()))
- closeButton.add_style_class_name('workspace-close-button-hover');
- }
- });
-
- closeButton.connect('leave-event', () => {
- closeButton.remove_style_class_name('workspace-close-button-hover');
- });
-
- this.add_child(closeButton);
- this._closeButton = closeButton;
-
- this.reactive = true;
- this._lastCloseClickTime = 0;
- }
-
- if (opt.SHOW_WST_LABELS_ON_HOVER)
- this._wsLabel.opacity = 0;
-
- this.connect('enter-event', () => {
- if (opt.CLOSE_WS_BUTTON_MODE && (!Meta.prefs_get_dynamic_workspaces() || (Meta.prefs_get_dynamic_workspaces() && global.workspace_manager.get_n_workspaces() - 1 !== this.metaWorkspace.index())))
- this._closeButton.opacity = 200;
- if (opt.SHOW_WST_LABELS_ON_HOVER) {
- this._wsLabel.ease({
- duration: 100,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- opacity: this._wsLabel._maxOpacity,
- });
- }
- });
-
- this.connect('leave-event', () => {
- this._closeButton.opacity = 0;
- if (opt.SHOW_WST_LABELS_ON_HOVER) {
- this._wsLabel.ease({
- duration: 100,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- opacity: 0,
- });
- }
- });
-
- if (opt.SHOW_WS_TMB_BG) {
- this._bgManager = new Background.BackgroundManager({
- monitorIndex: this.monitorIndex,
- container: this._viewport,
- vignette: false,
- controlPosition: false,
- });
-
- this._viewport.set_child_below_sibling(this._bgManager.backgroundActor, null);
-
- // full brightness of the thumbnail bg draws unnecessary attention
- // there is a grey bg under the wallpaper
- this._bgManager.backgroundActor.opacity = 220;
- }
-
- this.connect('destroy', () => {
- if (this._wsIndexConId)
- this.metaWorkspace.disconnect(this._wsIndexConId);
-
- if (this._nWindowsConId)
- this.metaWorkspace.disconnect(this._nWindowsConId);
-
- if (this._updateLabelTimeout)
- GLib.source_remove(this._updateLabelTimeout);
-
- if (this._bgManager)
- this._bgManager.destroy();
- });
- },
-
- _closeWorkspace() {
- // CLOSE_WS_BUTTON_MODE 1: single click, 2: double-click, 3: Ctrl
-
- if (opt.CLOSE_WS_BUTTON_MODE === 2) {
- const doubleClickTime = Clutter.Settings.get_default().double_click_time;
- const clickDelay = Date.now() - this._lastCloseClickTime;
- if (clickDelay > doubleClickTime) {
- this._lastCloseClickTime = Date.now();
- return;
- }
- } else if (opt.CLOSE_WS_BUTTON_MODE === 3 && !Me.Util.isCtrlPressed()) {
- return;
- }
-
- // close windows on this monitor
- const windows = global.display.get_tab_list(0, null).filter(
- w => w.get_monitor() === this.monitorIndex && w.get_workspace() === this.metaWorkspace
- );
-
- for (let i = 0; i < windows.length; i++) {
- if (!windows[i].is_on_all_workspaces())
- windows[i].delete(global.get_current_time() + i);
- }
- },
-
- activate(time) {
- if (this.state > ThumbnailState.NORMAL)
- return;
-
- // if Static Workspace overview mode active, a click on the already active workspace should activate the window picker mode
- const wsIndex = this.metaWorkspace.index();
- const lastWsIndex = global.display.get_workspace_manager().get_n_workspaces() - 1;
- const stateAdjustment = Main.overview._overview.controls._stateAdjustment;
-
- if (stateAdjustment.value === ControlsState.APP_GRID) {
- if (this.metaWorkspace.active) {
- Main.overview._overview.controls._shiftState(Meta.MotionDirection.DOWN);
- // if searchActive, hide it immediately
- Main.overview.searchEntry.set_text('');
- } else {
- this.metaWorkspace.activate(time);
- }
- } else if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && wsIndex < lastWsIndex) {
- if (stateAdjustment.value > 1)
- stateAdjustment.value = 1;
-
-
- // spread windows
- // in OVERVIEW MODE 2 windows are not spread and workspace is not scaled
- // we need to repeat transition to the overview state 1 (window picker), but with spreading windows animation
- if (this.metaWorkspace.active) {
- Main.overview._overview.controls._searchController._setSearchActive(false);
- opt.WORKSPACE_MODE = 1;
- // setting value to 0 would reset WORKSPACE_MODE
- stateAdjustment.value = 0.01;
- stateAdjustment.ease(1, {
- duration: 200,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- });
- } else {
- // switch ws
- this.metaWorkspace.activate(time);
- }
- // a click on the current workspace should go back to the main view
- } else if (this.metaWorkspace.active) {
- Main.overview.hide();
- } else {
- this.metaWorkspace.activate(time);
- }
- },
-
- // Draggable target interface used only by ThumbnailsBox
- handleDragOverInternal(source, actor, time) {
- if (source === Main.xdndHandler) {
- this.metaWorkspace.activate(time);
- return DND.DragMotionResult.CONTINUE;
- }
-
- if (this.state > ThumbnailState.NORMAL)
- return DND.DragMotionResult.CONTINUE;
-
- if (source.metaWindow &&
- !this._isMyWindow(source.metaWindow.get_compositor_private()))
- return DND.DragMotionResult.MOVE_DROP;
- if (source.app && source.app.can_open_new_window())
- return DND.DragMotionResult.COPY_DROP;
- if (!source.app && source.shellWorkspaceLaunch)
- return DND.DragMotionResult.COPY_DROP;
-
- if (source instanceof AppDisplay.FolderIcon)
- return DND.DragMotionResult.COPY_DROP;
-
-
- return DND.DragMotionResult.CONTINUE;
- },
-
- acceptDropInternal(source, actor, time) {
- if (this.state > ThumbnailState.NORMAL)
- return false;
-
- if (source.metaWindow) {
- let win = source.metaWindow.get_compositor_private();
- if (this._isMyWindow(win))
- return false;
-
- let metaWindow = win.get_meta_window();
- Main.moveWindowToMonitorAndWorkspace(metaWindow,
- this.monitorIndex, this.metaWorkspace.index());
- return true;
- } else if (source.app && source.app.can_open_new_window()) {
- if (source.animateLaunchAtPos)
- source.animateLaunchAtPos(actor.x, actor.y);
-
- source.app.open_new_window(this.metaWorkspace.index());
- return true;
- } else if (!source.app && source.shellWorkspaceLaunch) {
- // While unused in our own drag sources, shellWorkspaceLaunch allows
- // extensions to define custom actions for their drag sources.
- source.shellWorkspaceLaunch({
- workspace: this.metaWorkspace.index(),
- timestamp: time,
- });
- return true;
- } else if (source instanceof AppDisplay.FolderIcon) {
- for (let app of source.view._apps) {
- // const app = Shell.AppSystem.get_default().lookup_app(id);
- app.open_new_window(this.metaWorkspace.index());
- }
- }
-
- return false;
- },
-};
-
-const ThumbnailsBoxCommon = {
- after__init(scrollAdjustment, monitorIndex, orientation = opt.ORIENTATION) {
- this._boxOrientation = orientation;
- },
-
- _activateThumbnailAtPoint(stageX, stageY, time, activateCurrent = false) {
- if (activateCurrent) {
- const thumbnail = this._thumbnails.find(t => t.metaWorkspace.active);
- if (thumbnail)
- thumbnail.activate(time);
- return;
- }
- const [r_, x, y] = this.transform_stage_point(stageX, stageY);
-
- let thumbnail;
-
- if (this._boxOrientation)
- thumbnail = this._thumbnails.find(t => y >= t.y && y <= t.y + t.height);
- else
- thumbnail = this._thumbnails.find(t => x >= t.x && x <= t.x + t.width);
-
- if (thumbnail)
- thumbnail.activate(time);
- },
-
- acceptDrop(source, actor, x, y, time) {
- if (this._dropWorkspace !== -1) {
- return this._thumbnails[this._dropWorkspace].acceptDropInternal(source, actor, time);
- } else if (this._dropPlaceholderPos !== -1) {
- if (!source.metaWindow &&
- (!source.app || !source.app.can_open_new_window()) &&
- (source.app || !source.shellWorkspaceLaunch) &&
- !(source instanceof AppDisplay.FolderIcon))
- return false;
-
-
- let isWindow = !!source.metaWindow;
-
- let newWorkspaceIndex;
- [newWorkspaceIndex, this._dropPlaceholderPos] = [this._dropPlaceholderPos, -1];
- this._spliceIndex = newWorkspaceIndex;
-
- Main.wm.insertWorkspace(newWorkspaceIndex);
-
- if (isWindow) {
- // Move the window to our monitor first if necessary.
- let thumbMonitor = this._thumbnails[newWorkspaceIndex].monitorIndex;
- Main.moveWindowToMonitorAndWorkspace(source.metaWindow,
- thumbMonitor, newWorkspaceIndex, true);
- } else if (source.app && source.app.can_open_new_window()) {
- if (source.animateLaunchAtPos)
- source.animateLaunchAtPos(actor.x, actor.y);
-
- source.app.open_new_window(newWorkspaceIndex);
- } else if (!source.app && source.shellWorkspaceLaunch) {
- // While unused in our own drag sources, shellWorkspaceLaunch allows
- // extensions to define custom actions for their drag sources.
- source.shellWorkspaceLaunch({
- workspace: newWorkspaceIndex,
- timestamp: time,
- });
- } else if (source instanceof AppDisplay.FolderIcon) {
- for (let app of source.view._apps) {
- // const app = Shell.AppSystem.get_default().lookup_app(id);
- app.open_new_window(newWorkspaceIndex);
- }
- }
-
- if (source.app || (!source.app && source.shellWorkspaceLaunch)) {
- // This new workspace will be automatically removed if the application fails
- // to open its first window within some time, as tracked by Shell.WindowTracker.
- // Here, we only add a very brief timeout to avoid the _immediate_ removal of the
- // workspace while we wait for the startup sequence to load.
- let workspaceManager = global.workspace_manager;
- Main.wm.keepWorkspaceAlive(workspaceManager.get_workspace_by_index(newWorkspaceIndex),
- WORKSPACE_KEEP_ALIVE_TIME);
- }
-
- // Start the animation on the workspace (which is actually
- // an old one which just became empty)
- let thumbnail = this._thumbnails[newWorkspaceIndex];
- this._setThumbnailState(thumbnail, ThumbnailState.NEW);
- thumbnail.slide_position = 1;
- thumbnail.collapse_fraction = 1;
-
- this._queueUpdateStates();
-
- return true;
- } else {
- return false;
- }
- },
-
- handleDragOver(source, actor, x, y, time) {
- // switch axis for vertical orientation
- if (this._boxOrientation)
- x = y;
-
- if (!source.metaWindow &&
- (!source.app || !source.app.can_open_new_window()) &&
- (source.app || !source.shellWorkspaceLaunch) &&
- source !== Main.xdndHandler && !(source instanceof AppDisplay.FolderIcon))
- return DND.DragMotionResult.CONTINUE;
-
- const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
- let canCreateWorkspaces = Meta.prefs_get_dynamic_workspaces();
- let spacing = this.get_theme_node().get_length('spacing');
-
- this._dropWorkspace = -1;
- let placeholderPos = -1;
- let length = this._thumbnails.length;
- for (let i = 0; i < length; i++) {
- const index = rtl ? length - i - 1 : i;
-
- if (canCreateWorkspaces && source !== Main.xdndHandler) {
- const [targetStart, targetEnd] =
- this._getPlaceholderTarget(index, spacing, rtl);
-
- if (x > targetStart && x <= targetEnd) {
- placeholderPos = index;
- break;
- }
- }
-
- if (this._withinWorkspace(x, index, rtl)) {
- this._dropWorkspace = index;
- break;
- }
- }
-
- if (this._dropPlaceholderPos !== placeholderPos) {
- this._dropPlaceholderPos = placeholderPos;
- this.queue_relayout();
- }
-
- if (this._dropWorkspace !== -1)
- return this._thumbnails[this._dropWorkspace].handleDragOverInternal(source, actor, time);
- else if (this._dropPlaceholderPos !== -1)
- return source.metaWindow ? DND.DragMotionResult.MOVE_DROP : DND.DragMotionResult.COPY_DROP;
- else
- return DND.DragMotionResult.CONTINUE;
- },
-
- _updateStates() {
- const controlsManager = Main.overview._overview.controls;
- const { currentState } = controlsManager._stateAdjustment.getStateTransitionParams();
- this.SLIDE_ANIMATION_TIME = 200;
- this.RESCALE_ANIMATION_TIME = 200;
- // remove rescale animation during this scale transition, it is redundant and delayed
- if ((currentState < 2 && currentState > 1) || controlsManager._searchController.searchActive)
- this.RESCALE_ANIMATION_TIME = 0;
-
- this._updateStateId = 0;
-
- // If we are animating the indicator, wait
- if (this._animatingIndicator)
- return;
-
- // Likewise if we are in the process of hiding
- if (!this._shouldShow && this.visible)
- return;
-
- // Then slide out any thumbnails that have been destroyed
- this._iterateStateThumbnails(ThumbnailState.REMOVING, thumbnail => {
- this._setThumbnailState(thumbnail, ThumbnailState.ANIMATING_OUT);
-
- thumbnail.ease_property('slide-position', 1, {
- duration: this.SLIDE_ANIMATION_TIME,
- mode: Clutter.AnimationMode.LINEAR,
- onComplete: () => {
- this._setThumbnailState(thumbnail, ThumbnailState.ANIMATED_OUT);
- this._queueUpdateStates();
- },
- });
- });
-
- // As long as things are sliding out, don't proceed
- if (this._stateCounts[ThumbnailState.ANIMATING_OUT] > 0)
- return;
-
- // Once that's complete, we can start scaling to the new size,
- // collapse any removed thumbnails and expand added ones
- this._iterateStateThumbnails(ThumbnailState.ANIMATED_OUT, thumbnail => {
- this._setThumbnailState(thumbnail, ThumbnailState.COLLAPSING);
- thumbnail.ease_property('collapse-fraction', 1, {
- duration: this.RESCALE_ANIMATION_TIME,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- onComplete: () => {
- this._stateCounts[thumbnail.state]--;
- thumbnail.state = ThumbnailState.DESTROYED;
-
- let index = this._thumbnails.indexOf(thumbnail);
- this._thumbnails.splice(index, 1);
- thumbnail.destroy();
-
- this._queueUpdateStates();
- },
- });
- });
-
- this._iterateStateThumbnails(ThumbnailState.NEW, thumbnail => {
- this._setThumbnailState(thumbnail, ThumbnailState.EXPANDING);
- thumbnail.ease_property('collapse-fraction', 0, {
- duration: this.SLIDE_ANIMATION_TIME,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- onComplete: () => {
- this._setThumbnailState(thumbnail, ThumbnailState.EXPANDED);
- this._queueUpdateStates();
- },
- });
- });
-
- if (this._pendingScaleUpdate) {
- this.ease_property('scale', this._targetScale, {
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- duration: this.RESCALE_ANIMATION_TIME,
- onComplete: () => this._queueUpdateStates(),
- });
- this._queueUpdateStates();
- this._pendingScaleUpdate = false;
- }
-
- // Wait until that's done
- if (this._scale !== this._targetScale ||
- this._stateCounts[ThumbnailState.COLLAPSING] > 0 ||
- this._stateCounts[ThumbnailState.EXPANDING] > 0)
- return;
-
- // And then slide in any new thumbnails
- this._iterateStateThumbnails(ThumbnailState.EXPANDED, thumbnail => {
- this._setThumbnailState(thumbnail, ThumbnailState.ANIMATING_IN);
- thumbnail.ease_property('slide-position', 0, {
- duration: this.SLIDE_ANIMATION_TIME,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- onComplete: () => {
- this._setThumbnailState(thumbnail, ThumbnailState.NORMAL);
- },
- });
- });
- },
-
- _getPlaceholderTarget(...args) {
- if (this._boxOrientation)
- return ThumbnailsBoxVertical._getPlaceholderTarget.bind(this)(...args);
- else
- return ThumbnailsBoxHorizontal._getPlaceholderTarget.bind(this)(...args);
- },
-
- _withinWorkspace(...args) {
- if (this._boxOrientation)
- return ThumbnailsBoxVertical._withinWorkspace.bind(this)(...args);
- else
- return ThumbnailsBoxHorizontal._withinWorkspace.bind(this)(...args);
- },
-
- vfunc_get_preferred_width(...args) {
- if (this._boxOrientation)
- return ThumbnailsBoxVertical.vfunc_get_preferred_width.bind(this)(...args);
- else
- return ThumbnailsBoxHorizontal.vfunc_get_preferred_width.bind(this)(...args);
- },
-
- vfunc_get_preferred_height(...args) {
- if (this._boxOrientation)
- return ThumbnailsBoxVertical.vfunc_get_preferred_height.bind(this)(...args);
- else
- return ThumbnailsBoxHorizontal.vfunc_get_preferred_height.bind(this)(...args);
- },
-
- vfunc_allocate(...args) {
- if (this._boxOrientation)
- return ThumbnailsBoxVertical.vfunc_allocate.bind(this)(...args);
- else
- return ThumbnailsBoxHorizontal.vfunc_allocate.bind(this)(...args);
- },
-
- _updateShouldShow(...args) {
- if (this._boxOrientation)
- return ThumbnailsBoxVertical._updateShouldShow.bind(this)(...args);
- else
- return ThumbnailsBoxHorizontal._updateShouldShow.bind(this)(...args);
- },
-};
-
-const ThumbnailsBoxVertical = {
- _getPlaceholderTarget(index, spacing, rtl) {
- this._dropPlaceholder.add_style_class_name('placeholder-vertical');
- const workspace = this._thumbnails[index];
-
- let targetY1;
- let targetY2;
-
- if (rtl) {
- const baseY = workspace.y + workspace.height;
- targetY1 = baseY - WORKSPACE_CUT_SIZE;
- targetY2 = baseY + spacing + WORKSPACE_CUT_SIZE;
- } else {
- targetY1 = workspace.y - spacing - WORKSPACE_CUT_SIZE;
- targetY2 = workspace.y + WORKSPACE_CUT_SIZE;
- }
-
- if (index === 0) {
- if (rtl)
- targetY2 -= spacing + WORKSPACE_CUT_SIZE;
- else
- targetY1 += spacing + WORKSPACE_CUT_SIZE;
- }
-
- if (index === this._dropPlaceholderPos) {
- const placeholderHeight = this._dropPlaceholder.get_height() + spacing;
- if (rtl)
- targetY2 += placeholderHeight;
- else
- targetY1 -= placeholderHeight;
- }
-
- return [targetY1, targetY2];
- },
-
- _withinWorkspace(y, index, rtl) {
- const length = this._thumbnails.length;
- const workspace = this._thumbnails[index];
-
- let workspaceY1 = workspace.y + WORKSPACE_CUT_SIZE;
- let workspaceY2 = workspace.y + workspace.height - WORKSPACE_CUT_SIZE;
-
- if (index === length - 1) {
- if (rtl)
- workspaceY1 -= WORKSPACE_CUT_SIZE;
- else
- workspaceY2 += WORKSPACE_CUT_SIZE;
- }
-
- return y > workspaceY1 && y <= workspaceY2;
- },
-
- vfunc_get_preferred_width(forHeight) {
- if (forHeight < 10)
- return [this._porthole.width, this._porthole.width];
-
- let themeNode = this.get_theme_node();
-
- forHeight = themeNode.adjust_for_width(forHeight);
-
- let spacing = themeNode.get_length('spacing');
- let nWorkspaces = this._thumbnails.length;
- let totalSpacing = (nWorkspaces - 1) * spacing;
-
- const avail = forHeight - totalSpacing;
-
- let scale = (avail / nWorkspaces) / this._porthole.height;
-
- const width = Math.round(this._porthole.width * scale);
- return themeNode.adjust_preferred_height(width, width);
- },
-
- vfunc_get_preferred_height(forWidth) {
- if (forWidth < 10)
- return [0, this._porthole.height];
- let themeNode = this.get_theme_node();
-
- let spacing = themeNode.get_length('spacing');
- let nWorkspaces = this._thumbnails.length;
-
- // remove also top/bottom box padding
- let totalSpacing = (nWorkspaces - 3) * spacing;
-
- const ratio = this._porthole.width / this._porthole.height;
- const tmbHeight = themeNode.adjust_for_width(forWidth) / ratio;
-
- const naturalheight = this._thumbnails.reduce((accumulator, thumbnail/* , index*/) => {
- const progress = 1 - thumbnail.collapse_fraction;
- const height = tmbHeight * progress;
- return accumulator + height;
- }, 0);
- return themeNode.adjust_preferred_width(totalSpacing, Math.round(naturalheight));
- },
-
- // removes extra space (extraWidth in the original function), we need the box as accurate as possible
- // for precise app grid transition animation
- vfunc_allocate(box) {
- this.set_allocation(box);
-
- let rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
-
- if (this._thumbnails.length === 0) // not visible
- return;
-
- let themeNode = this.get_theme_node();
- box = themeNode.get_content_box(box);
-
- const portholeWidth = this._porthole.width;
- const portholeHeight = this._porthole.height;
- const spacing = themeNode.get_length('spacing');
-
- /* const nWorkspaces = this._thumbnails.length;*/
-
- // Compute the scale we'll need once everything is updated,
- // unless we are currently transitioning
- if (this._expandFraction === 1) {
- // remove size "breathing" during adding/removing workspaces
-
- /* const totalSpacing = (nWorkspaces - 1) * spacing;
- const availableHeight = (box.get_height() - totalSpacing) / nWorkspaces; */
-
- const hScale = box.get_width() / portholeWidth;
- /* const vScale = availableHeight / portholeHeight;*/
- const vScale = box.get_height() / portholeHeight;
- const newScale = Math.min(hScale, vScale);
-
- if (newScale !== this._targetScale) {
- if (this._targetScale > 0) {
- // We don't ease immediately because we need to observe the
- // ordering in queueUpdateStates - if workspaces have been
- // removed we need to slide them out as the first thing.
- this._targetScale = newScale;
- this._pendingScaleUpdate = true;
- } else {
- this._targetScale = this._scale = newScale;
- }
-
- this._queueUpdateStates();
- }
- }
-
- const ratio = portholeWidth / portholeHeight;
- const thumbnailFullHeight = Math.round(portholeHeight * this._scale);
- const thumbnailWidth = Math.round(thumbnailFullHeight * ratio);
- const thumbnailHeight = thumbnailFullHeight * this._expandFraction;
- const roundedVScale = thumbnailHeight / portholeHeight;
-
- let indicatorValue = this._scrollAdjustment.value;
- let indicatorUpperWs = Math.ceil(indicatorValue);
- let indicatorLowerWs = Math.floor(indicatorValue);
-
- let indicatorLowerY1 = 0;
- let indicatorLowerY2 = 0;
- let indicatorUpperY1 = 0;
- let indicatorUpperY2 = 0;
-
- let indicatorThemeNode = this._indicator.get_theme_node();
- let indicatorTopFullBorder = indicatorThemeNode.get_padding(St.Side.TOP) + indicatorThemeNode.get_border_width(St.Side.TOP);
- let indicatorBottomFullBorder = indicatorThemeNode.get_padding(St.Side.BOTTOM) + indicatorThemeNode.get_border_width(St.Side.BOTTOM);
- let indicatorLeftFullBorder = indicatorThemeNode.get_padding(St.Side.LEFT) + indicatorThemeNode.get_border_width(St.Side.LEFT);
- let indicatorRightFullBorder = indicatorThemeNode.get_padding(St.Side.RIGHT) + indicatorThemeNode.get_border_width(St.Side.RIGHT);
-
- let y = box.y1;
-
- if (this._dropPlaceholderPos === -1) {
- this._dropPlaceholder.allocate_preferred_size(
- ...this._dropPlaceholder.get_position());
-
- const laters = global.compositor.get_laters();
- laters.add(Meta.LaterType.BEFORE_REDRAW, () => {
- this._dropPlaceholder.hide();
- });
- }
-
- let childBox = new Clutter.ActorBox();
-
- for (let i = 0; i < this._thumbnails.length; i++) {
- const thumbnail = this._thumbnails[i];
- if (i > 0)
- y += spacing - Math.round(thumbnail.collapse_fraction * spacing);
-
- const x1 = box.x1;
- const x2 = x1 + thumbnailWidth;
-
- if (i === this._dropPlaceholderPos) {
- let [, placeholderHeight] = this._dropPlaceholder.get_preferred_width(-1);
- childBox.x1 = x1;
- childBox.x2 = x2;
-
- if (rtl) {
- childBox.y2 = box.y2 - Math.round(y);
- childBox.y1 = box.y2 - Math.round(y + placeholderHeight);
- } else {
- childBox.y1 = Math.round(y);
- childBox.y2 = Math.round(y + placeholderHeight);
- }
-
- this._dropPlaceholder.allocate(childBox);
-
- const laters = global.compositor.get_laters();
- laters.add(Meta.LaterType.BEFORE_REDRAW, () => {
- this._dropPlaceholder.show();
- });
- y += placeholderHeight + spacing;
- }
-
- // We might end up with thumbnailWidth being something like 99.33
- // pixels. To make this work and not end up with a gap at the end,
- // we need some thumbnails to be 99 pixels and some 100 pixels width;
- // we compute an actual scale separately for each thumbnail.
- const y1 = Math.round(y);
- const y2 = Math.round(y + thumbnailHeight);
- const roundedHScale = (y2 - y1) / portholeHeight;
-
- // Allocating a scaled actor is funny - x1/y1 correspond to the origin
- // of the actor, but x2/y2 are increased by the *unscaled* size.
- if (rtl) {
- childBox.y2 = box.y2 - y1;
- childBox.y1 = box.y2 - (y1 + thumbnailHeight);
- } else {
- childBox.y1 = y1;
- childBox.y2 = y1 + thumbnailHeight;
- }
- childBox.x1 = x1;
- childBox.x2 = x1 + thumbnailWidth;
-
- thumbnail.setScale(roundedHScale, roundedVScale);
- thumbnail.allocate(childBox);
-
- if (i === indicatorUpperWs) {
- indicatorUpperY1 = childBox.y1;
- indicatorUpperY2 = childBox.y2;
- }
- if (i === indicatorLowerWs) {
- indicatorLowerY1 = childBox.y1;
- indicatorLowerY2 = childBox.y2;
- }
-
- // We round the collapsing portion so that we don't get thumbnails resizing
- // during an animation due to differences in rounded, but leave the uncollapsed
- // portion unrounded so that non-animating we end up with the right total
- y += thumbnailHeight - Math.round(thumbnailHeight * thumbnail.collapse_fraction);
- }
-
- childBox.x1 = box.x1;
- childBox.x2 = box.x1 + thumbnailWidth;
-
- const indicatorY1 = indicatorLowerY1 +
- (indicatorUpperY1 - indicatorLowerY1) * (indicatorValue % 1);
- const indicatorY2 = indicatorLowerY2 +
- (indicatorUpperY2 - indicatorLowerY2) * (indicatorValue % 1);
-
- childBox.y1 = indicatorY1 - indicatorTopFullBorder;
- childBox.y2 = indicatorY2 + indicatorBottomFullBorder;
- childBox.x1 -= indicatorLeftFullBorder;
- childBox.x2 += indicatorRightFullBorder;
- this._indicator.allocate(childBox);
- },
-
- _updateShouldShow() {
- const shouldShow = opt.SHOW_WS_TMB;
- if (this._shouldShow === shouldShow)
- return;
-
- this._shouldShow = shouldShow;
- this.notify('should-show');
- },
-};
-
-// ThumbnailsBox Horizontal
-
-const ThumbnailsBoxHorizontal = {
- _getPlaceholderTarget(index, spacing, rtl) {
- const workspace = this._thumbnails[index];
-
- let targetX1;
- let targetX2;
-
- if (rtl) {
- const baseX = workspace.x + workspace.width;
- targetX1 = baseX - WORKSPACE_CUT_SIZE;
- targetX2 = baseX + spacing + WORKSPACE_CUT_SIZE;
- } else {
- targetX1 = workspace.x - spacing - WORKSPACE_CUT_SIZE;
- targetX2 = workspace.x + WORKSPACE_CUT_SIZE;
- }
-
- if (index === 0) {
- if (rtl)
- targetX2 -= spacing + WORKSPACE_CUT_SIZE;
- else
- targetX1 += spacing + WORKSPACE_CUT_SIZE;
- }
-
- if (index === this._dropPlaceholderPos) {
- const placeholderWidth = this._dropPlaceholder.get_width() + spacing;
- if (rtl)
- targetX2 += placeholderWidth;
- else
- targetX1 -= placeholderWidth;
- }
-
- return [targetX1, targetX2];
- },
-
- _withinWorkspace(x, index, rtl) {
- const length = this._thumbnails.length;
- const workspace = this._thumbnails[index];
-
- let workspaceX1 = workspace.x + WORKSPACE_CUT_SIZE;
- let workspaceX2 = workspace.x + workspace.width - WORKSPACE_CUT_SIZE;
-
- if (index === length - 1) {
- if (rtl)
- workspaceX1 -= WORKSPACE_CUT_SIZE;
- else
- workspaceX2 += WORKSPACE_CUT_SIZE;
- }
-
- return x > workspaceX1 && x <= workspaceX2;
- },
-
- vfunc_get_preferred_height(forWidth) {
- if (forWidth < 10)
- return [this._porthole.height, this._porthole.height];
-
- let themeNode = this.get_theme_node();
-
- forWidth = themeNode.adjust_for_width(forWidth);
-
- let spacing = themeNode.get_length('spacing');
- let nWorkspaces = this._thumbnails.length;
- let totalSpacing = (nWorkspaces - 1) * spacing;
-
- const avail = forWidth - totalSpacing;
-
- let scale = (avail / nWorkspaces) / this._porthole.width;
-
- const height = Math.round(this._porthole.height * scale);
-
- return themeNode.adjust_preferred_height(height, height);
- },
-
- vfunc_get_preferred_width(forHeight) {
- if (forHeight < 10)
- return [0, this._porthole.width];
-
- let themeNode = this.get_theme_node();
-
- let spacing = themeNode.get_length('spacing');
- let nWorkspaces = this._thumbnails.length;
- // remove also left/right box padding from the total spacing
- let totalSpacing = (nWorkspaces - 3) * spacing;
-
- const ratio = this._porthole.height / this._porthole.width;
-
- const tmbWidth = themeNode.adjust_for_height(forHeight) / ratio;
-
- const naturalWidth = this._thumbnails.reduce((accumulator, thumbnail) => {
- const progress = 1 - thumbnail.collapse_fraction;
- const width = tmbWidth * progress;
- return accumulator + width;
- }, 0);
-
- return themeNode.adjust_preferred_width(totalSpacing, naturalWidth);
- },
-
- vfunc_allocate(box) {
- this.set_allocation(box);
-
- let rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
-
- if (this._thumbnails.length === 0) // not visible
- return;
-
- let themeNode = this.get_theme_node();
- box = themeNode.get_content_box(box);
-
- const portholeWidth = this._porthole.width;
- const portholeHeight = this._porthole.height;
- const spacing = themeNode.get_length('spacing');
-
- /* const nWorkspaces = this._thumbnails.length; */
-
- // Compute the scale we'll need once everything is updated,
- // unless we are currently transitioning
- if (this._expandFraction === 1) {
- // remove size "breathing" during adding/removing workspaces
-
- /* const totalSpacing = (nWorkspaces - 1) * spacing;
- const availableWidth = (box.get_width() - totalSpacing) / nWorkspaces;
-
- const hScale = availableWidth / portholeWidth; */
- const hScale = box.get_width() / portholeWidth;
- const vScale = box.get_height() / portholeHeight;
- const newScale = Math.min(hScale, vScale);
-
- if (newScale !== this._targetScale) {
- if (this._targetScale > 0) {
- // We don't ease immediately because we need to observe the
- // ordering in queueUpdateStates - if workspaces have been
- // removed we need to slide them out as the first thing.
- this._targetScale = newScale;
- this._pendingScaleUpdate = true;
- } else {
- this._targetScale = this._scale = newScale;
- }
-
- this._queueUpdateStates();
- }
- }
-
- const ratio = portholeWidth / portholeHeight;
- const thumbnailFullHeight = Math.round(portholeHeight * this._scale);
- const thumbnailWidth = Math.round(thumbnailFullHeight * ratio);
- const thumbnailHeight = thumbnailFullHeight * this._expandFraction;
- const roundedVScale = thumbnailHeight / portholeHeight;
-
- let indicatorValue = this._scrollAdjustment.value;
- let indicatorUpperWs = Math.ceil(indicatorValue);
- let indicatorLowerWs = Math.floor(indicatorValue);
-
- let indicatorLowerX1 = 0;
- let indicatorLowerX2 = 0;
- let indicatorUpperX1 = 0;
- let indicatorUpperX2 = 0;
-
- let indicatorThemeNode = this._indicator.get_theme_node();
- let indicatorTopFullBorder = indicatorThemeNode.get_padding(St.Side.TOP) + indicatorThemeNode.get_border_width(St.Side.TOP);
- let indicatorBottomFullBorder = indicatorThemeNode.get_padding(St.Side.BOTTOM) + indicatorThemeNode.get_border_width(St.Side.BOTTOM);
- let indicatorLeftFullBorder = indicatorThemeNode.get_padding(St.Side.LEFT) + indicatorThemeNode.get_border_width(St.Side.LEFT);
- let indicatorRightFullBorder = indicatorThemeNode.get_padding(St.Side.RIGHT) + indicatorThemeNode.get_border_width(St.Side.RIGHT);
-
- let x = box.x1;
-
- if (this._dropPlaceholderPos === -1) {
- this._dropPlaceholder.allocate_preferred_size(
- ...this._dropPlaceholder.get_position());
-
- const laters = global.compositor.get_laters();
- laters.add(Meta.LaterType.BEFORE_REDRAW, () => {
- this._dropPlaceholder.hide();
- });
- }
-
- let childBox = new Clutter.ActorBox();
-
- for (let i = 0; i < this._thumbnails.length; i++) {
- const thumbnail = this._thumbnails[i];
- if (i > 0)
- x += spacing - Math.round(thumbnail.collapse_fraction * spacing);
-
- const y1 = box.y1;
- const y2 = y1 + thumbnailHeight;
-
- if (i === this._dropPlaceholderPos) {
- const [, placeholderWidth] = this._dropPlaceholder.get_preferred_width(-1);
- childBox.y1 = y1;
- childBox.y2 = y2;
-
- if (rtl) {
- childBox.x2 = box.x2 - Math.round(x);
- childBox.x1 = box.x2 - Math.round(x + placeholderWidth);
- } else {
- childBox.x1 = Math.round(x);
- childBox.x2 = Math.round(x + placeholderWidth);
- }
-
- this._dropPlaceholder.allocate(childBox);
-
- const laters = global.compositor.get_laters();
- laters.add(Meta.LaterType.BEFORE_REDRAW, () => {
- this._dropPlaceholder.show();
- });
- x += placeholderWidth + spacing;
- }
-
- // We might end up with thumbnailWidth being something like 99.33
- // pixels. To make this work and not end up with a gap at the end,
- // we need some thumbnails to be 99 pixels and some 100 pixels width;
- // we compute an actual scale separately for each thumbnail.
- const x1 = Math.round(x);
- const x2 = Math.round(x + thumbnailWidth);
- const roundedHScale = (x2 - x1) / portholeWidth;
-
- // Allocating a scaled actor is funny - x1/y1 correspond to the origin
- // of the actor, but x2/y2 are increased by the *unscaled* size.
- if (rtl) {
- childBox.x2 = box.x2 - x1;
- childBox.x1 = box.x2 - (x1 + thumbnailWidth);
- } else {
- childBox.x1 = x1;
- childBox.x2 = x1 + thumbnailWidth;
- }
- childBox.y1 = y1;
- childBox.y2 = y1 + thumbnailHeight;
-
- thumbnail.setScale(roundedHScale, roundedVScale);
- thumbnail.allocate(childBox);
-
- if (i === indicatorUpperWs) {
- indicatorUpperX1 = childBox.x1;
- indicatorUpperX2 = childBox.x2;
- }
- if (i === indicatorLowerWs) {
- indicatorLowerX1 = childBox.x1;
- indicatorLowerX2 = childBox.x2;
- }
-
- // We round the collapsing portion so that we don't get thumbnails resizing
- // during an animation due to differences in rounded, but leave the uncollapsed
- // portion unrounded so that non-animating we end up with the right total
- x += thumbnailWidth - Math.round(thumbnailWidth * thumbnail.collapse_fraction);
- }
-
- childBox.y1 = box.y1;
- childBox.y2 = box.y1 + thumbnailHeight;
-
- const indicatorX1 = indicatorLowerX1 +
- (indicatorUpperX1 - indicatorLowerX1) * (indicatorValue % 1);
- const indicatorX2 = indicatorLowerX2 +
- (indicatorUpperX2 - indicatorLowerX2) * (indicatorValue % 1);
-
- childBox.x1 = indicatorX1 - indicatorLeftFullBorder;
- childBox.x2 = indicatorX2 + indicatorRightFullBorder;
- childBox.y1 -= indicatorTopFullBorder;
- childBox.y2 += indicatorBottomFullBorder;
- this._indicator.allocate(childBox);
- },
-
- _updateShouldShow: ThumbnailsBoxVertical._updateShouldShow,
-};