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>
This commit is contained in:
Georges Basile Stavracas Neto 2021-02-02 16:34:59 -03:00 committed by Marge Bot
parent 9c6d8e2aad
commit bf1fa3879f
2 changed files with 89 additions and 18 deletions

View File

@ -42,7 +42,7 @@ class ControlsManagerLayout extends Clutter.BoxLayout {
stateAdjustment.connect('notify::value', () => this.layout_changed()); stateAdjustment.connect('notify::value', () => this.layout_changed());
} }
_getWorkspacesBoxForState(state, box, searchHeight, dashHeight, thumbnailsHeight) { _computeWorkspacesBoxForState(state, box, searchHeight, dashHeight, thumbnailsHeight) {
const workspaceBox = box.copy(); const workspaceBox = box.copy();
const [width, height] = workspaceBox.get_size(); const [width, height] = workspaceBox.get_size();
const { spacing } = this; const { spacing } = this;
@ -145,7 +145,7 @@ class ControlsManagerLayout extends Clutter.BoxLayout {
// Update cached boxes // Update cached boxes
for (const state of Object.values(ControlsState)) { for (const state of Object.values(ControlsState)) {
this._cachedWorkspaceBoxes.set( this._cachedWorkspaceBoxes.set(
state, this._getWorkspacesBoxForState(state, ...params)); state, this._computeWorkspacesBoxForState(state, ...params));
} }
let workspacesBox; let workspacesBox;
@ -184,6 +184,10 @@ class ControlsManagerLayout extends Clutter.BoxLayout {
this._viewSelector.allocate(childBox); this._viewSelector.allocate(childBox);
} }
getWorkspacesBoxForState(state) {
return this._cachedWorkspaceBoxes.get(state);
}
}); });
var OverviewAdjustment = GObject.registerClass( var OverviewAdjustment = GObject.registerClass(
@ -288,6 +292,7 @@ class ControlsManager extends St.Widget {
this._thumbnailsBox = this._thumbnailsBox =
new WorkspaceThumbnail.ThumbnailsBox(this._workspaceAdjustment); new WorkspaceThumbnail.ThumbnailsBox(this._workspaceAdjustment);
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay( this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay(
this,
this._workspaceAdjustment, this._workspaceAdjustment,
this._stateAdjustment); this._stateAdjustment);
this._appDisplay = new AppDisplay.AppDisplay(); this._appDisplay = new AppDisplay.AppDisplay();
@ -564,6 +569,10 @@ class ControlsManager extends St.Widget {
}); });
} }
getWorkspacesBoxForState(state) {
return this.layoutManager.getWorkspacesBoxForState(state);
}
get searchEntry() { get searchEntry() {
return this._searchEntry; return this._searchEntry;
} }

View File

@ -94,11 +94,12 @@ var FitMode = {
var WorkspacesView = GObject.registerClass( var WorkspacesView = GObject.registerClass(
class WorkspacesView extends WorkspacesViewBase { class WorkspacesView extends WorkspacesViewBase {
_init(monitorIndex, scrollAdjustment, fitModeAdjustment, overviewAdjustment) { _init(monitorIndex, controls, scrollAdjustment, fitModeAdjustment, overviewAdjustment) {
let workspaceManager = global.workspace_manager; let workspaceManager = global.workspace_manager;
super._init(monitorIndex, overviewAdjustment); super._init(monitorIndex, overviewAdjustment);
this._controls = controls;
this._fitModeAdjustment = fitModeAdjustment; this._fitModeAdjustment = fitModeAdjustment;
this._fitModeNotifyId = this._fitModeAdjustment.connect('notify::value', () => { this._fitModeNotifyId = this._fitModeAdjustment.connect('notify::value', () => {
this._updateVisibility(); this._updateVisibility();
@ -133,15 +134,14 @@ class WorkspacesView extends WorkspacesViewBase {
this._updateVisibility(); this._updateVisibility();
} }
_getFitAllBox(box, spacing, vertical) { _getFirstFitAllWorkspaceBox(box, spacing, vertical) {
const { nWorkspaces } = global.workspaceManager; const { nWorkspaces } = global.workspaceManager;
const [width, height] = box.get_size(); const [width, height] = box.get_size();
const [workspace] = this._workspaces; const [workspace] = this._workspaces;
const fitAllBox = new Clutter.ActorBox(); const fitAllBox = new Clutter.ActorBox();
let x1 = 0; let [x1, y1] = box.get_origin();
let y1 = 0;
// Spacing here is not only the space between workspaces, but also the // Spacing here is not only the space between workspaces, but also the
// space before the first workspace, and after the last one. This prevents // space before the first workspace, and after the last one. This prevents
@ -179,7 +179,7 @@ class WorkspacesView extends WorkspacesViewBase {
return fitAllBox; return fitAllBox;
} }
_getFitSingleBox(box, spacing, vertical) { _getFirstFitSingleWorkspaceBox(box, spacing, vertical) {
const [width, height] = box.get_size(); const [width, height] = box.get_size();
const [workspace] = this._workspaces; const [workspace] = this._workspaces;
@ -189,8 +189,7 @@ class WorkspacesView extends WorkspacesViewBase {
? adj.value : adj.upper - adj.value - 1; ? adj.value : adj.upper - adj.value - 1;
// Single fit mode implies centered too // Single fit mode implies centered too
let x1 = 0; let [x1, y1] = box.get_origin();
let y1 = 0;
if (vertical) { if (vertical) {
const [, workspaceHeight] = workspace.get_preferred_height(width); const [, workspaceHeight] = workspace.get_preferred_height(width);
y1 += (height - workspaceHeight) / 2; y1 += (height - workspaceHeight) / 2;
@ -273,6 +272,64 @@ class WorkspacesView extends WorkspacesViewBase {
}); });
} }
_getFitModeForState(state) {
const { ControlsState } = OverviewControls;
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;
const { transitioning, initialState, finalState } =
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];
}
_updateWorkspaceMode() { _updateWorkspaceMode() {
this._updateWorkspacesState(); this._updateWorkspacesState();
} }
@ -288,15 +345,16 @@ class WorkspacesView extends WorkspacesViewBase {
const fitMode = this._fitModeAdjustment.value; const fitMode = this._fitModeAdjustment.value;
let [fitSingleBox, fitAllBox] = this._getInitialBoxes(box);
const fitSingleSpacing = const fitSingleSpacing =
this._getSpacing(box, FitMode.SINGLE, vertical); this._getSpacing(fitSingleBox, FitMode.SINGLE, vertical);
const fitSingleBox = fitSingleBox =
this._getFitSingleBox(box, fitSingleSpacing, vertical); this._getFirstFitSingleWorkspaceBox(fitSingleBox, fitSingleSpacing, vertical);
const fitAllSpacing = const fitAllSpacing =
this._getSpacing(box, FitMode.ALL, vertical); this._getSpacing(fitAllBox, FitMode.ALL, vertical);
const fitAllBox = fitAllBox =
this._getFitAllBox(box, fitAllSpacing, vertical); this._getFirstFitAllWorkspaceBox(fitAllBox, fitAllSpacing, vertical);
// Account for RTL locales by reversing the list // Account for RTL locales by reversing the list
const workspaces = this._workspaces.slice(); const workspaces = this._workspaces.slice();
@ -517,12 +575,13 @@ class ExtraWorkspaceView extends WorkspacesViewBase {
var WorkspacesDisplay = GObject.registerClass( var WorkspacesDisplay = GObject.registerClass(
class WorkspacesDisplay extends St.Widget { class WorkspacesDisplay extends St.Widget {
_init(scrollAdjustment, overviewAdjustment) { _init(controls, scrollAdjustment, overviewAdjustment) {
super._init({ super._init({
clip_to_allocation: true, clip_to_allocation: true,
layout_manager: new Clutter.BinLayout(), layout_manager: new Clutter.BinLayout(),
}); });
this._controls = controls;
this._overviewAdjustment = overviewAdjustment; this._overviewAdjustment = overviewAdjustment;
this._fitModeAdjustment = new St.Adjustment({ this._fitModeAdjustment = new St.Adjustment({
actor: this, actor: this,
@ -775,8 +834,11 @@ class WorkspacesDisplay extends St.Widget {
if (this._workspacesOnlyOnPrimary && i !== this._primaryIndex) { if (this._workspacesOnlyOnPrimary && i !== this._primaryIndex) {
view = new ExtraWorkspaceView(i, this._overviewAdjustment); view = new ExtraWorkspaceView(i, this._overviewAdjustment);
} else { } else {
view = new WorkspacesView(i, this._scrollAdjustment, view = new WorkspacesView(i,
this._fitModeAdjustment, this._overviewAdjustment); this._controls,
this._scrollAdjustment,
this._fitModeAdjustment,
this._overviewAdjustment);
} }
this._workspacesViews.push(view); this._workspacesViews.push(view);