appDisplay: Factor out shared code into BaseAppView
The two BaseAppView subclasses now share a lot in terms of widgetry: they both have a scroll view, pagination dots, swipe management, etc. Move this shared code into BaseAppView. Notice, however, that BaseAppView only creates the widgetry, but it doesn't add them to any specific layout. FolderView arranges the widgetry in a vertical box, while AppDisplay arranges it in a ShellStack. Add a new 'orientation' parameter to BaseAppView and use it to determine the orientation of the pagination dots, the swipe tracker direction, and the scroll event handling. It is worth noticing that the scroll event is a bit more sophisticated now: when the orientation is horizontal, it handles all directions since mice wheels usually only generate up/down events. https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1271
This commit is contained in:
parent
45d8e11123
commit
cff0752bcc
@ -117,13 +117,65 @@ var BaseAppView = GObject.registerClass({
|
||||
'view-loaded': {},
|
||||
},
|
||||
}, class BaseAppView extends St.Widget {
|
||||
_init(params = {}) {
|
||||
_init(params = {}, orientation = Clutter.Orientation.VERTICAL) {
|
||||
super._init(params);
|
||||
|
||||
this._grid = this._createGrid();
|
||||
// Standard hack for ClutterBinLayout
|
||||
this._grid.x_expand = true;
|
||||
|
||||
const vertical = orientation === Clutter.Orientation.VERTICAL;
|
||||
|
||||
// Scroll View
|
||||
this._scrollView = new St.ScrollView({
|
||||
clip_to_allocation: true,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
reactive: true,
|
||||
});
|
||||
this._scrollView.set_policy(
|
||||
vertical ? St.PolicyType.NEVER : St.PolicyType.EXTERNAL,
|
||||
vertical ? St.PolicyType.EXTERNAL : St.PolicyType.NEVER);
|
||||
|
||||
this._canScroll = true; // limiting scrolling speed
|
||||
this._scrollTimeoutId = 0;
|
||||
this._scrollView.connect('scroll-event', this._onScroll.bind(this));
|
||||
|
||||
this._scrollView.add_actor(this._grid);
|
||||
|
||||
const scroll = vertical ? this._scrollView.vscroll : this._scrollView.hscroll;
|
||||
this._adjustment = scroll.adjustment;
|
||||
this._adjustment.connect('notify::value', adj => {
|
||||
this._pageIndicators.setCurrentPosition(adj.value / adj.page_size);
|
||||
});
|
||||
|
||||
// Page Indicators
|
||||
if (vertical)
|
||||
this._pageIndicators = new PageIndicators.AnimatedPageIndicators();
|
||||
else
|
||||
this._pageIndicators = new PageIndicators.PageIndicators(orientation);
|
||||
|
||||
this._pageIndicators.y_expand = vertical;
|
||||
this._pageIndicators.connect('page-activated',
|
||||
(indicators, pageIndex) => {
|
||||
this.goToPage(pageIndex);
|
||||
});
|
||||
this._pageIndicators.connect('scroll-event', (actor, event) => {
|
||||
this._scrollView.event(event, false);
|
||||
});
|
||||
|
||||
// Swipe
|
||||
this._swipeTracker = new SwipeTracker.SwipeTracker(this._scrollView,
|
||||
Shell.ActionMode.OVERVIEW | Shell.ActionMode.POPUP);
|
||||
this._swipeTracker.orientation = orientation;
|
||||
this._swipeTracker.connect('begin', this._swipeBegin.bind(this));
|
||||
this._swipeTracker.connect('update', this._swipeUpdate.bind(this));
|
||||
this._swipeTracker.connect('end', this._swipeEnd.bind(this));
|
||||
|
||||
this._availWidth = 0;
|
||||
this._availHeight = 0;
|
||||
this._orientation = orientation;
|
||||
|
||||
this._items = new Map();
|
||||
this._orderedItems = [];
|
||||
|
||||
@ -142,6 +194,86 @@ var BaseAppView = GObject.registerClass({
|
||||
return new IconGrid.IconGrid();
|
||||
}
|
||||
|
||||
_onScroll(actor, event) {
|
||||
if (this._swipeTracker.canHandleScrollEvent(event))
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (!this._canScroll)
|
||||
return Clutter.EVENT_STOP;
|
||||
|
||||
const rtl = this.get_text_direction() === Clutter.TextDirection.RTL;
|
||||
const vertical = this._orientation === Clutter.Orientation.VERTICAL;
|
||||
|
||||
let nextPage = this._grid.currentPage;
|
||||
switch (event.get_scroll_direction()) {
|
||||
case Clutter.ScrollDirection.UP:
|
||||
nextPage -= 1;
|
||||
break;
|
||||
|
||||
case Clutter.ScrollDirection.DOWN:
|
||||
nextPage += 1;
|
||||
break;
|
||||
|
||||
case Clutter.ScrollDirection.LEFT:
|
||||
if (vertical)
|
||||
return Clutter.EVENT_STOP;
|
||||
nextPage += rtl ? 1 : -1;
|
||||
break;
|
||||
|
||||
case Clutter.ScrollDirection.RIGHT:
|
||||
if (vertical)
|
||||
return Clutter.EVENT_STOP;
|
||||
nextPage += rtl ? -1 : 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
|
||||
this.goToPage(nextPage);
|
||||
|
||||
this._canScroll = false;
|
||||
this._scrollTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||
SCROLL_TIMEOUT_TIME, () => {
|
||||
this._canScroll = true;
|
||||
this._scrollTimeoutId = 0;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
|
||||
_swipeBegin(tracker, monitor) {
|
||||
if (monitor !== Main.layoutManager.primaryIndex)
|
||||
return;
|
||||
|
||||
const adjustment = this._adjustment;
|
||||
adjustment.remove_transition('value');
|
||||
|
||||
const progress = adjustment.value / adjustment.page_size;
|
||||
const points = Array.from({ length: this._grid.nPages }, (v, i) => i);
|
||||
const size = tracker.orientation === Clutter.Orientation.VERTICAL
|
||||
? this._scrollView.height : this._scrollView.width;
|
||||
|
||||
tracker.confirmSwipe(size, points, progress, Math.round(progress));
|
||||
}
|
||||
|
||||
_swipeUpdate(tracker, progress) {
|
||||
const adjustment = this._adjustment;
|
||||
adjustment.value = progress * adjustment.page_size;
|
||||
}
|
||||
|
||||
_swipeEnd(tracker, duration, endProgress) {
|
||||
const adjustment = this._adjustment;
|
||||
const value = endProgress * adjustment.page_size;
|
||||
|
||||
adjustment.ease(value, {
|
||||
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
||||
duration,
|
||||
onComplete: () => this.goToPage(endProgress, false),
|
||||
});
|
||||
}
|
||||
|
||||
_redisplay() {
|
||||
let oldApps = this._orderedItems.slice();
|
||||
let oldAppIds = oldApps.map(icon => icon.id);
|
||||
@ -272,7 +404,22 @@ var BaseAppView = GObject.registerClass({
|
||||
}
|
||||
}
|
||||
|
||||
vfunc_allocate(box) {
|
||||
const width = box.get_width();
|
||||
const height = box.get_height();
|
||||
|
||||
this.adaptToSize(width, height);
|
||||
|
||||
super.vfunc_allocate(box);
|
||||
}
|
||||
|
||||
vfunc_map() {
|
||||
this._swipeTracker.enabled = true;
|
||||
super.vfunc_map();
|
||||
}
|
||||
|
||||
vfunc_unmap() {
|
||||
this._swipeTracker.enabled = false;
|
||||
this._clearAnimateLater();
|
||||
super.vfunc_unmap();
|
||||
}
|
||||
@ -298,8 +445,43 @@ var BaseAppView = GObject.registerClass({
|
||||
this._grid.ease(params);
|
||||
}
|
||||
|
||||
adaptToSize(_width, _height) {
|
||||
throw new GObject.NotImplementedError('adaptToSize in %s'.format(this.constructor.name));
|
||||
goToPage(pageNumber, animate = true) {
|
||||
pageNumber = Math.clamp(pageNumber, 0, this._grid.nPages - 1);
|
||||
|
||||
if (this._grid.currentPage === pageNumber)
|
||||
return;
|
||||
|
||||
this._grid.goToPage(pageNumber, animate);
|
||||
}
|
||||
|
||||
adaptToSize(width, height) {
|
||||
let box = new Clutter.ActorBox({
|
||||
x2: width,
|
||||
y2: height,
|
||||
});
|
||||
box = this._scrollView.get_theme_node().get_content_box(box);
|
||||
box = this._grid.get_theme_node().get_content_box(box);
|
||||
|
||||
const availWidth = box.get_width();
|
||||
const availHeight = box.get_height();
|
||||
const oldNPages = this._grid.nPages;
|
||||
|
||||
this._grid.adaptToSize(availWidth, availHeight);
|
||||
|
||||
if (this._availWidth !== availWidth ||
|
||||
this._availHeight !== availHeight ||
|
||||
oldNPages !== this._grid.nPages) {
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
|
||||
this._adjustment.value = 0;
|
||||
this._grid.currentPage = 0;
|
||||
this._pageIndicators.setNPages(this._grid.nPages);
|
||||
this._pageIndicators.setCurrentPosition(0);
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
this._availWidth = availWidth;
|
||||
this._availHeight = availHeight;
|
||||
}
|
||||
});
|
||||
|
||||
@ -314,59 +496,24 @@ class AppDisplay extends BaseAppView {
|
||||
|
||||
this._grid._delegate = this;
|
||||
|
||||
this._scrollView.add_style_class_name('all-apps');
|
||||
|
||||
this._stack = new St.Widget({
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
});
|
||||
this.add_actor(this._stack);
|
||||
|
||||
this._scrollView = new St.ScrollView({
|
||||
style_class: 'all-apps',
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
reactive: true,
|
||||
});
|
||||
this._scrollView.add_actor(this._grid);
|
||||
this._stack.add_actor(this._scrollView);
|
||||
|
||||
this._scrollView.set_policy(St.PolicyType.NEVER,
|
||||
St.PolicyType.EXTERNAL);
|
||||
this._adjustment = this._scrollView.vscroll.adjustment;
|
||||
this._adjustment.connect('notify::value', adj => {
|
||||
this._pageIndicators.setCurrentPosition(adj.value / adj.page_size);
|
||||
});
|
||||
|
||||
this._pageIndicators = new PageIndicators.AnimatedPageIndicators();
|
||||
this._pageIndicators.connect('page-activated',
|
||||
(indicators, pageIndex) => {
|
||||
this.goToPage(pageIndex);
|
||||
});
|
||||
this._pageIndicators.connect('scroll-event', (actor, event) => {
|
||||
this._scrollView.event(event, false);
|
||||
});
|
||||
this.add_actor(this._pageIndicators);
|
||||
|
||||
this._folderIcons = [];
|
||||
|
||||
this._scrollView.connect('scroll-event', this._onScroll.bind(this));
|
||||
|
||||
this._swipeTracker = new SwipeTracker.SwipeTracker(
|
||||
this._scrollView, Shell.ActionMode.OVERVIEW);
|
||||
this._swipeTracker.connect('begin', this._swipeBegin.bind(this));
|
||||
this._swipeTracker.connect('update', this._swipeUpdate.bind(this));
|
||||
this._swipeTracker.connect('end', this._swipeEnd.bind(this));
|
||||
|
||||
this._currentDialog = null;
|
||||
this._displayingDialog = false;
|
||||
this._currentDialogDestroyId = 0;
|
||||
|
||||
this._canScroll = true; // limiting scrolling speed
|
||||
this._scrollTimeoutId = 0;
|
||||
|
||||
this._availWidth = 0;
|
||||
this._availHeight = 0;
|
||||
|
||||
this._lastOvershootY = -1;
|
||||
this._lastOvershootTimeoutId = 0;
|
||||
|
||||
@ -404,15 +551,6 @@ class AppDisplay extends BaseAppView {
|
||||
}
|
||||
}
|
||||
|
||||
vfunc_allocate(box) {
|
||||
box = this.get_theme_node().get_content_box(box);
|
||||
let availWidth = box.get_width();
|
||||
let availHeight = box.get_height();
|
||||
this.adaptToSize(availWidth, availHeight);
|
||||
|
||||
super.vfunc_allocate(box);
|
||||
}
|
||||
|
||||
_onDestroy() {
|
||||
if (this._scrollTimeoutId !== 0) {
|
||||
GLib.source_remove(this._scrollTimeoutId);
|
||||
@ -424,7 +562,6 @@ class AppDisplay extends BaseAppView {
|
||||
this._keyPressEventId =
|
||||
global.stage.connect('key-press-event',
|
||||
this._onKeyPressEvent.bind(this));
|
||||
this._swipeTracker.enabled = true;
|
||||
super.vfunc_map();
|
||||
}
|
||||
|
||||
@ -433,7 +570,6 @@ class AppDisplay extends BaseAppView {
|
||||
global.stage.disconnect(this._keyPressEventId);
|
||||
this._keyPressEventId = 0;
|
||||
}
|
||||
this._swipeTracker.enabled = false;
|
||||
super.vfunc_unmap();
|
||||
}
|
||||
|
||||
@ -581,68 +717,14 @@ class AppDisplay extends BaseAppView {
|
||||
if (this._displayingDialog && this._currentDialog)
|
||||
this._currentDialog.popdown();
|
||||
|
||||
this._grid.goToPage(pageNumber, animate);
|
||||
super.goToPage(pageNumber, animate);
|
||||
}
|
||||
|
||||
_onScroll(actor, event) {
|
||||
if (this._displayingDialog || !this._scrollView.reactive)
|
||||
return Clutter.EVENT_STOP;
|
||||
|
||||
if (this._swipeTracker.canHandleScrollEvent(event))
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (!this._canScroll)
|
||||
return Clutter.EVENT_STOP;
|
||||
|
||||
let direction = event.get_scroll_direction();
|
||||
if (direction == Clutter.ScrollDirection.UP)
|
||||
this.goToPage(this._grid.currentPage - 1);
|
||||
else if (direction == Clutter.ScrollDirection.DOWN)
|
||||
this.goToPage(this._grid.currentPage + 1);
|
||||
else
|
||||
return Clutter.EVENT_STOP;
|
||||
|
||||
this._canScroll = false;
|
||||
this._scrollTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||
SCROLL_TIMEOUT_TIME, () => {
|
||||
this._canScroll = true;
|
||||
this._scrollTimeoutId = 0;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
|
||||
_swipeBegin(tracker, monitor) {
|
||||
if (monitor !== Main.layoutManager.primaryIndex)
|
||||
return;
|
||||
|
||||
let adjustment = this._adjustment;
|
||||
adjustment.remove_transition('value');
|
||||
|
||||
let progress = adjustment.value / adjustment.page_size;
|
||||
let points = Array.from({ length: this._grid.nPages }, (v, i) => i);
|
||||
|
||||
tracker.confirmSwipe(this._scrollView.height,
|
||||
points, progress, Math.round(progress));
|
||||
}
|
||||
|
||||
_swipeUpdate(tracker, progress) {
|
||||
let adjustment = this._adjustment;
|
||||
adjustment.value = progress * adjustment.page_size;
|
||||
}
|
||||
|
||||
_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);
|
||||
},
|
||||
});
|
||||
return super._onScroll(actor, event);
|
||||
}
|
||||
|
||||
_onKeyPressEvent(actor, event) {
|
||||
@ -682,36 +764,6 @@ class AppDisplay extends BaseAppView {
|
||||
}
|
||||
|
||||
|
||||
// Called before allocation to calculate dynamic spacing
|
||||
adaptToSize(width, height) {
|
||||
let box = new Clutter.ActorBox();
|
||||
box.x1 = 0;
|
||||
box.x2 = width;
|
||||
box.y1 = 0;
|
||||
box.y2 = height;
|
||||
box = this.get_theme_node().get_content_box(box);
|
||||
box = this._scrollView.get_theme_node().get_content_box(box);
|
||||
box = this._grid.get_theme_node().get_content_box(box);
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
let oldNPages = this._grid.nPages;
|
||||
|
||||
this._grid.adaptToSize(availWidth, availHeight);
|
||||
|
||||
if (this._availWidth != availWidth || this._availHeight != availHeight || oldNPages != this._grid.nPages) {
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
|
||||
this._adjustment.value = 0;
|
||||
this._grid.currentPage = 0;
|
||||
this._pageIndicators.setNPages(this._grid.nPages);
|
||||
this._pageIndicators.setCurrentPosition(0);
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
this._availWidth = availWidth;
|
||||
this._availHeight = availHeight;
|
||||
}
|
||||
|
||||
_resetOvershoot() {
|
||||
if (this._lastOvershootTimeoutId)
|
||||
GLib.source_remove(this._lastOvershootTimeoutId);
|
||||
@ -974,7 +1026,7 @@ class FolderView extends BaseAppView {
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
});
|
||||
}, Clutter.Orientation.HORIZONTAL);
|
||||
|
||||
// If it not expand, the parent doesn't take into account its preferred_width when allocating
|
||||
// the second time it allocates, so we apply the "Standard hack for ClutterBinLayout"
|
||||
@ -984,14 +1036,6 @@ class FolderView extends BaseAppView {
|
||||
this._parentView = parentView;
|
||||
this._grid._delegate = this;
|
||||
|
||||
this._scrollView = new St.ScrollView({
|
||||
overlay_scrollbars: true,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
});
|
||||
this._scrollView.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.NEVER);
|
||||
this._scrollView.add_actor(this._grid);
|
||||
|
||||
const box = new St.BoxLayout({
|
||||
vertical: true,
|
||||
reactive: true,
|
||||
@ -999,28 +1043,9 @@ class FolderView extends BaseAppView {
|
||||
y_expand: true,
|
||||
});
|
||||
box.add_child(this._scrollView);
|
||||
|
||||
// Page Dots
|
||||
this._adjustment = this._scrollView.hscroll.adjustment;
|
||||
this._adjustment.connect('notify::value', adj => {
|
||||
this._pageIndicators.setCurrentPosition(adj.value / adj.page_size);
|
||||
});
|
||||
|
||||
this._pageIndicators = new PageIndicators.PageIndicators(Clutter.Orientation.HORIZONTAL);
|
||||
this._pageIndicators.y_expand = false;
|
||||
this._pageIndicators.connect('page-activated',
|
||||
(indicators, pageIndex) => {
|
||||
this._grid.goToPage(pageIndex);
|
||||
});
|
||||
this._pageIndicators.connect('scroll-event', (actor, event) => {
|
||||
this._scrollView.event(event, false);
|
||||
});
|
||||
box.add_child(this._pageIndicators);
|
||||
this.add_child(box);
|
||||
|
||||
this._availWidth = 0;
|
||||
this._availHeight = 0;
|
||||
|
||||
let action = new Clutter.PanAction({ interpolate: true });
|
||||
action.connect('pan', this._onPan.bind(this));
|
||||
this._scrollView.add_action(action);
|
||||
@ -1032,18 +1057,6 @@ class FolderView extends BaseAppView {
|
||||
return new FolderGrid();
|
||||
}
|
||||
|
||||
vfunc_allocate(box) {
|
||||
const node = this.get_theme_node();
|
||||
const contentBox = node.get_content_box(box);
|
||||
|
||||
const [width, height] = contentBox.get_size();
|
||||
this.adaptToSize(width, height);
|
||||
|
||||
this._grid.topPadding = 0;
|
||||
|
||||
super.vfunc_allocate(box);
|
||||
}
|
||||
|
||||
// Overridden from BaseAppView
|
||||
animate(animationDirection) {
|
||||
this._grid.animatePulse(animationDirection);
|
||||
@ -1080,26 +1093,10 @@ class FolderView extends BaseAppView {
|
||||
}
|
||||
|
||||
adaptToSize(width, height) {
|
||||
const oldNPages = this._grid.nPages;
|
||||
const [, indicatorHeight] = this._pageIndicators.get_preferred_height(-1);
|
||||
height -= indicatorHeight;
|
||||
|
||||
this._grid.adaptToSize(width, height);
|
||||
|
||||
if (this._availWidth !== width ||
|
||||
this._availHeight !== height ||
|
||||
oldNPages !== this._grid.nPages) {
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
|
||||
this._adjustment.value = 0;
|
||||
this._grid.currentPage = 0;
|
||||
this._pageIndicators.setNPages(this._grid.nPages);
|
||||
this._pageIndicators.setCurrentPosition(0);
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
this._availWidth = width;
|
||||
this._availHeight = height;
|
||||
super.adaptToSize(width, height);
|
||||
}
|
||||
|
||||
_loadApps() {
|
||||
|
Loading…
Reference in New Issue
Block a user