865 lines
33 KiB
JavaScript
865 lines
33 KiB
JavaScript
|
/**
|
||
|
* Vertical Workspaces
|
||
|
* workspacesView.js
|
||
|
*
|
||
|
* @author GdH <G-dH@github.com>
|
||
|
* @copyright 2022 - 2023
|
||
|
* @license GPL-3.0
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
const { GObject, Clutter, Meta, St } = imports.gi;
|
||
|
|
||
|
const Main = imports.ui.main;
|
||
|
const Util = imports.misc.util;
|
||
|
const WorkspacesView = imports.ui.workspacesView;
|
||
|
//const SecondaryMonitorDisplay = WorkspacesView.SecondaryMonitorDisplay;
|
||
|
// first call of item defined using const in other module returns undefined
|
||
|
WorkspacesView.SecondaryMonitorDisplay;
|
||
|
const ControlsState = imports.ui.overviewControls.ControlsState;
|
||
|
const FitMode = imports.ui.workspacesView.FitMode;
|
||
|
|
||
|
const SIDE_CONTROLS_ANIMATION_TIME = imports.ui.overview.ANIMATION_TIME;
|
||
|
|
||
|
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||
|
const SEARCH_WINDOWS_PREFIX = Me.imports.windowSearchProvider.prefix;
|
||
|
const SEARCH_RECENT_FILES_PREFIX = Me.imports.recentFilesSearchProvider.prefix;
|
||
|
|
||
|
const _Util = Me.imports.util;
|
||
|
let _overrides;
|
||
|
|
||
|
let opt;
|
||
|
|
||
|
|
||
|
|
||
|
function update(reset = false) {
|
||
|
if (_overrides) {
|
||
|
_overrides.removeAll();
|
||
|
global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, 1, -1);
|
||
|
}
|
||
|
|
||
|
if (reset) {
|
||
|
_overrides = null;
|
||
|
opt = null;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
opt = Me.imports.settings.opt;
|
||
|
|
||
|
_overrides = new _Util.Overrides();
|
||
|
|
||
|
_overrides.addOverride('WorkspacesView', WorkspacesView.WorkspacesView.prototype, WorkspacesViewCommon);
|
||
|
_overrides.addOverride('WorkspacesDisplay', WorkspacesView.WorkspacesDisplay.prototype, WorkspacesDisplay);
|
||
|
|
||
|
if (opt.ORIENTATION) {
|
||
|
// switch internal workspace orientation in GS
|
||
|
global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, -1, 1);
|
||
|
_overrides.addOverride('SecondaryMonitorDisplay', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayVertical);
|
||
|
} else {
|
||
|
_overrides.addOverride('SecondaryMonitorDisplay', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayHorizontal);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var WorkspacesViewCommon = {
|
||
|
_getFirstFitSingleWorkspaceBox: function(box, spacing, vertical) {
|
||
|
let [width, height] = box.get_size();
|
||
|
const [workspace] = this._workspaces;
|
||
|
|
||
|
const rtl = this.text_direction === Clutter.TextDirection.RTL;
|
||
|
const adj = this._scrollAdjustment;
|
||
|
const currentWorkspace = vertical || !rtl
|
||
|
? adj.value : adj.upper - adj.value - 1;
|
||
|
|
||
|
// Single fit mode implies centered too
|
||
|
let [x1, y1] = box.get_origin();
|
||
|
const [, workspaceWidth] = workspace ? workspace.get_preferred_width(Math.floor(height)) : [,width];
|
||
|
const [, workspaceHeight] = workspace ? workspace.get_preferred_height(workspaceWidth) : [,height];
|
||
|
|
||
|
if (vertical) {
|
||
|
x1 += (width - workspaceWidth) / 2;
|
||
|
y1 -= currentWorkspace * (workspaceHeight + spacing);
|
||
|
} else {
|
||
|
x1 += (width - workspaceWidth) / 2;
|
||
|
x1 -= currentWorkspace * (workspaceWidth + spacing);
|
||
|
}
|
||
|
|
||
|
const fitSingleBox = new Clutter.ActorBox({x1, y1});
|
||
|
|
||
|
fitSingleBox.set_size(workspaceWidth, workspaceHeight);
|
||
|
|
||
|
return fitSingleBox;
|
||
|
},
|
||
|
|
||
|
// set spacing between ws previews
|
||
|
_getSpacing: function(box, fitMode, vertical) {
|
||
|
const [width, height] = box.get_size();
|
||
|
const [workspace] = this._workspaces;
|
||
|
|
||
|
if (!workspace) return;
|
||
|
|
||
|
let availableSpace;
|
||
|
let workspaceSize;
|
||
|
if (vertical) {
|
||
|
[, workspaceSize] = workspace.get_preferred_height(width);
|
||
|
availableSpace = height;
|
||
|
} else {
|
||
|
[, workspaceSize] = workspace.get_preferred_width(height);
|
||
|
availableSpace = width;
|
||
|
}
|
||
|
|
||
|
const spacing = (availableSpace - workspaceSize * 0.4) * (1 - fitMode);
|
||
|
const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
|
||
|
return Math.clamp(spacing,
|
||
|
opt.WORKSPACE_MIN_SPACING * scaleFactor,
|
||
|
opt.WORKSPACE_MAX_SPACING * scaleFactor);
|
||
|
},
|
||
|
|
||
|
// this function has duplicate in OverviewControls so we use one function for both to avoid issues with syncing them
|
||
|
_getFitModeForState: function(state) {
|
||
|
return _getFitModeForState(state);
|
||
|
},
|
||
|
|
||
|
// normal view 0, spread windows 1
|
||
|
_getWorkspaceModeForOverviewState: function(state) {
|
||
|
|
||
|
switch (state) {
|
||
|
case ControlsState.HIDDEN:
|
||
|
return 0;
|
||
|
case ControlsState.WINDOW_PICKER:
|
||
|
return opt.WORKSPACE_MODE;
|
||
|
case ControlsState.APP_GRID:
|
||
|
return ((this._monitorIndex !== global.display.get_primary_monitor() || !opt.WS_ANIMATION) && !opt.OVERVIEW_MODE) ? 1 : 0;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
},
|
||
|
|
||
|
_updateVisibility: function() {
|
||
|
let workspaceManager = global.workspace_manager;
|
||
|
let active = workspaceManager.get_active_workspace_index();
|
||
|
|
||
|
const fitMode = this._fitModeAdjustment.value;
|
||
|
const singleFitMode = fitMode === FitMode.SINGLE;
|
||
|
|
||
|
for (let w = 0; w < this._workspaces.length; w++) {
|
||
|
let workspace = this._workspaces[w];
|
||
|
|
||
|
if (this._animating || this._gestureActive || !singleFitMode) {
|
||
|
//workspace.show();
|
||
|
} else {
|
||
|
workspace.visible = Math.abs(w - active) <= opt.NUMBER_OF_VISIBLE_NEIGHBORS;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// disable scaling and hide inactive workspaces
|
||
|
_updateWorkspacesState: function() {
|
||
|
const adj = this._scrollAdjustment;
|
||
|
const fitMode = this._fitModeAdjustment.value;
|
||
|
|
||
|
let { initialState, finalState, progress, currentState } =
|
||
|
this._overviewAdjustment.getStateTransitionParams();
|
||
|
|
||
|
const workspaceMode = (1 - fitMode) * Util.lerp(
|
||
|
this._getWorkspaceModeForOverviewState(initialState),
|
||
|
this._getWorkspaceModeForOverviewState(finalState),
|
||
|
progress);
|
||
|
|
||
|
|
||
|
const currentMonitor = Main.layoutManager.primaryMonitor.index;
|
||
|
|
||
|
// define the transition values here to save time in each ws
|
||
|
let scaleX, scaleY;
|
||
|
if (opt.ORIENTATION) { //vertical 1 / horizontal 0
|
||
|
scaleX = 1;
|
||
|
scaleY = 0.1;
|
||
|
} else {
|
||
|
scaleX = 0.1;
|
||
|
scaleY = 1;
|
||
|
}
|
||
|
|
||
|
const secondaryMonitor = this._monitorIndex !== global.display.get_primary_monitor();
|
||
|
const blockSecondaryAppGrid = opt.OVERVIEW_MODE && currentState >= 1;
|
||
|
// Hide inactive workspaces
|
||
|
this._workspaces.forEach((w, index) => {
|
||
|
if (!(blockSecondaryAppGrid && secondaryMonitor))
|
||
|
w.stateAdjustment.value = workspaceMode;
|
||
|
//w.stateAdjustment.value = workspaceMode;
|
||
|
|
||
|
const distanceToCurrentWorkspace = Math.abs(adj.value - index);
|
||
|
|
||
|
const scaleProgress = 1 - Math.clamp(distanceToCurrentWorkspace, 0, 1);
|
||
|
|
||
|
// if we disable workspaces that we can't or don't need to see, transition animations will be noticeably smoother
|
||
|
|
||
|
// only the current ws needs to be visible during overview transition animations
|
||
|
// and only current and adjacent ws when switching ws
|
||
|
if (opt.WORKSPACE_MAX_SPACING > 340) { // large spacing - only one workspace needs to be visible at once in the overview
|
||
|
w.visible = scaleProgress || ((currentState % 1) && !distanceToCurrentWorkspace);
|
||
|
|
||
|
// horizontal orientation - 2 adjacent workspaces can be visible on the screen with the current one
|
||
|
// in order to keep animations as smooth as possible, hide all ws that cannot/shouldn't be visible at the given time
|
||
|
} else {
|
||
|
//
|
||
|
w.visible = w.monitorIndex !== currentMonitor || scaleProgress || (!opt.WS_ANIMATION && distanceToCurrentWorkspace < opt.NUMBER_OF_VISIBLE_NEIGHBORS)
|
||
|
|| (distanceToCurrentWorkspace < opt.NUMBER_OF_VISIBLE_NEIGHBORS && currentState <= ControlsState.WINDOW_PICKER
|
||
|
&& ((initialState < ControlsState.APP_GRID && finalState < ControlsState.APP_GRID))
|
||
|
);
|
||
|
|
||
|
// after transition from APP_GRID to WINDOW_PICKER state,
|
||
|
// adjacent workspaces are hidden and we need them to show up
|
||
|
// make them visible during animation can impact smoothness of the animation
|
||
|
// so we show them after the animation finished, scaling animation will make impression that they move in from outside the monitor
|
||
|
if (!w.visible && distanceToCurrentWorkspace <= opt.NUMBER_OF_VISIBLE_NEIGHBORS && currentState === ControlsState.WINDOW_PICKER) {
|
||
|
w.scale_x = scaleX;
|
||
|
w.scale_y = scaleY;
|
||
|
w.visible = true;
|
||
|
w.ease({
|
||
|
duration: 100,
|
||
|
scale_x: 1,
|
||
|
scale_y: 1,
|
||
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// force ws preview bg corner radiuses where GS doesn't do it
|
||
|
if (opt.SHOW_WS_PREVIEW_BG && opt.OVERVIEW_MODE === 1 && distanceToCurrentWorkspace < 2) {
|
||
|
w._background._updateBorderRadius(Math.min(1, w._overviewAdjustment.value));
|
||
|
}
|
||
|
|
||
|
// hide workspace background
|
||
|
if (!opt.SHOW_WS_PREVIEW_BG && w._background.opacity) {
|
||
|
w._background.opacity = 0;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// SecondaryMonitorDisplay Vertical
|
||
|
var SecondaryMonitorDisplayVertical = {
|
||
|
_getThumbnailParamsForState: function(state) {
|
||
|
|
||
|
let opacity, scale, translation_x;
|
||
|
switch (state) {
|
||
|
case ControlsState.HIDDEN:
|
||
|
opacity = 255;
|
||
|
scale = 1;
|
||
|
translation_x = 0;
|
||
|
if (!Main.layoutManager._startingUp && (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2)) {
|
||
|
translation_x = this._thumbnails.width * (opt.SEC_WS_TMB_LEFT ? -1 : 1);
|
||
|
}
|
||
|
break;
|
||
|
case ControlsState.WINDOW_PICKER:
|
||
|
case ControlsState.APP_GRID:
|
||
|
opacity = 255;
|
||
|
scale = 1;
|
||
|
translation_x = 0;
|
||
|
break;
|
||
|
default:
|
||
|
opacity = 255;
|
||
|
scale = 1;
|
||
|
translation_x = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return { opacity, scale, translation_x };
|
||
|
},
|
||
|
|
||
|
_getThumbnailsWidth: function(box, spacing) {
|
||
|
if (!this._thumbnails.visible)
|
||
|
return 0;
|
||
|
|
||
|
const [width, height] = box.get_size();
|
||
|
const { expandFraction } = this._thumbnails;
|
||
|
const [, thumbnailsWidth] = this._thumbnails.get_preferred_custom_width(height - 2 * spacing);
|
||
|
return Math.min(
|
||
|
thumbnailsWidth * expandFraction,
|
||
|
width * opt.MAX_THUMBNAIL_SCALE);
|
||
|
},
|
||
|
|
||
|
_getWorkspacesBoxForState: function(state, box, padding, thumbnailsWidth, spacing) {
|
||
|
//const { ControlsState } = OverviewControls;
|
||
|
const workspaceBox = box.copy();
|
||
|
const [width, height] = workspaceBox.get_size();
|
||
|
|
||
|
switch (state) {
|
||
|
case ControlsState.HIDDEN:
|
||
|
break;
|
||
|
case ControlsState.WINDOW_PICKER:
|
||
|
case ControlsState.APP_GRID:
|
||
|
if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
let wWidth = Math.round(width - thumbnailsWidth - 5 * spacing);
|
||
|
let wHeight = Math.round(Math.min(wWidth / (width / height), height - 1.7 * padding));
|
||
|
wWidth *= opt.WS_PREVIEW_SCALE;
|
||
|
wHeight *= opt.WS_PREVIEW_SCALE;
|
||
|
|
||
|
let wsbX;
|
||
|
let offset = Math.round(width - thumbnailsWidth - wWidth) / 2;
|
||
|
if (opt.SEC_WS_TMB_LEFT) {
|
||
|
wsbX = thumbnailsWidth + offset;
|
||
|
} else {
|
||
|
wsbX = offset;
|
||
|
}
|
||
|
|
||
|
const wsbY = Math.round((height - wHeight) / 2);
|
||
|
|
||
|
workspaceBox.set_origin(wsbX, wsbY);
|
||
|
workspaceBox.set_size(wWidth, wHeight);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return workspaceBox;
|
||
|
},
|
||
|
|
||
|
vfunc_allocate: function(box) {
|
||
|
this.set_allocation(box);
|
||
|
|
||
|
const themeNode = this.get_theme_node();
|
||
|
const contentBox = themeNode.get_content_box(box);
|
||
|
const [width, height] = contentBox.get_size();
|
||
|
const { expandFraction } = this._thumbnails;
|
||
|
const spacing = themeNode.get_length('spacing') * expandFraction;
|
||
|
const padding = Math.round(0.1 * height);
|
||
|
|
||
|
let thumbnailsWidth = this._getThumbnailsWidth(contentBox, spacing);
|
||
|
let [, thumbnailsHeight] = this._thumbnails.get_preferred_custom_height(thumbnailsWidth);
|
||
|
thumbnailsHeight = Math.min(thumbnailsHeight, height - 2 * spacing);
|
||
|
|
||
|
this._thumbnails.visible = opt.SHOW_WS_TMB;
|
||
|
if (this._thumbnails.visible) {
|
||
|
let wsTmbX;
|
||
|
if (opt.SEC_WS_TMB_LEFT) { // left
|
||
|
wsTmbX = Math.round(spacing / 4);
|
||
|
this._thumbnails._positionLeft = true;
|
||
|
} else {
|
||
|
wsTmbX = Math.round(width - spacing / 4 - thumbnailsWidth);
|
||
|
this._thumbnails._positionLeft = false;
|
||
|
}
|
||
|
|
||
|
const childBox = new Clutter.ActorBox();
|
||
|
const availSpace = height - thumbnailsHeight - 2 * spacing;
|
||
|
|
||
|
let wsTmbY = availSpace / 2;
|
||
|
wsTmbY -= opt.SEC_WS_TMB_POSITION_ADJUSTMENT * wsTmbY - spacing;
|
||
|
|
||
|
childBox.set_origin(Math.round(wsTmbX), Math.round(wsTmbY));
|
||
|
childBox.set_size(thumbnailsWidth, thumbnailsHeight);
|
||
|
this._thumbnails.allocate(childBox);
|
||
|
}
|
||
|
|
||
|
const {
|
||
|
currentState, initialState, finalState, transitioning, progress,
|
||
|
} = this._overviewAdjustment.getStateTransitionParams();
|
||
|
|
||
|
let workspacesBox;
|
||
|
const workspaceParams = [contentBox, padding, thumbnailsWidth, spacing];
|
||
|
if (!transitioning) {
|
||
|
workspacesBox =
|
||
|
this._getWorkspacesBoxForState(currentState, ...workspaceParams);
|
||
|
} else {
|
||
|
const initialBox =
|
||
|
this._getWorkspacesBoxForState(initialState, ...workspaceParams);
|
||
|
const finalBox =
|
||
|
this._getWorkspacesBoxForState(finalState, ...workspaceParams);
|
||
|
workspacesBox = initialBox.interpolate(finalBox, progress);
|
||
|
}
|
||
|
this._workspacesView.allocate(workspacesBox);
|
||
|
},
|
||
|
|
||
|
_updateThumbnailVisibility: function() {
|
||
|
if (opt.OVERVIEW_MODE2) {
|
||
|
this.set_child_above_sibling(this._thumbnails, null);
|
||
|
}
|
||
|
|
||
|
const visible = !(this._settings.get_boolean('workspaces-only-on-primary') ||
|
||
|
opt.SEC_WS_TMB_POSITION === 3); // 3 - disabled
|
||
|
|
||
|
if (this._thumbnails.visible === visible)
|
||
|
return;
|
||
|
|
||
|
this._thumbnails.show();
|
||
|
this._updateThumbnailParams();
|
||
|
this._thumbnails.ease_property('expand-fraction', visible ? 1 : 0, {
|
||
|
duration: SIDE_CONTROLS_ANIMATION_TIME,
|
||
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||
|
onComplete: () => {
|
||
|
this._thumbnails.visible = visible;
|
||
|
this._thumbnails._indicator.visible = visible;
|
||
|
},
|
||
|
});
|
||
|
},
|
||
|
|
||
|
_updateThumbnailParams: function() {
|
||
|
// workaround for upstream bug - secondary thumbnails boxes don't catch 'showing' signal on the shell startup and don't populate the box with thumbnails
|
||
|
// the tmbBox contents is also destroyed when overview state adjustment gets above 1 when swiping gesture from window picker to app grid
|
||
|
if (!this._thumbnails._thumbnails.length) {
|
||
|
this._thumbnails._createThumbnails();
|
||
|
}
|
||
|
|
||
|
if (!this._thumbnails.visible)
|
||
|
return;
|
||
|
|
||
|
const { initialState, finalState, progress } =
|
||
|
this._overviewAdjustment.getStateTransitionParams();
|
||
|
|
||
|
const initialParams = this._getThumbnailParamsForState(initialState);
|
||
|
const finalParams = this._getThumbnailParamsForState(finalState);
|
||
|
|
||
|
/*const opacity =
|
||
|
Util.lerp(initialParams.opacity, finalParams.opacity, progress);
|
||
|
const scale =
|
||
|
Util.lerp(initialParams.scale, finalParams.scale, progress);*/
|
||
|
|
||
|
// 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);
|
||
|
const translation_x = (!Main.layoutManager._startingUp && ((!opt.SHOW_WS_PREVIEW_BG && !(opt.OVERVIEW_MODE2)) || animateOverviewMode2))
|
||
|
? Util.lerp(initialParams.translation_x, finalParams.translation_x, progress)
|
||
|
: 0;
|
||
|
|
||
|
this._thumbnails.set({
|
||
|
opacity: 255,
|
||
|
//scale_x: scale,
|
||
|
//scale_y: scale,
|
||
|
translation_x,
|
||
|
});
|
||
|
},
|
||
|
|
||
|
_updateWorkspacesView: function() {
|
||
|
if (this._workspacesView)
|
||
|
this._workspacesView.destroy();
|
||
|
|
||
|
if (this._settings.get_boolean('workspaces-only-on-primary')) {
|
||
|
this._workspacesView = new WorkspacesView.ExtraWorkspaceView(
|
||
|
this._monitorIndex,
|
||
|
this._overviewAdjustment);
|
||
|
} else {
|
||
|
this._workspacesView = new WorkspacesView.WorkspacesView(
|
||
|
this._monitorIndex,
|
||
|
this._controls,
|
||
|
this._scrollAdjustment,
|
||
|
// Secondary monitors don't need FitMode.ALL since there is workspace switcher always visible
|
||
|
//this._fitModeAdjustment,
|
||
|
new St.Adjustment({
|
||
|
actor: this,
|
||
|
value: 0,//FitMode.SINGLE,
|
||
|
lower: 0,//FitMode.SINGLE,
|
||
|
upper: 0,//FitMode.SINGLE,
|
||
|
}),
|
||
|
//secondaryOverviewAdjustment);
|
||
|
this._overviewAdjustment);
|
||
|
}
|
||
|
this.add_child(this._workspacesView);
|
||
|
this._thumbnails.opacity = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// SecondaryMonitorDisplay Horizontal
|
||
|
var SecondaryMonitorDisplayHorizontal = {
|
||
|
_getThumbnailParamsForState: function(state) {
|
||
|
//const { ControlsState } = OverviewControls;
|
||
|
|
||
|
let opacity, scale, translation_y;
|
||
|
switch (state) {
|
||
|
case ControlsState.HIDDEN:
|
||
|
opacity = 255;
|
||
|
scale = 1;
|
||
|
translation_y = 0;
|
||
|
if (!Main.layoutManager._startingUp && (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2)) {
|
||
|
translation_y = this._thumbnails.height * (opt.SEC_WS_TMB_TOP ? -1 : 1);
|
||
|
}
|
||
|
break;
|
||
|
case ControlsState.WINDOW_PICKER:
|
||
|
case ControlsState.APP_GRID:
|
||
|
opacity = 255;
|
||
|
scale = 1;
|
||
|
translation_y = 0;
|
||
|
break;
|
||
|
default:
|
||
|
opacity = 255;
|
||
|
scale = 1;
|
||
|
translation_y = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return { opacity, scale, translation_y };
|
||
|
},
|
||
|
|
||
|
_getWorkspacesBoxForState: function(state, box, padding, thumbnailsHeight, spacing) {
|
||
|
//const { ControlsState } = OverviewControls;
|
||
|
const workspaceBox = box.copy();
|
||
|
const [width, height] = workspaceBox.get_size();
|
||
|
|
||
|
switch (state) {
|
||
|
case ControlsState.HIDDEN:
|
||
|
break;
|
||
|
case ControlsState.WINDOW_PICKER:
|
||
|
case ControlsState.APP_GRID:
|
||
|
if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
let wHeight = Math.round(Math.min(height - thumbnailsHeight - 5 * spacing));
|
||
|
let wWidth = Math.round(Math.min(wHeight * (width / height), width - 1.7 * padding));
|
||
|
wWidth *= opt.WS_PREVIEW_SCALE;
|
||
|
wHeight *= opt.WS_PREVIEW_SCALE;
|
||
|
|
||
|
let wsbY;
|
||
|
let offset = Math.round((height - thumbnailsHeight - wHeight) / 2);
|
||
|
if (opt.WS_TMB_TOP) {
|
||
|
wsbY = thumbnailsHeight + offset;
|
||
|
} else {
|
||
|
wsbY = offset;
|
||
|
}
|
||
|
|
||
|
const wsbX = Math.round((width - wWidth) / 2);
|
||
|
|
||
|
workspaceBox.set_origin(wsbX, wsbY);
|
||
|
workspaceBox.set_size(wWidth, wHeight);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return workspaceBox;
|
||
|
},
|
||
|
|
||
|
_getThumbnailsHeight: function(box) {
|
||
|
if (!this._thumbnails.visible)
|
||
|
return 0;
|
||
|
|
||
|
const [width, height] = box.get_size();
|
||
|
const { expandFraction } = this._thumbnails;
|
||
|
const [thumbnailsHeight] = this._thumbnails.get_preferred_height(width);
|
||
|
return Math.min(
|
||
|
thumbnailsHeight * expandFraction,
|
||
|
height * opt.MAX_THUMBNAIL_SCALE);
|
||
|
},
|
||
|
|
||
|
vfunc_allocate: function(box) {
|
||
|
this.set_allocation(box);
|
||
|
|
||
|
const themeNode = this.get_theme_node();
|
||
|
const contentBox = themeNode.get_content_box(box);
|
||
|
const [width, height] = contentBox.get_size();
|
||
|
const { expandFraction } = this._thumbnails;
|
||
|
const spacing = themeNode.get_length('spacing') * expandFraction;
|
||
|
const padding = Math.round(0.1 * height);
|
||
|
|
||
|
let thumbnailsHeight = this._getThumbnailsHeight(contentBox);
|
||
|
let [, thumbnailsWidth] = this._thumbnails.get_preferred_custom_width(thumbnailsHeight);
|
||
|
thumbnailsWidth = Math.min(thumbnailsWidth, width - 2 * spacing);
|
||
|
|
||
|
this._thumbnails.visible = opt.SHOW_WS_TMB;
|
||
|
if (this._thumbnails.visible) {
|
||
|
let wsTmbY;
|
||
|
if (opt.SEC_WS_TMB_TOP) {
|
||
|
wsTmbY = Math.round(spacing / 4);
|
||
|
} else {
|
||
|
wsTmbY = Math.round(height - spacing / 4 - thumbnailsHeight);
|
||
|
}
|
||
|
|
||
|
const childBox = new Clutter.ActorBox();
|
||
|
const availSpace = width - thumbnailsWidth - 2 * spacing;
|
||
|
|
||
|
let wsTmbX = availSpace / 2;
|
||
|
wsTmbX -= opt.SEC_WS_TMB_POSITION_ADJUSTMENT * wsTmbX - spacing;
|
||
|
|
||
|
childBox.set_origin(Math.round(wsTmbX), Math.round(wsTmbY));
|
||
|
childBox.set_size(thumbnailsWidth, thumbnailsHeight);
|
||
|
this._thumbnails.allocate(childBox);
|
||
|
}
|
||
|
|
||
|
const {
|
||
|
currentState, initialState, finalState, transitioning, progress,
|
||
|
} = this._overviewAdjustment.getStateTransitionParams();
|
||
|
|
||
|
let workspacesBox;
|
||
|
const workspaceParams = [contentBox, padding, thumbnailsHeight, spacing];
|
||
|
if (!transitioning) {
|
||
|
workspacesBox =
|
||
|
this._getWorkspacesBoxForState(currentState, ...workspaceParams);
|
||
|
} else {
|
||
|
const initialBox =
|
||
|
this._getWorkspacesBoxForState(initialState, ...workspaceParams);
|
||
|
const finalBox =
|
||
|
this._getWorkspacesBoxForState(finalState, ...workspaceParams);
|
||
|
workspacesBox = initialBox.interpolate(finalBox, progress);
|
||
|
}
|
||
|
this._workspacesView.allocate(workspacesBox);
|
||
|
},
|
||
|
|
||
|
_updateThumbnailVisibility: SecondaryMonitorDisplayVertical._updateThumbnailVisibility,
|
||
|
|
||
|
_updateThumbnailParams: function() {
|
||
|
// workaround for upstream bug - secondary thumbnails boxes don't catch 'showing' signal on the shell startup and don't populate the box with thumbnails
|
||
|
// the tmbBox contents is also destroyed when overview state adjustment gets above 1 when swiping gesture from window picker to app grid
|
||
|
if (!this._thumbnails._thumbnails.length) {
|
||
|
this._thumbnails._createThumbnails();
|
||
|
}
|
||
|
|
||
|
if (!this._thumbnails.visible)
|
||
|
return;
|
||
|
|
||
|
const { initialState, finalState, progress } =
|
||
|
this._overviewAdjustment.getStateTransitionParams();
|
||
|
|
||
|
const initialParams = this._getThumbnailParamsForState(initialState);
|
||
|
const finalParams = this._getThumbnailParamsForState(finalState);
|
||
|
|
||
|
/*const opacity =
|
||
|
Util.lerp(initialParams.opacity, finalParams.opacity, progress);
|
||
|
const scale =
|
||
|
Util.lerp(initialParams.scale, finalParams.scale, progress);*/
|
||
|
|
||
|
// 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);
|
||
|
const translation_y = (!Main.layoutManager._startingUp && ((!opt.SHOW_WS_PREVIEW_BG && !(opt.OVERVIEW_MODE2)) || animateOverviewMode2))
|
||
|
? Util.lerp(initialParams.translation_y, finalParams.translation_y, progress)
|
||
|
: 0;
|
||
|
|
||
|
this._thumbnails.set({
|
||
|
opacity: 255,
|
||
|
//scale_x: scale,
|
||
|
//scale_y: scale,
|
||
|
translation_y,
|
||
|
});
|
||
|
},
|
||
|
|
||
|
_updateWorkspacesView: function() {
|
||
|
if (this._workspacesView)
|
||
|
this._workspacesView.destroy();
|
||
|
|
||
|
if (this._settings.get_boolean('workspaces-only-on-primary')) {
|
||
|
this._workspacesView = new WorkspacesView.ExtraWorkspaceView(
|
||
|
this._monitorIndex,
|
||
|
this._overviewAdjustment);
|
||
|
} else {
|
||
|
this._workspacesView = new WorkspacesView.WorkspacesView(
|
||
|
this._monitorIndex,
|
||
|
this._controls,
|
||
|
this._scrollAdjustment,
|
||
|
// Secondary monitors don't need FitMode.ALL since there is workspace switcher always visible
|
||
|
//this._fitModeAdjustment,
|
||
|
new St.Adjustment({
|
||
|
actor: this,
|
||
|
value: 0,//FitMode.SINGLE,
|
||
|
lower: 0,//FitMode.SINGLE,
|
||
|
upper: 0,//FitMode.SINGLE,
|
||
|
}),
|
||
|
//secondaryOverviewAdjustment);
|
||
|
this._overviewAdjustment);
|
||
|
}
|
||
|
this.add_child(this._workspacesView);
|
||
|
this._thumbnails.opacity = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var WorkspacesDisplay = {
|
||
|
_updateWorkspacesViews: function() {
|
||
|
for (let i = 0; i < this._workspacesViews.length; i++)
|
||
|
this._workspacesViews[i].destroy();
|
||
|
|
||
|
this._primaryIndex = Main.layoutManager.primaryIndex;
|
||
|
this._workspacesViews = [];
|
||
|
let monitors = Main.layoutManager.monitors;
|
||
|
for (let i = 0; i < monitors.length; i++) {
|
||
|
let view;
|
||
|
if (i === this._primaryIndex) {
|
||
|
view = new WorkspacesView.WorkspacesView(i,
|
||
|
this._controls,
|
||
|
this._scrollAdjustment,
|
||
|
this._fitModeAdjustment,
|
||
|
this._overviewAdjustment);
|
||
|
|
||
|
view.visible = this._primaryVisible;
|
||
|
this.bind_property('opacity', view, 'opacity', GObject.BindingFlags.SYNC_CREATE);
|
||
|
this.add_child(view);
|
||
|
} else {
|
||
|
view = new WorkspacesView.SecondaryMonitorDisplay(i,
|
||
|
this._controls,
|
||
|
this._scrollAdjustment,
|
||
|
// Secondary monitors don't need FitMode.ALL since there is workspace switcher always visible
|
||
|
//this._fitModeAdjustment,
|
||
|
new St.Adjustment({
|
||
|
actor: this,
|
||
|
value: 0,//FitMode.SINGLE,
|
||
|
lower: 0,//FitMode.SINGLE,
|
||
|
upper: 0,//FitMode.SINGLE,
|
||
|
}),
|
||
|
this._overviewAdjustment);
|
||
|
Main.layoutManager.overviewGroup.add_actor(view);
|
||
|
}
|
||
|
|
||
|
this._workspacesViews.push(view);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onScrollEvent: function(actor, event) {
|
||
|
if (this._swipeTracker.canHandleScrollEvent(event))
|
||
|
return Clutter.EVENT_PROPAGATE;
|
||
|
|
||
|
if (!this.mapped)
|
||
|
return Clutter.EVENT_PROPAGATE;
|
||
|
|
||
|
if (this._workspacesOnlyOnPrimary &&
|
||
|
this._getMonitorIndexForEvent(event) != this._primaryIndex)
|
||
|
return Clutter.EVENT_PROPAGATE;
|
||
|
|
||
|
const isShiftPressed = (event.get_state() & Clutter.ModifierType.SHIFT_MASK) != 0;
|
||
|
/*const isCtrlPressed = (event.get_state() & Clutter.ModifierType.CONTROL_MASK) != 0;
|
||
|
const isAltPressed = (event.get_state() & Clutter.ModifierType.MOD1_MASK) != 0;
|
||
|
const noModifiersPressed = !(isCtrlPressed && isShiftPressed && isAltPressed);
|
||
|
|
||
|
if (OVERVIEW_MODE2 && noModifiersPressed) {
|
||
|
Main.overview.hide();
|
||
|
return Clutter.EVENT_STOP;
|
||
|
}*/
|
||
|
|
||
|
let direction = event.get_scroll_direction();
|
||
|
|
||
|
if (/*SHIFT_REORDERS_WS && */isShiftPressed) {
|
||
|
if (direction === Clutter.ScrollDirection.UP) {
|
||
|
direction = -1;
|
||
|
}
|
||
|
else if (direction === Clutter.ScrollDirection.DOWN) {
|
||
|
direction = 1;
|
||
|
} else {
|
||
|
direction = 0;
|
||
|
}
|
||
|
|
||
|
if (direction) {
|
||
|
_reorderWorkspace(direction);
|
||
|
// make all workspaces on primary monitor visible for case the new position is hidden
|
||
|
Main.overview._overview._controls._workspacesDisplay._workspacesViews[0]._workspaces.forEach(w => w.visible = true);
|
||
|
return Clutter.EVENT_STOP;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return Main.wm.handleWorkspaceScroll(event);
|
||
|
},
|
||
|
|
||
|
_onKeyPressEvent: function(actor, event) {
|
||
|
const symbol = event.get_key_symbol();
|
||
|
/*const { ControlsState } = OverviewControls;
|
||
|
if (this._overviewAdjustment.value !== ControlsState.WINDOW_PICKER && symbol !== Clutter.KEY_space)
|
||
|
return Clutter.EVENT_PROPAGATE;*/
|
||
|
|
||
|
/*if (!this.reactive)
|
||
|
return Clutter.EVENT_PROPAGATE;**/
|
||
|
const isCtrlPressed = (event.get_state() & Clutter.ModifierType.CONTROL_MASK) != 0;
|
||
|
const isShiftPressed = (event.get_state() & Clutter.ModifierType.SHIFT_MASK) != 0;
|
||
|
const isAltPressed = (event.get_state() & Clutter.ModifierType.MOD1_MASK) != 0;
|
||
|
const { workspaceManager } = global;
|
||
|
const vertical = workspaceManager.layout_rows === -1;
|
||
|
const rtl = this.get_text_direction() === Clutter.TextDirection.RTL;
|
||
|
|
||
|
let which;
|
||
|
switch (symbol) {
|
||
|
/*case Clutter.KEY_Return:*/
|
||
|
case Clutter.KEY_Page_Up:
|
||
|
if (vertical)
|
||
|
which = Meta.MotionDirection.UP;
|
||
|
else if (rtl)
|
||
|
which = Meta.MotionDirection.RIGHT;
|
||
|
else
|
||
|
which = Meta.MotionDirection.LEFT;
|
||
|
break;
|
||
|
case Clutter.KEY_Page_Down:
|
||
|
if (vertical)
|
||
|
which = Meta.MotionDirection.DOWN;
|
||
|
else if (rtl)
|
||
|
which = Meta.MotionDirection.LEFT;
|
||
|
else
|
||
|
which = Meta.MotionDirection.RIGHT;
|
||
|
break;
|
||
|
case Clutter.KEY_Home:
|
||
|
which = 0;
|
||
|
break;
|
||
|
case Clutter.KEY_End:
|
||
|
which = workspaceManager.n_workspaces - 1;
|
||
|
break;
|
||
|
case Clutter.KEY_space:
|
||
|
if (isCtrlPressed && isShiftPressed) {
|
||
|
_Util.openPreferences();
|
||
|
} else if (isAltPressed) {
|
||
|
Main.ctrlAltTabManager._items.forEach(i => {if (i.sortGroup === 1 && i.name === 'Dash') Main.ctrlAltTabManager.focusGroup(i)});
|
||
|
} else if (opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED && isCtrlPressed) {
|
||
|
_Util.activateSearchProvider(SEARCH_RECENT_FILES_PREFIX);
|
||
|
} else if (opt.WINDOW_SEARCH_PROVIDER_ENABLED/* && SEARCH_WINDOWS_SPACE*/) {
|
||
|
_Util.activateSearchProvider(SEARCH_WINDOWS_PREFIX);
|
||
|
}
|
||
|
return Clutter.EVENT_STOP;
|
||
|
case Clutter.KEY_Down:
|
||
|
case Clutter.KEY_Left:
|
||
|
case Clutter.KEY_Right:
|
||
|
case Clutter.KEY_Up:
|
||
|
if (Main.overview._overview._controls._searchController.searchActive) {
|
||
|
Main.overview.searchEntry.grab_key_focus();
|
||
|
} else /*if (OVERVIEW_MODE && !WORKSPACE_MODE)*/ {
|
||
|
Main.ctrlAltTabManager._items.forEach(i => {if (i.sortGroup === 1 && i.name === 'Dash') Main.ctrlAltTabManager.focusGroup(i)});
|
||
|
}
|
||
|
return Clutter.EVENT_STOP;
|
||
|
default:
|
||
|
return Clutter.EVENT_PROPAGATE;
|
||
|
}
|
||
|
|
||
|
let ws;
|
||
|
if (which < 0)
|
||
|
// Negative workspace numbers are directions
|
||
|
ws = workspaceManager.get_active_workspace().get_neighbor(which);
|
||
|
else
|
||
|
// Otherwise it is a workspace index
|
||
|
ws = workspaceManager.get_workspace_by_index(which);
|
||
|
|
||
|
if (/*SHIFT_REORDERS_WS && */isShiftPressed) {
|
||
|
let direction;
|
||
|
if (which === Meta.MotionDirection.UP || which === Meta.MotionDirection.LEFT)
|
||
|
direction = -1;
|
||
|
else if (which === Meta.MotionDirection.DOWN || which === Meta.MotionDirection.RIGHT)
|
||
|
direction = 1;
|
||
|
if (direction)
|
||
|
_reorderWorkspace(direction);
|
||
|
// make all workspaces on primary monitor visible for case the new position is hidden
|
||
|
Main.overview._overview._controls._workspacesDisplay._workspacesViews[0]._workspaces.forEach(w => w.visible = true);
|
||
|
return Clutter.EVENT_STOP;
|
||
|
}
|
||
|
|
||
|
if (ws)
|
||
|
Main.wm.actionMoveWorkspace(ws);
|
||
|
|
||
|
return Clutter.EVENT_STOP;
|
||
|
},
|
||
|
}
|
||
|
|
||
|
// 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ------------------ Reorder Workspaces - callback for Dash and workspacesDisplay -----------------------------------
|
||
|
|
||
|
function _reorderWorkspace(direction = 0) {
|
||
|
let activeWs = global.workspace_manager.get_active_workspace();
|
||
|
let activeWsIdx = activeWs.index();
|
||
|
let targetIdx = activeWsIdx + direction;
|
||
|
if (targetIdx > -1 && targetIdx < (global.workspace_manager.get_n_workspaces())) {
|
||
|
global.workspace_manager.reorder_workspace(activeWs, targetIdx);
|
||
|
}
|
||
|
}
|