2011-09-28 13:16:26 +00:00
|
|
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
2010-01-22 02:33:48 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
import Clutter from 'gi://Clutter';
|
|
|
|
import Gio from 'gi://Gio';
|
|
|
|
import GObject from 'gi://GObject';
|
|
|
|
import Meta from 'gi://Meta';
|
2023-08-08 18:07:03 +00:00
|
|
|
import Mtk from 'gi://Mtk';
|
2023-07-10 09:53:00 +00:00
|
|
|
import Shell from 'gi://Shell';
|
|
|
|
import St from 'gi://St';
|
2010-01-22 02:33:48 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
import * as Layout from './layout.js';
|
2010-01-22 02:33:48 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
import * as Main from './main.js';
|
|
|
|
import * as OverviewControls from './overviewControls.js';
|
|
|
|
import * as SwipeTracker from './swipeTracker.js';
|
|
|
|
import * as Util from '../misc/util.js';
|
|
|
|
import * as Workspace from './workspace.js';
|
2023-08-11 22:32:35 +00:00
|
|
|
import {ThumbnailsBox} from './workspaceThumbnail.js';
|
2023-07-10 09:53:00 +00:00
|
|
|
|
|
|
|
const WORKSPACE_SWITCH_TIME = 250;
|
2010-01-22 02:33:48 +00:00
|
|
|
|
2018-11-08 09:47:25 +00:00
|
|
|
const MUTTER_SCHEMA = 'org.gnome.mutter';
|
2010-03-16 15:51:24 +00:00
|
|
|
|
2020-12-29 21:05:29 +00:00
|
|
|
const WORKSPACE_MIN_SPACING = 24;
|
|
|
|
const WORKSPACE_MAX_SPACING = 80;
|
|
|
|
|
2020-12-31 17:19:43 +00:00
|
|
|
const WORKSPACE_INACTIVE_SCALE = 0.94;
|
|
|
|
|
2021-05-12 14:38:09 +00:00
|
|
|
const SECONDARY_WORKSPACE_SCALE = 0.80;
|
2021-02-20 12:34:39 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
const WorkspacesViewBase = GObject.registerClass({
|
2019-08-20 21:43:54 +00:00
|
|
|
GTypeFlags: GObject.TypeFlags.ABSTRACT,
|
2019-07-16 09:24:13 +00:00
|
|
|
}, class WorkspacesViewBase extends St.Widget {
|
2021-01-02 19:06:34 +00:00
|
|
|
_init(monitorIndex, overviewAdjustment) {
|
2020-06-25 23:15:33 +00:00
|
|
|
super._init({
|
|
|
|
style_class: 'workspaces-view',
|
2020-06-26 16:38:28 +00:00
|
|
|
x_expand: true,
|
|
|
|
y_expand: true,
|
2020-06-25 23:15:33 +00:00
|
|
|
});
|
2019-07-16 09:24:13 +00:00
|
|
|
this.connect('destroy', this._onDestroy.bind(this));
|
|
|
|
global.focus_manager.add_group(this);
|
2010-01-22 02:33:48 +00:00
|
|
|
|
2013-09-11 16:12:42 +00:00
|
|
|
this._monitorIndex = monitorIndex;
|
2010-02-18 15:43:58 +00:00
|
|
|
|
2013-09-11 16:12:42 +00:00
|
|
|
this._inDrag = false;
|
2021-08-15 22:36:59 +00:00
|
|
|
Main.overview.connectObject(
|
|
|
|
'window-drag-begin', this._dragBegin.bind(this),
|
|
|
|
'window-drag-end', this._dragEnd.bind(this), this);
|
2021-01-02 19:06:34 +00:00
|
|
|
|
|
|
|
this._overviewAdjustment = overviewAdjustment;
|
2021-08-15 22:36:59 +00:00
|
|
|
overviewAdjustment.connectObject('notify::value',
|
|
|
|
() => this._updateWorkspaceMode(), this);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2013-09-11 16:12:42 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onDestroy() {
|
2013-09-11 16:12:42 +00:00
|
|
|
this._dragEnd();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2013-09-11 16:12:42 +00:00
|
|
|
|
2020-06-03 00:22:40 +00:00
|
|
|
_dragBegin() {
|
2013-09-11 16:12:42 +00:00
|
|
|
this._inDrag = true;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2013-09-11 16:12:42 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_dragEnd() {
|
2013-09-11 16:12:42 +00:00
|
|
|
this._inDrag = false;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2013-09-11 16:12:42 +00:00
|
|
|
|
2021-01-02 19:06:34 +00:00
|
|
|
_updateWorkspaceMode() {
|
|
|
|
}
|
|
|
|
|
2020-06-03 16:17:54 +00:00
|
|
|
vfunc_allocate(box) {
|
|
|
|
this.set_allocation(box);
|
|
|
|
|
|
|
|
for (const child of this)
|
|
|
|
child.allocate_available_size(0, 0, box.get_width(), box.get_height());
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2020-06-26 16:38:28 +00:00
|
|
|
|
|
|
|
vfunc_get_preferred_width() {
|
|
|
|
return [0, 0];
|
|
|
|
}
|
|
|
|
|
|
|
|
vfunc_get_preferred_height() {
|
|
|
|
return [0, 0];
|
|
|
|
}
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
2013-09-11 16:12:42 +00:00
|
|
|
|
2023-07-30 12:56:59 +00:00
|
|
|
/** @enum {number} */
|
2023-07-10 09:53:00 +00:00
|
|
|
export const FitMode = {
|
2020-12-29 21:05:29 +00:00
|
|
|
SINGLE: 0,
|
|
|
|
ALL: 1,
|
|
|
|
};
|
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
export const WorkspacesView = GObject.registerClass(
|
2019-07-16 09:24:13 +00:00
|
|
|
class WorkspacesView extends WorkspacesViewBase {
|
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 19:34:59 +00:00
|
|
|
_init(monitorIndex, controls, scrollAdjustment, fitModeAdjustment, overviewAdjustment) {
|
2018-01-03 07:55:38 +00:00
|
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
|
2021-01-02 19:06:34 +00:00
|
|
|
super._init(monitorIndex, overviewAdjustment);
|
2013-09-11 14:09:13 +00: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 19:34:59 +00:00
|
|
|
this._controls = controls;
|
2020-12-29 21:05:29 +00:00
|
|
|
this._fitModeAdjustment = fitModeAdjustment;
|
2021-08-15 22:36:59 +00:00
|
|
|
this._fitModeAdjustment.connectObject('notify::value', () => {
|
2020-12-29 21:05:29 +00:00
|
|
|
this._updateVisibility();
|
2020-12-31 17:19:43 +00:00
|
|
|
this._updateWorkspacesState();
|
2020-12-29 21:05:29 +00:00
|
|
|
this.queue_relayout();
|
2021-08-15 22:36:59 +00:00
|
|
|
}, this);
|
2020-12-29 21:05:29 +00:00
|
|
|
|
2010-11-12 09:28:28 +00:00
|
|
|
this._animating = false; // tweening
|
2019-03-20 17:19:01 +00:00
|
|
|
this._gestureActive = false; // touch(pad) gestures
|
2010-01-22 02:33:48 +00:00
|
|
|
|
2019-07-07 17:51:55 +00:00
|
|
|
this._scrollAdjustment = scrollAdjustment;
|
2021-08-15 22:36:59 +00:00
|
|
|
this._scrollAdjustment.connectObject('notify::value',
|
|
|
|
this._onScrollAdjustmentChanged.bind(this), this);
|
2010-01-22 02:33:48 +00:00
|
|
|
|
2013-09-11 14:09:13 +00:00
|
|
|
this._workspaces = [];
|
|
|
|
this._updateWorkspaces();
|
2021-08-15 22:36:59 +00:00
|
|
|
workspaceManager.connectObject(
|
|
|
|
'notify::n-workspaces', this._updateWorkspaces.bind(this),
|
2021-12-03 19:04:05 +00:00
|
|
|
'context-switched', this._refreshWorkspaces.bind(this),
|
2021-08-15 22:36:59 +00:00
|
|
|
'workspaces-reordered', () => {
|
2019-07-09 12:03:22 +00:00
|
|
|
this._workspaces.sort((a, b) => {
|
|
|
|
return a.metaWorkspace.index() - b.metaWorkspace.index();
|
|
|
|
});
|
2020-06-26 14:04:44 +00:00
|
|
|
this._workspaces.forEach(
|
|
|
|
(ws, i) => this.set_child_at_index(ws, i));
|
2021-08-15 22:36:59 +00:00
|
|
|
}, this);
|
2019-07-09 12:03:22 +00:00
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
global.window_manager.connectObject('switch-workspace',
|
|
|
|
this._activeWorkspaceChanged.bind(this), this);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-11-25 14:44:17 +00: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 19:34:59 +00:00
|
|
|
_getFirstFitAllWorkspaceBox(box, spacing, vertical) {
|
2023-08-06 22:40:20 +00:00
|
|
|
const {nWorkspaces} = global.workspaceManager;
|
2020-12-29 21:05:29 +00:00
|
|
|
const [width, height] = box.get_size();
|
|
|
|
const [workspace] = this._workspaces;
|
|
|
|
|
|
|
|
const fitAllBox = new Clutter.ActorBox();
|
|
|
|
|
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 19:34:59 +00:00
|
|
|
let [x1, y1] = box.get_origin();
|
2020-12-29 21:05:29 +00:00
|
|
|
|
|
|
|
// Spacing here is not only the space between workspaces, but also the
|
|
|
|
// space before the first workspace, and after the last one. This prevents
|
|
|
|
// workspaces from touching the edges of the allocation box.
|
|
|
|
if (vertical) {
|
|
|
|
const availableHeight = height - spacing * (nWorkspaces + 1);
|
|
|
|
let workspaceHeight = availableHeight / nWorkspaces;
|
|
|
|
let [, workspaceWidth] =
|
|
|
|
workspace.get_preferred_width(workspaceHeight);
|
|
|
|
|
|
|
|
y1 = spacing;
|
|
|
|
if (workspaceWidth > width) {
|
|
|
|
[, workspaceHeight] = workspace.get_preferred_height(width);
|
|
|
|
y1 += Math.max((availableHeight - workspaceHeight * nWorkspaces) / 2, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
fitAllBox.set_size(width, workspaceHeight);
|
|
|
|
} else {
|
|
|
|
const availableWidth = width - spacing * (nWorkspaces + 1);
|
|
|
|
let workspaceWidth = availableWidth / nWorkspaces;
|
|
|
|
let [, workspaceHeight] =
|
|
|
|
workspace.get_preferred_height(workspaceWidth);
|
|
|
|
|
|
|
|
x1 = spacing;
|
|
|
|
if (workspaceHeight > height) {
|
|
|
|
[, workspaceWidth] = workspace.get_preferred_width(height);
|
|
|
|
x1 += Math.max((availableWidth - workspaceWidth * nWorkspaces) / 2, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
fitAllBox.set_size(workspaceWidth, height);
|
|
|
|
}
|
|
|
|
|
|
|
|
fitAllBox.set_origin(x1, y1);
|
|
|
|
|
|
|
|
return fitAllBox;
|
|
|
|
}
|
|
|
|
|
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 19:34:59 +00:00
|
|
|
_getFirstFitSingleWorkspaceBox(box, spacing, vertical) {
|
2020-12-29 21:05:29 +00:00
|
|
|
const [width, height] = box.get_size();
|
|
|
|
const [workspace] = this._workspaces;
|
|
|
|
|
2020-12-30 15:44:00 +00:00
|
|
|
const rtl = this.text_direction === Clutter.TextDirection.RTL;
|
|
|
|
const adj = this._scrollAdjustment;
|
|
|
|
const currentWorkspace = vertical || !rtl
|
2021-02-09 09:17:29 +00:00
|
|
|
? adj.value : adj.upper - adj.value - 1;
|
2020-12-30 15:44:00 +00:00
|
|
|
|
2020-12-29 21:05:29 +00:00
|
|
|
// Single fit mode implies centered too
|
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 19:34:59 +00:00
|
|
|
let [x1, y1] = box.get_origin();
|
2020-12-29 21:05:29 +00:00
|
|
|
if (vertical) {
|
|
|
|
const [, workspaceHeight] = workspace.get_preferred_height(width);
|
|
|
|
y1 += (height - workspaceHeight) / 2;
|
2020-12-30 15:44:00 +00:00
|
|
|
y1 -= currentWorkspace * (workspaceHeight + spacing);
|
2020-12-29 21:05:29 +00:00
|
|
|
} else {
|
|
|
|
const [, workspaceWidth] = workspace.get_preferred_width(height);
|
|
|
|
x1 += (width - workspaceWidth) / 2;
|
2020-12-30 15:44:00 +00:00
|
|
|
x1 -= currentWorkspace * (workspaceWidth + spacing);
|
2020-12-29 21:05:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
const fitSingleBox = new Clutter.ActorBox({x1, y1});
|
2020-12-29 21:05:29 +00:00
|
|
|
|
|
|
|
if (vertical) {
|
|
|
|
const [, workspaceHeight] = workspace.get_preferred_height(width);
|
|
|
|
fitSingleBox.set_size(width, workspaceHeight);
|
|
|
|
} else {
|
|
|
|
const [, workspaceWidth] = workspace.get_preferred_width(height);
|
|
|
|
fitSingleBox.set_size(workspaceWidth, height);
|
|
|
|
}
|
|
|
|
|
|
|
|
return fitSingleBox;
|
|
|
|
}
|
|
|
|
|
|
|
|
_getSpacing(box, fitMode, vertical) {
|
|
|
|
const [width, height] = box.get_size();
|
|
|
|
const [workspace] = this._workspaces;
|
|
|
|
|
|
|
|
let availableSpace;
|
|
|
|
let workspaceSize;
|
|
|
|
if (vertical) {
|
|
|
|
[, workspaceSize] = workspace.get_preferred_height(width);
|
|
|
|
availableSpace = (height - workspaceSize) / 2;
|
|
|
|
} else {
|
|
|
|
[, workspaceSize] = workspace.get_preferred_width(height);
|
|
|
|
availableSpace = (width - workspaceSize) / 2;
|
|
|
|
}
|
|
|
|
|
2021-02-19 18:37:58 +00:00
|
|
|
const spacing = (availableSpace - workspaceSize * 0.4) * (1 - fitMode);
|
2023-08-06 22:40:20 +00:00
|
|
|
const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
|
2020-12-29 21:05:29 +00:00
|
|
|
|
2021-02-19 18:20:32 +00:00
|
|
|
return Math.clamp(spacing, WORKSPACE_MIN_SPACING * scaleFactor,
|
|
|
|
WORKSPACE_MAX_SPACING * scaleFactor);
|
2020-12-29 21:05:29 +00:00
|
|
|
}
|
|
|
|
|
2021-01-15 18:58:33 +00:00
|
|
|
_getWorkspaceModeForOverviewState(state) {
|
2023-08-06 22:40:20 +00:00
|
|
|
const {ControlsState} = OverviewControls;
|
2021-01-02 19:06:34 +00:00
|
|
|
|
2021-01-15 18:58:33 +00:00
|
|
|
switch (state) {
|
|
|
|
case ControlsState.HIDDEN:
|
|
|
|
return 0;
|
|
|
|
case ControlsState.WINDOW_PICKER:
|
|
|
|
return 1;
|
|
|
|
case ControlsState.APP_GRID:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_updateWorkspacesState() {
|
2020-12-31 17:19:43 +00:00
|
|
|
const adj = this._scrollAdjustment;
|
2021-01-01 16:10:19 +00:00
|
|
|
const fitMode = this._fitModeAdjustment.value;
|
2021-01-02 19:06:34 +00:00
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
const {initialState, finalState, progress} =
|
2021-01-15 18:58:33 +00:00
|
|
|
this._overviewAdjustment.getStateTransitionParams();
|
|
|
|
|
|
|
|
const workspaceMode = (1 - fitMode) * Util.lerp(
|
|
|
|
this._getWorkspaceModeForOverviewState(initialState),
|
|
|
|
this._getWorkspaceModeForOverviewState(finalState),
|
|
|
|
progress);
|
2020-12-31 17:19:43 +00:00
|
|
|
|
|
|
|
// Fade and scale inactive workspaces
|
|
|
|
this._workspaces.forEach((w, index) => {
|
2021-01-02 19:06:34 +00:00
|
|
|
w.stateAdjustment.value = workspaceMode;
|
2021-01-01 16:10:19 +00:00
|
|
|
|
2020-12-31 17:19:43 +00:00
|
|
|
const distanceToCurrentWorkspace = Math.abs(adj.value - index);
|
|
|
|
|
2021-01-15 18:58:33 +00:00
|
|
|
const scaleProgress = 1 - Math.clamp(distanceToCurrentWorkspace, 0, 1);
|
2020-12-31 17:19:43 +00:00
|
|
|
|
2021-01-15 18:58:33 +00:00
|
|
|
const scale = Util.lerp(WORKSPACE_INACTIVE_SCALE, 1, scaleProgress);
|
2020-12-31 17:19:43 +00:00
|
|
|
w.set_scale(scale, scale);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
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 19:34:59 +00:00
|
|
|
_getFitModeForState(state) {
|
2023-08-06 22:40:20 +00:00
|
|
|
const {ControlsState} = OverviewControls;
|
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 19:34:59 +00:00
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case ControlsState.HIDDEN:
|
|
|
|
case ControlsState.WINDOW_PICKER:
|
|
|
|
return FitMode.SINGLE;
|
|
|
|
case ControlsState.APP_GRID:
|
|
|
|
return FitMode.ALL;
|
|
|
|
default:
|
|
|
|
return FitMode.SINGLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_getInitialBoxes(box) {
|
|
|
|
const offsetBox = new Clutter.ActorBox();
|
|
|
|
offsetBox.set_size(...box.get_size());
|
|
|
|
|
|
|
|
let fitSingleBox = offsetBox;
|
|
|
|
let fitAllBox = offsetBox;
|
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
const {transitioning, initialState, finalState} =
|
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 19:34:59 +00:00
|
|
|
this._overviewAdjustment.getStateTransitionParams();
|
|
|
|
|
|
|
|
const isPrimary = Main.layoutManager.primaryIndex === this._monitorIndex;
|
|
|
|
|
|
|
|
if (isPrimary && transitioning) {
|
|
|
|
const initialFitMode = this._getFitModeForState(initialState);
|
|
|
|
const finalFitMode = this._getFitModeForState(finalState);
|
|
|
|
|
|
|
|
// Only use the relative boxes when the overview is in a state
|
|
|
|
// transition, and the corresponding fit modes are different.
|
|
|
|
if (initialFitMode !== finalFitMode) {
|
|
|
|
const initialBox =
|
|
|
|
this._controls.getWorkspacesBoxForState(initialState).copy();
|
|
|
|
const finalBox =
|
|
|
|
this._controls.getWorkspacesBoxForState(finalState).copy();
|
|
|
|
|
|
|
|
// Boxes are relative to ControlsManager, transform them;
|
|
|
|
// this.apply_relative_transform_to_point(controls,
|
|
|
|
// new Graphene.Point3D());
|
|
|
|
// would be more correct, but also more expensive
|
|
|
|
const [parentOffsetX, parentOffsetY] =
|
|
|
|
this.get_parent().allocation.get_origin();
|
|
|
|
[initialBox, finalBox].forEach(b => {
|
|
|
|
b.set_origin(b.x1 - parentOffsetX, b.y1 - parentOffsetY);
|
|
|
|
});
|
|
|
|
|
|
|
|
if (initialFitMode === FitMode.SINGLE)
|
|
|
|
[fitSingleBox, fitAllBox] = [initialBox, finalBox];
|
|
|
|
else
|
|
|
|
[fitAllBox, fitSingleBox] = [initialBox, finalBox];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return [fitSingleBox, fitAllBox];
|
|
|
|
}
|
|
|
|
|
2021-01-02 19:06:34 +00:00
|
|
|
_updateWorkspaceMode() {
|
|
|
|
this._updateWorkspacesState();
|
|
|
|
}
|
|
|
|
|
2020-06-26 14:04:44 +00:00
|
|
|
vfunc_allocate(box) {
|
|
|
|
this.set_allocation(box);
|
|
|
|
|
2023-08-28 17:44:11 +00:00
|
|
|
if (this._workspaces.length === 0)
|
2020-06-26 14:04:44 +00:00
|
|
|
return;
|
|
|
|
|
2020-12-29 21:05:29 +00:00
|
|
|
const vertical = global.workspaceManager.layout_rows === -1;
|
2020-06-26 14:04:44 +00:00
|
|
|
const rtl = this.text_direction === Clutter.TextDirection.RTL;
|
|
|
|
|
2020-12-29 21:05:29 +00:00
|
|
|
const fitMode = this._fitModeAdjustment.value;
|
2020-12-11 18:57:49 +00: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 19:34:59 +00:00
|
|
|
let [fitSingleBox, fitAllBox] = this._getInitialBoxes(box);
|
2020-12-29 21:05:29 +00:00
|
|
|
const fitSingleSpacing =
|
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 19:34:59 +00:00
|
|
|
this._getSpacing(fitSingleBox, FitMode.SINGLE, vertical);
|
|
|
|
fitSingleBox =
|
|
|
|
this._getFirstFitSingleWorkspaceBox(fitSingleBox, fitSingleSpacing, vertical);
|
2020-06-26 14:04:44 +00:00
|
|
|
|
2020-12-29 21:05:29 +00:00
|
|
|
const fitAllSpacing =
|
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 19:34:59 +00:00
|
|
|
this._getSpacing(fitAllBox, FitMode.ALL, vertical);
|
|
|
|
fitAllBox =
|
|
|
|
this._getFirstFitAllWorkspaceBox(fitAllBox, fitAllSpacing, vertical);
|
2020-12-29 21:05:29 +00:00
|
|
|
|
|
|
|
// Account for RTL locales by reversing the list
|
|
|
|
const workspaces = this._workspaces.slice();
|
|
|
|
if (rtl)
|
|
|
|
workspaces.reverse();
|
|
|
|
|
2021-03-09 17:12:02 +00:00
|
|
|
const [fitSingleX1, fitSingleY1] = fitSingleBox.get_origin();
|
|
|
|
const [fitSingleWidth, fitSingleHeight] = fitSingleBox.get_size();
|
|
|
|
const [fitAllX1, fitAllY1] = fitAllBox.get_origin();
|
|
|
|
const [fitAllWidth, fitAllHeight] = fitAllBox.get_size();
|
|
|
|
|
2020-12-29 21:05:29 +00:00
|
|
|
workspaces.forEach(child => {
|
|
|
|
if (fitMode === FitMode.SINGLE)
|
|
|
|
box = fitSingleBox;
|
|
|
|
else if (fitMode === FitMode.ALL)
|
|
|
|
box = fitAllBox;
|
|
|
|
else
|
|
|
|
box = fitSingleBox.interpolate(fitAllBox, fitMode);
|
|
|
|
|
|
|
|
child.allocate_align_fill(box, 0.5, 0.5, false, false);
|
|
|
|
|
|
|
|
if (vertical) {
|
|
|
|
fitSingleBox.set_origin(
|
2021-03-09 17:12:02 +00:00
|
|
|
fitSingleX1,
|
|
|
|
fitSingleBox.y1 + fitSingleHeight + fitSingleSpacing);
|
2020-12-29 21:05:29 +00:00
|
|
|
fitAllBox.set_origin(
|
2021-03-09 17:12:02 +00:00
|
|
|
fitAllX1,
|
|
|
|
fitAllBox.y1 + fitAllHeight + fitAllSpacing);
|
2020-12-29 21:05:29 +00:00
|
|
|
} else {
|
|
|
|
fitSingleBox.set_origin(
|
2021-03-09 17:12:02 +00:00
|
|
|
fitSingleBox.x1 + fitSingleWidth + fitSingleSpacing,
|
|
|
|
fitSingleY1);
|
2020-12-29 21:05:29 +00:00
|
|
|
fitAllBox.set_origin(
|
2021-03-09 17:12:02 +00:00
|
|
|
fitAllBox.x1 + fitAllWidth + fitAllSpacing,
|
|
|
|
fitAllY1);
|
2020-12-29 21:05:29 +00:00
|
|
|
}
|
2020-06-26 14:04:44 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
getActiveWorkspace() {
|
2018-01-03 07:55:38 +00:00
|
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
let active = workspaceManager.get_active_workspace_index();
|
2010-07-15 14:21:32 +00:00
|
|
|
return this._workspaces[active];
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2010-07-15 14:21:32 +00:00
|
|
|
|
2021-01-02 19:17:17 +00:00
|
|
|
prepareToLeaveOverview() {
|
2020-06-28 12:42:44 +00:00
|
|
|
for (let w = 0; w < this._workspaces.length; w++)
|
2021-01-01 16:10:19 +00:00
|
|
|
this._workspaces[w].prepareToLeaveOverview();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2010-02-18 15:43:58 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
syncStacking(stackIndices) {
|
2010-01-22 02:33:48 +00:00
|
|
|
for (let i = 0; i < this._workspaces.length; i++)
|
|
|
|
this._workspaces[i].syncStacking(stackIndices);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2010-01-22 02:33:48 +00:00
|
|
|
|
2020-06-26 14:04:44 +00:00
|
|
|
_scrollToActive() {
|
2023-08-06 22:40:20 +00:00
|
|
|
const {workspaceManager} = global;
|
2020-06-26 14:04:44 +00:00
|
|
|
const active = workspaceManager.get_active_workspace_index();
|
2010-03-22 23:01:44 +00:00
|
|
|
|
2020-06-26 14:04:44 +00:00
|
|
|
this._animating = true;
|
|
|
|
this._updateVisibility();
|
2010-02-10 05:56:36 +00:00
|
|
|
|
2020-06-26 14:04:44 +00:00
|
|
|
this._scrollAdjustment.remove_transition('value');
|
|
|
|
this._scrollAdjustment.ease(active, {
|
|
|
|
duration: WORKSPACE_SWITCH_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
|
|
|
onComplete: () => {
|
|
|
|
this._animating = false;
|
|
|
|
this._updateVisibility();
|
|
|
|
},
|
|
|
|
});
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2010-02-11 14:52:49 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_updateVisibility() {
|
2018-01-03 07:55:38 +00:00
|
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
let active = workspaceManager.get_active_workspace_index();
|
2010-02-11 14:52:49 +00:00
|
|
|
|
2020-12-30 15:44:00 +00:00
|
|
|
const fitMode = this._fitModeAdjustment.value;
|
|
|
|
const singleFitMode = fitMode === FitMode.SINGLE;
|
|
|
|
|
2010-03-22 23:01:44 +00:00
|
|
|
for (let w = 0; w < this._workspaces.length; w++) {
|
|
|
|
let workspace = this._workspaces[w];
|
2019-08-20 02:10:46 +00:00
|
|
|
|
2020-12-30 15:44:00 +00:00
|
|
|
if (this._animating || this._gestureActive || !singleFitMode)
|
2019-07-16 09:24:13 +00:00
|
|
|
workspace.show();
|
2019-08-20 02:10:46 +00:00
|
|
|
else
|
2020-12-29 21:05:29 +00:00
|
|
|
workspace.visible = Math.abs(w - active) <= 1;
|
2010-02-11 14:52:49 +00:00
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2010-02-11 14:52:49 +00:00
|
|
|
|
2021-12-03 19:04:05 +00:00
|
|
|
_refreshWorkspaces() {
|
|
|
|
for (let ws = this._workspaces.pop(); ws; ws = this._workspaces.pop()) {
|
|
|
|
ws.destroy();
|
|
|
|
}
|
|
|
|
this._updateWorkspaces();
|
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_updateWorkspaces() {
|
2018-01-03 07:55:38 +00:00
|
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
let newNumWorkspaces = workspaceManager.n_workspaces;
|
2013-09-11 14:09:13 +00:00
|
|
|
|
2014-01-03 18:04:06 +00:00
|
|
|
for (let j = 0; j < newNumWorkspaces; j++) {
|
2018-01-03 07:55:38 +00:00
|
|
|
let metaWorkspace = workspaceManager.get_workspace_by_index(j);
|
2014-01-03 18:04:06 +00:00
|
|
|
let workspace;
|
|
|
|
|
|
|
|
if (j >= this._workspaces.length) { /* added */
|
2021-02-05 16:22:18 +00:00
|
|
|
workspace = new Workspace.Workspace(
|
|
|
|
metaWorkspace,
|
|
|
|
this._monitorIndex,
|
|
|
|
this._overviewAdjustment);
|
2019-07-16 09:24:13 +00:00
|
|
|
this.add_actor(workspace);
|
2014-01-03 18:04:06 +00:00
|
|
|
this._workspaces[j] = workspace;
|
|
|
|
} else {
|
|
|
|
workspace = this._workspaces[j];
|
|
|
|
|
2023-08-07 00:51:19 +00:00
|
|
|
if (workspace.metaWorkspace !== metaWorkspace) { /* removed */
|
2014-01-03 18:04:06 +00:00
|
|
|
workspace.destroy();
|
|
|
|
this._workspaces.splice(j, 1);
|
|
|
|
} /* else kept */
|
2011-06-01 12:47:51 +00:00
|
|
|
}
|
2010-01-22 02:33:48 +00:00
|
|
|
}
|
2013-09-11 14:09:13 +00:00
|
|
|
|
2020-09-29 16:38:40 +00:00
|
|
|
for (let j = this._workspaces.length - 1; j >= newNumWorkspaces; j--) {
|
|
|
|
this._workspaces[j].destroy();
|
|
|
|
this._workspaces.splice(j, 1);
|
|
|
|
}
|
2020-12-31 17:19:43 +00:00
|
|
|
|
|
|
|
this._updateWorkspacesState();
|
2023-03-18 16:54:35 +00:00
|
|
|
this._updateVisibility();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2010-01-22 02:33:48 +00:00
|
|
|
|
2019-01-31 14:08:10 +00:00
|
|
|
_activeWorkspaceChanged(_wm, _from, _to, _direction) {
|
2020-06-26 14:04:44 +00:00
|
|
|
this._scrollToActive();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2010-01-22 02:33:48 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onDestroy() {
|
2017-10-31 01:19:44 +00:00
|
|
|
super._onDestroy();
|
2013-09-11 16:12:42 +00:00
|
|
|
|
2020-06-26 16:38:28 +00:00
|
|
|
this._workspaces = [];
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2010-03-16 15:51:24 +00:00
|
|
|
|
2019-03-20 17:19:01 +00:00
|
|
|
startTouchGesture() {
|
|
|
|
this._gestureActive = true;
|
2020-07-10 13:41:09 +00:00
|
|
|
|
|
|
|
this._updateVisibility();
|
2019-03-20 17:19:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
endTouchGesture() {
|
|
|
|
this._gestureActive = false;
|
|
|
|
|
|
|
|
// Make sure title captions etc are shown as necessary
|
2020-06-26 14:04:44 +00:00
|
|
|
this._scrollToActive();
|
2019-03-20 17:19:01 +00:00
|
|
|
this._updateVisibility();
|
|
|
|
}
|
|
|
|
|
2010-07-11 12:41:17 +00:00
|
|
|
// sync the workspaces' positions to the value of the scroll adjustment
|
2010-02-10 05:56:36 +00:00
|
|
|
// and change the active workspace if appropriate
|
2020-12-18 15:21:58 +00:00
|
|
|
_onScrollAdjustmentChanged() {
|
2020-06-26 14:04:44 +00:00
|
|
|
if (!this.has_allocation())
|
2010-02-10 05:56:36 +00:00
|
|
|
return;
|
|
|
|
|
2020-06-26 14:04:44 +00:00
|
|
|
const adj = this._scrollAdjustment;
|
|
|
|
const allowSwitch =
|
|
|
|
adj.get_transition('value') === null && !this._gestureActive;
|
|
|
|
|
2018-01-03 07:55:38 +00:00
|
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
let active = workspaceManager.get_active_workspace_index();
|
2010-02-10 05:56:36 +00:00
|
|
|
let current = Math.round(adj.value);
|
|
|
|
|
2020-06-26 14:04:44 +00:00
|
|
|
if (allowSwitch && active !== current) {
|
2013-01-19 01:26:44 +00:00
|
|
|
if (!this._workspaces[current]) {
|
|
|
|
// The current workspace was destroyed. This could happen
|
|
|
|
// when you are on the last empty workspace, and consolidate
|
|
|
|
// windows using the thumbnail bar.
|
|
|
|
// In that case, the intended behavior is to stay on the empty
|
|
|
|
// workspace, which is the last one, so pick it.
|
|
|
|
current = this._workspaces.length - 1;
|
|
|
|
}
|
|
|
|
|
2010-02-14 23:32:57 +00:00
|
|
|
let metaWorkspace = this._workspaces[current].metaWorkspace;
|
2010-07-11 12:41:17 +00:00
|
|
|
metaWorkspace.activate(global.get_current_time());
|
2010-02-10 05:56:36 +00:00
|
|
|
}
|
|
|
|
|
2020-12-31 17:19:43 +00:00
|
|
|
this._updateWorkspacesState();
|
2020-12-30 15:44:00 +00:00
|
|
|
this.queue_relayout();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
2010-10-04 14:42:11 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
export const ExtraWorkspaceView = GObject.registerClass(
|
2019-07-16 09:24:13 +00:00
|
|
|
class ExtraWorkspaceView extends WorkspacesViewBase {
|
2021-01-02 19:06:34 +00:00
|
|
|
_init(monitorIndex, overviewAdjustment) {
|
|
|
|
super._init(monitorIndex, overviewAdjustment);
|
2021-02-05 16:22:18 +00:00
|
|
|
this._workspace =
|
|
|
|
new Workspace.Workspace(null, monitorIndex, overviewAdjustment);
|
2019-07-16 09:24:13 +00:00
|
|
|
this.add_actor(this._workspace);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2013-09-11 16:12:42 +00:00
|
|
|
|
2021-01-02 19:06:34 +00:00
|
|
|
_updateWorkspaceMode() {
|
|
|
|
const overviewState = this._overviewAdjustment.value;
|
|
|
|
|
|
|
|
const progress = Math.clamp(overviewState,
|
|
|
|
OverviewControls.ControlsState.HIDDEN,
|
|
|
|
OverviewControls.ControlsState.WINDOW_PICKER);
|
|
|
|
|
|
|
|
this._workspace.stateAdjustment.value = progress;
|
|
|
|
}
|
|
|
|
|
2021-02-20 12:33:29 +00:00
|
|
|
vfunc_allocate(box) {
|
|
|
|
this.set_allocation(box);
|
|
|
|
|
|
|
|
const [width, height] = box.get_size();
|
|
|
|
const [, childWidth] = this._workspace.get_preferred_width(height);
|
|
|
|
|
|
|
|
const childBox = new Clutter.ActorBox();
|
|
|
|
childBox.set_origin(Math.round((width - childWidth) / 2), 0);
|
|
|
|
childBox.set_size(childWidth, height);
|
|
|
|
this._workspace.allocate(childBox);
|
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
getActiveWorkspace() {
|
2016-06-24 16:15:48 +00:00
|
|
|
return this._workspace;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-24 16:15:48 +00:00
|
|
|
|
2021-01-02 19:17:17 +00:00
|
|
|
prepareToLeaveOverview() {
|
2021-01-02 19:06:34 +00:00
|
|
|
this._workspace.prepareToLeaveOverview();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2013-09-11 16:12:42 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
syncStacking(stackIndices) {
|
2013-09-11 16:12:42 +00:00
|
|
|
this._workspace.syncStacking(stackIndices);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2013-09-11 16:12:42 +00:00
|
|
|
|
2019-03-20 17:19:01 +00:00
|
|
|
startTouchGesture() {
|
|
|
|
}
|
|
|
|
|
|
|
|
endTouchGesture() {
|
|
|
|
}
|
2013-11-04 22:23:44 +00:00
|
|
|
});
|
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
export const SecondaryMonitorDisplay = GObject.registerClass(
|
2021-02-19 21:03:07 +00:00
|
|
|
class SecondaryMonitorDisplay extends St.Widget {
|
|
|
|
_init(monitorIndex, controls, scrollAdjustment, fitModeAdjustment, overviewAdjustment) {
|
|
|
|
this._monitorIndex = monitorIndex;
|
|
|
|
this._controls = controls;
|
|
|
|
this._scrollAdjustment = scrollAdjustment;
|
|
|
|
this._fitModeAdjustment = fitModeAdjustment;
|
|
|
|
this._overviewAdjustment = overviewAdjustment;
|
|
|
|
|
|
|
|
super._init({
|
2021-02-20 10:18:29 +00:00
|
|
|
style_class: 'secondary-monitor-workspaces',
|
2021-02-19 21:03:07 +00:00
|
|
|
constraints: new Layout.MonitorConstraint({
|
|
|
|
index: this._monitorIndex,
|
|
|
|
work_area: true,
|
|
|
|
}),
|
2021-03-03 11:37:51 +00:00
|
|
|
clip_to_allocation: true,
|
2021-02-19 21:03:07 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
this.connect('destroy', () => this._onDestroy());
|
|
|
|
|
2021-02-20 10:18:29 +00:00
|
|
|
this._thumbnails = new ThumbnailsBox(
|
|
|
|
this._scrollAdjustment, monitorIndex);
|
|
|
|
this.add_child(this._thumbnails);
|
|
|
|
|
|
|
|
this._thumbnails.connect('notify::should-show',
|
|
|
|
() => this._updateThumbnailVisibility());
|
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
this._overviewAdjustment.connectObject('notify::value', () => {
|
|
|
|
this._updateThumbnailParams();
|
|
|
|
this.queue_relayout();
|
|
|
|
}, this);
|
2021-02-20 10:18:29 +00:00
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
this._settings = new Gio.Settings({schema_id: MUTTER_SCHEMA});
|
2021-02-19 21:03:07 +00:00
|
|
|
this._settings.connect('changed::workspaces-only-on-primary',
|
2021-02-20 10:18:29 +00:00
|
|
|
() => this._workspacesOnPrimaryChanged());
|
|
|
|
this._workspacesOnPrimaryChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
_getThumbnailParamsForState(state) {
|
2023-08-06 22:40:20 +00:00
|
|
|
const {ControlsState} = OverviewControls;
|
2021-02-20 10:18:29 +00:00
|
|
|
|
|
|
|
let opacity, scale;
|
|
|
|
switch (state) {
|
|
|
|
case ControlsState.HIDDEN:
|
|
|
|
case ControlsState.WINDOW_PICKER:
|
|
|
|
opacity = 255;
|
|
|
|
scale = 1;
|
|
|
|
break;
|
|
|
|
case ControlsState.APP_GRID:
|
|
|
|
opacity = 0;
|
|
|
|
scale = 0.5;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
opacity = 255;
|
|
|
|
scale = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
return {opacity, scale};
|
2021-02-19 21:03:07 +00:00
|
|
|
}
|
|
|
|
|
2021-02-20 10:18:29 +00:00
|
|
|
_getThumbnailsHeight(box) {
|
|
|
|
if (!this._thumbnails.visible)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
const [width, height] = box.get_size();
|
2023-08-06 22:40:20 +00:00
|
|
|
const {expandFraction} = this._thumbnails;
|
2021-02-20 10:18:29 +00:00
|
|
|
const [thumbnailsHeight] = this._thumbnails.get_preferred_height(width);
|
|
|
|
return Math.min(
|
|
|
|
thumbnailsHeight * expandFraction,
|
2023-08-11 22:32:35 +00:00
|
|
|
height * this._thumbnails.maxThumbnailScale);
|
2021-02-20 10:18:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_getWorkspacesBoxForState(state, box, padding, thumbnailsHeight, spacing) {
|
2023-08-06 22:40:20 +00:00
|
|
|
const {ControlsState} = OverviewControls;
|
2021-02-20 12:34:39 +00:00
|
|
|
const workspaceBox = box.copy();
|
|
|
|
const [width, height] = workspaceBox.get_size();
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case ControlsState.HIDDEN:
|
|
|
|
break;
|
|
|
|
case ControlsState.WINDOW_PICKER:
|
2021-02-20 10:18:29 +00:00
|
|
|
workspaceBox.set_origin(0, padding + thumbnailsHeight + spacing);
|
|
|
|
workspaceBox.set_size(
|
|
|
|
width,
|
|
|
|
height - 2 * padding - thumbnailsHeight - spacing);
|
|
|
|
break;
|
2021-02-20 12:34:39 +00:00
|
|
|
case ControlsState.APP_GRID:
|
|
|
|
workspaceBox.set_origin(0, padding);
|
|
|
|
workspaceBox.set_size(
|
|
|
|
width,
|
|
|
|
height - 2 * padding);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return workspaceBox;
|
|
|
|
}
|
|
|
|
|
2021-02-19 21:03:07 +00:00
|
|
|
vfunc_allocate(box) {
|
|
|
|
this.set_allocation(box);
|
|
|
|
|
|
|
|
const themeNode = this.get_theme_node();
|
|
|
|
const contentBox = themeNode.get_content_box(box);
|
2021-02-20 10:18:29 +00:00
|
|
|
const [width, height] = contentBox.get_size();
|
2023-08-06 22:40:20 +00:00
|
|
|
const {expandFraction} = this._thumbnails;
|
2021-02-20 10:18:29 +00:00
|
|
|
const spacing = themeNode.get_length('spacing') * expandFraction;
|
2021-02-20 12:34:39 +00:00
|
|
|
const padding =
|
|
|
|
Math.round((1 - SECONDARY_WORKSPACE_SCALE) * height / 2);
|
|
|
|
|
2021-02-20 10:18:29 +00:00
|
|
|
const thumbnailsHeight = this._getThumbnailsHeight(contentBox);
|
|
|
|
|
|
|
|
if (this._thumbnails.visible) {
|
|
|
|
const childBox = new Clutter.ActorBox();
|
|
|
|
childBox.set_origin(0, padding);
|
|
|
|
childBox.set_size(width, thumbnailsHeight);
|
|
|
|
this._thumbnails.allocate(childBox);
|
|
|
|
}
|
|
|
|
|
2021-02-20 12:34:39 +00:00
|
|
|
const {
|
|
|
|
currentState, initialState, finalState, transitioning, progress,
|
|
|
|
} = this._overviewAdjustment.getStateTransitionParams();
|
|
|
|
|
|
|
|
let workspacesBox;
|
2021-02-20 10:18:29 +00:00
|
|
|
const workspaceParams = [contentBox, padding, thumbnailsHeight, spacing];
|
2021-02-20 12:34:39 +00:00
|
|
|
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);
|
2021-02-19 21:03:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_onDestroy() {
|
|
|
|
if (this._settings)
|
|
|
|
this._settings.run_dispose();
|
|
|
|
this._settings = null;
|
2021-02-20 10:18:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_workspacesOnPrimaryChanged() {
|
|
|
|
this._updateWorkspacesView();
|
|
|
|
this._updateThumbnailVisibility();
|
2021-02-19 21:03:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_updateWorkspacesView() {
|
|
|
|
if (this._workspacesView)
|
|
|
|
this._workspacesView.destroy();
|
|
|
|
|
|
|
|
if (this._settings.get_boolean('workspaces-only-on-primary')) {
|
|
|
|
this._workspacesView = new ExtraWorkspaceView(
|
|
|
|
this._monitorIndex,
|
|
|
|
this._overviewAdjustment);
|
|
|
|
} else {
|
|
|
|
this._workspacesView = new WorkspacesView(
|
|
|
|
this._monitorIndex,
|
|
|
|
this._controls,
|
|
|
|
this._scrollAdjustment,
|
|
|
|
this._fitModeAdjustment,
|
|
|
|
this._overviewAdjustment);
|
|
|
|
}
|
|
|
|
this.add_child(this._workspacesView);
|
|
|
|
}
|
|
|
|
|
2021-02-20 10:18:29 +00:00
|
|
|
_updateThumbnailVisibility() {
|
|
|
|
const visible =
|
|
|
|
this._thumbnails.should_show &&
|
|
|
|
!this._settings.get_boolean('workspaces-only-on-primary');
|
|
|
|
|
|
|
|
if (this._thumbnails.visible === visible)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._thumbnails.show();
|
2021-05-12 14:55:34 +00:00
|
|
|
this._updateThumbnailParams();
|
2021-02-20 10:18:29 +00:00
|
|
|
this._thumbnails.ease_property('expand-fraction', visible ? 1 : 0, {
|
|
|
|
duration: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
onComplete: () => (this._thumbnails.visible = visible),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
_updateThumbnailParams() {
|
2021-05-12 14:55:34 +00:00
|
|
|
if (!this._thumbnails.visible)
|
|
|
|
return;
|
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
const {initialState, finalState, progress} =
|
2021-02-20 10:18:29 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
this._thumbnails.set({
|
|
|
|
opacity,
|
|
|
|
scale_x: scale,
|
|
|
|
scale_y: scale,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-02-19 21:03:07 +00:00
|
|
|
getActiveWorkspace() {
|
|
|
|
return this._workspacesView.getActiveWorkspace();
|
|
|
|
}
|
|
|
|
|
|
|
|
prepareToLeaveOverview() {
|
|
|
|
this._workspacesView.prepareToLeaveOverview();
|
|
|
|
}
|
|
|
|
|
|
|
|
syncStacking(stackIndices) {
|
|
|
|
this._workspacesView.syncStacking(stackIndices);
|
|
|
|
}
|
|
|
|
|
|
|
|
startTouchGesture() {
|
|
|
|
this._workspacesView.startTouchGesture();
|
|
|
|
}
|
|
|
|
|
|
|
|
endTouchGesture() {
|
|
|
|
this._workspacesView.endTouchGesture();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
export const WorkspacesDisplay = GObject.registerClass(
|
2019-07-16 09:24:13 +00:00
|
|
|
class WorkspacesDisplay extends St.Widget {
|
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 19:34:59 +00:00
|
|
|
_init(controls, scrollAdjustment, overviewAdjustment) {
|
2020-06-02 17:15:45 +00:00
|
|
|
super._init({
|
2020-06-26 16:38:28 +00:00
|
|
|
layout_manager: new Clutter.BinLayout(),
|
2022-02-20 15:53:43 +00:00
|
|
|
reactive: true,
|
2020-06-02 17:15:45 +00:00
|
|
|
});
|
2012-12-13 19:19:44 +00: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 19:34:59 +00:00
|
|
|
this._controls = controls;
|
2021-01-02 19:06:34 +00:00
|
|
|
this._overviewAdjustment = overviewAdjustment;
|
2020-12-29 21:05:29 +00:00
|
|
|
this._fitModeAdjustment = new St.Adjustment({
|
|
|
|
actor: this,
|
|
|
|
value: FitMode.SINGLE,
|
|
|
|
lower: FitMode.SINGLE,
|
|
|
|
upper: FitMode.ALL,
|
|
|
|
});
|
|
|
|
|
2019-07-07 17:51:55 +00:00
|
|
|
let workspaceManager = global.workspace_manager;
|
2019-07-08 08:03:20 +00:00
|
|
|
this._scrollAdjustment = scrollAdjustment;
|
2019-07-07 17:51:55 +00:00
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
global.window_manager.connectObject('switch-workspace',
|
|
|
|
this._activeWorkspaceChanged.bind(this), this);
|
2019-07-07 17:51:55 +00:00
|
|
|
|
2019-07-08 08:47:04 +00:00
|
|
|
this._swipeTracker = new SwipeTracker.SwipeTracker(
|
2021-01-08 20:25:45 +00:00
|
|
|
Main.layoutManager.overviewGroup,
|
2021-03-01 16:53:55 +00:00
|
|
|
Clutter.Orientation.HORIZONTAL,
|
2021-01-08 20:25:45 +00:00
|
|
|
Shell.ActionMode.OVERVIEW,
|
2023-08-06 22:40:20 +00:00
|
|
|
{allowDrag: false});
|
2021-02-07 20:06:02 +00:00
|
|
|
this._swipeTracker.allowLongSwipes = true;
|
2019-07-08 08:47:04 +00:00
|
|
|
this._swipeTracker.connect('begin', this._switchWorkspaceBegin.bind(this));
|
|
|
|
this._swipeTracker.connect('update', this._switchWorkspaceUpdate.bind(this));
|
|
|
|
this._swipeTracker.connect('end', this._switchWorkspaceEnd.bind(this));
|
2020-01-28 19:08:32 +00:00
|
|
|
this.connect('notify::mapped', this._updateSwipeTracker.bind(this));
|
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
workspaceManager.connectObject(
|
|
|
|
'workspaces-reordered', this._workspacesReordered.bind(this),
|
|
|
|
'notify::layout-rows', this._updateTrackerOrientation.bind(this), this);
|
2021-02-11 23:24:13 +00:00
|
|
|
this._updateTrackerOrientation();
|
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
Main.overview.connectObject(
|
|
|
|
'window-drag-begin', this._windowDragBegin.bind(this),
|
|
|
|
'window-drag-end', this._windowDragEnd.bind(this), this);
|
2019-03-20 17:19:01 +00:00
|
|
|
|
2020-06-05 05:23:53 +00:00
|
|
|
this._primaryVisible = true;
|
2011-11-25 17:25:31 +00:00
|
|
|
this._primaryIndex = Main.layoutManager.primaryIndex;
|
2012-12-16 23:46:34 +00:00
|
|
|
this._workspacesViews = [];
|
2011-11-25 17:25:31 +00:00
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
this._settings = new Gio.Settings({schema_id: MUTTER_SCHEMA});
|
2011-01-31 01:35:58 +00:00
|
|
|
|
2020-01-28 19:08:32 +00:00
|
|
|
this._inWindowDrag = false;
|
2021-01-13 22:24:49 +00:00
|
|
|
this._leavingOverview = false;
|
2019-08-12 14:25:48 +00:00
|
|
|
|
2019-07-07 17:51:55 +00:00
|
|
|
this._gestureActive = false; // touch(pad) gestures
|
2020-01-28 19:08:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_windowDragBegin() {
|
|
|
|
this._inWindowDrag = true;
|
|
|
|
this._updateSwipeTracker();
|
|
|
|
}
|
|
|
|
|
|
|
|
_windowDragEnd() {
|
|
|
|
this._inWindowDrag = false;
|
|
|
|
this._updateSwipeTracker();
|
|
|
|
}
|
|
|
|
|
|
|
|
_updateSwipeTracker() {
|
2021-01-13 22:24:49 +00:00
|
|
|
this._swipeTracker.enabled =
|
|
|
|
this.mapped &&
|
|
|
|
!this._inWindowDrag &&
|
|
|
|
!this._leavingOverview;
|
2019-07-07 17:51:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_workspacesReordered() {
|
|
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
|
|
|
|
this._scrollAdjustment.value =
|
|
|
|
workspaceManager.get_active_workspace_index();
|
|
|
|
}
|
|
|
|
|
2020-06-25 22:25:22 +00:00
|
|
|
_activeWorkspaceChanged(_wm, _from, to, _direction) {
|
2019-07-08 08:47:04 +00:00
|
|
|
if (this._gestureActive)
|
2019-07-07 17:51:55 +00:00
|
|
|
return;
|
|
|
|
|
2020-06-25 22:25:22 +00:00
|
|
|
this._scrollAdjustment.ease(to, {
|
2019-07-08 08:47:04 +00:00
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
2019-07-07 17:51:55 +00:00
|
|
|
duration: WORKSPACE_SWITCH_TIME,
|
|
|
|
});
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2012-10-19 11:55:18 +00:00
|
|
|
|
2021-02-11 23:24:13 +00:00
|
|
|
_updateTrackerOrientation() {
|
2023-08-06 22:40:20 +00:00
|
|
|
const {layoutRows} = global.workspace_manager;
|
2021-02-11 23:24:13 +00:00
|
|
|
this._swipeTracker.orientation = layoutRows !== -1
|
|
|
|
? Clutter.Orientation.HORIZONTAL
|
|
|
|
: Clutter.Orientation.VERTICAL;
|
|
|
|
}
|
|
|
|
|
2019-07-08 08:47:04 +00:00
|
|
|
_directionForProgress(progress) {
|
|
|
|
if (global.workspace_manager.layout_rows === -1) {
|
|
|
|
return progress > 0
|
|
|
|
? Meta.MotionDirection.DOWN
|
|
|
|
: Meta.MotionDirection.UP;
|
|
|
|
} else if (this.text_direction === Clutter.TextDirection.RTL) {
|
|
|
|
return progress > 0
|
|
|
|
? Meta.MotionDirection.LEFT
|
|
|
|
: Meta.MotionDirection.RIGHT;
|
|
|
|
} else {
|
|
|
|
return progress > 0
|
|
|
|
? Meta.MotionDirection.RIGHT
|
|
|
|
: Meta.MotionDirection.LEFT;
|
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2012-11-26 04:40:48 +00:00
|
|
|
|
2019-07-08 08:47:04 +00:00
|
|
|
_switchWorkspaceBegin(tracker, monitor) {
|
|
|
|
if (this._workspacesOnlyOnPrimary && monitor !== this._primaryIndex)
|
|
|
|
return;
|
2019-03-20 17:19:01 +00:00
|
|
|
|
2019-07-08 08:47:04 +00:00
|
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
let adjustment = this._scrollAdjustment;
|
|
|
|
if (this._gestureActive)
|
|
|
|
adjustment.remove_transition('value');
|
|
|
|
|
2021-02-16 02:50:52 +00:00
|
|
|
const distance = global.workspace_manager.layout_rows === -1
|
|
|
|
? this.height : this.width;
|
|
|
|
|
2019-03-20 17:19:01 +00:00
|
|
|
for (let i = 0; i < this._workspacesViews.length; i++)
|
|
|
|
this._workspacesViews[i].startTouchGesture();
|
|
|
|
|
2019-07-08 08:47:04 +00:00
|
|
|
let progress = adjustment.value / adjustment.page_size;
|
|
|
|
let points = Array.from(
|
2023-08-06 22:40:20 +00:00
|
|
|
{length: workspaceManager.n_workspaces}, (v, i) => i);
|
2019-03-20 17:19:01 +00:00
|
|
|
|
2019-07-08 08:47:04 +00:00
|
|
|
tracker.confirmSwipe(distance, points, progress, Math.round(progress));
|
|
|
|
|
|
|
|
this._gestureActive = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_switchWorkspaceUpdate(tracker, progress) {
|
2019-03-20 17:19:01 +00:00
|
|
|
let adjustment = this._scrollAdjustment;
|
2019-07-08 08:47:04 +00:00
|
|
|
adjustment.value = progress * adjustment.page_size;
|
2019-03-20 17:19:01 +00:00
|
|
|
}
|
|
|
|
|
2019-07-08 08:47:04 +00:00
|
|
|
_switchWorkspaceEnd(tracker, duration, endProgress) {
|
2019-03-20 17:19:01 +00:00
|
|
|
let workspaceManager = global.workspace_manager;
|
2019-07-08 08:47:04 +00:00
|
|
|
let newWs = workspaceManager.get_workspace_by_index(endProgress);
|
|
|
|
|
|
|
|
this._scrollAdjustment.ease(endProgress, {
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
|
|
|
duration,
|
|
|
|
onComplete: () => {
|
2020-06-29 14:50:27 +00:00
|
|
|
if (!newWs.active)
|
2019-07-08 08:47:04 +00:00
|
|
|
newWs.activate(global.get_current_time());
|
|
|
|
this._endTouchGesture();
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2019-03-20 17:19:01 +00:00
|
|
|
|
2019-07-08 08:47:04 +00:00
|
|
|
_endTouchGesture() {
|
|
|
|
for (let i = 0; i < this._workspacesViews.length; i++)
|
|
|
|
this._workspacesViews[i].endTouchGesture();
|
|
|
|
this._gestureActive = false;
|
2019-03-20 17:19:01 +00:00
|
|
|
}
|
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
vfunc_navigate_focus(from, direction) {
|
2021-08-13 00:52:09 +00:00
|
|
|
return this._getPrimaryView()?.navigate_focus(from, direction, false);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2013-11-04 22:23:44 +00:00
|
|
|
|
2020-06-05 05:23:53 +00:00
|
|
|
setPrimaryWorkspaceVisible(visible) {
|
|
|
|
if (this._primaryVisible === visible)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._primaryVisible = visible;
|
|
|
|
|
|
|
|
const primaryIndex = Main.layoutManager.primaryIndex;
|
|
|
|
const primaryWorkspace = this._workspacesViews[primaryIndex];
|
|
|
|
if (primaryWorkspace)
|
|
|
|
primaryWorkspace.visible = visible;
|
|
|
|
}
|
|
|
|
|
2021-01-02 19:17:17 +00:00
|
|
|
prepareToEnterOverview() {
|
2020-06-02 17:15:45 +00:00
|
|
|
this.show();
|
2011-11-25 17:25:31 +00:00
|
|
|
this._updateWorkspacesViews();
|
2020-05-20 11:39:11 +00:00
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
Main.overview.connectObject(
|
|
|
|
'windows-restacked', this._onRestacked.bind(this),
|
|
|
|
'scroll-event', this._onScrollEvent.bind(this), this);
|
2015-03-26 15:17:35 +00:00
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
global.stage.connectObject(
|
|
|
|
'key-press-event', this._onKeyPressEvent.bind(this), this);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2010-10-04 14:42:11 +00:00
|
|
|
|
2021-01-02 19:17:17 +00:00
|
|
|
prepareToLeaveOverview() {
|
2020-06-28 12:42:44 +00:00
|
|
|
for (let i = 0; i < this._workspacesViews.length; i++)
|
2021-01-02 19:17:17 +00:00
|
|
|
this._workspacesViews[i].prepareToLeaveOverview();
|
2020-07-11 16:19:27 +00:00
|
|
|
|
2021-01-13 22:24:49 +00:00
|
|
|
this._leavingOverview = true;
|
|
|
|
this._updateSwipeTracker();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-11-25 23:02:13 +00:00
|
|
|
|
2020-06-02 17:15:45 +00:00
|
|
|
vfunc_hide() {
|
2021-08-15 22:36:59 +00:00
|
|
|
Main.overview.disconnectObject(this);
|
|
|
|
global.stage.disconnectObject(this);
|
|
|
|
|
2011-11-25 17:25:31 +00:00
|
|
|
for (let i = 0; i < this._workspacesViews.length; i++)
|
|
|
|
this._workspacesViews[i].destroy();
|
2012-12-16 23:46:34 +00:00
|
|
|
this._workspacesViews = [];
|
2020-06-02 17:15:45 +00:00
|
|
|
|
2021-01-13 22:24:49 +00:00
|
|
|
this._leavingOverview = false;
|
|
|
|
|
2020-06-02 17:15:45 +00:00
|
|
|
super.vfunc_hide();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-11-25 17:25:31 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_updateWorkspacesViews() {
|
2012-12-16 23:46:34 +00:00
|
|
|
for (let i = 0; i < this._workspacesViews.length; i++)
|
|
|
|
this._workspacesViews[i].destroy();
|
2011-11-25 17:25:31 +00:00
|
|
|
|
2015-02-04 14:35:34 +00:00
|
|
|
this._primaryIndex = Main.layoutManager.primaryIndex;
|
2011-11-25 17:25:31 +00:00
|
|
|
this._workspacesViews = [];
|
|
|
|
let monitors = Main.layoutManager.monitors;
|
|
|
|
for (let i = 0; i < monitors.length; i++) {
|
2013-09-11 16:12:42 +00:00
|
|
|
let view;
|
2021-02-19 21:03:07 +00:00
|
|
|
if (i === this._primaryIndex) {
|
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 19:34:59 +00:00
|
|
|
view = new WorkspacesView(i,
|
|
|
|
this._controls,
|
|
|
|
this._scrollAdjustment,
|
|
|
|
this._fitModeAdjustment,
|
|
|
|
this._overviewAdjustment);
|
2020-06-05 05:23:53 +00:00
|
|
|
|
2020-06-26 16:38:28 +00:00
|
|
|
view.visible = this._primaryVisible;
|
2021-01-14 23:23:15 +00:00
|
|
|
this.bind_property('opacity', view, 'opacity', GObject.BindingFlags.SYNC_CREATE);
|
2020-06-26 16:38:28 +00:00
|
|
|
this.add_child(view);
|
|
|
|
} else {
|
2021-02-19 21:03:07 +00:00
|
|
|
view = new SecondaryMonitorDisplay(i,
|
|
|
|
this._controls,
|
|
|
|
this._scrollAdjustment,
|
|
|
|
this._fitModeAdjustment,
|
|
|
|
this._overviewAdjustment);
|
2020-06-26 16:38:28 +00:00
|
|
|
Main.layoutManager.overviewGroup.add_actor(view);
|
|
|
|
}
|
2021-02-19 21:03:07 +00:00
|
|
|
|
|
|
|
this._workspacesViews.push(view);
|
2020-06-26 16:38:28 +00:00
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-11-25 17:25:31 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_getMonitorIndexForEvent(event) {
|
2016-06-24 16:15:48 +00:00
|
|
|
let [x, y] = event.get_coords();
|
2023-08-08 18:07:03 +00:00
|
|
|
const rect = new Mtk.Rectangle({x, y, width: 1, height: 1});
|
2018-01-03 07:55:38 +00:00
|
|
|
return global.display.get_monitor_index_for_rect(rect);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-24 16:15:48 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_getPrimaryView() {
|
2012-12-16 23:46:34 +00:00
|
|
|
if (!this._workspacesViews.length)
|
2011-11-25 17:25:31 +00:00
|
|
|
return null;
|
2013-09-11 16:12:42 +00:00
|
|
|
return this._workspacesViews[this._primaryIndex];
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2010-02-14 23:32:57 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
activeWorkspaceHasMaximizedWindows() {
|
2021-01-04 16:42:26 +00:00
|
|
|
const primaryView = this._getPrimaryView();
|
|
|
|
return primaryView
|
|
|
|
? primaryView.getActiveWorkspace().hasMaximizedWindows()
|
|
|
|
: false;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-11-25 23:02:13 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onRestacked(overview, stackIndices) {
|
2011-11-25 17:25:31 +00:00
|
|
|
for (let i = 0; i < this._workspacesViews.length; i++)
|
|
|
|
this._workspacesViews[i].syncStacking(stackIndices);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-01-31 01:44:05 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onScrollEvent(actor, event) {
|
2019-07-08 08:47:04 +00:00
|
|
|
if (this._swipeTracker.canHandleScrollEvent(event))
|
|
|
|
return Clutter.EVENT_PROPAGATE;
|
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
if (!this.mapped)
|
2013-11-29 18:17:34 +00:00
|
|
|
return Clutter.EVENT_PROPAGATE;
|
2016-05-26 16:51:30 +00:00
|
|
|
|
|
|
|
if (this._workspacesOnlyOnPrimary &&
|
2023-08-07 00:51:19 +00:00
|
|
|
this._getMonitorIndexForEvent(event) !== this._primaryIndex)
|
2016-05-26 16:51:30 +00:00
|
|
|
return Clutter.EVENT_PROPAGATE;
|
|
|
|
|
2021-01-29 19:22:52 +00:00
|
|
|
return Main.wm.handleWorkspaceScroll(event);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2015-03-26 15:17:35 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onKeyPressEvent(actor, event) {
|
2023-08-06 22:40:20 +00:00
|
|
|
const {ControlsState} = OverviewControls;
|
2021-04-08 15:31:54 +00:00
|
|
|
if (this._overviewAdjustment.value !== ControlsState.WINDOW_PICKER)
|
2015-03-26 15:17:35 +00:00
|
|
|
return Clutter.EVENT_PROPAGATE;
|
2021-04-08 15:19:37 +00:00
|
|
|
|
2022-02-20 15:53:43 +00:00
|
|
|
if (!this.reactive)
|
|
|
|
return Clutter.EVENT_PROPAGATE;
|
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
const {workspaceManager} = global;
|
2021-04-08 15:19:37 +00:00
|
|
|
const vertical = workspaceManager.layout_rows === -1;
|
|
|
|
const rtl = this.get_text_direction() === Clutter.TextDirection.RTL;
|
|
|
|
|
2022-02-21 14:36:50 +00:00
|
|
|
let which;
|
2015-03-26 15:17:35 +00:00
|
|
|
switch (event.get_key_symbol()) {
|
|
|
|
case Clutter.KEY_Page_Up:
|
2021-04-08 15:19:37 +00:00
|
|
|
if (vertical)
|
2022-02-21 14:36:50 +00:00
|
|
|
which = Meta.MotionDirection.UP;
|
2021-04-08 15:19:37 +00:00
|
|
|
else if (rtl)
|
2022-02-21 14:36:50 +00:00
|
|
|
which = Meta.MotionDirection.RIGHT;
|
2021-04-08 15:19:37 +00:00
|
|
|
else
|
2022-02-21 14:36:50 +00:00
|
|
|
which = Meta.MotionDirection.LEFT;
|
2015-03-26 15:17:35 +00:00
|
|
|
break;
|
|
|
|
case Clutter.KEY_Page_Down:
|
2021-04-08 15:19:37 +00:00
|
|
|
if (vertical)
|
2022-02-21 14:36:50 +00:00
|
|
|
which = Meta.MotionDirection.DOWN;
|
2021-04-08 15:19:37 +00:00
|
|
|
else if (rtl)
|
2022-02-21 14:36:50 +00:00
|
|
|
which = Meta.MotionDirection.LEFT;
|
2021-04-08 15:19:37 +00:00
|
|
|
else
|
2022-02-21 14:36:50 +00:00
|
|
|
which = Meta.MotionDirection.RIGHT;
|
|
|
|
break;
|
|
|
|
case Clutter.KEY_Home:
|
|
|
|
which = 0;
|
|
|
|
break;
|
|
|
|
case Clutter.KEY_End:
|
|
|
|
which = workspaceManager.n_workspaces - 1;
|
2015-03-26 15:17:35 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return Clutter.EVENT_PROPAGATE;
|
|
|
|
}
|
2021-04-08 15:19:37 +00:00
|
|
|
|
2022-02-21 14:36:50 +00:00
|
|
|
let ws;
|
|
|
|
if (which < 0)
|
|
|
|
// Negative workspace numbers are directions
|
|
|
|
// with respect to the current workspace
|
|
|
|
ws = workspaceManager.get_active_workspace().get_neighbor(which);
|
|
|
|
else
|
|
|
|
// Otherwise it is a workspace index
|
|
|
|
ws = workspaceManager.get_workspace_by_index(which);
|
|
|
|
|
2021-04-08 15:19:37 +00:00
|
|
|
if (ws)
|
|
|
|
Main.wm.actionMoveWorkspace(ws);
|
2022-02-21 14:36:50 +00:00
|
|
|
|
2015-03-26 15:17:35 +00:00
|
|
|
return Clutter.EVENT_STOP;
|
2010-02-14 23:32:57 +00:00
|
|
|
}
|
2020-12-29 21:51:40 +00:00
|
|
|
|
2021-02-19 21:03:07 +00:00
|
|
|
get _workspacesOnlyOnPrimary() {
|
|
|
|
return this._settings.get_boolean('workspaces-only-on-primary');
|
|
|
|
}
|
|
|
|
|
2020-12-29 21:51:40 +00:00
|
|
|
get fitModeAdjustment() {
|
|
|
|
return this._fitModeAdjustment;
|
|
|
|
}
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|