workspacesView: Use side-by-side layout
Currently, WorkspacesView positions each workspace on a per-page layout, each page with the allocated width and height of WorkspaceView. This layout doesn't work well with horizontal workspaces. Layout workspaces side by side, instead of per page. The layout is influenced by a "fit mode", which reflects the different behaviors exposed in the mockup. This fit mode represents whether a single or all workspaces will fit available geometry. The single fit mode is always used for now. Next commits will make it switch to the all fit mode when in the app grid state. The translation_{x,y} also needed to reflect the switch to a side-by-side layout, and use the geometry of the workspaces to determine the offset. Notice that, when the fit mode is ALL, there's no translation applied. Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1613>
This commit is contained in:
parent
3abfc25858
commit
26c5434222
@ -5,6 +5,7 @@ const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
|
|||||||
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const SwipeTracker = imports.ui.swipeTracker;
|
const SwipeTracker = imports.ui.swipeTracker;
|
||||||
|
const Util = imports.misc.util;
|
||||||
const Workspace = imports.ui.workspace;
|
const Workspace = imports.ui.workspace;
|
||||||
|
|
||||||
var { ANIMATION_TIME } = imports.ui.overview;
|
var { ANIMATION_TIME } = imports.ui.overview;
|
||||||
@ -13,6 +14,9 @@ var SCROLL_TIMEOUT_TIME = 150;
|
|||||||
|
|
||||||
const MUTTER_SCHEMA = 'org.gnome.mutter';
|
const MUTTER_SCHEMA = 'org.gnome.mutter';
|
||||||
|
|
||||||
|
const WORKSPACE_MIN_SPACING = 24;
|
||||||
|
const WORKSPACE_MAX_SPACING = 80;
|
||||||
|
|
||||||
var WorkspacesViewBase = GObject.registerClass({
|
var WorkspacesViewBase = GObject.registerClass({
|
||||||
GTypeFlags: GObject.TypeFlags.ABSTRACT,
|
GTypeFlags: GObject.TypeFlags.ABSTRACT,
|
||||||
}, class WorkspacesViewBase extends St.Widget {
|
}, class WorkspacesViewBase extends St.Widget {
|
||||||
@ -63,14 +67,25 @@ var WorkspacesViewBase = GObject.registerClass({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var FitMode = {
|
||||||
|
SINGLE: 0,
|
||||||
|
ALL: 1,
|
||||||
|
};
|
||||||
|
|
||||||
var WorkspacesView = GObject.registerClass(
|
var WorkspacesView = GObject.registerClass(
|
||||||
class WorkspacesView extends WorkspacesViewBase {
|
class WorkspacesView extends WorkspacesViewBase {
|
||||||
_init(monitorIndex, scrollAdjustment) {
|
_init(monitorIndex, scrollAdjustment, fitModeAdjustment) {
|
||||||
let workspaceManager = global.workspace_manager;
|
let workspaceManager = global.workspace_manager;
|
||||||
|
|
||||||
super._init(monitorIndex);
|
super._init(monitorIndex);
|
||||||
this.clip_to_allocation = true;
|
this.clip_to_allocation = true;
|
||||||
|
|
||||||
|
this._fitModeAdjustment = fitModeAdjustment;
|
||||||
|
this._fitModeNotifyId = this._fitModeAdjustment.connect('notify::value', () => {
|
||||||
|
this._updateVisibility();
|
||||||
|
this.queue_relayout();
|
||||||
|
});
|
||||||
|
|
||||||
this._animating = false; // tweening
|
this._animating = false; // tweening
|
||||||
this._gestureActive = false; // touch(pad) gestures
|
this._gestureActive = false; // touch(pad) gestures
|
||||||
|
|
||||||
@ -97,29 +112,150 @@ class WorkspacesView extends WorkspacesViewBase {
|
|||||||
this._activeWorkspaceChanged.bind(this));
|
this._activeWorkspaceChanged.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getFitAllBox(box, spacing, vertical) {
|
||||||
|
const { nWorkspaces } = global.workspaceManager;
|
||||||
|
const [width, height] = box.get_size();
|
||||||
|
const [workspace] = this._workspaces;
|
||||||
|
|
||||||
|
const fitAllBox = new Clutter.ActorBox();
|
||||||
|
|
||||||
|
let x1 = 0;
|
||||||
|
let y1 = 0;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getFitSingleBox(box, spacing, vertical) {
|
||||||
|
const [width, height] = box.get_size();
|
||||||
|
const [workspace] = this._workspaces;
|
||||||
|
|
||||||
|
// Single fit mode implies centered too
|
||||||
|
let x1 = 0;
|
||||||
|
let y1 = 0;
|
||||||
|
if (vertical) {
|
||||||
|
const [, workspaceHeight] = workspace.get_preferred_height(width);
|
||||||
|
y1 += (height - workspaceHeight) / 2;
|
||||||
|
} else {
|
||||||
|
const [, workspaceWidth] = workspace.get_preferred_width(height);
|
||||||
|
x1 += (width - workspaceWidth) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fitSingleBox = new Clutter.ActorBox({ x1, y1 });
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const spacing = (availableSpace - workspaceSize * 0.05) * (1 - fitMode);
|
||||||
|
|
||||||
|
return Math.clamp(spacing, WORKSPACE_MIN_SPACING, WORKSPACE_MAX_SPACING);
|
||||||
|
}
|
||||||
|
|
||||||
vfunc_allocate(box) {
|
vfunc_allocate(box) {
|
||||||
this.set_allocation(box);
|
this.set_allocation(box);
|
||||||
|
|
||||||
if (this.get_n_children() === 0)
|
if (this.get_n_children() === 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const { workspaceManager } = global;
|
const vertical = global.workspaceManager.layout_rows === -1;
|
||||||
const { nWorkspaces } = workspaceManager;
|
|
||||||
|
|
||||||
const vertical = workspaceManager.layout_rows === -1;
|
|
||||||
const rtl = this.text_direction === Clutter.TextDirection.RTL;
|
const rtl = this.text_direction === Clutter.TextDirection.RTL;
|
||||||
|
|
||||||
const [width, height] = box.get_size();
|
const fitMode = this._fitModeAdjustment.value;
|
||||||
const childBox = box.copy();
|
|
||||||
|
|
||||||
this._workspaces.forEach((child, index) => {
|
const fitSingleSpacing =
|
||||||
if (rtl && !vertical)
|
this._getSpacing(box, FitMode.SINGLE, vertical);
|
||||||
index = nWorkspaces - index - 1;
|
const fitSingleBox =
|
||||||
|
this._getFitSingleBox(box, fitSingleSpacing, vertical);
|
||||||
|
|
||||||
childBox.set_origin(
|
const fitAllSpacing =
|
||||||
vertical ? 0 : index * width,
|
this._getSpacing(box, FitMode.ALL, vertical);
|
||||||
vertical ? index * height : 0);
|
const fitAllBox =
|
||||||
child.allocate_align_fill(childBox, 0.5, 0.5, false, false);
|
this._getFitAllBox(box, fitAllSpacing, vertical);
|
||||||
|
|
||||||
|
// Account for RTL locales by reversing the list
|
||||||
|
const workspaces = this._workspaces.slice();
|
||||||
|
if (rtl)
|
||||||
|
workspaces.reverse();
|
||||||
|
|
||||||
|
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(
|
||||||
|
fitSingleBox.x1,
|
||||||
|
fitSingleBox.y1 + fitSingleBox.get_height() + fitSingleSpacing);
|
||||||
|
fitAllBox.set_origin(
|
||||||
|
fitAllBox.x1,
|
||||||
|
fitAllBox.y1 + fitAllBox.get_height() + fitAllSpacing);
|
||||||
|
} else {
|
||||||
|
fitSingleBox.set_origin(
|
||||||
|
fitSingleBox.x1 + fitSingleBox.get_width() + fitSingleSpacing,
|
||||||
|
fitSingleBox.y1);
|
||||||
|
fitAllBox.set_origin(
|
||||||
|
fitAllBox.x1 + fitAllBox.get_width() + fitAllSpacing,
|
||||||
|
fitAllBox.y1);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._updateScrollPosition();
|
this._updateScrollPosition();
|
||||||
@ -175,10 +311,8 @@ class WorkspacesView extends WorkspacesViewBase {
|
|||||||
|
|
||||||
if (this._animating || this._gestureActive)
|
if (this._animating || this._gestureActive)
|
||||||
workspace.show();
|
workspace.show();
|
||||||
else if (this._inDrag)
|
|
||||||
workspace.visible = Math.abs(w - active) <= 1;
|
|
||||||
else
|
else
|
||||||
workspace.visible = w == active;
|
workspace.visible = Math.abs(w - active) <= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,6 +357,7 @@ class WorkspacesView extends WorkspacesViewBase {
|
|||||||
super._onDestroy();
|
super._onDestroy();
|
||||||
|
|
||||||
this._scrollAdjustment.disconnect(this._onScrollId);
|
this._scrollAdjustment.disconnect(this._onScrollId);
|
||||||
|
this._fitModeAdjustment.disconnect(this._fitModeNotifyId);
|
||||||
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
||||||
let workspaceManager = global.workspace_manager;
|
let workspaceManager = global.workspace_manager;
|
||||||
workspaceManager.disconnect(this._updateWorkspacesId);
|
workspaceManager.disconnect(this._updateWorkspacesId);
|
||||||
@ -286,14 +421,28 @@ class WorkspacesView extends WorkspacesViewBase {
|
|||||||
const workspaceManager = global.workspace_manager;
|
const workspaceManager = global.workspace_manager;
|
||||||
const vertical = workspaceManager.layout_rows === -1;
|
const vertical = workspaceManager.layout_rows === -1;
|
||||||
const rtl = this.text_direction === Clutter.TextDirection.RTL;
|
const rtl = this.text_direction === Clutter.TextDirection.RTL;
|
||||||
const progress = vertical || !rtl
|
const fitMode = this._fitModeAdjustment.value;
|
||||||
|
let progress = vertical || !rtl
|
||||||
? adj.value : adj.upper - adj.value - 1;
|
? adj.value : adj.upper - adj.value - 1;
|
||||||
|
progress = Util.lerp(progress / (adj.upper - 1), 0, fitMode);
|
||||||
|
|
||||||
|
// Use workspaces geometry to determine the size to offset
|
||||||
|
const firstWorkspaceBox = rtl
|
||||||
|
? this._workspaces[this._workspaces.length - 1].allocation
|
||||||
|
: this._workspaces[0].allocation;
|
||||||
|
const lastWorkspaceBox = rtl
|
||||||
|
? this._workspaces[0].allocation
|
||||||
|
: this._workspaces[this._workspaces.length - 1].allocation;
|
||||||
|
const [workspaceWidth, workspaceHeight] = firstWorkspaceBox.get_size();
|
||||||
|
const size = vertical
|
||||||
|
? lastWorkspaceBox.y2 - firstWorkspaceBox.y1 - workspaceHeight
|
||||||
|
: lastWorkspaceBox.x2 - firstWorkspaceBox.x1 - workspaceWidth;
|
||||||
|
|
||||||
for (const ws of this._workspaces) {
|
for (const ws of this._workspaces) {
|
||||||
if (vertical)
|
if (vertical)
|
||||||
ws.translation_y = -progress * this.height;
|
ws.translation_y = -progress * size;
|
||||||
else
|
else
|
||||||
ws.translation_x = -progress * this.width;
|
ws.translation_x = -progress * size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -338,6 +487,13 @@ class WorkspacesDisplay extends St.Widget {
|
|||||||
});
|
});
|
||||||
this.connect('notify::allocation', this._updateWorkspacesActualGeometry.bind(this));
|
this.connect('notify::allocation', this._updateWorkspacesActualGeometry.bind(this));
|
||||||
|
|
||||||
|
this._fitModeAdjustment = new St.Adjustment({
|
||||||
|
actor: this,
|
||||||
|
value: FitMode.SINGLE,
|
||||||
|
lower: FitMode.SINGLE,
|
||||||
|
upper: FitMode.ALL,
|
||||||
|
});
|
||||||
|
|
||||||
Main.overview.connect('relayout',
|
Main.overview.connect('relayout',
|
||||||
() => this._updateWorkspacesActualGeometry());
|
() => this._updateWorkspacesActualGeometry());
|
||||||
|
|
||||||
@ -626,7 +782,7 @@ class WorkspacesDisplay extends St.Widget {
|
|||||||
if (this._workspacesOnlyOnPrimary && i != this._primaryIndex)
|
if (this._workspacesOnlyOnPrimary && i != this._primaryIndex)
|
||||||
view = new ExtraWorkspaceView(i);
|
view = new ExtraWorkspaceView(i);
|
||||||
else
|
else
|
||||||
view = new WorkspacesView(i, this._scrollAdjustment);
|
view = new WorkspacesView(i, this._scrollAdjustment, this._fitModeAdjustment);
|
||||||
|
|
||||||
this._workspacesViews.push(view);
|
this._workspacesViews.push(view);
|
||||||
Main.layoutManager.overviewGroup.add_actor(view);
|
Main.layoutManager.overviewGroup.add_actor(view);
|
||||||
|
Loading…
Reference in New Issue
Block a user