From d55df2e630716908c9eb6f7536d1dbeacac01bd5 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 16 Nov 2021 16:51:34 +1100 Subject: [PATCH] =?UTF-8?q?fix(web):=20remove=20canvas=20use=20for=20iOS?= =?UTF-8?q?=20compatibility=20=F0=9F=8D=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #5831. Cherry-pick of #5915. iOS 15 has a significant crashing bug whereby use of a canvas element completely crashes the browser in some contexts. In particular, we have observed this when using a WKWebView in a keyboard extension without 'Allow Full Access' switched on. Keyman uses canvas element to draw a nice looking key preview (key tip) on iPhones. This is a critical issue for Keyman for iPhone. Keyman for iPad is not affected because the iPad version does not use key previews. The resolution here is to remove use of the canvas drawing for key tips and use a simplified pure HTML/CSS shape. I have not conditioned this fix on platform; I have currently opted to apply this to all platforms. This issue will be reported to Apple for resolution. The issue applies so far to iOS 15.0, 15.1. --- web/source/osk/visualKeyboard.ts | 162 +++++++----------------- web/source/resources/osk/kmwosk.css | 184 ++++++++++++++++++---------- 2 files changed, 160 insertions(+), 186 deletions(-) diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index f99ca9265d..0cbc0bfcbc 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -647,7 +647,7 @@ namespace com.keyman.osk { subkeyDelayTimer: number; popupDelay: number = 500; menuEvent: KeyElement; // Used by embedded-mode. - keytip: {key: KeyElement, state: boolean, element?: HTMLDivElement}; + keytip: {key: KeyElement, state: boolean, element?, tip?, cap?: HTMLDivElement, label?: HTMLSpanElement}; popupCallout: HTMLDivElement; //#region OSK constructor and helpers @@ -1003,7 +1003,7 @@ namespace com.keyman.osk { layerChangeHandler: text.SystemStoreMutationHandler = function(this: VisualKeyboard, source: text.MutableSystemStore, newValue: string) { - // This handler is also triggered on state-key state changes (K_CAPS) that + // This handler is also triggered on state-key state changes (K_CAPS) that // may not actually change the layer. if(this) { this._UpdateVKShiftStyle(); @@ -2566,20 +2566,8 @@ namespace com.keyman.osk { // Create and display the preview if(on && !popup) { - var y0 = dom.Utils.getAbsoluteY(oskManager._Box), - h0 = oskManager._Box.offsetHeight, - xLeft = dom.Utils.getAbsoluteX(key), - xTop = dom.Utils.getAbsoluteY(key), - xWidth = key.offsetWidth, - xHeight = key.offsetHeight, - kc = key.firstChild, - kcs = kc.style, - kts = tip.element.style, - ktLabel = tip.element.childNodes[1], - ktls = ktLabel.style, - edge = 0, - canvas = tip.element.firstChild, - previewFontScale = 1.8; + var kc = key.firstChild, + kts = tip.element.style; // Find key text element for(var i=0; i window.innerWidth - xWidth - xOverflow) { - edge = 1; + this.keytip.cap.style.left = (canvasWidth - xWidth) + 'px'; xLeft -= xOverflow; + } else { + this.keytip.cap.style.left = ((canvasWidth - xWidth) / 2) + 'px'; } + kts.left=(xLeft - xOverflow) + 'px'; + // For now, should only be true (in production) when keyman.isEmbedded == true. let constrainPopup = keyman.isEmbedded; @@ -2639,16 +2632,18 @@ namespace com.keyman.osk { let bottomY = parseInt(cs.bottom, 10); let tipHeight = parseInt(cs.height, 10); - let delta = 0; - if(tipHeight + bottomY > oskHeight && constrainPopup) { - delta = tipHeight + bottomY - oskHeight; - canvas.height = canvas.height - delta; - kts.height = canvas.height + 'px'; + this.keytip.cap.style.width = xWidth + 'px'; + this.keytip.tip.style.height = (canvasHeight / 2) + 'px'; + this.keytip.cap.style.top = (canvasHeight / 2) + 'px'; + this.keytip.cap.style.height = (canvasHeight / 2 - 1) + 'px'; + + if(constrainPopup && tipHeight + bottomY > oskHeight) { + const delta = tipHeight + bottomY - oskHeight; + kts.height = (canvasHeight-delta) + 'px'; + const hx = Math.max(0, (canvasHeight-delta)-(canvasHeight/2)); + this.keytip.cap.style.height = hx + 'px'; } - this.drawPreview(canvas, xWidth, xHeight, edge, delta); - - kts.left=(xLeft - xOverflow) + 'px'; kts.display = 'block'; } else { // Hide the key preview tip.element.style.display = 'none'; @@ -2659,82 +2654,6 @@ namespace com.keyman.osk { tip.state = on; }; - /** - * Draw key preview in element using CANVAS - * @param {Object} canvas CANVAS element - * @param {number} w width of touched key, px - * @param {number} h height of touched key, px - * @param {number} edge -1 left edge, 1 right edge, else 0 - */ - drawPreview(canvas: HTMLCanvasElement, w: number, h: number, edge: number, delta?: number) { - let util = com.keyman.singleton.util; - let device = util.device; - - delta = delta || 0; - - var ctx = canvas.getContext('2d'), dx = (canvas.width - w)/2, hMax = canvas.height + delta, - w0 = 0, w1 = dx, w2 = w + dx, w3 = w + 2 * dx, - h1 = 0.5 * hMax, h2 = 0.6 * hMax, h3 = hMax, r = 8; - - let hBoundedMax = canvas.height; - - h2 = h2 > hBoundedMax ? hBoundedMax : h2; - h3 = hMax > hBoundedMax ? hBoundedMax : h3; - - if(device.OS == 'Android') { - r = 3; - } - - // Adjust the preview shape at the edge of the keyboard - switch(edge) { - case -1: - w1 -= dx; - w2 -= dx; - break; - case 1: - w1 += dx; - w2 += dx; - break; - } - - // Clear the canvas - ctx.clearRect(0,0,canvas.width,canvas.height); - - // Define appearance of preview (cannot be done directly in CSS) - if(device.OS == 'Android') { - var wx=(w1+w2)/2; - w1 = w2 = wx; - } - ctx.fillStyle = device.styles.popupCanvasBackgroundColor; - ctx.lineWidth = 1; - ctx.strokeStyle = '#cccccc'; - - // Draw outline - ctx.save(); - ctx.beginPath(); - ctx.moveTo(w0+r,0); - ctx.arcTo(w3,0,w3,r,r); - if(device.OS == 'Android') { - ctx.arcTo(w3,h1,w2,h2,r); - ctx.arcTo(w2,h2,w1,h2,r); - } else { - let lowerR = 0; - if(h3 > h2) { - lowerR = h3-h2 > r ? r : h3-h2; - } - ctx.arcTo(w3,h1,w2,h2,r); - ctx.arcTo(w2,h2,w2-lowerR,h3,lowerR); - ctx.arcTo(w2,h3,w1,h3,lowerR); - ctx.arcTo(w1,h3,w1,h2-lowerR,lowerR); - } - ctx.arcTo(w1,h2,w0,h1-r,r); - ctx.arcTo(w0,h1,w0,r,r); - ctx.arcTo(w0,0,w0+r,0,r); - ctx.fill(); - ctx.stroke(); - ctx.restore(); - }; - /** * Create a key preview element for phone devices */ @@ -2754,10 +2673,15 @@ namespace com.keyman.osk { // The following style is critical, so do not rely on external CSS tipElement.style.pointerEvents='none'; + tipElement.style.display='none'; - // Add CANVAS element for outline and SPAN for key label - tipElement.appendChild(util._CreateElement('canvas')); - tipElement.appendChild(util._CreateElement('span')); + tipElement.appendChild(this.keytip.tip = document.createElement('div')); + tipElement.appendChild(this.keytip.cap = document.createElement('div')); + this.keytip.tip.appendChild(this.keytip.label = document.createElement('span')); + + this.keytip.tip.className = 'kmw-keytip-tip'; + this.keytip.cap.className = 'kmw-keytip-cap'; + this.keytip.label.className = 'kmw-keytip-label'; } // Always append to _Box (since cleared during OSK Load) diff --git a/web/source/resources/osk/kmwosk.css b/web/source/resources/osk/kmwosk.css index 06ee07820d..c72e05265b 100644 --- a/web/source/resources/osk/kmwosk.css +++ b/web/source/resources/osk/kmwosk.css @@ -5,13 +5,13 @@ /* kmwosk.css: main CSS for keymanweb on-screen keyboard and other objects. -*/ +*/ @font-face { font-family: SpecialOSK; font-style: normal; font-weight: normal; - src: url('keymanweb-osk.ttf') format('truetype'); + src: url('keymanweb-osk.ttf') format('truetype'); } /* kmw-key-square applies only to OSK key elements, kmw-key-square-ex applies only to popup key elements */ @@ -54,7 +54,7 @@ .phone.ios .kmw-key-layer-group {background-color: #cfd3d9} .phone.ios .kmw-key {border: none; border-bottom: solid 1px #8a8d90; box-shadow:none; border-radius: 5px;} -.phone.ios .kmw-key.kmw-key-default {color:#000;background-color:#fdfdfe;} +.phone.ios .kmw-key.kmw-key-default {color:#000;background-color:#fdfdfe;} .phone.ios .kmw-key.kmw-key-shift, .phone.ios .kmw-key.kmw-key-special {color:#fff;background-color:#b2b9c5;} .phone.ios .kmw-key.kmw-key-shift-on, @@ -76,10 +76,10 @@ } .ios .kmw-banner-bar { - background-color: #cfd3d9; + background-color: #cfd3d9; position: absolute; - top: 6px; - height: 100%; + top: 6px; + height: 100%; width: 100%; } @@ -112,13 +112,13 @@ /* Probably best to make this its own CSS that can be optionally included? */ @media (prefers-color-scheme: dark) { .ios .kmw-banner-bar { - background-color: #0f1319; + background-color: #0f1319; } .ios .kmw-banner-bar .kmw-banner-separator { border-left: solid 1px #8a8d90 } - + .ios .kmw-suggestion-text { color: #fff; } @@ -126,10 +126,10 @@ .phone.ios.kmw-osk-frame, .tablet.ios.kmw-osk-frame { background-color: #0f1319; - } + } } -.phone.android .kmw-key-layer-group {background-color: #333;} +.phone.android .kmw-key-layer-group {background-color: #333;} .phone.android .kmw-key {border: none; border-bottom: solid 1px #8a8d90; box-shadow:none; border-radius: 3px;} .phone.android .kmw-key.kmw-key-default {color:#fff;background-color:#777;} .phone.android .kmw-key.kmw-key-shift, @@ -146,11 +146,11 @@ .phone.android.kmw-osk-frame { background-color: #222;} .phone.android .kmw-banner-bar { - background-color: #222; - position: absolute; + background-color: #222; + position: absolute; top: 4px; - height: 100%; - width: 100%; + height: 100%; + width: 100%; } .phone.android .kmw-banner-bar .kmw-suggest-option { @@ -172,25 +172,25 @@ .tablet .kmw-key-layer{position:fixed;left:0;bottom:0;width:100%;height:100%;margin:0;padding:0;background-color:transparent;overflow:hidden;} .tablet .kmw-key-row {position:fixed;width:100%;height:20%;margin:0;padding:0;border:none;overflow:hidden;} .tablet .kmw-key-square {position:fixed; display:inline-block; height:100%; max-height:100%; overflow:hidden; margin:0 0 0 0; z-index: 10000; padding:0 0 0 0; background-color:transparent;cursor:default; border:none;-webkit-tap-highlight-color:rgba(0,0,0,0)} -.tablet .kmw-key-square-ex {display:inline-block;box-sizing:border-box;margin:0 0 0 5px;z-index:10001; padding:0; background-color:transparent; cursor:default;} +.tablet .kmw-key-square-ex {display:inline-block;box-sizing:border-box;margin:0 0 0 5px;z-index:10001; padding:0; background-color:transparent; cursor:default;} .tablet .kmw-key {display:block;position:fixed;margin:0px;border-radius:8px;text-align:center;box-sizing:border-box;overflow:visible; border:solid 2px #999999; box-shadow: 0px -1px 1px 0px rgba(0,0,0,1) inset; } .tablet .kmw-key-label{position:absolute;left:7%;top:2%;font:0.5em Arial;color:#aaa;background-color:transparent;z-index:10000;} -.tablet .kmw-key-text{position:relative;left:0;top:0;-webkit-user-select:none;} +.tablet .kmw-key-text{position:relative;left:0;top:0;-webkit-user-select:none;} .tablet.ios .kmw-key-layer-group {background-color: #cfd3d9} .tablet.ios .kmw-key {border: none; border-bottom: solid 1px #8a8d90; box-shadow:none; border-radius: 5px;} -.tablet.ios .kmw-key.kmw-key-default {color:#000;background-color:#fdfdfe;} +.tablet.ios .kmw-key.kmw-key-default {color:#000;background-color:#fdfdfe;} .tablet.ios .kmw-key.kmw-key-shift, .tablet.ios .kmw-key.kmw-key-special {color:#fff;background-color:#b2b9c5;} .tablet.ios .kmw-key.kmw-key-shift-on, .tablet.ios .kmw-key.kmw-key-special-on {color:#fff;background-color:#88f;} .tablet.ios .kmw-key.kmw-key-touched {background-color:#447;} .tablet.ios .kmw-suggest-option.kmw-suggest-touched {background-color:#88f;} -.tablet.ios .kmw-key-deadkey{color:#048204;background-color:#fdfdfe;} +.tablet.ios .kmw-key-deadkey{color:#048204;background-color:#fdfdfe;} /* Probably best to make this its own CSS that can be optionally included? */ @media (prefers-color-scheme: dark) { @@ -206,7 +206,7 @@ .tablet .kmw-key-row {-webkit-touch-callout:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:rgba(0,0,0,0);} -.tablet.android .kmw-key-layer-group {background-color: #333;} +.tablet.android .kmw-key-layer-group {background-color: #333;} .tablet.android .kmw-key {border: none; border-bottom: solid 1px #8a8d90; box-shadow:none; border-radius: 5px;} .tablet.android .kmw-key.kmw-key-default {color:#fff;background-color:#777;} .tablet.android .kmw-key.kmw-key-shift, @@ -217,19 +217,19 @@ .tablet.android .kmw-key.kmw-spacebar {background-color: #777;} .tablet.android .kmw-key.kmw-spacebar.kmw-key-touched {background-color: #447;} .tablet.android .kmw-spacebar-caption {color: #aaa;} -.tablet.android .kmw-key-deadkey{color:#0d0;background-color:#777;} +.tablet.android .kmw-key-deadkey{color:#0d0;background-color:#777;} .tablet.android .kmw-banner-bar { - background-color: #b4b4b8; + background-color: #b4b4b8; position: absolute; top: 4px; - height: 100%; + height: 100%; width: 100%; } .tablet.android .kmw-banner-bar .kmw-suggest-option { - display:inline-block; - text-align: center; + display:inline-block; + text-align: center; vertical-align: middle; } @@ -238,18 +238,18 @@ } .tablet.android .kmw-suggest-option.kmw-suggest-touched {background-color:#447;} -/* Vertical centering of text labels on keys */ +/* Vertical centering of text labels on keys */ .kmw-key {text-align:center; white-space:nowrap;} .kmw-key:before {content:'.'; display:inline-block; height:100%; vertical-align:middle; max-width:0px; visibility:hidden;} -.kmw-key span {display:inline-block} +.kmw-key span {display:inline-block} + - .desktop .kmw-osk-frame{position:absolute;width:auto;height:auto;left:0;top:0;display:none;margin:0;padding:0; border:2px solid #ad4a28;border-radius:2px;background-color:#666;box-sizing:border-box;-moz-box-sizing:border-box;} .desktop .kmw-osk-inner-frame{margin:0;background-color:#666;border:2px solid #ad4a28;box-sizing:border-box;-moz-box-sizing:border-box;} .desktop .kmw-key-layer-group{width:100%;height:100%;margin:0;padding:0;border:none;background:transparent;} .desktop .kmw-key-layer{width:100%;height:100%;margin:0;padding:0;background-color:transparent;overflow:hidden;} -.desktop .kmw-key-row{position:static;width:100%;height:20%;margin:0;padding:0;border:none;overflow:hidden;} +.desktop .kmw-key-row{position:static;width:100%;height:20%;margin:0;padding:0;border:none;overflow:hidden;} .desktop .kmw-key-square{position:relative;height:100%;display:inline-block;float:left;margin:0 0 0 5px;z-index:10000; font-size:0.8em;overflow:hidden; padding:1px 0 1px 0;background-color:transparent;cursor:default;box-sizing:border-box;-moz-box-sizing:border-box;border:solid 1px transparent} .desktop .kmw-key {display:block;margin:0;border-radius:6px;text-align:center;box-sizing:border-box;-moz-box-sizing:border-box; @@ -285,15 +285,15 @@ .kmw-footer-resize{cursor:se-resize;position:absolute;right:2px;bottom:2px;width:16px;height:16px;overflow:hidden; font-family:SpecialOSK;color:white;} .kmw-footer-resize:hover{font-weight:bold;} -.kmw-footer-resize:before {content:'\e023';} +.kmw-footer-resize:before {content:'\e023';} -.kmw-title-bar-image {cursor: default; float:right; padding: 2px 2px 0 0; width:16px; height:16px; +.kmw-title-bar-image {cursor: default; float:right; padding: 2px 2px 0 0; width:16px; height:16px; font-family:SpecialOSK; color:white;} .kmw-title-bar-image:hover{font-weight:bold;} #kmw-pin-image:before{content:'\e024';} #kmw-config-image:before{content:'\e030';} #kmw-help-image:before{content:'\e042';} -#kmw-close-button:before {content:'\e025';} +#kmw-close-button:before {content:'\e025';} /* Common key appearance styles (can override with form-factor styles if necessary) */ .kmw-key-default{color:#000;background-color:#eee;} @@ -309,20 +309,70 @@ .tablet .kmw-key-shift, .tablet .kmw-key-shift-on{font-size:0.8em !important;} .phone .kmw-key-shift, .phone .kmw-key-shift-on{font-size:0.7em !important;} -/* Set special font and style for modifier and special key images */ +/* Set special font and style for modifier and special key images */ body div.kmw-key-shift span.kmw-key-text {font-family:SpecialOSK !important;font-size:1em !important;} -body div.kmw-key-shift-on span.kmw-key-text {font-family:SpecialOSK !important;font-size:1em !important;} +body div.kmw-key-shift-on span.kmw-key-text {font-family:SpecialOSK !important;font-size:1em !important;} #kmw-popup-keys div.kmw-key-shift span.kmw-key-text {font-family:SpecialOSK !important;} /* Style for callout used on phones */ #kmw-popup-callout { - position:fixed; display:block; background-color: #fdfdfe; + position:fixed; display:block; background-color: #fdfdfe; border-radius: 0 0 6px 6px; z-index:10001; pointer-events:none; } /* Key preview styles */ + div.ios div.kmw-keytip { - position:fixed; left:0; top:0; width:3em; height:3em; background-color:rgb(0,0,0,0);overflow:visible; + position: fixed; + left: 0; + top: 0; + width: 3em; + height: 3em; + background-color: rgb(0, 0, 0, 0); + overflow: visible; +} + +div.ios div.kmw-keytip-tip { + position: absolute; + top: 0; + width: 100%; + border-radius: 6px 6px 4px 4px; + border: solid 1px #cccccc; + border-bottom: solid 1px #8a8d90; +} + +div.ios div.kmw-keytip-cap { + position: absolute; + border-radius: 0 0 5px 5px; + border-top: solid 1px white; + border-bottom: solid 1px #8a8d90; +} + +span.kmw-keytip-label { + display: block; + transform: translateY(-50%); + top: 50%; + position: relative; +} + +div.ios div.kmw-keytip-cap, +div.ios div.kmw-keytip-tip { + background:white; +} + +div.android div.kmw-keytip { + position:fixed; + left: 0; + top: 0; + width: 3em; + height: 3em; + color: white; + overflow: visible; +} + +div.android div.kmw-keytip-tip { + border-radius: 6px; + background: #999; } /* Dark mode - ensure text is colored appropriately for key tips. */ @@ -331,51 +381,51 @@ div.ios div.kmw-keytip { color:#fff; } + div.ios div.kmw-keytip-cap, + div.ios div.kmw-keytip-tip { + background: #0f1319; + } + + div.ios div.kmw-keytip-cap { + border-top: solid 1px #0f1319; + } + /* Style for callout used on phones */ #kmw-popup-callout { background-color: #0f1319; } } -div.android div.kmw-keytip { - position:fixed; left:0; top:0; width:3em; height:3em; - color:#fff; - background-color: rgb(0,0,0,0); - overflow:visible; -} - -div.android #keytip {background-color:#f00;} - /* Box styles for keyboard-specific OSK (e.g. EuroLatin) and if no keyboard active (desktop only) */ .kmw-osk-static, .kmw-osk-none{text-align:left;font:12px sans-serif;border:solid 1px #ad4a28;color:blue;background-color:white;} .kmw-osk-none{padding:4px 6px 6px} .kmw-osk-none:before{content:'Installing keyboard...';} - + /* OSK language menu styles */ #kmw-language-menu{position:fixed;left:0;width:232px;max-width:232px;z-index:10004;background-color:rgba(128,128,128,1); border:3px solid #888;padding:0;border-radius:4px;-webkit-border-radius:4px; - box-shadow:3px 3px 2px #ccc;-webkit-box-shadow:3px 3px 2px #ccc; + box-shadow:3px 3px 2px #ccc;-webkit-box-shadow:3px 3px 2px #ccc; overflow:hidden; -ms-touch-action:none;-webkit-user-select:none; } - + #kmw-language-menu *{-ms-touch-action:none;-webkit-user-select:none;} - -#kmw-language-menu p{font-family:Arial,Helvetica,sans-serif; font-size:16px; - border-top:1px dotted #989898; border-bottom:1px dotted #989898; + +#kmw-language-menu p{font-family:Arial,Helvetica,sans-serif; font-size:16px; + border-top:1px dotted #989898; border-bottom:1px dotted #989898; margin:0; padding:10px 0px 10px 10px; overflow:hidden; color:#0000ff; background-color:transparent; white-space:nowrap; - } + } -#kmw-language-menu div.kbd-list-open p.kbd-list-entry {display: block;} +#kmw-language-menu div.kbd-list-open p.kbd-list-entry {display: block;} #kmw-language-menu div.kbd-list-closed p.kbd-list-entry {display: none;} -#kmw-language-menu-background{position:fixed;left:0;top:0;width:100%;height:100%;background-color:transparent;z-index:10003;} +#kmw-language-menu-background{position:fixed;left:0;top:0;width:100%;height:100%;background-color:transparent;z-index:10003;} #kmw-menu-scroll-container{position:absolute;left:0;top:0px;width:200px;max-width:200px;overflow:auto;z-index:10005;} #kmw-menu-scroller{position:absolute;left:0;top:0;width:200px;max-width:200px;background-color:#e0e0e0;overflow:auto;overflow-x:hidden;} #kmw-menu-index {position:absolute;right:0;width:32px;font-size:14px;} -#kmw-menu-index p {text-align:center;border:none;padding:0px 2px;margin:0;color:white;} +#kmw-menu-index p {text-align:center;border:none;padding:0px 2px;margin:0;color:white;} #kmw-menu-footer{height:2px;background-color:#808080;} @@ -389,15 +439,15 @@ div.android #keytip {background-color:#f00;} #kmw-popup-keys {position:fixed;display:block;width:auto;height:auto;overflow-y:visible; padding: 4px; border: 1px solid #ddd; border-radius:8px; background-color:#fdfdfe; border:none; z-index:10000;} - + .tablet.ios #kmw-popup-keys {padding: 4px 7px 0px 0px;} .phone.ios #kmw-popup-keys {padding: 4px 5px 0px 0px;} - + .tablet.ios #kmw-popup-keys .kmw-key{border:none;} .phone.ios #kmw-popup-keys .kmw-key{border:none;} -.phone.android #kmw-popup-keys {border:none; border-radius: 2px; background-color:#ccc; padding:5px 5px 0 0;} -.tablet.android #kmw-popup-keys {border:1px solid #eee; border-radius: 3px; background-color:#888; padding:8px 12px 4px 4px;} +.phone.android #kmw-popup-keys {border:none; border-radius: 2px; background-color:#ccc; padding:5px 5px 0 0;} +.tablet.android #kmw-popup-keys {border:1px solid #eee; border-radius: 3px; background-color:#888; padding:8px 12px 4px 4px;} @media (prefers-color-scheme: dark) { #kmw-popup-keys { @@ -406,9 +456,9 @@ div.android #keytip {background-color:#f00;} } /* Filter (shim) to darken screen and highlight popup keys */ -#kmw-popup-shim { +#kmw-popup-shim { position: fixed; width: 100%; height: 100%; bottom: 0; left: 0; - display: block; ; opacity: 0.15; background-color: #000; pointer-events: none; + display: block; ; opacity: 0.15; background-color: #000; pointer-events: none; } /* Styles for other KMW elements (keyboard loading wait notification, etc.) */ @@ -416,12 +466,12 @@ div.android #keytip {background-color:#f00;} background-color:rgba(0,0,0,0.0);z-index:11000;} .kmw-wait-box{width:240px; height:80px;margin-top:20%;margin-left:auto;margin-right:auto; border:3px solid #ad4a28;border-radius:8px;text-align:center;padding:0px;background:white;} -.kmw-alert-close{float:right; height:24px; width:24px; font:1em bold Arial,sans-serif;color:#ad4a28;} +.kmw-alert-close{float:right; height:24px; width:24px; font:1em bold Arial,sans-serif;color:#ad4a28;} /*.kmw-alert-close{float:right; height:24px; width:24px; font:2em bold Arial,sans-serif;color:#ad4a28;} */ -.kmw-alert-close:before{content:'\00d7'} -/*.kmw-alert-close{float:right;background:url('icons.gif') no-repeat -30px 0; height:13px; width:15px;}*/ +.kmw-alert-close:before{content:'\00d7'} +/*.kmw-alert-close{float:right;background:url('icons.gif') no-repeat -30px 0; height:13px; width:15px;}*/ .kmw-wait-text{clear:both; margin:4px;white-space:nowrap;} -.kmw-wait-graphic{width:100%;height:75%;background:url('ajax-loader.gif') no-repeat;background-position:center top;} +.kmw-wait-graphic{width:100%;height:75%;background:url('ajax-loader.gif') no-repeat;background-position:center top;} .kmw-alert-text{margin:10px;white-space:default;font-family:Arial,sans-serif;} .kmw-spacebar-caption{font:0.6em Arial !important;color:rgba(0,0,0,0.25);} @@ -436,7 +486,7 @@ div.android #keytip {background-color:#f00;} .desktop-static.kmw-osk-inner-frame{margin:0;background-color:#666;border:2px solid #ad4a28;box-sizing:border-box;-moz-box-sizing:border-box;} .desktop-static .kmw-key-layer-group{width:100%;height:100%;margin:0;padding:0;border:none;background:transparent;} .desktop-static .kmw-key-layer{width:100%;height:100%;margin:0;padding:0;background-color:transparent;overflow:hidden;} -.desktop-static .kmw-key-row{position:static;width:100%;height:20%;margin:0;padding:0;border:none;overflow:hidden;} +.desktop-static .kmw-key-row{position:static;width:100%;height:20%;margin:0;padding:0;border:none;overflow:hidden;} .desktop-static .kmw-key-square{position:relative;height:100%;display:inline-block;float:left;margin:0 0 0 5px;z-index:10000; font-size:0.8em;overflow:hidden; padding:1px 0 1px 0;background-color:transparent;cursor:default;box-sizing:border-box;-moz-box-sizing:border-box;border:solid 1px transparent} .desktop-static .kmw-key {display:block;margin:0;border-radius:6px;text-align:center;box-sizing:border-box;-moz-box-sizing:border-box; @@ -447,7 +497,7 @@ div.android #keytip {background-color:#f00;} .phone-static.kmw-osk-inner-frame{margin:0;background-color:#666;border:2px solid #ad4a28;box-sizing:border-box;-moz-box-sizing:border-box;} .phone-static .kmw-key-layer-group{width:100%;height:100%;margin:0;padding:0;border:none;background:transparent;} .phone-static .kmw-key-layer{width:100%;height:100%;margin:0;padding:0;background-color:transparent;overflow:hidden;} -.phone-static .kmw-key-row{position:relative;width:100%;height:20%;margin:0;padding:0;border:none;overflow:hidden;} +.phone-static .kmw-key-row{position:relative;width:100%;height:20%;margin:0;padding:0;border:none;overflow:hidden;} .phone-static .kmw-key-square{position:absolute;height:100%;display:inline-block;float:left;margin:0 0 0 5px;z-index:10000; font-size:0.8em;overflow:hidden; padding:1px 0 1px 0;background-color:transparent;cursor:default;box-sizing:border-box;-moz-box-sizing:border-box;} .phone-static .kmw-key {display:block;margin:0;border-radius:6px;text-align:center;box-sizing:border-box;-moz-box-sizing:border-box; @@ -458,7 +508,7 @@ div.android #keytip {background-color:#f00;} .tablet-static.kmw-osk-inner-frame{margin:0;background-color:#666;border:2px solid #ad4a28;box-sizing:border-box;-moz-box-sizing:border-box;} .tablet-static .kmw-key-layer-group{width:100%;height:100%;margin:0;padding:0;border:none;background:transparent;} .tablet-static .kmw-key-layer{width:100%;height:100%;margin:0;padding:0;background-color:transparent;overflow:hidden;} -.tablet-static .kmw-key-row{position:relative;width:100%;height:20%;margin:0;padding:0;border:none;overflow:hidden;} +.tablet-static .kmw-key-row{position:relative;width:100%;height:20%;margin:0;padding:0;border:none;overflow:hidden;} .tablet-static .kmw-key-square{position:absolute;height:100%;display:inline-block;float:left;margin:0 0 0 5px;z-index:10000; font-size:0.8em;overflow:hidden; padding:1px 0 1px 0;background-color:transparent;cursor:default;box-sizing:border-box;-moz-box-sizing:border-box;} .tablet-static .kmw-key {display:block;margin:0;border-radius:6px;text-align:center;box-sizing:border-box;-moz-box-sizing:border-box;