1633 lines
72 KiB
JavaScript
1633 lines
72 KiB
JavaScript
/**
|
|
* V-Shell (Vertical Workspaces)
|
|
* overviewControls.js
|
|
*
|
|
* @author GdH <G-dH@github.com>
|
|
* @copyright 2022 - 2024
|
|
* @license GPL-3.0
|
|
*
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
import Clutter from 'gi://Clutter';
|
|
import GLib from 'gi://GLib';
|
|
import GObject from 'gi://GObject';
|
|
import St from 'gi://St';
|
|
import Shell from 'gi://Shell';
|
|
|
|
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
|
import * as Overview from 'resource:///org/gnome/shell/ui/overview.js';
|
|
import * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js';
|
|
import * as WorkspacesView from 'resource:///org/gnome/shell/ui/workspacesView.js';
|
|
import * as Background from 'resource:///org/gnome/shell/ui/background.js';
|
|
import * as Util from 'resource:///org/gnome/shell/misc/util.js';
|
|
|
|
let Me;
|
|
let opt;
|
|
// gettext
|
|
let _;
|
|
|
|
const ControlsState = OverviewControls.ControlsState;
|
|
const FitMode = WorkspacesView.FitMode;
|
|
|
|
const STARTUP_ANIMATION_TIME = 500;
|
|
const ANIMATION_TIME = Overview.ANIMATION_TIME;
|
|
const DASH_MAX_SIZE_RATIO = 0.35;
|
|
|
|
let _timeouts;
|
|
|
|
export const OverviewControlsModule = class {
|
|
constructor(me) {
|
|
Me = me;
|
|
opt = Me.opt;
|
|
_ = Me.gettext;
|
|
|
|
this._firstActivation = true;
|
|
this.moduleEnabled = false;
|
|
this._overrides = null;
|
|
}
|
|
|
|
cleanGlobals() {
|
|
Me = null;
|
|
opt = null;
|
|
_ = null;
|
|
}
|
|
|
|
update(reset) {
|
|
this._removeTimeouts();
|
|
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(' OverviewControlsModule - Keeping untouched');
|
|
}
|
|
|
|
_activateModule() {
|
|
if (!this._overrides)
|
|
this._overrides = new Me.Util.Overrides();
|
|
|
|
_timeouts = {};
|
|
|
|
this._replaceOnSearchChanged();
|
|
|
|
this._overrides.addOverride('ControlsManager', OverviewControls.ControlsManager.prototype, ControlsManagerCommon);
|
|
this._overrides.addOverride('ControlsManagerLayoutCommon', Main.overview._overview.controls.layoutManager, ControlsManagerLayoutCommon);
|
|
if (opt.ORIENTATION === Clutter.Orientation.VERTICAL)
|
|
this._overrides.addOverride('ControlsManagerLayout', Main.overview._overview.controls.layoutManager, ControlsManagerLayoutVertical);
|
|
else
|
|
this._overrides.addOverride('ControlsManagerLayout', Main.overview._overview.controls.layoutManager, ControlsManagerLayoutHorizontal);
|
|
|
|
// Allow user to close the overview by clicking on an empty space on the primary monitor's overview
|
|
// Secondary monitors are handled in workspacesView
|
|
this._addClickToCloseOverview();
|
|
|
|
// Update custom workAreaBox
|
|
Main.overview._overview.controls.layoutManager._updateWorkAreaBox();
|
|
|
|
console.debug(' OverviewControlsModule - Activated');
|
|
}
|
|
|
|
_disableModule() {
|
|
if (this._overrides)
|
|
this._overrides.removeAll();
|
|
this._overrides = null;
|
|
|
|
const reset = true;
|
|
this._replaceOnSearchChanged(reset);
|
|
Main.overview._overview._controls._appDisplay.opacity = 255;
|
|
this._addClickToCloseOverview(reset);
|
|
|
|
console.debug(' OverviewControlsModule - Disabled');
|
|
}
|
|
|
|
_removeTimeouts() {
|
|
if (_timeouts) {
|
|
Object.values(_timeouts).forEach(t => {
|
|
if (t)
|
|
GLib.source_remove(t);
|
|
});
|
|
_timeouts = null;
|
|
}
|
|
}
|
|
|
|
_replaceOnSearchChanged(reset) {
|
|
const searchController = Main.overview.searchController;
|
|
if (reset) {
|
|
if (this._searchControllerSigId) {
|
|
searchController.disconnect(this._searchControllerSigId);
|
|
this._searchControllerSigId = 0;
|
|
}
|
|
if (this._originalSearchControllerSigId) {
|
|
searchController.unblock_signal_handler(this._originalSearchControllerSigId);
|
|
this._originalSearchControllerSigId = 0;
|
|
}
|
|
searchController._searchResults.translation_x = 0;
|
|
searchController._searchResults.translation_y = 0;
|
|
Main.overview.searchEntry.visible = true;
|
|
Main.overview.searchEntry.opacity = 255;
|
|
} else {
|
|
// reconnect signal to use custom function (callbacks cannot be overridden in class prototype, they are already in memory as a copy for the given callback)
|
|
if (!this._originalSearchControllerSigId) {
|
|
this._originalSearchControllerSigId = GObject.signal_handler_find(searchController, { signalId: 'notify', detail: 'search-active' });
|
|
if (this._originalSearchControllerSigId)
|
|
searchController.block_signal_handler(this._originalSearchControllerSigId);
|
|
}
|
|
|
|
if (!this._searchControllerSigId)
|
|
this._searchControllerSigId = searchController.connect('notify::search-active', () => Main.overview._overview.controls._onSearchChanged());
|
|
}
|
|
}
|
|
|
|
_addClickToCloseOverview(reset) {
|
|
const overview = Main.overview._overview;
|
|
|
|
overview.reactive = false;
|
|
if (this._clickEmptyConId) {
|
|
overview.disconnect(this._clickEmptyConId);
|
|
this._clickEmptyConId = 0;
|
|
}
|
|
|
|
if (reset || !opt.CLICK_EMPTY_CLOSE)
|
|
return;
|
|
|
|
overview.reactive = true;
|
|
this._clickEmptyConId = overview.connect('button-release-event', (actor, event) => {
|
|
const button = event.get_button();
|
|
const overviewState = overview.controls._stateAdjustment.value;
|
|
const buttonPrimary = button === Clutter.BUTTON_PRIMARY;
|
|
const buttonSecondary = button === Clutter.BUTTON_SECONDARY;
|
|
const buttonAny = buttonPrimary || buttonSecondary;
|
|
|
|
if ((overviewState === 1 && buttonAny) || (overviewState === 2 && buttonSecondary))
|
|
Main.overview.hide();
|
|
});
|
|
}
|
|
};
|
|
|
|
const ControlsManagerCommon = {
|
|
// this function is used as a callback by a signal handler, needs to be reconnected after modification as the original callback uses a copy of the original function
|
|
/* _update: function() {
|
|
...
|
|
}*/
|
|
|
|
prepareToEnterOverview() {
|
|
this._searchController.prepareToEnterOverview();
|
|
this._workspacesDisplay.prepareToEnterOverview();
|
|
// Workaround for thumbnailsBox not re-scaling after switching workspace outside of overview using a trackpad
|
|
this._thumbnailsBox._updateIndicator();
|
|
|
|
Main.overview._overview.controls.opacity = 255;
|
|
|
|
// Ensure that overview backgrounds are ready when needed
|
|
if (!this._bgManagers && (opt.SHOW_BG_IN_OVERVIEW || !opt.SHOW_WS_PREVIEW_BG))
|
|
this._setBackground();
|
|
else if (this._bgManagers && !(opt.SHOW_BG_IN_OVERVIEW || !opt.SHOW_WS_PREVIEW_BG))
|
|
this._setBackground(true);
|
|
|
|
// store pointer X coordinate for OVERVIEW_MODE 1 - to prevent immediate switch to WORKSPACE_MODE 1 if the mouse pointer is steady
|
|
opt.showingPointerX = global.get_pointer()[0];
|
|
},
|
|
|
|
// this function has duplicate in WorkspaceView so we use one function for both to avoid issues with syncing them
|
|
_getFitModeForState(state) {
|
|
return _getFitModeForState(state);
|
|
},
|
|
|
|
_updateThumbnailsBox() {
|
|
const { shouldShow } = this._thumbnailsBox;
|
|
const thumbnailsBoxVisible = shouldShow;
|
|
this._thumbnailsBox.visible = thumbnailsBoxVisible;
|
|
|
|
// this call should be directly in _update(), but it's used as a callback function and it would require to reconnect the signal
|
|
this._updateOverview();
|
|
},
|
|
|
|
// this function is pure addition to the original code and handles wsDisp transition to APP_GRID view
|
|
_updateOverview() {
|
|
this._workspacesDisplay.translation_x = 0;
|
|
this._workspacesDisplay.translation_y = 0;
|
|
this._workspacesDisplay.scale_x = 1;
|
|
this._workspacesDisplay.scale_y = 1;
|
|
const { initialState, finalState, progress, currentState } = this._stateAdjustment.getStateTransitionParams();
|
|
|
|
const paramsForState = s => {
|
|
let opacity;
|
|
switch (s) {
|
|
case ControlsState.HIDDEN:
|
|
case ControlsState.WINDOW_PICKER:
|
|
opacity = 255;
|
|
break;
|
|
case ControlsState.APP_GRID:
|
|
opacity = 0;
|
|
break;
|
|
default:
|
|
opacity = 255;
|
|
break;
|
|
}
|
|
return { opacity };
|
|
};
|
|
|
|
let initialParams = paramsForState(initialState);
|
|
let finalParams = paramsForState(finalState);
|
|
|
|
let opacity = Math.round(Util.lerp(initialParams.opacity, finalParams.opacity, progress));
|
|
|
|
let workspacesDisplayVisible = opacity !== 0;
|
|
|
|
// improve transition from search results to desktop
|
|
if (finalState === 0 && this._searchController._searchResults.visible)
|
|
this._searchController.hide();
|
|
|
|
// reset Static Workspace window picker mode
|
|
if (currentState === 0 && opt.OVERVIEW_MODE && opt.WORKSPACE_MODE)
|
|
opt.WORKSPACE_MODE = 0;
|
|
|
|
if (!opt.WS_ANIMATION || (!opt.SHOW_WS_TMB && opt.SHOW_WS_PREVIEW_BG)) {
|
|
this._workspacesDisplay.opacity = opacity;
|
|
} else if (!opt.SHOW_WS_TMB_BG && opt.SHOW_WS_PREVIEW_BG) {
|
|
// fade out ws wallpaper during transition to ws switcher if ws switcher background disabled
|
|
const workspaces = this._workspacesDisplay._workspacesViews[global.display.get_primary_monitor()]?._workspaces;
|
|
// Speed up the workspace background opacity transition
|
|
if (opt.WORKSPACE_MAX_SPACING < opt.WS_MAX_SPACING_OFF_SCREEN && workspaces)
|
|
// If workspacesDisplay max spacing is set so adjacent workspaces could be visible on the screen
|
|
workspaces.forEach(w => w._background.set_opacity(Math.max(0, opacity - (255 - opacity))));
|
|
else if (workspaces)
|
|
// If adjacent workspaces should not be visible on the screen, set the opacity only for the visible one
|
|
workspaces[this._workspaceAdjustment.value]?._background.set_opacity(Math.max(0, opacity - (255 - opacity)));
|
|
}
|
|
|
|
// if ws preview background is disabled, animate tmb box and dash
|
|
const tmbBox = this._thumbnailsBox;
|
|
const dash = this.dash;
|
|
const searchEntryBin = this._searchEntryBin;
|
|
// this dash transition collides with startup animation and freezes GS for good, needs to be delayed (first Main.overview 'hiding' event enables it)
|
|
const skipDash = Me.Util.dashNotDefault();
|
|
|
|
// OVERVIEW_MODE 2 should animate dash and wsTmbBox only if WORKSPACE_MODE === 0 (windows not spread)
|
|
const animateOverviewMode2 = opt.OVERVIEW_MODE2 && !(finalState === 1 && opt.WORKSPACE_MODE);
|
|
if (!Main.layoutManager._startingUp && ((!opt.SHOW_WS_PREVIEW_BG && !opt.OVERVIEW_MODE2) || animateOverviewMode2)) {
|
|
if (!tmbBox._translationOriginal || Math.abs(tmbBox._translationOriginal[0]) > 500) { // swipe gesture can call this calculation before tmbBox is realized, giving nonsense width
|
|
const [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY] = this._getOverviewTranslations(dash, tmbBox, searchEntryBin);
|
|
tmbBox._translationOriginal = [tmbTranslationX, tmbTranslationY];
|
|
dash._translationOriginal = [dashTranslationX, dashTranslationY];
|
|
searchEntryBin._translationOriginal = searchTranslationY;
|
|
}
|
|
if (finalState === 0 || initialState === 0) {
|
|
const prg = Math.abs((finalState === 0 ? 0 : 1) - progress);
|
|
tmbBox.translation_x = Math.round(prg * tmbBox._translationOriginal[0]);
|
|
tmbBox.translation_y = Math.round(prg * tmbBox._translationOriginal[1]);
|
|
if (!skipDash) {
|
|
dash.translation_x = Math.round(prg * dash._translationOriginal[0]);
|
|
dash.translation_y = Math.round(prg * dash._translationOriginal[1]);
|
|
}
|
|
searchEntryBin.translation_y = Math.round(prg * searchEntryBin._translationOriginal);
|
|
}
|
|
if (progress === 1) {
|
|
tmbBox._translationOriginal = 0;
|
|
if (!skipDash)
|
|
dash._translationOriginal = 0;
|
|
|
|
searchEntryBin._translationOriginal = 0;
|
|
}
|
|
} else if (!Main.layoutManager._startingUp && (tmbBox.translation_x || tmbBox.translation_y)) {
|
|
tmbBox.translation_x = 0;
|
|
tmbBox.translation_y = 0;
|
|
if (!skipDash) {
|
|
dash.translation_x = 0;
|
|
dash.translation_y = 0;
|
|
}
|
|
searchEntryBin.translation_y = 0;
|
|
}
|
|
|
|
if (!Main.layoutManager._startingUp) {
|
|
if (initialState === ControlsState.HIDDEN && finalState === ControlsState.APP_GRID)
|
|
this._appDisplay.opacity = Math.round(progress * 255);
|
|
else
|
|
this._appDisplay.opacity = 255 - opacity;
|
|
}
|
|
|
|
if (currentState === ControlsState.APP_GRID) {
|
|
// in app grid hide workspaces so they're not blocking app grid or ws thumbnails
|
|
this._workspacesDisplay.scale_x = 0;
|
|
} else {
|
|
this._workspacesDisplay.scale_x = 1;
|
|
}
|
|
if (opt.LEAVING_SEARCH && currentState <= ControlsState.WINDOW_PICKER) {
|
|
opt.LEAVING_SEARCH = false;
|
|
}
|
|
|
|
this._workspacesDisplay.setPrimaryWorkspaceVisible(workspacesDisplayVisible);
|
|
|
|
if (!this.dash._isAbove && progress > 0 && opt.OVERVIEW_MODE2) {
|
|
// set searchEntry above appDisplay
|
|
this.set_child_above_sibling(this._searchEntryBin, null);
|
|
// move dash above wsTmb for case that dash and wsTmb animate from the same side
|
|
if (!Me.Util.dashNotDefault())
|
|
this.set_child_above_sibling(dash, null);
|
|
this.set_child_below_sibling(this._thumbnailsBox, null);
|
|
this.set_child_below_sibling(this._workspacesDisplay, null);
|
|
this.set_child_below_sibling(this._appDisplay, null);
|
|
} else if (!this.dash._isAbove && progress === 1 && finalState > ControlsState.HIDDEN) {
|
|
// set dash above workspace in the overview
|
|
this.set_child_above_sibling(this._thumbnailsBox, null);
|
|
this.set_child_above_sibling(this._searchEntryBin, null);
|
|
if (!Me.Util.dashNotDefault())
|
|
this.set_child_above_sibling(this.dash, null);
|
|
if (Main.layoutManager.panelBox.get_parent() === Main.layoutManager.overviewGroup)
|
|
Main.layoutManager.overviewGroup.set_child_above_sibling(Main.layoutManager.panelBox, Main.overview._overview);
|
|
this.dash._isAbove = true;
|
|
} else if (this.dash._isAbove && progress < 1) {
|
|
// keep dash below for ws transition between the overview and hidden state
|
|
this.set_child_above_sibling(this._workspacesDisplay, null);
|
|
if (Main.layoutManager.panelBox.get_parent() === Main.layoutManager.overviewGroup)
|
|
Main.layoutManager.overviewGroup.set_child_below_sibling(Main.layoutManager.panelBox, Main.overview._overview);
|
|
this.dash._isAbove = false;
|
|
}
|
|
},
|
|
|
|
// fix for upstream bug - appGrid.visible after transition from APP_GRID to HIDDEN
|
|
_updateAppDisplayVisibility(stateTransitionParams = null) {
|
|
if (!stateTransitionParams)
|
|
stateTransitionParams = this._stateAdjustment.getStateTransitionParams();
|
|
|
|
const { currentState } = stateTransitionParams;
|
|
if (this.dash.showAppsButton.checked)
|
|
this._searchTransition = false;
|
|
|
|
// if !APP_GRID_ANIMATION, appGrid needs to be hidden in WINDOW_PICKER mode (1)
|
|
// but needs to be visible for transition from HIDDEN (0) to APP_GRID (2)
|
|
this._appDisplay.visible =
|
|
currentState > ControlsState.HIDDEN &&
|
|
!this._searchController.searchActive &&
|
|
!(currentState === ControlsState.WINDOW_PICKER && !opt.APP_GRID_ANIMATION) &&
|
|
!this._searchTransition;
|
|
},
|
|
|
|
_activateSearchAppGridMode() {
|
|
if (!this._origAppGridContent) {
|
|
this._origAppGridContent = {
|
|
usage: opt.APP_GRID_USAGE,
|
|
favorites: opt.APP_GRID_EXCLUDE_FAVORITES,
|
|
running: opt.APP_GRID_EXCLUDE_RUNNING,
|
|
incompletePages: this._appDisplay._grid.layoutManager.allowIncompletePages,
|
|
order: opt.APP_GRID_ORDER,
|
|
};
|
|
opt.APP_GRID_ORDER = 3;
|
|
opt.APP_GRID_USAGE = true;
|
|
opt.APP_GRID_EXCLUDE_FAVORITES = false;
|
|
opt.APP_GRID_EXCLUDE_RUNNING = false;
|
|
this._appDisplay._grid.layoutManager.allowIncompletePages = false;
|
|
this._appDisplay._redisplay();
|
|
}
|
|
},
|
|
|
|
_deactivateSearchAppGridMode() {
|
|
if (this._origAppGridContent) {
|
|
const icons = this._appDisplay._orderedItems;
|
|
icons.forEach(icon => {
|
|
icon.visible = true;
|
|
});
|
|
|
|
opt.APP_GRID_ORDER = this._origAppGridContent.order;
|
|
opt.APP_GRID_USAGE = this._origAppGridContent.usage;
|
|
opt.APP_GRID_EXCLUDE_FAVORITES = this._origAppGridContent.favorites;
|
|
opt.APP_GRID_EXCLUDE_RUNNING = this._origAppGridContent.running;
|
|
this._appDisplay._grid.layoutManager.allowIncompletePages = this._origAppGridContent.incompletePages;
|
|
this._origAppGridContent = null;
|
|
this._appDisplay._redisplay();
|
|
}
|
|
},
|
|
|
|
_onSearchChanged() {
|
|
// something is somewhere setting the opacity to 0 if V-Shell is rebased while in overview / search
|
|
this._searchController.opacity = 255;
|
|
|
|
const { finalState, currentState } = this._stateAdjustment.getStateTransitionParams();
|
|
|
|
const { searchActive } = this._searchController;
|
|
const SIDE_CONTROLS_ANIMATION_TIME = 250; // OverviewControls.SIDE_CONTROLS_ANIMATION_TIME = Overview.ANIMATION_TIME = 250
|
|
|
|
const entry = this._searchEntry;
|
|
if (opt.SHOW_SEARCH_ENTRY) {
|
|
entry.visible = true;
|
|
entry.opacity = 255;
|
|
} else if (!(searchActive && entry.visible)) {
|
|
entry.visible = true;
|
|
entry.opacity = searchActive ? 0 : 255;
|
|
// show search entry only if the user starts typing, and hide it when leaving the search mode
|
|
entry.ease({
|
|
opacity: searchActive ? 255 : 0,
|
|
duration: SIDE_CONTROLS_ANIMATION_TIME / 2,
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
onComplete: () => {
|
|
entry.visible = searchActive;
|
|
},
|
|
});
|
|
}
|
|
|
|
// if user start typing or activated search provider during overview animation, this switcher will be called again after animation ends
|
|
if (opt.SEARCH_VIEW_ANIMATION && Main.overview._animationInProgress && finalState !== ControlsState.HIDDEN)
|
|
return;
|
|
|
|
if (!searchActive) {
|
|
if (!this.dash.showAppsButton.checked)
|
|
opt.LEAVING_SEARCH = true;
|
|
|
|
if (this._origAppGridContent)
|
|
this._deactivateSearchAppGridMode();
|
|
|
|
this._workspacesDisplay.reactive = true;
|
|
this._workspacesDisplay.setPrimaryWorkspaceVisible(true);
|
|
} else {
|
|
if (opt.SEARCH_APP_GRID_MODE && this.dash.showAppsButton.checked) {
|
|
this._activateSearchAppGridMode();
|
|
return;
|
|
}
|
|
|
|
if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE)
|
|
this._searchController._searchResults._statusText.add_style_class_name('search-statustext-om2');
|
|
else
|
|
this._searchController._searchResults._statusText.remove_style_class_name('search-statustext-om2');
|
|
this._searchController.show();
|
|
entry.visible = true;
|
|
entry.opacity = 255;
|
|
opt.LEAVING_SEARCH = false;
|
|
}
|
|
|
|
if (opt.SHOW_BG_IN_OVERVIEW && this._bgManagers)
|
|
this._updateBackground(this._bgManagers[0]);
|
|
this._searchTransition = true;
|
|
|
|
this._searchController._searchResults.translation_x = 0;
|
|
this._searchController._searchResults.translation_y = 0;
|
|
this._searchController.visible = true;
|
|
|
|
if (opt.SEARCH_VIEW_ANIMATION && ![4, 8].includes(opt.WS_TMB_POSITION)) {
|
|
this._updateAppDisplayVisibility();
|
|
this._searchController._searchResults._statusBin.opacity = 1;
|
|
|
|
let translationX = 0;
|
|
let translationY = 0;
|
|
const geometry = global.display.get_monitor_geometry(global.display.get_primary_monitor());
|
|
|
|
switch (opt.SEARCH_VIEW_ANIMATION) {
|
|
case 1:
|
|
// make it longer to cover the delay before results appears
|
|
translationX = geometry.width;
|
|
translationY = 0;
|
|
break;
|
|
case 2:
|
|
translationX = -geometry.width;
|
|
translationY = 0;
|
|
break;
|
|
case 3:
|
|
translationX = 0;
|
|
translationY = geometry.height;
|
|
break;
|
|
case 5:
|
|
translationX = 0;
|
|
translationY = -geometry.height;
|
|
break;
|
|
}
|
|
|
|
if (searchActive) {
|
|
this._searchController._searchResults.translation_x = translationX;
|
|
this._searchController._searchResults.translation_y = translationY;
|
|
} else {
|
|
this._searchController._searchResults.translation_x = 0;
|
|
this._searchController._searchResults.translation_y = 0;
|
|
}
|
|
|
|
this._searchController._searchResults.ease({
|
|
delay: 150, // wait for results
|
|
opacity: searchActive ? 255 : 0,
|
|
translation_x: searchActive ? 0 : translationX,
|
|
translation_y: searchActive ? 0 : translationY,
|
|
duration: SIDE_CONTROLS_ANIMATION_TIME,
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
onComplete: () => {
|
|
this._searchController.visible = searchActive;
|
|
this._searchTransition = false;
|
|
this._searchController._searchResults._statusBin.opacity = 255;
|
|
},
|
|
});
|
|
|
|
this._workspacesDisplay.opacity = 255;
|
|
} else {
|
|
this._appDisplay.ease({
|
|
opacity: searchActive || currentState < 2 ? 0 : 255,
|
|
duration: SIDE_CONTROLS_ANIMATION_TIME / 2,
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
onComplete: () => {
|
|
this._updateAppDisplayVisibility();
|
|
},
|
|
});
|
|
|
|
this._workspacesDisplay.setPrimaryWorkspaceVisible(true);
|
|
|
|
this._searchController._searchResults.ease({
|
|
opacity: searchActive ? 255 : 0,
|
|
duration: searchActive ? SIDE_CONTROLS_ANIMATION_TIME / 2 : 0,
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
onComplete: () => (this._searchController.visible = searchActive),
|
|
});
|
|
}
|
|
|
|
// reuse already tuned overview transition, just replace APP_GRID with the search view
|
|
if (!(opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) && !Main.overview._animationInProgress && finalState !== ControlsState.HIDDEN && !this.dash.showAppsButton.checked) {
|
|
this._searchController._searchResults._content.remove_style_class_name('search-section-content-bg-om2');
|
|
this._searchEntry.remove_style_class_name('search-entry-om2');
|
|
const duration = opt.SEARCH_VIEW_ANIMATION ? 140 : 0;
|
|
this._stateAdjustment.ease(searchActive ? ControlsState.APP_GRID : ControlsState.WINDOW_PICKER, {
|
|
// shorter animation time when entering search view can avoid stuttering in transition
|
|
// collecting search results take some time and the problematic part is the realization of the object on the screen
|
|
// if the ws animation ends before this event, the whole transition is smoother
|
|
// removing the ws transition (duration: 0) seems like the best solution here
|
|
duration: searchActive ? duration : SIDE_CONTROLS_ANIMATION_TIME,
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
onComplete: () => {
|
|
this._workspacesDisplay.setPrimaryWorkspaceVisible(!searchActive);
|
|
// Set the delay before processing a new search entry to 150 on deactivation, so search providers can't make make the workspace animation stuttering
|
|
// set it back to 0 after in-animation, so the search can be snappy
|
|
opt.SEARCH_DELAY = searchActive || !opt.SEARCH_VIEW_ANIMATION ? 0 : 150;
|
|
},
|
|
});
|
|
} else if (opt.OVERVIEW_MODE2 && !(opt.WORKSPACE_MODE || this.dash.showAppsButton.checked)) {
|
|
// add background to search results and make searchEntry border thicker for better visibility
|
|
this._searchController._searchResults._content.add_style_class_name('search-section-content-bg-om2');
|
|
this._searchEntry.add_style_class_name('search-entry-om2');
|
|
} else {
|
|
this._searchController._searchResults._content.remove_style_class_name('search-section-content-bg-om2');
|
|
this._searchEntry.remove_style_class_name('search-entry-om2');
|
|
}
|
|
},
|
|
|
|
async runStartupAnimation(callback) {
|
|
this._ignoreShowAppsButtonToggle = true;
|
|
|
|
this.prepareToEnterOverview();
|
|
|
|
this._stateAdjustment.value = ControlsState.HIDDEN;
|
|
this._stateAdjustment.ease(ControlsState.WINDOW_PICKER, {
|
|
duration: ANIMATION_TIME,
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
});
|
|
|
|
this.dash.showAppsButton.checked = false;
|
|
this._ignoreShowAppsButtonToggle = false;
|
|
|
|
// Set the opacity here to avoid a 1-frame flicker
|
|
this.opacity = 1;
|
|
this._appDisplay.opacity = 1;
|
|
|
|
// We can't run the animation before the first allocation happens
|
|
await this.layout_manager.ensureAllocation();
|
|
|
|
this._setBackground();
|
|
Me.Modules.panelModule.update();
|
|
Main.panel.opacity = 255;
|
|
|
|
// Opacity
|
|
this.ease({
|
|
opacity: opt.STARTUP_STATE === 1 ? 0 : 255,
|
|
duration: STARTUP_ANIMATION_TIME,
|
|
mode: Clutter.AnimationMode.LINEAR,
|
|
});
|
|
|
|
const dash = this.dash;
|
|
const tmbBox = this._thumbnailsBox;
|
|
|
|
// Set the opacity here to avoid a 1-frame flicker
|
|
dash.opacity = 0;
|
|
for (const view of this._workspacesDisplay._workspacesViews) {
|
|
if (view._monitorIndex !== global.display.get_primary_monitor())
|
|
view._thumbnails.opacity = 0;
|
|
}
|
|
|
|
const searchEntryBin = this._searchEntryBin;
|
|
const [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY] =
|
|
this._getOverviewTranslations(dash, tmbBox, searchEntryBin);
|
|
|
|
const onComplete = function () {
|
|
// running init callback again causes issues (multiple connections)
|
|
if (!Main.overview._startupInitComplete)
|
|
callback();
|
|
|
|
const appDisplayModule = Me.Modules.appDisplayModule;
|
|
if (!appDisplayModule.moduleEnabled)
|
|
this._finishStartupSequence();
|
|
else
|
|
this._realizeAppDisplayAndFinishSequence();
|
|
|
|
Main.overview._startupInitComplete = true;
|
|
}.bind(this);
|
|
|
|
if (dash.visible && !Me.Util.dashNotDefault()) {
|
|
dash.translation_x = dashTranslationX;
|
|
dash.translation_y = dashTranslationY;
|
|
dash.opacity = 255;
|
|
dash.ease({
|
|
translation_x: 0,
|
|
translation_y: 0,
|
|
delay: STARTUP_ANIMATION_TIME / 2,
|
|
duration: STARTUP_ANIMATION_TIME,
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
onComplete,
|
|
});
|
|
} else {
|
|
// set dash opacity to make it visible if user enable it later
|
|
dash.opacity = 255;
|
|
// if dash is hidden, substitute the ease timeout with GLib.timeout
|
|
_timeouts.startupAnim2 = GLib.timeout_add(
|
|
GLib.PRIORITY_DEFAULT,
|
|
// delay + animation time
|
|
STARTUP_ANIMATION_TIME * 2 * St.Settings.get().slow_down_factor,
|
|
() => {
|
|
onComplete();
|
|
_timeouts.startupAnim2 = 0;
|
|
return GLib.SOURCE_REMOVE;
|
|
}
|
|
);
|
|
}
|
|
|
|
if (searchEntryBin.visible) {
|
|
searchEntryBin.translation_y = searchTranslationY;
|
|
searchEntryBin.ease({
|
|
translation_y: 0,
|
|
delay: STARTUP_ANIMATION_TIME / 2,
|
|
duration: STARTUP_ANIMATION_TIME,
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
});
|
|
}
|
|
|
|
if (tmbBox.visible) {
|
|
tmbBox.translation_x = tmbTranslationX;
|
|
tmbBox.translation_y = tmbTranslationY;
|
|
tmbBox.ease({
|
|
translation_x: 0,
|
|
translation_y: 0,
|
|
delay: STARTUP_ANIMATION_TIME / 2,
|
|
duration: STARTUP_ANIMATION_TIME,
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
});
|
|
}
|
|
|
|
// upstream bug - following animation will be cancelled, don't know where
|
|
// needs further investigation
|
|
const workspacesViews = this._workspacesDisplay._workspacesViews;
|
|
if (workspacesViews.length > 1) {
|
|
for (const view of workspacesViews) {
|
|
if (view._monitorIndex !== global.display.get_primary_monitor() && view._thumbnails.visible) {
|
|
const secTmbBox = view._thumbnails;
|
|
|
|
if (opt.SEC_WS_TMB_LEFT)
|
|
secTmbBox.translation_x = -(secTmbBox.width + 12); // compensate for padding
|
|
else if (opt.SEC_WS_TMB_RIGHT)
|
|
secTmbBox.translation_x = secTmbBox.width + 12;
|
|
else if (opt.SEC_WS_TMB_TOP)
|
|
secTmbBox.translation_y = -(secTmbBox.height + 12);
|
|
else if (opt.SEC_WS_TMB_BOTTOM)
|
|
secTmbBox.translation_y = secTmbBox.height + 12;
|
|
|
|
secTmbBox.opacity = 255;
|
|
|
|
secTmbBox.ease({
|
|
translation_y: 0,
|
|
delay: STARTUP_ANIMATION_TIME / 2,
|
|
duration: STARTUP_ANIMATION_TIME,
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
_realizeAppDisplayAndFinishSequence() {
|
|
const appDisplayModule = Me.Modules.appDisplayModule;
|
|
// realize app grid for smoother first animation
|
|
appDisplayModule._repopulateAppDisplay(false, this._finishStartupSequence.bind(this));
|
|
},
|
|
|
|
_finishStartupSequence() {
|
|
if (!this._bgManagers)
|
|
this._setBackground();
|
|
|
|
_timeouts.finishStartup = GLib.idle_add(
|
|
GLib.PRIORITY_LOW, () => {
|
|
this._appDisplay.opacity = 255;
|
|
if (opt.STARTUP_STATE === 1) {
|
|
Main.overview.hide();
|
|
} else if (opt.STARTUP_STATE === 2) {
|
|
Main.overview.show(2); // just because of DtD, because we skipped startup animation
|
|
this.dash.showAppsButton.checked = true;
|
|
} else if (!opt.STARTUP_STATE && Me.Util.dashNotDefault()) {
|
|
Main.overview.show();
|
|
}
|
|
|
|
_timeouts.finishStartup = 0;
|
|
return GLib.SOURCE_REMOVE;
|
|
}
|
|
);
|
|
},
|
|
|
|
setInitialTranslations() {
|
|
const dash = this.dash;
|
|
const tmbBox = this._thumbnailsBox;
|
|
const searchEntryBin = this._searchEntryBin;
|
|
const [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY] =
|
|
this._getOverviewTranslations(dash, tmbBox, searchEntryBin);
|
|
if (!Me.Util.dashNotDefault()) {
|
|
dash.translation_x = dashTranslationX;
|
|
dash.translation_y = dashTranslationY;
|
|
}
|
|
tmbBox.translation_x = tmbTranslationX;
|
|
tmbBox.translation_y = tmbTranslationY;
|
|
searchEntryBin.translation_y = searchTranslationY;
|
|
},
|
|
|
|
_getOverviewTranslations(dash, tmbBox, searchEntryBin) {
|
|
const animationsDisabled = !St.Settings.get().enable_animations || ((opt.SHOW_WS_PREVIEW_BG && !opt.OVERVIEW_MODE2) && !Main.layoutManager._startingUp);
|
|
if (animationsDisabled)
|
|
return [0, 0, 0, 0, 0];
|
|
|
|
let searchTranslationY = 0;
|
|
if (searchEntryBin.visible) {
|
|
const offset = (dash.visible && (!opt.DASH_VERTICAL ? dash.height + 12 : 0)) +
|
|
(opt.WS_TMB_TOP ? tmbBox.height + 12 : 0);
|
|
searchTranslationY = -searchEntryBin.height - offset - 30;
|
|
}
|
|
|
|
let tmbTranslationX = 0;
|
|
let tmbTranslationY = 0;
|
|
let offset;
|
|
if (tmbBox.visible) {
|
|
const tmbWidth = tmbBox.width === Infinity ? 0 : tmbBox.width;
|
|
const tmbHeight = tmbBox.height === Infinity ? 0 : tmbBox.height;
|
|
switch (opt.WS_TMB_POSITION) {
|
|
case 3: // left
|
|
offset = 10 + (dash?.visible && opt.DASH_LEFT ? dash.width : 0);
|
|
tmbTranslationX = -tmbWidth - offset;
|
|
tmbTranslationY = 0;
|
|
break;
|
|
case 1: // right
|
|
offset = 10 + (dash?.visible && opt.DASH_RIGHT ? dash.width : 0);
|
|
tmbTranslationX = tmbWidth + offset;
|
|
tmbTranslationY = 0;
|
|
break;
|
|
case 0: // top
|
|
offset = 10 + (dash?.visible && opt.DASH_TOP ? dash.height : 0) + Main.panel.height;
|
|
tmbTranslationX = 0;
|
|
tmbTranslationY = -tmbHeight - offset;
|
|
break;
|
|
case 2: // bottom
|
|
offset = 10 + (dash?.visible && opt.DASH_BOTTOM ? dash.height : 0) + Main.panel.height; // just for case the panel is at bottom
|
|
tmbTranslationX = 0;
|
|
tmbTranslationY = tmbHeight + offset;
|
|
break;
|
|
}
|
|
}
|
|
|
|
let dashTranslationX = 0;
|
|
let dashTranslationY = 0;
|
|
let position = opt.DASH_POSITION;
|
|
// if DtD replaced the original Dash, read its position
|
|
if (Me.Util.dashIsDashToDock())
|
|
position = dash._position;
|
|
|
|
if (dash?.visible) {
|
|
const dashWidth = dash.width === Infinity ? 0 : dash.width;
|
|
const dashHeight = dash.height === Infinity ? 0 : dash.height;
|
|
switch (position) {
|
|
case 0: // top
|
|
dashTranslationX = 0;
|
|
dashTranslationY = -dashHeight - dash.margin_bottom - Main.panel.height;
|
|
break;
|
|
case 1: // right
|
|
dashTranslationX = dashWidth;
|
|
dashTranslationY = 0;
|
|
break;
|
|
case 2: // bottom
|
|
dashTranslationX = 0;
|
|
dashTranslationY = dashHeight + dash.margin_bottom + Main.panel.height;
|
|
break;
|
|
case 3: // left
|
|
dashTranslationX = -dashWidth;
|
|
dashTranslationY = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY];
|
|
},
|
|
|
|
animateToOverview(state, callback) {
|
|
this._ignoreShowAppsButtonToggle = true;
|
|
this._searchTransition = false;
|
|
|
|
this._stateAdjustment.value = ControlsState.HIDDEN;
|
|
|
|
// building window thumbnails takes some time and with many windows on the workspace
|
|
// the time can be close to or longer than ANIMATION_TIME
|
|
// in which case the the animation is greatly delayed, stuttering, or even skipped
|
|
// for user it is more acceptable to watch delayed smooth animation,
|
|
// even if it takes little more time, than jumping frames
|
|
let delay = 0;
|
|
if (opt.DELAY_OVERVIEW_ANIMATION)
|
|
delay = global.display.get_tab_list(0, global.workspace_manager.get_active_workspace()).length * 3;
|
|
|
|
this._stateAdjustment.ease(state, {
|
|
delay,
|
|
duration: 250, // Overview.ANIMATION_TIME,
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
onStopped: () => {
|
|
if (callback)
|
|
callback();
|
|
},
|
|
});
|
|
|
|
this.dash.showAppsButton.checked =
|
|
state === ControlsState.APP_GRID;
|
|
|
|
this._ignoreShowAppsButtonToggle = false;
|
|
},
|
|
|
|
_setBackground(reset = false) {
|
|
if (this._bgManagers) {
|
|
this._bgManagers.forEach(bg => {
|
|
this._stateAdjustment.disconnect(bg._fadeSignal);
|
|
bg.destroy();
|
|
});
|
|
}
|
|
|
|
// if (!SHOW_BG_IN_OVERVIEW && !SHOW_WS_PREVIEW_BG) the background is used for static transition from wallpaper to empty bg in the overview
|
|
if (reset || (!opt.SHOW_BG_IN_OVERVIEW && opt.SHOW_WS_PREVIEW_BG)) {
|
|
delete this._bgManagers;
|
|
return;
|
|
}
|
|
|
|
this._bgManagers = [];
|
|
for (const monitor of Main.layoutManager.monitors) {
|
|
const bgManager = new Background.BackgroundManager({
|
|
monitorIndex: monitor.index,
|
|
container: Main.layoutManager.overviewGroup,
|
|
vignette: true,
|
|
});
|
|
|
|
bgManager.backgroundActor.content.vignette_sharpness = 0;
|
|
bgManager.backgroundActor.content.brightness = 1;
|
|
|
|
bgManager._fadeSignal = this._stateAdjustment.connect('notify::value', v => {
|
|
this._updateBackground(bgManager, v.value, v);
|
|
});
|
|
|
|
if (monitor.index === global.display.get_primary_monitor()) {
|
|
bgManager._primary = true;
|
|
this._bgManagers.unshift(bgManager); // primary monitor first
|
|
} else {
|
|
bgManager._primary = false;
|
|
this._bgManagers.push(bgManager);
|
|
}
|
|
}
|
|
},
|
|
|
|
_updateBackground(bgManager, stateValue = 2, stateAdjustment = null) {
|
|
// Just in case something destroys our background (like older versions of Blur My Shell)
|
|
if (this._bgManagers[0] && !Main.layoutManager.overviewGroup.get_children().includes(this._bgManagers[0].backgroundActor)) {
|
|
console.error(`[${Me.metadata.name}]`, 'Error: The overview background has been destroyed, possibly by another incompatible extension');
|
|
// remove and disconnect our destroyed backgrounds to avoid further errors
|
|
this._setBackground(true);
|
|
return;
|
|
}
|
|
|
|
const finalState = stateAdjustment?.getStateTransitionParams().finalState;
|
|
if (!opt.SHOW_BG_IN_OVERVIEW && !opt.SHOW_WS_PREVIEW_BG) {
|
|
// if no bg shown in the overview, fade out the wallpaper
|
|
if (bgManager.backgroundActor.get_effect('blur'))
|
|
bgManager.backgroundActor.remove_effect_by_name('blur');
|
|
if (!(opt.OVERVIEW_MODE2 && opt.WORKSPACE_MODE && finalState === 1))
|
|
bgManager.backgroundActor.opacity = Util.lerp(255, 0, Math.min(stateValue, 1));
|
|
} else {
|
|
bgManager.backgroundActor.opacity = 255;
|
|
let VIGNETTE, BRIGHTNESS, bgValue;
|
|
if (opt.OVERVIEW_MODE2 && stateValue <= 1 && !opt.WORKSPACE_MODE) {
|
|
VIGNETTE = 0;
|
|
BRIGHTNESS = 1;
|
|
bgValue = stateValue;
|
|
} else {
|
|
VIGNETTE = 0.2;
|
|
BRIGHTNESS = opt.OVERVIEW_BG_BRIGHTNESS;
|
|
if (opt.OVERVIEW_MODE2 && stateValue > 1 && !opt.WORKSPACE_MODE)
|
|
bgValue = stateValue - 1;
|
|
else
|
|
bgValue = stateValue;
|
|
}
|
|
|
|
let blurEffect = bgManager.backgroundActor.get_effect('blur');
|
|
if (!blurEffect) {
|
|
blurEffect = new Shell.BlurEffect({
|
|
brightness: 1,
|
|
mode: Shell.BlurMode.ACTOR,
|
|
});
|
|
bgManager.backgroundActor.add_effect_with_name('blur', blurEffect);
|
|
}
|
|
|
|
// In GNOME 46 the "sigma" property has been renamed to "radius"
|
|
const radius = blurEffect.sigma !== undefined ? 'sigma' : 'radius';
|
|
|
|
const searchActive = this._searchController.searchActive;
|
|
if (searchActive)
|
|
BRIGHTNESS = opt.SEARCH_BG_BRIGHTNESS;
|
|
|
|
bgManager.backgroundActor.content.vignette_sharpness = VIGNETTE;
|
|
bgManager.backgroundActor.content.brightness = BRIGHTNESS;
|
|
|
|
let vignetteInit, brightnessInit;// , sigmaInit;
|
|
if (opt.SHOW_BG_IN_OVERVIEW && opt.SHOW_WS_PREVIEW_BG) {
|
|
vignetteInit = VIGNETTE;
|
|
brightnessInit = BRIGHTNESS;
|
|
// sigmaInit = opt.OVERVIEW_BG_BLUR_SIGMA;
|
|
} else {
|
|
vignetteInit = 0;
|
|
brightnessInit = 1;
|
|
// sigmaInit = 0;
|
|
}
|
|
|
|
if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) {
|
|
bgManager.backgroundActor.content.vignette_sharpness = Util.lerp(vignetteInit, VIGNETTE, bgValue);
|
|
bgManager.backgroundActor.content.brightness = Util.lerp(brightnessInit, BRIGHTNESS, bgValue);
|
|
} else {
|
|
bgManager.backgroundActor.content.vignette_sharpness = Util.lerp(vignetteInit, VIGNETTE, Math.min(stateValue, 1));
|
|
bgManager.backgroundActor.content.brightness = Util.lerp(brightnessInit, BRIGHTNESS, Math.min(stateValue, 1));
|
|
}
|
|
|
|
if (opt.OVERVIEW_BG_BLUR_SIGMA || opt.APP_GRID_BG_BLUR_SIGMA) {
|
|
// reduce number of steps of blur transition to improve performance
|
|
const step = opt.SMOOTH_BLUR_TRANSITIONS ? 0.05 : 0.2;
|
|
const progress = stateValue - (stateValue % step);
|
|
if (opt.SHOW_WS_PREVIEW_BG && stateValue < 1 && !searchActive) { // no need to animate transition, unless appGrid state is involved, static bg is covered by the ws preview bg
|
|
if (blurEffect[radius] !== opt.OVERVIEW_BG_BLUR_SIGMA)
|
|
blurEffect[radius] = opt.OVERVIEW_BG_BLUR_SIGMA;
|
|
} else if (stateValue < 1 && !searchActive && !(opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE)) {
|
|
const sigma = Math.round(Util.lerp(0, opt.OVERVIEW_BG_BLUR_SIGMA, progress));
|
|
if (sigma !== blurEffect[radius])
|
|
blurEffect[radius] = sigma;
|
|
} else if (stateValue < 1 && !searchActive && (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && blurEffect[radius])) {
|
|
const sigma = Math.round(Util.lerp(0, opt.OVERVIEW_BG_BLUR_SIGMA, progress));
|
|
if (sigma !== blurEffect[radius])
|
|
blurEffect[radius] = sigma;
|
|
} else if (stateValue > 1 && !searchActive && (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && finalState === 1)) {
|
|
const sigma = Math.round(Util.lerp(0, opt.OVERVIEW_BG_BLUR_SIGMA, progress % 1));
|
|
if (sigma !== blurEffect[radius])
|
|
blurEffect[radius] = sigma;
|
|
} else if ((stateValue > 1 && bgManager._primary) || searchActive) {
|
|
const sigma = Math.round(Util.lerp(opt.OVERVIEW_BG_BLUR_SIGMA, opt.APP_GRID_BG_BLUR_SIGMA, progress % 1));
|
|
if (sigma !== blurEffect[radius])
|
|
blurEffect[radius] = sigma;
|
|
} else if (stateValue === 1 && !(opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE)) {
|
|
blurEffect[radius] = opt.OVERVIEW_BG_BLUR_SIGMA;
|
|
} else if (stateValue === 0 || (stateValue === 1 && (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE))) {
|
|
blurEffect[radius] = 0;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
};
|
|
|
|
const ControlsManagerLayoutCommon = {
|
|
after__updateWorkAreaBox() {
|
|
const workArea = this._workAreaBox.copy();
|
|
|
|
// opt.PANEL_OVERVIEW_ONLY removes affectsStruts panel property
|
|
if (opt.get('panelModule') && opt.PANEL_OVERVIEW_ONLY) {
|
|
let offsetY = 0;
|
|
let reduction = 0;
|
|
reduction = Main.panel.height;
|
|
offsetY = opt.PANEL_POSITION_TOP ? reduction : 0;
|
|
|
|
const startX = workArea.x1;
|
|
const startY = workArea.y1 + offsetY;
|
|
const width = workArea.get_width();
|
|
const height = workArea.get_height() - reduction;
|
|
|
|
workArea.set_origin(startX, startY);
|
|
workArea.set_size(width, height);
|
|
}
|
|
|
|
this._workAreaBoxForVShellConfig = workArea;
|
|
},
|
|
|
|
_updatePositionFromDashToDock() {
|
|
// update variables that cannot be processed within settings
|
|
const dash = Main.overview.dash;
|
|
opt.DASH_POSITION = dash._position;
|
|
opt.DASH_TOP = opt.DASH_POSITION === 0;
|
|
opt.DASH_RIGHT = opt.DASH_POSITION === 1;
|
|
opt.DASH_BOTTOM = opt.DASH_POSITION === 2;
|
|
opt.DASH_LEFT = opt.DASH_POSITION === 3;
|
|
opt.DASH_VERTICAL = opt.DASH_LEFT || opt.DASH_RIGHT;
|
|
},
|
|
|
|
_dashToDockAffectsWorkArea() {
|
|
const dash = Main.overview.dash;
|
|
const dtd = dash.get_parent()?.get_parent()?.get_parent();
|
|
const layoutManager = Main.layoutManager;
|
|
const index = layoutManager._findActor(dtd);
|
|
const data = index > -1 ? layoutManager._trackedActors[index] : null;
|
|
const affectsStruts = data?.affectsStruts;
|
|
return !!affectsStruts;
|
|
},
|
|
};
|
|
|
|
const ControlsManagerLayoutVertical = {
|
|
_computeWorkspacesBoxForState(state, box, wsTmbWidth, wsTmbHeight, leftBoxOffset, rightBoxOffset, topBoxOffset, bottomBoxOffset, centeredBoxOffset) {
|
|
const workspaceBox = box.copy();
|
|
let [width, height] = this._workAreaBoxForVShellConfig.get_size();
|
|
const startX = this._workAreaBoxForVShellConfig.x1;
|
|
const startY = this._workAreaBoxForVShellConfig.y1;
|
|
|
|
let wsBoxWidth, wsBoxHeight, wsBoxY, wsBoxX;
|
|
|
|
switch (state) {
|
|
case ControlsState.HIDDEN:
|
|
workspaceBox.set_origin(...this._workAreaBox.get_origin());
|
|
workspaceBox.set_size(...this._workAreaBox.get_size());
|
|
break;
|
|
case ControlsState.WINDOW_PICKER:
|
|
case ControlsState.APP_GRID:
|
|
if (opt.WS_ANIMATION && opt.SHOW_WS_TMB && state === ControlsState.APP_GRID) {
|
|
workspaceBox.set_origin(...this._workspacesThumbnails.get_position());
|
|
workspaceBox.set_size(wsTmbWidth, wsTmbHeight);
|
|
} else if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) {
|
|
workspaceBox.set_origin(...this._workAreaBox.get_origin());
|
|
workspaceBox.set_size(...this._workAreaBox.get_size());
|
|
} else {
|
|
wsBoxWidth = width - leftBoxOffset - rightBoxOffset;
|
|
wsBoxHeight = height - topBoxOffset - bottomBoxOffset;
|
|
|
|
const ratio = width / height;
|
|
let wRatio = wsBoxWidth / wsBoxHeight;
|
|
let scale = ratio / wRatio;
|
|
|
|
if (scale > 1) {
|
|
wsBoxHeight /= scale;
|
|
wsBoxWidth = wsBoxHeight * ratio;
|
|
} else {
|
|
wsBoxWidth *= scale;
|
|
wsBoxHeight = wsBoxWidth / ratio;
|
|
}
|
|
|
|
// height decides the actual size, ratio is given by the workArea
|
|
wsBoxHeight = Math.round(wsBoxHeight * opt.WS_PREVIEW_SCALE);
|
|
wsBoxWidth = Math.round(wsBoxWidth * opt.WS_PREVIEW_SCALE);
|
|
|
|
let xOffset = 0;
|
|
let yOffset = 0;
|
|
|
|
const yAvailableSpace = Math.round((height - topBoxOffset - wsBoxHeight - bottomBoxOffset) / 2);
|
|
yOffset = topBoxOffset + yAvailableSpace;
|
|
|
|
const centeredBoxX = Math.round((width - wsBoxWidth) / 2);
|
|
|
|
this._xAlignCenter = false;
|
|
if (centeredBoxX < centeredBoxOffset) {
|
|
xOffset = Math.round(leftBoxOffset + (width - leftBoxOffset - wsBoxWidth - rightBoxOffset) / 2);
|
|
} else {
|
|
xOffset = centeredBoxX;
|
|
this._xAlignCenter = true;
|
|
}
|
|
|
|
wsBoxX = startX + xOffset;
|
|
wsBoxY = startY + yOffset;
|
|
workspaceBox.set_origin(wsBoxX, wsBoxY);
|
|
workspaceBox.set_size(wsBoxWidth, wsBoxHeight);
|
|
}
|
|
}
|
|
|
|
return workspaceBox;
|
|
},
|
|
|
|
_getAppDisplayBoxForState(state, box, leftBoxOffset, rightBoxOffset, topBoxOffset, bottomBoxOffset) {
|
|
const appDisplayBox = new Clutter.ActorBox();
|
|
const startX = this._workAreaBoxForVShellConfig.x1;
|
|
const startY = this._workAreaBoxForVShellConfig.y1;
|
|
let [width, height] = this._workAreaBoxForVShellConfig.get_size();
|
|
const centeredBoxOffset = Math.max(leftBoxOffset, rightBoxOffset);
|
|
|
|
const adWidth = opt.CENTER_APP_GRID
|
|
? width - 2 * centeredBoxOffset
|
|
: width - leftBoxOffset - rightBoxOffset;
|
|
const adHeight = height - topBoxOffset - bottomBoxOffset;
|
|
|
|
const appDisplayX = startX +
|
|
(opt.CENTER_APP_GRID
|
|
? Math.round((width - adWidth) / 2)
|
|
: leftBoxOffset
|
|
);
|
|
const appDisplayY = startY + topBoxOffset;
|
|
|
|
switch (state) {
|
|
case ControlsState.HIDDEN:
|
|
case ControlsState.WINDOW_PICKER:
|
|
// 1 - left, 2 - right, 3 - bottom, 5 - top
|
|
switch (opt.APP_GRID_ANIMATION) {
|
|
case 0:
|
|
appDisplayBox.set_origin(appDisplayX, appDisplayY);
|
|
break;
|
|
case 1:
|
|
appDisplayBox.set_origin(startX + width, appDisplayY);
|
|
break;
|
|
case 2:
|
|
appDisplayBox.set_origin(box.x1 - adWidth, appDisplayY);
|
|
break;
|
|
case 3:
|
|
appDisplayBox.set_origin(appDisplayX, box.y2);
|
|
break;
|
|
case 5:
|
|
appDisplayBox.set_origin(appDisplayX, box.y1 - adHeight);
|
|
break;
|
|
}
|
|
break;
|
|
case ControlsState.APP_GRID:
|
|
appDisplayBox.set_origin(appDisplayX, appDisplayY);
|
|
break;
|
|
}
|
|
|
|
appDisplayBox.set_size(adWidth, adHeight);
|
|
return appDisplayBox;
|
|
},
|
|
|
|
vfunc_allocate(container, box) {
|
|
const childBox = new Clutter.ActorBox();
|
|
const startX = this._workAreaBoxForVShellConfig.x1;
|
|
const startY = this._workAreaBoxForVShellConfig.y1;
|
|
let [width, height] = this._workAreaBoxForVShellConfig.get_size();
|
|
|
|
const transitionParams = this._stateAdjustment.getStateTransitionParams();
|
|
const spacing = opt.SPACING;
|
|
|
|
// Dash
|
|
const maxDashHeight = Math.round(box.get_height() * DASH_MAX_SIZE_RATIO);
|
|
const maxDashWidth = Math.round(maxDashHeight * 0.8);
|
|
let dashHeight = 0;
|
|
let dashWidth = 0;
|
|
|
|
// dash cloud be overridden by the Dash to Dock clone
|
|
if (Me.Util.dashIsDashToDock()) {
|
|
this._updatePositionFromDashToDock();
|
|
// If DtD affects workArea, dash size needs to be 0 + spacing
|
|
const dash = Main.overview.dash;
|
|
if (this._dashToDockAffectsWorkArea()) {
|
|
if (opt.DASH_VERTICAL)
|
|
dashWidth = spacing;
|
|
else
|
|
dashHeight = spacing;
|
|
} else {
|
|
dashHeight = dash.height;
|
|
dashWidth = dash.width;
|
|
if (opt.DASH_VERTICAL)
|
|
dashWidth += spacing;
|
|
else
|
|
dashHeight += spacing;
|
|
}
|
|
} else if (this._dash.visible) {
|
|
// default dock
|
|
if (opt.DASH_VERTICAL) {
|
|
this._dash.setMaxSize(maxDashWidth, height);
|
|
[, dashWidth] = this._dash.get_preferred_width(height);
|
|
[, dashHeight] = this._dash.get_preferred_height(dashWidth);
|
|
dashWidth = Math.min(dashWidth, maxDashWidth);
|
|
dashHeight = Math.min(dashHeight, height);
|
|
} else if (!opt.WS_TMB_FULL) {
|
|
this._dash.setMaxSize(width, maxDashHeight);
|
|
[, dashHeight] = this._dash.get_preferred_height(width);
|
|
[, dashWidth] = this._dash.get_preferred_width(dashHeight);
|
|
dashHeight = Math.min(dashHeight, maxDashHeight);
|
|
dashWidth = Math.min(dashWidth, width);
|
|
}
|
|
}
|
|
|
|
// Workspace Thumbnails
|
|
let wsTmbWidth = 0;
|
|
let wsTmbHeight = 0;
|
|
|
|
if (opt.SHOW_WS_TMB) {
|
|
const searchActive = this._searchController.searchActive;
|
|
let maxWsTmbScale = this._dash.showAppsButton.checked && !(searchActive && !opt.SEARCH_APP_GRID_MODE)
|
|
? opt.MAX_THUMBNAIL_SCALE_APPGRID
|
|
: opt.MAX_THUMBNAIL_SCALE;
|
|
if (transitionParams.currentState % 1 && !opt.MAX_THUMBNAIL_SCALE_STABLE && !searchActive && !opt.LEAVING_SEARCH) {
|
|
const initState = transitionParams.initialState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE;
|
|
const finalState = transitionParams.finalState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE;
|
|
maxWsTmbScale = Util.lerp(initState, finalState, transitionParams.progress);
|
|
}
|
|
wsTmbWidth = Math.round(width * maxWsTmbScale);
|
|
|
|
let totalTmbSpacing;
|
|
[totalTmbSpacing, wsTmbHeight] = this._workspacesThumbnails.get_preferred_height(wsTmbWidth);
|
|
wsTmbHeight += totalTmbSpacing;
|
|
|
|
const wstTopOffset = !opt.WS_TMB_FULL && opt.DASH_TOP ? dashHeight : spacing;
|
|
const wstBottomOffset = !opt.WS_TMB_FULL && opt.DASH_BOTTOM ? dashHeight : spacing;
|
|
const wstLeftOffset = opt.DASH_LEFT ? dashWidth : spacing;
|
|
const wstRightOffset = opt.DASH_RIGHT ? dashWidth : spacing;
|
|
|
|
const wsTmbHeightMax = height - wstTopOffset - wstBottomOffset;
|
|
|
|
// Reduce size to fit wsTmb to the screen
|
|
if (wsTmbHeight > wsTmbHeightMax) {
|
|
wsTmbHeight = wsTmbHeightMax;
|
|
wsTmbWidth = this._workspacesThumbnails.get_preferred_width(wsTmbHeight)[1];
|
|
}
|
|
|
|
let wsTmbX = opt.WS_TMB_LEFT
|
|
? startX + wstLeftOffset
|
|
: startX + width - wstRightOffset - wsTmbWidth;
|
|
|
|
let offset = (height - wstTopOffset - wsTmbHeight - wstBottomOffset) / 2;
|
|
offset = Math.round(offset - (opt.WS_TMB_POSITION_ADJUSTMENT * offset));
|
|
const wsTmbY = startY + wstTopOffset + offset;
|
|
|
|
childBox.set_origin(wsTmbX, wsTmbY);
|
|
childBox.set_size(Math.max(wsTmbWidth, 1), Math.max(wsTmbHeight, 1));
|
|
|
|
this._workspacesThumbnails.allocate(childBox);
|
|
}
|
|
|
|
if (this._dash.visible) {
|
|
const wMaxWidth = width - spacing - wsTmbWidth - 2 * spacing - (opt.DASH_VERTICAL ? dashWidth + spacing : 0);
|
|
if (opt.WS_TMB_FULL && !opt.DASH_VERTICAL) {
|
|
this._dash.setMaxSize(wMaxWidth, maxDashHeight);
|
|
[, dashHeight] = this._dash.get_preferred_height(wMaxWidth);
|
|
[, dashWidth] = this._dash.get_preferred_width(dashHeight);
|
|
dashHeight = Math.min(dashHeight, maxDashHeight);
|
|
dashWidth = Math.min(dashWidth, wMaxWidth);
|
|
}
|
|
|
|
let dashX = opt.DASH_RIGHT ? width - dashWidth : 0;
|
|
let dashY = opt.DASH_TOP ? startY : startY + height - dashHeight;
|
|
|
|
if (!opt.DASH_VERTICAL) {
|
|
const dashLeftOffset = (opt.WS_TMB_FULL || opt.CENTER_DASH_WS) && opt.WS_TMB_LEFT ? wsTmbWidth + spacing : 0;
|
|
const dashRightOffset = (opt.WS_TMB_FULL || opt.CENTER_DASH_WS) && opt.WS_TMB_RIGHT ? wsTmbWidth + spacing : 0;
|
|
let offset = (width - dashWidth - (opt.CENTER_DASH_WS && !this._xAlignCenter ? dashLeftOffset + dashRightOffset : 0)) / 2;
|
|
offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing);
|
|
dashX = startX + (opt.CENTER_DASH_WS ? dashLeftOffset : 0) + offset;
|
|
if (opt.WS_TMB_FULL) // Limit the adjustment while keeping the center of adjustment on the screen center
|
|
dashX = Math.clamp(startX + dashLeftOffset + spacing, dashX, startX + width - dashRightOffset - spacing - dashWidth);
|
|
} else {
|
|
const offset = (height - dashHeight) / 2;
|
|
dashY = startY + (offset - opt.DASH_POSITION_ADJUSTMENT * (offset - spacing));
|
|
}
|
|
dashY = Math.round(dashY);
|
|
|
|
childBox.set_origin(startX + dashX, dashY);
|
|
childBox.set_size(dashWidth, dashHeight);
|
|
this._dash.allocate(childBox);
|
|
}
|
|
|
|
// View box offsets
|
|
const leftBoxOffset = (opt.DASH_LEFT ? dashWidth : spacing) + (opt.WS_TMB_LEFT ? wsTmbWidth + spacing : 0);
|
|
const rightBoxOffset = (opt.DASH_RIGHT ? dashWidth : spacing) + (opt.WS_TMB_RIGHT ? wsTmbWidth + spacing : 0);
|
|
let topBoxOffset = (opt.DASH_TOP ? dashHeight : spacing) + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0);
|
|
const bottomBoxOffset = (opt.DASH_BOTTOM ? dashHeight : spacing) + (opt.WS_TMB_BOTTOM ? wsTmbHeight + spacing : 0);
|
|
const centeredBoxOffset = Math.max(leftBoxOffset, rightBoxOffset);
|
|
|
|
// App grid needs to be calculated for the max wsTmbWidth in app grid, independently on the current wsTmb scale
|
|
const wsTmbWidthAppGrid = Math.round(width * opt.MAX_THUMBNAIL_SCALE_APPGRID);
|
|
const leftBoxOffsetAppGrid = (opt.DASH_LEFT ? dashWidth : spacing) + (opt.WS_TMB_LEFT ? wsTmbWidthAppGrid + spacing : 0);
|
|
const rightBoxOffsetAppGrid = (opt.DASH_RIGHT ? dashWidth : spacing) + (opt.WS_TMB_RIGHT ? wsTmbWidthAppGrid + spacing : 0);
|
|
|
|
// searchEntry
|
|
const [searchEntryHeight] = this._searchEntry.get_preferred_height(width - wsTmbWidth);
|
|
const searchEntryY = startY + topBoxOffset;
|
|
|
|
const searchX = startX +
|
|
(opt.CENTER_SEARCH_VIEW || this._xAlignCenter
|
|
? centeredBoxOffset
|
|
: leftBoxOffset); // xAlignCenter is set by wsBox
|
|
|
|
const searchWidth =
|
|
width - (opt.CENTER_SEARCH_VIEW || this._xAlignCenter
|
|
? 2 * centeredBoxOffset
|
|
: leftBoxOffset + rightBoxOffset);
|
|
|
|
childBox.set_origin(searchX, searchEntryY);
|
|
childBox.set_size(searchWidth, searchEntryHeight);
|
|
|
|
this._searchEntry.allocate(childBox);
|
|
|
|
// searchResults
|
|
const searchY = startY + topBoxOffset + searchEntryHeight + spacing;
|
|
const searchHeight = height - topBoxOffset - bottomBoxOffset - searchEntryHeight - 2 * spacing;
|
|
|
|
childBox.set_origin(searchX, searchY);
|
|
childBox.set_size(searchWidth, searchHeight);
|
|
this._searchController.allocate(childBox);
|
|
|
|
// Add searchEntry height if needed
|
|
topBoxOffset += opt.SHOW_SEARCH_ENTRY ? searchEntryHeight + spacing : 0;
|
|
|
|
// workspace
|
|
let params = [box, wsTmbWidth, wsTmbHeight, leftBoxOffset, rightBoxOffset, topBoxOffset, bottomBoxOffset, centeredBoxOffset];
|
|
|
|
// Update cached boxes
|
|
for (const state of Object.values(ControlsState)) {
|
|
this._cachedWorkspaceBoxes.set(
|
|
state, this._computeWorkspacesBoxForState(state, ...params));
|
|
}
|
|
|
|
let workspacesBox;
|
|
if (!transitionParams.transitioning)
|
|
workspacesBox = this._cachedWorkspaceBoxes.get(transitionParams.currentState);
|
|
|
|
if (!workspacesBox) {
|
|
const initialBox = this._cachedWorkspaceBoxes.get(transitionParams.initialState);
|
|
const finalBox = this._cachedWorkspaceBoxes.get(transitionParams.finalState);
|
|
workspacesBox = initialBox.interpolate(finalBox, transitionParams.progress);
|
|
}
|
|
|
|
this._workspacesDisplay.allocate(workspacesBox);
|
|
|
|
// appDisplay
|
|
params = [
|
|
box,
|
|
leftBoxOffsetAppGrid,
|
|
rightBoxOffsetAppGrid,
|
|
topBoxOffset,
|
|
bottomBoxOffset,
|
|
];
|
|
let appDisplayBox;
|
|
if (!transitionParams.transitioning) {
|
|
appDisplayBox =
|
|
this._getAppDisplayBoxForState(transitionParams.currentState, ...params);
|
|
} else {
|
|
const initialBox =
|
|
this._getAppDisplayBoxForState(transitionParams.initialState, ...params);
|
|
const finalBox =
|
|
this._getAppDisplayBoxForState(transitionParams.finalState, ...params);
|
|
|
|
appDisplayBox = initialBox.interpolate(finalBox, transitionParams.progress);
|
|
}
|
|
this._appDisplay.allocate(appDisplayBox);
|
|
|
|
this._runPostAllocation();
|
|
},
|
|
};
|
|
|
|
const ControlsManagerLayoutHorizontal = {
|
|
_computeWorkspacesBoxForState: ControlsManagerLayoutVertical._computeWorkspacesBoxForState,
|
|
|
|
_getAppDisplayBoxForState: ControlsManagerLayoutVertical._getAppDisplayBoxForState,
|
|
|
|
vfunc_allocate(container, box) {
|
|
const childBox = new Clutter.ActorBox();
|
|
const startX = this._workAreaBoxForVShellConfig.x1;
|
|
const startY = this._workAreaBoxForVShellConfig.y1;
|
|
let [width, height] = this._workAreaBoxForVShellConfig.get_size();
|
|
|
|
const transitionParams = this._stateAdjustment.getStateTransitionParams();
|
|
const spacing = opt.SPACING;
|
|
|
|
// Dash
|
|
const maxDashHeight = Math.round(box.get_height() * DASH_MAX_SIZE_RATIO);
|
|
const maxDashWidth = Math.round(maxDashHeight * 0.8);
|
|
let dashHeight = 0;
|
|
let dashWidth = 0;
|
|
|
|
// dash cloud be overridden by the Dash to Dock clone
|
|
if (Me.Util.dashIsDashToDock()) {
|
|
this._updatePositionFromDashToDock();
|
|
// If DtD affects workArea, dash size needs to be 0
|
|
const dash = Main.overview.dash;
|
|
if (this._dashToDockAffectsWorkArea()) {
|
|
if (opt.DASH_VERTICAL)
|
|
dashWidth = spacing;
|
|
else
|
|
dashHeight = spacing;
|
|
} else {
|
|
dashHeight = dash.height;
|
|
dashWidth = dash.width;
|
|
if (opt.DASH_VERTICAL)
|
|
dashWidth += spacing;
|
|
else
|
|
dashHeight += spacing;
|
|
}
|
|
} else if (this._dash.visible) {
|
|
// default dock
|
|
if (!opt.DASH_VERTICAL) {
|
|
this._dash.setMaxSize(width, maxDashHeight);
|
|
[, dashHeight] = this._dash.get_preferred_height(width);
|
|
[, dashWidth] = this._dash.get_preferred_width(dashHeight);
|
|
dashHeight = Math.min(dashHeight, maxDashHeight);
|
|
dashWidth = Math.min(dashWidth, width - spacing);
|
|
} else if (!opt.WS_TMB_FULL) {
|
|
this._dash.setMaxSize(maxDashWidth, height);
|
|
[, dashWidth] = this._dash.get_preferred_width(height);
|
|
[, dashHeight] = this._dash.get_preferred_height(dashWidth);
|
|
dashHeight = Math.min(dashHeight, height - spacing);
|
|
dashWidth = Math.min(dashWidth, width);
|
|
}
|
|
}
|
|
|
|
const [searchEntryHeight] = this._searchEntry.get_preferred_height(width);
|
|
|
|
// Workspace Thumbnails
|
|
let wsTmbWidth = 0;
|
|
let wsTmbHeight = 0;
|
|
|
|
if (opt.SHOW_WS_TMB) {
|
|
const searchActive = this._searchController.searchActive;
|
|
let maxWsTmbScale = this._dash.showAppsButton.checked && !(searchActive && !opt.SEARCH_APP_GRID_MODE)
|
|
? opt.MAX_THUMBNAIL_SCALE_APPGRID
|
|
: opt.MAX_THUMBNAIL_SCALE;
|
|
if (transitionParams.currentState % 1 && !opt.MAX_THUMBNAIL_SCALE_STABLE && !searchActive && !opt.LEAVING_SEARCH) {
|
|
const initState = transitionParams.initialState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE;
|
|
const finalState = transitionParams.finalState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE;
|
|
maxWsTmbScale = Util.lerp(initState, finalState, transitionParams.progress);
|
|
}
|
|
|
|
wsTmbHeight = Math.round(height * maxWsTmbScale);
|
|
|
|
let totalTmbSpacing;
|
|
[totalTmbSpacing, wsTmbWidth] = this._workspacesThumbnails.get_preferred_width(wsTmbHeight);
|
|
wsTmbWidth += totalTmbSpacing;
|
|
|
|
const wstLeftOffset = !opt.WS_TMB_FULL && opt.DASH_LEFT ? dashWidth : spacing;
|
|
const wstRightOffset = !opt.WS_TMB_FULL && opt.DASH_RIGHT ? dashWidth : spacing;
|
|
const wstTopOffset = opt.DASH_TOP ? dashHeight : spacing;
|
|
const wstBottomOffset = opt.DASH_BOTTOM ? dashHeight : spacing;
|
|
|
|
const wsTmbWidthMax = width - wstLeftOffset - wstRightOffset;
|
|
// Reduce size to fit wsTmb to the screen
|
|
if (wsTmbWidth > wsTmbWidthMax) {
|
|
wsTmbWidth = wsTmbWidthMax;
|
|
wsTmbHeight = this._workspacesThumbnails.get_preferred_height(wsTmbWidth)[1];
|
|
}
|
|
|
|
let wsTmbY = opt.WS_TMB_TOP
|
|
? startY + wstTopOffset
|
|
: startY + height - wstBottomOffset - wsTmbHeight;
|
|
|
|
let offset = (width - wstLeftOffset - wsTmbWidth - wstRightOffset) / 2;
|
|
offset = Math.round(offset - (opt.WS_TMB_POSITION_ADJUSTMENT * offset));
|
|
const wsTmbX = startX + wstLeftOffset + offset;
|
|
|
|
childBox.set_origin(wsTmbX, wsTmbY);
|
|
childBox.set_size(Math.max(wsTmbWidth, 1), Math.max(wsTmbHeight, 1));
|
|
this._workspacesThumbnails.allocate(childBox);
|
|
}
|
|
|
|
if (this._dash.visible) {
|
|
if (opt.WS_TMB_FULL && opt.DASH_VERTICAL) {
|
|
const wMaxHeight = height - spacing - wsTmbHeight;
|
|
this._dash.setMaxSize(maxDashWidth, wMaxHeight);
|
|
[, dashWidth] = this._dash.get_preferred_width(wMaxHeight);
|
|
[, dashHeight] = this._dash.get_preferred_height(dashWidth);
|
|
dashWidth = Math.min(dashWidth, maxDashWidth);
|
|
dashHeight = Math.min(dashHeight, wMaxHeight);
|
|
}
|
|
|
|
let dashX = opt.DASH_RIGHT ? width - dashWidth : 0;
|
|
let dashY = opt.DASH_TOP ? startY : startY + height - dashHeight;
|
|
|
|
if (opt.DASH_VERTICAL) {
|
|
const dashTopOffset = (opt.WS_TMB_FULL || opt.CENTER_DASH_WS) && opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0;
|
|
const dashBottomOffset = (opt.WS_TMB_FULL || opt.CENTER_DASH_WS) && opt.WS_TMB_BOTTOM ? wsTmbHeight + spacing : 0;
|
|
let offset = (height - dashHeight - (opt.CENTER_DASH_WS ? dashTopOffset + dashBottomOffset : 0)) / 2;
|
|
offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing);
|
|
dashY = startY + (opt.CENTER_DASH_WS ? dashTopOffset : 0) + offset;
|
|
if (opt.WS_TMB_FULL) // Limit the adjustment while keeping the center of adjustment on the screen center
|
|
dashY = Math.clamp(startY + dashTopOffset + spacing, dashY, startY + height - dashBottomOffset - spacing - dashHeight);
|
|
} else {
|
|
const offset = (width - dashWidth) / 2;
|
|
dashX = startX + (offset - opt.DASH_POSITION_ADJUSTMENT * (offset - spacing));
|
|
}
|
|
dashX = Math.round(dashX);
|
|
|
|
childBox.set_origin(startX + dashX, dashY);
|
|
childBox.set_size(dashWidth, dashHeight);
|
|
this._dash.allocate(childBox);
|
|
}
|
|
|
|
// Main view offsets
|
|
const leftBoxOffset = opt.DASH_LEFT ? dashWidth : spacing;
|
|
const rightBoxOffset = opt.DASH_RIGHT ? dashWidth : spacing;
|
|
let topBoxOffset = (opt.DASH_TOP ? dashHeight : spacing) + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0);
|
|
const bottomBoxOffset = (opt.DASH_BOTTOM ? dashHeight : spacing) + (opt.WS_TMB_BOTTOM ? wsTmbHeight + spacing : 0);
|
|
const centeredBoxOffset = Math.max(leftBoxOffset, rightBoxOffset);
|
|
|
|
// App grid needs to be calculated for the max wsTmbWidth in app grid, independently on the current wsTmb scale
|
|
const wsTmbHeightAppGrid = Math.round(height * opt.MAX_THUMBNAIL_SCALE_APPGRID);
|
|
const topBoxOffsetAppGrid = (opt.DASH_TOP ? dashHeight : spacing) + (opt.WS_TMB_TOP ? wsTmbHeightAppGrid + spacing : 0) + (opt.SHOW_SEARCH_ENTRY ? searchEntryHeight + spacing : 0);
|
|
const bottomBoxOffsetAppGrid = (opt.DASH_BOTTOM ? dashHeight : spacing) + (opt.WS_TMB_BOTTOM ? wsTmbHeightAppGrid + spacing : 0);
|
|
|
|
// searchEntry
|
|
const searchEntryY = startY + topBoxOffset;
|
|
|
|
const searchX = startX +
|
|
(opt.CENTER_SEARCH_VIEW || this._xAlignCenter
|
|
? centeredBoxOffset
|
|
: leftBoxOffset); // xAlignCenter is set by wsBox
|
|
|
|
const searchWidth =
|
|
width - (opt.CENTER_SEARCH_VIEW || this._xAlignCenter
|
|
? 2 * centeredBoxOffset
|
|
: leftBoxOffset + rightBoxOffset);
|
|
|
|
childBox.set_origin(searchX, searchEntryY);
|
|
childBox.set_size(searchWidth, searchEntryHeight);
|
|
|
|
this._searchEntry.allocate(childBox);
|
|
|
|
// searchResults
|
|
const searchY = startY + topBoxOffset + searchEntryHeight + spacing;
|
|
const searchHeight = height - topBoxOffset - bottomBoxOffset - searchEntryHeight - 2 * spacing;
|
|
|
|
childBox.set_origin(searchX, searchY);
|
|
childBox.set_size(searchWidth, searchHeight);
|
|
this._searchController.allocate(childBox);
|
|
|
|
// Add searchEntry height if needed
|
|
topBoxOffset += opt.SHOW_SEARCH_ENTRY ? searchEntryHeight + spacing : 0;
|
|
|
|
// Workspace
|
|
let params = [
|
|
box,
|
|
wsTmbWidth,
|
|
wsTmbHeight,
|
|
leftBoxOffset,
|
|
rightBoxOffset,
|
|
topBoxOffset,
|
|
bottomBoxOffset,
|
|
centeredBoxOffset,
|
|
];
|
|
|
|
// Update cached boxes
|
|
for (const state of Object.values(ControlsState)) {
|
|
this._cachedWorkspaceBoxes.set(
|
|
state, this._computeWorkspacesBoxForState(state, ...params));
|
|
}
|
|
|
|
let workspacesBox;
|
|
if (!transitionParams.transitioning)
|
|
workspacesBox = this._cachedWorkspaceBoxes.get(transitionParams.currentState);
|
|
|
|
if (!workspacesBox) {
|
|
const initialBox = this._cachedWorkspaceBoxes.get(transitionParams.initialState);
|
|
const finalBox = this._cachedWorkspaceBoxes.get(transitionParams.finalState);
|
|
workspacesBox = initialBox.interpolate(finalBox, transitionParams.progress);
|
|
}
|
|
|
|
this._workspacesDisplay.allocate(workspacesBox);
|
|
|
|
// appDisplay
|
|
params = [
|
|
box,
|
|
leftBoxOffset === spacing ? 0 : leftBoxOffset,
|
|
rightBoxOffset === spacing ? 0 : rightBoxOffset,
|
|
topBoxOffsetAppGrid,
|
|
bottomBoxOffsetAppGrid,
|
|
];
|
|
let appDisplayBox;
|
|
if (!transitionParams.transitioning) {
|
|
appDisplayBox =
|
|
this._getAppDisplayBoxForState(transitionParams.currentState, ...params);
|
|
} else {
|
|
const initialBox =
|
|
this._getAppDisplayBoxForState(transitionParams.initialState, ...params);
|
|
const finalBox =
|
|
this._getAppDisplayBoxForState(transitionParams.finalState, ...params);
|
|
|
|
appDisplayBox = initialBox.interpolate(finalBox, transitionParams.progress);
|
|
}
|
|
this._appDisplay.allocate(appDisplayBox);
|
|
|
|
this._runPostAllocation();
|
|
},
|
|
};
|
|
|
|
// same copy of this function should be available in OverviewControls and WorkspacesView
|
|
function _getFitModeForState(state) {
|
|
switch (state) {
|
|
case ControlsState.HIDDEN:
|
|
case ControlsState.WINDOW_PICKER:
|
|
return FitMode.SINGLE;
|
|
case ControlsState.APP_GRID:
|
|
if (opt.WS_ANIMATION && opt.SHOW_WS_TMB)
|
|
return FitMode.ALL;
|
|
else
|
|
return FitMode.SINGLE;
|
|
default:
|
|
return FitMode.SINGLE;
|
|
}
|
|
}
|