From c0b10655d3cf871a39d925cf07bb731dcac5f54a Mon Sep 17 00:00:00 2001 From: Dannii Willis Date: Thu, 24 Oct 2024 10:40:54 +1000 Subject: [PATCH] Work around iOS Safari's substantial delay of the `visualViewport:resize` event See https://github.com/curiousdannii/parchment/issues/134 and https://stackoverflow.com/q/72747030/2854284 When the TextInput loses focus we now immediately set the gameport to the height of window.innerHeight, which hopefully will be accurate enough Also remove the old iOS 15 fix --- src/glkote/web/core.css | 8 -------- src/glkote/web/input.ts | 7 +++++++ src/glkote/web/metrics.ts | 26 +++++++++----------------- src/glkote/web/web.ts | 2 +- src/glkote/web/windows.ts | 4 ++-- 5 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/glkote/web/core.css b/src/glkote/web/core.css index 38ab7c9..7aab36d 100644 --- a/src/glkote/web/core.css +++ b/src/glkote/web/core.css @@ -64,12 +64,4 @@ div#gameport { position: absolute; top: 0; visibility: hidden; -} - -/* iOS 15: When the virtual keyboard is active, the URL bar is not correctly accounted for in visualViewport.height - https://bugs.webkit.org/show_bug.cgi?id=229876 - Should be fixed in iOS 15.1 - Account for it by adding some padding to the #gameport */ -.ios15fix { - padding-bottom: 55px; } \ No newline at end of file diff --git a/src/glkote/web/input.ts b/src/glkote/web/input.ts index 40e1ff9..30628da 100644 --- a/src/glkote/web/input.ts +++ b/src/glkote/web/input.ts @@ -54,6 +54,13 @@ export class TextInput { } private onblur() { + // If this input lost focus and no other input gained focus, then tell the metrics to resize the gameport + // This is to support iOS better, which delays its `visualViewport:resize` event significantly (~700ms) + const input_is_active = document.activeElement?.tagName === 'INPUT' + if (!input_is_active) { + this.window.manager.glkote.metrics_calculator.set_gameport_height(window.innerHeight) + } + scroll_window() } diff --git a/src/glkote/web/metrics.ts b/src/glkote/web/metrics.ts index 784fcb0..8e55583 100644 --- a/src/glkote/web/metrics.ts +++ b/src/glkote/web/metrics.ts @@ -34,8 +34,6 @@ function metrics_differ(newmetrics: protocol.NormalisedMetrics, oldmetrics: prot oldmetrics.width !== newmetrics.width) } -const ios15_0 = /(iPad; CPU|iPhone) OS 15_0/i.test(navigator.userAgent) - export default class Metrics { // Shares the current_metrics and DOM of WebGlkOte private metrics: protocol.NormalisedMetrics @@ -181,26 +179,20 @@ export default class Metrics { }, 200, {leading: false}) on_visualViewport_resize = () => { + // The iOS virtual keyboard does not change the gameport height, but it does change the viewport + // Try to account for this by setting the gameport to the viewport height + this.set_gameport_height(visualViewport!.height) + } + + /** Update the gameport height and then send new metrics */ + set_gameport_height(height: number) { // Don't do anything if the window is pinch zoomed if (is_pinch_zoomed()){ return } - // The iOS virtual keyboard does not change the gameport height, but it does change the viewport - // Try to account for this by setting the gameport to the viewport height - const gameport = this.glkote.dom.gameport() - const input_is_active = document.activeElement?.tagName === 'INPUT' - - // But first... - // iOS 15: When the virtual keyboard is active, the URL bar is not correctly accounted for in visualViewport.height - // https://bugs.webkit.org/show_bug.cgi?id=229876 - // Should be fixed in iOS 15.1 - // Account for it by adding some padding to the #gameport - if (ios15_0) { - gameport.toggleClass('ios15fix', input_is_active) - } - // And then set the outer height to the viewport height, accounting for any padding or margin - gameport.outerHeight(visualViewport!.height, true) + // We set the outer height to account for any padding or margin + this.glkote.dom.gameport().outerHeight(height, true) // Safari might have scrolled weirdly, so try to put it right window.scrollTo(0, 0) diff --git a/src/glkote/web/web.ts b/src/glkote/web/web.ts index 5e74058..d0aee1d 100644 --- a/src/glkote/web/web.ts +++ b/src/glkote/web/web.ts @@ -47,7 +47,7 @@ export default class WebGlkOte extends GlkOte.GlkOteBase implements GlkOte.GlkOt prefix: '', windowport_id: 'windowport', }) - private metrics_calculator: Metrics + metrics_calculator: Metrics private showing_error = false private showing_loading = true private transcript_recorder?: TranscriptRecorder diff --git a/src/glkote/web/windows.ts b/src/glkote/web/windows.ts index 418d050..a3f243c 100644 --- a/src/glkote/web/windows.ts +++ b/src/glkote/web/windows.ts @@ -723,7 +723,7 @@ export default class Windows extends Map { blorb?: Blorb // Note will be set after this is constructed, in WebGlkOte.init canvasResizeObserver?: ResizeObserver // Will only be created if the browser's ResizeObserver supports devicePixelContentBoxSize private dom: DOM - private glkote: WebGlkOte + glkote: WebGlkOte history: string[] = [] private metrics: protocol.NormalisedMetrics send_event: EventFunc @@ -892,7 +892,7 @@ export default class Windows extends Map { top: update.top, width: update.width, }) - .toggleClass('hidden', update.hidden) + .toggleClass('hidden', !!update.hidden) if (win.type === 'buffer') { win.scroll_to_bottom(true) }