diff --git a/common/core/web/keyboard-processor/src/keyboards/defaultLayouts.ts b/common/core/web/keyboard-processor/src/keyboards/defaultLayouts.ts index c06e11429e5..1cf5406f968 100644 --- a/common/core/web/keyboard-processor/src/keyboards/defaultLayouts.ts +++ b/common/core/web/keyboard-processor/src/keyboards/defaultLayouts.ts @@ -140,6 +140,15 @@ namespace com.keyman.keyboards { var i, j, k, m, row, rows: LayoutRow[], key: LayoutKey, keys: LayoutKey[]; var chiral: boolean = (kbdBitmask & Codes.modifierBitmasks.IS_CHIRAL) != 0; + if(PVK['F']) { + // The KeymanWeb compiler generates a string of the format `[italic ][bold ] 1em ""` + // We will ignore the bold, italic and font size spec + let legacyFontSpec = /^(?:(?:italic|bold) )* *[0-9.eE-]+(?:[a-z]+) "(.+)"$/.exec(PVK['F']); + if(legacyFontSpec) { + layout.font = legacyFontSpec[1]; + } + } + var kmw10Plus = !(typeof keyLabels == 'undefined' || !keyLabels); if(!kmw10Plus) { // Save the processed key label information to the keyboard's general data. diff --git a/web/source/dom/domEventHandlers.ts b/web/source/dom/domEventHandlers.ts index e3491d8c58d..bb2bd197089 100644 --- a/web/source/dom/domEventHandlers.ts +++ b/web/source/dom/domEventHandlers.ts @@ -492,8 +492,8 @@ namespace com.keyman.dom { } private static selectTouch(e: TouchEvent): Touch { - /** - * During multi-touch event's, it's possible for one or more touches of said multi-touch + /* + * During multi-touch events, it's possible for one or more touches of said multi-touch * to be against irrelevant parts of the page. We only want to consider touches against * valid OutputTargets - against elements of the page that KMW can attach to. * With touch active... that's a TouchAliasElement. @@ -521,7 +521,6 @@ namespace com.keyman.dom { /** * Handle receiving focus by simulated input field - * */ setFocus: (e?: TouchEvent|MSPointerEvent) => void = function(this: DOMTouchHandlers, e?: TouchEvent|MSPointerEvent): void { DOMEventHandlers.states.setFocusTimer(); @@ -682,7 +681,7 @@ namespace com.keyman.dom { } } - /** + /* * This event will trigger before keymanweb.setBlur is triggered. Now that we're allowing independent keyboard settings * for controls, we have to act here to preserve the outgoing control's keyboard settings. * @@ -696,7 +695,7 @@ namespace com.keyman.dom { this.keyman.domManager.lastActiveElement = target; target.showCaret(); - /** + /* * If we 'just activated' the KeymanWeb UI, we need to save the new keyboard change as appropriate. * If not, we need to activate the control's preferred keyboard. */ diff --git a/web/source/dom/domManager.ts b/web/source/dom/domManager.ts index 1b00d74d9cb..d141cbe0735 100644 --- a/web/source/dom/domManager.ts +++ b/web/source/dom/domManager.ts @@ -462,7 +462,7 @@ namespace com.keyman.dom { return; }; - /** + /** * Function isKMWDisabled * Scope Private * @param {Element} x An element from the page. @@ -663,7 +663,7 @@ namespace com.keyman.dom { } } - /** + /** * Function _DetachFromIframe * Scope Private * @param {Element} Pelem IFrame to which KMW will be attached @@ -1300,7 +1300,7 @@ namespace com.keyman.dom { * * @param {Object|string} e element id or element * @param {boolean=} setFocus optionally set focus (KMEW-123) - **/ + */ setActiveElement(e: string|HTMLElement, setFocus?: boolean) { if(typeof e == "string") { // Can't instanceof string, and String is a different type. e = document.getElementById(e); @@ -1423,7 +1423,7 @@ namespace com.keyman.dom { * * @param {string|Object} e element or element id * - **/ + */ moveToElement(e:string|HTMLElement) { var i; @@ -1473,7 +1473,7 @@ namespace com.keyman.dom { * before applying any keymanweb styles or classes * * @return {string} - **/ + */ getBaseFont() { var util = this.keyman.util; var ipInput = document.getElementsByTagName<'input'>('input'), @@ -1614,21 +1614,11 @@ namespace com.keyman.dom { keyman.modelManager.init(); this.keyman._MasterDocument = window.document; - /** + /* * Initialization of touch devices and browser interfaces must be done * after all resources are loaded, during final stage of initialization - * */ - // Treat Android devices as phones if either (reported) screen dimension is less than 4" - if(device.OS == 'Android') - { - // Determine actual device characteristics I3363 (Build 301) - // TODO: device.dpi may no longer be needed - if so, get rid of it. - var dpi = device.getDPI(); //TODO: this will not work when called from HEAD!! - device.formFactor=((screen.height < 4.0 * dpi) || (screen.width < 4.0 * dpi)) ? 'phone' : 'tablet'; - } - // Set exposed initialization flag member for UI (and other) code to use this.keyman.setInitialized(1); @@ -1785,7 +1775,7 @@ namespace com.keyman.dom { this.attachmentObserver.observe(observationTarget, observationConfig); } - /** + /* * Setup of handlers for dynamic detection of the kmw-disabled class tag that controls enablement. */ observationConfig = { subtree: true, attributes: true, attributeOldValue: true, attributeFilter: ['class', 'readonly']}; @@ -1812,7 +1802,7 @@ namespace com.keyman.dom { /** * Initialize the desktop user interface as soon as it is ready - **/ + */ initializeUI() { if(this.keyman.ui && this.keyman.ui['initialize'] instanceof Function) { this.keyman.ui['initialize'](); diff --git a/web/source/kmwdevice.ts b/web/source/kmwdevice.ts index 5424f8c94c1..2217bc89e49 100644 --- a/web/source/kmwdevice.ts +++ b/web/source/kmwdevice.ts @@ -33,28 +33,28 @@ namespace com.keyman { /** * Get device horizontal DPI for touch devices, to set actual size of active regions * Note that the actual physical DPI may be somewhat different. - * - * @return {number} - */ + * + * @return {number} + */ getDPI(): number { var t=document.createElement('DIV') ,s=t.style,dpi=96; if(document.readyState !== 'complete') { return dpi; } - + t.id='calculateDPI'; s.position='absolute'; s.display='block';s.visibility='hidden'; s.left='10px'; s.top='10px'; s.width='1in'; s.height='10px'; document.body.appendChild(t); dpi=(typeof window.devicePixelRatio == 'undefined') ? t.offsetWidth : t.offsetWidth * window.devicePixelRatio; document.body.removeChild(t); - return dpi; + return dpi; } detect() : void { var IEVersion = Device._GetIEVersion(); var possMacSpoof = false; - + if(navigator && navigator.userAgent) { var agent=navigator.userAgent; @@ -62,7 +62,7 @@ namespace com.keyman { this.OS='iOS'; this.formFactor='tablet'; this.dyPortrait=this.dyLandscape=0; - } else if(agent.indexOf('iPhone') >= 0) { + } else if(agent.indexOf('iPhone') >= 0) { this.OS='iOS'; this.formFactor='phone'; this.dyPortrait=this.dyLandscape=25; @@ -78,7 +78,7 @@ namespace com.keyman { } else if(agent.indexOf('Linux') >= 0) { this.OS='Linux'; } else if(agent.indexOf('Macintosh') >= 0) { - // Starting with 13.1, "Macintosh" can reflect iPads (by default) or iPhones + // Starting with 13.1, "Macintosh" can reflect iPads (by default) or iPhones // (by user setting); a new "Request Desktop Website" setting for Safari will // change the user agent string to match a desktop Mac. // @@ -86,7 +86,7 @@ namespace com.keyman { // '_' instead. So, we have to check for both. Yay. let regex = /Intel Mac OS X (\d+(?:[_\.]\d+)+)/i; let results = regex.exec(agent); - + // Match result: a version string with components separated by underscores. if(!results) { console.warn("KMW could not properly parse the user agent string." @@ -105,7 +105,7 @@ namespace com.keyman { if(agent.indexOf('Touch') >= 0) { this.formFactor='phone'; // will be redefined as tablet if resolution high enough } - + // Windows Phone and Tablet PC if(typeof navigator.msMaxTouchPoints == 'number' && navigator.msMaxTouchPoints > 0) { this.touchable=true; @@ -113,14 +113,21 @@ namespace com.keyman { } } - // var sxx=device.formFactor; - // Check and possibly revise form factor according to actual screen size (adjusted for Galaxy S, maybe OK generally?) - if(this.formFactor == 'tablet' && Math.min(screen.width,screen.height) < 400) { - this.formFactor='phone'; - } - - // Trust what iOS tells us for phone vs tablet. - if(this.formFactor == 'phone' && Math.max(screen.width,screen.height) > 720 && this.OS != 'iOS') { + // We look at the screen resolution for Android, because we can't tell from + // the user agent string whether or not this is supposed to be a tablet. + // It seems that there are a handful of older phones out there that report a + // higher resolution than 700px*___px, but it is proving hard to test these, + // and the majority have an aspect ratio <= 0.5625 anyway. + // But we trust what iOS tells us for phone vs tablet. + + const dimMin = Math.min(screen.width,screen.height), dimMax = Math.max(screen.width,screen.height); + const aspect = dimMin / dimMax; + + if(this.OS != 'iOS' && + this.formFactor == 'phone' && + ((dimMin >= 600 && aspect > 0.5625) || // 0.5625 -> 1920x1080 is common phone res + (aspect >= 0.625)) // all reported devices with aspect >= 0.625 are tablets per https://screensiz.es/ + ) { this.formFactor='tablet'; } @@ -132,7 +139,7 @@ namespace com.keyman { if(this.OS == 'iOS' && !('ongesturestart' in window) && !possibleChromeEmulation) { this.OS='Android'; } - + // Determine application or browser this.browser='web'; if(IEVersion < 999) { @@ -160,7 +167,7 @@ namespace com.keyman { } else if(navigator.userAgent.indexOf('Safari') >= 0) { this.browser='safari'; } - } + } } if(possMacSpoof && this.browser == 'safari') { @@ -193,13 +200,13 @@ namespace com.keyman { static _GetIEVersion() { var n, agent=''; - + if('userAgent' in navigator) { agent=navigator.userAgent; } - + // Test first for old versions - if('selection' in document) { // only defined for IE and not for IE 11!!! + if('selection' in document) { // only defined for IE and not for IE 11!!! var appVer=navigator.appVersion; n=appVer.indexOf('MSIE '); if(n >= 0) { @@ -207,27 +214,27 @@ namespace com.keyman { if((document as Document).compatMode == 'BackCompat') { return 6; } - + appVer=appVer.substr(n+5); n=appVer.indexOf('.'); if(n > 0) { return parseInt(appVer.substr(0,n),10); } - } + } } - + // Finally test for IE 11 (and later?) n=agent.indexOf('Trident/'); if(n < 0) { return 999; } - + agent=agent.substr(n+8); n=agent.indexOf('.'); if(n > 0){ return parseInt(agent.substr(0,n),10)+4; } - + return 999; } diff --git a/web/source/kmwutils.ts b/web/source/kmwutils.ts index d19b1c475ab..26db146b09f 100644 --- a/web/source/kmwutils.ts +++ b/web/source/kmwutils.ts @@ -442,6 +442,11 @@ namespace com.keyman { // This can sometimes fail with some browsers if called before document defined, // so catch the exception try { + // For emulation of iOS on a desktop device, use a default value + if(this.device.formFactor == 'desktop') { + return 1; + } + // Get viewport width var viewportWidth = document.documentElement.clientWidth; diff --git a/web/source/osk/oskBaseKey.ts b/web/source/osk/oskBaseKey.ts index d8fc20f165b..12193258f28 100644 --- a/web/source/osk/oskBaseKey.ts +++ b/web/source/osk/oskBaseKey.ts @@ -54,7 +54,7 @@ namespace com.keyman.osk { let q = document.createElement('div'); q.className='kmw-key-label'; if(x > 0) { - q.innerHTML=String.fromCharCode(x); + q.innerText=String.fromCharCode(x); } else { // Keyman-only virtual keys have no corresponding physical key. // So, no text for the key-cap. @@ -128,7 +128,7 @@ namespace com.keyman.osk { this.square.style.width = vkbd.layoutWidth.scaledBy(key.proportionalWidth).styleString; this.square.style.marginLeft = vkbd.layoutWidth.scaledBy(key.proportionalPad).styleString; this.btn.style.width = vkbd.usesFixedWidthScaling ? this.square.style.width : '100%'; - + if(vkbd.usesFixedHeightScaling) { // Matches its row's height. this.square.style.height = vkbd.layoutHeight.scaledBy(this.row.heightFraction).styleString; diff --git a/web/source/osk/oskView.ts b/web/source/osk/oskView.ts index d4bfd23f44a..4b78fbac932 100644 --- a/web/source/osk/oskView.ts +++ b/web/source/osk/oskView.ts @@ -374,25 +374,9 @@ namespace com.keyman.osk { public defaultFontSize(device: utils.DeviceSpec, isEmbedded: boolean): ParsedLengthStyle { if(device.touchable) { - var fontScale: number = 1; - if(device.formFactor == 'phone') { - fontScale = 1.6 * (isEmbedded ? 0.65 : 0.6) * 1.2; // Combines original scaling factor with one previously applied to the layer group. - } else { - // The following is a *temporary* fix for small format tablets, e.g. PendoPad - var pixelRatio = 1; - if(device.OS == 'android' && 'devicePixelRatio' in window) { - pixelRatio = window.devicePixelRatio; - } - - let defaultHeight = this.bannerView.height + this.getDefaultKeyboardHeight(); - if(device.OS == 'android' && device.formFactor == 'tablet' && defaultHeight < 300 * pixelRatio) { - fontScale *= 1.2; - } else { - fontScale *= 2; //'2.5em'; - } - } - - // Finalize the font size parameter. + const fontScale = device.formFactor == 'phone' + ? 1.6 * (isEmbedded ? 0.65 : 0.6) * 1.2 // Combines original scaling factor with one previously applied to the layer group. + : 2; // iPad or Android tablet return ParsedLengthStyle.special(fontScale, 'em'); } else { return this.computedHeight ? ParsedLengthStyle.inPixels(this.computedHeight / 8) : undefined; @@ -487,13 +471,11 @@ namespace com.keyman.osk { this.needsLayout = false; // Step 3: perform layout operations. - if(!this._baseFontSize && this.parsedBaseFontSize) { - // Make sure to initialize the default font size if it hasn't already been set! - this.banner.element.style.fontSize = this.baseFontSize; - if(this.vkbd) { - this.vkbd.fontSize = this.parsedBaseFontSize; - } + this.banner.element.style.fontSize = this.baseFontSize; + if(this.vkbd) { + this.vkbd.fontSize = this.parsedBaseFontSize; } + if(!pending) { this.headerView?.refreshLayout(); this.bannerView.refreshLayout(); @@ -627,7 +609,9 @@ namespace com.keyman.osk { // Ensure the keyboard view is modeling the correct state. (Correct layer, etc.) this.keyboardView.updateState(); - this.refreshLayoutIfNeeded(); + // We need to recalc the font size here because the layer did not have + // calculated dimensions available before it was visible + this.refreshLayout(); } }.bind(this); diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 85f3abd3a93..63c53019af3 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -1232,12 +1232,6 @@ namespace com.keyman.osk { gs.fontSize = this.fontSize.styleString; bs.fontSize = ParsedLengthStyle.forScalar(fs).styleString; - // Needs the refreshed layout info to work correctly. - for (const layerId in this.layerGroup.layers) { - const layer = this.layerGroup.layers[layerId]; - layer.refreshLayout(this, paddedHeight, this.height); - } - // NEW CODE ------ // Step 1: have the necessary conditions been met? @@ -1268,9 +1262,8 @@ namespace com.keyman.osk { // END NEW CODE ----------- // Needs the refreshed layout info to work correctly. - for (const layerId in this.layerGroup.layers) { - const layer = this.layerGroup.layers[layerId]; - layer.refreshLayout(this, paddedHeight, this._computedHeight); + if(this.currentLayer) { + this.currentLayer.refreshLayout(this, paddedHeight, this._computedHeight); } } diff --git a/windows/src/developer/TIKE/xml/kmw/test.js b/windows/src/developer/TIKE/xml/kmw/test.js index 462c89b6e3a..9e309ccdcff 100644 --- a/windows/src/developer/TIKE/xml/kmw/test.js +++ b/windows/src/developer/TIKE/xml/kmw/test.js @@ -226,10 +226,6 @@ window.onload = function() { newOSK.setSize(targetDevice.dimensions[0]+'px', targetDevice.dimensions[1]+'px'); } document.getElementById('osk-host').appendChild(newOSK.element); - - keyman.osk = newOSK; - newOSK.activeKeyboard = keyman.core.activeKeyboard; - keyman.alignInputs(); } setOSK();