iconGrid: Add drop target API
Add a new drop target API. The bulk of it is implemented by IconGridLayout, since it's the layout manager that knows where each icon is placed at. https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1284
This commit is contained in:
parent
f50205e9b4
commit
427b9ac75f
@ -404,6 +404,58 @@ var BaseAppView = GObject.registerClass({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getDropTarget(x, y, source) {
|
||||||
|
const { currentPage } = this._grid;
|
||||||
|
|
||||||
|
let [item, dragLocation] = this._grid.getDropTarget(x, y);
|
||||||
|
|
||||||
|
const [sourcePage, sourcePosition] = this._grid.getItemPosition(source);
|
||||||
|
const targetPage = currentPage;
|
||||||
|
let targetPosition = item
|
||||||
|
? this._grid.getItemPosition(item)[1] : -1;
|
||||||
|
|
||||||
|
// In case we're hovering over the edge of an item but the
|
||||||
|
// reflow will happen in the opposite direction (the drag
|
||||||
|
// can't "naturally push the item away"), we instead set the
|
||||||
|
// drop target to the adjacent item that can be pushed away
|
||||||
|
// in the reflow-direction.
|
||||||
|
//
|
||||||
|
// We must avoid doing that if we're hovering over the first
|
||||||
|
// or last column though, in that case there is no adjacent
|
||||||
|
// icon we could push away.
|
||||||
|
if (dragLocation === IconGrid.DragLocation.START_EDGE &&
|
||||||
|
targetPosition > sourcePosition &&
|
||||||
|
targetPage === sourcePage) {
|
||||||
|
const nColumns = this._grid.layout_manager.columns_per_page;
|
||||||
|
const targetColumn = targetPosition % nColumns;
|
||||||
|
|
||||||
|
if (targetColumn > 0) {
|
||||||
|
targetPosition -= 1;
|
||||||
|
dragLocation = IconGrid.DragLocation.END_EDGE;
|
||||||
|
}
|
||||||
|
} else if (dragLocation === IconGrid.DragLocation.END_EDGE &&
|
||||||
|
(targetPosition < sourcePosition ||
|
||||||
|
targetPage !== sourcePage)) {
|
||||||
|
const nColumns = this._grid.layout_manager.columns_per_page;
|
||||||
|
const targetColumn = targetPosition % nColumns;
|
||||||
|
|
||||||
|
if (targetColumn < nColumns - 1) {
|
||||||
|
targetPosition += 1;
|
||||||
|
dragLocation = IconGrid.DragLocation.START_EDGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append to the page if dragging over empty area
|
||||||
|
if (dragLocation === IconGrid.DragLocation.EMPTY_SPACE) {
|
||||||
|
const pageItems =
|
||||||
|
this._grid.getItemsAtPage(currentPage).filter(c => c.visible);
|
||||||
|
|
||||||
|
targetPosition = pageItems.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [targetPage, targetPosition, dragLocation];
|
||||||
|
}
|
||||||
|
|
||||||
vfunc_allocate(box) {
|
vfunc_allocate(box) {
|
||||||
const width = box.get_width();
|
const width = box.get_width();
|
||||||
const height = box.get_height();
|
const height = box.get_height();
|
||||||
|
@ -52,6 +52,17 @@ const defaultGridModes = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
var LEFT_DIVIDER_LEEWAY = 20;
|
||||||
|
var RIGHT_DIVIDER_LEEWAY = 20;
|
||||||
|
|
||||||
|
var DragLocation = {
|
||||||
|
INVALID: 0,
|
||||||
|
START_EDGE: 1,
|
||||||
|
ON_ICON: 2,
|
||||||
|
END_EDGE: 3,
|
||||||
|
EMPTY_SPACE: 4,
|
||||||
|
};
|
||||||
|
|
||||||
var BaseIcon = GObject.registerClass(
|
var BaseIcon = GObject.registerClass(
|
||||||
class BaseIcon extends St.Bin {
|
class BaseIcon extends St.Bin {
|
||||||
_init(label, params) {
|
_init(label, params) {
|
||||||
@ -946,6 +957,99 @@ var IconGridLayout = GObject.registerClass({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getDropTarget:
|
||||||
|
* @param {int} x: position of the horizontal axis
|
||||||
|
* @param {int} y: position of the vertical axis
|
||||||
|
*
|
||||||
|
* Retrieves the item located at (@x, @y), as well as the drag location.
|
||||||
|
* Both @x and @y are relative to the grid.
|
||||||
|
*
|
||||||
|
* @returns {[Clutter.Actor, DragLocation]} the item and drag location
|
||||||
|
* under (@x, @y)
|
||||||
|
*/
|
||||||
|
getDropTarget(x, y) {
|
||||||
|
const childSize = this._getChildrenMaxSize();
|
||||||
|
const [leftEmptySpace, topEmptySpace, hSpacing, vSpacing] =
|
||||||
|
this._calculateSpacing(childSize);
|
||||||
|
|
||||||
|
const isRtl =
|
||||||
|
Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
|
||||||
|
|
||||||
|
let page = this._orientation === Clutter.Orientation.VERTICAL
|
||||||
|
? Math.floor(y / this._pageHeight)
|
||||||
|
: Math.floor(x / this._pageWidth);
|
||||||
|
|
||||||
|
// Out of bounds
|
||||||
|
if (page >= this._pages.length)
|
||||||
|
return [null, DragLocation.INVALID];
|
||||||
|
|
||||||
|
if (isRtl && this._orientation === Clutter.Orientation.HORIZONTAL)
|
||||||
|
page = swap(page, this._pages.length);
|
||||||
|
|
||||||
|
// Page-relative coordinates from now on
|
||||||
|
x %= this._pageWidth;
|
||||||
|
y %= this._pageHeight;
|
||||||
|
|
||||||
|
if (x < leftEmptySpace || y < topEmptySpace)
|
||||||
|
return [null, DragLocation.INVALID];
|
||||||
|
|
||||||
|
const gridWidth =
|
||||||
|
childSize * this._columnsPerPage +
|
||||||
|
hSpacing * (this._columnsPerPage - 1);
|
||||||
|
const gridHeight =
|
||||||
|
childSize * this._rowsPerPage +
|
||||||
|
vSpacing * (this._rowsPerPage - 1);
|
||||||
|
|
||||||
|
if (x > leftEmptySpace + gridWidth || y > topEmptySpace + gridHeight)
|
||||||
|
return [null, DragLocation.INVALID];
|
||||||
|
|
||||||
|
const halfHSpacing = hSpacing / 2;
|
||||||
|
const halfVSpacing = vSpacing / 2;
|
||||||
|
const visibleItems = this._getVisibleChildrenForPage(page);
|
||||||
|
|
||||||
|
for (const item of visibleItems) {
|
||||||
|
const childBox = item.allocation.copy();
|
||||||
|
|
||||||
|
// Page offset
|
||||||
|
switch (this._orientation) {
|
||||||
|
case Clutter.Orientation.HORIZONTAL:
|
||||||
|
childBox.set_origin(childBox.x1 % this._pageWidth, childBox.y1);
|
||||||
|
break;
|
||||||
|
case Clutter.Orientation.VERTICAL:
|
||||||
|
childBox.set_origin(childBox.x1, childBox.y1 % this._pageHeight);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outside the icon boundaries
|
||||||
|
if (x < childBox.x1 - halfHSpacing ||
|
||||||
|
x > childBox.x2 + halfHSpacing ||
|
||||||
|
y < childBox.y1 - halfVSpacing ||
|
||||||
|
y > childBox.y2 + halfVSpacing)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let dragLocation;
|
||||||
|
|
||||||
|
if (x < childBox.x1 + LEFT_DIVIDER_LEEWAY)
|
||||||
|
dragLocation = DragLocation.START_EDGE;
|
||||||
|
else if (x > childBox.x2 - RIGHT_DIVIDER_LEEWAY)
|
||||||
|
dragLocation = DragLocation.END_EDGE;
|
||||||
|
else
|
||||||
|
dragLocation = DragLocation.ON_ICON;
|
||||||
|
|
||||||
|
if (isRtl) {
|
||||||
|
if (dragLocation === DragLocation.START_EDGE)
|
||||||
|
dragLocation = DragLocation.END_EDGE;
|
||||||
|
else if (dragLocation === DragLocation.END_EDGE)
|
||||||
|
dragLocation = DragLocation.START_EDGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [item, dragLocation];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [null, DragLocation.EMPTY_SPACE];
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
get allow_incomplete_pages() {
|
get allow_incomplete_pages() {
|
||||||
return this._allowIncompletePages;
|
return this._allowIncompletePages;
|
||||||
@ -1559,6 +1663,11 @@ var IconGrid = GObject.registerClass({
|
|||||||
this.queue_relayout();
|
this.queue_relayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDropTarget(x, y) {
|
||||||
|
const layoutManager = this.layout_manager;
|
||||||
|
return layoutManager.getDropTarget(x, y, this._currentPage);
|
||||||
|
}
|
||||||
|
|
||||||
get itemsPerPage() {
|
get itemsPerPage() {
|
||||||
const layoutManager = this.layout_manager;
|
const layoutManager = this.layout_manager;
|
||||||
return layoutManager.rows_per_page * layoutManager.columns_per_page;
|
return layoutManager.rows_per_page * layoutManager.columns_per_page;
|
||||||
|
Loading…
Reference in New Issue
Block a user