[gnome-shell/gbsneto/40-stuff: 59/68] workspacesView: Allow dragging between workspaces
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/gbsneto/40-stuff: 59/68] workspacesView: Allow dragging between workspaces
- Date: Wed, 20 Jan 2021 22:41:08 +0000 (UTC)
commit 01c960b3682116b7519e82933cf556702d67f16f
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Thu Oct 1 15:25:24 2020 -0300
workspacesView: Allow dragging between workspaces
Add a placeholder actor that creates and moves to the new
workspace, and either launch the app there, or move the
window to it.
data/theme/dash-placeholder.svg | 29 +++-
.../gnome-shell-sass/widgets/_window-picker.scss | 6 +
js/ui/workspacesView.js | 180 ++++++++++++++++++++-
3 files changed, 206 insertions(+), 9 deletions(-)
---
diff --git a/data/theme/dash-placeholder.svg b/data/theme/dash-placeholder.svg
index cbae148a28..d76c9c5887 100644
--- a/data/theme/dash-placeholder.svg
+++ b/data/theme/dash-placeholder.svg
@@ -1,14 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
- width="76"
- height="27"
+ width="27"
+ height="76"
id="svg11252"
version="1.1">
+ <metadata
+ id="metadata19">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
<defs
id="defs11254">
<radialGradient
@@ -55,13 +68,13 @@
</defs>
<g
id="layer1"
- transform="translate(-337,-518.86218)">
+ transform="rotate(-90,-52.931091,465.93109)">
<g
id="g99967"
style="display:inline"
transform="translate(326,44.862171)">
<rect
-
style="opacity:0.49375;color:#000000;fill:url(#radialGradient68155-2-3);fill-opacity:1;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.49375;fill:url(#radialGradient68155-2-3);fill-opacity:1;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
id="rect99969"
width="76"
height="2"
@@ -70,7 +83,7 @@
rx="0"
ry="0" />
<path
-
style="opacity:0.43125;color:#000000;fill:url(#radialGradient68157-0-8);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.43125;fill:url(#radialGradient68157-0-8);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
id="path99971"
d="M 61,487.5 C 61,493.29899 56.29899,498 50.5,498 44.70101,498 40,493.29899 40,487.5 40,481.70101
44.70101,477 50.5,477 c 5.79899,0 10.5,4.70101 10.5,10.5 z"
transform="matrix(1.2857143,0,0,1.2857143,-14.428572,-139.28571)" />
@@ -78,7 +91,7 @@
transform="matrix(0.43589747,0,0,0.43589747,28.487179,275)"
d="M 61,487.5 C 61,493.29899 56.29899,498 50.5,498 44.70101,498 40,493.29899 40,487.5 40,481.70101
44.70101,477 50.5,477 c 5.79899,0 10.5,4.70101 10.5,10.5 z"
id="path99973"
-
style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
/>
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
/>
</g>
</g>
</svg>
diff --git a/data/theme/gnome-shell-sass/widgets/_window-picker.scss
b/data/theme/gnome-shell-sass/widgets/_window-picker.scss
index 4cf3e12e2f..2d97348f6b 100644
--- a/data/theme/gnome-shell-sass/widgets/_window-picker.scss
+++ b/data/theme/gnome-shell-sass/widgets/_window-picker.scss
@@ -58,3 +58,9 @@ $window_clone_border_size: 6px;
background-color: darken($selected_bg_color, 5%);
}
}
+
+// drag and drop indicator
+.workspace-dnd-placeholder {
+ background-image: url("resource:///org/gnome/shell/theme/dash-placeholder.svg");
+ background-position: center;
+}
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index fddeaface8..21648e26cc 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -3,6 +3,7 @@
const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
+const DND = imports.ui.dnd;
const Main = imports.ui.main;
const SwipeTracker = imports.ui.swipeTracker;
const OverviewControls = imports.ui.overviewControls;
@@ -11,6 +12,7 @@ const Workspace = imports.ui.workspace;
var { ANIMATION_TIME } = imports.ui.overview;
var WORKSPACE_SWITCH_TIME = 250;
var SCROLL_TIMEOUT_TIME = 150;
+var WORKSPACE_KEEP_ALIVE_TIME = 100;
const MUTTER_SCHEMA = 'org.gnome.mutter';
@@ -89,6 +91,71 @@ var WorkspacesViewBase = GObject.registerClass({
}
});
+var WorkspaceDragPlaceholder = GObject.registerClass(
+class WorkspaceDragPlaceholder extends St.Widget {
+ _init(monitorIndex) {
+ super._init({
+ style_class: 'workspace-dnd-placeholder',
+ visible: false,
+ });
+
+ this._monitorIndex = monitorIndex;
+ this._delegate = this;
+ this._position = -1;
+ }
+
+ acceptDrop(source, actor, x, y, time) {
+ if (this._position === -1)
+ return false;
+
+ const newWorkspaceIndex = this._position + 1;
+
+ Main.wm.insertWorkspace(newWorkspaceIndex);
+
+ const { workspaceManager } = global;
+ const workspace =
+ workspaceManager.get_workspace_by_index(newWorkspaceIndex);
+
+ const isWindow = !!source.metaWindow;
+ if (isWindow) {
+ // Move the window to our monitor first if necessary.
+ if (source.metaWindow.get_monitor() !== this._monitorIndex)
+ source.metaWindow.move_to_monitor(this._monitorIndex);
+ source.metaWindow.change_workspace_by_index(newWorkspaceIndex, true);
+ workspace.activate(time);
+ } else if (source.app && source.app.can_open_new_window()) {
+ if (source.animateLaunchAtPos)
+ source.animateLaunchAtPos(actor.x, actor.y);
+ source.app.open_new_window(newWorkspaceIndex);
+ } else if (!source.app && source.shellWorkspaceLaunch) {
+ // While unused in our own drag sources, shellWorkspaceLaunch allows
+ // extensions to define custom actions for their drag sources.
+ source.shellWorkspaceLaunch({
+ workspace: newWorkspaceIndex,
+ timestamp: time,
+ });
+ }
+
+ if (source.app || (!source.app && source.shellWorkspaceLaunch)) {
+ // This new workspace will be automatically removed if the application fails
+ // to open its first window within some time, as tracked by Shell.WindowTracker.
+ // Here, we only add a very brief timeout to avoid the _immediate_ removal of the
+ // workspace while we wait for the startup sequence to load.
+ Main.wm.keepWorkspaceAlive(workspace, WORKSPACE_KEEP_ALIVE_TIME);
+ }
+
+ return isWindow;
+ }
+
+ get position() {
+ return this._position;
+ }
+
+ set position(position) {
+ this._position = position;
+ }
+});
+
var WorkspacesView = GObject.registerClass(
class WorkspacesView extends WorkspacesViewBase {
_init(monitorIndex, scrollAdjustment, snapAdjustment, overviewAdjustment) {
@@ -102,6 +169,8 @@ class WorkspacesView extends WorkspacesViewBase {
this.queue_relayout();
});
+ this._delegate = this;
+
this._animating = false; // tweening
this._gestureActive = false; // touch(pad) gestures
@@ -109,6 +178,9 @@ class WorkspacesView extends WorkspacesViewBase {
this._onScrollId = this._scrollAdjustment.connect('notify::value',
this._onScrollAdjustmentChanged.bind(this));
+ this._placeholder = new WorkspaceDragPlaceholder(this._monitorIndex);
+ this.add_child(this._placeholder);
+
this._workspaces = [];
this._updateWorkspaces();
this._updateWorkspacesId =
@@ -128,6 +200,19 @@ class WorkspacesView extends WorkspacesViewBase {
this._activeWorkspaceChanged.bind(this));
}
+ _removeDragMonitor() {
+ if (!this._dragMonitor)
+ return;
+
+ DND.removeDragMonitor(this._dragMonitor);
+ delete this._dragMonitor;
+ }
+
+ _resetDropTarget() {
+ this._placeholder.hide();
+ this._placeholder.position = -1;
+ }
+
_getHorizontalSnapBox(box, spacing, vertical) {
const { nWorkspaces } = global.workspaceManager;
const [width, height] = box.get_size();
@@ -292,7 +377,7 @@ class WorkspacesView extends WorkspacesViewBase {
if (rtl)
workspaces.reverse();
- workspaces.forEach(child => {
+ workspaces.forEach((child, index) => {
if (snapProgress === 0)
box = horizontalBox;
else if (snapProgress === 1)
@@ -302,6 +387,26 @@ class WorkspacesView extends WorkspacesViewBase {
child.allocate_align_fill(box, 0.5, 0.5, false, false);
+ // Drop placeholder
+ if (this._placeholder.visible &&
+ this._placeholder.position === index) {
+ const spacing =
+ Math.interpolate(horizontalSpacing, verticalSpacing, snapProgress);
+ const placeholderBox = box.copy();
+ if (vertical) {
+ placeholderBox.y1 = box.y2;
+ placeholderBox.set_size(
+ box.get_width(),
+ spacing);
+ } else {
+ placeholderBox.x1 = box.x2;
+ placeholderBox.set_size(
+ spacing,
+ box.get_height());
+ }
+ this._placeholder.allocate(placeholderBox);
+ }
+
if (vertical) {
verticalBox.set_origin(
verticalBox.x1,
@@ -392,9 +497,17 @@ class WorkspacesView extends WorkspacesViewBase {
this._scrollToActive();
}
+ _dragEnd() {
+ super._dragEnd();
+ this._removeDragMonitor();
+ this._resetDropTarget();
+ }
+
_onDestroy() {
super._onDestroy();
+ this._removeDragMonitor();
+
this._scrollAdjustment.disconnect(this._onScrollId);
this._snapAdjustment.disconnect(this._snapNotifyId);
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
@@ -413,6 +526,71 @@ class WorkspacesView extends WorkspacesViewBase {
this._scrollToActive();
}
+ _getWorkspaceTarget(x, y) {
+ if (this.get_n_children() < 2)
+ return [false, -1];
+
+ const vertical = global.workspaceManager.layout_rows === -1;
+ const spacing = Math.interpolate(
+ this._getSpacing(this.allocation, Clutter.Orientation.HORIZONTAL, vertical),
+ this._getSpacing(this.allocation, Clutter.Orientation.VERTICAL, vertical),
+ this._snapAdjustment.value);
+
+ for (let i = 0; i < this._workspaces.length; i++) {
+ const workspace = this._workspaces[i];
+ const { allocation } = workspace;
+
+ const [workspaceWidth, workspaceHeight] = allocation.get_size();
+ const [workspaceX, workspaceY] = allocation.get_origin();
+
+ if (y < workspaceY ||
+ y > workspaceY + workspaceHeight ||
+ x < workspaceX)
+ break;
+
+ if (y >= workspaceY &&
+ y < workspaceY + workspaceHeight &&
+ x > workspaceX + workspaceWidth &&
+ x <= workspaceX + workspaceWidth + spacing)
+ return [true, i];
+ }
+
+ return [false, -1];
+ }
+
+ _updateWorkpaceDropTarget(x, y) {
+ const [isBetween, previousWorkspace] = this._getWorkspaceTarget(x, y);
+ this._placeholder.visible = isBetween;
+ this._placeholder.position = previousWorkspace;
+
+ return isBetween;
+ }
+
+ handleDragOver(source, actor, x, y) {
+ const inBetween = this._updateWorkpaceDropTarget(x, y);
+
+ if (!this._dragMonitor) {
+ this._dragMonitor = {
+ dragMotion: dragEvent => {
+ const [result, localX, localY] =
+ this.transform_stage_point(dragEvent.x, dragEvent.y);
+
+ if (!result)
+ return DND.DragMotionResult.CONTINUE;
+
+ if (!this._updateWorkpaceDropTarget(localX, localY))
+ this._removeDragMonitor();
+ return DND.DragMotionResult.CONTINUE;
+ },
+ };
+ DND.addDragMonitor(this._dragMonitor);
+ }
+
+ return inBetween
+ ? DND.DragMotionResult.MOVE_DROP
+ : DND.DragMotionResult.CONTINUE;
+ }
+
// sync the workspaces' positions to the value of the scroll adjustment
// and change the active workspace if appropriate
_onScrollAdjustmentChanged() {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]