Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

map.isMoving() match move events fix #9647 #9679

Merged
merged 3 commits into from
May 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions src/ui/handler_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ class HandlerManager {
return !!this._eventsInProgress.rotate;
}

isMoving() {
return Boolean(isMoving(this._eventsInProgress)) || this.isZooming();
}

_blockedByActive(activeHandlers: { [string]: Handler }, allowed: Array<string>, myName: string) {
for (const name in activeHandlers) {
if (name === myName) continue;
Expand Down Expand Up @@ -430,17 +434,23 @@ class HandlerManager {
const wasMoving = isMoving(this._eventsInProgress);
const nowMoving = isMoving(newEventsInProgress);

if (!wasMoving && nowMoving) {
this._fireEvent('movestart', nowMoving.originalEvent);
}
const startEvents = {};

for (const eventName in newEventsInProgress) {
const {originalEvent} = newEventsInProgress[eventName];
const isStart = !this._eventsInProgress[eventName];
this._eventsInProgress[eventName] = newEventsInProgress[eventName];
if (isStart) {
this._fireEvent(`${eventName}start`, originalEvent);
if (!this._eventsInProgress[eventName]) {
startEvents[`${eventName}start`] = originalEvent;
}
this._eventsInProgress[eventName] = newEventsInProgress[eventName];
}

// fire start events only after this._eventsInProgress has been updated
if (!wasMoving && nowMoving) {
this._fireEvent('movestart', nowMoving.originalEvent);
}

for (const name in startEvents) {
this._fireEvent(name, startEvents[name]);
}

if (newEventsInProgress.rotate) this._bearingChanged = true;
Expand All @@ -454,16 +464,22 @@ class HandlerManager {
this._fireEvent(eventName, originalEvent);
}

const endEvents = {};

let originalEndEvent;
for (const eventName in this._eventsInProgress) {
const {handlerName, originalEvent} = this._eventsInProgress[eventName];
if (!this._handlersById[handlerName].isActive()) {
delete this._eventsInProgress[eventName];
originalEndEvent = deactivatedHandlers[handlerName] || originalEvent;
this._fireEvent(`${eventName}end`, originalEndEvent);
endEvents[`${eventName}end`] = originalEndEvent;
}
}

for (const name in endEvents) {
this._fireEvent(name, endEvents[name]);
}

const stillMoving = isMoving(this._eventsInProgress);
if ((wasMoving || nowMoving) && !stillMoving) {
this._updatingCamera = true;
Expand Down
2 changes: 1 addition & 1 deletion src/ui/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,7 @@ class Map extends Camera {
* var isMoving = map.isMoving();
*/
isMoving(): boolean {
return this._moving || this.handlers.isActive();
return this._moving || this.handlers.isMoving();
}

/**
Expand Down
2 changes: 2 additions & 0 deletions test/unit/ui/handler/drag_rotate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,11 @@ test('DragRotateHandler ensures that map.isMoving() returns true during drag', (

simulate.mousedown(map.getCanvas(), {buttons: 2, button: 2});
simulate.mousemove(map.getCanvas(), {buttons: 2, clientX: 10, clientY: 10});
map._renderTaskQueue.run();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This particular test is now failing from CI, is this call actually processing the events correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was missing another one of these calls after the next event.

In old releases map.isMoving() would return true immediately after a mousemove event that causes a rotation. Now map.isMoving() only returns true on the next frame when the move events have fired and the camera events have changed. Does this seem ok to you?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not think that having a one frame delay difference for this type of API as being critical. As long as we still cover the following requirements:

Returns true if the map is panning, zooming, rotating, or pitching due to a camera animation or user gesture.

t.ok(map.isMoving());

simulate.mouseup(map.getCanvas(), {buttons: 0, button: 2});
map._renderTaskQueue.run();
t.ok(!map.isMoving());

map.remove();
Expand Down
12 changes: 12 additions & 0 deletions test/unit/ui/map/isMoving.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,18 @@ test('Map#isMoving returns true during a camera zoom animation', (t) => {
test('Map#isMoving returns true when drag panning', (t) => {
const map = createMap(t);

map.on('movestart', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know if we have enough coverage around whether those events are triggered under a similar context?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test case is focused around isMoving. But what I'm thinking is covering whether we call the lambda function at all, if we fail to call that lambda it would go unnoticed.

Maybe collecting how many times those lambdas are called and testing that at the end of this block could be useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in this case there is enough coverage elsewhere but we could change the test to avoid this problem.

t.equal(map.isMoving(), true);
});
map.on('dragstart', () => {
t.equal(map.isMoving(), true);
});

map.on('dragend', () => {
t.equal(map.isMoving(), false);
});
map.on('moveend', () => {
t.equal(map.isMoving(), false);
map.remove();
t.end();
});
Expand All @@ -65,12 +71,18 @@ test('Map#isMoving returns true when drag rotating', (t) => {
// Prevent inertial rotation.
t.stub(browser, 'now').returns(0);

map.on('movestart', () => {
t.equal(map.isMoving(), true);
});
map.on('rotatestart', () => {
t.equal(map.isMoving(), true);
});

map.on('rotateend', () => {
t.equal(map.isMoving(), false);
});
map.on('moveend', () => {
t.equal(map.isMoving(), false);
map.remove();
t.end();
});
Expand Down
29 changes: 29 additions & 0 deletions test/unit/ui/map_events.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -601,3 +601,32 @@ test(`Map#on click fires subsequent click event if there is no corresponding mou
map.remove();
t.end();
});

test("Map#isMoving() returns false in mousedown/mouseup/click with no movement", (t) => {
const map = createMap(t, {interactive: true, clickTolerance: 4});
let mousedown, mouseup, click;
map.on('mousedown', () => { mousedown = map.isMoving(); });
map.on('mouseup', () => { mouseup = map.isMoving(); });
map.on('click', () => { click = map.isMoving(); });

const canvas = map.getCanvas();
const MouseEvent = window(canvas).MouseEvent;

canvas.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, clientX: 100, clientY: 100}));
t.equal(mousedown, false);
map._renderTaskQueue.run();
t.equal(mousedown, false);

canvas.dispatchEvent(new MouseEvent('mouseup', {bubbles: true, clientX: 100, clientY: 100}));
t.equal(mouseup, false);
map._renderTaskQueue.run();
t.equal(mouseup, false);

canvas.dispatchEvent(new MouseEvent('click', {bubbles: true, clientX: 100, clientY: 100}));
t.equal(click, false);
map._renderTaskQueue.run();
t.equal(click, false);

map.remove();
t.end();
});