workspaceAnimation: Support multiple screens
Currently, there's one animation for the whole canvas. While it looks fine with just one screen, it causes windows to move between screens when switching workspaces. Instead, have a separate animation on each screen, and sync their progress so that at any given time the progress "fraction" is the same between all screens. Clip all animations to their screens so that the windows don't leak to other screens. If a window is placed between every screen, can end up in multiple animations, in that case each part is still animated separately. Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1213 https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1326
This commit is contained in:
parent
cfd792fe86
commit
c3aa25e4e3
@ -12,10 +12,11 @@ const WINDOW_ANIMATION_TIME = 250;
|
|||||||
|
|
||||||
const WorkspaceGroup = GObject.registerClass(
|
const WorkspaceGroup = GObject.registerClass(
|
||||||
class WorkspaceGroup extends Clutter.Actor {
|
class WorkspaceGroup extends Clutter.Actor {
|
||||||
_init(workspace, movingWindow) {
|
_init(workspace, monitor, movingWindow) {
|
||||||
super._init();
|
super._init();
|
||||||
|
|
||||||
this._workspace = workspace;
|
this._workspace = workspace;
|
||||||
|
this._monitor = monitor;
|
||||||
this._movingWindow = movingWindow;
|
this._movingWindow = movingWindow;
|
||||||
this._windowRecords = [];
|
this._windowRecords = [];
|
||||||
|
|
||||||
@ -34,6 +35,11 @@ class WorkspaceGroup extends Clutter.Actor {
|
|||||||
if (!window.showing_on_its_workspace())
|
if (!window.showing_on_its_workspace())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
const geometry = global.display.get_monitor_geometry(this._monitor.index);
|
||||||
|
const [intersects, intersection_] = window.get_frame_rect().intersect(geometry);
|
||||||
|
if (!intersects)
|
||||||
|
return false;
|
||||||
|
|
||||||
const isSticky =
|
const isSticky =
|
||||||
window.is_on_all_workspaces() || window === this._movingWindow;
|
window.is_on_all_workspaces() || window === this._movingWindow;
|
||||||
|
|
||||||
@ -66,8 +72,8 @@ class WorkspaceGroup extends Clutter.Actor {
|
|||||||
for (const windowActor of windowActors) {
|
for (const windowActor of windowActors) {
|
||||||
const clone = new Clutter.Clone({
|
const clone = new Clutter.Clone({
|
||||||
source: windowActor,
|
source: windowActor,
|
||||||
x: windowActor.x,
|
x: windowActor.x - this._monitor.x,
|
||||||
y: windowActor.y,
|
y: windowActor.y - this._monitor.y,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.add_child(clone);
|
this.add_child(clone);
|
||||||
@ -98,10 +104,20 @@ class WorkspaceGroup extends Clutter.Actor {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const MonitorGroup = GObject.registerClass(
|
const MonitorGroup = GObject.registerClass({
|
||||||
class MonitorGroup extends Clutter.Actor {
|
Properties: {
|
||||||
_init(monitor) {
|
'progress': GObject.ParamSpec.double(
|
||||||
super._init();
|
'progress', 'progress', 'progress',
|
||||||
|
GObject.ParamFlags.READWRITE,
|
||||||
|
-Infinity, Infinity, 0),
|
||||||
|
},
|
||||||
|
}, class MonitorGroup extends Clutter.Actor {
|
||||||
|
_init(monitor, workspaceIndices, movingWindow) {
|
||||||
|
super._init({
|
||||||
|
clip_to_allocation: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._monitor = monitor;
|
||||||
|
|
||||||
const constraint = new Layout.MonitorConstraint({ index: monitor.index });
|
const constraint = new Layout.MonitorConstraint({ index: monitor.index });
|
||||||
this.add_constraint(constraint);
|
this.add_constraint(constraint);
|
||||||
@ -110,6 +126,48 @@ class MonitorGroup extends Clutter.Actor {
|
|||||||
|
|
||||||
this.add_child(background);
|
this.add_child(background);
|
||||||
|
|
||||||
|
this._container = new Clutter.Actor();
|
||||||
|
this.add_child(this._container);
|
||||||
|
|
||||||
|
const stickyGroup = new WorkspaceGroup(null, monitor, movingWindow);
|
||||||
|
this.add_child(stickyGroup);
|
||||||
|
|
||||||
|
this._workspaceGroups = [];
|
||||||
|
|
||||||
|
const workspaceManager = global.workspace_manager;
|
||||||
|
const vertical = workspaceManager.layout_rows === -1;
|
||||||
|
const activeWorkspace = workspaceManager.get_active_workspace();
|
||||||
|
|
||||||
|
let x = 0;
|
||||||
|
let y = 0;
|
||||||
|
|
||||||
|
for (const i of workspaceIndices) {
|
||||||
|
const ws = workspaceManager.get_workspace_by_index(i);
|
||||||
|
const fullscreen = ws.list_windows().some(w => w.get_monitor() === monitor.index && w.is_fullscreen());
|
||||||
|
|
||||||
|
if (i > 0 && vertical && !fullscreen && monitor.index === Main.layoutManager.primaryIndex) {
|
||||||
|
// We have to shift windows up or down by the height of the panel to prevent having a
|
||||||
|
// visible gap between the windows while switching workspaces. Since fullscreen windows
|
||||||
|
// hide the panel, they don't need to be shifted up or down.
|
||||||
|
y -= Main.panel.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
const group = new WorkspaceGroup(ws, monitor, movingWindow);
|
||||||
|
|
||||||
|
this._workspaceGroups.push(group);
|
||||||
|
this._container.add_child(group);
|
||||||
|
group.set_position(x, y);
|
||||||
|
|
||||||
|
if (vertical)
|
||||||
|
y += this.baseDistance;
|
||||||
|
else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
|
||||||
|
x -= this.baseDistance;
|
||||||
|
else
|
||||||
|
x += this.baseDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.progress = this.getWorkspaceProgress(activeWorkspace);
|
||||||
|
|
||||||
this._bgManager = new Background.BackgroundManager({
|
this._bgManager = new Background.BackgroundManager({
|
||||||
container: background,
|
container: background,
|
||||||
monitorIndex: monitor.index,
|
monitorIndex: monitor.index,
|
||||||
@ -122,6 +180,84 @@ class MonitorGroup extends Clutter.Actor {
|
|||||||
_onDestroy() {
|
_onDestroy() {
|
||||||
this._bgManager.destroy();
|
this._bgManager.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get baseDistance() {
|
||||||
|
if (global.workspace_manager.layout_rows === -1)
|
||||||
|
return this._monitor.height;
|
||||||
|
else
|
||||||
|
return this._monitor.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
get progress() {
|
||||||
|
if (global.workspace_manager.layout_rows === -1)
|
||||||
|
return -this._container.y / this.baseDistance;
|
||||||
|
else if (this.get_text_direction() === Clutter.TextDirection.RTL)
|
||||||
|
return this._container.x / this.baseDistance;
|
||||||
|
else
|
||||||
|
return -this._container.x / this.baseDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
set progress(p) {
|
||||||
|
if (global.workspace_manager.layout_rows === -1)
|
||||||
|
this._container.y = -Math.round(p * this.baseDistance);
|
||||||
|
else if (this.get_text_direction() === Clutter.TextDirection.RTL)
|
||||||
|
this._container.x = Math.round(p * this.baseDistance);
|
||||||
|
else
|
||||||
|
this._container.x = -Math.round(p * this.baseDistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
get index() {
|
||||||
|
return this._monitor.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
getWorkspaceProgress(workspace) {
|
||||||
|
const group = this._workspaceGroups.find(g =>
|
||||||
|
g.workspace.index() === workspace.index());
|
||||||
|
return this._getWorkspaceGroupProgress(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getWorkspaceGroupProgress(group) {
|
||||||
|
if (global.workspace_manager.layout_rows === -1)
|
||||||
|
return group.y / this.baseDistance;
|
||||||
|
else if (this.get_text_direction() === Clutter.TextDirection.RTL)
|
||||||
|
return -group.x / this.baseDistance;
|
||||||
|
else
|
||||||
|
return group.x / this.baseDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSnapPoints() {
|
||||||
|
return this._workspaceGroups.map(g =>
|
||||||
|
this._getWorkspaceGroupProgress(g));
|
||||||
|
}
|
||||||
|
|
||||||
|
findClosestWorkspace(progress) {
|
||||||
|
const distances = this.getSnapPoints().map(p =>
|
||||||
|
Math.abs(p - progress));
|
||||||
|
const index = distances.indexOf(Math.min(...distances));
|
||||||
|
return this._workspaceGroups[index].workspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
_interpolateProgress(progress, monitorGroup) {
|
||||||
|
if (this.index === monitorGroup.index)
|
||||||
|
return progress;
|
||||||
|
|
||||||
|
const points1 = monitorGroup.getSnapPoints();
|
||||||
|
const points2 = this.getSnapPoints();
|
||||||
|
|
||||||
|
const upper = points1.indexOf(points1.find(p => p >= progress));
|
||||||
|
const lower = points1.indexOf(points1.slice().reverse().find(p => p <= progress));
|
||||||
|
|
||||||
|
if (points1[upper] === points1[lower])
|
||||||
|
return points2[upper];
|
||||||
|
|
||||||
|
const t = (progress - points1[lower]) / (points1[upper] - points1[lower]);
|
||||||
|
|
||||||
|
return points2[lower] + (points2[upper] - points2[lower]) * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSwipeForMonitor(progress, monitorGroup) {
|
||||||
|
this.progress = this._interpolateProgress(progress, monitorGroup);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var WorkspaceAnimationController = class {
|
var WorkspaceAnimationController = class {
|
||||||
@ -153,73 +289,39 @@ var WorkspaceAnimationController = class {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const workspaceManager = global.workspace_manager;
|
const workspaceManager = global.workspace_manager;
|
||||||
const vertical = workspaceManager.layout_rows === -1;
|
|
||||||
const nWorkspaces = workspaceManager.get_n_workspaces();
|
const nWorkspaces = workspaceManager.get_n_workspaces();
|
||||||
const activeWorkspaceIndex = workspaceManager.get_active_workspace_index();
|
|
||||||
|
|
||||||
const switchData = {};
|
const switchData = {};
|
||||||
|
|
||||||
this._switchData = switchData;
|
this._switchData = switchData;
|
||||||
switchData.stickyGroup = new WorkspaceGroup(null, this.movingWindow);
|
switchData.monitors = [];
|
||||||
switchData.workspaceGroups = [];
|
|
||||||
switchData.gestureActivated = false;
|
switchData.gestureActivated = false;
|
||||||
switchData.inProgress = false;
|
switchData.inProgress = false;
|
||||||
|
|
||||||
switchData.container = new Clutter.Actor();
|
|
||||||
switchData.backgroundGroup = new Clutter.Actor();
|
|
||||||
|
|
||||||
for (const monitor of Main.layoutManager.monitors)
|
|
||||||
switchData.backgroundGroup.add_child(new MonitorGroup(monitor));
|
|
||||||
|
|
||||||
Main.uiGroup.insert_child_above(switchData.backgroundGroup, global.window_group);
|
|
||||||
Main.uiGroup.insert_child_above(switchData.container, switchData.backgroundGroup);
|
|
||||||
Main.uiGroup.insert_child_above(switchData.stickyGroup, switchData.container);
|
|
||||||
|
|
||||||
let x = 0;
|
|
||||||
let y = 0;
|
|
||||||
|
|
||||||
if (!workspaceIndices)
|
if (!workspaceIndices)
|
||||||
workspaceIndices = [...Array(nWorkspaces).keys()];
|
workspaceIndices = [...Array(nWorkspaces).keys()];
|
||||||
|
|
||||||
for (const i of workspaceIndices) {
|
const monitors = Meta.prefs_get_workspaces_only_on_primary()
|
||||||
const ws = workspaceManager.get_workspace_by_index(i);
|
? [Main.layoutManager.primaryMonitor] : Main.layoutManager.monitors;
|
||||||
const fullscreen = ws.list_windows().some(w => w.is_fullscreen());
|
|
||||||
|
|
||||||
if (y > 0 && vertical && !fullscreen) {
|
for (const monitor of monitors) {
|
||||||
// We have to shift windows up or down by the height of the panel to prevent having a
|
if (Meta.prefs_get_workspaces_only_on_primary() &&
|
||||||
// visible gap between the windows while switching workspaces. Since fullscreen windows
|
monitor.index !== Main.layoutManager.primaryIndex)
|
||||||
// hide the panel, they don't need to be shifted up or down.
|
continue;
|
||||||
y -= Main.panel.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
const group = new WorkspaceGroup(ws, this.movingWindow);
|
const group = new MonitorGroup(monitor, workspaceIndices, this.movingWindow);
|
||||||
|
|
||||||
switchData.workspaceGroups.push(group);
|
Main.uiGroup.insert_child_above(group, global.window_group);
|
||||||
switchData.container.add_child(group);
|
|
||||||
switchData.container.set_child_above_sibling(group, null);
|
|
||||||
group.set_position(x, y);
|
|
||||||
|
|
||||||
if (vertical)
|
switchData.monitors.push(group);
|
||||||
y += global.screen_height;
|
|
||||||
else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
|
|
||||||
x -= global.screen_width;
|
|
||||||
else
|
|
||||||
x += global.screen_width;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeGroup = this._findWorkspaceGroupByIndex(activeWorkspaceIndex);
|
|
||||||
if (vertical)
|
|
||||||
switchData.container.y = -activeGroup.y;
|
|
||||||
else
|
|
||||||
switchData.container.x = -activeGroup.x;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_finishWorkspaceSwitch(switchData) {
|
_finishWorkspaceSwitch(switchData) {
|
||||||
this._switchData = null;
|
this._switchData = null;
|
||||||
|
|
||||||
switchData.backgroundGroup.destroy();
|
switchData.monitors.forEach(m => m.destroy());
|
||||||
switchData.container.destroy();
|
|
||||||
switchData.stickyGroup.destroy();
|
|
||||||
|
|
||||||
this.movingWindow = null;
|
this.movingWindow = null;
|
||||||
}
|
}
|
||||||
@ -253,45 +355,32 @@ var WorkspaceAnimationController = class {
|
|||||||
this._prepareWorkspaceSwitch(workspaceIndices);
|
this._prepareWorkspaceSwitch(workspaceIndices);
|
||||||
this._switchData.inProgress = true;
|
this._switchData.inProgress = true;
|
||||||
|
|
||||||
const fromGroup = this._findWorkspaceGroupByIndex(from);
|
const fromWs = global.workspace_manager.get_workspace_by_index(from);
|
||||||
const toGroup = this._findWorkspaceGroupByIndex(to);
|
const toWs = global.workspace_manager.get_workspace_by_index(to);
|
||||||
|
|
||||||
this._switchData.container.x = -fromGroup.x;
|
for (const monitorGroup of this._switchData.monitors) {
|
||||||
this._switchData.container.y = -fromGroup.y;
|
monitorGroup.progress = monitorGroup.getWorkspaceProgress(fromWs);
|
||||||
|
const progress = monitorGroup.getWorkspaceProgress(toWs);
|
||||||
|
|
||||||
this._switchData.container.ease({
|
const params = {
|
||||||
x: -toGroup.x,
|
duration: WINDOW_ANIMATION_TIME,
|
||||||
y: -toGroup.y,
|
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
||||||
duration: WINDOW_ANIMATION_TIME,
|
};
|
||||||
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
|
||||||
onComplete: () => {
|
if (monitorGroup.index === Main.layoutManager.primaryIndex) {
|
||||||
this._finishWorkspaceSwitch(this._switchData);
|
params.onComplete = () => {
|
||||||
onComplete();
|
this._finishWorkspaceSwitch(this._switchData);
|
||||||
this._swipeTracker.enabled = true;
|
onComplete();
|
||||||
},
|
this._swipeTracker.enabled = true;
|
||||||
});
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
monitorGroup.ease_property('progress', progress, params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getProgressForWorkspace(workspaceGroup) {
|
_findMonitorGroup(monitorIndex) {
|
||||||
if (global.workspace_manager.layout_rows === -1)
|
return this._switchData.monitors.find(m => m.index === monitorIndex);
|
||||||
return workspaceGroup.y / global.screen_height;
|
|
||||||
else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
|
|
||||||
return -workspaceGroup.x / global.screen_width;
|
|
||||||
else
|
|
||||||
return workspaceGroup.x / global.screen_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
_findWorkspaceGroupByIndex(index) {
|
|
||||||
return this._switchData.workspaceGroups.find(g => g.workspace.index() === index);
|
|
||||||
}
|
|
||||||
|
|
||||||
_findClosestWorkspaceGroup(progress) {
|
|
||||||
const distances = this._switchData.workspaceGroups.map(g => {
|
|
||||||
const workspaceProgress = this._getProgressForWorkspace(g);
|
|
||||||
return Math.abs(workspaceProgress - progress);
|
|
||||||
});
|
|
||||||
const index = distances.indexOf(Math.min(...distances));
|
|
||||||
return this._switchData.workspaceGroups[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_switchWorkspaceBegin(tracker, monitor) {
|
_switchWorkspaceBegin(tracker, monitor) {
|
||||||
@ -305,31 +394,22 @@ var WorkspaceAnimationController = class {
|
|||||||
? Clutter.Orientation.HORIZONTAL
|
? Clutter.Orientation.HORIZONTAL
|
||||||
: Clutter.Orientation.VERTICAL;
|
: Clutter.Orientation.VERTICAL;
|
||||||
|
|
||||||
const baseDistance = horiz ? global.screen_width : global.screen_height;
|
|
||||||
|
|
||||||
let progress;
|
|
||||||
let cancelProgress;
|
|
||||||
if (this._switchData && this._switchData.gestureActivated) {
|
if (this._switchData && this._switchData.gestureActivated) {
|
||||||
this._switchData.container.remove_all_transitions();
|
for (const group of this._switchData.monitors)
|
||||||
if (!horiz)
|
group.remove_all_transitions();
|
||||||
progress = -this._switchData.container.y / baseDistance;
|
|
||||||
else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
|
|
||||||
progress = this._switchData.container.x / baseDistance;
|
|
||||||
else
|
|
||||||
progress = -this._switchData.container.x / baseDistance;
|
|
||||||
|
|
||||||
const wsGroup = this._findClosestWorkspaceGroup(progress);
|
|
||||||
cancelProgress = this._getProgressForWorkspaceGroup(wsGroup);
|
|
||||||
} else {
|
} else {
|
||||||
this._prepareWorkspaceSwitch();
|
this._prepareWorkspaceSwitch();
|
||||||
|
|
||||||
const activeIndex = workspaceManager.get_active_workspace_index();
|
|
||||||
const wsGroup = this._findWorkspaceGroupByIndex(activeIndex);
|
|
||||||
|
|
||||||
progress = cancelProgress = this._getProgressForWorkspace(wsGroup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const points = this._switchData.workspaceGroups.map(this._getProgressForWorkspace);
|
const monitorGroup = this._findMonitorGroup(monitor);
|
||||||
|
const baseDistance = monitorGroup.baseDistance;
|
||||||
|
const progress = monitorGroup.progress;
|
||||||
|
|
||||||
|
const closestWs = monitorGroup.findClosestWorkspace(progress);
|
||||||
|
const cancelProgress = monitorGroup.getWorkspaceProgress(closestWs);
|
||||||
|
const points = monitorGroup.getSnapPoints();
|
||||||
|
|
||||||
|
this._switchData.baseMonitorGroup = monitorGroup;
|
||||||
|
|
||||||
tracker.confirmSwipe(baseDistance, points, progress, cancelProgress);
|
tracker.confirmSwipe(baseDistance, points, progress, cancelProgress);
|
||||||
}
|
}
|
||||||
@ -338,39 +418,37 @@ var WorkspaceAnimationController = class {
|
|||||||
if (!this._switchData)
|
if (!this._switchData)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let xPos = 0;
|
for (const monitorGroup of this._switchData.monitors)
|
||||||
let yPos = 0;
|
monitorGroup.updateSwipeForMonitor(progress, this._switchData.baseMonitorGroup);
|
||||||
|
|
||||||
if (global.workspace_manager.layout_rows === -1)
|
|
||||||
yPos = -Math.round(progress * global.screen_height);
|
|
||||||
else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
|
|
||||||
xPos = Math.round(progress * global.screen_width);
|
|
||||||
else
|
|
||||||
xPos = -Math.round(progress * global.screen_width);
|
|
||||||
|
|
||||||
this._switchData.container.set_position(xPos, yPos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_switchWorkspaceEnd(tracker, duration, endProgress) {
|
_switchWorkspaceEnd(tracker, duration, endProgress) {
|
||||||
if (!this._switchData)
|
if (!this._switchData)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const newGroup = this._findClosestWorkspaceGroup(endProgress);
|
|
||||||
|
|
||||||
const switchData = this._switchData;
|
const switchData = this._switchData;
|
||||||
switchData.gestureActivated = true;
|
switchData.gestureActivated = true;
|
||||||
|
|
||||||
this._switchData.container.ease({
|
const newWs = switchData.baseMonitorGroup.findClosestWorkspace(endProgress);
|
||||||
x: -newGroup.x,
|
|
||||||
y: -newGroup.y,
|
for (const monitorGroup of this._switchData.monitors) {
|
||||||
duration,
|
const progress = monitorGroup.getWorkspaceProgress(newWs);
|
||||||
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
|
||||||
onComplete: () => {
|
const params = {
|
||||||
if (!newGroup.workspace.active)
|
duration,
|
||||||
newGroup.workspace.activate(global.get_current_time());
|
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
||||||
this._finishWorkspaceSwitch(switchData);
|
};
|
||||||
},
|
|
||||||
});
|
if (monitorGroup.index === Main.layoutManager.primaryIndex) {
|
||||||
|
params.onComplete = () => {
|
||||||
|
if (!newWs.active)
|
||||||
|
newWs.activate(global.get_current_time());
|
||||||
|
this._finishWorkspaceSwitch(switchData);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
monitorGroup.ease_property('progress', progress, params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get gestureActive() {
|
get gestureActive() {
|
||||||
|
Loading…
Reference in New Issue
Block a user