appDisplay: Move DnD code to BaseAppView
This code will be shared with FolderView in the next commit, so avoid duplication already and move the to-be-shared code into the base class. Because BaseAppView can handle vertical and horizontal orientations, adapt the drag overshoot code to also handle horizontal overshoot. https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1284
This commit is contained in:
parent
60311aa4d1
commit
704e08dc08
@ -122,6 +122,7 @@ var BaseAppView = GObject.registerClass({
|
|||||||
super._init(params);
|
super._init(params);
|
||||||
|
|
||||||
this._grid = this._createGrid();
|
this._grid = this._createGrid();
|
||||||
|
this._grid._delegate = this;
|
||||||
// Standard hack for ClutterBinLayout
|
// Standard hack for ClutterBinLayout
|
||||||
this._grid.x_expand = true;
|
this._grid.x_expand = true;
|
||||||
|
|
||||||
@ -189,6 +190,46 @@ var BaseAppView = GObject.registerClass({
|
|||||||
this._parentalControlsManager.connect('app-filter-changed', () => {
|
this._parentalControlsManager.connect('app-filter-changed', () => {
|
||||||
this._redisplay();
|
this._redisplay();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Drag n' Drop
|
||||||
|
this._lastOvershoot = -1;
|
||||||
|
this._lastOvershootTimeoutId = 0;
|
||||||
|
this._delayedMoveId = 0;
|
||||||
|
this._targetDropPosition = null;
|
||||||
|
|
||||||
|
this._dragBeginId =
|
||||||
|
Main.overview.connect('item-drag-begin', this._onDragBegin.bind(this));
|
||||||
|
this._dragEndId =
|
||||||
|
Main.overview.connect('item-drag-end', this._onDragEnd.bind(this));
|
||||||
|
this._dragCancelledId =
|
||||||
|
Main.overview.connect('item-drag-cancelled', this._onDragCancelled.bind(this));
|
||||||
|
|
||||||
|
this.connect('destroy', this._onDestroy.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
_onDestroy() {
|
||||||
|
this._removeDelayedMove();
|
||||||
|
|
||||||
|
if (this._dragBeginId > 0) {
|
||||||
|
Main.overview.disconnect(this._dragBeginId);
|
||||||
|
this._dragBeginId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._dragEndId > 0) {
|
||||||
|
Main.overview.disconnect(this._dragEndId);
|
||||||
|
this._dragEndId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._dragCancelledId > 0) {
|
||||||
|
Main.overview.disconnect(this._dragCancelledId);
|
||||||
|
this._dragCancelledId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._dragMonitor) {
|
||||||
|
DND.removeDragMonitor(this._dragMonitor);
|
||||||
|
this._dragMonitor = null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_createGrid() {
|
_createGrid() {
|
||||||
@ -275,6 +316,178 @@ var BaseAppView = GObject.registerClass({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_maybeMoveItem(dragEvent) {
|
||||||
|
const [success, x, y] =
|
||||||
|
this._grid.transform_stage_point(dragEvent.x, dragEvent.y);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const { source } = dragEvent;
|
||||||
|
const [page, position, dragLocation] =
|
||||||
|
this._getDropTarget(x, y, source);
|
||||||
|
const item = position !== -1
|
||||||
|
? this._grid.getItemAt(page, position) : null;
|
||||||
|
|
||||||
|
|
||||||
|
// Dragging over invalid parts of the grid cancels the timeout
|
||||||
|
if (item === source ||
|
||||||
|
dragLocation === IconGrid.DragLocation.INVALID ||
|
||||||
|
dragLocation === IconGrid.DragLocation.ON_ICON) {
|
||||||
|
this._removeDelayedMove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._targetDropPosition ||
|
||||||
|
this._targetDropPosition.page !== page ||
|
||||||
|
this._targetDropPosition.position !== position) {
|
||||||
|
// Update the item with a small delay
|
||||||
|
this._removeDelayedMove();
|
||||||
|
this._targetDropPosition = { page, position };
|
||||||
|
|
||||||
|
this._delayedMoveId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||||
|
DELAYED_MOVE_TIMEOUT, () => {
|
||||||
|
this._moveItem(source, page, position);
|
||||||
|
this._targetDropPosition = null;
|
||||||
|
this._delayedMoveId = 0;
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_removeDelayedMove() {
|
||||||
|
if (this._delayedMoveId > 0) {
|
||||||
|
GLib.source_remove(this._delayedMoveId);
|
||||||
|
this._delayedMoveId = 0;
|
||||||
|
}
|
||||||
|
this._targetDropPosition = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_resetOvershoot() {
|
||||||
|
if (this._lastOvershootTimeoutId)
|
||||||
|
GLib.source_remove(this._lastOvershootTimeoutId);
|
||||||
|
this._lastOvershootTimeoutId = 0;
|
||||||
|
this._lastOvershoot = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleDragOvershoot(dragEvent) {
|
||||||
|
const [gridX, gridY] = this.get_transformed_position();
|
||||||
|
const [gridWidth, gridHeight] = this.get_transformed_size();
|
||||||
|
|
||||||
|
const vertical = this._orientation === Clutter.Orientation.VERTICAL;
|
||||||
|
const gridStart = vertical ? gridY : gridX;
|
||||||
|
const gridEnd = vertical
|
||||||
|
? gridY + gridHeight - OVERSHOOT_THRESHOLD
|
||||||
|
: gridX + gridWidth - OVERSHOOT_THRESHOLD;
|
||||||
|
|
||||||
|
// Already animating
|
||||||
|
if (this._adjustment.get_transition('value') !== null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Within the grid boundaries
|
||||||
|
const dragPosition = vertical ? dragEvent.y : dragEvent.x;
|
||||||
|
if (dragPosition > gridStart && dragPosition < gridEnd) {
|
||||||
|
// Check whether we moved out the area of the last switch
|
||||||
|
if (Math.abs(this._lastOvershoot - dragPosition) > OVERSHOOT_THRESHOLD)
|
||||||
|
this._resetOvershoot();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Still in the area of the previous page switch
|
||||||
|
if (this._lastOvershoot >= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const currentPosition = this._adjustment.value;
|
||||||
|
const maxPosition = this._adjustment.upper - this._adjustment.page_size;
|
||||||
|
|
||||||
|
if (dragPosition <= gridStart && currentPosition > 0)
|
||||||
|
this.goToPage(this._grid.currentPage - 1);
|
||||||
|
else if (dragPosition >= gridEnd && currentPosition < maxPosition)
|
||||||
|
this.goToPage(this._grid.currentPage + 1);
|
||||||
|
else
|
||||||
|
return; // don't go beyond first/last page
|
||||||
|
|
||||||
|
this._lastOvershoot = dragPosition;
|
||||||
|
|
||||||
|
if (this._lastOvershootTimeoutId > 0)
|
||||||
|
GLib.source_remove(this._lastOvershootTimeoutId);
|
||||||
|
|
||||||
|
this._lastOvershootTimeoutId =
|
||||||
|
GLib.timeout_add(GLib.PRIORITY_DEFAULT, OVERSHOOT_TIMEOUT, () => {
|
||||||
|
this._resetOvershoot();
|
||||||
|
this._handleDragOvershoot(dragEvent);
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
});
|
||||||
|
GLib.Source.set_name_by_id(this._lastOvershootTimeoutId,
|
||||||
|
'[gnome-shell] this._lastOvershootTimeoutId');
|
||||||
|
}
|
||||||
|
|
||||||
|
_onDragBegin() {
|
||||||
|
this._dragMonitor = {
|
||||||
|
dragMotion: this._onDragMotion.bind(this),
|
||||||
|
};
|
||||||
|
DND.addDragMonitor(this._dragMonitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onDragMotion(dragEvent) {
|
||||||
|
if (!(dragEvent.source instanceof AppViewItem))
|
||||||
|
return DND.DragMotionResult.CONTINUE;
|
||||||
|
|
||||||
|
const appIcon = dragEvent.source;
|
||||||
|
|
||||||
|
// Handle the drag overshoot. When dragging to above the
|
||||||
|
// icon grid, move to the page above; when dragging below,
|
||||||
|
// move to the page below.
|
||||||
|
if (appIcon instanceof AppViewItem)
|
||||||
|
this._handleDragOvershoot(dragEvent);
|
||||||
|
|
||||||
|
this._maybeMoveItem(dragEvent);
|
||||||
|
|
||||||
|
return DND.DragMotionResult.CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
_onDragEnd() {
|
||||||
|
if (this._dragMonitor) {
|
||||||
|
DND.removeDragMonitor(this._dragMonitor);
|
||||||
|
this._dragMonitor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._resetOvershoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onDragCancelled() {
|
||||||
|
// At this point, the positions aren't stored yet, thus _redisplay()
|
||||||
|
// will move all items to their original positions
|
||||||
|
this._redisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
_canAccept(source) {
|
||||||
|
return source instanceof AppViewItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDragOver(source) {
|
||||||
|
if (!this._canAccept(source))
|
||||||
|
return DND.DragMotionResult.NO_DROP;
|
||||||
|
|
||||||
|
return DND.DragMotionResult.MOVE_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptDrop(source) {
|
||||||
|
if (!this._canAccept(source))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Dropped before the icon was moved
|
||||||
|
if (this._targetDropPosition) {
|
||||||
|
const { page, position } = this._targetDropPosition;
|
||||||
|
|
||||||
|
this._moveItem(source, page, position);
|
||||||
|
this._removeDelayedMove();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
_addItem(item, page, position) {
|
_addItem(item, page, position) {
|
||||||
let itemIndex = 0;
|
let itemIndex = 0;
|
||||||
|
|
||||||
@ -646,7 +859,6 @@ class AppDisplay extends BaseAppView {
|
|||||||
y_expand: true,
|
y_expand: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
this._grid._delegate = this;
|
|
||||||
this._pageManager = new PageManager();
|
this._pageManager = new PageManager();
|
||||||
|
|
||||||
this._scrollView.add_style_class_name('all-apps');
|
this._scrollView.add_style_class_name('all-apps');
|
||||||
@ -667,10 +879,6 @@ class AppDisplay extends BaseAppView {
|
|||||||
this._displayingDialog = false;
|
this._displayingDialog = false;
|
||||||
this._currentDialogDestroyId = 0;
|
this._currentDialogDestroyId = 0;
|
||||||
|
|
||||||
this._lastOvershootY = -1;
|
|
||||||
this._lastOvershootTimeoutId = 0;
|
|
||||||
this._delayedMoveId = 0;
|
|
||||||
this._targetDropPosition = null;
|
|
||||||
this._placeholder = null;
|
this._placeholder = null;
|
||||||
|
|
||||||
Main.overview.connect('hidden', () => this.goToPage(0));
|
Main.overview.connect('hidden', () => this.goToPage(0));
|
||||||
@ -687,12 +895,6 @@ class AppDisplay extends BaseAppView {
|
|||||||
Main.queueDeferredWork(this._redisplayWorkId);
|
Main.queueDeferredWork(this._redisplayWorkId);
|
||||||
});
|
});
|
||||||
|
|
||||||
Main.overview.connect('item-drag-begin', this._onDragBegin.bind(this));
|
|
||||||
Main.overview.connect('item-drag-end', this._onDragEnd.bind(this));
|
|
||||||
Main.overview.connect('item-drag-cancelled', this._onDragCancelled.bind(this));
|
|
||||||
|
|
||||||
this.connect('destroy', this._onDestroy.bind(this));
|
|
||||||
|
|
||||||
this._switcherooNotifyId = global.connect('notify::switcheroo-control',
|
this._switcherooNotifyId = global.connect('notify::switcheroo-control',
|
||||||
() => this._updateDiscreteGpuAvailable());
|
() => this._updateDiscreteGpuAvailable());
|
||||||
this._updateDiscreteGpuAvailable();
|
this._updateDiscreteGpuAvailable();
|
||||||
@ -709,7 +911,7 @@ class AppDisplay extends BaseAppView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onDestroy() {
|
_onDestroy() {
|
||||||
this._removeDelayedMove();
|
super._onDestroy();
|
||||||
|
|
||||||
if (this._scrollTimeoutId !== 0) {
|
if (this._scrollTimeoutId !== 0) {
|
||||||
GLib.source_remove(this._scrollTimeoutId);
|
GLib.source_remove(this._scrollTimeoutId);
|
||||||
@ -975,112 +1177,17 @@ class AppDisplay extends BaseAppView {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_removeDelayedMove() {
|
|
||||||
if (this._delayedMoveId > 0) {
|
|
||||||
GLib.source_remove(this._delayedMoveId);
|
|
||||||
this._delayedMoveId = 0;
|
|
||||||
}
|
|
||||||
this._targetDropPosition = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_maybeMoveItem(dragEvent) {
|
_maybeMoveItem(dragEvent) {
|
||||||
const [success, x, y] =
|
const clonedEvent = {
|
||||||
this._grid.transform_stage_point(dragEvent.x, dragEvent.y);
|
...dragEvent,
|
||||||
|
source: this._placeholder ? this._placeholder : dragEvent.source,
|
||||||
if (!success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const source = this._placeholder ? this._placeholder : dragEvent.source;
|
|
||||||
const [page, position, dragLocation] =
|
|
||||||
this._getDropTarget(x, y, source);
|
|
||||||
const item = position !== -1
|
|
||||||
? this._grid.getItemAt(page, position) : null;
|
|
||||||
|
|
||||||
|
|
||||||
// Dragging over invalid parts of the grid cancels the timeout
|
|
||||||
if (item === source ||
|
|
||||||
dragLocation === IconGrid.DragLocation.INVALID ||
|
|
||||||
dragLocation === IconGrid.DragLocation.ON_ICON) {
|
|
||||||
this._removeDelayedMove();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._targetDropPosition ||
|
|
||||||
this._targetDropPosition.page !== page ||
|
|
||||||
this._targetDropPosition.position !== position) {
|
|
||||||
// Update the item with a small delay
|
|
||||||
this._removeDelayedMove();
|
|
||||||
this._targetDropPosition = { page, position };
|
|
||||||
|
|
||||||
this._delayedMoveId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
|
||||||
DELAYED_MOVE_TIMEOUT, () => {
|
|
||||||
this._moveItem(source, page, position);
|
|
||||||
this._targetDropPosition = null;
|
|
||||||
this._delayedMoveId = 0;
|
|
||||||
return GLib.SOURCE_REMOVE;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_resetOvershoot() {
|
|
||||||
if (this._lastOvershootTimeoutId)
|
|
||||||
GLib.source_remove(this._lastOvershootTimeoutId);
|
|
||||||
this._lastOvershootTimeoutId = 0;
|
|
||||||
this._lastOvershootY = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleDragOvershoot(dragEvent) {
|
|
||||||
let [, gridY] = this.get_transformed_position();
|
|
||||||
let [, gridHeight] = this.get_transformed_size();
|
|
||||||
const gridBottom = gridY + gridHeight - OVERSHOOT_THRESHOLD;
|
|
||||||
|
|
||||||
// Already animating
|
|
||||||
if (this._adjustment.get_transition('value') !== null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Within the grid boundaries
|
|
||||||
if (dragEvent.y > gridY && dragEvent.y < gridBottom) {
|
|
||||||
// Check whether we moved out the area of the last switch
|
|
||||||
if (Math.abs(this._lastOvershootY - dragEvent.y) > OVERSHOOT_THRESHOLD)
|
|
||||||
this._resetOvershoot();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Still in the area of the previous page switch
|
|
||||||
if (this._lastOvershootY >= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let currentY = this._adjustment.value;
|
|
||||||
let maxY = this._adjustment.upper - this._adjustment.page_size;
|
|
||||||
|
|
||||||
if (dragEvent.y <= gridY && currentY > 0)
|
|
||||||
this.goToPage(this._grid.currentPage - 1);
|
|
||||||
else if (dragEvent.y >= gridBottom && currentY < maxY)
|
|
||||||
this.goToPage(this._grid.currentPage + 1);
|
|
||||||
else
|
|
||||||
return; // don't go beyond first/last page
|
|
||||||
|
|
||||||
this._lastOvershootY = dragEvent.y;
|
|
||||||
|
|
||||||
if (this._lastOvershootTimeoutId > 0)
|
|
||||||
GLib.source_remove(this._lastOvershootTimeoutId);
|
|
||||||
|
|
||||||
this._lastOvershootTimeoutId =
|
|
||||||
GLib.timeout_add(GLib.PRIORITY_DEFAULT, OVERSHOOT_TIMEOUT, () => {
|
|
||||||
this._resetOvershoot();
|
|
||||||
this._handleDragOvershoot(dragEvent);
|
|
||||||
return GLib.SOURCE_REMOVE;
|
|
||||||
});
|
|
||||||
GLib.Source.set_name_by_id(this._lastOvershootTimeoutId,
|
|
||||||
'[gnome-shell] this._lastOvershootTimeoutId');
|
|
||||||
}
|
|
||||||
|
|
||||||
_onDragBegin(_overview, source) {
|
|
||||||
this._dragMonitor = {
|
|
||||||
dragMotion: this._onDragMotion.bind(this),
|
|
||||||
};
|
};
|
||||||
DND.addDragMonitor(this._dragMonitor);
|
|
||||||
|
super._maybeMoveItem(clonedEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onDragBegin(overview, source) {
|
||||||
|
super._onDragBegin(overview, source);
|
||||||
|
|
||||||
// When dragging from a folder dialog, the dragged app icon doesn't
|
// When dragging from a folder dialog, the dragged app icon doesn't
|
||||||
// exist in AppDisplay. We work around that by adding a placeholder
|
// exist in AppDisplay. We work around that by adding a placeholder
|
||||||
@ -1091,66 +1198,30 @@ class AppDisplay extends BaseAppView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onDragMotion(dragEvent) {
|
_onDragMotion(dragEvent) {
|
||||||
if (!(dragEvent.source instanceof AppViewItem))
|
if (this._currentDialog)
|
||||||
return DND.DragMotionResult.CONTINUE;
|
return DND.DragMotionResult.CONTINUE;
|
||||||
|
|
||||||
let appIcon = dragEvent.source;
|
return super._onDragMotion(dragEvent);
|
||||||
|
|
||||||
// Handle the drag overshoot. When dragging to above the
|
|
||||||
// icon grid, move to the page above; when dragging below,
|
|
||||||
// move to the page below.
|
|
||||||
if (appIcon instanceof AppViewItem)
|
|
||||||
this._handleDragOvershoot(dragEvent);
|
|
||||||
|
|
||||||
this._maybeMoveItem(dragEvent);
|
|
||||||
|
|
||||||
return DND.DragMotionResult.CONTINUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDragEnd() {
|
_onDragEnd() {
|
||||||
if (this._dragMonitor) {
|
super._onDragEnd();
|
||||||
DND.removeDragMonitor(this._dragMonitor);
|
|
||||||
this._dragMonitor = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._resetOvershoot();
|
|
||||||
this._removePlaceholder();
|
this._removePlaceholder();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDragCancelled(_overview, source) {
|
_onDragCancelled(overview, source) {
|
||||||
const view = _getViewFromIcon(source);
|
const view = _getViewFromIcon(source);
|
||||||
|
|
||||||
if (view instanceof FolderView)
|
if (view instanceof FolderView)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// At this point, the positions aren't stored yet, thus _redisplay()
|
super._onDragCancelled(overview, source);
|
||||||
// will move all items to their original positions
|
|
||||||
this._redisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
_canAccept(source) {
|
|
||||||
return source instanceof AppViewItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleDragOver(source) {
|
|
||||||
if (!this._canAccept(source))
|
|
||||||
return DND.DragMotionResult.NO_DROP;
|
|
||||||
|
|
||||||
return DND.DragMotionResult.MOVE_DROP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
acceptDrop(source) {
|
acceptDrop(source) {
|
||||||
if (!this._canAccept(source))
|
if (!super.acceptDrop(source))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Dropped before the icon was moved
|
|
||||||
if (this._targetDropPosition) {
|
|
||||||
const { page, position } = this._targetDropPosition;
|
|
||||||
|
|
||||||
this._moveItem(source, page, position);
|
|
||||||
this._removeDelayedMove();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._savePages();
|
this._savePages();
|
||||||
|
|
||||||
let view = _getViewFromIcon(source);
|
let view = _getViewFromIcon(source);
|
||||||
|
Loading…
Reference in New Issue
Block a user