2013-02-14 14:41:38 -05:00
|
|
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
2019-01-31 09:07:06 -05:00
|
|
|
/* exported ControlsManager */
|
2013-02-14 14:41:38 -05:00
|
|
|
|
2021-04-15 06:00:12 -04:00
|
|
|
const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
|
2013-02-14 14:41:38 -05:00
|
|
|
|
2021-01-14 18:23:15 -05:00
|
|
|
const AppDisplay = imports.ui.appDisplay;
|
2013-02-25 18:05:45 -05:00
|
|
|
const Dash = imports.ui.dash;
|
2021-02-15 15:39:14 -05:00
|
|
|
const Layout = imports.ui.layout;
|
2021-01-02 15:41:54 -05:00
|
|
|
const Main = imports.ui.main;
|
2020-12-30 15:45:09 -05:00
|
|
|
const Overview = imports.ui.overview;
|
2021-01-15 07:59:58 -05:00
|
|
|
const SearchController = imports.ui.searchController;
|
2021-01-14 18:23:15 -05:00
|
|
|
const Util = imports.misc.util;
|
2021-01-02 15:41:54 -05:00
|
|
|
const WindowManager = imports.ui.windowManager;
|
2021-01-14 18:23:15 -05:00
|
|
|
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
|
|
|
const WorkspacesView = imports.ui.workspacesView;
|
|
|
|
|
|
|
|
const SMALL_WORKSPACE_RATIO = 0.15;
|
2021-01-22 08:36:37 -05:00
|
|
|
const DASH_MAX_HEIGHT_RATIO = 0.15;
|
2013-02-15 18:25:36 -05:00
|
|
|
|
2020-06-27 20:45:18 -04:00
|
|
|
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
|
|
|
|
2020-12-30 15:45:09 -05:00
|
|
|
var SIDE_CONTROLS_ANIMATION_TIME = Overview.ANIMATION_TIME;
|
|
|
|
|
2020-12-30 15:32:03 -05:00
|
|
|
var ControlsState = {
|
|
|
|
HIDDEN: 0,
|
|
|
|
WINDOW_PICKER: 1,
|
|
|
|
APP_GRID: 2,
|
|
|
|
};
|
|
|
|
|
2020-12-30 15:16:18 -05:00
|
|
|
var ControlsManagerLayout = GObject.registerClass(
|
|
|
|
class ControlsManagerLayout extends Clutter.BoxLayout {
|
2021-01-14 18:23:15 -05:00
|
|
|
_init(searchEntry, appDisplay, workspacesDisplay, workspacesThumbnails,
|
2021-01-15 07:59:58 -05:00
|
|
|
searchController, dash, stateAdjustment) {
|
2020-12-30 15:16:18 -05:00
|
|
|
super._init({ orientation: Clutter.Orientation.VERTICAL });
|
|
|
|
|
2021-01-14 18:23:15 -05:00
|
|
|
this._appDisplay = appDisplay;
|
|
|
|
this._workspacesDisplay = workspacesDisplay;
|
|
|
|
this._workspacesThumbnails = workspacesThumbnails;
|
2020-12-30 15:32:03 -05:00
|
|
|
this._stateAdjustment = stateAdjustment;
|
2020-12-30 15:16:18 -05:00
|
|
|
this._searchEntry = searchEntry;
|
2021-01-15 07:59:58 -05:00
|
|
|
this._searchController = searchController;
|
2020-12-30 15:16:18 -05:00
|
|
|
this._dash = dash;
|
2020-12-30 15:32:03 -05:00
|
|
|
|
2021-02-10 16:09:04 -05:00
|
|
|
this._cachedWorkspaceBoxes = new Map();
|
2021-02-15 15:39:14 -05:00
|
|
|
this._postAllocationCallbacks = [];
|
2021-02-10 16:09:04 -05:00
|
|
|
|
2020-12-30 15:32:03 -05:00
|
|
|
stateAdjustment.connect('notify::value', () => this.layout_changed());
|
2020-12-30 15:16:18 -05:00
|
|
|
}
|
|
|
|
|
2021-06-18 13:51:02 -04:00
|
|
|
_computeWorkspacesBoxForState(state, workAreaBox, searchHeight, dashHeight, thumbnailsHeight) {
|
|
|
|
const workspaceBox = workAreaBox.copy();
|
|
|
|
const [startX, startY] = workAreaBox.get_origin();
|
2021-01-14 18:23:15 -05:00
|
|
|
const [width, height] = workspaceBox.get_size();
|
|
|
|
const { spacing } = this;
|
2021-02-16 16:03:29 -05:00
|
|
|
const { expandFraction } = this._workspacesThumbnails;
|
2021-01-14 18:23:15 -05:00
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case ControlsState.HIDDEN:
|
|
|
|
break;
|
|
|
|
case ControlsState.WINDOW_PICKER:
|
2021-06-18 13:51:02 -04:00
|
|
|
workspaceBox.set_origin(startX,
|
2021-03-09 11:31:30 -05:00
|
|
|
startY + searchHeight + spacing +
|
2021-02-16 16:03:29 -05:00
|
|
|
thumbnailsHeight + spacing * expandFraction);
|
2021-01-14 18:23:15 -05:00
|
|
|
workspaceBox.set_size(width,
|
|
|
|
height -
|
|
|
|
dashHeight - spacing -
|
|
|
|
searchHeight - spacing -
|
2021-02-16 16:03:29 -05:00
|
|
|
thumbnailsHeight - spacing * expandFraction);
|
2021-01-14 18:23:15 -05:00
|
|
|
break;
|
|
|
|
case ControlsState.APP_GRID:
|
2021-06-18 13:51:02 -04:00
|
|
|
workspaceBox.set_origin(startX, startY + searchHeight + spacing);
|
2021-01-14 18:23:15 -05:00
|
|
|
workspaceBox.set_size(
|
|
|
|
width,
|
2021-02-22 07:10:02 -05:00
|
|
|
Math.round(height * SMALL_WORKSPACE_RATIO));
|
2021-01-14 18:23:15 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return workspaceBox;
|
|
|
|
}
|
|
|
|
|
2021-06-18 13:51:02 -04:00
|
|
|
_getAppDisplayBoxForState(state, workAreaBox, searchHeight, dashHeight, appGridBox) {
|
|
|
|
const [startX, startY] = workAreaBox.get_origin();
|
|
|
|
const [width, height] = workAreaBox.get_size();
|
2021-01-20 17:25:13 -05:00
|
|
|
const appDisplayBox = new Clutter.ActorBox();
|
|
|
|
const { spacing } = this;
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case ControlsState.HIDDEN:
|
|
|
|
case ControlsState.WINDOW_PICKER:
|
2021-06-18 13:51:02 -04:00
|
|
|
appDisplayBox.set_origin(startX, workAreaBox.y2);
|
2021-01-20 17:25:13 -05:00
|
|
|
break;
|
|
|
|
case ControlsState.APP_GRID:
|
2021-06-18 13:51:02 -04:00
|
|
|
appDisplayBox.set_origin(startX,
|
2021-03-09 11:31:30 -05:00
|
|
|
startY + searchHeight + spacing + appGridBox.get_height());
|
2021-01-20 17:25:13 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
appDisplayBox.set_size(width,
|
|
|
|
height -
|
|
|
|
searchHeight - spacing -
|
|
|
|
appGridBox.get_height() - spacing -
|
|
|
|
dashHeight);
|
|
|
|
|
|
|
|
return appDisplayBox;
|
|
|
|
}
|
|
|
|
|
2021-02-15 15:39:14 -05:00
|
|
|
_runPostAllocation() {
|
|
|
|
if (this._postAllocationCallbacks.length === 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._postAllocationCallbacks.forEach(cb => cb());
|
|
|
|
this._postAllocationCallbacks = [];
|
|
|
|
}
|
|
|
|
|
2020-12-30 15:16:18 -05:00
|
|
|
vfunc_set_container(container) {
|
|
|
|
this._container = container;
|
2021-07-08 14:42:55 -04:00
|
|
|
if (container)
|
|
|
|
this.hookup_style(container);
|
2020-12-30 15:16:18 -05:00
|
|
|
}
|
|
|
|
|
2021-03-11 09:26:14 -05:00
|
|
|
vfunc_get_preferred_width(_container, _forHeight) {
|
|
|
|
// The MonitorConstraint will allocate us a fixed size anyway
|
|
|
|
return [0, 0];
|
|
|
|
}
|
|
|
|
|
|
|
|
vfunc_get_preferred_height(_container, _forWidth) {
|
|
|
|
// The MonitorConstraint will allocate us a fixed size anyway
|
|
|
|
return [0, 0];
|
|
|
|
}
|
|
|
|
|
2021-06-18 13:51:02 -04:00
|
|
|
vfunc_allocate(_container, _box) {
|
2020-12-30 15:16:18 -05:00
|
|
|
const childBox = new Clutter.ActorBox();
|
|
|
|
|
|
|
|
const { spacing } = this;
|
|
|
|
|
2021-06-18 13:28:55 -04:00
|
|
|
const monitor = Main.layoutManager.findMonitorForActor(this._container);
|
|
|
|
const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index);
|
2021-06-18 13:51:02 -04:00
|
|
|
const startX = workArea.x - monitor.x;
|
2021-06-18 13:28:55 -04:00
|
|
|
const startY = workArea.y - monitor.y;
|
2021-06-18 13:51:02 -04:00
|
|
|
const workAreaBox = new Clutter.ActorBox();
|
|
|
|
workAreaBox.set_origin(startX, startY);
|
|
|
|
workAreaBox.set_size(workArea.width, workArea.height);
|
|
|
|
const [width, height] = workAreaBox.get_size();
|
2020-12-30 15:16:18 -05:00
|
|
|
let availableHeight = height;
|
2021-06-18 13:51:02 -04:00
|
|
|
const availableWidth = width;
|
2020-12-30 15:16:18 -05:00
|
|
|
|
|
|
|
// Search entry
|
2021-03-09 11:31:30 -05:00
|
|
|
let [searchHeight] = this._searchEntry.get_preferred_height(width);
|
2021-06-18 13:51:02 -04:00
|
|
|
childBox.set_origin(startX, startY);
|
2020-12-30 15:16:18 -05:00
|
|
|
childBox.set_size(width, searchHeight);
|
|
|
|
this._searchEntry.allocate(childBox);
|
|
|
|
|
|
|
|
availableHeight -= searchHeight + spacing;
|
|
|
|
|
|
|
|
// Dash
|
2021-06-18 13:51:02 -04:00
|
|
|
const maxDashHeight = Math.round(workAreaBox.get_height() * DASH_MAX_HEIGHT_RATIO);
|
2021-01-22 08:36:37 -05:00
|
|
|
this._dash.setMaxSize(width, maxDashHeight);
|
|
|
|
|
|
|
|
let [, dashHeight] = this._dash.get_preferred_height(width);
|
|
|
|
dashHeight = Math.min(dashHeight, maxDashHeight);
|
2021-06-18 13:51:02 -04:00
|
|
|
childBox.set_origin(startX, startY + height - dashHeight);
|
2020-12-30 15:16:18 -05:00
|
|
|
childBox.set_size(width, dashHeight);
|
|
|
|
this._dash.allocate(childBox);
|
|
|
|
|
|
|
|
availableHeight -= dashHeight + spacing;
|
|
|
|
|
2021-01-14 18:23:15 -05:00
|
|
|
// Workspace Thumbnails
|
|
|
|
let thumbnailsHeight = 0;
|
|
|
|
if (this._workspacesThumbnails.visible) {
|
2021-02-16 16:03:29 -05:00
|
|
|
const { expandFraction } = this._workspacesThumbnails;
|
2021-01-14 18:23:15 -05:00
|
|
|
[thumbnailsHeight] =
|
|
|
|
this._workspacesThumbnails.get_preferred_height(width);
|
|
|
|
thumbnailsHeight = Math.min(
|
2021-02-16 16:03:29 -05:00
|
|
|
thumbnailsHeight * expandFraction,
|
2021-01-14 18:23:15 -05:00
|
|
|
height * WorkspaceThumbnail.MAX_THUMBNAIL_SCALE);
|
2021-06-18 13:51:02 -04:00
|
|
|
childBox.set_origin(startX, startY + searchHeight + spacing);
|
2021-01-14 18:23:15 -05:00
|
|
|
childBox.set_size(width, thumbnailsHeight);
|
|
|
|
this._workspacesThumbnails.allocate(childBox);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Workspaces
|
2021-06-18 13:51:02 -04:00
|
|
|
let params = [workAreaBox, searchHeight, dashHeight, thumbnailsHeight];
|
2021-01-14 18:23:15 -05:00
|
|
|
const transitionParams = this._stateAdjustment.getStateTransitionParams();
|
|
|
|
|
2021-02-10 16:09:04 -05:00
|
|
|
// Update cached boxes
|
|
|
|
for (const state of Object.values(ControlsState)) {
|
|
|
|
this._cachedWorkspaceBoxes.set(
|
workspacesView: Interpolate against relative workspace boxes
The overview transition consists of getting the initial and final
states of the overview adjustment, derivating various other internal
states from them (such as the fit mode, opacities, translations, etc),
and finally interpolating the allocation boxes.
When interpolating between the fit mode, WorkspacesView uses the current
allocation box to derivate the SINGLE and ALL fit mode boxes. However,
that creates a curved path during overview transitions. What we really
want to do here is calculate the fit mode box relative to the corresponding
overview state. For example:
+----------------+----------+------------------------+
| Overview State | Fit Mode | Workspaces geometry |
+----------------+----------+------------------------+
| HIDDEN | SINGLE | Cover entire screen |
| WINDOW PICKER | SINGLE | Between minimap & Dash |
| APP GRID | ALL | 15% screen height |
+----------------+----------+------------------------+
Using the table above as the reference, when the overview transitions
between WINDOW PICKER and APP GRID, we must interpolate between
(SINGLE fit mode @ between minimap & Dash) and (ALL fit mode @ 15% screen
height). That way, we always interpolate the final boxes, which corrects
the odd path that workspaces follow during this transition.
Make the WorkspacesView of the primary monitor use these cached boxes
when the overview is in the middle of a transition, and the fit modes of
the initial and final state differ, to calculate the workspaces positions.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1624>
2021-02-02 14:34:59 -05:00
|
|
|
state, this._computeWorkspacesBoxForState(state, ...params));
|
2021-02-10 16:09:04 -05:00
|
|
|
}
|
|
|
|
|
2021-01-14 18:23:15 -05:00
|
|
|
let workspacesBox;
|
|
|
|
if (!transitionParams.transitioning) {
|
2021-02-10 16:09:04 -05:00
|
|
|
workspacesBox = this._cachedWorkspaceBoxes.get(transitionParams.currentState);
|
2021-01-14 18:23:15 -05:00
|
|
|
} else {
|
2021-02-10 16:09:04 -05:00
|
|
|
const initialBox = this._cachedWorkspaceBoxes.get(transitionParams.initialState);
|
|
|
|
const finalBox = this._cachedWorkspaceBoxes.get(transitionParams.finalState);
|
2021-01-14 18:23:15 -05:00
|
|
|
workspacesBox = initialBox.interpolate(finalBox, transitionParams.progress);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._workspacesDisplay.allocate(workspacesBox);
|
|
|
|
|
|
|
|
// AppDisplay
|
2021-03-09 11:37:22 -05:00
|
|
|
if (this._appDisplay.visible) {
|
|
|
|
const workspaceAppGridBox =
|
|
|
|
this._cachedWorkspaceBoxes.get(ControlsState.APP_GRID);
|
|
|
|
|
2021-06-18 13:51:02 -04:00
|
|
|
params = [workAreaBox, searchHeight, dashHeight, workspaceAppGridBox];
|
2021-03-09 11:37:22 -05:00
|
|
|
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);
|
|
|
|
}
|
2021-01-14 18:23:15 -05:00
|
|
|
|
2021-03-09 11:37:22 -05:00
|
|
|
this._appDisplay.allocate(appDisplayBox);
|
2021-01-20 17:25:13 -05:00
|
|
|
}
|
2020-12-30 15:32:03 -05:00
|
|
|
|
2021-01-15 07:59:58 -05:00
|
|
|
// Search
|
2021-06-18 13:51:02 -04:00
|
|
|
childBox.set_origin(startX, startY + searchHeight + spacing);
|
|
|
|
childBox.set_size(availableWidth, availableHeight);
|
2020-12-30 15:32:03 -05:00
|
|
|
|
2021-01-15 07:59:58 -05:00
|
|
|
this._searchController.allocate(childBox);
|
2021-02-15 15:39:14 -05:00
|
|
|
|
|
|
|
this._runPostAllocation();
|
|
|
|
}
|
|
|
|
|
|
|
|
ensureAllocation() {
|
|
|
|
this.layout_changed();
|
|
|
|
return new Promise(
|
|
|
|
resolve => this._postAllocationCallbacks.push(resolve));
|
2020-12-30 15:16:18 -05:00
|
|
|
}
|
workspacesView: Interpolate against relative workspace boxes
The overview transition consists of getting the initial and final
states of the overview adjustment, derivating various other internal
states from them (such as the fit mode, opacities, translations, etc),
and finally interpolating the allocation boxes.
When interpolating between the fit mode, WorkspacesView uses the current
allocation box to derivate the SINGLE and ALL fit mode boxes. However,
that creates a curved path during overview transitions. What we really
want to do here is calculate the fit mode box relative to the corresponding
overview state. For example:
+----------------+----------+------------------------+
| Overview State | Fit Mode | Workspaces geometry |
+----------------+----------+------------------------+
| HIDDEN | SINGLE | Cover entire screen |
| WINDOW PICKER | SINGLE | Between minimap & Dash |
| APP GRID | ALL | 15% screen height |
+----------------+----------+------------------------+
Using the table above as the reference, when the overview transitions
between WINDOW PICKER and APP GRID, we must interpolate between
(SINGLE fit mode @ between minimap & Dash) and (ALL fit mode @ 15% screen
height). That way, we always interpolate the final boxes, which corrects
the odd path that workspaces follow during this transition.
Make the WorkspacesView of the primary monitor use these cached boxes
when the overview is in the middle of a transition, and the fit modes of
the initial and final state differ, to calculate the workspaces positions.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1624>
2021-02-02 14:34:59 -05:00
|
|
|
|
|
|
|
getWorkspacesBoxForState(state) {
|
|
|
|
return this._cachedWorkspaceBoxes.get(state);
|
|
|
|
}
|
2020-12-30 15:16:18 -05:00
|
|
|
});
|
|
|
|
|
2021-02-11 18:20:31 -05:00
|
|
|
var OverviewAdjustment = GObject.registerClass({
|
|
|
|
Properties: {
|
|
|
|
'gesture-in-progress': GObject.ParamSpec.boolean(
|
|
|
|
'gesture-in-progress', 'Gesture in progress', 'Gesture in progress',
|
|
|
|
GObject.ParamFlags.READWRITE,
|
|
|
|
false),
|
|
|
|
},
|
|
|
|
}, class OverviewAdjustment extends St.Adjustment {
|
2021-01-15 13:28:23 -05:00
|
|
|
_init(actor) {
|
|
|
|
super._init({
|
|
|
|
actor,
|
|
|
|
value: ControlsState.WINDOW_PICKER,
|
|
|
|
lower: ControlsState.HIDDEN,
|
|
|
|
upper: ControlsState.APP_GRID,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
getStateTransitionParams() {
|
|
|
|
const currentState = this.value;
|
|
|
|
|
|
|
|
const transition = this.get_transition('value');
|
|
|
|
let initialState = transition
|
|
|
|
? transition.get_interval().peek_initial_value()
|
|
|
|
: currentState;
|
|
|
|
let finalState = transition
|
|
|
|
? transition.get_interval().peek_final_value()
|
|
|
|
: currentState;
|
|
|
|
|
|
|
|
if (initialState > finalState) {
|
|
|
|
initialState = Math.ceil(initialState);
|
|
|
|
finalState = Math.floor(finalState);
|
|
|
|
} else {
|
|
|
|
initialState = Math.floor(initialState);
|
|
|
|
finalState = Math.ceil(finalState);
|
|
|
|
}
|
|
|
|
|
|
|
|
const length = Math.abs(finalState - initialState);
|
|
|
|
const progress = length > 0
|
|
|
|
? Math.abs((currentState - initialState) / length)
|
|
|
|
: 1;
|
|
|
|
|
|
|
|
return {
|
2021-02-11 18:20:31 -05:00
|
|
|
transitioning: transition !== null || this.gestureInProgress,
|
2021-01-15 13:28:23 -05:00
|
|
|
currentState,
|
|
|
|
initialState,
|
|
|
|
finalState,
|
|
|
|
progress,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-07-16 05:24:13 -04:00
|
|
|
var ControlsManager = GObject.registerClass(
|
|
|
|
class ControlsManager extends St.Widget {
|
2020-12-30 15:02:14 -05:00
|
|
|
_init() {
|
2019-07-16 05:24:13 -04:00
|
|
|
super._init({
|
2020-12-30 15:16:18 -05:00
|
|
|
style_class: 'controls-manager',
|
2019-07-16 05:24:13 -04:00
|
|
|
x_expand: true,
|
|
|
|
y_expand: true,
|
2019-08-20 17:43:54 -04:00
|
|
|
clip_to_allocation: true,
|
2019-07-16 05:24:13 -04:00
|
|
|
});
|
|
|
|
|
2021-01-01 12:00:30 -05:00
|
|
|
this._ignoreShowAppsButtonToggle = false;
|
|
|
|
|
2020-12-30 15:02:14 -05:00
|
|
|
this._searchEntry = new St.Entry({
|
|
|
|
style_class: 'search-entry',
|
|
|
|
/* Translators: this is the text displayed
|
|
|
|
in the search entry when no search is
|
|
|
|
active; it should not exceed ~30
|
|
|
|
characters. */
|
|
|
|
hint_text: _('Type to search'),
|
|
|
|
track_hover: true,
|
|
|
|
can_focus: true,
|
|
|
|
});
|
|
|
|
this._searchEntry.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
|
2021-02-15 15:38:36 -05:00
|
|
|
this._searchEntryBin = new St.Bin({
|
2020-12-30 15:02:14 -05:00
|
|
|
child: this._searchEntry,
|
|
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
|
|
});
|
|
|
|
|
2013-02-25 18:05:45 -05:00
|
|
|
this.dash = new Dash.Dash();
|
|
|
|
|
2019-07-08 04:03:20 -04:00
|
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
let activeWorkspaceIndex = workspaceManager.get_active_workspace_index();
|
|
|
|
|
|
|
|
this._workspaceAdjustment = new St.Adjustment({
|
2020-06-16 16:03:07 -04:00
|
|
|
actor: this,
|
2019-07-08 04:03:20 -04:00
|
|
|
value: activeWorkspaceIndex,
|
|
|
|
lower: 0,
|
|
|
|
page_increment: 1,
|
|
|
|
page_size: 1,
|
|
|
|
step_increment: 0,
|
|
|
|
upper: workspaceManager.n_workspaces,
|
|
|
|
});
|
|
|
|
|
2021-01-15 13:28:23 -05:00
|
|
|
this._stateAdjustment = new OverviewAdjustment(this);
|
2021-01-14 18:23:15 -05:00
|
|
|
this._stateAdjustment.connect('notify::value', this._update.bind(this));
|
2020-12-30 15:32:03 -05:00
|
|
|
|
2019-07-08 04:03:20 -04:00
|
|
|
this._nWorkspacesNotifyId =
|
|
|
|
workspaceManager.connect('notify::n-workspaces',
|
|
|
|
this._updateAdjustment.bind(this));
|
|
|
|
|
2021-12-03 14:04:05 -05:00
|
|
|
this._contextSwitchedId = workspaceManager.connect('context-switched', this._updateAdjustment.bind(this));
|
|
|
|
|
2021-01-15 07:59:58 -05:00
|
|
|
this._searchController = new SearchController.SearchController(
|
|
|
|
this._searchEntry,
|
2021-01-14 18:23:15 -05:00
|
|
|
this.dash.showAppsButton);
|
2021-01-15 07:59:58 -05:00
|
|
|
this._searchController.connect('notify::search-active', this._onSearchChanged.bind(this));
|
2021-01-14 18:23:15 -05:00
|
|
|
|
workspaceThumbnail: Update monitor index on 'monitors-changed'
For the primary monitor workspace thumbnail, we must keep the monitor
index in sync with what is currently the primary monitor index,
otherwise we might end up trying to move windows to non-existing
monitors.
For example, if the primary monitor index was 1 when the thumbnail box
was created, but later, the primary monitor index changed to 0, with the
other monitor being turned off, moving a window to one of the workspaces
on the workspace thumbnail, gnome-shell would attempt to move it to the
monitor with the index the primary monitor had in the past, with the
problem being that that monitor no longer exists.
Fix this by listening on the 'monitors-changed' signal on the layout
manager, and update the monitor index of the primary workspace
thumbnails box. Make sure to connect to the signal before creating the
thumbnails box, as the thumbnails box itself will listen to the signal
and recreate its actual thumbnails, and it must do this with the up to
date monitor index.
Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/4075
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1810>
2021-04-14 11:12:12 -04:00
|
|
|
Main.layoutManager.connect('monitors-changed', () => {
|
|
|
|
this._thumbnailsBox.setMonitorIndex(Main.layoutManager.primaryIndex);
|
|
|
|
});
|
2021-02-26 08:09:15 -05:00
|
|
|
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox(
|
|
|
|
this._workspaceAdjustment, Main.layoutManager.primaryIndex);
|
2021-02-16 16:03:29 -05:00
|
|
|
this._thumbnailsBox.connect('notify::should-show', () => {
|
|
|
|
this._thumbnailsBox.show();
|
|
|
|
this._thumbnailsBox.ease_property('expand-fraction',
|
|
|
|
this._thumbnailsBox.should_show ? 1 : 0, {
|
|
|
|
duration: SIDE_CONTROLS_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
onComplete: () => this._updateThumbnailsBox(),
|
|
|
|
});
|
|
|
|
});
|
2021-02-16 14:40:43 -05:00
|
|
|
|
2021-01-14 18:23:15 -05:00
|
|
|
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay(
|
workspacesView: Interpolate against relative workspace boxes
The overview transition consists of getting the initial and final
states of the overview adjustment, derivating various other internal
states from them (such as the fit mode, opacities, translations, etc),
and finally interpolating the allocation boxes.
When interpolating between the fit mode, WorkspacesView uses the current
allocation box to derivate the SINGLE and ALL fit mode boxes. However,
that creates a curved path during overview transitions. What we really
want to do here is calculate the fit mode box relative to the corresponding
overview state. For example:
+----------------+----------+------------------------+
| Overview State | Fit Mode | Workspaces geometry |
+----------------+----------+------------------------+
| HIDDEN | SINGLE | Cover entire screen |
| WINDOW PICKER | SINGLE | Between minimap & Dash |
| APP GRID | ALL | 15% screen height |
+----------------+----------+------------------------+
Using the table above as the reference, when the overview transitions
between WINDOW PICKER and APP GRID, we must interpolate between
(SINGLE fit mode @ between minimap & Dash) and (ALL fit mode @ 15% screen
height). That way, we always interpolate the final boxes, which corrects
the odd path that workspaces follow during this transition.
Make the WorkspacesView of the primary monitor use these cached boxes
when the overview is in the middle of a transition, and the fit modes of
the initial and final state differ, to calculate the workspaces positions.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1624>
2021-02-02 14:34:59 -05:00
|
|
|
this,
|
2020-12-30 15:45:09 -05:00
|
|
|
this._workspaceAdjustment,
|
|
|
|
this._stateAdjustment);
|
2021-01-14 18:23:15 -05:00
|
|
|
this._appDisplay = new AppDisplay.AppDisplay();
|
2013-02-15 18:25:36 -05:00
|
|
|
|
2021-02-15 15:38:36 -05:00
|
|
|
this.add_child(this._searchEntryBin);
|
2021-01-14 18:23:15 -05:00
|
|
|
this.add_child(this._appDisplay);
|
2020-12-30 15:16:18 -05:00
|
|
|
this.add_child(this.dash);
|
2021-01-15 07:59:58 -05:00
|
|
|
this.add_child(this._searchController);
|
2021-01-14 18:23:15 -05:00
|
|
|
this.add_child(this._thumbnailsBox);
|
|
|
|
this.add_child(this._workspacesDisplay);
|
2013-02-25 18:05:45 -05:00
|
|
|
|
2021-02-15 15:38:36 -05:00
|
|
|
this.layout_manager = new ControlsManagerLayout(
|
|
|
|
this._searchEntryBin,
|
2021-01-14 18:23:15 -05:00
|
|
|
this._appDisplay,
|
|
|
|
this._workspacesDisplay,
|
|
|
|
this._thumbnailsBox,
|
2021-01-15 07:59:58 -05:00
|
|
|
this._searchController,
|
2021-01-14 18:23:15 -05:00
|
|
|
this.dash,
|
|
|
|
this._stateAdjustment);
|
2013-02-17 23:45:24 -05:00
|
|
|
|
2020-12-30 15:45:09 -05:00
|
|
|
this.dash.showAppsButton.connect('notify::checked',
|
|
|
|
this._onShowAppsButtonToggled.bind(this));
|
|
|
|
|
2021-01-14 18:23:15 -05:00
|
|
|
Main.ctrlAltTabManager.addGroup(
|
|
|
|
this.appDisplay,
|
|
|
|
_('Applications'),
|
|
|
|
'view-app-grid-symbolic', {
|
|
|
|
proxy: this,
|
|
|
|
focusCallback: () => {
|
|
|
|
this.dash.showAppsButton.checked = true;
|
|
|
|
this.appDisplay.navigate_focus(
|
|
|
|
null, St.DirectionType.TAB_FORWARD, false);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
Main.ctrlAltTabManager.addGroup(
|
|
|
|
this._workspacesDisplay,
|
|
|
|
_('Windows'),
|
|
|
|
'focus-windows-symbolic', {
|
|
|
|
proxy: this,
|
|
|
|
focusCallback: () => {
|
|
|
|
this.dash.showAppsButton.checked = false;
|
|
|
|
this._workspacesDisplay.navigate_focus(
|
|
|
|
null, St.DirectionType.TAB_FORWARD, false);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2020-06-27 20:45:18 -04:00
|
|
|
this._a11ySettings = new Gio.Settings({ schema_id: A11Y_SCHEMA });
|
|
|
|
|
2021-04-15 06:00:12 -04:00
|
|
|
this._lastOverlayKeyTime = 0;
|
2020-06-27 20:45:18 -04:00
|
|
|
global.display.connect('overlay-key', () => {
|
|
|
|
if (this._a11ySettings.get_boolean('stickykeys-enable'))
|
|
|
|
return;
|
|
|
|
|
2017-11-15 12:15:49 -05:00
|
|
|
const { initialState, finalState, transitioning } =
|
|
|
|
this._stateAdjustment.getStateTransitionParams();
|
|
|
|
|
2021-04-15 06:00:12 -04:00
|
|
|
const time = GLib.get_monotonic_time() / 1000;
|
|
|
|
const timeDiff = time - this._lastOverlayKeyTime;
|
|
|
|
this._lastOverlayKeyTime = time;
|
|
|
|
|
|
|
|
const shouldShift = St.Settings.get().enable_animations
|
|
|
|
? transitioning && finalState > initialState
|
|
|
|
: Main.overview.visible && timeDiff < Overview.ANIMATION_TIME;
|
|
|
|
|
|
|
|
if (shouldShift)
|
2017-11-15 12:15:49 -05:00
|
|
|
this._shiftState(Meta.MotionDirection.UP);
|
|
|
|
else
|
|
|
|
Main.overview.toggle();
|
2020-06-27 20:45:18 -04:00
|
|
|
});
|
|
|
|
|
2021-01-02 15:41:54 -05:00
|
|
|
Main.wm.addKeybinding(
|
|
|
|
'toggle-application-view',
|
|
|
|
new Gio.Settings({ schema_id: WindowManager.SHELL_KEYBINDINGS_SCHEMA }),
|
|
|
|
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
|
|
|
|
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
|
|
|
|
this._toggleAppsPage.bind(this));
|
|
|
|
|
2020-06-13 07:26:09 -04:00
|
|
|
Main.wm.addKeybinding('shift-overview-up',
|
|
|
|
new Gio.Settings({ schema_id: WindowManager.SHELL_KEYBINDINGS_SCHEMA }),
|
|
|
|
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
|
|
|
|
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
|
|
|
|
() => this._shiftState(Meta.MotionDirection.UP));
|
|
|
|
|
|
|
|
Main.wm.addKeybinding('shift-overview-down',
|
|
|
|
new Gio.Settings({ schema_id: WindowManager.SHELL_KEYBINDINGS_SCHEMA }),
|
|
|
|
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
|
|
|
|
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
|
|
|
|
() => this._shiftState(Meta.MotionDirection.DOWN));
|
|
|
|
|
2019-07-08 04:03:20 -04:00
|
|
|
this.connect('destroy', this._onDestroy.bind(this));
|
2021-01-14 18:23:15 -05:00
|
|
|
|
|
|
|
this._update();
|
|
|
|
}
|
|
|
|
|
|
|
|
_getFitModeForState(state) {
|
|
|
|
switch (state) {
|
|
|
|
case ControlsState.HIDDEN:
|
|
|
|
case ControlsState.WINDOW_PICKER:
|
|
|
|
return WorkspacesView.FitMode.SINGLE;
|
|
|
|
case ControlsState.APP_GRID:
|
|
|
|
return WorkspacesView.FitMode.ALL;
|
|
|
|
default:
|
|
|
|
return WorkspacesView.FitMode.SINGLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_getThumbnailsBoxParams() {
|
|
|
|
const { initialState, finalState, progress } =
|
|
|
|
this._stateAdjustment.getStateTransitionParams();
|
|
|
|
|
|
|
|
const paramsForState = s => {
|
|
|
|
let opacity, scale, translationY;
|
|
|
|
switch (s) {
|
|
|
|
case ControlsState.HIDDEN:
|
|
|
|
case ControlsState.WINDOW_PICKER:
|
|
|
|
opacity = 255;
|
|
|
|
scale = 1;
|
|
|
|
translationY = 0;
|
|
|
|
break;
|
|
|
|
case ControlsState.APP_GRID:
|
|
|
|
opacity = 0;
|
|
|
|
scale = 0.5;
|
|
|
|
translationY = this._thumbnailsBox.height / 2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
opacity = 255;
|
|
|
|
scale = 1;
|
|
|
|
translationY = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return { opacity, scale, translationY };
|
|
|
|
};
|
|
|
|
|
|
|
|
const initialParams = paramsForState(initialState);
|
|
|
|
const finalParams = paramsForState(finalState);
|
|
|
|
|
|
|
|
return [
|
|
|
|
Util.lerp(initialParams.opacity, finalParams.opacity, progress),
|
|
|
|
Util.lerp(initialParams.scale, finalParams.scale, progress),
|
|
|
|
Util.lerp(initialParams.translationY, finalParams.translationY, progress),
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
_updateThumbnailsBox(animate = false) {
|
2021-02-16 14:40:43 -05:00
|
|
|
const { shouldShow } = this._thumbnailsBox;
|
2021-01-15 07:59:58 -05:00
|
|
|
const { searchActive } = this._searchController;
|
2021-01-14 18:23:15 -05:00
|
|
|
const [opacity, scale, translationY] = this._getThumbnailsBoxParams();
|
|
|
|
|
2021-02-16 14:40:43 -05:00
|
|
|
const thumbnailsBoxVisible = shouldShow && !searchActive && opacity !== 0;
|
2021-01-14 18:23:15 -05:00
|
|
|
if (thumbnailsBoxVisible) {
|
|
|
|
this._thumbnailsBox.opacity = 0;
|
|
|
|
this._thumbnailsBox.visible = thumbnailsBoxVisible;
|
|
|
|
}
|
|
|
|
|
|
|
|
const params = {
|
2021-01-15 07:50:39 -05:00
|
|
|
opacity: searchActive ? 0 : opacity,
|
2021-01-14 18:23:15 -05:00
|
|
|
duration: animate ? SIDE_CONTROLS_ANIMATION_TIME : 0,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
onComplete: () => (this._thumbnailsBox.visible = thumbnailsBoxVisible),
|
|
|
|
};
|
|
|
|
|
2021-01-15 07:50:39 -05:00
|
|
|
if (!searchActive) {
|
2021-01-14 18:23:15 -05:00
|
|
|
params.scale_x = scale;
|
|
|
|
params.scale_y = scale;
|
|
|
|
params.translation_y = translationY;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._thumbnailsBox.ease(params);
|
|
|
|
}
|
|
|
|
|
2021-04-10 12:27:01 -04:00
|
|
|
_updateAppDisplayVisibility(stateTransitionParams = null) {
|
|
|
|
if (!stateTransitionParams)
|
|
|
|
stateTransitionParams = this._stateAdjustment.getStateTransitionParams();
|
|
|
|
|
2021-06-12 15:11:52 -04:00
|
|
|
const { initialState, finalState } = stateTransitionParams;
|
|
|
|
const state = Math.max(initialState, finalState);
|
2021-04-10 12:27:01 -04:00
|
|
|
|
2021-03-09 11:37:22 -05:00
|
|
|
this._appDisplay.visible =
|
2021-04-10 12:27:01 -04:00
|
|
|
state > ControlsState.WINDOW_PICKER &&
|
2021-03-09 11:37:22 -05:00
|
|
|
!this._searchController.searchActive;
|
|
|
|
}
|
|
|
|
|
2021-01-14 18:23:15 -05:00
|
|
|
_update() {
|
|
|
|
const params = this._stateAdjustment.getStateTransitionParams();
|
|
|
|
|
|
|
|
const fitMode = Util.lerp(
|
|
|
|
this._getFitModeForState(params.initialState),
|
|
|
|
this._getFitModeForState(params.finalState),
|
|
|
|
params.progress);
|
|
|
|
|
|
|
|
const { fitModeAdjustment } = this._workspacesDisplay;
|
|
|
|
fitModeAdjustment.value = fitMode;
|
|
|
|
|
|
|
|
this._updateThumbnailsBox();
|
2021-04-10 12:27:01 -04:00
|
|
|
this._updateAppDisplayVisibility(params);
|
2021-01-14 18:23:15 -05:00
|
|
|
}
|
|
|
|
|
2021-01-15 07:50:39 -05:00
|
|
|
_onSearchChanged() {
|
2021-01-15 07:59:58 -05:00
|
|
|
const { searchActive } = this._searchController;
|
2021-01-14 18:23:15 -05:00
|
|
|
|
2021-01-15 07:50:39 -05:00
|
|
|
if (!searchActive) {
|
2021-03-09 11:37:22 -05:00
|
|
|
this._updateAppDisplayVisibility();
|
2021-01-14 18:23:15 -05:00
|
|
|
this._workspacesDisplay.reactive = true;
|
|
|
|
this._workspacesDisplay.setPrimaryWorkspaceVisible(true);
|
|
|
|
} else {
|
2021-01-15 07:59:58 -05:00
|
|
|
this._searchController.show();
|
2021-01-14 18:23:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
this._updateThumbnailsBox(true);
|
|
|
|
|
|
|
|
this._appDisplay.ease({
|
2021-01-15 07:50:39 -05:00
|
|
|
opacity: searchActive ? 0 : 255,
|
2021-01-14 18:23:15 -05:00
|
|
|
duration: SIDE_CONTROLS_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
2021-03-09 11:37:22 -05:00
|
|
|
onComplete: () => this._updateAppDisplayVisibility(),
|
2021-01-14 18:23:15 -05:00
|
|
|
});
|
|
|
|
this._workspacesDisplay.ease({
|
2021-01-15 07:50:39 -05:00
|
|
|
opacity: searchActive ? 0 : 255,
|
2021-01-14 18:23:15 -05:00
|
|
|
duration: SIDE_CONTROLS_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
onComplete: () => {
|
2021-01-15 07:50:39 -05:00
|
|
|
this._workspacesDisplay.reactive = !searchActive;
|
|
|
|
this._workspacesDisplay.setPrimaryWorkspaceVisible(!searchActive);
|
2021-01-14 18:23:15 -05:00
|
|
|
},
|
|
|
|
});
|
2021-01-15 07:59:58 -05:00
|
|
|
this._searchController.ease({
|
2021-01-15 07:50:39 -05:00
|
|
|
opacity: searchActive ? 255 : 0,
|
2021-01-14 18:23:15 -05:00
|
|
|
duration: SIDE_CONTROLS_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
2021-01-15 07:59:58 -05:00
|
|
|
onComplete: () => (this._searchController.visible = searchActive),
|
2021-01-14 18:23:15 -05:00
|
|
|
});
|
2019-07-08 04:03:20 -04:00
|
|
|
}
|
|
|
|
|
2020-12-30 15:45:09 -05:00
|
|
|
_onShowAppsButtonToggled() {
|
2021-01-01 12:00:30 -05:00
|
|
|
if (this._ignoreShowAppsButtonToggle)
|
|
|
|
return;
|
|
|
|
|
2020-12-30 15:45:09 -05:00
|
|
|
const checked = this.dash.showAppsButton.checked;
|
|
|
|
|
|
|
|
const value = checked
|
|
|
|
? ControlsState.APP_GRID : ControlsState.WINDOW_PICKER;
|
|
|
|
this._stateAdjustment.remove_transition('value');
|
|
|
|
this._stateAdjustment.ease(value, {
|
|
|
|
duration: SIDE_CONTROLS_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-01-02 15:41:54 -05:00
|
|
|
_toggleAppsPage() {
|
|
|
|
if (Main.overview.visible) {
|
|
|
|
const checked = this.dash.showAppsButton.checked;
|
|
|
|
this.dash.showAppsButton.checked = !checked;
|
|
|
|
} else {
|
|
|
|
Main.overview.show(ControlsState.APP_GRID);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-13 07:26:09 -04:00
|
|
|
_shiftState(direction) {
|
|
|
|
let { currentState, finalState } = this._stateAdjustment.getStateTransitionParams();
|
|
|
|
|
|
|
|
if (direction === Meta.MotionDirection.DOWN)
|
|
|
|
finalState = Math.max(finalState - 1, ControlsState.HIDDEN);
|
|
|
|
else if (direction === Meta.MotionDirection.UP)
|
|
|
|
finalState = Math.min(finalState + 1, ControlsState.APP_GRID);
|
|
|
|
|
|
|
|
if (finalState === currentState)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (currentState === ControlsState.HIDDEN &&
|
|
|
|
finalState === ControlsState.WINDOW_PICKER) {
|
|
|
|
Main.overview.show();
|
|
|
|
} else if (finalState === ControlsState.HIDDEN) {
|
|
|
|
Main.overview.hide();
|
|
|
|
} else {
|
|
|
|
this._stateAdjustment.ease(finalState, {
|
|
|
|
duration: SIDE_CONTROLS_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
onComplete: () => {
|
|
|
|
this.dash.showAppsButton.checked =
|
|
|
|
finalState === ControlsState.APP_GRID;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-08 04:03:20 -04:00
|
|
|
_onDestroy() {
|
|
|
|
global.workspace_manager.disconnect(this._nWorkspacesNotifyId);
|
2021-12-03 14:04:05 -05:00
|
|
|
global.workspace_manager.disconnect(this._contextSwitchedId);
|
2019-07-08 04:03:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
_updateAdjustment() {
|
|
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
let newNumWorkspaces = workspaceManager.n_workspaces;
|
|
|
|
let activeIndex = workspaceManager.get_active_workspace_index();
|
|
|
|
|
|
|
|
this._workspaceAdjustment.upper = newNumWorkspaces;
|
|
|
|
|
|
|
|
// A workspace might have been inserted or removed before the active
|
|
|
|
// one, causing the adjustment to go out of sync, so update the value
|
2020-04-27 19:04:08 -04:00
|
|
|
this._workspaceAdjustment.remove_transition('value');
|
2019-07-08 04:03:20 -04:00
|
|
|
this._workspaceAdjustment.value = activeIndex;
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2020-12-30 15:02:14 -05:00
|
|
|
|
2021-01-14 18:23:15 -05:00
|
|
|
vfunc_unmap() {
|
|
|
|
this._workspacesDisplay.hide();
|
|
|
|
super.vfunc_unmap();
|
|
|
|
}
|
|
|
|
|
2021-01-02 15:19:37 -05:00
|
|
|
animateToOverview(state, callback) {
|
2021-01-02 15:28:44 -05:00
|
|
|
this._ignoreShowAppsButtonToggle = true;
|
|
|
|
|
2021-01-15 07:59:58 -05:00
|
|
|
this._searchController.prepareToEnterOverview();
|
2021-01-14 18:23:15 -05:00
|
|
|
this._workspacesDisplay.prepareToEnterOverview();
|
|
|
|
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
|
|
|
|
Main.overview.fadeOutDesktop();
|
2021-01-01 12:00:30 -05:00
|
|
|
|
|
|
|
this._stateAdjustment.value = ControlsState.HIDDEN;
|
2021-01-02 15:19:37 -05:00
|
|
|
this._stateAdjustment.ease(state, {
|
2021-01-01 12:00:30 -05:00
|
|
|
duration: Overview.ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
2021-01-01 12:18:18 -05:00
|
|
|
onStopped: () => {
|
|
|
|
if (callback)
|
|
|
|
callback();
|
|
|
|
},
|
2021-01-01 12:00:30 -05:00
|
|
|
});
|
2021-01-02 15:28:44 -05:00
|
|
|
|
|
|
|
this.dash.showAppsButton.checked =
|
|
|
|
state === ControlsState.APP_GRID;
|
|
|
|
|
|
|
|
this._ignoreShowAppsButtonToggle = false;
|
2021-01-01 11:39:07 -05:00
|
|
|
}
|
|
|
|
|
2021-01-01 12:18:18 -05:00
|
|
|
animateFromOverview(callback) {
|
2021-01-01 12:00:30 -05:00
|
|
|
this._ignoreShowAppsButtonToggle = true;
|
|
|
|
|
2021-01-14 18:23:15 -05:00
|
|
|
this._workspacesDisplay.prepareToLeaveOverview();
|
|
|
|
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
|
|
|
|
Main.overview.fadeInDesktop();
|
2021-01-01 12:00:30 -05:00
|
|
|
|
|
|
|
this._stateAdjustment.ease(ControlsState.HIDDEN, {
|
|
|
|
duration: Overview.ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
onStopped: () => {
|
|
|
|
this.dash.showAppsButton.checked = false;
|
|
|
|
this._ignoreShowAppsButtonToggle = false;
|
2021-01-01 12:18:18 -05:00
|
|
|
|
|
|
|
if (callback)
|
|
|
|
callback();
|
2021-01-01 12:00:30 -05:00
|
|
|
},
|
|
|
|
});
|
2021-01-01 11:39:07 -05:00
|
|
|
}
|
|
|
|
|
workspacesView: Interpolate against relative workspace boxes
The overview transition consists of getting the initial and final
states of the overview adjustment, derivating various other internal
states from them (such as the fit mode, opacities, translations, etc),
and finally interpolating the allocation boxes.
When interpolating between the fit mode, WorkspacesView uses the current
allocation box to derivate the SINGLE and ALL fit mode boxes. However,
that creates a curved path during overview transitions. What we really
want to do here is calculate the fit mode box relative to the corresponding
overview state. For example:
+----------------+----------+------------------------+
| Overview State | Fit Mode | Workspaces geometry |
+----------------+----------+------------------------+
| HIDDEN | SINGLE | Cover entire screen |
| WINDOW PICKER | SINGLE | Between minimap & Dash |
| APP GRID | ALL | 15% screen height |
+----------------+----------+------------------------+
Using the table above as the reference, when the overview transitions
between WINDOW PICKER and APP GRID, we must interpolate between
(SINGLE fit mode @ between minimap & Dash) and (ALL fit mode @ 15% screen
height). That way, we always interpolate the final boxes, which corrects
the odd path that workspaces follow during this transition.
Make the WorkspacesView of the primary monitor use these cached boxes
when the overview is in the middle of a transition, and the fit modes of
the initial and final state differ, to calculate the workspaces positions.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1624>
2021-02-02 14:34:59 -05:00
|
|
|
getWorkspacesBoxForState(state) {
|
|
|
|
return this.layoutManager.getWorkspacesBoxForState(state);
|
|
|
|
}
|
|
|
|
|
2021-01-04 10:23:45 -05:00
|
|
|
gestureBegin(tracker) {
|
|
|
|
const baseDistance = global.screen_height;
|
|
|
|
const progress = this._stateAdjustment.value;
|
|
|
|
const points = [
|
|
|
|
ControlsState.HIDDEN,
|
|
|
|
ControlsState.WINDOW_PICKER,
|
|
|
|
ControlsState.APP_GRID,
|
|
|
|
];
|
|
|
|
|
|
|
|
const transition = this._stateAdjustment.get_transition('value');
|
|
|
|
const cancelProgress = transition
|
|
|
|
? transition.get_interval().peek_final_value()
|
|
|
|
: Math.round(progress);
|
2021-04-29 09:51:11 -04:00
|
|
|
this._stateAdjustment.remove_transition('value');
|
2021-01-04 10:23:45 -05:00
|
|
|
|
|
|
|
tracker.confirmSwipe(baseDistance, points, progress, cancelProgress);
|
|
|
|
this._workspacesDisplay.prepareToEnterOverview();
|
|
|
|
this._searchController.prepareToEnterOverview();
|
|
|
|
this._stateAdjustment.gestureInProgress = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
gestureProgress(progress) {
|
|
|
|
this._stateAdjustment.value = progress;
|
|
|
|
}
|
|
|
|
|
|
|
|
gestureEnd(target, duration, onComplete) {
|
|
|
|
if (target === ControlsState.HIDDEN)
|
|
|
|
this._workspacesDisplay.prepareToLeaveOverview();
|
|
|
|
|
|
|
|
this.dash.showAppsButton.checked =
|
|
|
|
target === ControlsState.APP_GRID;
|
|
|
|
|
|
|
|
this._stateAdjustment.remove_transition('value');
|
|
|
|
this._stateAdjustment.ease(target, {
|
|
|
|
duration,
|
2021-02-28 09:57:53 -05:00
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
2021-01-04 10:23:45 -05:00
|
|
|
onComplete,
|
|
|
|
});
|
|
|
|
|
|
|
|
this._stateAdjustment.gestureInProgress = false;
|
|
|
|
}
|
|
|
|
|
2021-02-15 15:39:14 -05:00
|
|
|
async runStartupAnimation(callback) {
|
|
|
|
this._ignoreShowAppsButtonToggle = true;
|
|
|
|
|
|
|
|
this._searchController.prepareToEnterOverview();
|
|
|
|
this._workspacesDisplay.prepareToEnterOverview();
|
|
|
|
|
|
|
|
this._stateAdjustment.value = ControlsState.HIDDEN;
|
|
|
|
this._stateAdjustment.ease(ControlsState.WINDOW_PICKER, {
|
|
|
|
duration: Overview.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 = 0;
|
|
|
|
|
|
|
|
// We can't run the animation before the first allocation happens
|
|
|
|
await this.layout_manager.ensureAllocation();
|
|
|
|
|
|
|
|
const { STARTUP_ANIMATION_TIME } = Layout;
|
|
|
|
|
|
|
|
// Opacity
|
|
|
|
this.ease({
|
|
|
|
opacity: 255,
|
|
|
|
duration: STARTUP_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.LINEAR,
|
|
|
|
});
|
|
|
|
|
|
|
|
// Search bar falls from the ceiling
|
|
|
|
const { primaryMonitor } = Main.layoutManager;
|
|
|
|
const [, y] = this._searchEntryBin.get_transformed_position();
|
|
|
|
const yOffset = y - primaryMonitor.y;
|
|
|
|
|
|
|
|
this._searchEntryBin.translation_y = -(yOffset + this._searchEntryBin.height);
|
|
|
|
this._searchEntryBin.ease({
|
|
|
|
translation_y: 0,
|
|
|
|
duration: STARTUP_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
});
|
|
|
|
|
|
|
|
// The Dash rises from the bottom. This is the last animation to finish,
|
|
|
|
// so run the callback there.
|
|
|
|
this.dash.translation_y = this.dash.height;
|
|
|
|
this.dash.ease({
|
|
|
|
translation_y: 0,
|
|
|
|
delay: STARTUP_ANIMATION_TIME,
|
|
|
|
duration: STARTUP_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
onComplete: () => callback(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-12-30 15:02:14 -05:00
|
|
|
get searchEntry() {
|
|
|
|
return this._searchEntry;
|
|
|
|
}
|
2021-01-14 18:23:15 -05:00
|
|
|
|
|
|
|
get appDisplay() {
|
|
|
|
return this._appDisplay;
|
|
|
|
}
|
2019-07-16 05:24:13 -04:00
|
|
|
});
|