From e347c423bf48d8ce0aea04ded2948cc83b0b4232 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 16 Jun 2021 12:57:05 +0700 Subject: [PATCH 01/58] refactor(web): wraps in-browser subkey selection with Promise --- web/source/osk/browser/subkeyPopup.ts | 18 ++++- web/source/osk/visualKeyboard.ts | 103 +++++++++++++++++--------- 2 files changed, 84 insertions(+), 37 deletions(-) diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index 4879140c13..c917c1829e 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -4,12 +4,16 @@ namespace com.keyman.osk.browser { export class SubkeyPopup { public readonly element: HTMLDivElement; public readonly shim: HTMLDivElement; + public readonly baseKey: KeyElement; - private baseKey: KeyElement; private callout: HTMLDivElement; - constructor(vkbd: VisualKeyboard, e: KeyElement) { + // Resolves the promise that generated this SubkeyPopup. + private resolver: (keyEvent: text.KeyEvent) => void; + + constructor(vkbd: VisualKeyboard, e: KeyElement, resolve: (keyEvent: text.KeyEvent) => void) { let keyman = com.keyman.singleton; + this.resolver = resolve; // A tag we directly set on a key element during its construction. let subKeySpec: OSKKeySpec[] = e['subKeys']; @@ -72,6 +76,13 @@ namespace com.keyman.osk.browser { } } + resolve(keyEvent: text.KeyEvent) { + if(this.resolver) { + this.resolver(keyEvent); + } + this.resolver = null; + } + reposition(vkbd: VisualKeyboard) { let keyman = com.keyman.singleton; @@ -182,6 +193,9 @@ namespace com.keyman.osk.browser { } clear() { + // If not yet resolved, resolve the corresponding Promise. + this.resolve(null); + // Remove the displayed subkey array, if any if(this.element.parentNode) { this.element.parentNode.removeChild(this.element); diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 99ca19ecfc..e1824e4757 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -6,6 +6,11 @@ /// namespace com.keyman.osk { + interface SubkeyDeferment { + timerId: number; + resolve: (KeyEvent) => void; + } + export class VisualKeyboard { // Legacy alias, maintaining a reference for code built against older // versions of KMW. @@ -60,10 +65,10 @@ namespace com.keyman.osk { currentTarget: KeyElement; // Popup key management - popupBaseKey: KeyElement; popupPending: boolean = false; - subkeyDelayTimer: number; + //subkeyDelayTimer: number; popupDelay: number = 500; + subkeyDeferment: SubkeyDeferment menuEvent: KeyElement; // Used by embedded-mode. keytip: KeyTip; subkeyPopup: browser.SubkeyPopup; @@ -425,7 +430,7 @@ namespace com.keyman.osk { let kbdAspectRatio = layerGroup.offsetWidth / this.kbdDiv.offsetHeight; let baseKeyProbabilities = this.layout.getLayer(this.layerId).getTouchProbabilities(touchKbdPos, kbdAspectRatio); - if(!this.popupBaseKey || !this.popupBaseKey.key) { + if(!this.subkeyPopup || !this.subkeyPopup.baseKey.key) { return baseKeyProbabilities; } else { // A temp-hack, as this was noted just before 14.0's release. @@ -437,7 +442,7 @@ namespace com.keyman.osk { let baseMass = 1.0; let baseKeyMass = 1.0; - let baseKeyID = this.popupBaseKey.key.spec.coreID; + let baseKeyID = this.subkeyPopup.baseKey.key.spec.coreID; let popupKeyMass = 0.0; let popupKeyID: string = null; @@ -560,7 +565,13 @@ namespace com.keyman.osk { } else { if(this.keyPending) { this.highlightKey(this.keyPending, false); - this.modelKeyClick(this.keyPending, this.touchPending); + + if(this.subkeyPopup) { + let keyEvent = this.initKeyEvent(this.keyPending, this.touchPending); + this.subkeyPopup.resolve(keyEvent); + } else { + this.modelKeyClick(this.keyPending, this.touchPending); + } this.clearPopup(); // Decrement the number of unreleased touch points to prevent // sending the keystroke again when the key is actually released @@ -594,7 +605,7 @@ namespace com.keyman.osk { } // Cancel (but do not execute) pending key if neither a popup key or the base key - if((t == null) || ((t.id.indexOf('popup') < 0) && (t.id != this.popupBaseKey.id))) { + if((t == null) || (!(t.key instanceof browser.OSKSubKey) && (t.id != this.subkeyPopup.baseKey.id))) { this.highlightKey(this.keyPending,false); this.clearPopup(); this.keyPending = null; @@ -630,10 +641,15 @@ namespace com.keyman.osk { // Process and clear highlighting of pending target if(this.keyPending) { this.highlightKey(this.keyPending,false); - + // Output character unless moved off key if(this.keyPending.className.indexOf('hidden') < 0 && tc > 0 && !beyondEdge) { - this.modelKeyClick(this.keyPending, e.changedTouches[0]); + if(this.subkeyPopup) { + let keyEvent = this.initKeyEvent(this.keyPending, e.changedTouches[0]); + this.subkeyPopup.resolve(keyEvent); + } else { + this.modelKeyClick(this.keyPending, e.changedTouches[0]); + } } this.clearPopup(); this.keyPending = null; @@ -710,7 +726,7 @@ namespace com.keyman.osk { this.keyPending=null; this.touchPending=null; } else { - if(key1 == this.popupBaseKey) { + if(this.subkeyPopup && key1 == this.subkeyPopup.baseKey) { if(!key1.classList.contains('kmw-key-touched')) { this.highlightKey(key1,true); } @@ -727,11 +743,11 @@ namespace com.keyman.osk { return; } - var sk=document.getElementById('kmw-popup-keys'); + var sk=this.subkeyPopup // Use the popup duplicate of the base key if a phone with a visible popup array - if(sk && sk.style.visibility == 'visible' && this.device.formFactor == 'phone' && key1 == this.popupBaseKey) { - key1 = sk.childNodes[0].firstChild; + if(sk && sk.element.style.visibility == 'visible' && this.device.formFactor == 'phone' && key1 == sk.baseKey) { + key1 = sk.element.childNodes[0].firstChild; } // Identify current touch position (to manage off-key release) @@ -745,15 +761,15 @@ namespace com.keyman.osk { // If popup is visible, need to move over popup, not over main keyboard this.highlightSubKeys(key1,x,y); - if(sk && sk.style.visibility == 'visible') { + if(sk && sk.element.style.visibility == 'visible') { // Once a subkey array is displayed, do not allow changing the base key. // Keep that array visible and accept no other options until the touch ends. - if(key1 && key1.id.indexOf('popup') < 0 && key1 != this.popupBaseKey) { + if(key1 && key1.id.indexOf('popup') < 0 && key1 != this.subkeyPopup.baseKey) { return; } // Highlight the base key on devices that do not append it to the subkey array. - if(key1 && key1 == this.popupBaseKey && key1.className.indexOf('kmw-key-touched') < 0) { + if(key1 && key1 == this.subkeyPopup.baseKey) { this.highlightKey(key1,true); } // Cancel touch if moved up and off keyboard, unless popup keys visible @@ -1072,15 +1088,16 @@ namespace com.keyman.osk { clearPopup() { // Remove the displayed subkey array, if any, and cancel popup request if(this.subkeyPopup) { + this.subkeyPopup.resolve(null); this.subkeyPopup.clear(); this.subkeyPopup = null; } - if(this.subkeyDelayTimer) { - window.clearTimeout(this.subkeyDelayTimer); - this.subkeyDelayTimer = null; + if(this.subkeyDeferment) { + window.clearTimeout(this.subkeyDeferment.timerId); + this.subkeyDeferment.resolve(null); + this.subkeyDeferment = null; } - this.popupBaseKey = null; } //#region 'native'-mode subkey handling @@ -1088,7 +1105,7 @@ namespace com.keyman.osk { * Display touch-hold array of 'sub-keys' above the currently touched key * @param {Object} e primary key element */ - showSubKeys(e: KeyElement) { + showSubKeys(e: KeyElement, resolve: (KeyElement) => void) { // Do not show subkeys if key already released if(this.keyPending == null) { return; @@ -1096,9 +1113,8 @@ namespace com.keyman.osk { // Clear key preview if any this.showKeyTip(null,false); - this.popupBaseKey = e; - let subKeys = this.subkeyPopup = new browser.SubkeyPopup(this, e); + let subKeys = this.subkeyPopup = new browser.SubkeyPopup(this, e, resolve); // Otherwise append the touch-hold (subkey) array to the OSK let keyman = com.keyman.singleton; @@ -1678,19 +1694,39 @@ namespace com.keyman.osk { * * @param {Object} key base key object */ - touchHold(key: KeyElement) { + touchHold(key: KeyElement, force?: boolean) { // Clear and restart the popup timer - if(this.subkeyDelayTimer) { - window.clearTimeout(this.subkeyDelayTimer); - this.subkeyDelayTimer = null; + if(this.subkeyDeferment) { + window.clearTimeout(this.subkeyDeferment.timerId); + // Cancel the potential subkey event. + this.subkeyDeferment.resolve(null); + this.subkeyDeferment = null; } if(typeof key['subKeys'] != 'undefined' && key['subKeys'] != null) { - this.subkeyDelayTimer = window.setTimeout( - function(this: VisualKeyboard) { - this.clearPopup(); - this.showSubKeys(key); - }.bind(this), this.popupDelay); + let _this = this; + + let promise = new Promise(function(resolve, reject) { + let timerId = window.setTimeout( + function() { + // It's no longer deferred; it's being fulfilled. + // Even if the actual subkey itself is still async. + _this.subkeyDeferment = null; + _this.clearPopup(); + _this.showSubKeys(key, resolve); + }, force ? 0 : _this.popupDelay); + + _this.subkeyDeferment = { + timerId: timerId, + resolve: resolve + }; + }).then(function(keyEvent: text.KeyEvent) { + if(keyEvent) { + // Do something. + console.log("Promise fulfilled for " + keyEvent.kName); + PreProcessor.raiseKeyEvent(keyEvent); + } + }); } }; @@ -1730,10 +1766,7 @@ namespace com.keyman.osk { // Show popup keys immediately if touch moved up towards key array (KMEW-100, Build 353) if((this.touchY-y > 5) && skBox == null) { - if(this.subkeyDelayTimer) { - window.clearTimeout(this.subkeyDelayTimer); - } - this.showSubKeys(k); + this.touchHold(k, true); } //#endregion }; From a8e051b6f48299dda4e23595ee9c9b791a8ef39a Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 16 Jun 2021 15:16:13 +0700 Subject: [PATCH 02/58] refactor(web): wraps embedded subkey selection with Promise --- web/source/kmwembedded.ts | 38 +++++++++++++--------- web/source/osk/embedded/subkeyDelegator.ts | 19 +++++++++++ web/source/osk/visualKeyboard.ts | 3 +- 3 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 web/source/osk/embedded/subkeyDelegator.ts diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index a95f16dcc9..0d3039371a 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -2,6 +2,7 @@ // References the base Keyman object (and consequently, the rest of the core objects). /// /// +/// // KeymanWeb 11.0 // Copyright 2019 SIL International @@ -60,8 +61,19 @@ namespace com.keyman.osk { // #3718: No longer prepend base key to subkey array - this.popupBaseKey = key; - this.popupPending=true; + let _this = this; + let promise = new Promise(function(resolve, reject) { + _this.subkeyDeferment = { + timerId: 0, + resolve: resolve + }; + + let delegator = new embedded.SubkeyDelegator(key, resolve); + _this.subkeyDelegator = delegator; + }).then(function(keyEvent) { + PreProcessor.raiseKeyEvent(keyEvent); + }); + window['oskCreatePopup'](key['subKeys'], xBase, yBase, key.offsetWidth, key.offsetHeight); } }; @@ -349,11 +361,13 @@ namespace com.keyman.text { keymanweb.domManager.initActiveElement(Lelem); var nextLayer: string; - + var delegator: com.keyman.osk.embedded.SubkeyDelegator = null; // This should be set if we're within this method... but it's best to guard against nulls here, just in case. - if(osk.vkbd.popupBaseKey && osk.vkbd.popupBaseKey['key']) { + if(osk.vkbd.subkeyDelegator) { + delegator = osk.vkbd.subkeyDelegator as com.keyman.osk.embedded.SubkeyDelegator; + // This is set with the base key of our current subkey elsewhere within the engine. - var baseKey: com.keyman.osk.OSKKeySpec = osk.vkbd.popupBaseKey['key'].spec; + var baseKey: com.keyman.osk.OSKKeySpec = delegator.baseKey.key.spec; var found = false; if(baseKey.coreID == keyName) { @@ -375,6 +389,8 @@ namespace com.keyman.text { if(!found) { console.warn("Could not find subkey '" + origArg + "' under the current base key '" + baseKey.coreID + "'!"); } + + osk.vkbd.subkeyDelegator = null; } else { console.warn("No base key exists for the subkey being executed: '" + origArg + "'"); } @@ -415,17 +431,9 @@ namespace com.keyman.text { Lkc.vkCode=Lkc.Lcode; - // Now that we have a valid key event, hand it off to the Processor for execution. - // This allows the Processor to also handle any predictive-text tasks necessary. - let retVal = com.keyman.osk.PreProcessor.handleClick(Lkc, com.keyman.dom.Utils.getOutputTarget(Lelem), null); - - // Special case for embedded to pass K_TAB back to device to process - if(Lkc.Lcode == Codes.keyCodes["K_TAB"] || Lkc.Lcode == Codes.keyCodes["K_TABBACK"] - || Lkc.Lcode == Codes.keyCodes["K_TABFWD"]) { - return false; + if(delegator) { + delegator.resolve(Lkc); } - - return retVal; }; /** diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts new file mode 100644 index 0000000000..de1df534f5 --- /dev/null +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -0,0 +1,19 @@ +namespace com.keyman.osk.embedded { + export class SubkeyDelegator { + private resolver: (keyEvent: text.KeyEvent) => void; + + public readonly baseKey: KeyElement; + + constructor(e: KeyElement, resolve: (keyEvent: text.KeyEvent) => void) { + this.resolver = resolve; + this.baseKey = e; + } + + public resolve(keyEvent: text.KeyEvent) { + if(this.resolver) { + this.resolver(keyEvent); + } + this.resolver = null; + } + } +} \ No newline at end of file diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index e1824e4757..d15eaf2c77 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -65,13 +65,12 @@ namespace com.keyman.osk { currentTarget: KeyElement; // Popup key management - popupPending: boolean = false; - //subkeyDelayTimer: number; popupDelay: number = 500; subkeyDeferment: SubkeyDeferment menuEvent: KeyElement; // Used by embedded-mode. keytip: KeyTip; subkeyPopup: browser.SubkeyPopup; + subkeyDelegator: any; // a temp, intermediate property during subkey abstraction work get layerId(): string { return this._layerId; From 7c08d9b6607b9c6d4e2057fb6fe810a8032abf55 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 16 Jun 2021 16:13:28 +0700 Subject: [PATCH 03/58] fix(web): forgotten null guard --- web/source/kmwembedded.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index 0d3039371a..e081d2927d 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -71,7 +71,9 @@ namespace com.keyman.osk { let delegator = new embedded.SubkeyDelegator(key, resolve); _this.subkeyDelegator = delegator; }).then(function(keyEvent) { - PreProcessor.raiseKeyEvent(keyEvent); + if(keyEvent) { + PreProcessor.raiseKeyEvent(keyEvent); + } }); window['oskCreatePopup'](key['subKeys'], xBase, yBase, key.offsetWidth, key.offsetHeight); From 8794f7d018394d7356a41e029353497e80d781a1 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 17 Jun 2021 09:37:34 +0700 Subject: [PATCH 04/58] change(web): allows passive cancel for subkey promises --- web/source/kmwembedded.ts | 7 ++---- web/source/osk/browser/subkeyPopup.ts | 6 ++++-- web/source/osk/visualKeyboard.ts | 31 ++++++++++++--------------- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index e081d2927d..c875f9cbe1 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -63,14 +63,11 @@ namespace com.keyman.osk { let _this = this; let promise = new Promise(function(resolve, reject) { - _this.subkeyDeferment = { - timerId: 0, - resolve: resolve - }; - let delegator = new embedded.SubkeyDelegator(key, resolve); _this.subkeyDelegator = delegator; }).then(function(keyEvent) { + // Allow active cancellation, even if the source should allow passive. + // It's an easy and cheap null guard. if(keyEvent) { PreProcessor.raiseKeyEvent(keyEvent); } diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index c917c1829e..c991ef38fc 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -193,8 +193,10 @@ namespace com.keyman.osk.browser { } clear() { - // If not yet resolved, resolve the corresponding Promise. - this.resolve(null); + // Discard the reference to the Promise's resolve method, allowing + // GC to clean it up. The corresponding Promise's contract allows + // passive cancellation. + this.resolver = null; // Remove the displayed subkey array, if any if(this.element.parentNode) { diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index d15eaf2c77..4b6b67418b 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -66,7 +66,7 @@ namespace com.keyman.osk { // Popup key management popupDelay: number = 500; - subkeyDeferment: SubkeyDeferment + subkeyDelayTimer: number; menuEvent: KeyElement; // Used by embedded-mode. keytip: KeyTip; subkeyPopup: browser.SubkeyPopup; @@ -1087,15 +1087,13 @@ namespace com.keyman.osk { clearPopup() { // Remove the displayed subkey array, if any, and cancel popup request if(this.subkeyPopup) { - this.subkeyPopup.resolve(null); this.subkeyPopup.clear(); this.subkeyPopup = null; } - if(this.subkeyDeferment) { - window.clearTimeout(this.subkeyDeferment.timerId); - this.subkeyDeferment.resolve(null); - this.subkeyDeferment = null; + if(this.subkeyDelayTimer) { + window.clearTimeout(this.subkeyDelayTimer); + this.subkeyDelayTimer = null; } } @@ -1695,34 +1693,33 @@ namespace com.keyman.osk { */ touchHold(key: KeyElement, force?: boolean) { // Clear and restart the popup timer - if(this.subkeyDeferment) { - window.clearTimeout(this.subkeyDeferment.timerId); + if(this.subkeyDelayTimer) { + window.clearTimeout(this.subkeyDelayTimer); // Cancel the potential subkey event. - this.subkeyDeferment.resolve(null); - this.subkeyDeferment = null; + this.subkeyDelayTimer = null; } if(typeof key['subKeys'] != 'undefined' && key['subKeys'] != null) { let _this = this; + // This Promise receives the user's selected subkey, should it exist. + // Note: the OSK's use of this Promise will allow for passive cancellation. + // If cancelled (so, `null`), the Promise ought remain unresolved. let promise = new Promise(function(resolve, reject) { let timerId = window.setTimeout( function() { // It's no longer deferred; it's being fulfilled. // Even if the actual subkey itself is still async. - _this.subkeyDeferment = null; + _this.subkeyDelayTimer = null; _this.clearPopup(); _this.showSubKeys(key, resolve); }, force ? 0 : _this.popupDelay); - _this.subkeyDeferment = { - timerId: timerId, - resolve: resolve - }; + _this.subkeyDelayTimer = timerId; }).then(function(keyEvent: text.KeyEvent) { + // Allow active cancellation, even if the source should allow passive. + // It's an easy and cheap null guard. if(keyEvent) { - // Do something. - console.log("Promise fulfilled for " + keyEvent.kName); PreProcessor.raiseKeyEvent(keyEvent); } }); From dc4532ecb3c723fda279357b5c61d38203609b75 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 17 Jun 2021 12:13:02 +0700 Subject: [PATCH 05/58] fix(web): base key rehighlighting during subkey event when embedded --- web/source/kmwembedded.ts | 1 + web/source/osk/visualKeyboard.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index c875f9cbe1..a6ad1cde2f 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -66,6 +66,7 @@ namespace com.keyman.osk { let delegator = new embedded.SubkeyDelegator(key, resolve); _this.subkeyDelegator = delegator; }).then(function(keyEvent) { + _this.subkeyDelegator = null; // Allow active cancellation, even if the source should allow passive. // It's an easy and cheap null guard. if(keyEvent) { diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 4b6b67418b..592e32c575 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -725,7 +725,8 @@ namespace com.keyman.osk { this.keyPending=null; this.touchPending=null; } else { - if(this.subkeyPopup && key1 == this.subkeyPopup.baseKey) { + // Re-apply highlighting to the key if it's the current popup's base key. + if(this.subkeyDelegator && key1 == this.subkeyDelegator.baseKey) { if(!key1.classList.contains('kmw-key-touched')) { this.highlightKey(key1,true); } From 2d06c65412e7ab376b8aca2e5f839d98dd6101ac Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 16 Jun 2021 16:10:49 +0700 Subject: [PATCH 06/58] refactor(web): simplifies embedded executePopupKey --- web/source/kmwembedded.ts | 45 +++++--------------------------- web/source/osk/visualKeyboard.ts | 11 +++++--- 2 files changed, 14 insertions(+), 42 deletions(-) diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index a6ad1cde2f..1996a17e48 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -360,8 +360,8 @@ namespace com.keyman.text { keymanweb.domManager.initActiveElement(Lelem); - var nextLayer: string; var delegator: com.keyman.osk.embedded.SubkeyDelegator = null; + let selectedKey: com.keyman.osk.OSKKeySpec = null; // This should be set if we're within this method... but it's best to guard against nulls here, just in case. if(osk.vkbd.subkeyDelegator) { delegator = osk.vkbd.subkeyDelegator as com.keyman.osk.embedded.SubkeyDelegator; @@ -371,15 +371,14 @@ namespace com.keyman.text { var found = false; if(baseKey.coreID == keyName) { - nextLayer = baseKey.nextlayer; + selectedKey = baseKey; found = true; } else { // Search for the specified subkey so we can retrieve its useful properties. // It should be within the popupBaseKey's subkey list. for(let subKey of baseKey.sk) { if(subKey.coreID == keyName) { - // ... to consider: why are we not just taking the keyspec wholesale right here? - nextLayer = subKey.nextlayer; + selectedKey = subKey; found = true; break; } @@ -395,42 +394,12 @@ namespace com.keyman.text { console.warn("No base key exists for the subkey being executed: '" + origArg + "'"); } - let Codes = com.keyman.text.Codes; - - // Check the virtual key - let Lkc: com.keyman.text.KeyEvent = { - Lmodifiers: keyShiftState, - Lstates: 0, - Lcode: Codes.keyCodes[keyName], - LisVirtualKey: true, - kName: keyName, - kNextLayer: nextLayer, - vkCode: null, // was originally undefined - isSynthetic: true, - device: keymanweb.util.device.coreSpec - }; - - // Process modifier key action - if(core.keyboardProcessor.selectLayer(Lkc, true)) { // ignores key's 'nextLayer' property for this check - return true; + let Lkc: com.keyman.text.KeyEvent = null; + if(selectedKey) { + Lkc = osk.vkbd.keyEventFromSpec(selectedKey as com.keyman.keyboards.ActiveKey, null); + Lkc.vkCode=Lkc.Lcode; } - // While we can't source the base KeyEvent properties for embedded subkeys the same way as native, - // we can handle many other pre-processing steps the same way with this common method. - core.keyboardProcessor.setSyntheticEventDefaults(Lkc); - - //if(!Lkc.Lcode) return false; // Value is now zero if not known (Build 347) - //Build 353: revert to prior test to try to fix lack of KMEI output, May 1, 2014 - if(isNaN(Lkc.Lcode) || !Lkc.Lcode) { - // Addresses modifier SHIFT keys. - if(nextLayer) { - core.keyboardProcessor.selectLayer(Lkc); - } - return false; - } - - Lkc.vkCode=Lkc.Lcode; - if(delegator) { delegator.resolve(Lkc); } diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index fbd09d3fce..5e6c407e81 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -960,8 +960,6 @@ namespace com.keyman.osk { // Turn off key highlighting (or preview) this.highlightKey(e,false); - let core = com.keyman.singleton.core; // only singleton-based ref currently needed here. - // Future note: we need to refactor osk.OSKKeySpec to instead be a 'tag field' for // keyboards.ActiveKey. (Prob with generics, allowing the Web-only parts to // be fully specified within the tag.) @@ -973,7 +971,13 @@ namespace com.keyman.osk { console.error("OSK key with ID '" + e.id + "', keyID '" + e.keyId + "' missing needed specification"); return null; } + + // Return the event object. + return this.keyEventFromSpec(keySpec, touch); + } + keyEventFromSpec(keySpec: keyboards.ActiveKey, touch?: Touch) { + let core = com.keyman.singleton.core; // only singleton-based ref currently needed here. // Start: mirrors _GetKeyEventProperties @@ -994,8 +998,7 @@ namespace com.keyman.osk { Lkc.source = touch; Lkc.keyDistribution = this.getTouchProbabilities(touch);; } - - // Return the event object. + return Lkc; } From 50ee62a7e02cec5d70efc915f1848863a825d0e3 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 24 Jun 2021 09:45:27 +0700 Subject: [PATCH 07/58] change(web): subkey lookup through base key --- .../src/keyboards/activeLayout.ts | 4 ++-- web/source/kmwembedded.ts | 13 ++++--------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/common/core/web/keyboard-processor/src/keyboards/activeLayout.ts b/common/core/web/keyboard-processor/src/keyboards/activeLayout.ts index 6cdddf7d83..442d23de64 100644 --- a/common/core/web/keyboard-processor/src/keyboards/activeLayout.ts +++ b/common/core/web/keyboard-processor/src/keyboards/activeLayout.ts @@ -221,10 +221,10 @@ namespace com.keyman.keyboards { return Lkc; } - public getSubkey(id: string): ActiveKey { + public getSubkey(coreID: string): ActiveKey { if(this.sk) { for(let key of this.sk) { - if(key.id == id) { + if(key.coreID == coreID) { return key; } } diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index 1996a17e48..6e05faf83b 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -374,15 +374,10 @@ namespace com.keyman.text { selectedKey = baseKey; found = true; } else { - // Search for the specified subkey so we can retrieve its useful properties. - // It should be within the popupBaseKey's subkey list. - for(let subKey of baseKey.sk) { - if(subKey.coreID == keyName) { - selectedKey = subKey; - found = true; - break; - } - } + // ... yeah, there are some funky type shenanigans between the two. + // OSKKeySpec is the OSK's... reinterpretation of the ActiveKey type. + selectedKey = (baseKey as com.keyman.keyboards.ActiveKey).getSubkey(keyName) as com.keyman.osk.OSKKeySpec; + found = !!selectedKey; } if(!found) { From d4d9808727fa5224cbb88ae4d99f25615ae74ed2 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 17 Jun 2021 14:33:13 +0700 Subject: [PATCH 08/58] refactor(web): in-browser subkey tracking managed by SubkeyPopup --- web/source/osk/browser/subkeyPopup.ts | 40 +++++++++- web/source/osk/visualKeyboard.ts | 109 +++++++++----------------- 2 files changed, 74 insertions(+), 75 deletions(-) diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index c991ef38fc..7c7ecdf260 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -4,7 +4,10 @@ namespace com.keyman.osk.browser { export class SubkeyPopup { public readonly element: HTMLDivElement; public readonly shim: HTMLDivElement; + + private vkbd: VisualKeyboard; public readonly baseKey: KeyElement; + private currentSelection: KeyElement; private callout: HTMLDivElement; @@ -15,6 +18,10 @@ namespace com.keyman.osk.browser { let keyman = com.keyman.singleton; this.resolver = resolve; + this.vkbd = vkbd; + this.baseKey = e; + this.currentSelection = null; + // A tag we directly set on a key element during its construction. let subKeySpec: OSKKeySpec[] = e['subKeys']; @@ -22,7 +29,7 @@ namespace com.keyman.osk.browser { // is possible while the array is visible. So it is simplest to let the keys have // position:static and display:inline-block var subKeys = this.element = document.createElement('div'); - this.baseKey = e; + var i; subKeys.id='kmw-popup-keys'; @@ -76,8 +83,12 @@ namespace com.keyman.osk.browser { } } - resolve(keyEvent: text.KeyEvent) { + finalize(touch: Touch) { if(this.resolver) { + let keyEvent: text.KeyEvent = null; + if(this.currentSelection) { + keyEvent = this.vkbd.initKeyEvent(this.currentSelection, touch); + } this.resolver(keyEvent); } this.resolver = null; @@ -211,5 +222,30 @@ namespace com.keyman.osk.browser { this.callout.parentNode.removeChild(this.callout); } } + + updateTouch(touch: Touch) { + let x = touch.clientX; + let y = touch.clientY; + + this.currentSelection = null; + + for(let i=0; i < this.baseKey['subKeys'].length; i++) { + try { + let sk= this.element.childNodes[i].firstChild as KeyElement; + let x0 = dom.Utils.getAbsoluteX(sk); + let y0 = dom.Utils.getAbsoluteY(sk);//-document.body.scrollTop; + + let x1=x0+sk.offsetWidth; + let y1=y0+sk.offsetHeight; + + let onKey=(x > x0 && x < x1 && y > y0 && y < y1); + if(onKey) { + this.baseKey.key.highlight(false); + this.currentSelection = sk; + } + sk.key.highlight(onKey); + } catch(ex){} + } + } } } \ No newline at end of file diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 5e6c407e81..f4072a1585 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -566,12 +566,11 @@ namespace com.keyman.osk { this.highlightKey(this.keyPending, false); if(this.subkeyPopup) { - let keyEvent = this.initKeyEvent(this.keyPending, this.touchPending); - this.subkeyPopup.resolve(keyEvent); + this.subkeyPopup.updateTouch(e.changedTouches[0]); + this.subkeyPopup.finalize(e.changedTouches[0]); } else { this.modelKeyClick(this.keyPending, this.touchPending); } - this.clearPopup(); // Decrement the number of unreleased touch points to prevent // sending the keystroke again when the key is actually released this.touchCount--; @@ -597,19 +596,18 @@ namespace com.keyman.osk { // Clear repeated backspace if active, preventing 'sticky' behavior. this.cancelDelete(); - if((sk && sk.style.visibility == 'visible')) { + if((this.subkeyPopup && this.subkeyPopup.element.style.visibility == 'visible')) { // Ignore release if a multiple touch if(e.touches.length > 0) { return; } - // Cancel (but do not execute) pending key if neither a popup key or the base key - if((t == null) || (!(t.key instanceof browser.OSKSubKey) && (t.id != this.subkeyPopup.baseKey.id))) { - this.highlightKey(this.keyPending,false); - this.clearPopup(); - this.keyPending = null; - this.touchPending = null; - } + this.subkeyPopup.finalize(e.changedTouches[0]); + this.highlightKey(this.keyPending,false); + this.keyPending = null; + this.touchPending = null; + + return; } // Only set when embedded in our Android/iOS app. Signals that the device is handling @@ -643,12 +641,7 @@ namespace com.keyman.osk { // Output character unless moved off key if(this.keyPending.className.indexOf('hidden') < 0 && tc > 0 && !beyondEdge) { - if(this.subkeyPopup) { - let keyEvent = this.initKeyEvent(this.keyPending, e.changedTouches[0]); - this.subkeyPopup.resolve(keyEvent); - } else { - this.modelKeyClick(this.keyPending, e.changedTouches[0]); - } + this.modelKeyClick(this.keyPending, e.changedTouches[0]); } this.clearPopup(); this.keyPending = null; @@ -716,6 +709,11 @@ namespace com.keyman.osk { return; } + // Clear previous key highlighting + if(!this.subkeyPopup && key0 && key1 && key1 !== key0) { + this.highlightKey(key0,false); + } + // Do not move over keys if device popup visible if(this.popupVisible) { if(key1 == null) { @@ -743,51 +741,33 @@ namespace com.keyman.osk { return; } - var sk=this.subkeyPopup + this.currentTarget = null; - // Use the popup duplicate of the base key if a phone with a visible popup array - if(sk && sk.element.style.visibility == 'visible' && this.device.formFactor == 'phone' && key1 == sk.baseKey) { - key1 = sk.element.childNodes[0].firstChild; + // If popup is visible, need to move over popup, not over main keyboard + this.highlightSubKeys(key1,x,y); + + if(this.subkeyPopup) { + this.subkeyPopup.updateTouch(e.touches[0]); + return; } // Identify current touch position (to manage off-key release) this.currentTarget = key1; - // Clear previous key highlighting - if(key0 && key1 && key1 !== key0) { + // _Box has (most of) the useful client values. + let _Box = this.kbdDiv.parentElement ? this.kbdDiv.parentElement : keyman.osk._Box; + let height = this.kbdDiv.offsetHeight; + // We need to adjust the offset properties by any offsets related to the active banner. + + // Determine the y-threshold at which touch-cancellation should automatically occur. + let rowCount = this.layers[this.layerIndex].row.length; + let yBufferThreshold = (0.333 * height / rowCount); // Allows vertical movement by 1/3 the height of a row. + var yMin = (this.kbdDiv && _Box) ? Math.max(5, this.kbdDiv.offsetTop - yBufferThreshold) : 5; + if(key0 && e.touches[0].pageY < yMin) { this.highlightKey(key0,false); - } - - // If popup is visible, need to move over popup, not over main keyboard - this.highlightSubKeys(key1,x,y); - - if(sk && sk.element.style.visibility == 'visible') { - // Once a subkey array is displayed, do not allow changing the base key. - // Keep that array visible and accept no other options until the touch ends. - if(key1 && key1.id.indexOf('popup') < 0 && key1 != this.subkeyPopup.baseKey) { - return; - } - - // Highlight the base key on devices that do not append it to the subkey array. - if(key1 && key1 == this.subkeyPopup.baseKey) { - this.highlightKey(key1,true); - } - // Cancel touch if moved up and off keyboard, unless popup keys visible - } else { - // _Box has (most of) the useful client values. - let _Box = this.kbdDiv.parentElement ? this.kbdDiv.parentElement : keyman.osk._Box; - let height = this.kbdDiv.offsetHeight; - - // Determine the y-threshold at which touch-cancellation should automatically occur. - let rowCount = this.layers[this.layerIndex].row.length; - let yBufferThreshold = (0.333 * height / rowCount); // Allows vertical movement by 1/3 the height of a row. - var yMin = (this.kbdDiv && _Box) ? Math.max(5, this.kbdDiv.offsetTop - yBufferThreshold) : 5; - if(key0 && e.touches[0].pageY < yMin) { - this.highlightKey(key0,false); - this.showKeyTip(null,false); - this.keyPending = null; - this.touchPending = null; - } + this.showKeyTip(null,false); + this.keyPending = null; + this.touchPending = null; } // Replace the target key, if any, by the new target key @@ -806,24 +786,6 @@ namespace com.keyman.osk { if(key0 && key1 && (key1 != key0) && (key1.id != '')) { // Display the touch-hold keys (after a pause) this.touchHold(key1); - /* - // Clear and restart the popup timer - if(this.subkeyDelayTimer) - { - window.clearTimeout(this.subkeyDelayTimer); - this.subkeyDelayTimer = null; - } - if(key1.subKeys != null) - { - this.subkeyDelayTimer = window.setTimeout( - function() - { - this.clearPopup(); - this.showSubKeys(key1); - }.bind(this), - this.popupDelay); - } - */ } }.bind(this); @@ -1728,6 +1690,7 @@ namespace com.keyman.osk { if(keyEvent) { PreProcessor.raiseKeyEvent(keyEvent); } + _this.clearPopup(); }); } }; From 125a2ec0d0377b76eae9acf2b3353f72342a1475 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 17 Jun 2021 15:48:11 +0700 Subject: [PATCH 09/58] refactor(web): two-stages embedded longpress/subkey --- web/source/kmwembedded.ts | 40 ++++++++++++++------- web/source/osk/banner.ts | 2 +- web/source/osk/embedded/pendingLongpress.ts | 25 +++++++++++++ web/source/osk/embedded/subkeyDelegator.ts | 9 +++-- web/source/osk/visualKeyboard.ts | 8 ++--- 5 files changed, 65 insertions(+), 19 deletions(-) create mode 100644 web/source/osk/embedded/pendingLongpress.ts diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index 6e05faf83b..24524ef150 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -2,7 +2,7 @@ // References the base Keyman object (and consequently, the rest of the core objects). /// /// -/// +/// // KeymanWeb 11.0 // Copyright 2019 SIL International @@ -62,17 +62,21 @@ namespace com.keyman.osk { // #3718: No longer prepend base key to subkey array let _this = this; - let promise = new Promise(function(resolve, reject) { - let delegator = new embedded.SubkeyDelegator(key, resolve); + let pendingLongpress = new embedded.PendingLongpress(key); + pendingLongpress.promise.then(function(delegator) { _this.subkeyDelegator = delegator; - }).then(function(keyEvent) { - _this.subkeyDelegator = null; - // Allow active cancellation, even if the source should allow passive. - // It's an easy and cheap null guard. - if(keyEvent) { - PreProcessor.raiseKeyEvent(keyEvent); + if(delegator) { + delegator.promise.then(function(keyEvent) { + _this.subkeyDelegator = null; + // Allow active cancellation, even if the source should allow passive. + // It's an easy and cheap null guard. + if(keyEvent) { + PreProcessor.raiseKeyEvent(keyEvent); + } + }); } }); + this.embeddedPendingLongpress = pendingLongpress; window['oskCreatePopup'](key['subKeys'], xBase, yBase, key.offsetWidth, key.offsetHeight); } @@ -287,9 +291,21 @@ namespace com.keyman.text { * @param {boolean} isVisible * **/ - keymanweb['popupVisible'] = function(isVisible) - { - osk.vkbd.popupVisible = isVisible; + keymanweb['popupVisible'] = function(isVisible) { + let delegator = osk.vkbd.subkeyDelegator as com.keyman.osk.embedded.SubkeyDelegator; + let pendingLongpress = osk.vkbd.embeddedPendingLongpress as com.keyman.osk.embedded.PendingLongpress; + + if(!isVisible) { + if(delegator) { + delegator.resolve(null); + osk.vkbd.subkeyDelegator = null; + } + } + + if(isVisible && osk.vkbd.embeddedPendingLongpress) { + // Fulfills the first-stage promise. + pendingLongpress.markActiveSubkeys(); + } }; /** diff --git a/web/source/osk/banner.ts b/web/source/osk/banner.ts index 7d69399876..3eaba765a2 100644 --- a/web/source/osk/banner.ts +++ b/web/source/osk/banner.ts @@ -493,7 +493,7 @@ namespace com.keyman.osk { // Utilized by the mobile apps; allows them to 'take over' touch handling, // blocking it within KMW when the apps are already managing an ongoing touch-hold. let keyman = com.keyman.singleton; - return keyman['osk'].vkbd.popupVisible; + return keyman['osk'].vkbd.subkeyDelegator; } protected dealiasSubTarget(target: HTMLDivElement): HTMLDivElement { diff --git a/web/source/osk/embedded/pendingLongpress.ts b/web/source/osk/embedded/pendingLongpress.ts new file mode 100644 index 0000000000..bff829a472 --- /dev/null +++ b/web/source/osk/embedded/pendingLongpress.ts @@ -0,0 +1,25 @@ +/// + +namespace com.keyman.osk.embedded { + export class PendingLongpress { + private resolver: (delegator: SubkeyDelegator) => void; + + public readonly baseKey: KeyElement; + public readonly promise: Promise; + + constructor(e: KeyElement) { + let _this = this; + this.promise = new Promise(function(resolve) { + _this.resolver = resolve; + }); + this.baseKey = e; + } + + public markActiveSubkeys() { + if(this.resolver) { + this.resolver(new SubkeyDelegator(this.baseKey)); + } + this.resolver = null; + } + } +} \ No newline at end of file diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts index de1df534f5..0005f47449 100644 --- a/web/source/osk/embedded/subkeyDelegator.ts +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -3,9 +3,14 @@ namespace com.keyman.osk.embedded { private resolver: (keyEvent: text.KeyEvent) => void; public readonly baseKey: KeyElement; + public readonly promise: Promise; + + constructor(e: KeyElement) { + let _this = this; + this.promise = new Promise(function(resolve) { + _this.resolver = resolve; + }); - constructor(e: KeyElement, resolve: (keyEvent: text.KeyEvent) => void) { - this.resolver = resolve; this.baseKey = e; } diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index f4072a1585..d0b2681797 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -45,7 +45,6 @@ namespace com.keyman.osk { // State-related properties ddOSK: boolean = false; - popupVisible: boolean; keyPending: KeyElement; touchPending: Touch; deleteKey: KeyElement; @@ -70,6 +69,7 @@ namespace com.keyman.osk { menuEvent: KeyElement; // Used by embedded-mode. keytip: KeyTip; subkeyPopup: browser.SubkeyPopup; + embeddedPendingLongpress: any; // a temp, intermediate property during subkey abstraction work subkeyDelegator: any; // a temp, intermediate property during subkey abstraction work get layerId(): string { @@ -518,7 +518,7 @@ namespace com.keyman.osk { // Prevent multi-touch if popup displayed var sk = document.getElementById('kmw-popup-keys'); - if((sk && sk.style.visibility == 'visible') || this.popupVisible) { + if((sk && sk.style.visibility == 'visible') || this.subkeyDelegator) { return; } @@ -615,7 +615,7 @@ namespace com.keyman.osk { // // Note that on iOS (at least), this.release() will trigger before kmwembedded.ts's // executePopupKey() function. - if(this.popupVisible) { + if(this.subkeyDelegator) { return; } @@ -715,7 +715,7 @@ namespace com.keyman.osk { } // Do not move over keys if device popup visible - if(this.popupVisible) { + if(this.subkeyDelegator) { if(key1 == null) { if(key0) { this.highlightKey(key0,false); From d99203545b6f6e70b82e0c7f1877270b6d407eaf Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 24 Jun 2021 10:05:35 +0700 Subject: [PATCH 10/58] refactor(web): encapsulates embedded popup-key lookup ops --- web/source/kmwembedded.ts | 34 ++++------------------ web/source/osk/embedded/subkeyDelegator.ts | 28 +++++++++++++++++- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index 24524ef150..9644141d10 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -297,7 +297,7 @@ namespace com.keyman.text { if(!isVisible) { if(delegator) { - delegator.resolve(null); + delegator.resolve(null, null); osk.vkbd.subkeyDelegator = null; } } @@ -377,43 +377,21 @@ namespace com.keyman.text { keymanweb.domManager.initActiveElement(Lelem); var delegator: com.keyman.osk.embedded.SubkeyDelegator = null; - let selectedKey: com.keyman.osk.OSKKeySpec = null; // This should be set if we're within this method... but it's best to guard against nulls here, just in case. if(osk.vkbd.subkeyDelegator) { delegator = osk.vkbd.subkeyDelegator as com.keyman.osk.embedded.SubkeyDelegator; - // This is set with the base key of our current subkey elsewhere within the engine. - var baseKey: com.keyman.osk.OSKKeySpec = delegator.baseKey.key.spec; - var found = false; - - if(baseKey.coreID == keyName) { - selectedKey = baseKey; - found = true; - } else { - // ... yeah, there are some funky type shenanigans between the two. - // OSKKeySpec is the OSK's... reinterpretation of the ActiveKey type. - selectedKey = (baseKey as com.keyman.keyboards.ActiveKey).getSubkey(keyName) as com.keyman.osk.OSKKeySpec; - found = !!selectedKey; - } - - if(!found) { - console.warn("Could not find subkey '" + origArg + "' under the current base key '" + baseKey.coreID + "'!"); + try { + delegator.resolve(keyName, osk.vkbd); + } catch (e) { + let err = e as Error; + console.warn(err.message); } osk.vkbd.subkeyDelegator = null; } else { console.warn("No base key exists for the subkey being executed: '" + origArg + "'"); } - - let Lkc: com.keyman.text.KeyEvent = null; - if(selectedKey) { - Lkc = osk.vkbd.keyEventFromSpec(selectedKey as com.keyman.keyboards.ActiveKey, null); - Lkc.vkCode=Lkc.Lcode; - } - - if(delegator) { - delegator.resolve(Lkc); - } }; /** diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts index 0005f47449..3b6b844b3f 100644 --- a/web/source/osk/embedded/subkeyDelegator.ts +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -14,8 +14,34 @@ namespace com.keyman.osk.embedded { this.baseKey = e; } - public resolve(keyEvent: text.KeyEvent) { + public resolve(keyCoreID: string, vkbd: VisualKeyboard) { if(this.resolver) { + // This is set with the base key of our current subkey elsewhere within the engine. + var baseKey: OSKKeySpec = this.baseKey.key.spec; + var found = false; + let selectedKey: OSKKeySpec; + + if(baseKey.coreID == keyCoreID) { + selectedKey = baseKey; + found = true; + } else { + // ... yeah, there are some funky type shenanigans between the two. + // OSKKeySpec is the OSK's... reinterpretation of the ActiveKey type. + selectedKey = (baseKey as keyboards.ActiveKey).getSubkey(keyCoreID) as OSKKeySpec; + found = !!selectedKey; + } + + if(!found) { + this.resolver(null); // Maintains existing behavior. + throw new Error("Could not find subkey '" + keyCoreID + "' under base key '" + baseKey.coreID + "'!"); + } + + let keyEvent: text.KeyEvent = null; + if(selectedKey) { + keyEvent = vkbd.keyEventFromSpec(selectedKey as keyboards.ActiveKey, null); + keyEvent.vkCode=keyEvent.Lcode; + } + this.resolver(keyEvent); } this.resolver = null; From 39ec2cbb2ead4d4179e80affcd7b3e40e7bc10b8 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 24 Jun 2021 10:15:49 +0700 Subject: [PATCH 11/58] change(web): embedded longpress tracks source vkbd --- web/source/kmwembedded.ts | 6 +++--- web/source/osk/embedded/pendingLongpress.ts | 6 ++++-- web/source/osk/embedded/subkeyDelegator.ts | 9 ++++++--- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index 9644141d10..4064dd9149 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -62,7 +62,7 @@ namespace com.keyman.osk { // #3718: No longer prepend base key to subkey array let _this = this; - let pendingLongpress = new embedded.PendingLongpress(key); + let pendingLongpress = new embedded.PendingLongpress(this, key); pendingLongpress.promise.then(function(delegator) { _this.subkeyDelegator = delegator; if(delegator) { @@ -297,7 +297,7 @@ namespace com.keyman.text { if(!isVisible) { if(delegator) { - delegator.resolve(null, null); + delegator.resolve(null); osk.vkbd.subkeyDelegator = null; } } @@ -382,7 +382,7 @@ namespace com.keyman.text { delegator = osk.vkbd.subkeyDelegator as com.keyman.osk.embedded.SubkeyDelegator; try { - delegator.resolve(keyName, osk.vkbd); + delegator.resolve(keyName); } catch (e) { let err = e as Error; console.warn(err.message); diff --git a/web/source/osk/embedded/pendingLongpress.ts b/web/source/osk/embedded/pendingLongpress.ts index bff829a472..8aa1983bc7 100644 --- a/web/source/osk/embedded/pendingLongpress.ts +++ b/web/source/osk/embedded/pendingLongpress.ts @@ -3,11 +3,13 @@ namespace com.keyman.osk.embedded { export class PendingLongpress { private resolver: (delegator: SubkeyDelegator) => void; + private readonly vkbd: VisualKeyboard; public readonly baseKey: KeyElement; public readonly promise: Promise; - constructor(e: KeyElement) { + constructor(vkbd: VisualKeyboard, e: KeyElement) { + this.vkbd = vkbd; let _this = this; this.promise = new Promise(function(resolve) { _this.resolver = resolve; @@ -17,7 +19,7 @@ namespace com.keyman.osk.embedded { public markActiveSubkeys() { if(this.resolver) { - this.resolver(new SubkeyDelegator(this.baseKey)); + this.resolver(new SubkeyDelegator(this.vkbd, this.baseKey)); } this.resolver = null; } diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts index 3b6b844b3f..719d655ffe 100644 --- a/web/source/osk/embedded/subkeyDelegator.ts +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -1,11 +1,14 @@ namespace com.keyman.osk.embedded { export class SubkeyDelegator { private resolver: (keyEvent: text.KeyEvent) => void; + private readonly vkbd: VisualKeyboard; public readonly baseKey: KeyElement; public readonly promise: Promise; - constructor(e: KeyElement) { + constructor(vkbd: VisualKeyboard, e: KeyElement) { + this.vkbd = vkbd; + let _this = this; this.promise = new Promise(function(resolve) { _this.resolver = resolve; @@ -14,7 +17,7 @@ namespace com.keyman.osk.embedded { this.baseKey = e; } - public resolve(keyCoreID: string, vkbd: VisualKeyboard) { + public resolve(keyCoreID: string) { if(this.resolver) { // This is set with the base key of our current subkey elsewhere within the engine. var baseKey: OSKKeySpec = this.baseKey.key.spec; @@ -38,7 +41,7 @@ namespace com.keyman.osk.embedded { let keyEvent: text.KeyEvent = null; if(selectedKey) { - keyEvent = vkbd.keyEventFromSpec(selectedKey as keyboards.ActiveKey, null); + keyEvent = this.vkbd.keyEventFromSpec(selectedKey as keyboards.ActiveKey, null); keyEvent.vkCode=keyEvent.Lcode; } From 0359a68c154e37de15d4dfd6a5580aee50cca96a Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 23 Jun 2021 14:24:39 +0700 Subject: [PATCH 12/58] fix(web): prevent error when attempting to reselect base key (embedded) --- web/source/osk/embedded/subkeyDelegator.ts | 49 ++++++++++++---------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts index 719d655ffe..ee70bbccc7 100644 --- a/web/source/osk/embedded/subkeyDelegator.ts +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -19,30 +19,33 @@ namespace com.keyman.osk.embedded { public resolve(keyCoreID: string) { if(this.resolver) { - // This is set with the base key of our current subkey elsewhere within the engine. - var baseKey: OSKKeySpec = this.baseKey.key.spec; - var found = false; - let selectedKey: OSKKeySpec; - - if(baseKey.coreID == keyCoreID) { - selectedKey = baseKey; - found = true; - } else { - // ... yeah, there are some funky type shenanigans between the two. - // OSKKeySpec is the OSK's... reinterpretation of the ActiveKey type. - selectedKey = (baseKey as keyboards.ActiveKey).getSubkey(keyCoreID) as OSKKeySpec; - found = !!selectedKey; - } - - if(!found) { - this.resolver(null); // Maintains existing behavior. - throw new Error("Could not find subkey '" + keyCoreID + "' under base key '" + baseKey.coreID + "'!"); - } - let keyEvent: text.KeyEvent = null; - if(selectedKey) { - keyEvent = this.vkbd.keyEventFromSpec(selectedKey as keyboards.ActiveKey, null); - keyEvent.vkCode=keyEvent.Lcode; + + if(keyCoreID != null) { + // This is set with the base key of our current subkey elsewhere within the engine. + var baseKey: OSKKeySpec = this.baseKey.key.spec; + var found = false; + let selectedKey: OSKKeySpec; + + if(baseKey.coreID == keyCoreID) { + selectedKey = baseKey; + found = true; + } else { + // ... yeah, there are some funky type shenanigans between the two. + // OSKKeySpec is the OSK's... reinterpretation of the ActiveKey type. + selectedKey = (baseKey as keyboards.ActiveKey).getSubkey(keyCoreID) as OSKKeySpec; + found = !!selectedKey; + } + + if(!found) { + this.resolver(null); // Maintains existing behavior. + throw new Error("Could not find subkey '" + keyCoreID + "' under base key '" + baseKey.coreID + "'!"); + } + + if(selectedKey) { + keyEvent = this.vkbd.keyEventFromSpec(selectedKey as keyboards.ActiveKey, null); + keyEvent.vkCode=keyEvent.Lcode; + } } this.resolver(keyEvent); From 36536f89bc398b05d55dc3e14509101a15142d58 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 17 Jun 2021 16:26:50 +0700 Subject: [PATCH 13/58] refactor(web): browser subkey control flow abstraction prep --- web/source/kmwembedded.ts | 2 - web/source/osk/browser/pendingLongpress.ts | 60 ++++++++++++++ web/source/osk/browser/subkeyPopup.ts | 10 ++- web/source/osk/visualKeyboard.ts | 95 ++++++++-------------- 4 files changed, 104 insertions(+), 63 deletions(-) create mode 100644 web/source/osk/browser/pendingLongpress.ts diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index 4064dd9149..d1d54dbc65 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -150,8 +150,6 @@ namespace com.keyman.text { // Skip full page initialization - skips native-mode only code keymanweb.isEmbedded = true; - com.keyman.osk.VisualKeyboard.prototype.popupDelay = 400; // Delay must be less than native touch-hold delay - // Set default device options keymanweb.setDefaultDeviceOptions = function(opt) { opt['attachType'] = 'manual'; diff --git a/web/source/osk/browser/pendingLongpress.ts b/web/source/osk/browser/pendingLongpress.ts new file mode 100644 index 0000000000..038478924b --- /dev/null +++ b/web/source/osk/browser/pendingLongpress.ts @@ -0,0 +1,60 @@ +/// + +namespace com.keyman.osk.browser { + export class PendingLongpress { + public readonly baseKey: KeyElement; + //public readonly initialTouch: Touch; + + public readonly subkeyUI: SubkeyPopup; + + public readonly promise: Promise; + + private readonly vkbd: VisualKeyboard; + private resolver: (subkeyPopup: SubkeyPopup) => void; + + private timerId: number; + private popupDelay: number = 500; + + constructor(vkbd: VisualKeyboard, baseKey: KeyElement/*, initialTouch: Touch*/) { + this.vkbd = vkbd; + this.baseKey = baseKey; + //this.initialTouch = initialTouch; + + let _this = this; + this.promise = new Promise(function(resolve, reject) { + _this.timerId = window.setTimeout( + function() { + // It's no longer deferred; it's being fulfilled. + // Even if the actual subkey itself is still async. + _this.showSubkeys(); + }, _this.popupDelay); + }); + + this.promise = new Promise(function(resolve) { + _this.resolver = resolve; + }); + } + + updateTouch(touch: Touch) { + this.subkeyUI.updateTouch(touch); + } + + public cancel() { + if(this.timerId) { + window.clearTimeout(this.timerId); + this.timerId = null; + } + + if(this.resolver) { + this.resolver(null); + this.resolver = null; + } + } + + public showSubkeys() { + if(this.resolver) { + this.resolver(new SubkeyPopup(this.vkbd, this.baseKey)); + } + } + } +} \ No newline at end of file diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index 7c7ecdf260..3c52532399 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -11,12 +11,18 @@ namespace com.keyman.osk.browser { private callout: HTMLDivElement; + public promise: Promise; + // Resolves the promise that generated this SubkeyPopup. private resolver: (keyEvent: text.KeyEvent) => void; - constructor(vkbd: VisualKeyboard, e: KeyElement, resolve: (keyEvent: text.KeyEvent) => void) { + constructor(vkbd: VisualKeyboard, e: KeyElement) { let keyman = com.keyman.singleton; - this.resolver = resolve; + let _this = this; + + this.promise = new Promise(function(resolve) { + _this.resolver = resolve; + }) this.vkbd = vkbd; this.baseKey = e; diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index d0b2681797..a7737be260 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -3,14 +3,9 @@ /// /// /// -/// +/// namespace com.keyman.osk { - interface SubkeyDeferment { - timerId: number; - resolve: (KeyEvent) => void; - } - export class VisualKeyboard { // Legacy alias, maintaining a reference for code built against older // versions of KMW. @@ -64,11 +59,10 @@ namespace com.keyman.osk { currentTarget: KeyElement; // Popup key management - popupDelay: number = 500; - subkeyDelayTimer: number; menuEvent: KeyElement; // Used by embedded-mode. keytip: KeyTip; subkeyPopup: browser.SubkeyPopup; + browserPendingLongpress: browser.PendingLongpress; embeddedPendingLongpress: any; // a temp, intermediate property during subkey abstraction work subkeyDelegator: any; // a temp, intermediate property during subkey abstraction work @@ -1059,37 +1053,11 @@ namespace com.keyman.osk { this.subkeyPopup = null; } - if(this.subkeyDelayTimer) { - window.clearTimeout(this.subkeyDelayTimer); - this.subkeyDelayTimer = null; + if(this.browserPendingLongpress) { + this.browserPendingLongpress.cancel(); } } - //#region 'native'-mode subkey handling - /** - * Display touch-hold array of 'sub-keys' above the currently touched key - * @param {Object} e primary key element - */ - showSubKeys(e: KeyElement, resolve: (KeyElement) => void) { - // Do not show subkeys if key already released - if(this.keyPending == null) { - return; - } - - // Clear key preview if any - this.showKeyTip(null,false); - - let subKeys = this.subkeyPopup = new browser.SubkeyPopup(this, e, resolve); - - // Otherwise append the touch-hold (subkey) array to the OSK - let keyman = com.keyman.singleton; - keyman.osk._Box.appendChild(subKeys.element); - keyman.osk._Box.appendChild(subKeys.shim); - - // Must be placed after its `.element` has been inserted into the DOM. - subKeys.reposition(this); - } - //#endregion /** @@ -1661,37 +1629,46 @@ namespace com.keyman.osk { */ touchHold(key: KeyElement, force?: boolean) { // Clear and restart the popup timer - if(this.subkeyDelayTimer) { - window.clearTimeout(this.subkeyDelayTimer); - // Cancel the potential subkey event. - this.subkeyDelayTimer = null; + if(this.browserPendingLongpress) { + this.browserPendingLongpress.cancel(); } if(typeof key['subKeys'] != 'undefined' && key['subKeys'] != null) { let _this = this; - // This Promise receives the user's selected subkey, should it exist. - // Note: the OSK's use of this Promise will allow for passive cancellation. - // If cancelled (so, `null`), the Promise ought remain unresolved. - let promise = new Promise(function(resolve, reject) { - let timerId = window.setTimeout( - function() { - // It's no longer deferred; it's being fulfilled. - // Even if the actual subkey itself is still async. - _this.subkeyDelayTimer = null; - _this.clearPopup(); - _this.showSubKeys(key, resolve); - }, force ? 0 : _this.popupDelay); + // First-level object/Promise: will produce a subkey popup when the longpress gesture completes. + // 'Returns' a second-level object/Promise: resolves when a subkey is selected or is cancelled. + this.browserPendingLongpress = new browser.PendingLongpress(this, key); + this.browserPendingLongpress.promise.then(function(subkeyPopup) { + _this.browserPendingLongpress = null; + if(subkeyPopup) { + // Clear key preview if any + _this.showKeyTip(null,false); - _this.subkeyDelayTimer = timerId; - }).then(function(keyEvent: text.KeyEvent) { - // Allow active cancellation, even if the source should allow passive. - // It's an easy and cheap null guard. - if(keyEvent) { - PreProcessor.raiseKeyEvent(keyEvent); + _this.subkeyPopup = subkeyPopup; + subkeyPopup.promise.then(function(keyEvent: text.KeyEvent) { + // Allow active cancellation, even if the source should allow passive. + // It's an easy and cheap null guard. + if(keyEvent) { + PreProcessor.raiseKeyEvent(keyEvent); + } + _this.clearPopup(); + }); + + // Otherwise append the touch-hold (subkey) array to the OSK + let keyman = com.keyman.singleton; + keyman.osk._Box.appendChild(subkeyPopup.element); + keyman.osk._Box.appendChild(subkeyPopup.shim); + + // Must be placed after its `.element` has been inserted into the DOM. + subkeyPopup.reposition(_this); } - _this.clearPopup(); }); + + if(force) { + // Instantly resolves the first-level promise. + this.browserPendingLongpress.showSubkeys(); + } } }; From 2015ef792768bed8a6a61123b4350e6ec8fa3425 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 17 Jun 2021 16:27:53 +0700 Subject: [PATCH 14/58] chore(web): mild formatting tweak --- web/source/osk/visualKeyboard.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index a7737be260..25e21c89d4 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -1655,13 +1655,13 @@ namespace com.keyman.osk { _this.clearPopup(); }); - // Otherwise append the touch-hold (subkey) array to the OSK - let keyman = com.keyman.singleton; - keyman.osk._Box.appendChild(subkeyPopup.element); - keyman.osk._Box.appendChild(subkeyPopup.shim); + // Otherwise append the touch-hold (subkey) array to the OSK + let keyman = com.keyman.singleton; + keyman.osk._Box.appendChild(subkeyPopup.element); + keyman.osk._Box.appendChild(subkeyPopup.shim); - // Must be placed after its `.element` has been inserted into the DOM. - subkeyPopup.reposition(_this); + // Must be placed after its `.element` has been inserted into the DOM. + subkeyPopup.reposition(_this); } }); From 3a982049197871036549f522c05287db9fbb399c Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 23 Jun 2021 10:50:36 +0700 Subject: [PATCH 15/58] fix(web): pending-longpress field management --- web/source/osk/browser/pendingLongpress.ts | 5 +---- web/source/osk/visualKeyboard.ts | 10 ++++++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/web/source/osk/browser/pendingLongpress.ts b/web/source/osk/browser/pendingLongpress.ts index 038478924b..f55b0a2ef8 100644 --- a/web/source/osk/browser/pendingLongpress.ts +++ b/web/source/osk/browser/pendingLongpress.ts @@ -22,6 +22,7 @@ namespace com.keyman.osk.browser { let _this = this; this.promise = new Promise(function(resolve, reject) { + _this.resolver = resolve; _this.timerId = window.setTimeout( function() { // It's no longer deferred; it's being fulfilled. @@ -29,10 +30,6 @@ namespace com.keyman.osk.browser { _this.showSubkeys(); }, _this.popupDelay); }); - - this.promise = new Promise(function(resolve) { - _this.resolver = resolve; - }); } updateTouch(touch: Touch) { diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 25e21c89d4..630b8945f2 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -1055,6 +1055,7 @@ namespace com.keyman.osk { if(this.browserPendingLongpress) { this.browserPendingLongpress.cancel(); + this.browserPendingLongpress = null; } } @@ -1631,6 +1632,7 @@ namespace com.keyman.osk { // Clear and restart the popup timer if(this.browserPendingLongpress) { this.browserPendingLongpress.cancel(); + this.browserPendingLongpress = null; } if(typeof key['subKeys'] != 'undefined' && key['subKeys'] != null) { @@ -1638,9 +1640,13 @@ namespace com.keyman.osk { // First-level object/Promise: will produce a subkey popup when the longpress gesture completes. // 'Returns' a second-level object/Promise: resolves when a subkey is selected or is cancelled. - this.browserPendingLongpress = new browser.PendingLongpress(this, key); + let pl = this.browserPendingLongpress = new browser.PendingLongpress(this, key); this.browserPendingLongpress.promise.then(function(subkeyPopup) { - _this.browserPendingLongpress = null; + // Clear the longpress field upon any sort of fulfillment if it is still the current one. + if(_this.browserPendingLongpress == pl) { + _this.browserPendingLongpress = null; + } + if(subkeyPopup) { // Clear key preview if any _this.showKeyTip(null,false); From c3748202631512c301e9f6d096e5b266a82e81c3 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 17 Jun 2021 14:33:13 +0700 Subject: [PATCH 16/58] refactor(web): in-browser subkey tracking managed by SubkeyPopup --- web/source/osk/visualKeyboard.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 630b8945f2..9b65e59347 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -1669,6 +1669,7 @@ namespace com.keyman.osk { // Must be placed after its `.element` has been inserted into the DOM. subkeyPopup.reposition(_this); } + _this.clearPopup(); }); if(force) { From 0d109b12b0e860ffc207c700d415ee0e7d96e6fa Mon Sep 17 00:00:00 2001 From: jahorton Date: Tue, 22 Jun 2021 10:45:17 +0700 Subject: [PATCH 17/58] refactor(web): 'easy' part of abstracted RealizedGesture for subkeys --- web/source/kmwembedded.ts | 23 +++++----- web/source/osk/banner.ts | 2 +- web/source/osk/browser/pendingLongpress.ts | 7 +-- web/source/osk/browser/subkeyPopup.ts | 13 ++++-- web/source/osk/embedded/subkeyDelegator.ts | 12 +++++- web/source/osk/realizedGesture.interface.ts | 9 ++++ web/source/osk/visualKeyboard.ts | 48 +++++++++------------ 7 files changed, 63 insertions(+), 51 deletions(-) create mode 100644 web/source/osk/realizedGesture.interface.ts diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index d1d54dbc65..0f209dd726 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -63,11 +63,11 @@ namespace com.keyman.osk { let _this = this; let pendingLongpress = new embedded.PendingLongpress(this, key); - pendingLongpress.promise.then(function(delegator) { - _this.subkeyDelegator = delegator; - if(delegator) { - delegator.promise.then(function(keyEvent) { - _this.subkeyDelegator = null; + pendingLongpress.promise.then(function(gesture) { + _this.subkeyGesture = gesture; + if(gesture) { + gesture.promise.then(function(keyEvent) { + _this.subkeyGesture = null; // Allow active cancellation, even if the source should allow passive. // It's an easy and cheap null guard. if(keyEvent) { @@ -290,13 +290,13 @@ namespace com.keyman.text { * **/ keymanweb['popupVisible'] = function(isVisible) { - let delegator = osk.vkbd.subkeyDelegator as com.keyman.osk.embedded.SubkeyDelegator; + let delegator = osk.vkbd.subkeyGesture as com.keyman.osk.embedded.SubkeyDelegator; let pendingLongpress = osk.vkbd.embeddedPendingLongpress as com.keyman.osk.embedded.PendingLongpress; if(!isVisible) { if(delegator) { delegator.resolve(null); - osk.vkbd.subkeyDelegator = null; + osk.vkbd.subkeyGesture = null; } } @@ -374,19 +374,18 @@ namespace com.keyman.text { keymanweb.domManager.initActiveElement(Lelem); - var delegator: com.keyman.osk.embedded.SubkeyDelegator = null; // This should be set if we're within this method... but it's best to guard against nulls here, just in case. - if(osk.vkbd.subkeyDelegator) { - delegator = osk.vkbd.subkeyDelegator as com.keyman.osk.embedded.SubkeyDelegator; + if(osk.vkbd.subkeyGesture) { + let gesture = osk.vkbd.subkeyGesture as com.keyman.osk.embedded.SubkeyDelegator; try { - delegator.resolve(keyName); + gesture.resolve(keyName); } catch (e) { let err = e as Error; console.warn(err.message); } - osk.vkbd.subkeyDelegator = null; + osk.vkbd.subkeyGesture = null; } else { console.warn("No base key exists for the subkey being executed: '" + origArg + "'"); } diff --git a/web/source/osk/banner.ts b/web/source/osk/banner.ts index 3eaba765a2..0ba00a3920 100644 --- a/web/source/osk/banner.ts +++ b/web/source/osk/banner.ts @@ -493,7 +493,7 @@ namespace com.keyman.osk { // Utilized by the mobile apps; allows them to 'take over' touch handling, // blocking it within KMW when the apps are already managing an ongoing touch-hold. let keyman = com.keyman.singleton; - return keyman['osk'].vkbd.subkeyDelegator; + return keyman['osk'].vkbd.subkeyGesture && keyman.isEmbedded; } protected dealiasSubTarget(target: HTMLDivElement): HTMLDivElement { diff --git a/web/source/osk/browser/pendingLongpress.ts b/web/source/osk/browser/pendingLongpress.ts index f55b0a2ef8..08f89caca0 100644 --- a/web/source/osk/browser/pendingLongpress.ts +++ b/web/source/osk/browser/pendingLongpress.ts @@ -3,12 +3,11 @@ namespace com.keyman.osk.browser { export class PendingLongpress { public readonly baseKey: KeyElement; + public readonly promise: Promise; //public readonly initialTouch: Touch; public readonly subkeyUI: SubkeyPopup; - public readonly promise: Promise; - private readonly vkbd: VisualKeyboard; private resolver: (subkeyPopup: SubkeyPopup) => void; @@ -32,10 +31,6 @@ namespace com.keyman.osk.browser { }); } - updateTouch(touch: Touch) { - this.subkeyUI.updateTouch(touch); - } - public cancel() { if(this.timerId) { window.clearTimeout(this.timerId); diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index 3c52532399..4b35fa09ab 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -1,17 +1,18 @@ /// +/// namespace com.keyman.osk.browser { - export class SubkeyPopup { + export class SubkeyPopup implements RealizedGesture { public readonly element: HTMLDivElement; public readonly shim: HTMLDivElement; private vkbd: VisualKeyboard; - public readonly baseKey: KeyElement; private currentSelection: KeyElement; - + private callout: HTMLDivElement; - public promise: Promise; + public readonly baseKey: KeyElement; + public readonly promise: Promise; // Resolves the promise that generated this SubkeyPopup. private resolver: (keyEvent: text.KeyEvent) => void; @@ -209,6 +210,10 @@ namespace com.keyman.osk.browser { } } + isVisible(): boolean { + return this.element.style.visibility == 'visible'; + } + clear() { // Discard the reference to the Promise's resolve method, allowing // GC to clean it up. The corresponding Promise's contract allows diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts index ee70bbccc7..73b29efc22 100644 --- a/web/source/osk/embedded/subkeyDelegator.ts +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -1,5 +1,7 @@ +/// + namespace com.keyman.osk.embedded { - export class SubkeyDelegator { + export class SubkeyDelegator implements RealizedGesture { private resolver: (keyEvent: text.KeyEvent) => void; private readonly vkbd: VisualKeyboard; @@ -52,5 +54,13 @@ namespace com.keyman.osk.embedded { } this.resolver = null; } + + public isVisible(): boolean { + return true; + } + + public clear() { + // no-op; it's fully controlled on the app side. + } } } \ No newline at end of file diff --git a/web/source/osk/realizedGesture.interface.ts b/web/source/osk/realizedGesture.interface.ts new file mode 100644 index 0000000000..6b18cff188 --- /dev/null +++ b/web/source/osk/realizedGesture.interface.ts @@ -0,0 +1,9 @@ +namespace com.keyman.osk { + export interface RealizedGesture { + readonly baseKey: KeyElement; + readonly promise: Promise; + + clear(): void; + isVisible(): boolean; + } +} \ No newline at end of file diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 9b65e59347..6a05671312 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -61,10 +61,9 @@ namespace com.keyman.osk { // Popup key management menuEvent: KeyElement; // Used by embedded-mode. keytip: KeyTip; - subkeyPopup: browser.SubkeyPopup; browserPendingLongpress: browser.PendingLongpress; - embeddedPendingLongpress: any; // a temp, intermediate property during subkey abstraction work - subkeyDelegator: any; // a temp, intermediate property during subkey abstraction work + embeddedPendingLongpress: any; // a temp, intermediate property during subkey abstraction work + subkeyGesture: RealizedGesture; get layerId(): string { return this._layerId; @@ -423,7 +422,7 @@ namespace com.keyman.osk { let kbdAspectRatio = layerGroup.offsetWidth / this.kbdDiv.offsetHeight; let baseKeyProbabilities = this.layout.getLayer(this.layerId).getTouchProbabilities(touchKbdPos, kbdAspectRatio); - if(!this.subkeyPopup || !this.subkeyPopup.baseKey.key) { + if(!this.subkeyGesture || !this.subkeyGesture.baseKey.key) { return baseKeyProbabilities; } else { // A temp-hack, as this was noted just before 14.0's release. @@ -435,7 +434,7 @@ namespace com.keyman.osk { let baseMass = 1.0; let baseKeyMass = 1.0; - let baseKeyID = this.subkeyPopup.baseKey.key.spec.coreID; + let baseKeyID = this.subkeyGesture.baseKey.key.spec.coreID; let popupKeyMass = 0.0; let popupKeyID: string = null; @@ -512,7 +511,7 @@ namespace com.keyman.osk { // Prevent multi-touch if popup displayed var sk = document.getElementById('kmw-popup-keys'); - if((sk && sk.style.visibility == 'visible') || this.subkeyDelegator) { + if((sk && sk.style.visibility == 'visible') || this.subkeyGesture) { return; } @@ -559,9 +558,10 @@ namespace com.keyman.osk { if(this.keyPending) { this.highlightKey(this.keyPending, false); - if(this.subkeyPopup) { - this.subkeyPopup.updateTouch(e.changedTouches[0]); - this.subkeyPopup.finalize(e.changedTouches[0]); + if(this.subkeyGesture && this.subkeyGesture instanceof browser.SubkeyPopup) { + let subkeyPopup = this.subkeyGesture as browser.SubkeyPopup; + subkeyPopup.updateTouch(e.changedTouches[0]); + subkeyPopup.finalize(e.changedTouches[0]); } else { this.modelKeyClick(this.keyPending, this.touchPending); } @@ -585,18 +585,21 @@ namespace com.keyman.osk { **/ release: (e: TouchEvent) => void = function(this: VisualKeyboard, e: TouchEvent) { // Prevent incorrect multi-touch behaviour if native or device popup visible - var sk = document.getElementById('kmw-popup-keys'), t = this.currentTarget; + var t = this.currentTarget; // Clear repeated backspace if active, preventing 'sticky' behavior. this.cancelDelete(); - if((this.subkeyPopup && this.subkeyPopup.element.style.visibility == 'visible')) { + if((this.subkeyGesture && this.subkeyGesture.isVisible())) { // Ignore release if a multiple touch if(e.touches.length > 0) { return; } - this.subkeyPopup.finalize(e.changedTouches[0]); + if(this.subkeyGesture instanceof browser.SubkeyPopup) { + let subkeyPopup = this.subkeyGesture as browser.SubkeyPopup; + subkeyPopup.finalize(e.changedTouches[0]); + } this.highlightKey(this.keyPending,false); this.keyPending = null; this.touchPending = null; @@ -604,15 +607,6 @@ namespace com.keyman.osk { return; } - // Only set when embedded in our Android/iOS app. Signals that the device is handling - // subkeys, so we shouldn't allow output for the base key. - // - // Note that on iOS (at least), this.release() will trigger before kmwembedded.ts's - // executePopupKey() function. - if(this.subkeyDelegator) { - return; - } - // Handle menu key release event if(t && t.id) { this.optionKey(t, t.id, false); @@ -1048,9 +1042,9 @@ namespace com.keyman.osk { clearPopup() { // Remove the displayed subkey array, if any, and cancel popup request - if(this.subkeyPopup) { - this.subkeyPopup.clear(); - this.subkeyPopup = null; + if(this.subkeyGesture) { + this.subkeyGesture.clear(); + this.subkeyGesture = null; } if(this.browserPendingLongpress) { @@ -1651,7 +1645,7 @@ namespace com.keyman.osk { // Clear key preview if any _this.showKeyTip(null,false); - _this.subkeyPopup = subkeyPopup; + _this.subkeyGesture = subkeyPopup; subkeyPopup.promise.then(function(keyEvent: text.KeyEvent) { // Allow active cancellation, even if the source should allow passive. // It's an easy and cheap null guard. @@ -1734,8 +1728,8 @@ namespace com.keyman.osk { return; } - var sk=this.subkeyPopup, - popup = (sk && sk.element.style.visibility == 'visible') + let sk = this.subkeyGesture; + let popup = (sk && sk.isVisible()); // If popup keys are active, do not show the key tip. on = popup ? false : on; From d5e563524292a623dac041f943cea67bc6192519 Mon Sep 17 00:00:00 2001 From: jahorton Date: Tue, 22 Jun 2021 12:00:25 +0700 Subject: [PATCH 18/58] refactor(web): merged subkey highlighting control --- web/source/osk/browser/subkeyPopup.ts | 10 +---- web/source/osk/embedded/subkeyDelegator.ts | 4 ++ web/source/osk/oskKey.ts | 14 +++++++ web/source/osk/realizedGesture.interface.ts | 1 + web/source/osk/visualKeyboard.ts | 46 +++++++-------------- 5 files changed, 34 insertions(+), 41 deletions(-) diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index 4b35fa09ab..f927a8bd61 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -235,21 +235,13 @@ namespace com.keyman.osk.browser { } updateTouch(touch: Touch) { - let x = touch.clientX; - let y = touch.clientY; - this.currentSelection = null; for(let i=0; i < this.baseKey['subKeys'].length; i++) { try { let sk= this.element.childNodes[i].firstChild as KeyElement; - let x0 = dom.Utils.getAbsoluteX(sk); - let y0 = dom.Utils.getAbsoluteY(sk);//-document.body.scrollTop; - - let x1=x0+sk.offsetWidth; - let y1=y0+sk.offsetHeight; - let onKey=(x > x0 && x < x1 && y > y0 && y < y1); + let onKey = sk.key.isUnderTouch(touch) if(onKey) { this.baseKey.key.highlight(false); this.currentSelection = sk; diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts index 73b29efc22..3fc2d33bb4 100644 --- a/web/source/osk/embedded/subkeyDelegator.ts +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -62,5 +62,9 @@ namespace com.keyman.osk.embedded { public clear() { // no-op; it's fully controlled on the app side. } + + updateTouch(touch: Touch) { + this.baseKey.key.highlight(this.baseKey.key.isUnderTouch(touch)); + } } } \ No newline at end of file diff --git a/web/source/osk/oskKey.ts b/web/source/osk/oskKey.ts index f9e4509992..da52968507 100644 --- a/web/source/osk/oskKey.ts +++ b/web/source/osk/oskKey.ts @@ -430,5 +430,19 @@ namespace com.keyman.osk { return t; } + + public isUnderTouch(touch: Touch): boolean { + let x = touch.clientX; + let y = touch.clientY; + + let btn = this.btn; + let x0 = dom.Utils.getAbsoluteX(btn); + let y0 = dom.Utils.getAbsoluteY(btn);//-document.body.scrollTop; + + let x1=x0 + btn.offsetWidth; + let y1=y0 + btn.offsetHeight; + + return (x > x0 && x < x1 && y > y0 && y < y1); + } } } \ No newline at end of file diff --git a/web/source/osk/realizedGesture.interface.ts b/web/source/osk/realizedGesture.interface.ts index 6b18cff188..877164898a 100644 --- a/web/source/osk/realizedGesture.interface.ts +++ b/web/source/osk/realizedGesture.interface.ts @@ -5,5 +5,6 @@ namespace com.keyman.osk { clear(): void; isVisible(): boolean; + updateTouch(touch: Touch): void; } } \ No newline at end of file diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 6a05671312..ef4158c9de 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -510,8 +510,7 @@ namespace com.keyman.osk { this.cancelDelete(); // Prevent multi-touch if popup displayed - var sk = document.getElementById('kmw-popup-keys'); - if((sk && sk.style.visibility == 'visible') || this.subkeyGesture) { + if(this.subkeyGesture && this.subkeyGesture.isVisible()) { return; } @@ -697,45 +696,28 @@ namespace com.keyman.osk { return; } - // Clear previous key highlighting - if(!this.subkeyPopup && key0 && key1 && key1 !== key0) { - this.highlightKey(key0,false); - } - - // Do not move over keys if device popup visible - if(this.subkeyDelegator) { - if(key1 == null) { - if(key0) { - this.highlightKey(key0,false); - } - this.keyPending=null; - this.touchPending=null; - } else { - // Re-apply highlighting to the key if it's the current popup's base key. - if(this.subkeyDelegator && key1 == this.subkeyDelegator.baseKey) { - if(!key1.classList.contains('kmw-key-touched')) { - this.highlightKey(key1,true); - } - this.keyPending = key1; - this.touchPending = e.touches[0]; - } else { - if(key0) { - this.highlightKey(key0,false); - } - this.keyPending = null; - this.touchPending = null; - } + // Clear previous key highlighting, allow subkey controller to + // highlight as appropriate. + if(this.subkeyGesture) { + if(key0) { + key0.key.highlight(false); } + this.subkeyGesture.updateTouch(e.touches[0]); + + this.keyPending = null; + this.touchPending = null; + return; } this.currentTarget = null; // If popup is visible, need to move over popup, not over main keyboard + // TODO: responsible for the shortcutting gesture for early subkey display. this.highlightSubKeys(key1,x,y); - if(this.subkeyPopup) { - this.subkeyPopup.updateTouch(e.touches[0]); + // As the previous line can trigger the start of the subkey gesture... + if(this.subkeyGesture) { return; } From 60081b7c4a4be0d491556449c16410d8b97cd79c Mon Sep 17 00:00:00 2001 From: jahorton Date: Tue, 22 Jun 2021 13:14:11 +0700 Subject: [PATCH 19/58] refactor(web): initial mutual abstraction for pending longpresses --- web/source/kmwembedded.ts | 25 ++++---- web/source/osk/browser/pendingLongpress.ts | 7 ++- web/source/osk/embedded/pendingLongpress.ts | 13 +++- web/source/osk/pendingGesture.interface.ts | 9 +++ web/source/osk/visualKeyboard.ts | 67 ++++++++------------- 5 files changed, 60 insertions(+), 61 deletions(-) create mode 100644 web/source/osk/pendingGesture.interface.ts diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index 0f209dd726..2b7bae20f0 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -54,7 +54,6 @@ namespace com.keyman.osk { * @param {Object} key base key element */ VisualKeyboard.prototype.touchHold = function(this: VisualKeyboard, key: KeyElement) { - let util = com.keyman.singleton.util; if(key['subKeys'] && (typeof(window['oskCreatePopup']) == 'function')) { var xBase = dom.Utils.getAbsoluteX(key) - dom.Utils.getAbsoluteX(this.kbdDiv) + key.offsetWidth/2, yBase = dom.Utils.getAbsoluteY(key); @@ -62,6 +61,8 @@ namespace com.keyman.osk { // #3718: No longer prepend base key to subkey array let _this = this; + window['oskCreatePopup'](key['subKeys'], xBase, yBase, key.offsetWidth, key.offsetHeight); + let pendingLongpress = new embedded.PendingLongpress(this, key); pendingLongpress.promise.then(function(gesture) { _this.subkeyGesture = gesture; @@ -76,9 +77,7 @@ namespace com.keyman.osk { }); } }); - this.embeddedPendingLongpress = pendingLongpress; - - window['oskCreatePopup'](key['subKeys'], xBase, yBase, key.offsetWidth, key.offsetHeight); + this.pendingSubkey = pendingLongpress; } }; @@ -89,10 +88,6 @@ namespace com.keyman.osk { } }; - VisualKeyboard.prototype.highlightSubKeys = function(this: VisualKeyboard, k, x, y) { - // a dummy function; it's only really used for 'native' KMW. - } - VisualKeyboard.prototype.waitForFonts = function(this: VisualKeyboard, kfd, ofd) { // a dummy function; it's only really used for 'native' KMW. return true; @@ -290,19 +285,21 @@ namespace com.keyman.text { * **/ keymanweb['popupVisible'] = function(isVisible) { - let delegator = osk.vkbd.subkeyGesture as com.keyman.osk.embedded.SubkeyDelegator; - let pendingLongpress = osk.vkbd.embeddedPendingLongpress as com.keyman.osk.embedded.PendingLongpress; + let gesture = osk.vkbd.subkeyGesture as com.keyman.osk.embedded.SubkeyDelegator; + let pendingLongpress = osk.vkbd.pendingSubkey; if(!isVisible) { - if(delegator) { - delegator.resolve(null); + if(gesture) { + gesture.resolve(null); osk.vkbd.subkeyGesture = null; + } else if(pendingLongpress) { + pendingLongpress.cancel(); } } - if(isVisible && osk.vkbd.embeddedPendingLongpress) { + if(isVisible && pendingLongpress) { // Fulfills the first-stage promise. - pendingLongpress.markActiveSubkeys(); + pendingLongpress.resolve(); } }; diff --git a/web/source/osk/browser/pendingLongpress.ts b/web/source/osk/browser/pendingLongpress.ts index 08f89caca0..9e708c579f 100644 --- a/web/source/osk/browser/pendingLongpress.ts +++ b/web/source/osk/browser/pendingLongpress.ts @@ -1,7 +1,8 @@ /// +/// namespace com.keyman.osk.browser { - export class PendingLongpress { + export class PendingLongpress implements PendingGesture { public readonly baseKey: KeyElement; public readonly promise: Promise; //public readonly initialTouch: Touch; @@ -26,7 +27,7 @@ namespace com.keyman.osk.browser { function() { // It's no longer deferred; it's being fulfilled. // Even if the actual subkey itself is still async. - _this.showSubkeys(); + _this.resolve(); }, _this.popupDelay); }); } @@ -43,7 +44,7 @@ namespace com.keyman.osk.browser { } } - public showSubkeys() { + public resolve() { if(this.resolver) { this.resolver(new SubkeyPopup(this.vkbd, this.baseKey)); } diff --git a/web/source/osk/embedded/pendingLongpress.ts b/web/source/osk/embedded/pendingLongpress.ts index 8aa1983bc7..6594edbac0 100644 --- a/web/source/osk/embedded/pendingLongpress.ts +++ b/web/source/osk/embedded/pendingLongpress.ts @@ -1,7 +1,8 @@ /// +/// namespace com.keyman.osk.embedded { - export class PendingLongpress { + export class PendingLongpress implements PendingGesture { private resolver: (delegator: SubkeyDelegator) => void; private readonly vkbd: VisualKeyboard; @@ -11,17 +12,25 @@ namespace com.keyman.osk.embedded { constructor(vkbd: VisualKeyboard, e: KeyElement) { this.vkbd = vkbd; let _this = this; + this.promise = new Promise(function(resolve) { _this.resolver = resolve; }); this.baseKey = e; } - public markActiveSubkeys() { + public resolve() { if(this.resolver) { this.resolver(new SubkeyDelegator(this.vkbd, this.baseKey)); } this.resolver = null; } + + public cancel() { + if(this.resolver) { + this.resolver(null); + this.resolver = null; + } + } } } \ No newline at end of file diff --git a/web/source/osk/pendingGesture.interface.ts b/web/source/osk/pendingGesture.interface.ts new file mode 100644 index 0000000000..dd42149360 --- /dev/null +++ b/web/source/osk/pendingGesture.interface.ts @@ -0,0 +1,9 @@ +namespace com.keyman.osk { + export interface PendingGesture { + readonly baseKey: KeyElement; + readonly promise: Promise; + + cancel(): void; + resolve?(): void; + } +} \ No newline at end of file diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index ef4158c9de..08d50ef506 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -58,11 +58,12 @@ namespace com.keyman.osk { touchCount: number; currentTarget: KeyElement; - // Popup key management + // Used by embedded-mode's globe key menuEvent: KeyElement; // Used by embedded-mode. + + // Popup key management keytip: KeyTip; - browserPendingLongpress: browser.PendingLongpress; - embeddedPendingLongpress: any; // a temp, intermediate property during subkey abstraction work + pendingSubkey: PendingGesture; subkeyGesture: RealizedGesture; get layerId(): string { @@ -714,9 +715,15 @@ namespace com.keyman.osk { // If popup is visible, need to move over popup, not over main keyboard // TODO: responsible for the shortcutting gesture for early subkey display. - this.highlightSubKeys(key1,x,y); - // As the previous line can trigger the start of the subkey gesture... + if(key1 && key1['subKeys'] != null) { + // Show popup keys immediately if touch moved up towards key array (KMEW-100, Build 353) + if((this.touchY - e.touches[0].pageY > 5) && this.pendingSubkey && this.pendingSubkey instanceof browser.PendingLongpress) { + this.pendingSubkey.resolve(); + } + } + + // As the previous block can trigger the start of the subkey gesture... if(this.subkeyGesture) { return; } @@ -1029,9 +1036,9 @@ namespace com.keyman.osk { this.subkeyGesture = null; } - if(this.browserPendingLongpress) { - this.browserPendingLongpress.cancel(); - this.browserPendingLongpress = null; + if(this.pendingSubkey) { + this.pendingSubkey.cancel(); + this.pendingSubkey = null; } } @@ -1606,9 +1613,9 @@ namespace com.keyman.osk { */ touchHold(key: KeyElement, force?: boolean) { // Clear and restart the popup timer - if(this.browserPendingLongpress) { - this.browserPendingLongpress.cancel(); - this.browserPendingLongpress = null; + if(this.pendingSubkey) { + this.pendingSubkey.cancel(); + this.pendingSubkey = null; } if(typeof key['subKeys'] != 'undefined' && key['subKeys'] != null) { @@ -1616,13 +1623,12 @@ namespace com.keyman.osk { // First-level object/Promise: will produce a subkey popup when the longpress gesture completes. // 'Returns' a second-level object/Promise: resolves when a subkey is selected or is cancelled. - let pl = this.browserPendingLongpress = new browser.PendingLongpress(this, key); - this.browserPendingLongpress.promise.then(function(subkeyPopup) { - // Clear the longpress field upon any sort of fulfillment if it is still the current one. - if(_this.browserPendingLongpress == pl) { - _this.browserPendingLongpress = null; + let pendingLongpress = this.pendingSubkey = new browser.PendingLongpress(this, key); + pendingLongpress.promise.then(function(subkeyPopup) { + if(_this.pendingSubkey == pendingLongpress) { + _this.pendingSubkey = null; } - + if(subkeyPopup) { // Clear key preview if any _this.showKeyTip(null,false); @@ -1648,9 +1654,9 @@ namespace com.keyman.osk { _this.clearPopup(); }); - if(force) { + if(force && this.pendingSubkey instanceof browser.PendingLongpress) { // Instantly resolves the first-level promise. - this.browserPendingLongpress.showSubkeys(); + this.pendingSubkey.resolve(); } } }; @@ -1673,29 +1679,6 @@ namespace com.keyman.osk { } }; - // Manage popup key highlighting - highlightSubKeys(k: KeyElement, x: number, y: number) { - // Test for subkey array, return if none - - // Issue: if `k` is itself a subkey, this won't do subkey highlighting correctly. - // That "common case" is actually handled through _standard_ key highlighting. - if(k == null || k['subKeys'] == null) { - return; - } - - // Highlight key at touch position (and clear other highlighting) - var skBox=document.getElementById('kmw-popup-keys'); - - //#region This section fills a different role than the method name would suggest. - // Might correspond better to a 'checkInstantSubkeys' or something. - - // Show popup keys immediately if touch moved up towards key array (KMEW-100, Build 353) - if((this.touchY-y > 5) && skBox == null) { - this.touchHold(k, true); - } - //#endregion - }; - /** * Add (or remove) the keytip preview (if KeymanWeb on a phone device) * From 0607178d25cb6ebc260dac15ff40f555d7c0af24 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 23 Jun 2021 11:52:58 +0700 Subject: [PATCH 20/58] fix(web): subkeys were being auto-cleared by mistake --- web/source/osk/visualKeyboard.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 08d50ef506..9076e23b4a 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -1651,7 +1651,6 @@ namespace com.keyman.osk { // Must be placed after its `.element` has been inserted into the DOM. subkeyPopup.reposition(_this); } - _this.clearPopup(); }); if(force && this.pendingSubkey instanceof browser.PendingLongpress) { From 8f9493d7ab97ae2712f78633f624c0942378d2be Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 24 Jun 2021 10:25:42 +0700 Subject: [PATCH 21/58] refactor(web): removes unused vars in executePopupKey --- web/source/kmwembedded.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index 2b7bae20f0..c2d1ac6534 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -341,8 +341,6 @@ namespace com.keyman.text { * @param {string} keyName key identifier **/ keymanweb['executePopupKey'] = function(keyName: string) { - let core = ( keymanweb).core; - var origArg = keyName; if(!keymanweb.core.activeKeyboard || !osk.vkbd) { return false; @@ -356,19 +354,14 @@ namespace com.keyman.text { // Can't just split on '-' because some layers like ctrl-shift contain it. let separatorIndex = keyName.lastIndexOf('-'); - var layer = core.keyboardProcessor.layerId; + //var layer = core.keyboardProcessor.layerId; if (separatorIndex > 0) { - layer = keyName.substring(0, separatorIndex); keyName = keyName.substring(separatorIndex+1); } - if(layer == 'undefined') { - layer=core.keyboardProcessor.layerId; - } // Note: this assumes Lelem is properly attached and has an element interface. // Currently true in the Android and iOS apps. - var Lelem=keymanweb.domManager.getLastActiveElement(),keyShiftState=com.keyman.text.KeyboardProcessor.getModifierState(layer); - + var Lelem=keymanweb.domManager.getLastActiveElement(); keymanweb.domManager.initActiveElement(Lelem); // This should be set if we're within this method... but it's best to guard against nulls here, just in case. From 5067337429ba4c07d3f40d4b20047943628ad242 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 23 Jun 2021 14:24:39 +0700 Subject: [PATCH 22/58] fix(web): selection of base key after subkey display (embedded) --- web/source/osk/embedded/subkeyDelegator.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts index 3fc2d33bb4..488634467c 100644 --- a/web/source/osk/embedded/subkeyDelegator.ts +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -8,6 +8,9 @@ namespace com.keyman.osk.embedded { public readonly baseKey: KeyElement; public readonly promise: Promise; + private movedFromBaseKey: boolean = false; + private baseKeySelected: boolean = false; + constructor(vkbd: VisualKeyboard, e: KeyElement) { this.vkbd = vkbd; @@ -23,7 +26,10 @@ namespace com.keyman.osk.embedded { if(this.resolver) { let keyEvent: text.KeyEvent = null; - if(keyCoreID != null) { + if(keyCoreID == null && this.baseKeySelected) { + keyEvent = this.vkbd.keyEventFromSpec(this.baseKey.key.spec as keyboards.ActiveKey, null); + this.baseKey.key.highlight(false); + } else { // This is set with the base key of our current subkey elsewhere within the engine. var baseKey: OSKKeySpec = this.baseKey.key.spec; var found = false; @@ -64,7 +70,15 @@ namespace com.keyman.osk.embedded { } updateTouch(touch: Touch) { - this.baseKey.key.highlight(this.baseKey.key.isUnderTouch(touch)); + let baseKeyTouched = this.baseKey.key.isUnderTouch(touch); + this.baseKeySelected = this.baseKey.key.isUnderTouch(touch) + + // Prevent highlighting & selection before the touch has moved from the base key. + if(this.movedFromBaseKey) { + this.baseKey.key.highlight(this.baseKeySelected); + } else { + this.movedFromBaseKey = !baseKeyTouched; + } } } } \ No newline at end of file From 3d615239a798017cb171d316b22df02cf1fb9ef3 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 23 Jun 2021 14:24:39 +0700 Subject: [PATCH 23/58] fix(web): prevent error when attempting to reselect base key (embedded) --- web/source/osk/embedded/subkeyDelegator.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts index 488634467c..213bd091e7 100644 --- a/web/source/osk/embedded/subkeyDelegator.ts +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -27,6 +27,7 @@ namespace com.keyman.osk.embedded { let keyEvent: text.KeyEvent = null; if(keyCoreID == null && this.baseKeySelected) { + // Handle selection of base key underneath the subkey array. keyEvent = this.vkbd.keyEventFromSpec(this.baseKey.key.spec as keyboards.ActiveKey, null); this.baseKey.key.highlight(false); } else { From aab0eb94e2370a3cffd72d869b9d7e7e870c3bbf Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 23 Jun 2021 13:04:51 +0700 Subject: [PATCH 24/58] refactor(web): initGestures, updateGestures --- web/source/kmwembedded.ts | 24 +---- web/source/osk/visualKeyboard.ts | 175 ++++++++++++++++++------------- 2 files changed, 110 insertions(+), 89 deletions(-) diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index c2d1ac6534..7e0cacd9a7 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -53,31 +53,17 @@ namespace com.keyman.osk { * * @param {Object} key base key element */ - VisualKeyboard.prototype.touchHold = function(this: VisualKeyboard, key: KeyElement) { - if(key['subKeys'] && (typeof(window['oskCreatePopup']) == 'function')) { + VisualKeyboard.prototype.startLongpress = function(this: VisualKeyboard, key: KeyElement): PendingGesture { + if(typeof(window['oskCreatePopup']) == 'function') { var xBase = dom.Utils.getAbsoluteX(key) - dom.Utils.getAbsoluteX(this.kbdDiv) + key.offsetWidth/2, yBase = dom.Utils.getAbsoluteY(key); // #3718: No longer prepend base key to subkey array - - let _this = this; window['oskCreatePopup'](key['subKeys'], xBase, yBase, key.offsetWidth, key.offsetHeight); - let pendingLongpress = new embedded.PendingLongpress(this, key); - pendingLongpress.promise.then(function(gesture) { - _this.subkeyGesture = gesture; - if(gesture) { - gesture.promise.then(function(keyEvent) { - _this.subkeyGesture = null; - // Allow active cancellation, even if the source should allow passive. - // It's an easy and cheap null guard. - if(keyEvent) { - PreProcessor.raiseKeyEvent(keyEvent); - } - }); - } - }); - this.pendingSubkey = pendingLongpress; + return new embedded.PendingLongpress(this, key); + } else { + return null; } }; diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 9076e23b4a..b4f0e1feca 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -570,7 +570,7 @@ namespace com.keyman.osk { this.touchCount--; } else { // If this key has subkey, start timer to display subkeys after delay, set up release - this.touchHold(key); + this.initGestures(key, e.changedTouches[0]); } this.keyPending = key; this.touchPending = e.changedTouches[0]; @@ -697,34 +697,9 @@ namespace com.keyman.osk { return; } - // Clear previous key highlighting, allow subkey controller to - // highlight as appropriate. - if(this.subkeyGesture) { - if(key0) { - key0.key.highlight(false); - } - this.subkeyGesture.updateTouch(e.touches[0]); - - this.keyPending = null; - this.touchPending = null; - - return; - } - - this.currentTarget = null; - - // If popup is visible, need to move over popup, not over main keyboard - // TODO: responsible for the shortcutting gesture for early subkey display. - - if(key1 && key1['subKeys'] != null) { - // Show popup keys immediately if touch moved up towards key array (KMEW-100, Build 353) - if((this.touchY - e.touches[0].pageY > 5) && this.pendingSubkey && this.pendingSubkey instanceof browser.PendingLongpress) { - this.pendingSubkey.resolve(); - } - } - - // As the previous block can trigger the start of the subkey gesture... - if(this.subkeyGesture) { + // Update all gesture tracking. The function returns true if further input processing + // should be blocked. + if(this.updateGestures(key1, key0, e.changedTouches[0])) { return; } @@ -759,11 +734,6 @@ namespace com.keyman.osk { this.highlightKey(key1,true); } } - - if(key0 && key1 && (key1 != key0) && (key1.id != '')) { - // Display the touch-hold keys (after a pause) - this.touchHold(key1); - } }.bind(this); /** @@ -1606,44 +1576,22 @@ namespace com.keyman.osk { } } - /** - * Touch hold key display management - * - * @param {Object} key base key object - */ - touchHold(key: KeyElement, force?: boolean) { - // Clear and restart the popup timer - if(this.pendingSubkey) { - this.pendingSubkey.cancel(); - this.pendingSubkey = null; - } - - if(typeof key['subKeys'] != 'undefined' && key['subKeys'] != null) { + /** + * Starts an implementation-specific longpress gesture. Separately implemented for + * in-browser and embedded modes. + * @param key The base key of the longpress. + * @returns + */ + startLongpress(key: KeyElement): PendingGesture { let _this = this; // First-level object/Promise: will produce a subkey popup when the longpress gesture completes. // 'Returns' a second-level object/Promise: resolves when a subkey is selected or is cancelled. - let pendingLongpress = this.pendingSubkey = new browser.PendingLongpress(this, key); + let pendingLongpress = new browser.PendingLongpress(this, key); pendingLongpress.promise.then(function(subkeyPopup) { - if(_this.pendingSubkey == pendingLongpress) { - _this.pendingSubkey = null; - } - + // In-browser-specific handling. if(subkeyPopup) { - // Clear key preview if any - _this.showKeyTip(null,false); - - _this.subkeyGesture = subkeyPopup; - subkeyPopup.promise.then(function(keyEvent: text.KeyEvent) { - // Allow active cancellation, even if the source should allow passive. - // It's an easy and cheap null guard. - if(keyEvent) { - PreProcessor.raiseKeyEvent(keyEvent); - } - _this.clearPopup(); - }); - - // Otherwise append the touch-hold (subkey) array to the OSK + // Append the touch-hold (subkey) array to the OSK let keyman = com.keyman.singleton; keyman.osk._Box.appendChild(subkeyPopup.element); keyman.osk._Box.appendChild(subkeyPopup.shim); @@ -1653,12 +1601,99 @@ namespace com.keyman.osk { } }); - if(force && this.pendingSubkey instanceof browser.PendingLongpress) { - // Instantly resolves the first-level promise. - this.pendingSubkey.resolve(); + return pendingLongpress; + } + + /** + * Initializes all supported gestures given a base key and the triggering touch coordinates. + * @param key The gesture's base key + * @param touch The starting touch coordinates for the gesture + * @returns + */ + initGestures(key: KeyElement, touch: Touch) { + if(key['subKeys']) { + let _this = this; + + let pendingLongpress = this.startLongpress(key); + if(pendingLongpress == null) { + return; + } + this.pendingSubkey = pendingLongpress; + + pendingLongpress.promise.then(function(subkeyPopup) { + if(_this.pendingSubkey == pendingLongpress) { + _this.pendingSubkey = null; + } + + if(subkeyPopup) { + // Clear key preview if any + _this.showKeyTip(null,false); + + _this.subkeyGesture = subkeyPopup; + subkeyPopup.promise.then(function(keyEvent: text.KeyEvent) { + // Allow active cancellation, even if the source should allow passive. + // It's an easy and cheap null guard. + if(keyEvent) { + PreProcessor.raiseKeyEvent(keyEvent); + } + _this.clearPopup(); + }); + } + }); } } - }; + + /** + * Updates all currently-pending and activated gestures. + * + * @param currentKey The key currently underneath the most recent touch coordinate + * @param previousKey The previously-selected key + * @param touch The current touch-coordinate for the gesture + * @returns true if should fully capture input, false if input should 'fall through'. + */ + updateGestures(currentKey: KeyElement, previousKey: KeyElement, touch: Touch): boolean { + let key0 = previousKey; + let key1 = currentKey; + + // Clear previous key highlighting, allow subkey controller to + // highlight as appropriate. + if(this.subkeyGesture) { + if(key0) { + key0.key.highlight(false); + } + this.subkeyGesture.updateTouch(touch); + + this.keyPending = null; + this.touchPending = null; + + return true; + } + + this.currentTarget = null; + + // If popup is visible, need to move over popup, not over main keyboard + // TODO: responsible for the shortcutting gesture for early subkey display. + + // TODO: Could be made part of the browser-specific implementation's update func? + if(key1 && key1['subKeys'] != null) { + // Show popup keys immediately if touch moved up towards key array (KMEW-100, Build 353) + if((this.touchY - touch.pageY > 5) && this.pendingSubkey && this.pendingSubkey instanceof browser.PendingLongpress) { + this.pendingSubkey.resolve(); + } + } + + // As the previous block can trigger the start of the subkey gesture... + if(this.subkeyGesture) { + return true; + } + + if(key0 && key1 && (key1 != key0) && (key1.id != '')) { + // Display the touch-hold keys (after a pause) + this.clearPopup(); + this.initGestures(key1, touch); + } + return false; + } optionKey(e: KeyElement, keyName: string, keyDown: boolean) { let keyman = com.keyman.singleton; From 625b5fd8c8e40407b4ea3956b3a01e98b57d1c1c Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 23 Jun 2021 13:14:54 +0700 Subject: [PATCH 25/58] fix(web): output base key if subkey touch returns to it (in-browser) --- web/source/osk/browser/subkeyPopup.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index f927a8bd61..d9c0c05077 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -236,6 +236,7 @@ namespace com.keyman.osk.browser { updateTouch(touch: Touch) { this.currentSelection = null; + let matchFound = false; for(let i=0; i < this.baseKey['subKeys'].length; i++) { try { @@ -245,10 +246,17 @@ namespace com.keyman.osk.browser { if(onKey) { this.baseKey.key.highlight(false); this.currentSelection = sk; + matchFound = true; } sk.key.highlight(onKey); } catch(ex){} } + + // Use the popup duplicate of the base key if a phone with a visible popup array + if(!matchFound && this.baseKey.key.isUnderTouch(touch)) { + this.baseKey.key.highlight(true); + this.currentSelection = this.baseKey; + } } } } \ No newline at end of file From 6034f252e70c2218a1983059324f32d2bdf15daa Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 24 Jun 2021 14:29:36 +0700 Subject: [PATCH 26/58] docs(web): tweak for note re future enhancement --- web/source/osk/visualKeyboard.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 9076e23b4a..dfb519e7d6 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -714,8 +714,9 @@ namespace com.keyman.osk { this.currentTarget = null; // If popup is visible, need to move over popup, not over main keyboard - // TODO: responsible for the shortcutting gesture for early subkey display. - + // Could be turned into a browser-longpress specific implementation within browser.PendingLongpress? + // Not completely sure what the correct, generalized abstraction would be for that... + // and this PR's already big enough, anyway. if(key1 && key1['subKeys'] != null) { // Show popup keys immediately if touch moved up towards key array (KMEW-100, Build 353) if((this.touchY - e.touches[0].pageY > 5) && this.pendingSubkey && this.pendingSubkey instanceof browser.PendingLongpress) { @@ -1628,7 +1629,7 @@ namespace com.keyman.osk { if(_this.pendingSubkey == pendingLongpress) { _this.pendingSubkey = null; } - + if(subkeyPopup) { // Clear key preview if any _this.showKeyTip(null,false); From 975e2343f15cfaeedc9386dbf4b4261a56fbddf8 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 24 Jun 2021 14:40:50 +0700 Subject: [PATCH 27/58] docs(web): fixes up refactored docs --- web/source/osk/visualKeyboard.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index b4f0e1feca..8c5e933e62 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -1672,9 +1672,9 @@ namespace com.keyman.osk { this.currentTarget = null; // If popup is visible, need to move over popup, not over main keyboard - // TODO: responsible for the shortcutting gesture for early subkey display. - - // TODO: Could be made part of the browser-specific implementation's update func? + // Could be turned into a browser-longpress specific implementation within browser.PendingLongpress? + // Not completely sure what the correct, generalized abstraction would be for that... + // and this PR's already big enough, anyway. if(key1 && key1['subKeys'] != null) { // Show popup keys immediately if touch moved up towards key array (KMEW-100, Build 353) if((this.touchY - touch.pageY > 5) && this.pendingSubkey && this.pendingSubkey instanceof browser.PendingLongpress) { @@ -1682,7 +1682,8 @@ namespace com.keyman.osk { } } - // As the previous block can trigger the start of the subkey gesture... + // If there is an active popup menu (which can occur from the previous block), + // a subkey popup exists; do not allow base key output. if(this.subkeyGesture) { return true; } From 3f793a0b850d7825ab0a420d0bb76188d18caa90 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 24 Jun 2021 14:42:48 +0700 Subject: [PATCH 28/58] docs(web): another doc tweak --- web/source/osk/visualKeyboard.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 8c5e933e62..22f9c76500 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -1655,8 +1655,7 @@ namespace com.keyman.osk { let key0 = previousKey; let key1 = currentKey; - // Clear previous key highlighting, allow subkey controller to - // highlight as appropriate. + // Clear previous key highlighting, allow subkey controller to highlight as appropriate. if(this.subkeyGesture) { if(key0) { key0.key.highlight(false); @@ -1689,7 +1688,8 @@ namespace com.keyman.osk { } if(key0 && key1 && (key1 != key0) && (key1.id != '')) { - // Display the touch-hold keys (after a pause) + // While there may not be an active subkey menu, we should probably update which base key + // is being highlighted by the current touch & start a pending longpress for it. this.clearPopup(); this.initGestures(key1, touch); } From 74dc8ad115e7aebabe55fc250f6399373e5f4d51 Mon Sep 17 00:00:00 2001 From: jahorton Date: Tue, 29 Jun 2021 07:48:34 +0700 Subject: [PATCH 29/58] docs(web): reason for SubkeyDelegator nomenclature --- web/source/osk/embedded/subkeyDelegator.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts index 213bd091e7..b2e75ae684 100644 --- a/web/source/osk/embedded/subkeyDelegator.ts +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -1,6 +1,8 @@ /// namespace com.keyman.osk.embedded { + // "Delegator", rather than "Popup", because KMW delegates display + selection + // of subkeys to the host app when in the embedded context. export class SubkeyDelegator implements RealizedGesture { private resolver: (keyEvent: text.KeyEvent) => void; private readonly vkbd: VisualKeyboard; From e0cf57d508dbf02465bd00cb0d10b56eea071d26 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Wed, 30 Jun 2021 10:11:46 +0700 Subject: [PATCH 30/58] chore(web): Apply suggestions from code review Co-authored-by: Marc Durdin --- web/source/osk/browser/pendingLongpress.ts | 11 ++++------- web/source/osk/browser/subkeyPopup.ts | 6 +++--- web/source/osk/embedded/subkeyDelegator.ts | 15 ++++++--------- web/source/osk/oskKey.ts | 6 +++--- web/source/osk/visualKeyboard.ts | 1 - 5 files changed, 16 insertions(+), 23 deletions(-) diff --git a/web/source/osk/browser/pendingLongpress.ts b/web/source/osk/browser/pendingLongpress.ts index 9e708c579f..a0b90f0bae 100644 --- a/web/source/osk/browser/pendingLongpress.ts +++ b/web/source/osk/browser/pendingLongpress.ts @@ -23,12 +23,9 @@ namespace com.keyman.osk.browser { let _this = this; this.promise = new Promise(function(resolve, reject) { _this.resolver = resolve; - _this.timerId = window.setTimeout( - function() { - // It's no longer deferred; it's being fulfilled. - // Even if the actual subkey itself is still async. - _this.resolve(); - }, _this.popupDelay); + // After the timeout, it's no longer deferred; it's being fulfilled. + // Even if the actual subkey itself is still async. + _this.timerId = window.setTimeout(_this.resolve, _this.popupDelay); }); } @@ -50,4 +47,4 @@ namespace com.keyman.osk.browser { } } } -} \ No newline at end of file +} diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index d9c0c05077..1207e1f32e 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -240,9 +240,9 @@ namespace com.keyman.osk.browser { for(let i=0; i < this.baseKey['subKeys'].length; i++) { try { - let sk= this.element.childNodes[i].firstChild as KeyElement; + let sk = this.element.childNodes[i].firstChild as KeyElement; - let onKey = sk.key.isUnderTouch(touch) + let onKey = sk.key.isUnderTouch(touch); if(onKey) { this.baseKey.key.highlight(false); this.currentSelection = sk; @@ -259,4 +259,4 @@ namespace com.keyman.osk.browser { } } } -} \ No newline at end of file +} diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts index b2e75ae684..e4ec911540 100644 --- a/web/source/osk/embedded/subkeyDelegator.ts +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -34,7 +34,7 @@ namespace com.keyman.osk.embedded { this.baseKey.key.highlight(false); } else { // This is set with the base key of our current subkey elsewhere within the engine. - var baseKey: OSKKeySpec = this.baseKey.key.spec; + let baseKey: OSKKeySpec = this.baseKey.key.spec; var found = false; let selectedKey: OSKKeySpec; @@ -53,10 +53,8 @@ namespace com.keyman.osk.embedded { throw new Error("Could not find subkey '" + keyCoreID + "' under base key '" + baseKey.coreID + "'!"); } - if(selectedKey) { - keyEvent = this.vkbd.keyEventFromSpec(selectedKey as keyboards.ActiveKey, null); - keyEvent.vkCode=keyEvent.Lcode; - } + keyEvent = this.vkbd.keyEventFromSpec(selectedKey as keyboards.ActiveKey, null); + keyEvent.vkCode=keyEvent.Lcode; } this.resolver(keyEvent); @@ -73,15 +71,14 @@ namespace com.keyman.osk.embedded { } updateTouch(touch: Touch) { - let baseKeyTouched = this.baseKey.key.isUnderTouch(touch); - this.baseKeySelected = this.baseKey.key.isUnderTouch(touch) + this.baseKeySelected = this.baseKey.key.isUnderTouch(touch); // Prevent highlighting & selection before the touch has moved from the base key. if(this.movedFromBaseKey) { this.baseKey.key.highlight(this.baseKeySelected); } else { - this.movedFromBaseKey = !baseKeyTouched; + this.movedFromBaseKey = !this.baseKeySelected; } } } -} \ No newline at end of file +} diff --git a/web/source/osk/oskKey.ts b/web/source/osk/oskKey.ts index da52968507..7a0bd2dd32 100644 --- a/web/source/osk/oskKey.ts +++ b/web/source/osk/oskKey.ts @@ -439,10 +439,10 @@ namespace com.keyman.osk { let x0 = dom.Utils.getAbsoluteX(btn); let y0 = dom.Utils.getAbsoluteY(btn);//-document.body.scrollTop; - let x1=x0 + btn.offsetWidth; - let y1=y0 + btn.offsetHeight; + let x1 = x0 + btn.offsetWidth; + let y1 = y0 + btn.offsetHeight; return (x > x0 && x < x1 && y > y0 && y < y1); } } -} \ No newline at end of file +} diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 22f9c76500..44024eb918 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -626,7 +626,6 @@ namespace com.keyman.osk { // Process and clear highlighting of pending target if(this.keyPending) { this.highlightKey(this.keyPending,false); - // Output character unless moved off key if(this.keyPending.className.indexOf('hidden') < 0 && tc > 0 && !beyondEdge) { this.modelKeyClick(this.keyPending, e.changedTouches[0]); From a8e82e5bea0a85d484e6e76d26d3f56b5ea3a742 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 30 Jun 2021 11:09:32 +0700 Subject: [PATCH 31/58] chore(web): more changes from PR review --- web/source/osk/browser/pendingLongpress.ts | 4 +--- web/source/osk/browser/subkeyPopup.ts | 4 +--- web/source/osk/embedded/subkeyDelegator.ts | 9 ++++----- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/web/source/osk/browser/pendingLongpress.ts b/web/source/osk/browser/pendingLongpress.ts index a0b90f0bae..51086eb50c 100644 --- a/web/source/osk/browser/pendingLongpress.ts +++ b/web/source/osk/browser/pendingLongpress.ts @@ -5,7 +5,6 @@ namespace com.keyman.osk.browser { export class PendingLongpress implements PendingGesture { public readonly baseKey: KeyElement; public readonly promise: Promise; - //public readonly initialTouch: Touch; public readonly subkeyUI: SubkeyPopup; @@ -15,10 +14,9 @@ namespace com.keyman.osk.browser { private timerId: number; private popupDelay: number = 500; - constructor(vkbd: VisualKeyboard, baseKey: KeyElement/*, initialTouch: Touch*/) { + constructor(vkbd: VisualKeyboard, baseKey: KeyElement) { this.vkbd = vkbd; this.baseKey = baseKey; - //this.initialTouch = initialTouch; let _this = this; this.promise = new Promise(function(resolve, reject) { diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index 1207e1f32e..d85d0bc088 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -236,7 +236,6 @@ namespace com.keyman.osk.browser { updateTouch(touch: Touch) { this.currentSelection = null; - let matchFound = false; for(let i=0; i < this.baseKey['subKeys'].length; i++) { try { @@ -246,14 +245,13 @@ namespace com.keyman.osk.browser { if(onKey) { this.baseKey.key.highlight(false); this.currentSelection = sk; - matchFound = true; } sk.key.highlight(onKey); } catch(ex){} } // Use the popup duplicate of the base key if a phone with a visible popup array - if(!matchFound && this.baseKey.key.isUnderTouch(touch)) { + if(this.currentSelection && this.baseKey.key.isUnderTouch(touch)) { this.baseKey.key.highlight(true); this.currentSelection = this.baseKey; } diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts index e4ec911540..7986c9cd61 100644 --- a/web/source/osk/embedded/subkeyDelegator.ts +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -35,21 +35,20 @@ namespace com.keyman.osk.embedded { } else { // This is set with the base key of our current subkey elsewhere within the engine. let baseKey: OSKKeySpec = this.baseKey.key.spec; - var found = false; let selectedKey: OSKKeySpec; if(baseKey.coreID == keyCoreID) { selectedKey = baseKey; - found = true; } else { // ... yeah, there are some funky type shenanigans between the two. // OSKKeySpec is the OSK's... reinterpretation of the ActiveKey type. selectedKey = (baseKey as keyboards.ActiveKey).getSubkey(keyCoreID) as OSKKeySpec; - found = !!selectedKey; } - if(!found) { - this.resolver(null); // Maintains existing behavior. + if(!selectedKey) { + // While we can't complete successfully, the subkey operation is done; we + // should still signal that and update related gesture state management. + this.resolver(null); throw new Error("Could not find subkey '" + keyCoreID + "' under base key '" + baseKey.coreID + "'!"); } From 052711da622a7477cfec331c4da820377f1f55de Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 30 Jun 2021 11:10:09 +0700 Subject: [PATCH 32/58] docs(web): for PendingGesture, RealizedGesture --- web/source/osk/pendingGesture.interface.ts | 36 +++++++++++++++++++++ web/source/osk/realizedGesture.interface.ts | 24 ++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/web/source/osk/pendingGesture.interface.ts b/web/source/osk/pendingGesture.interface.ts index dd42149360..8cea889b89 100644 --- a/web/source/osk/pendingGesture.interface.ts +++ b/web/source/osk/pendingGesture.interface.ts @@ -1,4 +1,40 @@ namespace com.keyman.osk { + /** + * Used for evaluating potential gestures. Classes adhering to this interface + * should instantiated whenever the (implied) state-machine allows a new touch + * event to mark the start of a potential new gesture. + * + * For example, whenever a user touches a base key and there are no "realized" + * (fully-completed, but as-of-yet unresolved) gestures, that state allows the + * start of a potential new longpress event. + * + * The role of the `PendingGesture` is complete whenever all touch-events and + * conditions necessary for a modeled gesture have been met. As this point, + * it should be `resolve`d, fulfilling its `promise`. This results in a + * `RealizedGesture` appropriate for the gesture type that is used to obtain + * the final `KeyEvent` for the overall gesture sequence. + * + * For example, a "longpress" is considered resolved once the user has maintained + * an active, stationary touch point on the same key for a sufficiently long + * period without releasing it. + * * Were it released earlier, that would result in selection of a base key. + * + * Alternatively, a "flick" might be considered resolved if: + * * a user has rapidly moved a touch point in a consistent direction + * * for a long enough distance + * * and _then_ releases that touch point within a short timeframe. + * + * The pending gesture should only `resolve` to a realized gesture once + * _all_ such conditions are met, confirming that this specific gesture, + * and _only_ this specific gesture, could have resulted from the active + * touch sequence. + * + * The `RealizedGesture` that results and is 'returned' via the Promise will + * be handled by the `VisualKeyboard` class, which will retrieve and forward + * any `KeyEvent` that results from the overall gesture input sequence. + * + * @see `RealizedGesture` + */ export interface PendingGesture { readonly baseKey: KeyElement; readonly promise: Promise; diff --git a/web/source/osk/realizedGesture.interface.ts b/web/source/osk/realizedGesture.interface.ts index 877164898a..6683b4357e 100644 --- a/web/source/osk/realizedGesture.interface.ts +++ b/web/source/osk/realizedGesture.interface.ts @@ -1,4 +1,28 @@ namespace com.keyman.osk { + /* + * Implementations of this interface allow individual types of gestures to + * specify any additional user interaction and functionality (which may + * include UI elements) appropriate for obtaining a key event that may be + * produced by the modeled gesture type. These should only be instantiated + * once the associated `PendingLongpress` is no longer 'pending' - once it + * has become clear that the input touch-event sequence could only correspond + * to the modeled gesture. + * + * For example, when a longpress gesture completes - and hence, the user has + * kept their finger stationary on the same key for a long enough period - + * we display a popup view presenting subkeys corresponding to the gesture's + * underlying element. This popup view accepts touch input and completes only + * upon release of the ongoing touch sequence. + * + * Gestures are events that occur over intervals of time, and since some of them + * will require time and user interaction after becoming 'realized', these cases + * will be inherently async. The simplest way to model this is with `Promise`s. + * + * If appropriate for the modeled gesture type, an implementation may supply an + * instantly-resolving `Promise``. This may be appropriate for modeling "flick" + * or "swipe" gestures in the future, which may require no additional input once + * such a gesture is fully realized. + */ export interface RealizedGesture { readonly baseKey: KeyElement; readonly promise: Promise; From cafe421e9890a38b24ef838a2909608f25af7d2a Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 30 Jun 2021 12:09:19 +0700 Subject: [PATCH 33/58] docs(web): gesture subclasses --- web/source/kmwembedded.ts | 20 ++++++++++++-- web/source/osk/browser/pendingLongpress.ts | 17 ++++++++++++ web/source/osk/browser/subkeyPopup.ts | 16 +++++++++++ web/source/osk/embedded/pendingLongpress.ts | 10 +++++++ web/source/osk/embedded/subkeyDelegator.ts | 30 +++++++++++++++++++-- 5 files changed, 89 insertions(+), 4 deletions(-) diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index 7e0cacd9a7..0cc1308a76 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -265,7 +265,9 @@ namespace com.keyman.text { }; /** - * Function called by Android and iOS when a device-implemented keyboard popup is displayed or hidden + * Function called by Android and iOS when a device-implemented keyboard popup + * is displayed or hidden. As this is controlled by the app, it's the perfect + * trigger for 'embedded'-mode gesture state management. * * @param {boolean} isVisible * @@ -274,15 +276,27 @@ namespace com.keyman.text { let gesture = osk.vkbd.subkeyGesture as com.keyman.osk.embedded.SubkeyDelegator; let pendingLongpress = osk.vkbd.pendingSubkey; + /* + * If a longpress popup was visible, but is no longer, this means that the + * associated longpress gesture was cancelled. It is possible for the base key + * to emit if selected at this time, but only if appropriate - and this is + * managed by the `SubkeyDelegator`. + */ if(!isVisible) { if(gesture) { gesture.resolve(null); osk.vkbd.subkeyGesture = null; } else if(pendingLongpress) { pendingLongpress.cancel(); + osk.vkbd.pendingSubkey = null; } } + /* + * If the popup was not visible, but now is, that means our previously-pending + * longpress is now 'realized' (complete). Certain aspects of the OSK rely on + * this state information, which will be properly updated by `resolve`. + */ if(isVisible && pendingLongpress) { // Fulfills the first-stage promise. pendingLongpress.resolve(); @@ -340,7 +354,7 @@ namespace com.keyman.text { // Can't just split on '-' because some layers like ctrl-shift contain it. let separatorIndex = keyName.lastIndexOf('-'); - //var layer = core.keyboardProcessor.layerId; + if (separatorIndex > 0) { keyName = keyName.substring(separatorIndex+1); } @@ -355,9 +369,11 @@ namespace com.keyman.text { let gesture = osk.vkbd.subkeyGesture as com.keyman.osk.embedded.SubkeyDelegator; try { + // Can trigger an error if the corresponding subkey cannot be found. gesture.resolve(keyName); } catch (e) { let err = e as Error; + // Prevent the Android app from triggering a "fatal error" keyboard reset. console.warn(err.message); } diff --git a/web/source/osk/browser/pendingLongpress.ts b/web/source/osk/browser/pendingLongpress.ts index 51086eb50c..00fca0e500 100644 --- a/web/source/osk/browser/pendingLongpress.ts +++ b/web/source/osk/browser/pendingLongpress.ts @@ -2,6 +2,23 @@ /// namespace com.keyman.osk.browser { + /** + * (Conceptually) represents a finite-state-machine that determines + * whether or not a series of touch events corresponds to a longpress + * touch input. The `resolve` method may be used to trigger the + * subkey menu early, as with the upward quick-display shortcut. + * + * This is the default implementation of longpress behavior for KMW. + * Alterate implementations are modeled through the `embedded` + * namespace's equivalent, which is designed to facilitate custom + * modeling for such gestures. + * + * Once the conditions to recognize a longpress gesture have been + * fulfilled, this class's `promise` will resolve with a `SubkeyPopup` + * matching the gesture's 'base' key, which itself provides a + * `promise` field that will resolve to a `KeyEvent` once the touch + * sequence is completed. + */ export class PendingLongpress implements PendingGesture { public readonly baseKey: KeyElement; public readonly promise: Promise; diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index d85d0bc088..85b0371746 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -2,6 +2,22 @@ /// namespace com.keyman.osk.browser { + /** + * Represents a 'realized' longpress gesture's default implementation + * within KeymanWeb. Once a touch sequence has been confirmed to + * correspond to a longpress gesture, implementations of this class + * provide the following: + * * The UI needed to present a subkey menu + * * The state management needed to present feedback about the + * currently-selected subkey to the user + * * A `Promise` that will resolve to the user's selected subkey + * once the longpress operation is complete. + * + * As selection of the subkey occurs after the subkey popup is + * displayed, selection of the subkey is inherently asynchronous. + * The `Promise` may also resolve to `null` if the user indicates + * the desire to cancel subkey selection. + */ export class SubkeyPopup implements RealizedGesture { public readonly element: HTMLDivElement; public readonly shim: HTMLDivElement; diff --git a/web/source/osk/embedded/pendingLongpress.ts b/web/source/osk/embedded/pendingLongpress.ts index 6594edbac0..3b407cd610 100644 --- a/web/source/osk/embedded/pendingLongpress.ts +++ b/web/source/osk/embedded/pendingLongpress.ts @@ -2,6 +2,16 @@ /// namespace com.keyman.osk.embedded { + /** + * As control over the subkey display timer and the subkey popup are + * both handled by the host app within the Android app, this class + * serves mostly to communicate longpress state management from the + * app to the VisualKeyboard. + * + * The `resolve()` function should be triggered, in some fashion, by + * the host app whenever it has recognized a completed longpress and + * will begin displaying its subkey popup. + */ export class PendingLongpress implements PendingGesture { private resolver: (delegator: SubkeyDelegator) => void; private readonly vkbd: VisualKeyboard; diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts index 7986c9cd61..207edf7310 100644 --- a/web/source/osk/embedded/subkeyDelegator.ts +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -1,8 +1,18 @@ /// namespace com.keyman.osk.embedded { - // "Delegator", rather than "Popup", because KMW delegates display + selection - // of subkeys to the host app when in the embedded context. + /** + * As the subkey popup view is handled by the host app when in embedded mode, + * this class represents the fact that KMW has "delegated" subkey UI and + * selection to the host app. Hence, "Delegator", rather than "Popup". + * + * The `resolve` method should be triggered, in some fashion, by the host app + * whenever the user has completed their longpress, potentially selecting + * a subkey. + * + * This class will also track the ongoing touch event in case the base key is + * reselected, which _is_ managed by this class, not the host app. + */ export class SubkeyDelegator implements RealizedGesture { private resolver: (keyEvent: text.KeyEvent) => void; private readonly vkbd: VisualKeyboard; @@ -24,6 +34,17 @@ namespace com.keyman.osk.embedded { this.baseKey = e; } + /** + * Resolves the ongoing longpress -> subkey gesture, fulfilling this + * `SubkeyDelegator`'s `promise` of a `KeyEvent`. + * + * If no subkey is selected but the original base key is, `resolve(null)` + * will return a key event corresponding to the base key. + + * + * @param keyCoreID {string} The 'core ID' (id + modifier layer) of + * a selected subkey. May be `null`. + */ public resolve(keyCoreID: string) { if(this.resolver) { let keyEvent: text.KeyEvent = null; @@ -69,6 +90,11 @@ namespace com.keyman.osk.embedded { // no-op; it's fully controlled on the app side. } + /** + * Allows this class to detect if the user may have changed their mind and + * re-selected the base key. + * @param touch + */ updateTouch(touch: Touch) { this.baseKeySelected = this.baseKey.key.isUnderTouch(touch); From c6a5961421ae9850802ae870b1f59a900eb2b315 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 30 Jun 2021 12:16:15 +0700 Subject: [PATCH 34/58] feat(web): adds error log to SubkeyPopup.updateTouch --- web/source/osk/browser/subkeyPopup.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index 85b0371746..3f4b1b3524 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -263,7 +263,13 @@ namespace com.keyman.osk.browser { this.currentSelection = sk; } sk.key.highlight(onKey); - } catch(ex){} + } catch(ex) { + if(ex.message) { + console.error("Unexpected error when attempting to update selected subkey:" + ex.message); + } else { + console.error("Unexpected error (and error type) when attempting to update selected subkey."); + } + } } // Use the popup duplicate of the base key if a phone with a visible popup array From 4f3e37bbb2f7942070ef46dcf990b7869b35e72b Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 30 Jun 2021 12:17:52 +0700 Subject: [PATCH 35/58] chore(web): removes dev-artifact comment --- web/source/osk/visualKeyboard.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 44024eb918..2095daab74 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -1671,8 +1671,6 @@ namespace com.keyman.osk { // If popup is visible, need to move over popup, not over main keyboard // Could be turned into a browser-longpress specific implementation within browser.PendingLongpress? - // Not completely sure what the correct, generalized abstraction would be for that... - // and this PR's already big enough, anyway. if(key1 && key1['subKeys'] != null) { // Show popup keys immediately if touch moved up towards key array (KMEW-100, Build 353) if((this.touchY - touch.pageY > 5) && this.pendingSubkey && this.pendingSubkey instanceof browser.PendingLongpress) { From 363811ebda0c85ee2a96592b716ee71ad7b433a2 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 30 Jun 2021 12:35:54 +0700 Subject: [PATCH 36/58] fix(web): embedded subkey cancellation --- web/source/osk/embedded/subkeyDelegator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts index 207edf7310..3fe6aba144 100644 --- a/web/source/osk/embedded/subkeyDelegator.ts +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -53,7 +53,7 @@ namespace com.keyman.osk.embedded { // Handle selection of base key underneath the subkey array. keyEvent = this.vkbd.keyEventFromSpec(this.baseKey.key.spec as keyboards.ActiveKey, null); this.baseKey.key.highlight(false); - } else { + } else if(keyCoreID != null) { // This is set with the base key of our current subkey elsewhere within the engine. let baseKey: OSKKeySpec = this.baseKey.key.spec; let selectedKey: OSKKeySpec; From 8aa9f49fd96013d24cc80de71c901233a9348a61 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 30 Jun 2021 13:02:43 +0700 Subject: [PATCH 37/58] change(web): failed subkey lookup - bye bye, catch & try --- web/source/kmwembedded.ts | 11 +---------- web/source/osk/embedded/subkeyDelegator.ts | 3 ++- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index 0cc1308a76..393f32727d 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -367,16 +367,7 @@ namespace com.keyman.text { // This should be set if we're within this method... but it's best to guard against nulls here, just in case. if(osk.vkbd.subkeyGesture) { let gesture = osk.vkbd.subkeyGesture as com.keyman.osk.embedded.SubkeyDelegator; - - try { - // Can trigger an error if the corresponding subkey cannot be found. - gesture.resolve(keyName); - } catch (e) { - let err = e as Error; - // Prevent the Android app from triggering a "fatal error" keyboard reset. - console.warn(err.message); - } - + gesture.resolve(keyName); osk.vkbd.subkeyGesture = null; } else { console.warn("No base key exists for the subkey being executed: '" + origArg + "'"); diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts index 3fe6aba144..d8c0c638c0 100644 --- a/web/source/osk/embedded/subkeyDelegator.ts +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -70,7 +70,8 @@ namespace com.keyman.osk.embedded { // While we can't complete successfully, the subkey operation is done; we // should still signal that and update related gesture state management. this.resolver(null); - throw new Error("Could not find subkey '" + keyCoreID + "' under base key '" + baseKey.coreID + "'!"); + console.error("Could not find subkey '" + keyCoreID + "' under base key '" + baseKey.coreID + "'!"); + return; } keyEvent = this.vkbd.keyEventFromSpec(selectedKey as keyboards.ActiveKey, null); From 8d0364016f6dd7812d1c0cbfe05c2df9dcd60f24 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 1 Jul 2021 08:11:43 +0700 Subject: [PATCH 38/58] fix(web): missed a '!' for in-browser base key reselection --- web/source/osk/browser/subkeyPopup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index 3f4b1b3524..f9ea22aefb 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -273,7 +273,7 @@ namespace com.keyman.osk.browser { } // Use the popup duplicate of the base key if a phone with a visible popup array - if(this.currentSelection && this.baseKey.key.isUnderTouch(touch)) { + if(!this.currentSelection && this.baseKey.key.isUnderTouch(touch)) { this.baseKey.key.highlight(true); this.currentSelection = this.baseKey; } From b77561893701891ad61effa69da0263f996b308b Mon Sep 17 00:00:00 2001 From: jahorton Date: Fri, 2 Jul 2021 09:09:58 +0700 Subject: [PATCH 39/58] fix(web): embedded ios stuck base key highlights --- web/source/osk/browser/subkeyPopup.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index f9ea22aefb..9b272f3bab 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -111,6 +111,7 @@ namespace com.keyman.osk.browser { let keyEvent: text.KeyEvent = null; if(this.currentSelection) { keyEvent = this.vkbd.initKeyEvent(this.currentSelection, touch); + this.currentSelection.key.highlight(false); } this.resolver(keyEvent); } From 543812c0d7828bef7bb58f0a7e59a7355849ff96 Mon Sep 17 00:00:00 2001 From: jahorton Date: Fri, 2 Jul 2021 09:25:29 +0700 Subject: [PATCH 40/58] fix(web): missing 'bind' for in-browser resolve --- web/source/osk/browser/pendingLongpress.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/source/osk/browser/pendingLongpress.ts b/web/source/osk/browser/pendingLongpress.ts index 00fca0e500..84b2364dc0 100644 --- a/web/source/osk/browser/pendingLongpress.ts +++ b/web/source/osk/browser/pendingLongpress.ts @@ -40,7 +40,7 @@ namespace com.keyman.osk.browser { _this.resolver = resolve; // After the timeout, it's no longer deferred; it's being fulfilled. // Even if the actual subkey itself is still async. - _this.timerId = window.setTimeout(_this.resolve, _this.popupDelay); + _this.timerId = window.setTimeout(_this.resolve.bind(_this), _this.popupDelay); }); } From c731f28fe08cf74f8b05f2791cb83b46734149e9 Mon Sep 17 00:00:00 2001 From: jahorton Date: Fri, 2 Jul 2021 09:37:30 +0700 Subject: [PATCH 41/58] docs(web): suggested tweaks from review --- web/source/kmwembedded.ts | 19 +++++++++++++------ web/source/osk/embedded/pendingLongpress.ts | 8 +++++--- web/source/osk/embedded/subkeyDelegator.ts | 10 +++++----- web/source/osk/pendingGesture.interface.ts | 4 ++-- web/source/osk/realizedGesture.interface.ts | 4 ++-- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index 1c8fbbf692..5dc967d4b3 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -63,6 +63,10 @@ namespace com.keyman.osk { return new embedded.PendingLongpress(this, key); } else { + // When embedded within our Android app, we expect the `oskCreatePopup` function to + // exist; all subkey control is delegated to the app. + // + // No function = big problem. return null; } }; @@ -266,7 +270,7 @@ namespace com.keyman.text { /** * Function called by Android and iOS when a device-implemented keyboard popup - * is displayed or hidden. As this is controlled by the app, it's the perfect + * is displayed or hidden. As this is controlled by the app, we use it as a * trigger for 'embedded'-mode gesture state management. * * @param {boolean} isVisible @@ -278,9 +282,9 @@ namespace com.keyman.text { /* * If a longpress popup was visible, but is no longer, this means that the - * associated longpress gesture was cancelled. It is possible for the base key - * to emit if selected at this time, but only if appropriate - and this is - * managed by the `SubkeyDelegator`. + * associated longpress gesture was cancelled. It is possible for the base + * key to emit if selected at this time; detecton of this is managed by + * the `SubkeyDelegator` class. */ if(!isVisible) { if(gesture) { @@ -294,8 +298,11 @@ namespace com.keyman.text { /* * If the popup was not visible, but now is, that means our previously-pending - * longpress is now 'realized' (complete). Certain aspects of the OSK rely on - * this state information, which will be properly updated by `resolve`. + * longpress is now 'realized' (complete). The OSK relies upon this state + * information, which will be properly updated by `resolve`. + * + * Prominent uses of such state info helps prevent change of base key, key + * previews, and key output from occurring while a subkey popup remains active. */ if(isVisible && pendingLongpress) { // Fulfills the first-stage promise. diff --git a/web/source/osk/embedded/pendingLongpress.ts b/web/source/osk/embedded/pendingLongpress.ts index 3b407cd610..504c60f1e3 100644 --- a/web/source/osk/embedded/pendingLongpress.ts +++ b/web/source/osk/embedded/pendingLongpress.ts @@ -8,9 +8,11 @@ namespace com.keyman.osk.embedded { * serves mostly to communicate longpress state management from the * app to the VisualKeyboard. * - * The `resolve()` function should be triggered, in some fashion, by - * the host app whenever it has recognized a completed longpress and - * will begin displaying its subkey popup. + * The `resolve()` function should be triggered by the host app + * whenever it has recognized a completed longpress and has thus + * begun displaying its subkey popup. (Should these two events + * ever become separated in time, the latter is the more critical + * aspect.) */ export class PendingLongpress implements PendingGesture { private resolver: (delegator: SubkeyDelegator) => void; diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts index d8c0c638c0..645bb8c6a7 100644 --- a/web/source/osk/embedded/subkeyDelegator.ts +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -2,9 +2,10 @@ namespace com.keyman.osk.embedded { /** - * As the subkey popup view is handled by the host app when in embedded mode, - * this class represents the fact that KMW has "delegated" subkey UI and - * selection to the host app. Hence, "Delegator", rather than "Popup". + * As the subkey popup view is handled by the host app when in embedded mode + * within our Android app, this class represents the fact that KMW has + * "delegated" subkey UI and selection to the host app. Hence, "Delegator", + * rather than "Popup". * * The `resolve` method should be triggered, in some fashion, by the host app * whenever the user has completed their longpress, potentially selecting @@ -40,7 +41,6 @@ namespace com.keyman.osk.embedded { * * If no subkey is selected but the original base key is, `resolve(null)` * will return a key event corresponding to the base key. - * * @param keyCoreID {string} The 'core ID' (id + modifier layer) of * a selected subkey. May be `null`. @@ -76,7 +76,7 @@ namespace com.keyman.osk.embedded { keyEvent = this.vkbd.keyEventFromSpec(selectedKey as keyboards.ActiveKey, null); keyEvent.vkCode=keyEvent.Lcode; - } + } // else /* if(keyCoreID == null) */ keyEvent = null; // As initialized at the top. this.resolver(keyEvent); } diff --git a/web/source/osk/pendingGesture.interface.ts b/web/source/osk/pendingGesture.interface.ts index 8cea889b89..637b108f58 100644 --- a/web/source/osk/pendingGesture.interface.ts +++ b/web/source/osk/pendingGesture.interface.ts @@ -1,8 +1,8 @@ namespace com.keyman.osk { /** * Used for evaluating potential gestures. Classes adhering to this interface - * should instantiated whenever the (implied) state-machine allows a new touch - * event to mark the start of a potential new gesture. + * should be instantiated whenever the (implied) state-machine allows a new + * touch event to mark the start of a potential new gesture. * * For example, whenever a user touches a base key and there are no "realized" * (fully-completed, but as-of-yet unresolved) gestures, that state allows the diff --git a/web/source/osk/realizedGesture.interface.ts b/web/source/osk/realizedGesture.interface.ts index 6683b4357e..faabf0a367 100644 --- a/web/source/osk/realizedGesture.interface.ts +++ b/web/source/osk/realizedGesture.interface.ts @@ -1,5 +1,5 @@ namespace com.keyman.osk { - /* + /** * Implementations of this interface allow individual types of gestures to * specify any additional user interaction and functionality (which may * include UI elements) appropriate for obtaining a key event that may be @@ -19,7 +19,7 @@ namespace com.keyman.osk { * will be inherently async. The simplest way to model this is with `Promise`s. * * If appropriate for the modeled gesture type, an implementation may supply an - * instantly-resolving `Promise``. This may be appropriate for modeling "flick" + * instantly-resolving `Promise`. This may be appropriate for modeling "flick" * or "swipe" gestures in the future, which may require no additional input once * such a gesture is fully realized. */ From da5c540661c63ee53252fc760e67abd2df8580dd Mon Sep 17 00:00:00 2001 From: jahorton Date: Fri, 2 Jul 2021 09:38:17 +0700 Subject: [PATCH 42/58] change(web): adds error log for missing embed-link func --- web/source/kmwembedded.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index 5dc967d4b3..d548909314 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -67,6 +67,7 @@ namespace com.keyman.osk { // exist; all subkey control is delegated to the app. // // No function = big problem. + console.error("Missing `oskCreatePopup` function for engine integration."); return null; } }; From fe9715127fe1476405e949676cf6db7573502c29 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Tue, 6 Jul 2021 11:29:11 +0700 Subject: [PATCH 43/58] fix(android/engine): Display dictionary help link --- .../main/java/com/tavultesoft/kmea/data/LexicalModel.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/LexicalModel.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/LexicalModel.java index 5e3bcb4894..f636fa9c76 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/LexicalModel.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/LexicalModel.java @@ -65,7 +65,7 @@ public class LexicalModel extends LanguageResource implements Serializable { String version = lexicalModelJSON.optString(KMManager.KMKey_Version, "1.0"); version = lexicalModelJSON.optString(KMManager.KMKey_LexicalModelVersion, version); - String helpLink = ""; // TOODO: Handle help links + String helpLink = lexicalModelJSON.optString(KMManager.KMKey_CustomHelpLink, ""); // language ID and language name from lexicalModelJSON. Iterate through language array String languageID = "", languageName = ""; @@ -95,9 +95,8 @@ public class LexicalModel extends LanguageResource implements Serializable { public LexicalModel(String packageID, String lexicalModelID, String lexicalModelName, String languageID, String languageName, String version, String helpLink, String kmp) { - // TODO: handle help links super(packageID, lexicalModelID, lexicalModelName, languageID, languageName, - version, "", kmp); + version, helpLink, kmp); } @Override From fefffc80fd2d5b766ae0c6bc0a0821ea11266668 Mon Sep 17 00:00:00 2001 From: jahorton Date: Tue, 6 Jul 2021 14:14:14 +0700 Subject: [PATCH 44/58] fix(web): more consistent clear on base key highlights --- web/source/osk/browser/subkeyPopup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index 9b272f3bab..1f7378d936 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -253,6 +253,7 @@ namespace com.keyman.osk.browser { updateTouch(touch: Touch) { this.currentSelection = null; + this.baseKey.key.highlight(false); for(let i=0; i < this.baseKey['subKeys'].length; i++) { try { @@ -260,7 +261,6 @@ namespace com.keyman.osk.browser { let onKey = sk.key.isUnderTouch(touch); if(onKey) { - this.baseKey.key.highlight(false); this.currentSelection = sk; } sk.key.highlight(onKey); From 43d5181092db17c4d7a24799e985a56c8e292a95 Mon Sep 17 00:00:00 2001 From: jahorton Date: Tue, 6 Jul 2021 14:27:37 +0700 Subject: [PATCH 45/58] change(web): more consistent base-key longpress release --- web/source/osk/browser/subkeyPopup.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index 1f7378d936..23c7e24bee 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -43,7 +43,11 @@ namespace com.keyman.osk.browser { this.vkbd = vkbd; this.baseKey = e; - this.currentSelection = null; + + // If the user doesn't move their finger and releases, we'll output the base key + // by default. + this.currentSelection = e; + e.key.highlight(true); // A tag we directly set on a key element during its construction. let subKeySpec: OSKKeySpec[] = e['subKeys']; From 35e063b8a0fbe4cc1f860a6f33ad1d1507808506 Mon Sep 17 00:00:00 2001 From: Keyman Build Agent Date: Tue, 6 Jul 2021 14:03:01 -0400 Subject: [PATCH 46/58] auto: increment master version to 15.0.82 --- HISTORY.md | 7 +++++++ VERSION.md | 2 +- .../web/input-processor/package-lock.json | 2 +- common/core/web/input-processor/package.json | 16 +++++++-------- .../web/keyboard-processor/package-lock.json | 2 +- .../core/web/keyboard-processor/package.json | 10 +++++----- .../core/web/tools/recorder/package-lock.json | 2 +- common/core/web/tools/recorder/package.json | 10 +++++----- .../tools/sentry-manager/package-lock.json | 2 +- .../web/tools/sentry-manager/package.json | 4 ++-- common/core/web/utils/package-lock.json | 2 +- common/core/web/utils/package.json | 6 +++--- common/models/templates/package-lock.json | 2 +- common/models/templates/package.json | 8 ++++---- common/models/types/package-lock.json | 2 +- common/models/types/package.json | 2 +- common/models/wordbreakers/package-lock.json | 2 +- common/models/wordbreakers/package.json | 4 ++-- common/predictive-text/package-lock.json | 2 +- common/predictive-text/package.json | 14 ++++++------- developer/js/package-lock.json | 2 +- developer/js/package.json | 8 ++++---- lerna.json | 2 +- resources/gosh/package-lock.json | 2 +- resources/gosh/package.json | 2 +- resources/web-environment/package-lock.json | 2 +- resources/web-environment/package.json | 4 ++-- web/package-lock.json | 2 +- web/package.json | 20 +++++++++---------- 29 files changed, 76 insertions(+), 69 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 5bc3feb1d6..075aa427bb 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,12 @@ # Keyman Version History +## 15.0.81 alpha 2021-07-06 + +* fix(android/engine): Fix font file path (#5424) +* chore(common): npm audit fixes (#5419) +* chore(web): updates regression test-deps to same versions as web (#5420) +* chore(windows): make build paths consistent (#5405) + ## 15.0.80 alpha 2021-07-02 * refactor(android): setKeyboard and setKeymanLanguage (#5338) diff --git a/VERSION.md b/VERSION.md index 9220ad1e60..a582683631 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -15.0.81 \ No newline at end of file +15.0.82 \ No newline at end of file diff --git a/common/core/web/input-processor/package-lock.json b/common/core/web/input-processor/package-lock.json index a2e109e69c..813df5bedd 100644 --- a/common/core/web/input-processor/package-lock.json +++ b/common/core/web/input-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "15.0.81", + "version": "15.0.82", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/core/web/input-processor/package.json b/common/core/web/input-processor/package.json index ff8afc5cdc..c30475e4f2 100644 --- a/common/core/web/input-processor/package.json +++ b/common/core/web/input-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "15.0.81", + "version": "15.0.82", "description": "The core text and prediction processing engine for KeymanWeb", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/lexical-model-compiler": "^15.0.81", - "@keymanapp/resources-gosh": "^15.0.81", + "@keymanapp/lexical-model-compiler": "^15.0.82", + "@keymanapp/resources-gosh": "^15.0.82", "@types/node": "^11.9.4", "chai": "^4.3.4", "mocha": "^8.4.0", @@ -34,11 +34,11 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/keyboard-processor": "^15.0.81", - "@keymanapp/lexical-model-layer": "^15.0.81", - "@keymanapp/models-types": "^15.0.81", - "@keymanapp/web-environment": "^15.0.81", - "@keymanapp/web-utils": "^15.0.81", + "@keymanapp/keyboard-processor": "^15.0.82", + "@keymanapp/lexical-model-layer": "^15.0.82", + "@keymanapp/models-types": "^15.0.82", + "@keymanapp/web-environment": "^15.0.82", + "@keymanapp/web-utils": "^15.0.82", "eventemitter3": "^4.0.0" } } diff --git a/common/core/web/keyboard-processor/package-lock.json b/common/core/web/keyboard-processor/package-lock.json index 3a028078b9..71c878a4fe 100644 --- a/common/core/web/keyboard-processor/package-lock.json +++ b/common/core/web/keyboard-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "15.0.81", + "version": "15.0.82", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/core/web/keyboard-processor/package.json b/common/core/web/keyboard-processor/package.json index 8c31643bb6..99e32fc663 100644 --- a/common/core/web/keyboard-processor/package.json +++ b/common/core/web/keyboard-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "15.0.81", + "version": "15.0.82", "description": "Core module for Keyman keyboard support in KeymanWeb.", "repository": { "type": "git", @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^15.0.81", + "@keymanapp/resources-gosh": "^15.0.82", "chai": "^4.3.4", "mocha": "^8.4.0", "mocha-teamcity-reporter": "^4.0.0", @@ -32,9 +32,9 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/models-types": "^15.0.81", - "@keymanapp/web-environment": "^15.0.81", - "@keymanapp/web-utils": "^15.0.81", + "@keymanapp/models-types": "^15.0.82", + "@keymanapp/web-environment": "^15.0.82", + "@keymanapp/web-utils": "^15.0.82", "@types/node": "^11.9.4" } } diff --git a/common/core/web/tools/recorder/package-lock.json b/common/core/web/tools/recorder/package-lock.json index 71dd357905..419ad1ed81 100644 --- a/common/core/web/tools/recorder/package-lock.json +++ b/common/core/web/tools/recorder/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "15.0.81", + "version": "15.0.82", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/core/web/tools/recorder/package.json b/common/core/web/tools/recorder/package.json index a56b6cd528..725c0b1a72 100644 --- a/common/core/web/tools/recorder/package.json +++ b/common/core/web/tools/recorder/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "15.0.81", + "version": "15.0.82", "description": "Core classes used to develop KeymanWeb test cases based on keystrokes", "main": "index.js", "scripts": { @@ -17,10 +17,10 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "dependencies": { - "@keymanapp/keyboard-processor": "^15.0.81", - "@keymanapp/models-types": "^15.0.81", - "@keymanapp/web-environment": "^15.0.81", - "@keymanapp/web-utils": "^15.0.81", + "@keymanapp/keyboard-processor": "^15.0.82", + "@keymanapp/models-types": "^15.0.82", + "@keymanapp/web-environment": "^15.0.82", + "@keymanapp/web-utils": "^15.0.82", "@types/node": "^11.9.4" }, "devDependencies": { diff --git a/common/core/web/tools/sentry-manager/package-lock.json b/common/core/web/tools/sentry-manager/package-lock.json index cb17ad7e4b..089d4f87d4 100644 --- a/common/core/web/tools/sentry-manager/package-lock.json +++ b/common/core/web/tools/sentry-manager/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "15.0.81", + "version": "15.0.82", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/sentry-manager/package.json b/common/core/web/tools/sentry-manager/package.json index 9e8809f51e..ff7874a7d7 100644 --- a/common/core/web/tools/sentry-manager/package.json +++ b/common/core/web/tools/sentry-manager/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "15.0.81", + "version": "15.0.82", "description": "Common setup for KeymanWeb's Sentry-based error reporting", "main": "dist/index.js", "scripts": { @@ -20,7 +20,7 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/web-environment": "^15.0.81", + "@keymanapp/web-environment": "^15.0.82", "@sentry/browser": "^5.27.4" } } diff --git a/common/core/web/utils/package-lock.json b/common/core/web/utils/package-lock.json index bc628008a3..8b9d5372e6 100644 --- a/common/core/web/utils/package-lock.json +++ b/common/core/web/utils/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "15.0.81", + "version": "15.0.82", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/utils/package.json b/common/core/web/utils/package.json index 9f70cd704f..5608dd5415 100644 --- a/common/core/web/utils/package.json +++ b/common/core/web/utils/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "15.0.81", + "version": "15.0.82", "description": "Common utility functions used throughout other Keyman packages", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -20,8 +20,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^15.0.81", - "@keymanapp/web-environment": "^15.0.81", + "@keymanapp/resources-gosh": "^15.0.82", + "@keymanapp/web-environment": "^15.0.82", "@types/node": "^14.0.5", "typescript": "^3.8.3" } diff --git a/common/models/templates/package-lock.json b/common/models/templates/package-lock.json index 7dd5375e55..29fff5ac75 100644 --- a/common/models/templates/package-lock.json +++ b/common/models/templates/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "15.0.81", + "version": "15.0.82", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/models/templates/package.json b/common/models/templates/package.json index 6199c86dd0..7f7520fd7e 100644 --- a/common/models/templates/package.json +++ b/common/models/templates/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "15.0.81", + "version": "15.0.82", "description": "Backing model implementations (templates) for Keyman's modelling layer", "keywords": [ "lm", @@ -42,8 +42,8 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^15.0.81", - "@keymanapp/web-utils": "^15.0.81", + "@keymanapp/models-types": "^15.0.82", + "@keymanapp/web-utils": "^15.0.82", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "@types/node": "^14.0.4", @@ -52,6 +52,6 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-wordbreakers": "^15.0.81" + "@keymanapp/models-wordbreakers": "^15.0.82" } } diff --git a/common/models/types/package-lock.json b/common/models/types/package-lock.json index 554c9cc229..981fddc5bf 100644 --- a/common/models/types/package-lock.json +++ b/common/models/types/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "15.0.81", + "version": "15.0.82", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/types/package.json b/common/models/types/package.json index 4279d6d966..ea116b48a5 100644 --- a/common/models/types/package.json +++ b/common/models/types/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "15.0.81", + "version": "15.0.82", "description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.", "types": "./index.d.ts", "scripts": { diff --git a/common/models/wordbreakers/package-lock.json b/common/models/wordbreakers/package-lock.json index 493ea66c94..e4666c5262 100644 --- a/common/models/wordbreakers/package-lock.json +++ b/common/models/wordbreakers/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "15.0.81", + "version": "15.0.82", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/models/wordbreakers/package.json b/common/models/wordbreakers/package.json index 905dc5799a..c89c3e33a6 100644 --- a/common/models/wordbreakers/package.json +++ b/common/models/wordbreakers/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "15.0.81", + "version": "15.0.82", "description": "Word breakers bundled in the modeling layer", "keywords": [ "lexical model", @@ -40,7 +40,7 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^15.0.81", + "@keymanapp/models-types": "^15.0.82", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "chai": "^4.3.4", diff --git a/common/predictive-text/package-lock.json b/common/predictive-text/package-lock.json index 22ba2e71b6..1d22e60fd8 100644 --- a/common/predictive-text/package-lock.json +++ b/common/predictive-text/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "15.0.81", + "version": "15.0.82", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/predictive-text/package.json b/common/predictive-text/package.json index 6f36d8c55b..1ec86395ef 100644 --- a/common/predictive-text/package.json +++ b/common/predictive-text/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "15.0.81", + "version": "15.0.82", "description": "Keyman/Predictive Text integration layer", "main": "build/index.js", "scripts": { @@ -21,8 +21,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/models-types": "^15.0.81", - "@keymanapp/resources-gosh": "^15.0.81", + "@keymanapp/models-types": "^15.0.82", + "@keymanapp/resources-gosh": "^15.0.82", "@types/node": "^10.17.21", "chai": "^4.3.4", "karma": "^6.3.4", @@ -46,10 +46,10 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-templates": "^15.0.81", - "@keymanapp/models-wordbreakers": "^15.0.81", - "@keymanapp/web-environment": "^15.0.81", - "@keymanapp/web-utils": "^15.0.81", + "@keymanapp/models-templates": "^15.0.82", + "@keymanapp/models-wordbreakers": "^15.0.82", + "@keymanapp/web-environment": "^15.0.82", + "@keymanapp/web-utils": "^15.0.82", "es6-shim": "^0.35.5", "string.prototype.codepointat": "^0.2.1", "string.prototype.startswith": "^0.2.0" diff --git a/developer/js/package-lock.json b/developer/js/package-lock.json index 7ed35172d1..6aeab2921e 100644 --- a/developer/js/package-lock.json +++ b/developer/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "15.0.81", + "version": "15.0.82", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/developer/js/package.json b/developer/js/package.json index e6cdd1355f..bb1f7d2e29 100644 --- a/developer/js/package.json +++ b/developer/js/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "15.0.81", + "version": "15.0.82", "description": "Keyman Developer lexical model compiler", "keywords": [ "keyboard", @@ -37,14 +37,14 @@ "node": ">=12.0.0" }, "dependencies": { - "@keymanapp/models-types": "^15.0.81", + "@keymanapp/models-types": "^15.0.82", "commander": "^3.0.0", "typescript": "^3.8.3", "xml2js": "^0.4.19" }, "devDependencies": { - "@keymanapp/models-templates": "^15.0.81", - "@keymanapp/models-wordbreakers": "^15.0.81", + "@keymanapp/models-templates": "^15.0.82", + "@keymanapp/models-wordbreakers": "^15.0.82", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", diff --git a/lerna.json b/lerna.json index 48bf23f036..e0f5d5589e 100644 --- a/lerna.json +++ b/lerna.json @@ -20,5 +20,5 @@ "ts-node", "typescript" ], - "version": "15.0.81" + "version": "15.0.82" } diff --git a/resources/gosh/package-lock.json b/resources/gosh/package-lock.json index 639e453c09..a3d5f82b3e 100644 --- a/resources/gosh/package-lock.json +++ b/resources/gosh/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/resources-gosh", - "version": "15.0.81", + "version": "15.0.82", "lockfileVersion": 1 } diff --git a/resources/gosh/package.json b/resources/gosh/package.json index 3b4566d713..e40648ac1a 100644 --- a/resources/gosh/package.json +++ b/resources/gosh/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/resources-gosh", - "version": "15.0.81", + "version": "15.0.82", "description": "Good Ol' Shell", "author": "Marc Durdin (https://github.com/mcdurdin)", "contributors": [ diff --git a/resources/web-environment/package-lock.json b/resources/web-environment/package-lock.json index 1b2f3939b1..408b511608 100644 --- a/resources/web-environment/package-lock.json +++ b/resources/web-environment/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/web-environment", - "version": "15.0.81", + "version": "15.0.82", "lockfileVersion": 1 } diff --git a/resources/web-environment/package.json b/resources/web-environment/package.json index 67cd6c2b01..d225ecbba8 100644 --- a/resources/web-environment/package.json +++ b/resources/web-environment/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-environment", - "version": "15.0.81", + "version": "15.0.82", "description": "A compilation utility for passing select environment variables into our TS builds", "author": "Joshua Horton (https://github.com/jahorton)", "homepage": "https://github.com/keymanapp/keyman", @@ -17,6 +17,6 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/resources-gosh": "^15.0.81" + "@keymanapp/resources-gosh": "^15.0.82" } } diff --git a/web/package-lock.json b/web/package-lock.json index a816876426..4a6121652e 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "15.0.81", + "version": "15.0.82", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/web/package.json b/web/package.json index 6ff8e612fb..832495e550 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "15.0.81", + "version": "15.0.82", "description": "Facilitates text input in any language.", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^15.0.81", - "@keymanapp/web-sentry-manager": "^15.0.81", + "@keymanapp/resources-gosh": "^15.0.82", + "@keymanapp/web-sentry-manager": "^15.0.82", "@sentry/cli": "^1.52.3", "chai": "^4.3.4", "google-closure-compiler-java": "^20200224.0.0", @@ -52,13 +52,13 @@ "sentry-cli": "sentry-cli" }, "dependencies": { - "@keymanapp/input-processor": "^15.0.81", - "@keymanapp/keyboard-processor": "^15.0.81", - "@keymanapp/lexical-model-layer": "^15.0.81", - "@keymanapp/models-types": "^15.0.81", - "@keymanapp/recorder-core": "^15.0.81", - "@keymanapp/web-environment": "^15.0.81", - "@keymanapp/web-utils": "^15.0.81", + "@keymanapp/input-processor": "^15.0.82", + "@keymanapp/keyboard-processor": "^15.0.82", + "@keymanapp/lexical-model-layer": "^15.0.82", + "@keymanapp/models-types": "^15.0.82", + "@keymanapp/recorder-core": "^15.0.82", + "@keymanapp/web-environment": "^15.0.82", + "@keymanapp/web-utils": "^15.0.82", "@types/node": "^11.9.4", "es6-shim": "^0.35.5", "eventemitter3": "^4.0.0" From 845d65e5e475792de5f0133f7dcf6c326f4a186b Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Wed, 7 Jul 2021 08:09:26 +1000 Subject: [PATCH 47/58] chore(windows): remove backup build step We no longer need to backup debug files as they are saved to the symbol store during a release build. This reduces the amount of storage required for a release build and should speed up the build also. Also fixes the broken build where backup had not been updated for the new consistent build paths. --- windows/src/Defines.mak | 2 - windows/src/Header.mak | 8 +--- windows/src/Makefile | 4 -- windows/src/buildtools/Makefile | 6 +-- windows/src/buildtools/buildpkg/Makefile | 3 -- windows/src/buildtools/inst/.gitignore | 2 - windows/src/buildtools/inst/Makefile | 37 ---------------- windows/src/buildtools/inst/copydebug.in | 42 ------------------- windows/src/buildtools/inst/srcbackup.in | 10 ----- windows/src/buildtools/inst/tds.reg | 5 --- windows/src/buildtools/inst/tds_dialog.txt | 1 - windows/src/desktop/help/Makefile | 3 -- windows/src/desktop/insthelp/Makefile | 3 -- windows/src/desktop/kmbrowserhost/Makefile | 3 -- windows/src/desktop/kmconfig/Makefile | 3 -- windows/src/desktop/kmshell/Makefile | 3 -- windows/src/desktop/setup/Makefile | 3 -- windows/src/developer/TIKE/Makefile | 4 -- windows/src/developer/inst/Makefile | 3 -- windows/src/developer/kmanalyze/Makefile | 3 -- windows/src/developer/kmcmpdll/Makefile | 3 -- windows/src/developer/kmcomp/Makefile | 3 -- windows/src/developer/kmconvert/Makefile | 3 -- windows/src/developer/kmdecomp/Makefile | 3 -- .../src/developer/samples/imsample/Makefile | 3 -- windows/src/developer/setup/Makefile | 3 -- windows/src/engine/inst/Makefile | 4 -- windows/src/engine/inst/insthelper/Makefile | 3 -- windows/src/engine/keyman/Makefile | 3 -- windows/src/engine/keyman32/Makefile | 3 -- windows/src/engine/keyman64/Makefile | 3 -- windows/src/engine/keymanmc/Makefile | 3 -- windows/src/engine/keymanx64/Makefile | 3 -- windows/src/engine/kmcomapi/Makefile | 3 -- windows/src/engine/kmrefresh/Makefile | 4 -- windows/src/engine/kmtip/Makefile | 4 -- windows/src/engine/mcompile/Makefile | 3 -- windows/src/engine/tsysinfo/Makefile | 3 -- windows/src/engine/tsysinfox64/Makefile | 3 -- windows/src/global/Makefile | 3 -- windows/src/support/charident/Makefile | 3 -- windows/src/support/etl2log/Makefile | 3 -- windows/src/support/km_yim/Makefile | 2 - windows/src/support/kmkb0045/Makefile | 3 -- windows/src/support/oskbulkrenderer/Makefile | 3 -- .../manual-tests/test_httpuploader/Makefile | 3 -- 46 files changed, 2 insertions(+), 226 deletions(-) delete mode 100644 windows/src/buildtools/inst/.gitignore delete mode 100644 windows/src/buildtools/inst/Makefile delete mode 100644 windows/src/buildtools/inst/copydebug.in delete mode 100644 windows/src/buildtools/inst/srcbackup.in delete mode 100644 windows/src/buildtools/inst/tds.reg delete mode 100644 windows/src/buildtools/inst/tds_dialog.txt diff --git a/windows/src/Defines.mak b/windows/src/Defines.mak index f430c09840..47a81c6b19 100644 --- a/windows/src/Defines.mak +++ b/windows/src/Defines.mak @@ -205,8 +205,6 @@ WIXHEAT=$(WIXPATH)\heat.exe LINKPATH=link.exe -BACKUPDEFAULTS=*.map *.tds *.rsm *.dbg *.pdb *.cod - TORTOISEPROC=C:\Progra~1\TortoiseSVN\bin\TortoiseProc.exe # diff --git a/windows/src/Header.mak b/windows/src/Header.mak index c6a0b15eac..dee277efbc 100644 --- a/windows/src/Header.mak +++ b/windows/src/Header.mak @@ -19,7 +19,7 @@ ROOT=c:\keyman\windows !include $(ROOT)\src\Defines.mak # -# The targets build, signcode, symbols, backup are standard +# The targets build, signcode, symbols are standard # targets for all projects # @@ -32,18 +32,12 @@ signcode: symbols: $(MAKE) -DTARGET=symbols $(TARGETS) - -backup: - $(MAKE) -DTARGET=backup $(TARGETS) !ELSE signcode: rem no signcode required symbols: rem no symbols required - -backup: - rem no backup required !ENDIF build-release: diff --git a/windows/src/Makefile b/windows/src/Makefile index 36c9ba9d17..4b35eac954 100644 --- a/windows/src/Makefile +++ b/windows/src/Makefile @@ -244,7 +244,6 @@ build-release-wrapper: clean-output global-versions $(MAKE) -DSIGNCODE_BUILD build-release $(MAKE) signcode $(MAKE) -DTARGET=test test - $(MAKE) backup !endif cd $(ROOT)\src\engine\inst @@ -260,9 +259,6 @@ build-release-wrapper: clean-output global-versions cd $(ROOT)\src\developer\inst $(MAKE) - - cd $(ROOT)\src\buildtools\inst - $(MAKE) !endif build-release-wrapper-oem: diff --git a/windows/src/buildtools/Makefile b/windows/src/buildtools/Makefile index fdfa300b52..d84bef5bef 100644 --- a/windows/src/buildtools/Makefile +++ b/windows/src/buildtools/Makefile @@ -6,7 +6,7 @@ NOTARGET_SIGNCODE=yes TARGETS=devtools sentrytool tds2dbg mkver \ buildunidata build_standards_data buildpkg test-klog delphiprojectmanager -CLEANS=inst clean-buildtools +CLEANS=clean-buildtools !include ..\Header.mak @@ -40,10 +40,6 @@ mkver: cd $(ROOT)\src\buildtools\mkver $(MAKE) $(TARGET) -inst: - cd $(ROOT)\src\buildtools\inst - $(MAKE) $(TARGET) - buildpkg: cd $(ROOT)\src\buildtools\buildpkg $(MAKE) $(TARGET) diff --git a/windows/src/buildtools/buildpkg/Makefile b/windows/src/buildtools/buildpkg/Makefile index 07ff23c4b7..dd567d8b31 100644 --- a/windows/src/buildtools/buildpkg/Makefile +++ b/windows/src/buildtools/buildpkg/Makefile @@ -17,7 +17,4 @@ clean: def-clean signcode: $(SIGNCODE) /d "Package Installer Creator for Server" $(PROGRAM)\online\Buildpkg.exe -backup: - rem #$(WZZIP) $(BUILD)\buildtools\Buildpkg.zip $(BACKUPDEFAULTS) Buildpkg.exe - !include ..\..\Target.mak diff --git a/windows/src/buildtools/inst/.gitignore b/windows/src/buildtools/inst/.gitignore deleted file mode 100644 index 5587f1f702..0000000000 --- a/windows/src/buildtools/inst/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# These files generated during release build should not be committed -copydebug.mak diff --git a/windows/src/buildtools/inst/Makefile b/windows/src/buildtools/inst/Makefile deleted file mode 100644 index d5c621084f..0000000000 --- a/windows/src/buildtools/inst/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# -# Install tools Makefile -# - -!include ..\..\Defines.mak - -build: copydebug - -clean: - cd $(ROOT)\src\buildtools\inst - -del copydebug.mak - -del srcbackup.mak - -copydebug: - cd $(ROOT)\src - -del $(ROOT)\src\tds_file.txt - dir /b/s desktop\*.dbg desktop\*.pdb > $(ROOT)\src\tds_file.txt - dir /b/s developer\*.dbg developer\*.pdb >> $(ROOT)\src\tds_file.txt - dir /b/s engine\*.dbg engine\*.pdb >> $(ROOT)\src\tds_file.txt - $(WZZIP) -x!vc*.pdb tds.zip @tds_file.txt - -del $(ROOT)\src\tds_file.txt - - cd $(ROOT)\src\buildtools\inst - $(MKVER_U) copydebug.in copydebug.mak - $(MAKE) -fcopydebug.mak - -uploadsymbols: - cd $(ROOT)\src\buildtools\inst - $(MKVER_U) copydebug.in copydebug.mak - $(MAKE) -fcopydebug.mak uploadsymbols - -srcbackup: - cd $(ROOT)\src\buildtools\inst - $(MKVER_U) $(ROOT)\src\buildtools\inst\srcbackup.in srcbackup.mak - make -fsrcbackup.mak - -!include ..\..\Target.mak diff --git a/windows/src/buildtools/inst/copydebug.in b/windows/src/buildtools/inst/copydebug.in deleted file mode 100644 index 4f7cb7e761..0000000000 --- a/windows/src/buildtools/inst/copydebug.in +++ /dev/null @@ -1,42 +0,0 @@ -# -# Copy debug information to the RELEASE directory -# - -SIGNCODE_BUILD=1 - -!include ..\..\Defines.mak - -copydebug: - -mkdir $(ROOT)\release\$Version - - echo mkdir c:\kmsymbols > $(ROOT)\src\install.bat - echo mkdir c:\kmsymbols\$Version >> $(ROOT)\src\install.bat - echo copy *.tds c:\kmsymbols\$Version >> $(ROOT)\src\install.bat - echo copy *.dbg c:\kmsymbols\$Version >> $(ROOT)\src\install.bat - echo copy *.pdb c:\kmsymbols\$Version >> $(ROOT)\src\install.bat - echo regedit /s tds.reg >> $(ROOT)\src\install.bat - $(WZZIP) $(ROOT)\src\tds.zip $(ROOT)\src\install.bat $(ROOT)\src\buildtools\inst\tds.reg - - rem Disabling symsrv upload from the build agents (2018-06-19 mcdurdin) - # "C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\symstore" add /r /f $(ROOT)\debug\*.* /s c:\Tavultesoft\Debug\Symbols /t "Keyman" /v "Build $Version" /c "Release $Version" - # "C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\symstore" add /r /f $(ROOT)\bin\*.* /s c:\Tavultesoft\Debug\Symbols /t "Keyman" /v "Build $Version" /c "Release $Version" - - # ping localhost # delay for a few seconds - # $(WZSE) $(ROOT)\src\tds.zip -setup -t $(ROOT)\src\buildtools\inst\tds_dialog.txt -c .\install.bat - copy $(ROOT)\src\tds.zip $(ROOT)\release\$Version\tds-$Version.zip - -del $(ROOT)\src\tds.zip - - del $(ROOT)\src\install.bat - - # wait 1 second -- gives winzip time to finish - # ping -n 2 127.0.0.1 - # copy $(ROOT)\src\tds.exe $(ROOT)\release\$Version\tds-$Version.exe - cd $(BUILD) - $(WZZIP) -r $(ROOT)\release\$Version\debug-$Version.zip *.zip - - -uploadsymbols: - rem Disabling symsrv upload from the build agents (2018-06-19 mcdurdin) - # "C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\symstore" add /r /f $(ROOT)\debug\*.* /s c:\Tavultesoft\Debug\Symbols /t "Keyman" /v "Build $Version" /c "Release $Version" - # "C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\symstore" add /r /f $(ROOT)\bin\*.* /s c:\Tavultesoft\Debug\Symbols /t "Keyman" /v "Build $Version" /c "Release $Version" - diff --git a/windows/src/buildtools/inst/srcbackup.in b/windows/src/buildtools/inst/srcbackup.in deleted file mode 100644 index f957819f7d..0000000000 --- a/windows/src/buildtools/inst/srcbackup.in +++ /dev/null @@ -1,10 +0,0 @@ -# -# Copy debug information to the RELEASE directory -# - -!include ..\..\Defines.mak - -srcbackup: - -mkdir $(ROOT)\release\$Version - cd $(ROOT)\src - $(WZZIP) -r -x!*.ipch -x!*.sdf -x!*.db $(ROOT)\release\$Version\src-$Version.zip *.* diff --git a/windows/src/buildtools/inst/tds.reg b/windows/src/buildtools/inst/tds.reg deleted file mode 100644 index b7c5ac6dd5..0000000000 --- a/windows/src/buildtools/inst/tds.reg +++ /dev/null @@ -1,5 +0,0 @@ -REGEDIT4 - -[HKEY_LOCAL_MACHINE\SOFTWARE\Tavultesoft\Exception] -"SymbolPath"="c:\\kmsymbols\\" - diff --git a/windows/src/buildtools/inst/tds_dialog.txt b/windows/src/buildtools/inst/tds_dialog.txt deleted file mode 100644 index e4d94c60ac..0000000000 --- a/windows/src/buildtools/inst/tds_dialog.txt +++ /dev/null @@ -1 +0,0 @@ -Install Tavultesoft Keyman Desktop and Keyman Developer Symbol Files for Exception Reports diff --git a/windows/src/desktop/help/Makefile b/windows/src/desktop/help/Makefile index b9aa91645b..6ef32d2b58 100644 --- a/windows/src/desktop/help/Makefile +++ b/windows/src/desktop/help/Makefile @@ -32,9 +32,6 @@ web: signcode: @rem -backup: - @rem - clean: @rem diff --git a/windows/src/desktop/insthelp/Makefile b/windows/src/desktop/insthelp/Makefile index 86796906d0..1c5f77dcc3 100644 --- a/windows/src/desktop/insthelp/Makefile +++ b/windows/src/desktop/insthelp/Makefile @@ -24,9 +24,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\desktop\insthelp.exe /t keyman-windows $(SYMSTORE) $(DEBUGPATH)\desktop\insthelp.dbg /t keyman-windows -backup: - $(WZZIP) $(BUILD)\desktop\insthelp.zip $(BACKUPDEFAULTS) insthelp.exe - install: rem diff --git a/windows/src/desktop/kmbrowserhost/Makefile b/windows/src/desktop/kmbrowserhost/Makefile index fa126024c8..32ba89e630 100644 --- a/windows/src/desktop/kmbrowserhost/Makefile +++ b/windows/src/desktop/kmbrowserhost/Makefile @@ -27,7 +27,4 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\desktop\kmbrowserhost.exe /t keyman-windows $(SYMSTORE) $(DEBUGPATH)\desktop\kmbrowserhost.dbg /t keyman-windows -backup: - $(WZZIP) $(BUILD)\desktop\kmbrowserhost.zip $(BACKUPDEFAULTS) $(WIN32_TARGET_PATH)\kmbrowserhost.exe $(WIN32_TARGET_PATH)\kmbrowserhost.dbg - !include ..\..\Target.mak diff --git a/windows/src/desktop/kmconfig/Makefile b/windows/src/desktop/kmconfig/Makefile index 75a3587c18..d063237156 100644 --- a/windows/src/desktop/kmconfig/Makefile +++ b/windows/src/desktop/kmconfig/Makefile @@ -23,9 +23,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\desktop\kmconfig.exe /t keyman-windows $(SYMSTORE) $(DEBUGPATH)\desktop\kmconfig.dbg /t keyman-windows -backup: - $(WZZIP) $(BUILD)\desktop\kmconfig.zip $(BACKUPDEFAULTS) kmconfig.exe - test-manifest: # test that linked manifest exists and correct $(MT) -nologo -inputresource:$(PROGRAM)\desktop\kmconfig.exe -validate_manifest diff --git a/windows/src/desktop/kmshell/Makefile b/windows/src/desktop/kmshell/Makefile index fd3093041b..65dd5b5297 100644 --- a/windows/src/desktop/kmshell/Makefile +++ b/windows/src/desktop/kmshell/Makefile @@ -38,9 +38,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\desktop\kmshell.exe /t keyman-windows $(SYMSTORE) $(DEBUGPATH)\desktop\kmshell.dbg /t keyman-windows -backup: - $(WZZIP) $(BUILD)\desktop\kmshell.zip $(BACKUPDEFAULTS) kmshell.exe - test-manifest: # test that (a) linked manifest exists and correct, and (b) has uiAccess=true $(MT) -nologo -inputresource:$(PROGRAM)\desktop\kmshell.exe -validate_manifest diff --git a/windows/src/desktop/setup/Makefile b/windows/src/desktop/setup/Makefile index 12a5195e55..499dc2e38e 100644 --- a/windows/src/desktop/setup/Makefile +++ b/windows/src/desktop/setup/Makefile @@ -33,9 +33,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\desktop\setup.exe /t keyman-windows $(SYMSTORE) $(DEBUGPATH)\desktop\setup.dbg /t keyman-windows -backup: - $(WZZIP) $(BUILD)\desktop\setup.zip $(BACKUPDEFAULTS) setup.exe - test-manifest: # test that (a) linked manifest exists and correct, and (b) has uiAccess=true $(MT) -nologo -inputresource:$(PROGRAM)\desktop\setup.exe -validate_manifest diff --git a/windows/src/developer/TIKE/Makefile b/windows/src/developer/TIKE/Makefile index 857feeed5c..cf466bc2f4 100644 --- a/windows/src/developer/TIKE/Makefile +++ b/windows/src/developer/TIKE/Makefile @@ -76,10 +76,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\developer\tike.exe /t keyman-developer $(SYMSTORE) $(DEBUGPATH)\developer\tike.dbg /t keyman-developer -backup: - $(WZZIP) $(BUILD)\developer\tike.zip $(BACKUPDEFAULTS) tike.exe - - install: $(COPY) $(PROGRAM)\developer\tike.exe "$(INSTALLPATH_KEYMANDEVELOPER)\tike.exe" diff --git a/windows/src/developer/inst/Makefile b/windows/src/developer/inst/Makefile index 3357750dd9..cdf3ac8549 100644 --- a/windows/src/developer/inst/Makefile +++ b/windows/src/developer/inst/Makefile @@ -44,9 +44,6 @@ build: signcode: @rem -backup: - @rem - wrap-symbols: @rem diff --git a/windows/src/developer/kmanalyze/Makefile b/windows/src/developer/kmanalyze/Makefile index 0bbac14a23..adda01501d 100644 --- a/windows/src/developer/kmanalyze/Makefile +++ b/windows/src/developer/kmanalyze/Makefile @@ -19,9 +19,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\developer\kmanalyze.exe /t keyman-developer $(SYMSTORE) $(DEBUGPATH)\developer\kmanalyze.pdb /t keyman-developer -backup: - $(WZZIP) $(BUILD)\developer\kmanalyze.exe $(BACKUPDEFAULTS) kmanalyze.exe - test-manifest: @rem diff --git a/windows/src/developer/kmcmpdll/Makefile b/windows/src/developer/kmcmpdll/Makefile index 72ba2d861a..dac9020154 100644 --- a/windows/src/developer/kmcmpdll/Makefile +++ b/windows/src/developer/kmcmpdll/Makefile @@ -39,9 +39,6 @@ wrap-symbols: $(SYMSTORE) $(DEBUGPATH)\developer\kmcmpdll.pdb /t keyman-developer $(SYMSTORE) $(DEBUGPATH)\developer\kmcmpdll.x64.pdb /t keyman-developer -backup: - $(WZZIP) $(BUILD)\developer\kmcmpdll.zip $(BACKUPDEFAULTS) $(PROGRAM)\developer\kmcmpdll.dll $(PROGRAM)\developer\kmcmpdll.x64.dll - test-manifest: @rem This target needed as dependency for TIKE and KMCMPDLL diff --git a/windows/src/developer/kmcomp/Makefile b/windows/src/developer/kmcomp/Makefile index 059810af2c..c866336036 100644 --- a/windows/src/developer/kmcomp/Makefile +++ b/windows/src/developer/kmcomp/Makefile @@ -45,9 +45,6 @@ wrap-symbols: $(SYMSTORE) $(DEBUGPATH)\developer\kmcomp.dbg /t keyman-developer #TODO: $(SYMSTORE) $(DEBUGPATH)\developer\kmcomp.x64.dbg /t keyman-developer -backup: - $(WZZIP) $(BUILD)\developer\kmcomp.zip $(BACKUPDEFAULTS) $(PROGRAM)\developer\kmcomp.exe $(PROGRAM)\developer\kmcomp.x64.exe - test-manifest: # test that (a) linked manifest exists and correct $(MT) -nologo -inputresource:$(PROGRAM)\developer\kmcomp.exe -validate_manifest diff --git a/windows/src/developer/kmconvert/Makefile b/windows/src/developer/kmconvert/Makefile index 9264d9f2ae..09eff7e0a8 100644 --- a/windows/src/developer/kmconvert/Makefile +++ b/windows/src/developer/kmconvert/Makefile @@ -30,9 +30,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\developer\kmconvert.exe /t keyman-developer $(SYMSTORE) $(DEBUGPATH)\developer\kmconvert.dbg /t keyman-developer -backup: - $(WZZIP) $(BUILD)\developer\kmconvert.zip $(BACKUPDEFAULTS) kmconvert.exe - test-manifest: # test that (a) linked manifest exists and correct $(MT) -nologo -inputresource:$(PROGRAM)\developer\kmconvert.exe -validate_manifest diff --git a/windows/src/developer/kmdecomp/Makefile b/windows/src/developer/kmdecomp/Makefile index 7bf26de79c..1c221fed1a 100644 --- a/windows/src/developer/kmdecomp/Makefile +++ b/windows/src/developer/kmdecomp/Makefile @@ -20,9 +20,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\developer\kmdecomp.exe /t keyman-developer $(SYMSTORE) $(DEBUGPATH)\developer\kmdecomp.pdb /t keyman-developer -backup: - $(WZZIP) $(BUILD)\developer\kmdecomp.zip $(BACKUPDEFAULTS) $(PROGRAM)\developer\kmdecomp.exe - test-manifest: @rem This target needed as dependency for KMDECOMP diff --git a/windows/src/developer/samples/imsample/Makefile b/windows/src/developer/samples/imsample/Makefile index d480d00d93..cb4b0b6786 100644 --- a/windows/src/developer/samples/imsample/Makefile +++ b/windows/src/developer/samples/imsample/Makefile @@ -18,9 +18,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\developer\imsample.dll /t keyman-developer $(SYMSTORE) $(DEBUGPATH)\developer\imsample.pdb /t keyman-developer -backup: - $(WZZIP) $(BUILD)\developer\imsample.zip $(BACKUPDEFAULTS) imsample.dll - clean: def-clean $(MSBUILD) imsample.sln $(MSBUILD_CLEAN) diff --git a/windows/src/developer/setup/Makefile b/windows/src/developer/setup/Makefile index 29abb289c2..3ec095b249 100644 --- a/windows/src/developer/setup/Makefile +++ b/windows/src/developer/setup/Makefile @@ -32,9 +32,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\developer\setup.exe /t keyman-developer $(SYMSTORE) $(ROOT)\src\developer\setup\setup.dbg /t keyman-developer -backup: - $(WZZIP) $(BUILD)\developer\devsetup.zip $(BACKUPDEFAULTS) setup.exe - test-manifest: # test that (a) linked manifest exists and correct, and (b) has uiAccess=true $(MT) -nologo -inputresource:$(PROGRAM)\developer\setup.exe -validate_manifest diff --git a/windows/src/engine/inst/Makefile b/windows/src/engine/inst/Makefile index 72bf68b4ab..db08b76ead 100644 --- a/windows/src/engine/inst/Makefile +++ b/windows/src/engine/inst/Makefile @@ -18,10 +18,6 @@ signcode: cd $(ROOT)\src\engine\inst\insthelper $(MAKE) signcode -backup: - cd $(ROOT)\src\engine\inst\insthelper - $(MAKE) backup - wrap-symbols: cd $(ROOT)\src\engine\inst\insthelper $(MAKE) symbols diff --git a/windows/src/engine/inst/insthelper/Makefile b/windows/src/engine/inst/insthelper/Makefile index 6af633808e..b71f45a804 100644 --- a/windows/src/engine/inst/insthelper/Makefile +++ b/windows/src/engine/inst/insthelper/Makefile @@ -23,7 +23,4 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\inst\insthelper.dll /t keyman-windows $(SYMSTORE) $(DEBUGPATH)\inst\insthelper.dbg /t keyman-windows -backup: - $(WZZIP) $(BUILD)\inst\insthelper.zip $(BACKUPDEFAULTS) insthelper.dll - !include ..\..\..\Target.mak diff --git a/windows/src/engine/keyman/Makefile b/windows/src/engine/keyman/Makefile index 86af3f9214..dcb68ff542 100644 --- a/windows/src/engine/keyman/Makefile +++ b/windows/src/engine/keyman/Makefile @@ -22,9 +22,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\engine\keyman.dll /t keyman-engine-windows $(SYMSTORE) $(DEBUGPATH)\engine\keyman.dbg /t keyman-engine-windows -backup: - $(WZZIP) $(BUILD)\engine\keyman.zip $(BACKUPDEFAULTS) keyman.exe - keymanmenuitem.res: rc keymanmenuitem.rc diff --git a/windows/src/engine/keyman32/Makefile b/windows/src/engine/keyman32/Makefile index 2caeb17985..9b1372370e 100644 --- a/windows/src/engine/keyman32/Makefile +++ b/windows/src/engine/keyman32/Makefile @@ -20,9 +20,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\engine\keyman32.dll /t keyman-engine-windows $(SYMSTORE) $(DEBUGPATH)\engine\keyman32.pdb /t keyman-engine-windows -backup: - $(WZZIP) $(BUILD)\engine\keyman32.zip $(BACKUPDEFAULTS) keyman32.dll - install: $(COPY) $(PROGRAM)\engine\keyman32.dll "$(INSTALLPATH_KEYMANENGINE)\keyman32.dll" echo You may want to manually tweak keyman-debug-etw.man and fill in $(INSTALLPATH_KEYMANENGINE)\keyman32.dll diff --git a/windows/src/engine/keyman64/Makefile b/windows/src/engine/keyman64/Makefile index e6714f289c..401d72fb6d 100644 --- a/windows/src/engine/keyman64/Makefile +++ b/windows/src/engine/keyman64/Makefile @@ -20,9 +20,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\engine\keyman64.dll /t keyman-engine-windows $(SYMSTORE) $(DEBUGPATH)\engine\keyman64.pdb /t keyman-engine-windows -backup: - $(WZZIP) $(BUILD)\engine\keyman64.zip $(BACKUPDEFAULTS) keyman64.dll - install: $(COPY) $(PROGRAM)\engine\keyman64.dll "$(INSTALLPATH_KEYMANENGINE)\keyman64.dll" diff --git a/windows/src/engine/keymanmc/Makefile b/windows/src/engine/keymanmc/Makefile index 57b35df8ce..d9405f85cb 100644 --- a/windows/src/engine/keymanmc/Makefile +++ b/windows/src/engine/keymanmc/Makefile @@ -23,9 +23,6 @@ signcode: wrap-symbols: rem no symbols -backup: - $(WZZIP) $(BUILD)\engine\keymanmc.zip $(BACKUPDEFAULTS) keymanmc.dll - install: $(COPY) $(PROGRAM)\engine\keymanmc.dll "$(INSTALLPATH_KEYMANENGINE)\keymanmc.dll" reg add HKLM\SYSTEM\CurrentControlSet\services\eventlog\Application\Keyman /v CategoryCount /t REG_DWORD /d 1 /f diff --git a/windows/src/engine/keymanx64/Makefile b/windows/src/engine/keymanx64/Makefile index 420a6d868a..148d040c4b 100644 --- a/windows/src/engine/keymanx64/Makefile +++ b/windows/src/engine/keymanx64/Makefile @@ -26,9 +26,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\engine\keymanx64.exe /t keyman-engine-windows $(SYMSTORE) $(DEBUGPATH)\engine\keymanx64.pdb /t keyman-engine-windows -backup: - $(WZZIP) $(BUILD)\engine\keymanx64.exe $(BACKUPDEFAULTS) keymanx64.exe - install: $(COPY) $(PROGRAM)\engine\keymanx64.exe "$(INSTALLPATH_KEYMANENGINE)" diff --git a/windows/src/engine/kmcomapi/Makefile b/windows/src/engine/kmcomapi/Makefile index d39b830626..b41d3ee724 100644 --- a/windows/src/engine/kmcomapi/Makefile +++ b/windows/src/engine/kmcomapi/Makefile @@ -50,7 +50,4 @@ wrap-symbols: $(SYMSTORE) $(DEBUGPATH)\engine\kmcomapi.dbg /t keyman-engine-windows #TODO: $(SYMSTORE) $(PROGRAM)\engine\kmcomapi.x64.dbg /t keyman-engine-windows -backup: - $(WZZIP) $(BUILD)\engine\kmcomapi.zip $(BACKUPDEFAULTS) $(WIN32_TARGET_PATH)\kmcomapi.dll $(WIN64_TARGET_PATH)\kmcomapi.x64.dll $(WIN32_TARGET_PATH)\kmcomapi.dbg - !include ..\..\Target.mak diff --git a/windows/src/engine/kmrefresh/Makefile b/windows/src/engine/kmrefresh/Makefile index a3b676ae52..d0f23daec9 100644 --- a/windows/src/engine/kmrefresh/Makefile +++ b/windows/src/engine/kmrefresh/Makefile @@ -29,10 +29,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\engine\kmrefresh.x64.exe /t keyman-engine-windows $(SYMSTORE) $(DEBUGPATH)\engine\kmrefresh.x64.pdb /t keyman-engine-windows -backup: - $(WZZIP) $(BUILD)\engine\kmrefresh.x86.exe $(BACKUPDEFAULTS) kmrefresh.x86.exe - $(WZZIP) $(BUILD)\engine\kmrefresh.x64.exe $(BACKUPDEFAULTS) kmrefresh.x64.exe - install: $(COPY) $(PROGRAM)\engine\kmrefresh.x86.exe "$(INSTALLPATH_KEYMANENGINE)" $(COPY) $(PROGRAM)\engine\kmrefresh.x64.exe "$(INSTALLPATH_KEYMANENGINE)" diff --git a/windows/src/engine/kmtip/Makefile b/windows/src/engine/kmtip/Makefile index 1e18252772..2b3ad8467a 100644 --- a/windows/src/engine/kmtip/Makefile +++ b/windows/src/engine/kmtip/Makefile @@ -26,10 +26,6 @@ wrap-symbols: $(SYMSTORE) $(DEBUGPATH)\engine\kmtip.pdb /t keyman-engine-windows $(SYMSTORE) $(DEBUGPATH)\engine\kmtip64.pdb /t keyman-engine-windows -backup: - $(WZZIP) $(BUILD)\engine\kmtip.zip x86\kmtip.pdb $(BACKUPDEFAULTS) x86\kmtip.dll - $(WZZIP) $(BUILD)\engine\kmtip64.zip x64\kmtip64.pdb $(BACKUPDEFAULTS) x64\kmtip64.dll - install: $(COPY) $(PROGRAM)\engine\kmtip.dll "$(INSTALLPATH_KEYMANENGINE)\kmtip.dll" $(COPY) $(PROGRAM)\engine\kmtip64.dll "$(INSTALLPATH_KEYMANENGINE)\kmtip64.dll" diff --git a/windows/src/engine/mcompile/Makefile b/windows/src/engine/mcompile/Makefile index 0f671c7268..447499576e 100644 --- a/windows/src/engine/mcompile/Makefile +++ b/windows/src/engine/mcompile/Makefile @@ -22,9 +22,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\engine\mcompile.exe /t keyman-engine-windows $(SYMSTORE) $(DEBUGPATH)\engine\mcompile.pdb /t keyman-engine-windows -backup: - $(WZZIP) $(BUILD)\engine\mcompile.exe $(BACKUPDEFAULTS) mcompile.exe - install: $(COPY) $(PROGRAM)\engine\mcompile.exe "$(INSTALLPATH_KEYMANENGINE)" diff --git a/windows/src/engine/tsysinfo/Makefile b/windows/src/engine/tsysinfo/Makefile index a7f4de01b5..49593cd211 100644 --- a/windows/src/engine/tsysinfo/Makefile +++ b/windows/src/engine/tsysinfo/Makefile @@ -32,9 +32,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\engine\tsysinfo.exe /t keyman-engine-windows $(SYMSTORE) $(DEBUGPATH)\engine\tsysinfo.dbg /t keyman-engine-windows -backup: - $(WZZIP) $(BUILD)\engine\tsysinfo.zip $(BACKUPDEFAULTS) tsysinfo.exe - install: $(COPY) $(PROGRAM)\engine\tsysinfo.exe "$(INSTALLPATH_KEYMANENGINE)" $(COPY) $(ROOT)\src\engine\tsysinfo\*.xsl "$(INSTALLPATH_KEYMANENGINE)" diff --git a/windows/src/engine/tsysinfox64/Makefile b/windows/src/engine/tsysinfox64/Makefile index 9ec8e58049..d5161d0ca1 100644 --- a/windows/src/engine/tsysinfox64/Makefile +++ b/windows/src/engine/tsysinfox64/Makefile @@ -18,9 +18,6 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\engine\tsysinfox64.exe /t keyman-engine-windows #$(SYMSTORE) $(DEBUGPATH)\engine\tsysinfox64.dbg /t keyman-engine-windows -backup: - $(WZZIP) $(BUILD)\engine\tsysinfox64.zip $(BACKUPDEFAULTS) $(WIN64_TARGET_PATH)\*.map $(WIN64_TARGET_PATH)\tsysinfox64.exe - install: echo Nothing to do. diff --git a/windows/src/global/Makefile b/windows/src/global/Makefile index 842f8a178d..f783e31de1 100644 --- a/windows/src/global/Makefile +++ b/windows/src/global/Makefile @@ -36,9 +36,6 @@ clean: signcode: @rem no signcode -backup: - @rem - test-manifest: @rem diff --git a/windows/src/support/charident/Makefile b/windows/src/support/charident/Makefile index d3c91a1ca6..dd6721c5c0 100644 --- a/windows/src/support/charident/Makefile +++ b/windows/src/support/charident/Makefile @@ -17,7 +17,4 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\support\charident.exe /t keyman-windows #TODO: $(SYMSTORE) $(DEBUGPATH)\support\charident.dbg /t keyman-windows -backup: - $(WZZIP) $(BUILD)\support\charident.zip $(BACKUPDEFAULTS) charident.exe - !include ..\..\Target.mak diff --git a/windows/src/support/etl2log/Makefile b/windows/src/support/etl2log/Makefile index 0e9ae69ffd..48bdb1a592 100644 --- a/windows/src/support/etl2log/Makefile +++ b/windows/src/support/etl2log/Makefile @@ -19,7 +19,4 @@ wrap-symbols: $(SYMSTORE) $(PROGRAM)\support\etl2log.exe /t keyman-windows $(SYMSTORE) $(DEBUGPATH)\support\etl2log.pdb /t keyman-windows -backup: - $(WZZIP) $(BUILD)\support\etl2log.zip $(BACKUPDEFAULTS) etl2log.exe - !include ..\..\Target.mak diff --git a/windows/src/support/km_yim/Makefile b/windows/src/support/km_yim/Makefile index 661c17e12a..f0f93e6fe2 100644 --- a/windows/src/support/km_yim/Makefile +++ b/windows/src/support/km_yim/Makefile @@ -19,7 +19,5 @@ signcode: wrap-symbols: rem -backup: - $(WZZIP) $(BUILD)\desktop\km_yim.zip $(BACKUPDEFAULTS) km_yim.exe !include ..\..\Target.mak diff --git a/windows/src/support/kmkb0045/Makefile b/windows/src/support/kmkb0045/Makefile index b521bfd026..3e914c8acd 100644 --- a/windows/src/support/kmkb0045/Makefile +++ b/windows/src/support/kmkb0045/Makefile @@ -15,8 +15,5 @@ clean: def-clean signcode: $(SIGNCODE) /d "Tavultesoft KMKB0045" $(PROGRAM)\support\kmkb0045.exe -backup: - $(WZZIP) $(BUILD)\support\kmkb0045.zip $(BACKUPDEFAULTS) kmkb0045.exe - !include ..\..\Target.mak diff --git a/windows/src/support/oskbulkrenderer/Makefile b/windows/src/support/oskbulkrenderer/Makefile index c008301503..12230ea01a 100644 --- a/windows/src/support/oskbulkrenderer/Makefile +++ b/windows/src/support/oskbulkrenderer/Makefile @@ -18,9 +18,6 @@ clean: def-clean signcode: rem Not signing this utility -backup: - rem No source code backup needed - wrap-symbols: @rem diff --git a/windows/src/test/manual-tests/test_httpuploader/Makefile b/windows/src/test/manual-tests/test_httpuploader/Makefile index 31bcb34c66..b6250f4bfd 100644 --- a/windows/src/test/manual-tests/test_httpuploader/Makefile +++ b/windows/src/test/manual-tests/test_httpuploader/Makefile @@ -15,7 +15,4 @@ clean: def-clean signcode: rem no signcode -backup: - rem no backup - !include ..\..\Target.mak From ec40487d99e2fd1ae7fe17f25d521f0caec352e3 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Wed, 7 Jul 2021 08:49:39 +0700 Subject: [PATCH 48/58] fix(android/engine): Fix namespacing for setActiveKeyboard() --- android/KMEA/app/src/main/assets/keyboard.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/KMEA/app/src/main/assets/keyboard.html b/android/KMEA/app/src/main/assets/keyboard.html index a6a4acb0e1..7cf2d95442 100644 --- a/android/KMEA/app/src/main/assets/keyboard.html +++ b/android/KMEA/app/src/main/assets/keyboard.html @@ -130,7 +130,8 @@ function setKeymanLanguage(k) { KeymanWeb.registerStub(k); - keyman.setActiveKeyboard(k.KP + '::'+k.KI, k.KLC); + // KMW will handle the k.KP:: namespacing + keyman.setActiveKeyboard(k.KI, k.KLC); keyman.osk.show(true); } From bd9773a715159ca0ab40faa62620d0a4ab3abc76 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Wed, 7 Jul 2021 09:35:28 +0700 Subject: [PATCH 49/58] fix(android/engine): register lexical model in switchToNextKeyboard --- .../app/src/main/java/com/tavultesoft/kmea/KMManager.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index dd44f9edfe..9b32aa91f5 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -152,7 +152,7 @@ public final class KMManager { private static boolean didLogHardwareKeystrokeException = false; - private static GlobeKeyAction inappKbGlobeKeyAction = GlobeKeyAction.GLOBE_KEY_ACTION_SHOW_MENU; + private static GlobeKeyAction inappKbGlobeKeyAction = GlobeKeyAction.GLOBE_KEY_ACTION_SWITCH_TO_NEXT_KEYBOARD; // For testing only. Do not merge to production private static GlobeKeyAction sysKbGlobeKeyAction = GlobeKeyAction.GLOBE_KEY_ACTION_SHOW_MENU; // This is used to keep track of the starting system keyboard index while the screen is locked private static int sysKbStartingIndexOnLockScreen = -1; @@ -1365,6 +1365,9 @@ public final class KMManager { public static void switchToNextKeyboard(Context context) { int index = KeyboardController.getInstance().getKeyboardIndex(KMKeyboard.currentKeyboard()); index++; + if (index >= KeyboardController.getInstance().get().size()) { + index = 0; + } Keyboard kbInfo = KeyboardController.getInstance().getKeyboardInfo(index); if (kbInfo == null) { index = 0; @@ -1378,6 +1381,8 @@ public final class KMManager { if (SystemKeyboard != null) { SystemKeyboard.setKeyboard(kbInfo); } + + registerAssociatedLexicalModel(kbInfo.getLanguageID()); } protected static IBinder getToken() { From 196e3aacbc07aa655471626f3f1a24d7cfa6f618 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Wed, 7 Jul 2021 10:19:51 +0700 Subject: [PATCH 50/58] chore(android/engine): Add comment --- android/KMEA/app/src/main/assets/keyboard.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/KMEA/app/src/main/assets/keyboard.html b/android/KMEA/app/src/main/assets/keyboard.html index 7cf2d95442..a61321bd9d 100644 --- a/android/KMEA/app/src/main/assets/keyboard.html +++ b/android/KMEA/app/src/main/assets/keyboard.html @@ -130,7 +130,7 @@ function setKeymanLanguage(k) { KeymanWeb.registerStub(k); - // KMW will handle the k.KP:: namespacing + // registerStub will mutate k.KI to have k.KP:: namespacing that setActiveKeyboard expects keyman.setActiveKeyboard(k.KI, k.KLC); keyman.osk.show(true); } From a54fecebcc9b72176c24b06ea818371eaa2807c8 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 7 Jul 2021 12:05:44 +0700 Subject: [PATCH 51/58] fix(common/models/types): fixes test script config --- common/models/types/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/models/types/package.json b/common/models/types/package.json index ea116b48a5..353bcbd107 100644 --- a/common/models/types/package.json +++ b/common/models/types/package.json @@ -4,7 +4,7 @@ "description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.", "types": "./index.d.ts", "scripts": { - "test": "tsc test.ts" + "test": "tsc test.ts -lib es6" }, "repository": { "type": "git", From 69a2e9ec274ea7f2d88ebe18c4f0b8e55c31eca4 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Wed, 7 Jul 2021 15:25:15 +0200 Subject: [PATCH 52/58] refactor(common/core/desktop): Fix file and class names - rename source files for `kmx_processor` -> `kmx_processor.*` - rename class `KMX_Processor` -> `KMX_ProcessEvent` with source files `kmx_processevent.*` Closes #5436. --- .../core/desktop/src/km_kbp_keyboard_api.cpp | 2 +- common/core/desktop/src/kmx/kmx_actions.cpp | 2 +- common/core/desktop/src/kmx/kmx_capslock.cpp | 8 +- common/core/desktop/src/kmx/kmx_consts.cpp | 2 +- common/core/desktop/src/kmx/kmx_context.cpp | 2 +- common/core/desktop/src/kmx/kmx_debug.cpp | 2 +- .../core/desktop/src/kmx/kmx_environment.cpp | 2 +- common/core/desktop/src/kmx/kmx_file.cpp | 16 +- common/core/desktop/src/kmx/kmx_modifiers.cpp | 4 +- common/core/desktop/src/kmx/kmx_options.cpp | 2 +- .../core/desktop/src/kmx/kmx_processevent.cpp | 813 +++++++++++++---- .../{kmx_processor.h => kmx_processevent.h} | 6 +- common/core/desktop/src/kmx/kmx_processor.cpp | 817 ++++-------------- ...kmx_processevent.hpp => kmx_processor.hpp} | 4 +- common/core/desktop/src/kmx/kmx_xstring.cpp | 2 +- common/core/desktop/tests/unit/kmx/kmx.cpp | 2 +- 16 files changed, 843 insertions(+), 843 deletions(-) rename common/core/desktop/src/kmx/{kmx_processor.h => kmx_processevent.h} (98%) rename common/core/desktop/src/kmx/{kmx_processevent.hpp => kmx_processor.hpp} (94%) diff --git a/common/core/desktop/src/km_kbp_keyboard_api.cpp b/common/core/desktop/src/km_kbp_keyboard_api.cpp index 8a2ef7b3ad..04c8eab956 100644 --- a/common/core/desktop/src/km_kbp_keyboard_api.cpp +++ b/common/core/desktop/src/km_kbp_keyboard_api.cpp @@ -13,7 +13,7 @@ #include #include "keyboard.hpp" #include "processor.hpp" -#include "kmx/kmx_processevent.hpp" +#include "kmx/kmx_processor.hpp" #include "mock/mock_processor.hpp" #include "rust/rust_mock_processor.hpp" diff --git a/common/core/desktop/src/kmx/kmx_actions.cpp b/common/core/desktop/src/kmx/kmx_actions.cpp index 03c70eb134..7eea892b63 100644 --- a/common/core/desktop/src/kmx/kmx_actions.cpp +++ b/common/core/desktop/src/kmx/kmx_actions.cpp @@ -2,7 +2,7 @@ Copyright: Copyright (C) 2003-2018 SIL International. Authors: mcdurdin */ -#include +#include using namespace km::kbp; using namespace kmx; diff --git a/common/core/desktop/src/kmx/kmx_capslock.cpp b/common/core/desktop/src/kmx/kmx_capslock.cpp index 999fa08f64..63856be18d 100644 --- a/common/core/desktop/src/kmx/kmx_capslock.cpp +++ b/common/core/desktop/src/kmx/kmx_capslock.cpp @@ -2,12 +2,12 @@ Copyright: Copyright (C) 2003-2018 SIL International. Authors: mcdurdin */ -#include +#include using namespace km::kbp; using namespace kmx; -void KMX_Processor::ResetCapsLock(void) +void KMX_ProcessEvent::ResetCapsLock(void) { DebugLog("ResetCapsLock: enter"); @@ -24,7 +24,7 @@ void KMX_Processor::ResetCapsLock(void) } -void KMX_Processor::KeyCapsLockPress(KMX_BOOL FIsUp) +void KMX_ProcessEvent::KeyCapsLockPress(KMX_BOOL FIsUp) { if(m_keyboard.Keyboard->dwFlags & KF_CAPSONONLY) { @@ -43,7 +43,7 @@ void KMX_Processor::KeyCapsLockPress(KMX_BOOL FIsUp) } -void KMX_Processor::KeyShiftPress(KMX_BOOL FIsUp) +void KMX_ProcessEvent::KeyShiftPress(KMX_BOOL FIsUp) { if(!m_environment.capsLock()) return; diff --git a/common/core/desktop/src/kmx/kmx_consts.cpp b/common/core/desktop/src/kmx/kmx_consts.cpp index 4e549df995..199e3ff45a 100644 --- a/common/core/desktop/src/kmx/kmx_consts.cpp +++ b/common/core/desktop/src/kmx/kmx_consts.cpp @@ -1,5 +1,5 @@ -#include "kmx_processor.h" +#include "kmx_processevent.h" namespace km { namespace kbp { diff --git a/common/core/desktop/src/kmx/kmx_context.cpp b/common/core/desktop/src/kmx/kmx_context.cpp index 6efa11f94f..79db902d7d 100644 --- a/common/core/desktop/src/kmx/kmx_context.cpp +++ b/common/core/desktop/src/kmx/kmx_context.cpp @@ -2,7 +2,7 @@ Copyright: Copyright (C) 2003-2018 SIL International. Authors: mcdurdin */ -#include +#include using namespace km::kbp; using namespace kmx; diff --git a/common/core/desktop/src/kmx/kmx_debug.cpp b/common/core/desktop/src/kmx/kmx_debug.cpp index a58fd8e17e..a820c6bc45 100644 --- a/common/core/desktop/src/kmx/kmx_debug.cpp +++ b/common/core/desktop/src/kmx/kmx_debug.cpp @@ -2,7 +2,7 @@ Copyright: Copyright (C) 2003-2018 SIL International. Authors: mcdurdin */ -#include "kmx_processor.h" +#include "kmx_processevent.h" #include #include diff --git a/common/core/desktop/src/kmx/kmx_environment.cpp b/common/core/desktop/src/kmx/kmx_environment.cpp index 4fac39182f..e2f7359469 100644 --- a/common/core/desktop/src/kmx/kmx_environment.cpp +++ b/common/core/desktop/src/kmx/kmx_environment.cpp @@ -2,7 +2,7 @@ Copyright: Copyright (C) 2003-2018 SIL International. Authors: mcdurdin */ -#include +#include #include #include #include diff --git a/common/core/desktop/src/kmx/kmx_file.cpp b/common/core/desktop/src/kmx/kmx_file.cpp index 2ce631a01c..1a6bdf64f3 100644 --- a/common/core/desktop/src/kmx/kmx_file.cpp +++ b/common/core/desktop/src/kmx/kmx_file.cpp @@ -2,7 +2,7 @@ Copyright: Copyright (C) 2003-2018 SIL International. Authors: mcdurdin */ -#include "kmx_processor.h" +#include "kmx_processevent.h" using namespace km::kbp; using namespace kmx; @@ -11,7 +11,7 @@ using namespace kmx; #include #endif -KMX_BOOL KMX_Processor::Load(km_kbp_path_name KeyboardName) +KMX_BOOL KMX_ProcessEvent::Load(km_kbp_path_name KeyboardName) { if(!LoadKeyboard(KeyboardName, &m_keyboard.Keyboard)) return FALSE; // I5136 @@ -75,7 +75,7 @@ unsigned long CalculateBufferCRC(unsigned long count, KMX_BYTE *p) return crc; } -KMX_BOOL KMX_Processor::LoadKeyboard(km_kbp_path_name fileName, LPKEYBOARD *lpKeyboard) +KMX_BOOL KMX_ProcessEvent::LoadKeyboard(km_kbp_path_name fileName, LPKEYBOARD *lpKeyboard) { PKMX_BYTE buf; FILE *fp; @@ -174,7 +174,7 @@ KMX_BOOL KMX_Processor::LoadKeyboard(km_kbp_path_name fileName, LPKEYBOARD *lpKe return TRUE; } -PKMX_WCHAR KMX_Processor::StringOffset(PKMX_BYTE base, KMX_DWORD offset) +PKMX_WCHAR KMX_ProcessEvent::StringOffset(PKMX_BYTE base, KMX_DWORD offset) { if(offset == 0) return NULL; return (PKMX_WCHAR)(base + offset); @@ -188,7 +188,7 @@ PKMX_WCHAR KMX_Processor::StringOffset(PKMX_BYTE base, KMX_DWORD offset) we don't copy the strings This method is used on 64-bit architectures. */ -LPKEYBOARD KMX_Processor::CopyKeyboard(PKMX_BYTE bufp, PKMX_BYTE base) +LPKEYBOARD KMX_ProcessEvent::CopyKeyboard(PKMX_BYTE bufp, PKMX_BYTE base) { PCOMP_KEYBOARD ckbp = (PCOMP_KEYBOARD) base; @@ -271,7 +271,7 @@ LPKEYBOARD KMX_Processor::CopyKeyboard(PKMX_BYTE bufp, PKMX_BYTE base) Fixup the keyboard by expanding pointers. On disk the pointers are stored relative to the beginning of the file, but we need real pointers. This method is used on 32-bit architectures. */ -LPKEYBOARD KMX_Processor::FixupKeyboard(PKMX_BYTE bufp, PKMX_BYTE base) +LPKEYBOARD KMX_ProcessEvent::FixupKeyboard(PKMX_BYTE bufp, PKMX_BYTE base) { KMX_DWORD i, j; PCOMP_KEYBOARD ckbp = (PCOMP_KEYBOARD) base; @@ -311,7 +311,7 @@ LPKEYBOARD KMX_Processor::FixupKeyboard(PKMX_BYTE bufp, PKMX_BYTE base) #endif -KMX_BOOL KMX_Processor::VerifyChecksum(PKMX_BYTE buf, size_t sz) +KMX_BOOL KMX_ProcessEvent::VerifyChecksum(PKMX_BYTE buf, size_t sz) { KMX_DWORD tempcs; PCOMP_KEYBOARD ckbp; @@ -324,7 +324,7 @@ KMX_BOOL KMX_Processor::VerifyChecksum(PKMX_BYTE buf, size_t sz) return tempcs == CalculateBufferCRC(sz, buf); } -KMX_BOOL KMX_Processor::VerifyKeyboard(PKMX_BYTE filebase, size_t sz) +KMX_BOOL KMX_ProcessEvent::VerifyKeyboard(PKMX_BYTE filebase, size_t sz) { KMX_DWORD i; PCOMP_KEYBOARD ckbp = (PCOMP_KEYBOARD) filebase; diff --git a/common/core/desktop/src/kmx/kmx_modifiers.cpp b/common/core/desktop/src/kmx/kmx_modifiers.cpp index 1da7a8a3c9..0de3ce6ed8 100644 --- a/common/core/desktop/src/kmx/kmx_modifiers.cpp +++ b/common/core/desktop/src/kmx/kmx_modifiers.cpp @@ -2,7 +2,7 @@ Copyright: Copyright (C) 2003-2018 SIL International. Authors: mcdurdin */ -#include "kmx_processor.h" +#include "kmx_processevent.h" using namespace km::kbp; using namespace kmx; @@ -78,7 +78,7 @@ static KMX_BYTE states[MAX_RSHIFT][MAX_KSHIFT] = { * K_CTRLFLAG and K_ALTFLAG as well as specific keys correctly. */ -KMX_BOOL KMX_Processor::IsEquivalentShift(KMX_UINT rshift, KMX_UINT kshift) { +KMX_BOOL KMX_ProcessEvent::IsEquivalentShift(KMX_UINT rshift, KMX_UINT kshift) { // // The rule shift must have ISVIRTUALKEY bit set for virt.keys // diff --git a/common/core/desktop/src/kmx/kmx_options.cpp b/common/core/desktop/src/kmx/kmx_options.cpp index 51538d2157..a4417cb755 100644 --- a/common/core/desktop/src/kmx/kmx_options.cpp +++ b/common/core/desktop/src/kmx/kmx_options.cpp @@ -3,7 +3,7 @@ Authors: mcdurdin */ #include "processor.hpp" -#include "kmx_processor.h" +#include "kmx_processevent.h" #include #include diff --git a/common/core/desktop/src/kmx/kmx_processevent.cpp b/common/core/desktop/src/kmx/kmx_processevent.cpp index 58bc29e33e..893ff1d9ba 100644 --- a/common/core/desktop/src/kmx/kmx_processevent.cpp +++ b/common/core/desktop/src/kmx/kmx_processevent.cpp @@ -1,189 +1,682 @@ -#include +/* + Copyright: Copyright (C) 2003-2018 SIL International. + Authors: mcdurdin +*/ +#include "kmx_processevent.h" #include "state.hpp" -#include "kmx/kmx_processevent.hpp" -namespace km { - namespace kbp +using namespace km::kbp; +using namespace kmx; + +/* Globals */ + +KMX_BOOL km::kbp::kmx::g_debug_ToConsole = FALSE; +KMX_BOOL km::kbp::kmx::g_debug_KeymanLog = TRUE; +KMX_BOOL km::kbp::kmx::g_silent = FALSE; + +/* +* KMX_ProcessEvent +*/ + +KMX_ProcessEvent::KMX_ProcessEvent() : m_actions(&m_context), m_options(&m_keyboard) { + m_indexStack = new KMX_WORD[GLOBAL_ContextStackSize]; + m_miniContext = new KMX_WCHAR[GLOBAL_ContextStackSize]; + m_miniContextIfLen = 0; +} + +KMX_ProcessEvent::~KMX_ProcessEvent() { + delete[] m_indexStack; + delete[] m_miniContext; +} + +char VKeyToChar(KMX_UINT modifiers, KMX_UINT vk) { + // We only map SHIFT and UNSHIFTED, and CAPS LOCK + + if ((modifiers & ~(K_SHIFTFLAG | CAPITALFLAG)) != 0) { + return 0; + } + + bool + shifted = (modifiers & K_SHIFTFLAG) == K_SHIFTFLAG, + caps = (modifiers & CAPITALFLAG) == CAPITALFLAG; + + if (vk == KM_KBP_VKEY_SPACE) { + // Override for space because it is the same for + // shifted and unshifted. + return 32; + } + + for (int i = 0; s_char_to_vkey[i].vk; i++) { + if (s_char_to_vkey[i].caps && caps) { + if (s_char_to_vkey[i].vk == vk && s_char_to_vkey[i].shifted == !shifted) return i + 32; + } + else { + if (s_char_to_vkey[i].vk == vk && s_char_to_vkey[i].shifted == shifted) return i + 32; + } + } + return 0; +} + +/* +* KMX_BOOL ProcessEvent(); +* +* Parameters: none +* +* Returns: TRUE if keystroke should be eaten +* +* Called by: FilterFunc +* +* ProcessEvent organizes the messages and gives them to the appropriate routines to +* process, and checks the state of Windows for the keyboard handling. +*/ + +KMX_BOOL KMX_ProcessEvent::ProcessEvent(km_kbp_state *state, KMX_UINT vkey, KMX_DWORD modifiers) +{ + LPKEYBOARD kbd = m_keyboard.Keyboard; + + m_kbp_state = state; + + if (m_environment.capsLock()) + modifiers |= CAPITALFLAG; + + m_state.vkey = vkey; + m_state.charCode = VKeyToChar(modifiers, vkey); + m_modifiers = modifiers; + m_state.LoopTimes = 0; + + if (kbd->StartGroup[BEGIN_UNICODE] == (KMX_DWORD) -1) { + DebugLog("Non-Unicode keyboards are not supported."); + return FALSE; + } + + LPGROUP gp = &kbd->dpGroupArray[kbd->StartGroup[BEGIN_UNICODE]]; + + KMX_BOOL fOutputKeystroke = FALSE; + + ProcessGroup(gp, &fOutputKeystroke); + + m_kbp_state = nullptr; + + return !fOutputKeystroke; +} + +/* +* PRIVATE KMX_BOOL ProcessGroup(LPGROUP gp); +* +* Parameters: gp Pointer to group to process inside +* +* Returns: TRUE if messages are to be sent, +* and FALSE if no messages are to be sent. +* +* Called by: ProcessEvent, recursive inside groups +* +* ProcessKey is where the keystroke conversion and output takes place. This routine +* has a lot of crucial code in it! +*/ + +KMX_BOOL KMX_ProcessEvent::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke) +{ + KMX_DWORD i; + LPKEY kkp = NULL; + PKMX_WCHAR p; + int sdmfI; + + /* + If the number of nested groups goes higher than 50, then break out - this is + a limitation of stack size. This is basically a catch-all for freaky apps that + cause message loopbacks and nasty things like that. Okay, it's really a catch all + for bugs! This means the user's system shouldn't hang. + */ + + LPKEYBOARD kbd = m_keyboard.Keyboard; + + sdmfI = -1; + + for(i = 0; i < kbd->cxGroupArray; i++) + if(gp == &kbd->dpGroupArray[i]) + { + sdmfI = i; + break; + } + + if(++m_state.LoopTimes > 50) { + DebugLog("Aborting output: m_state.LoopTimes exceeded."); + m_state.StopOutput = TRUE; + return FALSE; + } - km_kbp_status kmx_processor::validate() const { - return _valid ? KM_KBP_STATUS_OK : KM_KBP_STATUS_INVALID_KEYBOARD; - } + /* + The rule matching loop. - kmx_processor::kmx_processor(kbp::path p) + This loop iterates through all the rules in the group that is currently being + processed. Each rule in a group can be of three different types: + 1. A virtual key rule, where the key to be matched is a virtual key + 2. A normal key rule (WM_CHAR), where the key to be matched is an Ascii char. + 3. A rule in a keyless group, where only the context is matched. + + The loop goes through and checks the rules like that. This loop could be optimized + with standard searching techniques - the ContextMatch may be difficult. + */ + + DebugLog("m_state.vkey: %s shiftFlags: %x; charCode: %X", Debug_VirtualKey(m_state.vkey), m_modifiers, m_state.charCode); // I4582 + DebugLog("m_context: %s", Debug_UnicodeString(m_context.GetFullContext())); + + if(gp) + { + for(kkp = gp->dpKeyArray, i=0; i < gp->cxKeyArray; i++, kkp++) { - p.replace_extension(".kmx"); - _valid = bool(_kmx.Load(p.c_str())); - - if (!_valid) - return; - - keyboard_attributes::options_store defaults; - _kmx.GetOptions()->Init(defaults); - - for (auto const & opt: defaults) + if(!ContextMatch(kkp)) continue; + if(!gp->fUsingKeys) { - if (!opt.empty() && opt.scope == KM_KBP_OPT_KEYBOARD ) - persisted_store()[opt.key] = opt.value; + if(kkp->dpContext[0] != 0) break; else continue; } - // Fill out attributes - auto v = _kmx.GetKeyboard()->Keyboard->version; - auto vs = std::to_string(v >> 16) + "." + std::to_string(v & 0xffff); - _attributes = keyboard_attributes(static_cast(p.stem()), - std::u16string(vs.begin(), vs.end()), p.parent(), defaults); - } + //if(kkp->Key == m_state.vkey) + //SendDebugMessageFormat(m_state.msg.hwnd, sdmKeyboard, 0, "kkp->Key: %d kkp->ShiftFlags: %x", + // kkp->Key, kkp->ShiftFlags); - - char16_t const * kmx_processor::lookup_option(km_kbp_option_scope scope, std::u16string const & key) const - { - char16_t const * pValue = nullptr; - switch(scope) + /* Keyman 6.0: support Virtual Characters */ + if(IsEquivalentShift(kkp->ShiftFlags, m_modifiers)) { - case KM_KBP_OPT_KEYBOARD: - pValue = _kmx.GetOptions()->LookUp(key); - break; - case KM_KBP_OPT_ENVIRONMENT: - pValue = _kmx.GetEnvironment()->LookUp(key); - break; - default: - break; + if(kkp->Key > VK__MAX && kkp->Key == m_state.vkey) break; // I3438 // I4582 + else if(kkp->Key == m_state.vkey) break; // I4169 } - - return pValue ? pValue : nullptr; + else if(kkp->ShiftFlags == 0 && kkp->Key == m_state.charCode && m_state.charCode != 0) break; } + } - option kmx_processor::update_option(km_kbp_option_scope scope, std::u16string const & key, std::u16string const & value) + if(!gp || i == gp->cxKeyArray) + { + /* + No rule was found that corresponded to the current state of the context and + keyboard. NoMatch should be checked for everything except virtual keys; and + context should also be kept. + + If the message was a virtual key, then just return without checking NoMatch. + NoMatch shouldn't be used for virtual keys because it will mean that no key + can ever get through that isn't matched - including arrows, func. keys, etc !! + Context is not kept for virtual keys being output. + */ + + DebugLog("No match was found in group %d of %d", sdmfI, m_keyboard.Keyboard->cxGroupArray); + + if(!gp || (m_state.charCode == 0 && gp->fUsingKeys)) // I4585 + // 7.0.241.0: I1133 - Fix mismatched parentheses on m_state.charCode - ie. we don't want to output this letter if !gp->fUsingKeys { - switch(scope) { - case KM_KBP_OPT_KEYBOARD: - _kmx.GetOptions()->Set(key, value); - persisted_store()[key] = value; - break; - case KM_KBP_OPT_ENVIRONMENT: - _kmx.GetEnvironment()->Load(key, value); - break; - default: - return option(); - break; - } + KMX_BOOL fIsBackspace = m_state.vkey == KM_KBP_VKEY_BKSP && (m_modifiers & (LCTRLFLAG|RCTRLFLAG|LALTFLAG|RALTFLAG)) == 0; // I4128 - return option(scope, key, value); + if(fIsBackspace) { // I4838 // I4933 + PKMX_WCHAR pdeletecontext = m_context.Buf(1); // I4933 + if(!pdeletecontext || *pdeletecontext == 0) { // I4933 + m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0); + *pOutputKeystroke = TRUE; // I4933 + return FALSE; // I4933 + } + m_actions.QueueAction(QIT_BACK, BK_BACKSPACE); // I4933 + } else { // I4024 // I4128 // I4287 // I4290 + DebugLog(" ... IsLegacy = FALSE; IsTIP = TRUE"); // I4128 + m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0); + *pOutputKeystroke = TRUE; + return FALSE; + } + } + else if (gp->dpNoMatch != NULL && *gp->dpNoMatch != 0) + { + /* NoMatch rule found, and is a character key */ + PostString(gp->dpNoMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke); + } + else if (m_state.charCode != 0 && m_state.charCode != 0xFFFF && gp->fUsingKeys) + { + /* No rule found, is a character key */ + m_actions.QueueAction(QIT_CHAR, m_state.charCode); } - km_kbp_status kmx_processor::process_event(km_kbp_state *state, km_kbp_virtual_key vk, uint16_t modifier_state) { - // Construct a context buffer from the items + return TRUE; + } - std::u16string ctxt; - auto cp = state->context(); - for (auto c = cp.begin(); c != cp.end(); c++) { - switch (c->type) { - case KM_KBP_CT_CHAR: - if (Uni_IsSMP(c->character)) { - ctxt += Uni_UTF32ToSurrogate1(c->character); - ctxt += Uni_UTF32ToSurrogate2(c->character); - } - else { - ctxt += (km_kbp_cp) c->character; - } - break; - case KM_KBP_CT_MARKER: - assert(c->marker > 0); - ctxt += UC_SENTINEL; - ctxt += CODE_DEADKEY; - ctxt += c->marker; - break; + DebugLog("match found in rule %d", i); + + /* + Save the context that will be used for output when the 'context' keyword is used. + For each deadkey, we need to add 2 characters; look in related stores as well... + */ + + assert(kkp != NULL); + + m_miniContextIfLen = xstrlen(kkp->dpContext) - xstrlen_ignoreifopt(kkp->dpContext); + + // 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support + if(*kkp->dpContext == UC_SENTINEL && *(kkp->dpContext+1) == CODE_NUL) + u16cpy(m_miniContext, /*GLOBAL_ContextStackSize,*/ m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)-1) /*, GLOBAL_ContextStackSize*/); // I3162 // I3536 + else + u16cpy(m_miniContext, /*GLOBAL_ContextStackSize,*/ m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)) /*, GLOBAL_ContextStackSize*/); // I3162 // I3536 + + m_miniContext[GLOBAL_ContextStackSize-1] = 0; + + /* + The next section includes several optimizations that make the code a little harder + to read, but are probably worth it in the time that they save. + + If the output string doesn't have a "context" byte at the start, post backspaces + to erase the appropriate number of characters in the application. If it does have + a "context" byte at the start, then the string won't change, and no backspaces are + necessary. You could go one step further with this optimization, in PostAllKeys, + by comparing the starts of the strings to see what is same, and not backspacing + that, but it is probably not necessary. + */ + + p = kkp->dpOutput; + if(*p != UC_SENTINEL || *(p+1) != CODE_CONTEXT) + { + for(p = decxstr((KMX_WCHAR *) u16chr(m_miniContext, 0), m_miniContext); p != NULL; p = decxstr(p, m_miniContext)) + { + if(*p == UC_SENTINEL) + switch(*(p+1)) + { + case CODE_DEADKEY: m_actions.QueueAction(QIT_BACK, BK_DEADKEY); break; + case CODE_NUL: break; // 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support + } + else + { + m_actions.QueueAction(QIT_BACK, 0); + } + } + p = kkp->dpOutput; + } + else + p = incxstr(p); // otherwise, the "context" entry has to be jumped over + + /* Use PostString to post the rest of the output string. */ + + if(PostString(p, m_keyboard.Keyboard, NULL, pOutputKeystroke) == psrCheckMatches) + { + if(gp->dpMatch && *gp->dpMatch) + { + PostString(gp->dpMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke); + } + } + + return TRUE; +} + +/* +* int PostString( PKMX_CHAR str, KMX_BOOL *useMode, LPMSG mp, +* LPKEYBOARD lpkb ); +* +* Parameters: str Pointer to string to send +* useMode Pointer to KMX_BOOL about whether a "use" command was found +* mp Pointer to MSG structure to copy in outputting messages +* lpkb Pointer to global keyboard structure +* +* Returns: 0 to continue, 1 and 2 to return. +* +* Called by: ProcessKey +* +* PostString posts a string of "context", "index", "beep", characters and virtual keys +* to the active application, via the Keyman PostKey buffer. +*/ + +int KMX_ProcessEvent::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR endstr, KMX_BOOL *pOutputKeystroke) +{ + PKMX_WCHAR p, q, temp; + LPSTORE s; + int n1, n2; + int i, n, shift; + KMX_BOOL FoundUse = FALSE; + + // TODO: Refactor to use incxstr + for(p = str; *p && (p < endstr || !endstr); p++) + { + if(*p == UC_SENTINEL) + switch(*(++p)) + { + case CODE_EXTENDED: // Start of a virtual key section w/shift codes + p++; + + shift = *p; //(*p<<8) | *(p+1); + m_actions.QueueAction(QIT_VSHIFTDOWN, shift); + + p++; + + m_actions.QueueAction(QIT_VKEYDOWN, *p); + m_actions.QueueAction(QIT_VKEYUP, *p); + + m_actions.QueueAction(QIT_VSHIFTUP, shift); + + p++; // CODE_EXTENDEDEND + ////// CODE_EXTENDEDEND will be incremented by loop + + break; + + case CODE_DEADKEY: // A deadkey to be output + p++; + m_actions.QueueAction(QIT_DEADKEY, *p); + break; + case CODE_BEEP: // Sound an 'iconasterisk' beep + m_actions.QueueAction(QIT_BELL, 0); + break; + case CODE_CONTEXT: // copy the context to the output + { + KMX_BOOL ignoreOutputKeystroke; + PostString(m_miniContext, lpkb, (PKMX_WCHAR) u16chr(m_miniContext, 0), &ignoreOutputKeystroke); + break; + } + case CODE_CONTEXTEX: + p++; + for (q = m_miniContext, i = m_miniContextIfLen; *q && i < *p - 1; i++, q = incxstr(q)); + if (*q) { + KMX_BOOL ignoreOutputKeystroke; + temp = incxstr(q); + PostString(q, lpkb, temp, &ignoreOutputKeystroke); + } + break; + case CODE_RETURN: // stop processing and start PostAllKeys + m_state.StopOutput = TRUE; + return psrPostMessages; + + case CODE_CALL: + p++; + DebugLog("CallDLL not supported [store=%d].\n", *p-1); + FoundUse = TRUE; + break; + case CODE_USE: // use another group + p++; + ProcessGroup(&lpkb->dpGroupArray[*p-1], pOutputKeystroke); + if(m_state.StopOutput) return psrPostMessages; + FoundUse = TRUE; + break; + case CODE_CLEARCONTEXT: + // no longer supported, no-op + break; + case CODE_INDEX: + p++; + s = &m_keyboard.Keyboard->dpStoreArray[*p - 1]; + p++; + + n = m_indexStack[*p - 1]; + for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--); + PostString(temp, lpkb, incxstr(temp), pOutputKeystroke); + break; + case CODE_SETOPT: + p++; + n1 = *p - 1; + p++; + n2 = *p - 1; + GetOptions()->Set(n1, n2); + break; + case CODE_RESETOPT: + p++; + n1 = *p - 1; + GetOptions()->Reset(m_kbp_state->processor(), n1); + break; + case CODE_SAVEOPT: + p++; + n1 = *p - 1; + GetOptions()->Save(*m_kbp_state, n1); + break; + case CODE_IFSYSTEMSTORE: + p+=3; + break; + case CODE_SETSYSTEMSTORE: + p+=2; + break; + } + else { + if(Uni_IsSurrogate1(*p) && Uni_IsSurrogate2(*(p+1))) { + m_actions.QueueAction(QIT_CHAR, Uni_SurrogateToUTF32(*p, *(p+1))); + p++; + } else { + m_actions.QueueAction(QIT_CHAR, *p); + } + } + } + return FoundUse ? psrPostMessages : psrCheckMatches; +} + + +KMX_BOOL KMX_ProcessEvent::IsMatchingBaseLayout(PKMX_WCHAR layoutName) // I3432 +{ + KMX_BOOL bEqual = u16icmp(layoutName, static_cast(m_environment.baseLayout().c_str())) == 0 || // I4583 + u16icmp(layoutName, static_cast(m_environment.baseLayoutAlt().c_str())) == 0; // I4583 + + return bEqual; +} + +KMX_BOOL KMX_ProcessEvent::IsMatchingPlatformString(PKMX_WCHAR platform) // I3432 +{ + // TODO retrieve platform string from client environment + // TODO cleanup to use a vector. + PKMX_WCHAR t = new KMX_WCHAR[u16len(m_environment.platform().c_str()) + 1], context = NULL; + u16cpy(t, /*wcslen(s->dpString)+1,*/ m_environment.platform().c_str()); + PKMX_WCHAR p = u16tok(t, u' ', &context); + while (p != NULL) { + if (u16icmp(platform, p) == 0) { + delete [] t; + return TRUE; + } + p = u16tok(NULL, u' ', &context); + } + + delete [] t; + return FALSE; +} + +KMX_BOOL KMX_ProcessEvent::IsMatchingPlatform(LPSTORE s) // I3432 +{ + PKMX_WCHAR t = new KMX_WCHAR[u16len(s->dpString)+1]; + u16cpy(t, /*wcslen(s->dpString)+1,*/ s->dpString); + PKMX_WCHAR context = NULL; + PKMX_WCHAR platform = u16tok(t, u' ', &context); + while(platform != NULL) + { + if(!IsMatchingPlatformString(platform)) + { + s->dwSystemID = TSS_PLATFORM_NOMATCH; + delete [] t; + return FALSE; + } + platform = u16tok(NULL, u' ', &context); + } + + s->dwSystemID = TSS_PLATFORM_MATCH; + delete [] t; + return TRUE; +} + +/* +* KMX_BOOL ContextMatch( LPKEY kkp ); +* +* Parameters: kkp Rule to compare +* +* Returns: 0 on OK, 1 on not equal +* +* Called by: ProcessKey +* +* ContextMatch compares the context of a rule with the current context. +*/ + +KMX_BOOL KMX_ProcessEvent::ContextMatch(LPKEY kkp) +{ + KMX_WORD /*i,*/ n; + PKMX_WCHAR p, q, qbuf, temp; + PKMX_WORD indexp; + LPSTORE s, t; + KMX_BOOL bEqual; + + memset(m_indexStack, 0, GLOBAL_ContextStackSize*sizeof(KMX_WORD)); + + p = kkp->dpContext; + + if(*p == 0) + { + return TRUE; + } + + if(*p == UC_SENTINEL && *(p+1) == CODE_NUL) + { + // If context buf is longer than the context, then obviously not start of doc. + if(m_context.Buf(xstrlen_ignoreifopt(p))) return FALSE; + p = incxstr(p); + if(*p == 0) return TRUE; + } + + for(PKMX_WCHAR pp = p; pp && *pp; pp = incxstr(pp)) + { + if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFOPT) + { + s = &m_keyboard.Keyboard->dpStoreArray[(*(pp+2))-1]; + t = &m_keyboard.Keyboard->dpStoreArray[(*(pp+4))-1]; + + bEqual = u16cmp(s->dpString, t->dpString) == 0; + if(*(pp+3) == 1 && bEqual) return FALSE; + if(*(pp+3) == 2 && !bEqual) return FALSE; + } + else if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFSYSTEMSTORE) + { + KMX_DWORD dwSystemID = *(pp+2)-1; + t = &m_keyboard.Keyboard->dpStoreArray[(*(pp+4))-1]; + switch(dwSystemID) + { + case TSS_PLATFORM_MATCH: // Cached platform result - a matching platform + bEqual = TRUE; + break; + case TSS_PLATFORM_NOMATCH: // Cached platform result - not a matching platform + bEqual = FALSE; + break; + case TSS_PLATFORM: + bEqual = IsMatchingPlatform(t); + break; + case TSS_BASELAYOUT: + bEqual = IsMatchingBaseLayout(t->dpString); + break; + default: + { + PKMX_WCHAR ss = GetSystemStore(m_keyboard.Keyboard, dwSystemID); + if(ss == NULL) return FALSE; + bEqual = u16cmp(ss, t->dpString) == 0; } } - _kmx.GetContext()->Set(ctxt.c_str()); - _kmx.GetActions()->ResetQueue(); - state->actions().clear(); + if(*(pp+3) == 1 && bEqual) return FALSE; + if(*(pp+3) == 2 && !bEqual) return FALSE; + } + } - if (!_kmx.ProcessEvent(state, vk, modifier_state)) { - // We need to output the default keystroke - state->actions().push_emit_keystroke(); - } + q = qbuf = m_context.Buf(xstrlen_ignoreifopt(p)); + if(!q) + { + return FALSE; // context buf is too short! + } + indexp = m_indexStack; - for (auto i = 0; i < _kmx.GetActions()->Length(); i++) { - auto a = _kmx.GetActions()->Get(i); - switch (a.ItemType) { - case QIT_CAPSLOCK: - //TODO: add Caps Event - //dwData = 0 == off; 1 == on - //state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_CAPSLOCK, {0,}, {0} }); - break; - case QIT_VKEYDOWN: - case QIT_VKEYUP: - case QIT_VSHIFTDOWN: - case QIT_VSHIFTUP: - //TODO: eliminate?? - break; - case QIT_CHAR: - state->context().push_character(a.dwData); - state->actions().push_character(a.dwData); - break; - case QIT_DEADKEY: - state->context().push_marker(a.dwData); - state->actions().push_marker(a.dwData); - break; - case QIT_BELL: - state->actions().push_alert(); - break; - case QIT_BACK: - switch (a.dwData) { - case BK_DEFAULT: - // This only happens if we know we have context to delete. Last item must be a character - assert(!state->context().empty() && state->context().back().type != KM_KBP_IT_MARKER); - if (!state->context().empty()) state->context().pop_back(); - state->actions().push_backspace(); - break; - case BK_DEADKEY: - // This only happens if we know we have context to delete. Last item must be a deadkey - assert(!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER); - if (!state->context().empty()) state->context().pop_back(); - break; - case BK_BACKSPACE: - // User-initiated backspace. We need to delete deadkeys from context, both sides of the character deleted - while (!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER) state->context().pop_back(); - if (!state->context().empty()) { - state->context().pop_back(); - while (!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER) state->context().pop_back(); - } - // Even if context is empty, we send the backspace event, because we may not - // know the context. - state->actions().push_backspace(); - break; - default: - assert(false); - } - break; - case QIT_INVALIDATECONTEXT: - state->actions().push_invalidate_context(); - break; - default: - //std::cout << "Unexpected item type " << a.ItemType << ", " << a.dwData << std::endl; - assert(false); + for(; *p && *q; p = incxstr(p)) + { + *indexp = 0; + + if(*p == UC_SENTINEL) + { + switch(*(p+1)) + { + case CODE_DEADKEY: + if(*q != UC_SENTINEL || *(q+1) != CODE_DEADKEY || *(q+2) != *(p+2)) + { + return FALSE; } + break; + case CODE_ANY: + s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1]; + + temp = xstrchr(s->dpString, q); + + if(temp != NULL) + *indexp = (KMX_WORD) xstrpos(temp, s->dpString); + + else + return FALSE; + break; + case CODE_NOTANY: + s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1]; + + if((temp = xstrchr(s->dpString, q)) != NULL) + return FALSE; + + break; + case CODE_INDEX: + s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1]; + *indexp = n = m_indexStack[(*(p+3))-1]; + + for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--); + if(n != 0) return FALSE; + if(xchrcmp(temp, q) != 0) return FALSE; + break; + case CODE_CONTEXTEX: + // only the nth character + for(n = *(p+2) - 1, temp = qbuf; temp < q && n > 0; n--, temp = incxstr(temp)); + if(n == 0) + if(xchrcmp(temp, q) != 0) return FALSE; + break; + case CODE_IFOPT: + case CODE_IFSYSTEMSTORE: + indexp++; + continue; // don't increment q + default: + return FALSE; } - - state->actions().commit(); - - return KM_KBP_STATUS_OK; } - - constexpr km_kbp_attr const engine_attrs = { - 256, - KM_KBP_LIB_CURRENT, - KM_KBP_LIB_AGE, - KM_KBP_LIB_REVISION, - KM_KBP_TECH_KMX, - "SIL International" - }; - - km_kbp_attr const & kmx_processor::attributes() const { - return engine_attrs; + else if(xchrcmp(p, q) != 0) + { + return FALSE; } + indexp++; + q = incxstr(q); + } - } // namespace kbp -} // namespace km + while(*p == UC_SENTINEL && (*(p+1) == CODE_IFOPT || *(p+1) == CODE_IFSYSTEMSTORE)) p = incxstr(p); // already tested + + return *p == *q; /*at least one must ==0 at this point*/ +} + +KMX_Actions *KMX_ProcessEvent::GetActions() { + return &m_actions; +} + +KMX_Context *KMX_ProcessEvent::GetContext() { + return &m_context; +} + +KMX_Options *KMX_ProcessEvent::GetOptions() { + return &m_options; +} + +KMX_Options const *KMX_ProcessEvent::GetOptions() const { + return &m_options; +} + +KMX_Environment *KMX_ProcessEvent::GetEnvironment() { + return &m_environment; +} + +KMX_Environment const *KMX_ProcessEvent::GetEnvironment() const { + return &m_environment; +} + +LPINTKEYBOARDINFO KMX_ProcessEvent::GetKeyboard() { + return &m_keyboard; +} + + +KMX_BOOL KMX_ProcessEvent::ReleaseKeyboardMemory(LPKEYBOARD kbd) +{ + if (!kbd) return TRUE; + delete kbd; + return TRUE; +} + +PKMX_WCHAR KMX_ProcessEvent::GetSystemStore(LPKEYBOARD kb, KMX_DWORD SystemID) +{ + for (KMX_DWORD i = 0; i < kb->cxStoreArray; i++) + if (kb->dpStoreArray[i].dwSystemID == SystemID) return kb->dpStoreArray[i].dpString; + + return NULL; +} diff --git a/common/core/desktop/src/kmx/kmx_processor.h b/common/core/desktop/src/kmx/kmx_processevent.h similarity index 98% rename from common/core/desktop/src/kmx/kmx_processor.h rename to common/core/desktop/src/kmx/kmx_processevent.h index 22ca170e84..029d5508ee 100644 --- a/common/core/desktop/src/kmx/kmx_processor.h +++ b/common/core/desktop/src/kmx/kmx_processevent.h @@ -24,7 +24,7 @@ namespace kmx { #define GLOBAL_ContextStackSize 80 -class KMX_Processor { +class KMX_ProcessEvent { private: PKMX_WORD m_indexStack; PKMX_WCHAR m_miniContext; @@ -78,8 +78,8 @@ private: KMX_BOOL IsEquivalentShift(KMX_UINT rshift, KMX_UINT kshift); public: - KMX_Processor(); - ~KMX_Processor(); + KMX_ProcessEvent(); + ~KMX_ProcessEvent(); KMX_BOOL Load(km_kbp_path_name keyboardName); KMX_BOOL ProcessEvent(km_kbp_state *state, KMX_UINT vkey, KMX_DWORD modifiers); // returns FALSE on error or key not matched diff --git a/common/core/desktop/src/kmx/kmx_processor.cpp b/common/core/desktop/src/kmx/kmx_processor.cpp index 01dfbc6e59..14bc4b2756 100644 --- a/common/core/desktop/src/kmx/kmx_processor.cpp +++ b/common/core/desktop/src/kmx/kmx_processor.cpp @@ -1,682 +1,189 @@ -/* - Copyright: Copyright (C) 2003-2018 SIL International. - Authors: mcdurdin -*/ -#include "kmx_processor.h" +#include #include "state.hpp" +#include "kmx/kmx_processor.hpp" -using namespace km::kbp; -using namespace kmx; - -/* Globals */ - -KMX_BOOL km::kbp::kmx::g_debug_ToConsole = FALSE; -KMX_BOOL km::kbp::kmx::g_debug_KeymanLog = TRUE; -KMX_BOOL km::kbp::kmx::g_silent = FALSE; - -/* -* KMX_Processor -*/ - -KMX_Processor::KMX_Processor() : m_actions(&m_context), m_options(&m_keyboard) { - m_indexStack = new KMX_WORD[GLOBAL_ContextStackSize]; - m_miniContext = new KMX_WCHAR[GLOBAL_ContextStackSize]; - m_miniContextIfLen = 0; -} - -KMX_Processor::~KMX_Processor() { - delete[] m_indexStack; - delete[] m_miniContext; -} - -char VKeyToChar(KMX_UINT modifiers, KMX_UINT vk) { - // We only map SHIFT and UNSHIFTED, and CAPS LOCK - - if ((modifiers & ~(K_SHIFTFLAG | CAPITALFLAG)) != 0) { - return 0; - } - - bool - shifted = (modifiers & K_SHIFTFLAG) == K_SHIFTFLAG, - caps = (modifiers & CAPITALFLAG) == CAPITALFLAG; - - if (vk == KM_KBP_VKEY_SPACE) { - // Override for space because it is the same for - // shifted and unshifted. - return 32; - } - - for (int i = 0; s_char_to_vkey[i].vk; i++) { - if (s_char_to_vkey[i].caps && caps) { - if (s_char_to_vkey[i].vk == vk && s_char_to_vkey[i].shifted == !shifted) return i + 32; - } - else { - if (s_char_to_vkey[i].vk == vk && s_char_to_vkey[i].shifted == shifted) return i + 32; - } - } - return 0; -} - -/* -* KMX_BOOL ProcessEvent(); -* -* Parameters: none -* -* Returns: TRUE if keystroke should be eaten -* -* Called by: FilterFunc -* -* ProcessEvent organizes the messages and gives them to the appropriate routines to -* process, and checks the state of Windows for the keyboard handling. -*/ - -KMX_BOOL KMX_Processor::ProcessEvent(km_kbp_state *state, KMX_UINT vkey, KMX_DWORD modifiers) -{ - LPKEYBOARD kbd = m_keyboard.Keyboard; - - m_kbp_state = state; - - if (m_environment.capsLock()) - modifiers |= CAPITALFLAG; - - m_state.vkey = vkey; - m_state.charCode = VKeyToChar(modifiers, vkey); - m_modifiers = modifiers; - m_state.LoopTimes = 0; - - if (kbd->StartGroup[BEGIN_UNICODE] == (KMX_DWORD) -1) { - DebugLog("Non-Unicode keyboards are not supported."); - return FALSE; - } - - LPGROUP gp = &kbd->dpGroupArray[kbd->StartGroup[BEGIN_UNICODE]]; - - KMX_BOOL fOutputKeystroke = FALSE; - - ProcessGroup(gp, &fOutputKeystroke); - - m_kbp_state = nullptr; - - return !fOutputKeystroke; -} - -/* -* PRIVATE KMX_BOOL ProcessGroup(LPGROUP gp); -* -* Parameters: gp Pointer to group to process inside -* -* Returns: TRUE if messages are to be sent, -* and FALSE if no messages are to be sent. -* -* Called by: ProcessEvent, recursive inside groups -* -* ProcessKey is where the keystroke conversion and output takes place. This routine -* has a lot of crucial code in it! -*/ - -KMX_BOOL KMX_Processor::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke) -{ - KMX_DWORD i; - LPKEY kkp = NULL; - PKMX_WCHAR p; - int sdmfI; - - /* - If the number of nested groups goes higher than 50, then break out - this is - a limitation of stack size. This is basically a catch-all for freaky apps that - cause message loopbacks and nasty things like that. Okay, it's really a catch all - for bugs! This means the user's system shouldn't hang. - */ - - LPKEYBOARD kbd = m_keyboard.Keyboard; - - sdmfI = -1; - - for(i = 0; i < kbd->cxGroupArray; i++) - if(gp == &kbd->dpGroupArray[i]) - { - sdmfI = i; - break; - } - - if(++m_state.LoopTimes > 50) +namespace km { + namespace kbp { - DebugLog("Aborting output: m_state.LoopTimes exceeded."); - m_state.StopOutput = TRUE; - return FALSE; - } - /* - The rule matching loop. + km_kbp_status kmx_processor::validate() const { + return _valid ? KM_KBP_STATUS_OK : KM_KBP_STATUS_INVALID_KEYBOARD; + } - This loop iterates through all the rules in the group that is currently being - processed. Each rule in a group can be of three different types: - 1. A virtual key rule, where the key to be matched is a virtual key - 2. A normal key rule (WM_CHAR), where the key to be matched is an Ascii char. - 3. A rule in a keyless group, where only the context is matched. - - The loop goes through and checks the rules like that. This loop could be optimized - with standard searching techniques - the ContextMatch may be difficult. - */ - - DebugLog("m_state.vkey: %s shiftFlags: %x; charCode: %X", Debug_VirtualKey(m_state.vkey), m_modifiers, m_state.charCode); // I4582 - DebugLog("m_context: %s", Debug_UnicodeString(m_context.GetFullContext())); - - if(gp) - { - for(kkp = gp->dpKeyArray, i=0; i < gp->cxKeyArray; i++, kkp++) + kmx_processor::kmx_processor(kbp::path p) { - if(!ContextMatch(kkp)) continue; - if(!gp->fUsingKeys) + p.replace_extension(".kmx"); + _valid = bool(_kmx.Load(p.c_str())); + + if (!_valid) + return; + + keyboard_attributes::options_store defaults; + _kmx.GetOptions()->Init(defaults); + + for (auto const & opt: defaults) { - if(kkp->dpContext[0] != 0) break; else continue; + if (!opt.empty() && opt.scope == KM_KBP_OPT_KEYBOARD ) + persisted_store()[opt.key] = opt.value; } + // Fill out attributes + auto v = _kmx.GetKeyboard()->Keyboard->version; + auto vs = std::to_string(v >> 16) + "." + std::to_string(v & 0xffff); - //if(kkp->Key == m_state.vkey) - //SendDebugMessageFormat(m_state.msg.hwnd, sdmKeyboard, 0, "kkp->Key: %d kkp->ShiftFlags: %x", - // kkp->Key, kkp->ShiftFlags); + _attributes = keyboard_attributes(static_cast(p.stem()), + std::u16string(vs.begin(), vs.end()), p.parent(), defaults); + } - /* Keyman 6.0: support Virtual Characters */ - if(IsEquivalentShift(kkp->ShiftFlags, m_modifiers)) + + char16_t const * kmx_processor::lookup_option(km_kbp_option_scope scope, std::u16string const & key) const + { + char16_t const * pValue = nullptr; + switch(scope) { - if(kkp->Key > VK__MAX && kkp->Key == m_state.vkey) break; // I3438 // I4582 - else if(kkp->Key == m_state.vkey) break; // I4169 + case KM_KBP_OPT_KEYBOARD: + pValue = _kmx.GetOptions()->LookUp(key); + break; + case KM_KBP_OPT_ENVIRONMENT: + pValue = _kmx.GetEnvironment()->LookUp(key); + break; + default: + break; } - else if(kkp->ShiftFlags == 0 && kkp->Key == m_state.charCode && m_state.charCode != 0) break; + + return pValue ? pValue : nullptr; } - } - if(!gp || i == gp->cxKeyArray) - { - /* - No rule was found that corresponded to the current state of the context and - keyboard. NoMatch should be checked for everything except virtual keys; and - context should also be kept. - - If the message was a virtual key, then just return without checking NoMatch. - NoMatch shouldn't be used for virtual keys because it will mean that no key - can ever get through that isn't matched - including arrows, func. keys, etc !! - Context is not kept for virtual keys being output. - */ - - DebugLog("No match was found in group %d of %d", sdmfI, m_keyboard.Keyboard->cxGroupArray); - - if(!gp || (m_state.charCode == 0 && gp->fUsingKeys)) // I4585 - // 7.0.241.0: I1133 - Fix mismatched parentheses on m_state.charCode - ie. we don't want to output this letter if !gp->fUsingKeys + option kmx_processor::update_option(km_kbp_option_scope scope, std::u16string const & key, std::u16string const & value) { - KMX_BOOL fIsBackspace = m_state.vkey == KM_KBP_VKEY_BKSP && (m_modifiers & (LCTRLFLAG|RCTRLFLAG|LALTFLAG|RALTFLAG)) == 0; // I4128 - - if(fIsBackspace) { // I4838 // I4933 - PKMX_WCHAR pdeletecontext = m_context.Buf(1); // I4933 - if(!pdeletecontext || *pdeletecontext == 0) { // I4933 - m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0); - *pOutputKeystroke = TRUE; // I4933 - return FALSE; // I4933 - } - m_actions.QueueAction(QIT_BACK, BK_BACKSPACE); // I4933 - } else { // I4024 // I4128 // I4287 // I4290 - DebugLog(" ... IsLegacy = FALSE; IsTIP = TRUE"); // I4128 - m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0); - *pOutputKeystroke = TRUE; - return FALSE; + switch(scope) { + case KM_KBP_OPT_KEYBOARD: + _kmx.GetOptions()->Set(key, value); + persisted_store()[key] = value; + break; + case KM_KBP_OPT_ENVIRONMENT: + _kmx.GetEnvironment()->Load(key, value); + break; + default: + return option(); + break; } - } - else if (gp->dpNoMatch != NULL && *gp->dpNoMatch != 0) - { - /* NoMatch rule found, and is a character key */ - PostString(gp->dpNoMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke); - } - else if (m_state.charCode != 0 && m_state.charCode != 0xFFFF && gp->fUsingKeys) - { - /* No rule found, is a character key */ - m_actions.QueueAction(QIT_CHAR, m_state.charCode); + + return option(scope, key, value); } - return TRUE; - } + km_kbp_status kmx_processor::process_event(km_kbp_state *state, km_kbp_virtual_key vk, uint16_t modifier_state) { + // Construct a context buffer from the items - DebugLog("match found in rule %d", i); - - /* - Save the context that will be used for output when the 'context' keyword is used. - For each deadkey, we need to add 2 characters; look in related stores as well... - */ - - assert(kkp != NULL); - - m_miniContextIfLen = xstrlen(kkp->dpContext) - xstrlen_ignoreifopt(kkp->dpContext); - - // 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support - if(*kkp->dpContext == UC_SENTINEL && *(kkp->dpContext+1) == CODE_NUL) - u16cpy(m_miniContext, /*GLOBAL_ContextStackSize,*/ m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)-1) /*, GLOBAL_ContextStackSize*/); // I3162 // I3536 - else - u16cpy(m_miniContext, /*GLOBAL_ContextStackSize,*/ m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)) /*, GLOBAL_ContextStackSize*/); // I3162 // I3536 - - m_miniContext[GLOBAL_ContextStackSize-1] = 0; - - /* - The next section includes several optimizations that make the code a little harder - to read, but are probably worth it in the time that they save. - - If the output string doesn't have a "context" byte at the start, post backspaces - to erase the appropriate number of characters in the application. If it does have - a "context" byte at the start, then the string won't change, and no backspaces are - necessary. You could go one step further with this optimization, in PostAllKeys, - by comparing the starts of the strings to see what is same, and not backspacing - that, but it is probably not necessary. - */ - - p = kkp->dpOutput; - if(*p != UC_SENTINEL || *(p+1) != CODE_CONTEXT) - { - for(p = decxstr((KMX_WCHAR *) u16chr(m_miniContext, 0), m_miniContext); p != NULL; p = decxstr(p, m_miniContext)) - { - if(*p == UC_SENTINEL) - switch(*(p+1)) - { - case CODE_DEADKEY: m_actions.QueueAction(QIT_BACK, BK_DEADKEY); break; - case CODE_NUL: break; // 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support - } - else - { - m_actions.QueueAction(QIT_BACK, 0); - } - } - p = kkp->dpOutput; - } - else - p = incxstr(p); // otherwise, the "context" entry has to be jumped over - - /* Use PostString to post the rest of the output string. */ - - if(PostString(p, m_keyboard.Keyboard, NULL, pOutputKeystroke) == psrCheckMatches) - { - if(gp->dpMatch && *gp->dpMatch) - { - PostString(gp->dpMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke); - } - } - - return TRUE; -} - -/* -* int PostString( PKMX_CHAR str, KMX_BOOL *useMode, LPMSG mp, -* LPKEYBOARD lpkb ); -* -* Parameters: str Pointer to string to send -* useMode Pointer to KMX_BOOL about whether a "use" command was found -* mp Pointer to MSG structure to copy in outputting messages -* lpkb Pointer to global keyboard structure -* -* Returns: 0 to continue, 1 and 2 to return. -* -* Called by: ProcessKey -* -* PostString posts a string of "context", "index", "beep", characters and virtual keys -* to the active application, via the Keyman PostKey buffer. -*/ - -int KMX_Processor::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR endstr, KMX_BOOL *pOutputKeystroke) -{ - PKMX_WCHAR p, q, temp; - LPSTORE s; - int n1, n2; - int i, n, shift; - KMX_BOOL FoundUse = FALSE; - - // TODO: Refactor to use incxstr - for(p = str; *p && (p < endstr || !endstr); p++) - { - if(*p == UC_SENTINEL) - switch(*(++p)) - { - case CODE_EXTENDED: // Start of a virtual key section w/shift codes - p++; - - shift = *p; //(*p<<8) | *(p+1); - m_actions.QueueAction(QIT_VSHIFTDOWN, shift); - - p++; - - m_actions.QueueAction(QIT_VKEYDOWN, *p); - m_actions.QueueAction(QIT_VKEYUP, *p); - - m_actions.QueueAction(QIT_VSHIFTUP, shift); - - p++; // CODE_EXTENDEDEND - ////// CODE_EXTENDEDEND will be incremented by loop - - break; - - case CODE_DEADKEY: // A deadkey to be output - p++; - m_actions.QueueAction(QIT_DEADKEY, *p); - break; - case CODE_BEEP: // Sound an 'iconasterisk' beep - m_actions.QueueAction(QIT_BELL, 0); - break; - case CODE_CONTEXT: // copy the context to the output - { - KMX_BOOL ignoreOutputKeystroke; - PostString(m_miniContext, lpkb, (PKMX_WCHAR) u16chr(m_miniContext, 0), &ignoreOutputKeystroke); - break; - } - case CODE_CONTEXTEX: - p++; - for (q = m_miniContext, i = m_miniContextIfLen; *q && i < *p - 1; i++, q = incxstr(q)); - if (*q) { - KMX_BOOL ignoreOutputKeystroke; - temp = incxstr(q); - PostString(q, lpkb, temp, &ignoreOutputKeystroke); - } - break; - case CODE_RETURN: // stop processing and start PostAllKeys - m_state.StopOutput = TRUE; - return psrPostMessages; - - case CODE_CALL: - p++; - DebugLog("CallDLL not supported [store=%d].\n", *p-1); - FoundUse = TRUE; - break; - case CODE_USE: // use another group - p++; - ProcessGroup(&lpkb->dpGroupArray[*p-1], pOutputKeystroke); - if(m_state.StopOutput) return psrPostMessages; - FoundUse = TRUE; - break; - case CODE_CLEARCONTEXT: - // no longer supported, no-op - break; - case CODE_INDEX: - p++; - s = &m_keyboard.Keyboard->dpStoreArray[*p - 1]; - p++; - - n = m_indexStack[*p - 1]; - for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--); - PostString(temp, lpkb, incxstr(temp), pOutputKeystroke); - break; - case CODE_SETOPT: - p++; - n1 = *p - 1; - p++; - n2 = *p - 1; - GetOptions()->Set(n1, n2); - break; - case CODE_RESETOPT: - p++; - n1 = *p - 1; - GetOptions()->Reset(m_kbp_state->processor(), n1); - break; - case CODE_SAVEOPT: - p++; - n1 = *p - 1; - GetOptions()->Save(*m_kbp_state, n1); - break; - case CODE_IFSYSTEMSTORE: - p+=3; - break; - case CODE_SETSYSTEMSTORE: - p+=2; - break; - } - else { - if(Uni_IsSurrogate1(*p) && Uni_IsSurrogate2(*(p+1))) { - m_actions.QueueAction(QIT_CHAR, Uni_SurrogateToUTF32(*p, *(p+1))); - p++; - } else { - m_actions.QueueAction(QIT_CHAR, *p); - } - } - } - return FoundUse ? psrPostMessages : psrCheckMatches; -} - - -KMX_BOOL KMX_Processor::IsMatchingBaseLayout(PKMX_WCHAR layoutName) // I3432 -{ - KMX_BOOL bEqual = u16icmp(layoutName, static_cast(m_environment.baseLayout().c_str())) == 0 || // I4583 - u16icmp(layoutName, static_cast(m_environment.baseLayoutAlt().c_str())) == 0; // I4583 - - return bEqual; -} - -KMX_BOOL KMX_Processor::IsMatchingPlatformString(PKMX_WCHAR platform) // I3432 -{ - // TODO retrieve platform string from client environment - // TODO cleanup to use a vector. - PKMX_WCHAR t = new KMX_WCHAR[u16len(m_environment.platform().c_str()) + 1], context = NULL; - u16cpy(t, /*wcslen(s->dpString)+1,*/ m_environment.platform().c_str()); - PKMX_WCHAR p = u16tok(t, u' ', &context); - while (p != NULL) { - if (u16icmp(platform, p) == 0) { - delete [] t; - return TRUE; - } - p = u16tok(NULL, u' ', &context); - } - - delete [] t; - return FALSE; -} - -KMX_BOOL KMX_Processor::IsMatchingPlatform(LPSTORE s) // I3432 -{ - PKMX_WCHAR t = new KMX_WCHAR[u16len(s->dpString)+1]; - u16cpy(t, /*wcslen(s->dpString)+1,*/ s->dpString); - PKMX_WCHAR context = NULL; - PKMX_WCHAR platform = u16tok(t, u' ', &context); - while(platform != NULL) - { - if(!IsMatchingPlatformString(platform)) - { - s->dwSystemID = TSS_PLATFORM_NOMATCH; - delete [] t; - return FALSE; - } - platform = u16tok(NULL, u' ', &context); - } - - s->dwSystemID = TSS_PLATFORM_MATCH; - delete [] t; - return TRUE; -} - -/* -* KMX_BOOL ContextMatch( LPKEY kkp ); -* -* Parameters: kkp Rule to compare -* -* Returns: 0 on OK, 1 on not equal -* -* Called by: ProcessKey -* -* ContextMatch compares the context of a rule with the current context. -*/ - -KMX_BOOL KMX_Processor::ContextMatch(LPKEY kkp) -{ - KMX_WORD /*i,*/ n; - PKMX_WCHAR p, q, qbuf, temp; - PKMX_WORD indexp; - LPSTORE s, t; - KMX_BOOL bEqual; - - memset(m_indexStack, 0, GLOBAL_ContextStackSize*sizeof(KMX_WORD)); - - p = kkp->dpContext; - - if(*p == 0) - { - return TRUE; - } - - if(*p == UC_SENTINEL && *(p+1) == CODE_NUL) - { - // If context buf is longer than the context, then obviously not start of doc. - if(m_context.Buf(xstrlen_ignoreifopt(p))) return FALSE; - p = incxstr(p); - if(*p == 0) return TRUE; - } - - for(PKMX_WCHAR pp = p; pp && *pp; pp = incxstr(pp)) - { - if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFOPT) - { - s = &m_keyboard.Keyboard->dpStoreArray[(*(pp+2))-1]; - t = &m_keyboard.Keyboard->dpStoreArray[(*(pp+4))-1]; - - bEqual = u16cmp(s->dpString, t->dpString) == 0; - if(*(pp+3) == 1 && bEqual) return FALSE; - if(*(pp+3) == 2 && !bEqual) return FALSE; - } - else if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFSYSTEMSTORE) - { - KMX_DWORD dwSystemID = *(pp+2)-1; - t = &m_keyboard.Keyboard->dpStoreArray[(*(pp+4))-1]; - switch(dwSystemID) - { - case TSS_PLATFORM_MATCH: // Cached platform result - a matching platform - bEqual = TRUE; - break; - case TSS_PLATFORM_NOMATCH: // Cached platform result - not a matching platform - bEqual = FALSE; - break; - case TSS_PLATFORM: - bEqual = IsMatchingPlatform(t); - break; - case TSS_BASELAYOUT: - bEqual = IsMatchingBaseLayout(t->dpString); - break; - default: - { - PKMX_WCHAR ss = GetSystemStore(m_keyboard.Keyboard, dwSystemID); - if(ss == NULL) return FALSE; - bEqual = u16cmp(ss, t->dpString) == 0; + std::u16string ctxt; + auto cp = state->context(); + for (auto c = cp.begin(); c != cp.end(); c++) { + switch (c->type) { + case KM_KBP_CT_CHAR: + if (Uni_IsSMP(c->character)) { + ctxt += Uni_UTF32ToSurrogate1(c->character); + ctxt += Uni_UTF32ToSurrogate2(c->character); + } + else { + ctxt += (km_kbp_cp) c->character; + } + break; + case KM_KBP_CT_MARKER: + assert(c->marker > 0); + ctxt += UC_SENTINEL; + ctxt += CODE_DEADKEY; + ctxt += c->marker; + break; } } - if(*(pp+3) == 1 && bEqual) return FALSE; - if(*(pp+3) == 2 && !bEqual) return FALSE; - } - } + _kmx.GetContext()->Set(ctxt.c_str()); + _kmx.GetActions()->ResetQueue(); + state->actions().clear(); - q = qbuf = m_context.Buf(xstrlen_ignoreifopt(p)); - if(!q) - { - return FALSE; // context buf is too short! - } - indexp = m_indexStack; - - for(; *p && *q; p = incxstr(p)) - { - *indexp = 0; - - if(*p == UC_SENTINEL) - { - switch(*(p+1)) - { - case CODE_DEADKEY: - if(*q != UC_SENTINEL || *(q+1) != CODE_DEADKEY || *(q+2) != *(p+2)) - { - return FALSE; - } - break; - case CODE_ANY: - s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1]; - - temp = xstrchr(s->dpString, q); - - if(temp != NULL) - *indexp = (KMX_WORD) xstrpos(temp, s->dpString); - - else - return FALSE; - break; - case CODE_NOTANY: - s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1]; - - if((temp = xstrchr(s->dpString, q)) != NULL) - return FALSE; - - break; - case CODE_INDEX: - s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1]; - *indexp = n = m_indexStack[(*(p+3))-1]; - - for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--); - if(n != 0) return FALSE; - if(xchrcmp(temp, q) != 0) return FALSE; - break; - case CODE_CONTEXTEX: - // only the nth character - for(n = *(p+2) - 1, temp = qbuf; temp < q && n > 0; n--, temp = incxstr(temp)); - if(n == 0) - if(xchrcmp(temp, q) != 0) return FALSE; - break; - case CODE_IFOPT: - case CODE_IFSYSTEMSTORE: - indexp++; - continue; // don't increment q - default: - return FALSE; + if (!_kmx.ProcessEvent(state, vk, modifier_state)) { + // We need to output the default keystroke + state->actions().push_emit_keystroke(); } + + for (auto i = 0; i < _kmx.GetActions()->Length(); i++) { + auto a = _kmx.GetActions()->Get(i); + switch (a.ItemType) { + case QIT_CAPSLOCK: + //TODO: add Caps Event + //dwData = 0 == off; 1 == on + //state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_CAPSLOCK, {0,}, {0} }); + break; + case QIT_VKEYDOWN: + case QIT_VKEYUP: + case QIT_VSHIFTDOWN: + case QIT_VSHIFTUP: + //TODO: eliminate?? + break; + case QIT_CHAR: + state->context().push_character(a.dwData); + state->actions().push_character(a.dwData); + break; + case QIT_DEADKEY: + state->context().push_marker(a.dwData); + state->actions().push_marker(a.dwData); + break; + case QIT_BELL: + state->actions().push_alert(); + break; + case QIT_BACK: + switch (a.dwData) { + case BK_DEFAULT: + // This only happens if we know we have context to delete. Last item must be a character + assert(!state->context().empty() && state->context().back().type != KM_KBP_IT_MARKER); + if (!state->context().empty()) state->context().pop_back(); + state->actions().push_backspace(); + break; + case BK_DEADKEY: + // This only happens if we know we have context to delete. Last item must be a deadkey + assert(!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER); + if (!state->context().empty()) state->context().pop_back(); + break; + case BK_BACKSPACE: + // User-initiated backspace. We need to delete deadkeys from context, both sides of the character deleted + while (!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER) state->context().pop_back(); + if (!state->context().empty()) { + state->context().pop_back(); + while (!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER) state->context().pop_back(); + } + // Even if context is empty, we send the backspace event, because we may not + // know the context. + state->actions().push_backspace(); + break; + default: + assert(false); + } + break; + case QIT_INVALIDATECONTEXT: + state->actions().push_invalidate_context(); + break; + default: + //std::cout << "Unexpected item type " << a.ItemType << ", " << a.dwData << std::endl; + assert(false); + } + } + + state->actions().commit(); + + return KM_KBP_STATUS_OK; } - else if(xchrcmp(p, q) != 0) - { - return FALSE; + + constexpr km_kbp_attr const engine_attrs = { + 256, + KM_KBP_LIB_CURRENT, + KM_KBP_LIB_AGE, + KM_KBP_LIB_REVISION, + KM_KBP_TECH_KMX, + "SIL International" + }; + + km_kbp_attr const & kmx_processor::attributes() const { + return engine_attrs; } - indexp++; - q = incxstr(q); - } - while(*p == UC_SENTINEL && (*(p+1) == CODE_IFOPT || *(p+1) == CODE_IFSYSTEMSTORE)) p = incxstr(p); // already tested - - return *p == *q; /*at least one must ==0 at this point*/ -} - -KMX_Actions *KMX_Processor::GetActions() { - return &m_actions; -} - -KMX_Context *KMX_Processor::GetContext() { - return &m_context; -} - -KMX_Options *KMX_Processor::GetOptions() { - return &m_options; -} - -KMX_Options const *KMX_Processor::GetOptions() const { - return &m_options; -} - -KMX_Environment *KMX_Processor::GetEnvironment() { - return &m_environment; -} - -KMX_Environment const *KMX_Processor::GetEnvironment() const { - return &m_environment; -} - -LPINTKEYBOARDINFO KMX_Processor::GetKeyboard() { - return &m_keyboard; -} - - -KMX_BOOL KMX_Processor::ReleaseKeyboardMemory(LPKEYBOARD kbd) -{ - if (!kbd) return TRUE; - delete kbd; - return TRUE; -} - -PKMX_WCHAR KMX_Processor::GetSystemStore(LPKEYBOARD kb, KMX_DWORD SystemID) -{ - for (KMX_DWORD i = 0; i < kb->cxStoreArray; i++) - if (kb->dpStoreArray[i].dwSystemID == SystemID) return kb->dpStoreArray[i].dpString; - - return NULL; -} + } // namespace kbp +} // namespace km diff --git a/common/core/desktop/src/kmx/kmx_processevent.hpp b/common/core/desktop/src/kmx/kmx_processor.hpp similarity index 94% rename from common/core/desktop/src/kmx/kmx_processevent.hpp rename to common/core/desktop/src/kmx/kmx_processor.hpp index 29ba66e321..4c71db4922 100644 --- a/common/core/desktop/src/kmx/kmx_processevent.hpp +++ b/common/core/desktop/src/kmx/kmx_processor.hpp @@ -10,7 +10,7 @@ #include #include -#include "kmx/kmx_processor.h" +#include "kmx/kmx_processevent.h" #include "keyboard.hpp" #include "processor.hpp" @@ -21,7 +21,7 @@ namespace kbp { private: bool _valid; - kmx::KMX_Processor _kmx; + kmx::KMX_ProcessEvent _kmx; public: kmx_processor(path); km_kbp_status process_event(km_kbp_state *state, diff --git a/common/core/desktop/src/kmx/kmx_xstring.cpp b/common/core/desktop/src/kmx/kmx_xstring.cpp index 87267278a9..47cb7d5d50 100644 --- a/common/core/desktop/src/kmx/kmx_xstring.cpp +++ b/common/core/desktop/src/kmx/kmx_xstring.cpp @@ -6,7 +6,7 @@ #include #include #include -#include "kmx_processor.h" +#include "kmx_processevent.h" #include "utfcodec.hpp" diff --git a/common/core/desktop/tests/unit/kmx/kmx.cpp b/common/core/desktop/tests/unit/kmx/kmx.cpp index 2f4ec39945..f2f1b3b6ee 100644 --- a/common/core/desktop/tests/unit/kmx/kmx.cpp +++ b/common/core/desktop/tests/unit/kmx/kmx.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include "path.hpp" #include "state.hpp" From 824e5a1f1a642ed3283f035351d469bf211349df Mon Sep 17 00:00:00 2001 From: Keyman Build Agent Date: Wed, 7 Jul 2021 14:01:12 -0400 Subject: [PATCH 53/58] auto: increment master version to 15.0.83 --- HISTORY.md | 4 ++++ VERSION.md | 2 +- .../web/input-processor/package-lock.json | 2 +- common/core/web/input-processor/package.json | 16 +++++++-------- .../web/keyboard-processor/package-lock.json | 2 +- .../core/web/keyboard-processor/package.json | 10 +++++----- .../core/web/tools/recorder/package-lock.json | 2 +- common/core/web/tools/recorder/package.json | 10 +++++----- .../tools/sentry-manager/package-lock.json | 2 +- .../web/tools/sentry-manager/package.json | 4 ++-- common/core/web/utils/package-lock.json | 2 +- common/core/web/utils/package.json | 6 +++--- common/models/templates/package-lock.json | 2 +- common/models/templates/package.json | 8 ++++---- common/models/types/package-lock.json | 2 +- common/models/types/package.json | 2 +- common/models/wordbreakers/package-lock.json | 2 +- common/models/wordbreakers/package.json | 4 ++-- common/predictive-text/package-lock.json | 2 +- common/predictive-text/package.json | 14 ++++++------- developer/js/package-lock.json | 2 +- developer/js/package.json | 8 ++++---- lerna.json | 2 +- resources/gosh/package-lock.json | 2 +- resources/gosh/package.json | 2 +- resources/web-environment/package-lock.json | 2 +- resources/web-environment/package.json | 4 ++-- web/package-lock.json | 2 +- web/package.json | 20 +++++++++---------- 29 files changed, 73 insertions(+), 69 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 075aa427bb..b4b988a49d 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,9 @@ # Keyman Version History +## 15.0.82 alpha 2021-07-07 + +* fix(common/models/types): fixes test script config (#5441) + ## 15.0.81 alpha 2021-07-06 * fix(android/engine): Fix font file path (#5424) diff --git a/VERSION.md b/VERSION.md index a582683631..5c3cbd6701 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -15.0.82 \ No newline at end of file +15.0.83 \ No newline at end of file diff --git a/common/core/web/input-processor/package-lock.json b/common/core/web/input-processor/package-lock.json index 813df5bedd..598c997656 100644 --- a/common/core/web/input-processor/package-lock.json +++ b/common/core/web/input-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "15.0.82", + "version": "15.0.83", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/core/web/input-processor/package.json b/common/core/web/input-processor/package.json index c30475e4f2..8cf312d46a 100644 --- a/common/core/web/input-processor/package.json +++ b/common/core/web/input-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "15.0.82", + "version": "15.0.83", "description": "The core text and prediction processing engine for KeymanWeb", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/lexical-model-compiler": "^15.0.82", - "@keymanapp/resources-gosh": "^15.0.82", + "@keymanapp/lexical-model-compiler": "^15.0.83", + "@keymanapp/resources-gosh": "^15.0.83", "@types/node": "^11.9.4", "chai": "^4.3.4", "mocha": "^8.4.0", @@ -34,11 +34,11 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/keyboard-processor": "^15.0.82", - "@keymanapp/lexical-model-layer": "^15.0.82", - "@keymanapp/models-types": "^15.0.82", - "@keymanapp/web-environment": "^15.0.82", - "@keymanapp/web-utils": "^15.0.82", + "@keymanapp/keyboard-processor": "^15.0.83", + "@keymanapp/lexical-model-layer": "^15.0.83", + "@keymanapp/models-types": "^15.0.83", + "@keymanapp/web-environment": "^15.0.83", + "@keymanapp/web-utils": "^15.0.83", "eventemitter3": "^4.0.0" } } diff --git a/common/core/web/keyboard-processor/package-lock.json b/common/core/web/keyboard-processor/package-lock.json index 71c878a4fe..6966187337 100644 --- a/common/core/web/keyboard-processor/package-lock.json +++ b/common/core/web/keyboard-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "15.0.82", + "version": "15.0.83", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/core/web/keyboard-processor/package.json b/common/core/web/keyboard-processor/package.json index 99e32fc663..66ad0570ff 100644 --- a/common/core/web/keyboard-processor/package.json +++ b/common/core/web/keyboard-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "15.0.82", + "version": "15.0.83", "description": "Core module for Keyman keyboard support in KeymanWeb.", "repository": { "type": "git", @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^15.0.82", + "@keymanapp/resources-gosh": "^15.0.83", "chai": "^4.3.4", "mocha": "^8.4.0", "mocha-teamcity-reporter": "^4.0.0", @@ -32,9 +32,9 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/models-types": "^15.0.82", - "@keymanapp/web-environment": "^15.0.82", - "@keymanapp/web-utils": "^15.0.82", + "@keymanapp/models-types": "^15.0.83", + "@keymanapp/web-environment": "^15.0.83", + "@keymanapp/web-utils": "^15.0.83", "@types/node": "^11.9.4" } } diff --git a/common/core/web/tools/recorder/package-lock.json b/common/core/web/tools/recorder/package-lock.json index 419ad1ed81..8616a8eea0 100644 --- a/common/core/web/tools/recorder/package-lock.json +++ b/common/core/web/tools/recorder/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "15.0.82", + "version": "15.0.83", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/core/web/tools/recorder/package.json b/common/core/web/tools/recorder/package.json index 725c0b1a72..fdc666752a 100644 --- a/common/core/web/tools/recorder/package.json +++ b/common/core/web/tools/recorder/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "15.0.82", + "version": "15.0.83", "description": "Core classes used to develop KeymanWeb test cases based on keystrokes", "main": "index.js", "scripts": { @@ -17,10 +17,10 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "dependencies": { - "@keymanapp/keyboard-processor": "^15.0.82", - "@keymanapp/models-types": "^15.0.82", - "@keymanapp/web-environment": "^15.0.82", - "@keymanapp/web-utils": "^15.0.82", + "@keymanapp/keyboard-processor": "^15.0.83", + "@keymanapp/models-types": "^15.0.83", + "@keymanapp/web-environment": "^15.0.83", + "@keymanapp/web-utils": "^15.0.83", "@types/node": "^11.9.4" }, "devDependencies": { diff --git a/common/core/web/tools/sentry-manager/package-lock.json b/common/core/web/tools/sentry-manager/package-lock.json index 089d4f87d4..6b0e06932e 100644 --- a/common/core/web/tools/sentry-manager/package-lock.json +++ b/common/core/web/tools/sentry-manager/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "15.0.82", + "version": "15.0.83", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/sentry-manager/package.json b/common/core/web/tools/sentry-manager/package.json index ff7874a7d7..43d3041c34 100644 --- a/common/core/web/tools/sentry-manager/package.json +++ b/common/core/web/tools/sentry-manager/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "15.0.82", + "version": "15.0.83", "description": "Common setup for KeymanWeb's Sentry-based error reporting", "main": "dist/index.js", "scripts": { @@ -20,7 +20,7 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/web-environment": "^15.0.82", + "@keymanapp/web-environment": "^15.0.83", "@sentry/browser": "^5.27.4" } } diff --git a/common/core/web/utils/package-lock.json b/common/core/web/utils/package-lock.json index 8b9d5372e6..497858dee7 100644 --- a/common/core/web/utils/package-lock.json +++ b/common/core/web/utils/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "15.0.82", + "version": "15.0.83", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/utils/package.json b/common/core/web/utils/package.json index 5608dd5415..096af0137c 100644 --- a/common/core/web/utils/package.json +++ b/common/core/web/utils/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "15.0.82", + "version": "15.0.83", "description": "Common utility functions used throughout other Keyman packages", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -20,8 +20,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^15.0.82", - "@keymanapp/web-environment": "^15.0.82", + "@keymanapp/resources-gosh": "^15.0.83", + "@keymanapp/web-environment": "^15.0.83", "@types/node": "^14.0.5", "typescript": "^3.8.3" } diff --git a/common/models/templates/package-lock.json b/common/models/templates/package-lock.json index 29fff5ac75..6866bc4c5b 100644 --- a/common/models/templates/package-lock.json +++ b/common/models/templates/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "15.0.82", + "version": "15.0.83", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/models/templates/package.json b/common/models/templates/package.json index 7f7520fd7e..9a0c75a776 100644 --- a/common/models/templates/package.json +++ b/common/models/templates/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "15.0.82", + "version": "15.0.83", "description": "Backing model implementations (templates) for Keyman's modelling layer", "keywords": [ "lm", @@ -42,8 +42,8 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^15.0.82", - "@keymanapp/web-utils": "^15.0.82", + "@keymanapp/models-types": "^15.0.83", + "@keymanapp/web-utils": "^15.0.83", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "@types/node": "^14.0.4", @@ -52,6 +52,6 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-wordbreakers": "^15.0.82" + "@keymanapp/models-wordbreakers": "^15.0.83" } } diff --git a/common/models/types/package-lock.json b/common/models/types/package-lock.json index 981fddc5bf..4eea6fd94a 100644 --- a/common/models/types/package-lock.json +++ b/common/models/types/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "15.0.82", + "version": "15.0.83", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/types/package.json b/common/models/types/package.json index 353bcbd107..10cbdbd728 100644 --- a/common/models/types/package.json +++ b/common/models/types/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "15.0.82", + "version": "15.0.83", "description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.", "types": "./index.d.ts", "scripts": { diff --git a/common/models/wordbreakers/package-lock.json b/common/models/wordbreakers/package-lock.json index e4666c5262..d6b00d8c4f 100644 --- a/common/models/wordbreakers/package-lock.json +++ b/common/models/wordbreakers/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "15.0.82", + "version": "15.0.83", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/models/wordbreakers/package.json b/common/models/wordbreakers/package.json index c89c3e33a6..2fd7eebf4b 100644 --- a/common/models/wordbreakers/package.json +++ b/common/models/wordbreakers/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "15.0.82", + "version": "15.0.83", "description": "Word breakers bundled in the modeling layer", "keywords": [ "lexical model", @@ -40,7 +40,7 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^15.0.82", + "@keymanapp/models-types": "^15.0.83", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "chai": "^4.3.4", diff --git a/common/predictive-text/package-lock.json b/common/predictive-text/package-lock.json index 1d22e60fd8..62c6da3066 100644 --- a/common/predictive-text/package-lock.json +++ b/common/predictive-text/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "15.0.82", + "version": "15.0.83", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/predictive-text/package.json b/common/predictive-text/package.json index 1ec86395ef..0b27eeb1b4 100644 --- a/common/predictive-text/package.json +++ b/common/predictive-text/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "15.0.82", + "version": "15.0.83", "description": "Keyman/Predictive Text integration layer", "main": "build/index.js", "scripts": { @@ -21,8 +21,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/models-types": "^15.0.82", - "@keymanapp/resources-gosh": "^15.0.82", + "@keymanapp/models-types": "^15.0.83", + "@keymanapp/resources-gosh": "^15.0.83", "@types/node": "^10.17.21", "chai": "^4.3.4", "karma": "^6.3.4", @@ -46,10 +46,10 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-templates": "^15.0.82", - "@keymanapp/models-wordbreakers": "^15.0.82", - "@keymanapp/web-environment": "^15.0.82", - "@keymanapp/web-utils": "^15.0.82", + "@keymanapp/models-templates": "^15.0.83", + "@keymanapp/models-wordbreakers": "^15.0.83", + "@keymanapp/web-environment": "^15.0.83", + "@keymanapp/web-utils": "^15.0.83", "es6-shim": "^0.35.5", "string.prototype.codepointat": "^0.2.1", "string.prototype.startswith": "^0.2.0" diff --git a/developer/js/package-lock.json b/developer/js/package-lock.json index 6aeab2921e..21226c2edc 100644 --- a/developer/js/package-lock.json +++ b/developer/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "15.0.82", + "version": "15.0.83", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/developer/js/package.json b/developer/js/package.json index bb1f7d2e29..2efef2c387 100644 --- a/developer/js/package.json +++ b/developer/js/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "15.0.82", + "version": "15.0.83", "description": "Keyman Developer lexical model compiler", "keywords": [ "keyboard", @@ -37,14 +37,14 @@ "node": ">=12.0.0" }, "dependencies": { - "@keymanapp/models-types": "^15.0.82", + "@keymanapp/models-types": "^15.0.83", "commander": "^3.0.0", "typescript": "^3.8.3", "xml2js": "^0.4.19" }, "devDependencies": { - "@keymanapp/models-templates": "^15.0.82", - "@keymanapp/models-wordbreakers": "^15.0.82", + "@keymanapp/models-templates": "^15.0.83", + "@keymanapp/models-wordbreakers": "^15.0.83", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", diff --git a/lerna.json b/lerna.json index e0f5d5589e..77a1339bec 100644 --- a/lerna.json +++ b/lerna.json @@ -20,5 +20,5 @@ "ts-node", "typescript" ], - "version": "15.0.82" + "version": "15.0.83" } diff --git a/resources/gosh/package-lock.json b/resources/gosh/package-lock.json index a3d5f82b3e..cd03a2a1ac 100644 --- a/resources/gosh/package-lock.json +++ b/resources/gosh/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/resources-gosh", - "version": "15.0.82", + "version": "15.0.83", "lockfileVersion": 1 } diff --git a/resources/gosh/package.json b/resources/gosh/package.json index e40648ac1a..808f0fb999 100644 --- a/resources/gosh/package.json +++ b/resources/gosh/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/resources-gosh", - "version": "15.0.82", + "version": "15.0.83", "description": "Good Ol' Shell", "author": "Marc Durdin (https://github.com/mcdurdin)", "contributors": [ diff --git a/resources/web-environment/package-lock.json b/resources/web-environment/package-lock.json index 408b511608..8bd9879fe4 100644 --- a/resources/web-environment/package-lock.json +++ b/resources/web-environment/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/web-environment", - "version": "15.0.82", + "version": "15.0.83", "lockfileVersion": 1 } diff --git a/resources/web-environment/package.json b/resources/web-environment/package.json index d225ecbba8..2da593a10e 100644 --- a/resources/web-environment/package.json +++ b/resources/web-environment/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-environment", - "version": "15.0.82", + "version": "15.0.83", "description": "A compilation utility for passing select environment variables into our TS builds", "author": "Joshua Horton (https://github.com/jahorton)", "homepage": "https://github.com/keymanapp/keyman", @@ -17,6 +17,6 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/resources-gosh": "^15.0.82" + "@keymanapp/resources-gosh": "^15.0.83" } } diff --git a/web/package-lock.json b/web/package-lock.json index 4a6121652e..2f0fe167a7 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "15.0.82", + "version": "15.0.83", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/web/package.json b/web/package.json index 832495e550..f32b3bd385 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "15.0.82", + "version": "15.0.83", "description": "Facilitates text input in any language.", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^15.0.82", - "@keymanapp/web-sentry-manager": "^15.0.82", + "@keymanapp/resources-gosh": "^15.0.83", + "@keymanapp/web-sentry-manager": "^15.0.83", "@sentry/cli": "^1.52.3", "chai": "^4.3.4", "google-closure-compiler-java": "^20200224.0.0", @@ -52,13 +52,13 @@ "sentry-cli": "sentry-cli" }, "dependencies": { - "@keymanapp/input-processor": "^15.0.82", - "@keymanapp/keyboard-processor": "^15.0.82", - "@keymanapp/lexical-model-layer": "^15.0.82", - "@keymanapp/models-types": "^15.0.82", - "@keymanapp/recorder-core": "^15.0.82", - "@keymanapp/web-environment": "^15.0.82", - "@keymanapp/web-utils": "^15.0.82", + "@keymanapp/input-processor": "^15.0.83", + "@keymanapp/keyboard-processor": "^15.0.83", + "@keymanapp/lexical-model-layer": "^15.0.83", + "@keymanapp/models-types": "^15.0.83", + "@keymanapp/recorder-core": "^15.0.83", + "@keymanapp/web-environment": "^15.0.83", + "@keymanapp/web-utils": "^15.0.83", "@types/node": "^11.9.4", "es6-shim": "^0.35.5", "eventemitter3": "^4.0.0" From 3770b69692345e2347565e04abf797a8d4f47834 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Thu, 8 Jul 2021 10:00:17 +0700 Subject: [PATCH 54/58] fix(web): Copy the keyboard stubs for registration --- android/KMEA/app/src/main/assets/keyboard.html | 3 +-- web/source/keyboards/kmwkeyboards.ts | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/android/KMEA/app/src/main/assets/keyboard.html b/android/KMEA/app/src/main/assets/keyboard.html index a61321bd9d..a6a4acb0e1 100644 --- a/android/KMEA/app/src/main/assets/keyboard.html +++ b/android/KMEA/app/src/main/assets/keyboard.html @@ -130,8 +130,7 @@ function setKeymanLanguage(k) { KeymanWeb.registerStub(k); - // registerStub will mutate k.KI to have k.KP:: namespacing that setActiveKeyboard expects - keyman.setActiveKeyboard(k.KI, k.KLC); + keyman.setActiveKeyboard(k.KP + '::'+k.KI, k.KLC); keyman.osk.show(true); } diff --git a/web/source/keyboards/kmwkeyboards.ts b/web/source/keyboards/kmwkeyboards.ts index ca640752db..765a3d3252 100644 --- a/web/source/keyboards/kmwkeyboards.ts +++ b/web/source/keyboards/kmwkeyboards.ts @@ -1518,11 +1518,13 @@ namespace com.keyman.keyboards { * and a keyboard stub must be registered before the keyboard is loaded * for the keyboard to be usable. * - * @param {Object} Pstub Keyboard stub object + * @param {Object} kStub Keyboard stub object * @return {?number} 1 if already registered, else null */ - _registerStub(Pstub): number { + _registerStub(kStub): number { var Lk; + // Make a copy of the stubs to mutate and register + let Pstub = Object.assign({}, kStub); // In initialization not complete, list the stub to be registered on completion of initialization if(!this.keymanweb.initialized) { From 7d2f2cd928f60ddec0cb6db2b8145c4bbc858b2f Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Thu, 8 Jul 2021 09:04:31 +0200 Subject: [PATCH 55/58] refactor(common/core/desktop): Rename Load method Rename KMX_Environment::Load to KMX_Environment::Set. Fixes #5435. --- common/core/desktop/src/kmx/kmx_capslock.cpp | 10 +++++----- common/core/desktop/src/kmx/kmx_environment.cpp | 14 +++++++------- common/core/desktop/src/kmx/kmx_environment.h | 2 +- common/core/desktop/src/kmx/kmx_processor.cpp | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/common/core/desktop/src/kmx/kmx_capslock.cpp b/common/core/desktop/src/kmx/kmx_capslock.cpp index 63856be18d..cd11290af7 100644 --- a/common/core/desktop/src/kmx/kmx_capslock.cpp +++ b/common/core/desktop/src/kmx/kmx_capslock.cpp @@ -11,10 +11,10 @@ void KMX_ProcessEvent::ResetCapsLock(void) { DebugLog("ResetCapsLock: enter"); - if(m_keyboard.Keyboard->dwFlags & KF_CAPSALWAYSOFF) + if (m_keyboard.Keyboard->dwFlags & KF_CAPSALWAYSOFF) { DebugLog("ResetCapsLock: caps lock should be always off"); - if(m_environment.capsLock()) + if (m_environment.capsLock()) { DebugLog("ResetCapsLock: caps lock is on, switching off caps lock"); m_actions.QueueAction(QIT_CAPSLOCK, 0); @@ -26,14 +26,14 @@ void KMX_ProcessEvent::ResetCapsLock(void) void KMX_ProcessEvent::KeyCapsLockPress(KMX_BOOL FIsUp) { - if(m_keyboard.Keyboard->dwFlags & KF_CAPSONONLY) + if (m_keyboard.Keyboard->dwFlags & KF_CAPSONONLY) { if(FIsUp && !m_environment.capsLock()) { m_actions.QueueAction(QIT_CAPSLOCK, 1); } } - else if(m_keyboard.Keyboard->dwFlags & KF_CAPSALWAYSOFF) + else if (m_keyboard.Keyboard->dwFlags & KF_CAPSALWAYSOFF) { if(!FIsUp && m_environment.capsLock()) { @@ -47,7 +47,7 @@ void KMX_ProcessEvent::KeyShiftPress(KMX_BOOL FIsUp) { if(!m_environment.capsLock()) return; - if(m_keyboard.Keyboard->dwFlags & KF_SHIFTFREESCAPS) + if (m_keyboard.Keyboard->dwFlags & KF_SHIFTFREESCAPS) { if(!FIsUp) { diff --git a/common/core/desktop/src/kmx/kmx_environment.cpp b/common/core/desktop/src/kmx/kmx_environment.cpp index e2f7359469..1e166225c1 100644 --- a/common/core/desktop/src/kmx/kmx_environment.cpp +++ b/common/core/desktop/src/kmx/kmx_environment.cpp @@ -21,12 +21,12 @@ namespace { } KMX_Environment::KMX_Environment() { - Load(KM_KBP_KMX_ENV_PLATFORM, DEFAULT_PLATFORM); - Load(KM_KBP_KMX_ENV_BASELAYOUT, DEFAULT_BASELAYOUT); - Load(KM_KBP_KMX_ENV_BASELAYOUTALT, DEFAULT_BASELAYOUTALT); - Load(KM_KBP_KMX_ENV_SIMULATEALTGR, DEFAULT_SIMULATEALTGR); - Load(KM_KBP_KMX_ENV_CAPSLOCK, DEFAULT_CAPSLOCK); - Load(KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT, DEFAULT_BASELAYOUTGIVESCTRLRALTFORRALT); + Set(KM_KBP_KMX_ENV_PLATFORM, DEFAULT_PLATFORM); + Set(KM_KBP_KMX_ENV_BASELAYOUT, DEFAULT_BASELAYOUT); + Set(KM_KBP_KMX_ENV_BASELAYOUTALT, DEFAULT_BASELAYOUTALT); + Set(KM_KBP_KMX_ENV_SIMULATEALTGR, DEFAULT_SIMULATEALTGR); + Set(KM_KBP_KMX_ENV_CAPSLOCK, DEFAULT_CAPSLOCK); + Set(KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT, DEFAULT_BASELAYOUTGIVESCTRLRALTFORRALT); } @@ -59,7 +59,7 @@ char16_t const * KMX_Environment::LookUp(std::u16string const & key) const { } -void KMX_Environment::Load(std::u16string const & key, std::u16string const & value) { +void KMX_Environment::Set(std::u16string const & key, std::u16string const & value) { assert(!key.empty()); if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_PLATFORM)) { diff --git a/common/core/desktop/src/kmx/kmx_environment.h b/common/core/desktop/src/kmx/kmx_environment.h index c8c0e212df..f1ee70faf6 100644 --- a/common/core/desktop/src/kmx/kmx_environment.h +++ b/common/core/desktop/src/kmx/kmx_environment.h @@ -20,7 +20,7 @@ private: km_kbp_cp const * default_value); public: KMX_Environment(); - void Load(std::u16string const & key, std::u16string const & value); + void Set(std::u16string const & key, std::u16string const & value); char16_t const * LookUp(std::u16string const & key) const; KMX_BOOL capsLock() const noexcept { return _capsLock; } diff --git a/common/core/desktop/src/kmx/kmx_processor.cpp b/common/core/desktop/src/kmx/kmx_processor.cpp index 14bc4b2756..18ec00d067 100644 --- a/common/core/desktop/src/kmx/kmx_processor.cpp +++ b/common/core/desktop/src/kmx/kmx_processor.cpp @@ -61,7 +61,7 @@ namespace km { persisted_store()[key] = value; break; case KM_KBP_OPT_ENVIRONMENT: - _kmx.GetEnvironment()->Load(key, value); + _kmx.GetEnvironment()->Set(key, value); break; default: return option(); From ec092ba1e341f70746a1d98634718e867221ee4d Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Thu, 8 Jul 2021 14:14:40 +0700 Subject: [PATCH 56/58] chore(web): Change syntax for clone --- web/source/keyboards/kmwkeyboards.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/web/source/keyboards/kmwkeyboards.ts b/web/source/keyboards/kmwkeyboards.ts index 765a3d3252..5640032681 100644 --- a/web/source/keyboards/kmwkeyboards.ts +++ b/web/source/keyboards/kmwkeyboards.ts @@ -1518,13 +1518,12 @@ namespace com.keyman.keyboards { * and a keyboard stub must be registered before the keyboard is loaded * for the keyboard to be usable. * - * @param {Object} kStub Keyboard stub object + * @param {Object} Pstub Keyboard stub object * @return {?number} 1 if already registered, else null */ - _registerStub(kStub): number { + _registerStub(Pstub): number { var Lk; - // Make a copy of the stubs to mutate and register - let Pstub = Object.assign({}, kStub); + Pstub = { ... Pstub}; // shallow clone the stub object // In initialization not complete, list the stub to be registered on completion of initialization if(!this.keymanweb.initialized) { From 603d1a473185cb2df327e13c0c653e46ed889faf Mon Sep 17 00:00:00 2001 From: Keyman Build Agent Date: Thu, 8 Jul 2021 14:02:37 -0400 Subject: [PATCH 57/58] auto: increment master version to 15.0.84 --- HISTORY.md | 12 +++++++++++ VERSION.md | 2 +- .../web/input-processor/package-lock.json | 2 +- common/core/web/input-processor/package.json | 16 +++++++-------- .../web/keyboard-processor/package-lock.json | 2 +- .../core/web/keyboard-processor/package.json | 10 +++++----- .../core/web/tools/recorder/package-lock.json | 2 +- common/core/web/tools/recorder/package.json | 10 +++++----- .../tools/sentry-manager/package-lock.json | 2 +- .../web/tools/sentry-manager/package.json | 4 ++-- common/core/web/utils/package-lock.json | 2 +- common/core/web/utils/package.json | 6 +++--- common/models/templates/package-lock.json | 2 +- common/models/templates/package.json | 8 ++++---- common/models/types/package-lock.json | 2 +- common/models/types/package.json | 2 +- common/models/wordbreakers/package-lock.json | 2 +- common/models/wordbreakers/package.json | 4 ++-- common/predictive-text/package-lock.json | 2 +- common/predictive-text/package.json | 14 ++++++------- developer/js/package-lock.json | 2 +- developer/js/package.json | 8 ++++---- lerna.json | 2 +- resources/gosh/package-lock.json | 2 +- resources/gosh/package.json | 2 +- resources/web-environment/package-lock.json | 2 +- resources/web-environment/package.json | 4 ++-- web/package-lock.json | 2 +- web/package.json | 20 +++++++++---------- 29 files changed, 81 insertions(+), 69 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index b4b988a49d..59feb28784 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,17 @@ # Keyman Version History +## 15.0.83 alpha 2021-07-08 + +* change(ios): setText now uses JSON serialization (#5321) +* feat(common/core/web): engine correction-prep optimizations (#5319) +* change(web): fat-finger-performance unit-test threshold (#5404) +* feat(web): spacebar text controls (#5348) +* chore(windows): remove backup build step (#5434) +* refactor(common/core/desktop): Fix file and class names 🚧 (#5445) +* refactor(web): longpresses, groundwork for additional gestures 🍕 (#5387) +* fix(android/engine): Display dictionary help link (#5427) +* refactor(common/core/desktop): Rename Load method 🚧 (#5444) + ## 15.0.82 alpha 2021-07-07 * fix(common/models/types): fixes test script config (#5441) diff --git a/VERSION.md b/VERSION.md index 5c3cbd6701..e0317f8c26 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -15.0.83 \ No newline at end of file +15.0.84 \ No newline at end of file diff --git a/common/core/web/input-processor/package-lock.json b/common/core/web/input-processor/package-lock.json index 598c997656..b263172ee1 100644 --- a/common/core/web/input-processor/package-lock.json +++ b/common/core/web/input-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "15.0.83", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/core/web/input-processor/package.json b/common/core/web/input-processor/package.json index 8cf312d46a..8664c94d23 100644 --- a/common/core/web/input-processor/package.json +++ b/common/core/web/input-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "15.0.83", + "version": "15.0.84", "description": "The core text and prediction processing engine for KeymanWeb", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/lexical-model-compiler": "^15.0.83", - "@keymanapp/resources-gosh": "^15.0.83", + "@keymanapp/lexical-model-compiler": "^15.0.84", + "@keymanapp/resources-gosh": "^15.0.84", "@types/node": "^11.9.4", "chai": "^4.3.4", "mocha": "^8.4.0", @@ -34,11 +34,11 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/keyboard-processor": "^15.0.83", - "@keymanapp/lexical-model-layer": "^15.0.83", - "@keymanapp/models-types": "^15.0.83", - "@keymanapp/web-environment": "^15.0.83", - "@keymanapp/web-utils": "^15.0.83", + "@keymanapp/keyboard-processor": "^15.0.84", + "@keymanapp/lexical-model-layer": "^15.0.84", + "@keymanapp/models-types": "^15.0.84", + "@keymanapp/web-environment": "^15.0.84", + "@keymanapp/web-utils": "^15.0.84", "eventemitter3": "^4.0.0" } } diff --git a/common/core/web/keyboard-processor/package-lock.json b/common/core/web/keyboard-processor/package-lock.json index 6966187337..147045dae3 100644 --- a/common/core/web/keyboard-processor/package-lock.json +++ b/common/core/web/keyboard-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "15.0.83", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/core/web/keyboard-processor/package.json b/common/core/web/keyboard-processor/package.json index 66ad0570ff..ab94677948 100644 --- a/common/core/web/keyboard-processor/package.json +++ b/common/core/web/keyboard-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "15.0.83", + "version": "15.0.84", "description": "Core module for Keyman keyboard support in KeymanWeb.", "repository": { "type": "git", @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^15.0.83", + "@keymanapp/resources-gosh": "^15.0.84", "chai": "^4.3.4", "mocha": "^8.4.0", "mocha-teamcity-reporter": "^4.0.0", @@ -32,9 +32,9 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/models-types": "^15.0.83", - "@keymanapp/web-environment": "^15.0.83", - "@keymanapp/web-utils": "^15.0.83", + "@keymanapp/models-types": "^15.0.84", + "@keymanapp/web-environment": "^15.0.84", + "@keymanapp/web-utils": "^15.0.84", "@types/node": "^11.9.4" } } diff --git a/common/core/web/tools/recorder/package-lock.json b/common/core/web/tools/recorder/package-lock.json index 8616a8eea0..3425b059ea 100644 --- a/common/core/web/tools/recorder/package-lock.json +++ b/common/core/web/tools/recorder/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "15.0.83", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/core/web/tools/recorder/package.json b/common/core/web/tools/recorder/package.json index fdc666752a..7bb39e4af8 100644 --- a/common/core/web/tools/recorder/package.json +++ b/common/core/web/tools/recorder/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "15.0.83", + "version": "15.0.84", "description": "Core classes used to develop KeymanWeb test cases based on keystrokes", "main": "index.js", "scripts": { @@ -17,10 +17,10 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "dependencies": { - "@keymanapp/keyboard-processor": "^15.0.83", - "@keymanapp/models-types": "^15.0.83", - "@keymanapp/web-environment": "^15.0.83", - "@keymanapp/web-utils": "^15.0.83", + "@keymanapp/keyboard-processor": "^15.0.84", + "@keymanapp/models-types": "^15.0.84", + "@keymanapp/web-environment": "^15.0.84", + "@keymanapp/web-utils": "^15.0.84", "@types/node": "^11.9.4" }, "devDependencies": { diff --git a/common/core/web/tools/sentry-manager/package-lock.json b/common/core/web/tools/sentry-manager/package-lock.json index 6b0e06932e..87cab704e6 100644 --- a/common/core/web/tools/sentry-manager/package-lock.json +++ b/common/core/web/tools/sentry-manager/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "15.0.83", + "version": "15.0.84", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/sentry-manager/package.json b/common/core/web/tools/sentry-manager/package.json index 43d3041c34..d92e9ade3f 100644 --- a/common/core/web/tools/sentry-manager/package.json +++ b/common/core/web/tools/sentry-manager/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "15.0.83", + "version": "15.0.84", "description": "Common setup for KeymanWeb's Sentry-based error reporting", "main": "dist/index.js", "scripts": { @@ -20,7 +20,7 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/web-environment": "^15.0.83", + "@keymanapp/web-environment": "^15.0.84", "@sentry/browser": "^5.27.4" } } diff --git a/common/core/web/utils/package-lock.json b/common/core/web/utils/package-lock.json index 497858dee7..af4acd9319 100644 --- a/common/core/web/utils/package-lock.json +++ b/common/core/web/utils/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "15.0.83", + "version": "15.0.84", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/utils/package.json b/common/core/web/utils/package.json index 096af0137c..54de85e484 100644 --- a/common/core/web/utils/package.json +++ b/common/core/web/utils/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "15.0.83", + "version": "15.0.84", "description": "Common utility functions used throughout other Keyman packages", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -20,8 +20,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^15.0.83", - "@keymanapp/web-environment": "^15.0.83", + "@keymanapp/resources-gosh": "^15.0.84", + "@keymanapp/web-environment": "^15.0.84", "@types/node": "^14.0.5", "typescript": "^3.8.3" } diff --git a/common/models/templates/package-lock.json b/common/models/templates/package-lock.json index 6866bc4c5b..4b3f57c598 100644 --- a/common/models/templates/package-lock.json +++ b/common/models/templates/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "15.0.83", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/models/templates/package.json b/common/models/templates/package.json index 9a0c75a776..efda44b4a1 100644 --- a/common/models/templates/package.json +++ b/common/models/templates/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "15.0.83", + "version": "15.0.84", "description": "Backing model implementations (templates) for Keyman's modelling layer", "keywords": [ "lm", @@ -42,8 +42,8 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^15.0.83", - "@keymanapp/web-utils": "^15.0.83", + "@keymanapp/models-types": "^15.0.84", + "@keymanapp/web-utils": "^15.0.84", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "@types/node": "^14.0.4", @@ -52,6 +52,6 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-wordbreakers": "^15.0.83" + "@keymanapp/models-wordbreakers": "^15.0.84" } } diff --git a/common/models/types/package-lock.json b/common/models/types/package-lock.json index 4eea6fd94a..2db4fccbf0 100644 --- a/common/models/types/package-lock.json +++ b/common/models/types/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "15.0.83", + "version": "15.0.84", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/types/package.json b/common/models/types/package.json index 10cbdbd728..1dbfa29e1a 100644 --- a/common/models/types/package.json +++ b/common/models/types/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "15.0.83", + "version": "15.0.84", "description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.", "types": "./index.d.ts", "scripts": { diff --git a/common/models/wordbreakers/package-lock.json b/common/models/wordbreakers/package-lock.json index d6b00d8c4f..aba2382baa 100644 --- a/common/models/wordbreakers/package-lock.json +++ b/common/models/wordbreakers/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "15.0.83", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/models/wordbreakers/package.json b/common/models/wordbreakers/package.json index 2fd7eebf4b..e379a2a6ff 100644 --- a/common/models/wordbreakers/package.json +++ b/common/models/wordbreakers/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "15.0.83", + "version": "15.0.84", "description": "Word breakers bundled in the modeling layer", "keywords": [ "lexical model", @@ -40,7 +40,7 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^15.0.83", + "@keymanapp/models-types": "^15.0.84", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "chai": "^4.3.4", diff --git a/common/predictive-text/package-lock.json b/common/predictive-text/package-lock.json index 62c6da3066..bacc164e2d 100644 --- a/common/predictive-text/package-lock.json +++ b/common/predictive-text/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "15.0.83", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/predictive-text/package.json b/common/predictive-text/package.json index 0b27eeb1b4..a21b30afdd 100644 --- a/common/predictive-text/package.json +++ b/common/predictive-text/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "15.0.83", + "version": "15.0.84", "description": "Keyman/Predictive Text integration layer", "main": "build/index.js", "scripts": { @@ -21,8 +21,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/models-types": "^15.0.83", - "@keymanapp/resources-gosh": "^15.0.83", + "@keymanapp/models-types": "^15.0.84", + "@keymanapp/resources-gosh": "^15.0.84", "@types/node": "^10.17.21", "chai": "^4.3.4", "karma": "^6.3.4", @@ -46,10 +46,10 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-templates": "^15.0.83", - "@keymanapp/models-wordbreakers": "^15.0.83", - "@keymanapp/web-environment": "^15.0.83", - "@keymanapp/web-utils": "^15.0.83", + "@keymanapp/models-templates": "^15.0.84", + "@keymanapp/models-wordbreakers": "^15.0.84", + "@keymanapp/web-environment": "^15.0.84", + "@keymanapp/web-utils": "^15.0.84", "es6-shim": "^0.35.5", "string.prototype.codepointat": "^0.2.1", "string.prototype.startswith": "^0.2.0" diff --git a/developer/js/package-lock.json b/developer/js/package-lock.json index 21226c2edc..bae7464bbb 100644 --- a/developer/js/package-lock.json +++ b/developer/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "15.0.83", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/developer/js/package.json b/developer/js/package.json index 2efef2c387..ca083f2bff 100644 --- a/developer/js/package.json +++ b/developer/js/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "15.0.83", + "version": "15.0.84", "description": "Keyman Developer lexical model compiler", "keywords": [ "keyboard", @@ -37,14 +37,14 @@ "node": ">=12.0.0" }, "dependencies": { - "@keymanapp/models-types": "^15.0.83", + "@keymanapp/models-types": "^15.0.84", "commander": "^3.0.0", "typescript": "^3.8.3", "xml2js": "^0.4.19" }, "devDependencies": { - "@keymanapp/models-templates": "^15.0.83", - "@keymanapp/models-wordbreakers": "^15.0.83", + "@keymanapp/models-templates": "^15.0.84", + "@keymanapp/models-wordbreakers": "^15.0.84", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", diff --git a/lerna.json b/lerna.json index 77a1339bec..6ecc7ec258 100644 --- a/lerna.json +++ b/lerna.json @@ -20,5 +20,5 @@ "ts-node", "typescript" ], - "version": "15.0.83" + "version": "15.0.84" } diff --git a/resources/gosh/package-lock.json b/resources/gosh/package-lock.json index cd03a2a1ac..cc27ea08bf 100644 --- a/resources/gosh/package-lock.json +++ b/resources/gosh/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/resources-gosh", - "version": "15.0.83", + "version": "15.0.84", "lockfileVersion": 1 } diff --git a/resources/gosh/package.json b/resources/gosh/package.json index 808f0fb999..26cfffb979 100644 --- a/resources/gosh/package.json +++ b/resources/gosh/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/resources-gosh", - "version": "15.0.83", + "version": "15.0.84", "description": "Good Ol' Shell", "author": "Marc Durdin (https://github.com/mcdurdin)", "contributors": [ diff --git a/resources/web-environment/package-lock.json b/resources/web-environment/package-lock.json index 8bd9879fe4..ec47e489d8 100644 --- a/resources/web-environment/package-lock.json +++ b/resources/web-environment/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/web-environment", - "version": "15.0.83", + "version": "15.0.84", "lockfileVersion": 1 } diff --git a/resources/web-environment/package.json b/resources/web-environment/package.json index 2da593a10e..b48d1d379e 100644 --- a/resources/web-environment/package.json +++ b/resources/web-environment/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-environment", - "version": "15.0.83", + "version": "15.0.84", "description": "A compilation utility for passing select environment variables into our TS builds", "author": "Joshua Horton (https://github.com/jahorton)", "homepage": "https://github.com/keymanapp/keyman", @@ -17,6 +17,6 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/resources-gosh": "^15.0.83" + "@keymanapp/resources-gosh": "^15.0.84" } } diff --git a/web/package-lock.json b/web/package-lock.json index 2f0fe167a7..60da2d0797 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "15.0.83", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/web/package.json b/web/package.json index f32b3bd385..f3488d8524 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "15.0.83", + "version": "15.0.84", "description": "Facilitates text input in any language.", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^15.0.83", - "@keymanapp/web-sentry-manager": "^15.0.83", + "@keymanapp/resources-gosh": "^15.0.84", + "@keymanapp/web-sentry-manager": "^15.0.84", "@sentry/cli": "^1.52.3", "chai": "^4.3.4", "google-closure-compiler-java": "^20200224.0.0", @@ -52,13 +52,13 @@ "sentry-cli": "sentry-cli" }, "dependencies": { - "@keymanapp/input-processor": "^15.0.83", - "@keymanapp/keyboard-processor": "^15.0.83", - "@keymanapp/lexical-model-layer": "^15.0.83", - "@keymanapp/models-types": "^15.0.83", - "@keymanapp/recorder-core": "^15.0.83", - "@keymanapp/web-environment": "^15.0.83", - "@keymanapp/web-utils": "^15.0.83", + "@keymanapp/input-processor": "^15.0.84", + "@keymanapp/keyboard-processor": "^15.0.84", + "@keymanapp/lexical-model-layer": "^15.0.84", + "@keymanapp/models-types": "^15.0.84", + "@keymanapp/recorder-core": "^15.0.84", + "@keymanapp/web-environment": "^15.0.84", + "@keymanapp/web-utils": "^15.0.84", "@types/node": "^11.9.4", "es6-shim": "^0.35.5", "eventemitter3": "^4.0.0" From c40783624aa681f4a1e01ebbc32599cd74951403 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Fri, 9 Jul 2021 06:29:25 +0700 Subject: [PATCH 58/58] chore(android/engine): Revert test code --- .../KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index 9b32aa91f5..1461039a60 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -152,7 +152,7 @@ public final class KMManager { private static boolean didLogHardwareKeystrokeException = false; - private static GlobeKeyAction inappKbGlobeKeyAction = GlobeKeyAction.GLOBE_KEY_ACTION_SWITCH_TO_NEXT_KEYBOARD; // For testing only. Do not merge to production + private static GlobeKeyAction inappKbGlobeKeyAction = GlobeKeyAction.GLOBE_KEY_ACTION_SHOW_MENU; private static GlobeKeyAction sysKbGlobeKeyAction = GlobeKeyAction.GLOBE_KEY_ACTION_SHOW_MENU; // This is used to keep track of the starting system keyboard index while the screen is locked private static int sysKbStartingIndexOnLockScreen = -1;