web: Allow context menu to work on iOS using PointerEvents (#9598)

Co-authored-by: nosamu <71368227+n0samu@users.noreply.github.com>
This commit is contained in:
Daniel Jacobs 2023-02-23 05:47:08 -05:00 committed by GitHub
parent e17b154d47
commit bcbf8c9108
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 72 additions and 9 deletions

View File

@ -133,6 +133,9 @@ export class RufflePlayer extends HTMLElement {
// Whether this device is a touch device. // Whether this device is a touch device.
// Set to true when a touch event is encountered. // Set to true when a touch event is encountered.
private isTouch = false; private isTouch = false;
// Whether this device sends contextmenu events.
// Set to true when a contextmenu event is seen.
private contextMenuSupported = false;
// The effective config loaded upon `.load()`. // The effective config loaded upon `.load()`.
private loadedConfig: Required<Config> = DEFAULT_CONFIG; private loadedConfig: Required<Config> = DEFAULT_CONFIG;
@ -148,6 +151,7 @@ export class RufflePlayer extends HTMLElement {
private _cachedDebugInfo: string | null = null; private _cachedDebugInfo: string | null = null;
private isExtension = false; private isExtension = false;
private longPressTimer: ReturnType<typeof setTimeout> | null = null;
/** /**
* Triggered when a movie metadata has been loaded (such as movie width and height). * Triggered when a movie metadata has been loaded (such as movie width and height).
@ -219,8 +223,21 @@ export class RufflePlayer extends HTMLElement {
this.preloader = this.shadow.getElementById("preloader")!; this.preloader = this.shadow.getElementById("preloader")!;
this.contextMenuElement = this.shadow.getElementById("context-menu")!; this.contextMenuElement = this.shadow.getElementById("context-menu")!;
window.addEventListener("pointerdown", this.pointerDown.bind(this));
this.addEventListener("contextmenu", this.showContextMenu.bind(this)); this.addEventListener("contextmenu", this.showContextMenu.bind(this));
this.addEventListener("pointerdown", this.pointerDown.bind(this)); this.container.addEventListener(
"pointerdown",
this.startLongPressTimer.bind(this)
);
this.container.addEventListener(
"pointerup",
this.checkLongPress.bind(this)
);
this.container.addEventListener(
"pointercancel",
this.clearLongPressTimer.bind(this)
);
this.addEventListener( this.addEventListener(
"fullscreenchange", "fullscreenchange",
this.fullScreenChange.bind(this) this.fullScreenChange.bind(this)
@ -229,7 +246,6 @@ export class RufflePlayer extends HTMLElement {
"webkitfullscreenchange", "webkitfullscreenchange",
this.fullScreenChange.bind(this) this.fullScreenChange.bind(this)
); );
window.addEventListener("click", this.hideContextMenu.bind(this));
this.instance = null; this.instance = null;
this.onFSCommand = null; this.onFSCommand = null;
@ -770,8 +786,6 @@ export class RufflePlayer extends HTMLElement {
} }
private pointerDown(event: PointerEvent): void { private pointerDown(event: PointerEvent): void {
// Give option to disable context menu when touch support is being used
// to avoid a long press triggering the context menu. (#1972)
if (event.pointerType === "touch" || event.pointerType === "pen") { if (event.pointerType === "touch" || event.pointerType === "pen") {
this.isTouch = true; this.isTouch = true;
} }
@ -877,6 +891,8 @@ export class RufflePlayer extends HTMLElement {
window.open(RUFFLE_ORIGIN, "_blank"); window.open(RUFFLE_ORIGIN, "_blank");
}, },
}); });
// Give option to disable context menu when touch support is being used
// to avoid a long press triggering the context menu. (#1972)
if (this.isTouch) { if (this.isTouch) {
items.push(null); items.push(null);
items.push({ items.push({
@ -887,8 +903,52 @@ export class RufflePlayer extends HTMLElement {
return items; return items;
} }
private showContextMenu(e: MouseEvent): void { private clearLongPressTimer(): void {
e.preventDefault(); if (this.longPressTimer) {
clearTimeout(this.longPressTimer);
this.longPressTimer = null;
}
}
private startLongPressTimer(): void {
const longPressTimeout = 800;
this.clearLongPressTimer();
this.longPressTimer = setTimeout(
() => this.clearLongPressTimer(),
longPressTimeout
);
}
private checkLongPress(event: PointerEvent): void {
if (this.longPressTimer) {
this.clearLongPressTimer();
// The pointerType condition is to ensure right-click does not trigger
// a context menu the wrong way the first time you right-click,
// before contextMenuSupported is set.
} else if (
!this.contextMenuSupported &&
event.pointerType !== "mouse"
) {
this.showContextMenu(event);
}
}
private showContextMenu(event: MouseEvent | PointerEvent): void {
event.preventDefault();
if (event.type === "contextmenu") {
this.contextMenuSupported = true;
window.addEventListener("click", this.hideContextMenu.bind(this), {
once: true,
});
} else {
window.addEventListener(
"pointerup",
this.hideContextMenu.bind(this),
{ once: true }
);
event.stopPropagation();
}
if ( if (
this.loadedConfig.contextMenu === false || this.loadedConfig.contextMenu === false ||
@ -934,7 +994,10 @@ export class RufflePlayer extends HTMLElement {
this.contextMenuElement.appendChild(menuItem); this.contextMenuElement.appendChild(menuItem);
if (enabled !== false) { if (enabled !== false) {
menuItem.addEventListener("click", onClick); menuItem.addEventListener(
this.isTouch ? "pointerup" : "click",
onClick
);
} else { } else {
menuItem.classList.add("disabled"); menuItem.classList.add("disabled");
} }
@ -948,8 +1011,8 @@ export class RufflePlayer extends HTMLElement {
this.contextMenuElement.style.display = "block"; this.contextMenuElement.style.display = "block";
const rect = this.getBoundingClientRect(); const rect = this.getBoundingClientRect();
const x = e.clientX - rect.x; const x = event.clientX - rect.x;
const y = e.clientY - rect.y; const y = event.clientY - rect.y;
const maxX = rect.width - this.contextMenuElement.clientWidth - 1; const maxX = rect.width - this.contextMenuElement.clientWidth - 1;
const maxY = rect.height - this.contextMenuElement.clientHeight - 1; const maxY = rect.height - this.contextMenuElement.clientHeight - 1;