js/appDisplay: Implement side page previews while DnDing

When DnDing an icon, we show both previous/next page, and optionally
a "placeholder" actor to allow creating new pages. These sides on the
scrollview are drop targets themselves, allowing to drop an app onto
the next/prev page without further navigation.

Still, preserve the checks to maybe switch to prev/next page without
finishing the DnD operation, for finer grained operations.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1630>
This commit is contained in:
Carlos Garnacho 2021-02-03 12:51:17 +01:00 committed by Marge Bot
parent d75ed55ed8
commit a00db66ffe
2 changed files with 46 additions and 4 deletions

View File

@ -141,6 +141,10 @@ $app_grid_fg_color: #fff;
background: rgba(255, 255, 255, 0.05); background: rgba(255, 255, 255, 0.05);
width: 88px; width: 88px;
&.dnd {
background: rgba(255, 255, 255, 0.1);
}
&.next { &.next {
&:ltr { border-radius: 15px 0px 0px 15px; } &:ltr { border-radius: 15px 0px 0px 15px; }
&:rtl { border-radius: 0px 15px 15px 0px; } &:rtl { border-radius: 0px 15px 15px 0px; }

View File

@ -56,6 +56,7 @@ var SidePages = {
NONE: 0, NONE: 0,
PREVIOUS: 1 << 0, PREVIOUS: 1 << 0,
NEXT: 1 << 1, NEXT: 1 << 1,
DND: 1 << 2,
}; };
function _getCategories(info) { function _getCategories(info) {
@ -155,6 +156,7 @@ var BaseAppView = GObject.registerClass({
enable_mouse_scrolling: false, enable_mouse_scrolling: false,
}); });
this._scrollView.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.NEVER); this._scrollView.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.NEVER);
this._scrollView._delegate = this;
this._canScroll = true; // limiting scrolling speed this._canScroll = true; // limiting scrolling speed
this._scrollTimeoutId = 0; this._scrollTimeoutId = 0;
@ -607,6 +609,7 @@ var BaseAppView = GObject.registerClass({
dragMotion: this._onDragMotion.bind(this), dragMotion: this._onDragMotion.bind(this),
}; };
DND.addDragMonitor(this._dragMonitor); DND.addDragMonitor(this._dragMonitor);
this._slideSidePages(SidePages.PREVIOUS | SidePages.NEXT | SidePages.DND);
} }
_onDragMotion(dragEvent) { _onDragMotion(dragEvent) {
@ -615,6 +618,14 @@ var BaseAppView = GObject.registerClass({
const appIcon = dragEvent.source; const appIcon = dragEvent.source;
this._dropPage = this._pageForCoords(dragEvent.x, dragEvent.y);
if (this._dropPage &&
this._dropPage === SidePages.PREVIOUS &&
this._grid.currentPage === 0) {
delete this._dropPage;
return DND.DragMotionResult.NO_DROP;
}
// Handle the drag overshoot. When dragging to above the // Handle the drag overshoot. When dragging to above the
// icon grid, move to the page above; when dragging below, // icon grid, move to the page above; when dragging below,
// move to the page below. // move to the page below.
@ -633,12 +644,15 @@ var BaseAppView = GObject.registerClass({
} }
this._resetOvershoot(); this._resetOvershoot();
this._slideSidePages(SidePages.NONE);
delete this._dropPage;
} }
_onDragCancelled() { _onDragCancelled() {
// At this point, the positions aren't stored yet, thus _redisplay() // At this point, the positions aren't stored yet, thus _redisplay()
// will move all items to their original positions // will move all items to their original positions
this._redisplay(); this._redisplay();
this._slideSidePages(SidePages.NONE);
} }
_canAccept(source) { _canAccept(source) {
@ -656,8 +670,16 @@ var BaseAppView = GObject.registerClass({
if (!this._canAccept(source)) if (!this._canAccept(source))
return false; return false;
if (this._dropPage) {
const increment = this._dropPage === SidePages.NEXT ? 1 : -1;
const { currentPage, nPages } = this._grid;
const page = Math.min(currentPage + increment, nPages);
const position = page < nPages ? -1 : 0;
this._moveItem(source, page, position);
this.goToPage(page);
} else if (this._delayedMoveData) {
// Dropped before the icon was moved // Dropped before the icon was moved
if (this._delayedMoveData) {
const { page, position } = this._delayedMoveData; const { page, position } = this._delayedMoveData;
this._moveItem(source, page, position); this._moveItem(source, page, position);
@ -1048,10 +1070,17 @@ var BaseAppView = GObject.registerClass({
let translationX = (1 - adjustment.value) * 100 * page; let translationX = (1 - adjustment.value) * 100 * page;
translationX = rtl ? -translationX : translationX; translationX = rtl ? -translationX : translationX;
const nextPage = this._grid.currentPage + page; const nextPage = this._grid.currentPage + page;
if (nextPage >= 0 && const hasFollowingPage = nextPage >= 0 &&
nextPage < this._grid.nPages - 1) { nextPage < this._grid.nPages;
if (hasFollowingPage) {
const items = this._grid.getItemsAtPage(nextPage); const items = this._grid.getItemsAtPage(nextPage);
items.forEach(item => (item.translation_x = translationX)); items.forEach(item => (item.translation_x = translationX));
}
if (hasFollowingPage ||
(page > 0 &&
this._grid.layout_manager.allow_incomplete_pages &&
(state & SidePages.DND) !== 0)) {
indicator.set({ indicator.set({
visible: true, visible: true,
opacity: adjustment.value * 255, opacity: adjustment.value * 255,
@ -1085,8 +1114,17 @@ var BaseAppView = GObject.registerClass({
this._pagesShown = state; this._pagesShown = state;
const showingNextPage = state & SidePages.NEXT; const showingNextPage = state & SidePages.NEXT;
const showingPrevPage = state & SidePages.PREVIOUS; const showingPrevPage = state & SidePages.PREVIOUS;
const dnd = state & SidePages.DND;
let adjustment; let adjustment;
if (dnd) {
this._nextPageIndicator.add_style_class_name('dnd');
this._prevPageIndicator.add_style_class_name('dnd');
} else {
this._nextPageIndicator.remove_style_class_name('dnd');
this._prevPageIndicator.remove_style_class_name('dnd');
}
adjustment = this._getPagePreviewAdjustment(1); adjustment = this._getPagePreviewAdjustment(1);
if (showingNextPage) { if (showingNextPage) {
adjustment = this._setupPagePreview(1, state); adjustment = this._setupPagePreview(1, state);