appDisplay: Use SwipeTracker

Replace existing panning and touchpad scrolling by SwipeTracker.

Since SwipeTracker only references one actor, redirect scroll events
from page indicators to the main scroll view.

Change programmatic scroll animation to use easeOutCubic interpolator
to match the gesture.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/826
This commit is contained in:
Alexander Mikhaylenko 2019-06-30 17:15:37 +05:00 committed by Florian Müllner
parent a11f417cd0
commit 3e6bcbb486

View File

@ -13,6 +13,7 @@ const Main = imports.ui.main;
const PageIndicators = imports.ui.pageIndicators; const PageIndicators = imports.ui.pageIndicators;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const Search = imports.ui.search; const Search = imports.ui.search;
const SwipeTracker = imports.ui.swipeTracker;
const Params = imports.misc.params; const Params = imports.misc.params;
const Util = imports.misc.util; const Util = imports.misc.util;
const SystemActions = imports.misc.systemActions; const SystemActions = imports.misc.systemActions;
@ -327,7 +328,9 @@ var AllView = GObject.registerClass({
(indicators, pageIndex) => { (indicators, pageIndex) => {
this.goToPage(pageIndex); this.goToPage(pageIndex);
}); });
this._pageIndicators.connect('scroll-event', this._onScroll.bind(this)); this._pageIndicators.connect('scroll-event', (actor, event) => {
this._scrollView.event(event, false);
});
this.add_actor(this._pageIndicators); this.add_actor(this._pageIndicators);
this._folderIcons = []; this._folderIcons = [];
@ -353,13 +356,12 @@ var AllView = GObject.registerClass({
this._scrollView.connect('scroll-event', this._onScroll.bind(this)); this._scrollView.connect('scroll-event', this._onScroll.bind(this));
let panAction = new Clutter.PanAction({ interpolate: false }); this._swipeTracker = new SwipeTracker.SwipeTracker(
panAction.connect('pan', this._onPan.bind(this)); this._scrollView, Shell.ActionMode.OVERVIEW);
panAction.connect('gesture-cancel', this._onPanEnd.bind(this)); this._swipeTracker.connect('begin', this._swipeBegin.bind(this));
panAction.connect('gesture-end', this._onPanEnd.bind(this)); this._swipeTracker.connect('update', this._swipeUpdate.bind(this));
this._panAction = panAction; this._swipeTracker.connect('end', this._swipeEnd.bind(this));
this._scrollView.add_action(panAction);
this._panning = false;
this._clickAction = new Clutter.ClickAction(); this._clickAction = new Clutter.ClickAction();
this._clickAction.connect('clicked', () => { this._clickAction.connect('clicked', () => {
if (!this._currentPopup) if (!this._currentPopup)
@ -424,6 +426,7 @@ var AllView = GObject.registerClass({
this._keyPressEventId = this._keyPressEventId =
global.stage.connect('key-press-event', global.stage.connect('key-press-event',
this._onKeyPressEvent.bind(this)); this._onKeyPressEvent.bind(this));
this._swipeTracker.enabled = true;
super.vfunc_map(); super.vfunc_map();
} }
@ -432,6 +435,7 @@ var AllView = GObject.registerClass({
global.stage.disconnect(this._keyPressEventId); global.stage.disconnect(this._keyPressEventId);
this._keyPressEventId = 0; this._keyPressEventId = 0;
} }
this._swipeTracker.enabled = false;
super.vfunc_unmap(); super.vfunc_unmap();
} }
@ -577,7 +581,7 @@ var AllView = GObject.registerClass({
return this._grid.getPageY(this._grid.currentPage); return this._grid.getPageY(this._grid.currentPage);
} }
goToPage(pageNumber) { goToPage(pageNumber, animate = true) {
pageNumber = clamp(pageNumber, 0, this._grid.nPages() - 1); pageNumber = clamp(pageNumber, 0, this._grid.nPages() - 1);
if (this._grid.currentPage == pageNumber && this._displayingPopup && this._currentPopup) if (this._grid.currentPage == pageNumber && this._displayingPopup && this._currentPopup)
@ -592,42 +596,16 @@ var AllView = GObject.registerClass({
return; return;
} }
let velocity; if (this._grid.currentPage === pageNumber)
if (!this._panning) return;
velocity = 0;
else
velocity = Math.abs(this._panAction.get_velocity(0)[2]);
// Tween the change between pages.
// If velocity is not specified (i.e. scrolling with mouse wheel),
// use the same speed regardless of original position
// if velocity is specified, it's in pixels per milliseconds
let diffToPage = this._diffToPage(pageNumber);
let childBox = this._scrollView.get_allocation_box();
let totalHeight = childBox.y2 - childBox.y1;
let time;
// Only take the velocity into account on page changes, otherwise
// return smoothly to the current page using the default velocity
if (this._grid.currentPage != pageNumber) {
let minVelocity = totalHeight / PAGE_SWITCH_TIME;
velocity = Math.max(minVelocity, velocity);
time = diffToPage / velocity;
} else {
time = PAGE_SWITCH_TIME * diffToPage / totalHeight;
}
// When changing more than one page, make sure to not take
// longer than PAGE_SWITCH_TIME
time = Math.min(time, PAGE_SWITCH_TIME);
this._grid.currentPage = pageNumber; this._grid.currentPage = pageNumber;
this._adjustment.ease(this._grid.getPageY(pageNumber), {
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
duration: time,
});
}
_diffToPage(pageNumber) { // Tween the change between pages.
let currentScrollPosition = this._adjustment.value; this._adjustment.ease(this._grid.getPageY(this._grid.currentPage), {
return Math.abs(currentScrollPosition - this._grid.getPageY(pageNumber)); mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
duration: animate ? PAGE_SWITCH_TIME : 0,
});
} }
openSpaceForPopup(item, side, nRows) { openSpaceForPopup(item, side, nRows) {
@ -650,6 +628,9 @@ var AllView = GObject.registerClass({
if (this._displayingPopup || !this._scrollView.reactive) if (this._displayingPopup || !this._scrollView.reactive)
return Clutter.EVENT_STOP; return Clutter.EVENT_STOP;
if (this._swipeTracker.canHandleScrollEvent(event))
return Clutter.EVENT_PROPAGATE;
if (!this._canScroll) if (!this._canScroll)
return Clutter.EVENT_STOP; return Clutter.EVENT_STOP;
@ -673,34 +654,36 @@ var AllView = GObject.registerClass({
return Clutter.EVENT_STOP; return Clutter.EVENT_STOP;
} }
_onPan(action) { _swipeBegin(tracker, monitor) {
if (this._displayingPopup) if (monitor !== Main.layoutManager.primaryIndex)
return false;
this._panning = true;
this._clickAction.release();
let [dist_, dx_, dy] = action.get_motion_delta(0);
let adjustment = this._adjustment;
adjustment.value -= (dy / this._scrollView.height) * adjustment.page_size;
return false;
}
_onPanEnd(action) {
if (this._displayingPopup)
return; return;
let pageHeight = this._grid.getPageHeight(); let adjustment = this._adjustment;
adjustment.remove_transition('value');
// Calculate the scroll value we'd be at, which is our current let progress = adjustment.value / adjustment.page_size;
// scroll plus any velocity the user had when they released let points = Array.from({ length: this._grid.nPages() }, (v, i) => i);
// their finger.
let velocity = -action.get_velocity(0)[2]; tracker.confirmSwipe(this._scrollView.height,
let endPanValue = this._adjustment.value + velocity; points, progress, Math.round(progress));
}
let closestPage = Math.round(endPanValue / pageHeight); _swipeUpdate(tracker, progress) {
this.goToPage(closestPage); let adjustment = this._adjustment;
adjustment.value = progress * adjustment.page_size;
}
this._panning = false; _swipeEnd(tracker, duration, endProgress) {
let adjustment = this._adjustment;
let value = endProgress * adjustment.page_size;
adjustment.ease(value, {
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
duration,
onComplete: () => {
this.goToPage(endProgress, false);
},
});
} }
_onKeyPressEvent(actor, event) { _onKeyPressEvent(actor, event) {