From 092df87fc7abd2899199b98a119854b872c5062c Mon Sep 17 00:00:00 2001 From: Dan Fabulich Date: Sun, 28 Nov 2021 23:38:03 -0800 Subject: [PATCH] Move the top-most grid window into the visual viewport on resize/scroll Fixes #50. When the iOS soft keyboard opens, the "resize" event doesn't fire on the window (the "layout viewport" hasn't changed) but the the "visual viewport" resizes. https://developer.mozilla.org/en-US/docs/Web/API/Visual_Viewport_API https://developers.google.com/web/updates/2017/09/visual-viewport-api In this commit, we respond to changes in the size of the visual viewport, moving the top-most grid window down to the top of the visual viewport. Since the vast majority of games have just one grid window at the top of the screen, this does what we want. I set this code to only slide around the top-most grid window when the game UI sets spacing to 0, because non-zero spacing introduces a visual border around the windows, with the background color coming from the #gameport, but if we slide a grid window down on top of a buffer window, it can't bring its spacing with it. (This is OK by me, because I'm really only doing this for Parchment's sake, which does set spacing 0. Lectrote uses spacing 4, which is fine; it won't be affected by these changes. I tested this code with spacing 4, showing that it has no effect, and with spacing 0, on iOS 15.) I'd previously experimented with re-measuring the window and using the visual viewport's dimensions for the computation, but we don't actually want to resize the primary buffer window smaller; that will screw up its scroll position, and, worse, it will look really weird to the user. Furthermore, we can't actually trust visualViewport.height on iOS 15; we can know where the top of the viewport is, but the 20px URL bar may overlay the bottom. See https://github.com/erkyrath/glkote/issues/50 for a screenshot of the buggy results. --- glkote.css | 2 ++ glkote.js | 16 +++++++++++++++- sample-demo.html | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/glkote.css b/glkote.css index f401952..b146d9b 100644 --- a/glkote.css +++ b/glkote.css @@ -63,6 +63,7 @@ font-size: 15px; line-height: 1.4; padding: 6px 10px 6px 10px; + z-index: 1; } .BufferLine { @@ -75,6 +76,7 @@ font-family: monaco, andale mono, lucidatypewriter, courier, courier new, monospace; /* necessary! */ font-size: 14px; padding: 6px 10px 6px 10px; + z-index: 2; } .GridLine { diff --git a/glkote.js b/glkote.js index d065c76..5e51f08 100644 --- a/glkote.js +++ b/glkote.js @@ -623,6 +623,16 @@ function create_resize_sensors() { This is one reason evhan_doc_resize() has debouncing logic. */ shrinkel.on('scroll', evhan); expandel.on('scroll', evhan); + + /* Move the top-most grid window into the viewport + when the iOS virtual keyboard pops up. */ + if (window.visualViewport) { + var visualViewportHandler = function () { + $('.topgrid').css({ top: visualViewport.offsetTop }); + } + visualViewport.addEventListener('resize', visualViewportHandler); + visualViewport.addEventListener('scroll', visualViewportHandler); + } } /* This function becomes GlkOte.update(). The game calls this to update @@ -923,9 +933,13 @@ function accept_one_window(arg) { if (win.type == 'graphics') typeclass = 'GraphicsWindow'; var rockclass = 'WindowRock_' + arg.rock; + var topclass = ""; + if (win.type == 'grid' && arg.top === 0 && current_metrics.inspacingy === 0) { + topclass = " topgrid"; + } frameel = $('
', { id: dom_prefix+'window'+arg.id, - 'class': 'WindowFrame HasNoInputField ' + typeclass + ' ' + rockclass }); + 'class': 'WindowFrame HasNoInputField ' + typeclass + ' ' + rockclass + topclass}); frameel.data('winid', arg.id); frameel.on('mousedown', arg.id, evhan_window_mousedown); if (perform_paging && win.type == 'buffer') diff --git a/sample-demo.html b/sample-demo.html index 2752390..efa6b11 100644 --- a/sample-demo.html +++ b/sample-demo.html @@ -79,7 +79,7 @@ Game = { accept: SampleDemo.game_accept, detect_external_links: 'match', - spacing: 4 + spacing: 0 };