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

Bubble scroll events when scrolling beyond the bounds of the viewport #1449

Closed
wants to merge 1 commit into from
Closed

Bubble scroll events when scrolling beyond the bounds of the viewport #1449

wants to merge 1 commit into from

Conversation

ghost
Copy link

@ghost ghost commented May 14, 2018

Closes #1343

The changes in this PR should allow the page to scroll when the terminal reaches it's top or bottom. If the terminal is scrolled then the events are canceled otherwise they bubble up.

src/Terminal.ts Outdated
this.viewport.onTouchMove(ev);
return this.cancel(ev);
if (<any>this.viewport.onTouchMove(ev) === false) {
return this.cancel(ev);
Copy link
Author

Choose a reason for hiding this comment

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

tsc wouldn't allow me to do a strict equality check without upcasting: cannot void === boolean. I'm not sure that this is the best way to handle it.

Copy link
Member

Choose a reason for hiding this comment

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

You would probably just want to changing onTouchMove/onWheel to always return a boolean

src/Viewport.ts Outdated
* Calls onScroll synchronously and determines whether the terminal was
* actually scrolled.
*/
private scrollSync(amount: number): boolean {
Copy link
Author

Choose a reason for hiding this comment

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

This seems to be the best way to avoid code duplication as we can't really prevent the scroll event because changing _viewportElement.scrollTop is necessary? I don't really like the method name either, feedback please 😕

src/Viewport.ts Outdated
ev.preventDefault();
if (this.scrollSync(amount)) {
// Prevent the page from scrolling when the terminal scrolls
ev.preventDefault();
Copy link
Author

Choose a reason for hiding this comment

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

Is the ev.preventDefault() statement meant for third party consumers? The terminal.cancel method was always being called after these handlers.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah feel free to remove this as it's handled in Terminal.cancel.

src/Terminal.ts Outdated
this.viewport.onTouchMove(ev);
return this.cancel(ev);
if (<any>this.viewport.onTouchMove(ev) === false) {
return this.cancel(ev);
Copy link
Member

Choose a reason for hiding this comment

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

You would probably just want to changing onTouchMove/onWheel to always return a boolean

src/Viewport.ts Outdated
@@ -99,7 +102,12 @@ export class Viewport implements IViewport {
* terminal to scroll to it.
* @param ev The scroll event.
*/
private _onScroll(ev: Event): void {
private _onScroll(ev?: Event): void {
// Prevent duplicate event handling
Copy link
Member

Choose a reason for hiding this comment

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

Oh the double call is prevented here, this sort of state between async callbacks should be avoided at all costs imo. What's the duplicate event handling we're concerned about?

Copy link
Author

Choose a reason for hiding this comment

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

OnTouchMove/onWheel both modify _viewportElement.scrollTop which causes the scroll event to fire on _viewportElement during the next event loop. If we call onScroll manually on the current event loop to determine whether the viewport was actually scrolled then the scroll event will still fire thus onScroll will get called a second time. We could remove this check if we don't modify _viewportElement.scrollTop but it's necessary?

Copy link
Author

Choose a reason for hiding this comment

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

nvm that, I missed what you were saying and now it doesn't seem necessary to prevent the handling.

src/Viewport.ts Outdated
@@ -179,13 +202,16 @@ export class Viewport implements IViewport {
* Handles the touchmove event, scrolling the viewport if the position shifted.
* @param ev The touch event.
*/
public onTouchMove(ev: TouchEvent): void {
let deltaY = this._lastTouchY - ev.touches[0].pageY;
public onTouchMove(ev: TouchEvent): boolean | void {
Copy link
Member

Choose a reason for hiding this comment

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

Can't we just return a boolean on this and then leave event cancellation up to this part?

xterm.js/src/Terminal.ts

Lines 1091 to 1109 in 7a9f291

// allow wheel scrolling in
// the shell for example
on(el, 'wheel', (ev: WheelEvent) => {
if (this.mouseEvents) return;
this.viewport.onWheel(ev);
return this.cancel(ev);
});
on(el, 'touchstart', (ev: TouchEvent) => {
if (this.mouseEvents) return;
this.viewport.onTouchStart(ev);
return this.cancel(ev);
});
on(el, 'touchmove', (ev: TouchEvent) => {
if (this.mouseEvents) return;
this.viewport.onTouchMove(ev);
return this.cancel(ev);
});

@Tyriar Tyriar changed the title Improve scrolling experience Bubble scroll events when scrolling beyond the bounds of the viewport May 14, 2018
@Tyriar Tyriar self-assigned this May 14, 2018
src/Viewport.ts Outdated
private scrollSync(amount: number): boolean {
const oldYDisp = this._terminal.buffer.ydisp;
this._viewportElement.scrollTop += amount;
this._onScroll();
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this._onScroll() shouldn't be needed in this function as it will fire when this._viewportElement.scrollTop changes?

Copy link
Author

@ghost ghost May 14, 2018

Choose a reason for hiding this comment

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

The event handler delegation is complicating things.

No, It wouldn't be needed if we could determine whether or not to cancel the events in onTouchMove/onWheel without it and without code duplication. The only other way that I can tell is to copy code from the _onScroll and _terminal.scrollLines functions to scrollSync but onScroll would still be called anyway.

The problem is that you can't check to see if buffer.ydisp has changed until after onScroll executes but that won't happen before returning from the original event handlers without calling it manually.

Copy link
Author

Choose a reason for hiding this comment

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

And maybe I'm over complicating things 😜

I haven't exactly tested to see if it this._onScroll is called immediately (during the current event loop) upon changing _viewportElement.scrollTop. However, I wouldn't think so... lol

I'll check the spec and get back to you on that.

Copy link
Author

Choose a reason for hiding this comment

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

I didn't find exactly what I was looking for in the specs however if we're to believe this mdn article about the event loop then when the event is triggered by modifying _viewportElement.scrollTop a new message is added to the message queue but won't be processed until the current message is processed to it's completion which is the end of the current event loop. See "Run-to-completion" in the linked article.

Copy link
Member

Choose a reason for hiding this comment

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

Can we bounds check this._viewportElement.scrollTop? If it's 0 we don't need to scroll, I think there's a way to check if it's at the end as well.

Copy link
Author

Choose a reason for hiding this comment

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

The bounds checking should be performed on the buffer and not _viewportElement.scrollTop? https://github.com/xtermjs/xterm.js/blob/master/src/Terminal.ts#L1213

We would be duplicating terminal.scrollLines?

Copy link
Member

Choose a reason for hiding this comment

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

@pro-src that would probably be better, as long as no sync issues occur.

@ghost
Copy link
Author

ghost commented May 14, 2018

@Tyriar updated.

return this.cancel(ev);
if (this.viewport.onWheel(ev)) {
return this.cancel(ev);
}
});

on(el, 'touchstart', (ev: TouchEvent) => {
Copy link
Author

Choose a reason for hiding this comment

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

The touchstart event is still always canceled.

@xtermjs xtermjs deleted a comment from coveralls May 15, 2018
@Tyriar
Copy link
Member

Tyriar commented May 15, 2018

I just tested the PR on Ubuntu 18.04/Chrome and all the scroll events are bubbling up, even when the terminal is being scrolled

@ghost
Copy link
Author

ghost commented May 15, 2018

Thanks for testing. I'll pull in changes from master and push an update when it's working. I'm writing tests for this as well.

@ghost
Copy link
Author

ghost commented May 18, 2018

I'm kinda stuck... Any ideas as to what's causing the issue with the PR? The tests pass but it still doesn't work.

@Tyriar
Copy link
Member

Tyriar commented May 18, 2018

@pro-src I don't think there's much testing in this area. Can you repro the issue I was seeing? Could be your browser if not?

@Tyriar Tyriar added the work-in-progress Do not merge label May 21, 2018
@ghost
Copy link
Author

ghost commented May 24, 2018

@pro-src I don't think there's much testing in this area. Can you repro the issue I was seeing? Could be your browser if not?

@Tyriar, I am able to reproduce that issue but the only in-browser testing I've done has been on unsupported mobile browsers. The node tests that I wrote are passing though. I don't know that I'll revisit this anytime soon.

@Tyriar
Copy link
Member

Tyriar commented Sep 8, 2018

Closing for housekeeping purposes, the PR is archived if we need the diff in the future.

@Tyriar Tyriar closed this Sep 8, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
work-in-progress Do not merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant