From da27d16ae6ee79e807272fd6885a784773bd452b Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Fri, 7 Dec 2018 09:29:09 +0700 Subject: [PATCH 01/24] Extracted key generation code into its own class. --- web/source/kmwosk.ts | 320 ++++++++++++++++++++++++------------------- 1 file changed, 179 insertions(+), 141 deletions(-) diff --git a/web/source/kmwosk.ts b/web/source/kmwosk.ts index bbcdfac6d0..93df7c933b 100644 --- a/web/source/kmwosk.ts +++ b/web/source/kmwosk.ts @@ -9,6 +9,8 @@ namespace com.keyman { width: string; nextlayer?: string; pad?: string; + widthpc?: number; + padpc?: number; constructor(id: string, text?: string, width?: string, sp?: string, nextlayer?: string, pad?: string) { this.id = id; @@ -19,6 +21,175 @@ namespace com.keyman { this.pad = pad; } } + + export class OSKKey { + spec: OSKKeySpec; + + private static keyman: KeymanBase; + + constructor(spec: OSKKeySpec) { + this.spec = spec; + } + + construct(keyman: KeymanBase, layout, layer, rowStyle: CSSStyleDeclaration, totalPercent: number): {element: HTMLDivElement, percent: number} { + OSKKey.keyman = keyman; + let util = keyman.util; + let osk = keyman['osk']; + let spec = this.spec; + let isDesktop = util.device.formFactor == 'desktop' + + let kDiv=util._CreateElement('div'); + kDiv['keyId']=spec['id']; + kDiv.className='kmw-key-square'; + + let ks=kDiv.style; + ks.width=this.objectUnits(spec['widthpc']); + + let originalPercent = totalPercent; + + if(!isDesktop) { + // Regularize interkey spacing by rounding key width and padding (Build 390) + //keys[j]['padpc']=Math.round(keys[j]['padpc']); + //keys[j]['widthpc']=Math.round(keys[j]['widthpc']); + ks.left=this.objectUnits(totalPercent+spec['padpc']); + ks.bottom=rowStyle.bottom; + ks.height=rowStyle.height; //must be specified in px for rest of layout to work correctly + } else { + ks.marginLeft=this.objectUnits(spec['padpc']); + } + + totalPercent=totalPercent+spec['padpc']+spec['widthpc']; + + let btn=util._CreateElement('div'); + + // Set button class + osk.setButtonClass(spec,btn,layout); + + // Set distinct phone and tablet button position properties + if(!isDesktop) { + btn.style.left=ks.left; + btn.style.width=ks.width; + } + + // Add the (US English) keycap label for desktop OSK or if KDU flag is non-zero + var q=null; + if(layout.keyLabels || isDesktop) { //desktop or KDU flag set + // Create the default key cap labels (letter keys, etc.) + var x=osk.keyCodes[spec.id]; + switch(x) { + case 186: x=59; break; + case 187: x=61; break; + case 188: x=44; break; + case 189: x=45; break; + case 190: x=46; break; + case 191: x=47; break; + case 192: x=96; break; + case 219: x=91; break; + case 220: x=92; break; + case 221: x=93; break; + case 222: x=39; break; + default: + if(x < 48 || x > 90) { + x=0; + } + } + + if(x > 0) { + q=util._CreateElement('div'); + q.className='kmw-key-label'; + q.innerHTML=String.fromCharCode(x); + //kDiv.appendChild(q); + btn.appendChild(q); + } + } + + // Define each key element id by layer id and key id (duplicate possible for SHIFT - does it matter?) + btn.id=layer['id']+'-'+spec.id; + // TODO: convert to 'this' instead. + btn['key']=spec; //attach reference to key layout spec to element + + // Add reference to subkey array if defined + if(typeof spec['sk'] != 'undefined' && spec['sk'] != null) { + var bsn,bsk=btn['subKeys']=spec['sk']; + for(bsn=0; bsn 90) x=0; - } - - if(x > 0) - { - q=util._CreateElement('DIV'); - q.className='kmw-key-label'; - q.innerHTML=String.fromCharCode(x); - //kDiv.appendChild(q); - btn.appendChild(q); - } - } - - // Define each key element id by layer id and key id (duplicate possible for SHIFT - does it matter?) - btn.id=layer['id']+'-'+key.id; - btn.key=key; //attach reference to key layout spec to element - - // Add reference to subkey array if defined - if(typeof key['sk'] != 'undefined' && key['sk'] != null) - { - var bsn,bsk=btn.subKeys=key['sk']; - for(bsn=0; bsn Date: Fri, 7 Dec 2018 10:02:56 +0700 Subject: [PATCH 02/24] Fragmented key generation into a few distinct functions. --- web/source/kmwosk.ts | 206 +++++++++++++++++++++++++------------------ 1 file changed, 118 insertions(+), 88 deletions(-) diff --git a/web/source/kmwosk.ts b/web/source/kmwosk.ts index 93df7c933b..a4631af632 100644 --- a/web/source/kmwosk.ts +++ b/web/source/kmwosk.ts @@ -31,7 +31,102 @@ namespace com.keyman { this.spec = spec; } - construct(keyman: KeymanBase, layout, layer, rowStyle: CSSStyleDeclaration, totalPercent: number): {element: HTMLDivElement, percent: number} { + // Produces a small reference label for the corresponding physical key on a US keyboard. + private generateKeyCapLabel(): HTMLDivElement { + // Create the default key cap labels (letter keys, etc.) + var x=OSKKey.keyman['osk'].keyCodes[this.spec.id]; + switch(x) { + case 186: x=59; break; + case 187: x=61; break; + case 188: x=44; break; + case 189: x=45; break; + case 190: x=46; break; + case 191: x=47; break; + case 192: x=96; break; + case 219: x=91; break; + case 220: x=92; break; + case 221: x=93; break; + case 222: x=39; break; + default: + if(x < 48 || x > 90) { + x=0; + } + } + + if(x > 0) { + let q=OSKKey.keyman.util._CreateElement('div'); + q.className='kmw-key-label'; + q.innerHTML=String.fromCharCode(x); + return q; + } else { + // Keyman-only virtual keys have no corresponding physical key. + return null; + } + } + + // Produces a HTMLSpanElement with the key's actual text. + private generateKeyText(layerId: string): HTMLSpanElement { + let util = OSKKey.keyman.util; + let spec = this.spec; + let osk = OSKKey.keyman['osk']; + + // Add OSK key labels + var t=util._CreateElement('span'), ts=t.style; + if(spec['text'] == null || spec['text'] == '') { + t.innerHTML='\xa0'; // default: nbsp. + if(typeof spec['id'] == 'string') { + // If the ID's Unicode-based, just use that code. + if(/^U_[0-9A-F]{4}$/i.test(spec['id'])) { + t.innerHTML=String.fromCharCode(parseInt(spec['id'].substr(2),16)); + } + } + } else { + t.innerHTML=spec['text']; + } + t.className='kmw-key-text'; + + // Use special case lookup for modifier keys + if(spec['sp'] == '1' || spec['sp'] == '2') { + // Unique layer-based transformation. + var tId=((spec['text'] == '*Tab*' && layerId == 'shift') ? '*TabLeft*' : spec['text']); + + // Transforms our *___* special key codes into their corresponding PUA character codes for keyboard display. + t.innerHTML=osk.renameSpecialKey(tId); + } + + //Override font spec if set for this key in the layout + if('font' in spec) { + ts.fontFamily=spec['font']; + } + if('fontsize' in spec) { + ts.fontSize=spec['fontsize']; + } + + return t; + } + + private processSubkeys(btn: HTMLDivElement) { + let spec = this.spec; + let osk = OSKKey.keyman['osk']; + + // Add reference to subkey array if defined + var bsn: number, bsk=btn['subKeys'] = spec['sk']; + // Transform any special keys into their PUA representations. + for(bsn=0; bsn 90) { - x=0; - } - } + if(layout.keyLabels || isDesktop) { + let keyCap = this.generateKeyCapLabel(); - if(x > 0) { - q=util._CreateElement('div'); - q.className='kmw-key-label'; - q.innerHTML=String.fromCharCode(x); - //kDiv.appendChild(q); - btn.appendChild(q); + if(keyCap) { + btn.appendChild(keyCap); } } // Define each key element id by layer id and key id (duplicate possible for SHIFT - does it matter?) - btn.id=layer['id']+'-'+spec.id; - // TODO: convert to 'this' instead. + btn.id=layerId+'-'+spec.id; + // TODO: convert btn['key'] to use the 'this' reference instead. btn['key']=spec; //attach reference to key layout spec to element - // Add reference to subkey array if defined - if(typeof spec['sk'] != 'undefined' && spec['sk'] != null) { - var bsn,bsk=btn['subKeys']=spec['sk']; - for(bsn=0; bsn Date: Fri, 7 Dec 2018 10:55:20 +0700 Subject: [PATCH 03/24] Performs basic extraction of subkey code, mild polymorphism. --- web/source/kmwosk.ts | 274 ++++++++++++++++++++++++------------------- 1 file changed, 153 insertions(+), 121 deletions(-) diff --git a/web/source/kmwosk.ts b/web/source/kmwosk.ts index a4631af632..a03ed080a1 100644 --- a/web/source/kmwosk.ts +++ b/web/source/kmwosk.ts @@ -25,50 +25,27 @@ namespace com.keyman { export class OSKKey { spec: OSKKeySpec; - private static keyman: KeymanBase; - constructor(spec: OSKKeySpec) { this.spec = spec; } - // Produces a small reference label for the corresponding physical key on a US keyboard. - private generateKeyCapLabel(): HTMLDivElement { - // Create the default key cap labels (letter keys, etc.) - var x=OSKKey.keyman['osk'].keyCodes[this.spec.id]; - switch(x) { - case 186: x=59; break; - case 187: x=61; break; - case 188: x=44; break; - case 189: x=45; break; - case 190: x=46; break; - case 191: x=47; break; - case 192: x=96; break; - case 219: x=91; break; - case 220: x=92; break; - case 221: x=93; break; - case 222: x=39; break; - default: - if(x < 48 || x > 90) { - x=0; - } - } - - if(x > 0) { - let q=OSKKey.keyman.util._CreateElement('div'); - q.className='kmw-key-label'; - q.innerHTML=String.fromCharCode(x); - return q; - } else { - // Keyman-only virtual keys have no corresponding physical key. - return null; - } + /** + * Replace default key names by special font codes for modifier keys + * + * @param {string} oldText + * @return {string} + **/ + protected renameSpecialKey(oldText: string): string { + let keyman = (window['keyman']) + // If a 'special key' mapping exists for the text, replace it with its corresponding special OSK character. + let specialCharacters = keyman['osk'].specialCharacters; + return specialCharacters[oldText] ? String.fromCharCode(0XE000 + specialCharacters[oldText]) : oldText; } // Produces a HTMLSpanElement with the key's actual text. - private generateKeyText(layerId: string): HTMLSpanElement { - let util = OSKKey.keyman.util; + protected generateKeyText(layerId: string): HTMLSpanElement { + let util = (window['keyman']).util; let spec = this.spec; - let osk = OSKKey.keyman['osk']; // Add OSK key labels var t=util._CreateElement('span'), ts=t.style; @@ -91,7 +68,7 @@ namespace com.keyman { var tId=((spec['text'] == '*Tab*' && layerId == 'shift') ? '*TabLeft*' : spec['text']); // Transforms our *___* special key codes into their corresponding PUA character codes for keyboard display. - t.innerHTML=osk.renameSpecialKey(tId); + t.innerHTML=this.renameSpecialKey(tId); } //Override font spec if set for this key in the layout @@ -104,10 +81,48 @@ namespace com.keyman { return t; } + } - private processSubkeys(btn: HTMLDivElement) { + export class OSKBaseKey extends OSKKey { + constructor(spec: OSKKeySpec) { + super(spec); + } + + // Produces a small reference label for the corresponding physical key on a US keyboard. + protected generateKeyCapLabel(): HTMLDivElement { + // Create the default key cap labels (letter keys, etc.) + var x = (window['keyman'])['osk'].keyCodes[this.spec.id]; + switch(x) { + case 186: x=59; break; + case 187: x=61; break; + case 188: x=44; break; + case 189: x=45; break; + case 190: x=46; break; + case 191: x=47; break; + case 192: x=96; break; + case 219: x=91; break; + case 220: x=92; break; + case 221: x=93; break; + case 222: x=39; break; + default: + if(x < 48 || x > 90) { + x=0; + } + } + + if(x > 0) { + let q = (window['keyman']).util._CreateElement('div'); + q.className='kmw-key-label'; + q.innerHTML=String.fromCharCode(x); + return q; + } else { + // Keyman-only virtual keys have no corresponding physical key. + return null; + } + } + + protected processSubkeys(btn: HTMLDivElement) { let spec = this.spec; - let osk = OSKKey.keyman['osk']; // Add reference to subkey array if defined var bsn: number, bsk=btn['subKeys'] = spec['sk']; @@ -115,19 +130,19 @@ namespace com.keyman { for(bsn=0; bsnwindow['keyman']).util._CreateElement('div'); skIcon.className='kmw-key-popup-icon'; //kDiv.appendChild(skIcon); btn.appendChild(skIcon); } - construct(keyman: KeymanBase, layout, layerId: string, rowStyle: CSSStyleDeclaration, totalPercent: number): {element: HTMLDivElement, percent: number} { - OSKKey.keyman = keyman; + construct(layout, layerId: string, rowStyle: CSSStyleDeclaration, totalPercent: number): {element: HTMLDivElement, percent: number} { + let keyman = (window['keyman']) let util = keyman.util; let osk = keyman['osk']; let spec = this.spec; @@ -205,7 +220,7 @@ namespace com.keyman { } objectUnits(v: number) { - if(OSKKey.keyman.util.device.formFactor == 'desktop') { + if((window['keyman']).util.device.formFactor == 'desktop') { return v + '%'; } else { return Math.round(v)+'px'; @@ -213,13 +228,97 @@ namespace com.keyman { } objectWidth() { - if(OSKKey.keyman.util.device.formFactor == 'desktop') { + if((window['keyman']).util.device.formFactor == 'desktop') { return 100; } else { return keyman['osk'].getWidth(); } } } + + export class OSKSubKey extends OSKKey { + constructor(spec: OSKKeySpec) { + super(spec); + } + + construct(baseKey: HTMLDivElement, topMargin: boolean): HTMLDivElement { + let osk = ( window['keyman']).osk; + let spec = this.spec; + + let kDiv=document.createElement('div'); + let tKey = osk.getDefaultKeyObject(); + let ks=kDiv.style; + + for(var tp in tKey) { + if(typeof spec[tp] != 'string') { + spec[tp]=tKey[tp]; + } + } + + kDiv.className='kmw-key-square-ex'; + kDiv['keyId']=spec['id']; + if(topMargin) { + ks.marginTop='5px'; + } + + if(typeof spec['width'] != 'undefined') { + ks.width=(parseInt(spec['width'],10)*baseKey.offsetWidth/100)+'px'; + } else { + ks.width=baseKey.offsetWidth+'px'; + } + ks.height=baseKey.offsetHeight+'px'; + + let btn=document.createElement('div'); + osk.setButtonClass(spec,btn); + + // Create (temporarily) unique ID by prefixing 'popup-' to actual key ID + if(typeof(spec['layer']) == 'string' && spec['layer'] != '') { + btn.id='popup-'+spec['layer']+'-'+spec['id']; + } else { + btn.id='popup-' + osk.layerId + '-'+spec['id']; + } + + // TODO: Swap to use the 'this' reference. + btn['key'] = spec; + + // Must set button size (in px) dynamically, not from CSS + let bs=btn.style; + bs.height=ks.height; + bs.width=ks.width; + + // Must set position explicitly, at least for Android + bs.position='absolute'; + + /// + let t=(window['keyman']).util._CreateElement('span'); + t.className='kmw-key-text'; + if(spec['text'] == null || spec['text'] == '') { + t.innerHTML='\xa0'; + if(typeof spec['id'] == 'string') { + if(/^U_[0-9A-F]{4}$/i.test(spec['id'])) { + t.innerHTML=String.fromCharCode(parseInt(spec['id'].substr(2),16)); + } + } + } else { + t.innerHTML=spec['text']; + } + + // Override the font name and size if set in the layout + let ts=t.style; + ts.fontSize=osk.fontSize; //Build 344, KMEW-90 + if(typeof spec['font'] == 'string' && spec['font'] != '') { + ts.fontFamily=spec['font']; + } + if(typeof spec['fontsize'] == 'string' && spec['fontsize'] != 0) { + ts.fontSize=spec['fontsize']; + } + + btn.appendChild(t); + kDiv.appendChild(btn); + + return kDiv; + } + } } /*** @@ -841,72 +940,16 @@ if(!window['keyman']['initialized']) { // Add nested button elements for each sub-key for(i=0; i 1 && nRow > 0) { - ks.marginTop='5px'; + needsTopMargin = true; } + sk=e.subKeys[i]; - if(typeof sk['width'] != 'undefined') { - kDiv.width=ks.width=(parseInt(sk['width'],10)*e.offsetWidth/100)+'px'; - } else { - kDiv.width=ks.width=e.offsetWidth+'px'; - } - ks.height=e.offsetHeight+'px'; - - btn=document.createElement('DIV'); - osk.setButtonClass(sk,btn); - - // Create (temporarily) unique ID by prefixing 'popup-' to actual key ID - if(typeof(sk['layer']) == 'string' && sk['layer'] != '') { - btn.id='popup-'+sk['layer']+'-'+sk['id']; - } else { - btn.id='popup-' + osk.layerId + '-'+sk['id']; - } - - btn.key = sk; - - // Must set button size (in px) dynamically, not from CSS - bs=btn.style; bs.height=ks.height; bs.width=ks.width; - - // Must set position explicitly, at least for Android - bs.position='absolute'; - t=util._CreateElement('SPAN'); - t.className='kmw-key-text'; - if(sk['text'] == null || sk['text'] == '') { - t.innerHTML='\xa0'; - if(typeof sk['id'] == 'string') { - if(/^U_[0-9A-F]{4}$/i.test(sk['id'])) { - t.innerHTML=String.fromCharCode(parseInt(sk['id'].substr(2),16)); - } - } - } else { - t.innerHTML=sk['text']; - } - - // Override the font name and size if set in the layout - ts=t.style; - ts.fontSize=osk.fontSize; //Build 344, KMEW-90 - if(typeof sk['font'] == 'string' && sk['font'] != '') { - ts.fontFamily=sk['font']; - } - if(typeof sk['fontsize'] == 'string' && sk['fontsize'] != 0) { - ts.fontSize=sk['fontsize']; - } - - btn.appendChild(t); - kDiv.appendChild(btn); + let keyGenerator = new com.keyman.OSKSubKey(sk); + let kDiv = keyGenerator.construct(e, needsTopMargin); + subKeys.appendChild(kDiv); } @@ -2569,8 +2612,8 @@ if(!window['keyman']['initialized']) { for(j=0; j Date: Fri, 7 Dec 2018 11:58:25 +0700 Subject: [PATCH 04/24] Further centralizes common sub-key/base-key code. --- web/source/kmwosk.ts | 69 ++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/web/source/kmwosk.ts b/web/source/kmwosk.ts index a03ed080a1..6e22aa5d89 100644 --- a/web/source/kmwosk.ts +++ b/web/source/kmwosk.ts @@ -7,6 +7,7 @@ namespace com.keyman { text?: string; sp?: string; width: string; + layer: string; nextlayer?: string; pad?: string; widthpc?: number; @@ -22,13 +23,15 @@ namespace com.keyman { } } - export class OSKKey { + export abstract class OSKKey { spec: OSKKeySpec; constructor(spec: OSKKeySpec) { this.spec = spec; } + abstract getId(): string; + /** * Replace default key names by special font codes for modifier keys * @@ -43,7 +46,7 @@ namespace com.keyman { } // Produces a HTMLSpanElement with the key's actual text. - protected generateKeyText(layerId: string): HTMLSpanElement { + protected generateKeyText(): HTMLSpanElement { let util = (window['keyman']).util; let spec = this.spec; @@ -65,17 +68,18 @@ namespace com.keyman { // Use special case lookup for modifier keys if(spec['sp'] == '1' || spec['sp'] == '2') { // Unique layer-based transformation. - var tId=((spec['text'] == '*Tab*' && layerId == 'shift') ? '*TabLeft*' : spec['text']); + var tId=((spec['text'] == '*Tab*' && spec.layer == 'shift') ? '*TabLeft*' : spec['text']); // Transforms our *___* special key codes into their corresponding PUA character codes for keyboard display. t.innerHTML=this.renameSpecialKey(tId); } //Override font spec if set for this key in the layout - if('font' in spec) { + ts.fontSize=(window['keyman']).osk.fontSize; //Build 344, KMEW-90 + if(typeof spec['font'] == 'string' && spec['font'] != '') { ts.fontFamily=spec['font']; } - if('fontsize' in spec) { + if(typeof spec['fontsize'] == 'string' && spec['fontsize'] != 0) { ts.fontSize=spec['fontsize']; } @@ -88,6 +92,11 @@ namespace com.keyman { super(spec); } + getId(): string { + // Define each key element id by layer id and key id (duplicate possible for SHIFT - does it matter?) + return this.spec.layer+'-'+this.spec.id; + } + // Produces a small reference label for the corresponding physical key on a US keyboard. protected generateKeyCapLabel(): HTMLDivElement { // Create the default key cap labels (letter keys, etc.) @@ -148,6 +157,8 @@ namespace com.keyman { let spec = this.spec; let isDesktop = util.device.formFactor == 'desktop' + spec.layer = layerId; + let kDiv=util._CreateElement('div'); kDiv['keyId']=spec['id']; kDiv.className='kmw-key-square'; @@ -187,7 +198,7 @@ namespace com.keyman { } // Define each key element id by layer id and key id (duplicate possible for SHIFT - does it matter?) - btn.id=layerId+'-'+spec.id; + btn.id=this.getId(); // TODO: convert btn['key'] to use the 'this' reference instead. btn['key']=spec; //attach reference to key layout spec to element @@ -209,7 +220,7 @@ namespace com.keyman { } // Add text to button and button to placeholder div - btn.appendChild(this.generateKeyText(layerId)); + btn.appendChild(this.generateKeyText()); kDiv.appendChild(btn); // Prevent user selection of key captions @@ -241,6 +252,17 @@ namespace com.keyman { super(spec); } + getId(): string { + let spec = this.spec; + // Create (temporarily) unique ID by prefixing 'popup-' to actual key ID + if(typeof(spec['layer']) == 'string' && spec['layer'] != '') { + return 'popup-'+spec['layer']+'-'+spec['id']; + } else { + // We only create subkeys when they're needed - the currently-active layer should be fine. + return 'popup-' + ( window['keyman']).osk.layerId + '-'+spec['id']; + } + } + construct(baseKey: HTMLDivElement, topMargin: boolean): HTMLDivElement { let osk = ( window['keyman']).osk; let spec = this.spec; @@ -271,12 +293,7 @@ namespace com.keyman { let btn=document.createElement('div'); osk.setButtonClass(spec,btn); - // Create (temporarily) unique ID by prefixing 'popup-' to actual key ID - if(typeof(spec['layer']) == 'string' && spec['layer'] != '') { - btn.id='popup-'+spec['layer']+'-'+spec['id']; - } else { - btn.id='popup-' + osk.layerId + '-'+spec['id']; - } + btn.id = this.getId(); // TODO: Swap to use the 'this' reference. btn['key'] = spec; @@ -289,31 +306,7 @@ namespace com.keyman { // Must set position explicitly, at least for Android bs.position='absolute'; - /// - let t=(window['keyman']).util._CreateElement('span'); - t.className='kmw-key-text'; - if(spec['text'] == null || spec['text'] == '') { - t.innerHTML='\xa0'; - if(typeof spec['id'] == 'string') { - if(/^U_[0-9A-F]{4}$/i.test(spec['id'])) { - t.innerHTML=String.fromCharCode(parseInt(spec['id'].substr(2),16)); - } - } - } else { - t.innerHTML=spec['text']; - } - - // Override the font name and size if set in the layout - let ts=t.style; - ts.fontSize=osk.fontSize; //Build 344, KMEW-90 - if(typeof spec['font'] == 'string' && spec['font'] != '') { - ts.fontFamily=spec['font']; - } - if(typeof spec['fontsize'] == 'string' && spec['fontsize'] != 0) { - ts.fontSize=spec['fontsize']; - } - - btn.appendChild(t); + btn.appendChild(this.generateKeyText()); kDiv.appendChild(btn); return kDiv; From 3518d9fbfeb4d17a1aae065b1ea2b5d845f101b3 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Fri, 7 Dec 2018 12:17:01 +0700 Subject: [PATCH 05/24] A few extra tweaks. --- web/source/kmwosk.ts | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/web/source/kmwosk.ts b/web/source/kmwosk.ts index 6e22aa5d89..7128d12da8 100644 --- a/web/source/kmwosk.ts +++ b/web/source/kmwosk.ts @@ -98,7 +98,7 @@ namespace com.keyman { } // Produces a small reference label for the corresponding physical key on a US keyboard. - protected generateKeyCapLabel(): HTMLDivElement { + private generateKeyCapLabel(): HTMLDivElement { // Create the default key cap labels (letter keys, etc.) var x = (window['keyman'])['osk'].keyCodes[this.spec.id]; switch(x) { @@ -130,11 +130,9 @@ namespace com.keyman { } } - protected processSubkeys(btn: HTMLDivElement) { - let spec = this.spec; - + private processSubkeys(btn: HTMLDivElement) { // Add reference to subkey array if defined - var bsn: number, bsk=btn['subKeys'] = spec['sk']; + var bsn: number, bsk=btn['subKeys'] = this.spec['sk']; // Transform any special keys into their PUA representations. for(bsn=0; bsnwindow['keyman']) - let util = keyman.util; - let osk = keyman['osk']; + let util = (window['keyman']).util; + let osk = (window['keyman']).osk; let spec = this.spec; let isDesktop = util.device.formFactor == 'desktop' @@ -199,7 +196,7 @@ namespace com.keyman { // Define each key element id by layer id and key id (duplicate possible for SHIFT - does it matter?) btn.id=this.getId(); - // TODO: convert btn['key'] to use the 'this' reference instead. + // Keyman 12 goal: convert btn['key'] to use the 'this' reference instead. btn['key']=spec; //attach reference to key layout spec to element // Define callbacks to handle key touches: iOS and Android tablets and phones @@ -237,14 +234,6 @@ namespace com.keyman { return Math.round(v)+'px'; } } - - objectWidth() { - if((window['keyman']).util.device.formFactor == 'desktop') { - return 100; - } else { - return keyman['osk'].getWidth(); - } - } } export class OSKSubKey extends OSKKey { @@ -295,7 +284,7 @@ namespace com.keyman { btn.id = this.getId(); - // TODO: Swap to use the 'this' reference. + // Plan for Keyman 12: swap to use the 'this' reference. btn['key'] = spec; // Must set button size (in px) dynamically, not from CSS @@ -881,7 +870,7 @@ if(!window['keyman']['initialized']) { // The holder is position:fixed, but the keys do not need to be, as no scrolling // is possible while the array is visible. So it is simplest to let the keys have // position:static and display:inline-block - var subKeys=document.createElement('DIV'),i,sk, + var subKeys=document.createElement('DIV'),i, t,ts,t1,ts1,kDiv,ks,btn,bs; var tKey = osk.getDefaultKeyObject(); @@ -938,9 +927,8 @@ if(!window['keyman']['initialized']) { if(nRows > 1 && nRow > 0) { needsTopMargin = true; } - sk=e.subKeys[i]; - let keyGenerator = new com.keyman.OSKSubKey(sk); + let keyGenerator = new com.keyman.OSKSubKey(e.subKeys[i]); let kDiv = keyGenerator.construct(e, needsTopMargin); subKeys.appendChild(kDiv); @@ -2446,12 +2434,10 @@ if(!window['keyman']['initialized']) { } // Get the actual available document width and scale factor according to device type - var objectUnits, objectWidth; + var objectWidth; if(formFactor == 'desktop') { - objectUnits = function(v) { return v + '%' }; objectWidth = 100; } else { - objectUnits = function(v) { return Math.round(v)+'px' }; objectWidth = osk.getWidth(); } @@ -2546,8 +2532,7 @@ if(!window['keyman']['initialized']) { for(j=0; j Date: Fri, 7 Dec 2018 12:19:24 +0700 Subject: [PATCH 06/24] A bit of documentation. --- web/source/kmwosk.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/source/kmwosk.ts b/web/source/kmwosk.ts index 7128d12da8..fbf89627b7 100644 --- a/web/source/kmwosk.ts +++ b/web/source/kmwosk.ts @@ -7,11 +7,11 @@ namespace com.keyman { text?: string; sp?: string; width: string; - layer: string; + layer?: string; // Added during OSK construction. nextlayer?: string; pad?: string; - widthpc?: number; - padpc?: number; + widthpc?: number; // Added during OSK construction. + padpc?: number; // Added during OSK construction. constructor(id: string, text?: string, width?: string, sp?: string, nextlayer?: string, pad?: string) { this.id = id; From 0525a5080f21c58eae68ac416195e8b324780bf8 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 11 Dec 2018 08:18:32 +0700 Subject: [PATCH 07/24] Added documentation to the 'generate osk key caps' method. --- web/source/kmwosk.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/source/kmwosk.ts b/web/source/kmwosk.ts index fbf89627b7..e0a48a7283 100644 --- a/web/source/kmwosk.ts +++ b/web/source/kmwosk.ts @@ -102,6 +102,8 @@ namespace com.keyman { // Create the default key cap labels (letter keys, etc.) var x = (window['keyman'])['osk'].keyCodes[this.spec.id]; switch(x) { + // Converts the keyman key id code for common symbol keys into its representative ASCII code. + // K_COLON -> K_BKQUOTE case 186: x=59; break; case 187: x=61; break; case 188: x=44; break; @@ -109,11 +111,13 @@ namespace com.keyman { case 190: x=46; break; case 191: x=47; break; case 192: x=96; break; + // K_LBRKT -> K_QUOTE case 219: x=91; break; case 220: x=92; break; case 221: x=93; break; case 222: x=39; break; default: + // No other symbol character represents a base key on the standard QWERTY English layout. if(x < 48 || x > 90) { x=0; } From 7563cbedce55be4fe3708cc346269e46ae53489d Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Thu, 13 Dec 2018 15:58:23 +0700 Subject: [PATCH 08/24] Add MigrateLanguagesArray function --- .../TIKE/compile/ValidateKeyboardInfo.pas | 94 ++++++++++++++----- 1 file changed, 70 insertions(+), 24 deletions(-) diff --git a/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas b/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas index 650dbc325a..c5a4f792ad 100644 --- a/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas +++ b/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas @@ -14,7 +14,10 @@ type json: TJSONObject; function DoFieldValidation: Boolean; function LoadJsonFile: Boolean; + function SaveJsonFile: Boolean; constructor Create(AJsonFile: string; ASilent: Boolean); + function MigrateLanguagesArray(alangs: TJSONArray; + var olangs: TJSONObject): Boolean; function Failed(message: string): Boolean; public class function Execute(JsonFile, JsonSchemaPath: string; FDistribution, FSilent: Boolean; FCallback: TCompilerCallback): Boolean; @@ -28,6 +31,7 @@ uses Winapi.Windows, BCP47Tag, + JsonUtil, Keyman.System.KeyboardInfoFile, Keyman.System.KMXFileLanguages; @@ -58,6 +62,27 @@ begin FSilent := ASilent; end; +function TValidateKeyboardInfo.MigrateLanguagesArray(alangs: TJSONArray; + var olangs: TJSONObject): Boolean; +var + i: Integer; + msg: string; +begin + Result := True; + for i := 0 to alangs.Count - 1 do + with TBCP47Tag.Create(alangs.Items[i].Value) do + try + if not IsValid(False, msg) then + Result := Failed(msg); + + if not IsCanonical(msg) then + Result := Failed(msg); + olangs.AddPair(alangs.Items[i].Value, 'something' ); + finally + Free; + end; +end; + function TValidateKeyboardInfo.DoFieldValidation: Boolean; var alangs: TJSONArray; @@ -76,36 +101,37 @@ begin Result := True; + // Migrate languages[] array to Object if langs is TJSONArray then begin alangs := langs as TJSONArray; - for i := 0 to alangs.Count - 1 do - with TBCP47Tag.Create(alangs.Items[i].Value) do - try - if not IsValid(False, msg) then - Result := Failed(msg); + olangs := TJSONObject.Create; + Result := MigrateLanguagesArray(alangs, olangs); + json.RemovePair(TKeyboardInfoFile.SLanguages); + json.AddPair(TKeyboardInfoFile.SLanguages, olangs); - if not IsCanonical(msg) then - Result := Failed(msg); - finally - Free; - end; - end - else - begin - olangs := langs as TJSONObject; - for i := 0 to olangs.Count - 1 do - with TBCP47Tag.Create(olangs.Pairs[i].JsonString.Value) do - try - if not IsValid(False, msg) then - Result := Failed(msg); + // Save and reload + if not SaveJsonFile then + Exit(Failed('Could not save updated keyboard_info file '+FJsonFile)); - if not IsCanonical(msg) then - Result := Failed(msg); - finally - Free; - end; + if not LoadJsonFile then + Exit(Failed('Cound not reopen updated keyboard info file'+FJsonFile)); + + langs := json.Values[TKeyboardInfoFile.SLanguages]; end; + + olangs := langs as TJSONObject; + for i := 0 to olangs.Count - 1 do + with TBCP47Tag.Create(olangs.Pairs[i].JsonString.Value) do + try + if not IsValid(False, msg) then + Result := Failed(msg); + + if not IsCanonical(msg) then + Result := Failed(msg); + finally + Free; + end; end; function TValidateKeyboardInfo.Failed(message: string): Boolean; @@ -132,6 +158,26 @@ begin Result := Assigned(json); end; +function TValidateKeyboardInfo.SaveJsonFile: Boolean; +var + str: TStringList; +begin + str := TStringList.Create; + try + PrettyPrintJSON(json, str); + with TStringStream.Create(str.Text, TEncoding.UTF8) do + try + // Use TStringStream so we don't get a default BOM prolog + SaveToFile(FJsonFile); + finally + Free; + end; + finally + str.Free; + end; + Result := True; +end; + class function TValidateKeyboardInfo.Execute(JsonFile, JsonSchemaPath: string; FDistribution, FSilent: Boolean; FCallback: TCompilerCallback): Boolean; var SchemaFile: string; From 50a5e091eab0bff1e5a3a3f661e14b50f65f238d Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Thu, 13 Dec 2018 21:00:05 +0700 Subject: [PATCH 09/24] Start trying to generate displayName and languageName --- .../TIKE/compile/ValidateKeyboardInfo.pas | 53 +++++++++++++++---- .../Keyman.System.KeyboardInfoFile.pas | 4 ++ 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas b/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas index c5a4f792ad..0a6585cbee 100644 --- a/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas +++ b/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas @@ -88,8 +88,9 @@ var alangs: TJSONArray; langs: TJSONValue; i: Integer; - olangs: TJSONObject; - msg: string; + olangs, olang: TJSONObject; + id, msg: string; + languagesMigrated, nameAdded: Boolean; begin if not LoadJsonFile then Exit(False); @@ -100,6 +101,8 @@ begin Exit(True); Result := True; + languagesMigrated := False; + nameAdded := False; // Migrate languages[] array to Object if langs is TJSONArray then @@ -109,29 +112,57 @@ begin Result := MigrateLanguagesArray(alangs, olangs); json.RemovePair(TKeyboardInfoFile.SLanguages); json.AddPair(TKeyboardInfoFile.SLanguages, olangs); - - // Save and reload - if not SaveJsonFile then - Exit(Failed('Could not save updated keyboard_info file '+FJsonFile)); - - if not LoadJsonFile then - Exit(Failed('Cound not reopen updated keyboard info file'+FJsonFile)); - langs := json.Values[TKeyboardInfoFile.SLanguages]; + languagesMigrated := True; end; olangs := langs as TJSONObject; for i := 0 to olangs.Count - 1 do - with TBCP47Tag.Create(olangs.Pairs[i].JsonString.Value) do + begin + id := olangs.Pairs[i].JsonString.Value; + with TBCP47Tag.Create(id) do try if not IsValid(False, msg) then Result := Failed(msg); if not IsCanonical(msg) then Result := Failed(msg); + + // Validate subtag names + olang := olangs.Values[id] as TJSONObject; + if (olang <> nil) and (olang.GetValue(TKeyboardInfoFile.SDisplayName) = nil) then + begin + olang.AddPair(TKeyboardInfoFile.SDisplayName, 'displayName1'); + nameAdded := True; + end; + + if (olang <> nil) and (olang.GetValue(TKeyboardInfoFile.SLanguageName) = nil) then + begin + olang.AddPair(TKeyboardInfoFile.SLanguageName, 'languageName1'); + nameAdded := True; + end; + + if nameAdded then + begin + olangs.RemovePair(olangs.Pairs[i].JsonString.Value); + olangs.AddPair(olangs.Pairs[i].JsonString.Value, olang); + end; + finally Free; end; + + end; + + if languagesMigrated or nameAdded then + begin + // Save and reload + if not SaveJsonFile then + Exit(Failed('Could not save updated keyboard_info file '+FJsonFile)); + + if not LoadJsonFile then + Exit(Failed('Cound not reopen updated keyboard info file'+FJsonFile)); + end; end; function TValidateKeyboardInfo.Failed(message: string): Boolean; diff --git a/windows/src/global/delphi/keyboards/Keyman.System.KeyboardInfoFile.pas b/windows/src/global/delphi/keyboards/Keyman.System.KeyboardInfoFile.pas index 7e294a218c..057c360bf6 100644 --- a/windows/src/global/delphi/keyboards/Keyman.System.KeyboardInfoFile.pas +++ b/windows/src/global/delphi/keyboards/Keyman.System.KeyboardInfoFile.pas @@ -5,6 +5,10 @@ interface type TKeyboardInfoFile = record const SLanguages = 'languages'; + const SDisplayName = 'displayName'; + const SLanguageName = 'languageName'; + const SScriptName = 'scriptName'; + const SRegionName = 'regionName'; end; From 71cc942c2294239bbf0a8233a58ee258d8f9428a Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Thu, 13 Dec 2018 23:33:14 +0700 Subject: [PATCH 10/24] Try to fix typing --- windows/src/developer/dw.bat | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 windows/src/developer/dw.bat diff --git a/windows/src/developer/dw.bat b/windows/src/developer/dw.bat new file mode 100644 index 0000000000..e086864d62 --- /dev/null +++ b/windows/src/developer/dw.bat @@ -0,0 +1,3 @@ +make kmcomp +copy ..\..\bin\Developer\kmcomp.exe d:\src\keyboards\tools\ +copy ..\..\bin\Developer\kmcmpdll.dll d:\src\keyboards\tools\ From 2e5fd959c11aec29652a01a139e84caf13a023ad Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Thu, 13 Dec 2018 23:34:44 +0700 Subject: [PATCH 11/24] Staged the wrong file --- .../TIKE/compile/ValidateKeyboardInfo.pas | 38 ++++++++++--------- windows/src/developer/dw.bat | 3 -- 2 files changed, 21 insertions(+), 20 deletions(-) delete mode 100644 windows/src/developer/dw.bat diff --git a/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas b/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas index 0a6585cbee..f080b11716 100644 --- a/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas +++ b/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas @@ -77,7 +77,7 @@ begin if not IsCanonical(msg) then Result := Failed(msg); - olangs.AddPair(alangs.Items[i].Value, 'something' ); + olangs.AddPair(alangs.Items[i].Value, TJSONString.Create('something')); finally Free; end; @@ -86,7 +86,7 @@ end; function TValidateKeyboardInfo.DoFieldValidation: Boolean; var alangs: TJSONArray; - langs: TJSONValue; + langs, lang: TJSONValue; i: Integer; olangs, olang: TJSONObject; id, msg: string; @@ -129,25 +129,29 @@ begin Result := Failed(msg); // Validate subtag names - olang := olangs.Values[id] as TJSONObject; - if (olang <> nil) and (olang.GetValue(TKeyboardInfoFile.SDisplayName) = nil) then + lang := olangs.Values[id]; + if lang is TJSONObject then begin - olang.AddPair(TKeyboardInfoFile.SDisplayName, 'displayName1'); - nameAdded := True; - end; + olang := lang as TJsonObject; + if (olang <> nil) and (olang.GetValue(TKeyboardInfoFile.SDisplayName) = nil) then + begin + olang.AddPair(TKeyboardInfoFile.SDisplayName, TJSONString.Create('displayName1')); + nameAdded := True; + end; - if (olang <> nil) and (olang.GetValue(TKeyboardInfoFile.SLanguageName) = nil) then - begin - olang.AddPair(TKeyboardInfoFile.SLanguageName, 'languageName1'); - nameAdded := True; - end; + if (olang <> nil) and (olang.GetValue(TKeyboardInfoFile.SLanguageName) = nil) then + begin + olang.AddPair(TKeyboardInfoFile.SLanguageName, TJSONString.Create('languageName1')); + nameAdded := True; + end; - if nameAdded then - begin - olangs.RemovePair(olangs.Pairs[i].JsonString.Value); - olangs.AddPair(olangs.Pairs[i].JsonString.Value, olang); - end; + if nameAdded then + begin + olangs.RemovePair(olangs.Pairs[i].JsonString.Value); + olangs.AddPair(olangs.Pairs[i].JsonString.Value, olang); + end; + end; finally Free; end; diff --git a/windows/src/developer/dw.bat b/windows/src/developer/dw.bat deleted file mode 100644 index e086864d62..0000000000 --- a/windows/src/developer/dw.bat +++ /dev/null @@ -1,3 +0,0 @@ -make kmcomp -copy ..\..\bin\Developer\kmcomp.exe d:\src\keyboards\tools\ -copy ..\..\bin\Developer\kmcmpdll.dll d:\src\keyboards\tools\ From a9e1c59274d3900252b2520a4c8db7c0cf252449 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Fri, 14 Dec 2018 12:49:36 +0700 Subject: [PATCH 12/24] Move migration code to MergeKeyboardInfo --- .../TIKE/compile/MergeKeyboardInfo.pas | 64 +++++++++ .../TIKE/compile/ValidateKeyboardInfo.pas | 132 ++++-------------- 2 files changed, 90 insertions(+), 106 deletions(-) diff --git a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas index 623601ef8d..d1efceafa1 100644 --- a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas +++ b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas @@ -87,6 +87,7 @@ type procedure AddHelpLink; procedure AddPlatformSupport; procedure CheckOrAddVersion; + function CheckOrMigrateLanguages: Boolean; function SaveJsonFile: Boolean; procedure CheckPackageKeyboardFilenames; procedure AddIsRTL; @@ -109,6 +110,7 @@ uses Keyman.System.RegExGroupHelperRSP19902, JsonUtil, + Keyman.System.KeyboardInfoFile, utilfiletypes, VersionInfo; @@ -178,6 +180,7 @@ begin CheckPackageKeyboardFilenames; CheckOrAddID; + CheckOrMigrateLanguages; AddName; AddIsRTL; AddAuthor; @@ -418,6 +421,67 @@ begin json.AddPair('id', FID); end; +function TMergeKeyboardInfo.CheckOrMigrateLanguages: Boolean; +var + v: TJSONValue; + alangs: TJSONArray; + langs, lang: TJSONValue; + olangs, olang: TJSONObject; + nameAdded: Boolean; + i: Integer; + msg: string; +begin + v := json.GetValue(TKeyboardInfoFile.SLanguages); + + // Migrate languages[] array to Object + if v is TJSONArray then + begin + alangs := v as TJSONArray; + olangs := TJSONObject.Create; + try + for i := 0 to alangs.Count - 1 do + olangs.AddPair(alangs.Items[i].Value, TJSONString.Create('something')); + + json.RemovePair(TKeyboardInfoFile.SLanguages); + json.AddPair(TKeyboardInfoFile.SLanguages, olangs); + except + on E:Exception do + begin + Free; + Exit(Failed('Fatal error '+E.ClassName+': '+E.Message)); + end; + end; + end; + + // Populate subtag names if needed + nameAdded := False; + v := json.GetValue(TKeyboardInfoFile.SLanguages); + { + if v is TJSONObject then + begin + olang := lang as TJsonObject; + if (olang <> nil) and (olang.GetValue(TKeyboardInfoFile.SDisplayName) = nil) then + begin + olang.AddPair(TKeyboardInfoFile.SDisplayName, TJSONString.Create('displayName1')); + nameAdded := True; + end; + + if (olang <> nil) and (olang.GetValue(TKeyboardInfoFile.SLanguageName) = nil) then + begin + olang.AddPair(TKeyboardInfoFile.SLanguageName, TJSONString.Create('languageName1')); + nameAdded := True; + end; + + if nameAdded then + begin + olangs.RemovePair(olangs.Pairs[i].JsonString.Value); + olangs.AddPair(olangs.Pairs[i].JsonString.Value, olang); + end; + + end; + } +end; + // // name -- from kmp.inf, js // diff --git a/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas b/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas index f080b11716..2a4c351dee 100644 --- a/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas +++ b/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas @@ -14,10 +14,7 @@ type json: TJSONObject; function DoFieldValidation: Boolean; function LoadJsonFile: Boolean; - function SaveJsonFile: Boolean; constructor Create(AJsonFile: string; ASilent: Boolean); - function MigrateLanguagesArray(alangs: TJSONArray; - var olangs: TJSONObject): Boolean; function Failed(message: string): Boolean; public class function Execute(JsonFile, JsonSchemaPath: string; FDistribution, FSilent: Boolean; FCallback: TCompilerCallback): Boolean; @@ -31,7 +28,6 @@ uses Winapi.Windows, BCP47Tag, - JsonUtil, Keyman.System.KeyboardInfoFile, Keyman.System.KMXFileLanguages; @@ -62,35 +58,14 @@ begin FSilent := ASilent; end; -function TValidateKeyboardInfo.MigrateLanguagesArray(alangs: TJSONArray; - var olangs: TJSONObject): Boolean; -var - i: Integer; - msg: string; -begin - Result := True; - for i := 0 to alangs.Count - 1 do - with TBCP47Tag.Create(alangs.Items[i].Value) do - try - if not IsValid(False, msg) then - Result := Failed(msg); - - if not IsCanonical(msg) then - Result := Failed(msg); - olangs.AddPair(alangs.Items[i].Value, TJSONString.Create('something')); - finally - Free; - end; -end; - function TValidateKeyboardInfo.DoFieldValidation: Boolean; var alangs: TJSONArray; - langs, lang: TJSONValue; + langs: TJSONValue; i: Integer; - olangs, olang: TJSONObject; - id, msg: string; - languagesMigrated, nameAdded: Boolean; + olangs: TJSONObject; + msg: string; + begin if not LoadJsonFile then Exit(False); @@ -101,71 +76,36 @@ begin Exit(True); Result := True; - languagesMigrated := False; - nameAdded := False; - // Migrate languages[] array to Object if langs is TJSONArray then begin alangs := langs as TJSONArray; - olangs := TJSONObject.Create; - Result := MigrateLanguagesArray(alangs, olangs); - json.RemovePair(TKeyboardInfoFile.SLanguages); - json.AddPair(TKeyboardInfoFile.SLanguages, olangs); - langs := json.Values[TKeyboardInfoFile.SLanguages]; - languagesMigrated := True; - end; - - olangs := langs as TJSONObject; - for i := 0 to olangs.Count - 1 do - begin - id := olangs.Pairs[i].JsonString.Value; - with TBCP47Tag.Create(id) do - try - if not IsValid(False, msg) then - Result := Failed(msg); - - if not IsCanonical(msg) then - Result := Failed(msg); - - // Validate subtag names - lang := olangs.Values[id]; - if lang is TJSONObject then - begin - olang := lang as TJsonObject; - if (olang <> nil) and (olang.GetValue(TKeyboardInfoFile.SDisplayName) = nil) then - begin - olang.AddPair(TKeyboardInfoFile.SDisplayName, TJSONString.Create('displayName1')); - nameAdded := True; - end; - - if (olang <> nil) and (olang.GetValue(TKeyboardInfoFile.SLanguageName) = nil) then - begin - olang.AddPair(TKeyboardInfoFile.SLanguageName, TJSONString.Create('languageName1')); - nameAdded := True; - end; - - if nameAdded then - begin - olangs.RemovePair(olangs.Pairs[i].JsonString.Value); - olangs.AddPair(olangs.Pairs[i].JsonString.Value, olang); - end; + for i := 0 to alangs.Count - 1 do + with TBCP47Tag.Create(alangs.Items[i].Value) do + try + if not IsValid(False, msg) then + Result := Failed(msg); + if not isCanonical(msg) then + Result := Failed(msg); + finally + Free; end; - finally - Free; - end; - - end; - - if languagesMigrated or nameAdded then + end + else begin - // Save and reload - if not SaveJsonFile then - Exit(Failed('Could not save updated keyboard_info file '+FJsonFile)); + olangs := langs as TJSONObject; + for i := 0 to olangs.Count - 1 do + with TBCP47Tag.Create(olangs.Pairs[i].JsonString.Value) do + try + if not IsValid(False, msg) then + Result := Failed(msg); - if not LoadJsonFile then - Exit(Failed('Cound not reopen updated keyboard info file'+FJsonFile)); + if not IsCanonical(msg) then + Result := Failed(msg); + finally + Free; + end; end; end; @@ -193,26 +133,6 @@ begin Result := Assigned(json); end; -function TValidateKeyboardInfo.SaveJsonFile: Boolean; -var - str: TStringList; -begin - str := TStringList.Create; - try - PrettyPrintJSON(json, str); - with TStringStream.Create(str.Text, TEncoding.UTF8) do - try - // Use TStringStream so we don't get a default BOM prolog - SaveToFile(FJsonFile); - finally - Free; - end; - finally - str.Free; - end; - Result := True; -end; - class function TValidateKeyboardInfo.Execute(JsonFile, JsonSchemaPath: string; FDistribution, FSilent: Boolean; FCallback: TCompilerCallback): Boolean; var SchemaFile: string; From 430ff3ecf491e6c6b9ca74ed5c25ff72e1533b21 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Fri, 14 Dec 2018 12:52:12 +0700 Subject: [PATCH 13/24] Revert ValidateKeyboardInfo --- windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas b/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas index 2a4c351dee..650dbc325a 100644 --- a/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas +++ b/windows/src/developer/TIKE/compile/ValidateKeyboardInfo.pas @@ -65,7 +65,6 @@ var i: Integer; olangs: TJSONObject; msg: string; - begin if not LoadJsonFile then Exit(False); @@ -86,7 +85,7 @@ begin if not IsValid(False, msg) then Result := Failed(msg); - if not isCanonical(msg) then + if not IsCanonical(msg) then Result := Failed(msg); finally Free; From c31c180610f0879c45c4ca02f0f4d4fcaa4374c7 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Fri, 14 Dec 2018 16:17:28 +0700 Subject: [PATCH 14/24] Start writing subtag names --- .../TIKE/compile/MergeKeyboardInfo.pas | 60 +++++++++++++++++-- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas index d1efceafa1..5495c2e98c 100644 --- a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas +++ b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas @@ -87,6 +87,7 @@ type procedure AddHelpLink; procedure AddPlatformSupport; procedure CheckOrAddVersion; + procedure AddSubtagNames(id: String; var o: TJSONObject); function CheckOrMigrateLanguages: Boolean; function SaveJsonFile: Boolean; procedure CheckPackageKeyboardFilenames; @@ -109,8 +110,10 @@ uses Keyman.System.RegExGroupHelperRSP19902, + BCP47Tag, JsonUtil, Keyman.System.KeyboardInfoFile, + Keyman.System.LanguageCodeUtils, utilfiletypes, VersionInfo; @@ -421,16 +424,55 @@ begin json.AddPair('id', FID); end; +procedure TMergeKeyboardInfo.AddSubtagNames(id: String; var o: TJSONObject); +var + displayName, languageName, scriptName, regionName: String; + v: TJSONValue; +begin + if id = '' then + Exit; + with TBCP47Tag.Create(id) do + try + TLanguageCodeUtils.BCP47Languages.TryGetValue(Language, languageName); + TLanguageCodeUtils.BCP47Scripts.TryGetValue(Script, scriptName); + TLanguageCodeUtils.BCP47Regions.TryGetValue(Region, regionName); + + displayName := TLanguageCodeUtils.LanguageName(languageName, scriptName, regionName); + + v := o.Values[TKeyboardInfoFile.SDisplayName]; + if not Assigned(v) then + o.AddPair(TKeyboardInfoFile.SDisplayName, displayName); + + v := o.Values[TKeyboardInfoFile.SLanguageName]; + if not Assigned(v) then + o.AddPair(TKeyboardInfoFile.SLanguageName, languageName); + + v := o.Values[TKeyboardInfoFile.SScriptName]; + if not Assigned(v) and (Script <> '') then + o.AddPair(TKeyboardInfoFile.SScriptName, scriptName); + + v := o.Values[TKeyboardInfoFile.SRegionName]; + if not Assigned(v) and (Region <> '') then + o.AddPair(TKeyboardInfoFile.SRegionName, regionName); + + finally + Free; + end; + +end; + function TMergeKeyboardInfo.CheckOrMigrateLanguages: Boolean; var v: TJSONValue; alangs: TJSONArray; - langs, lang: TJSONValue; - olangs, olang: TJSONObject; + olangs: TJSONObject; + o: TJSONObject; nameAdded: Boolean; i: Integer; - msg: string; + id: string; begin + Result := True; + v := json.GetValue(TKeyboardInfoFile.SLanguages); // Migrate languages[] array to Object @@ -438,9 +480,16 @@ begin begin alangs := v as TJSONArray; olangs := TJSONObject.Create; + o := TJSONObject.Create; try for i := 0 to alangs.Count - 1 do - olangs.AddPair(alangs.Items[i].Value, TJSONString.Create('something')); + begin + id := alangs.Items[i].Value; + if id = '' then + continue; + AddSubtagNames(id, o); + olangs.AddPair(id, o); + end; json.RemovePair(TKeyboardInfoFile.SLanguages); json.AddPair(TKeyboardInfoFile.SLanguages, olangs); @@ -453,10 +502,11 @@ begin end; end; + { // Populate subtag names if needed nameAdded := False; v := json.GetValue(TKeyboardInfoFile.SLanguages); - { + if v is TJSONObject then begin olang := lang as TJsonObject; From 56cf43f210cf95eca322ef8dc80a01f1f5ad4e89 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Fri, 14 Dec 2018 16:30:37 +0700 Subject: [PATCH 15/24] clean up unused var --- windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas index 5495c2e98c..582f259412 100644 --- a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas +++ b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas @@ -467,7 +467,6 @@ var alangs: TJSONArray; olangs: TJSONObject; o: TJSONObject; - nameAdded: Boolean; i: Integer; id: string; begin @@ -493,6 +492,9 @@ begin json.RemovePair(TKeyboardInfoFile.SLanguages); json.AddPair(TKeyboardInfoFile.SLanguages, olangs); + + // Migration complete and subtags populated + Exit; except on E:Exception do begin @@ -504,7 +506,6 @@ begin { // Populate subtag names if needed - nameAdded := False; v := json.GetValue(TKeyboardInfoFile.SLanguages); if v is TJSONObject then From abb1b9f7bdd77c3b07a02f095fe9f720f811e071 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Fri, 14 Dec 2018 23:33:52 +0700 Subject: [PATCH 16/24] Try to fix NPE --- .../TIKE/compile/MergeKeyboardInfo.pas | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas index 582f259412..c68c558f16 100644 --- a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas +++ b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas @@ -479,29 +479,30 @@ begin begin alangs := v as TJSONArray; olangs := TJSONObject.Create; - o := TJSONObject.Create; try - for i := 0 to alangs.Count - 1 do - begin - id := alangs.Items[i].Value; - if id = '' then - continue; - AddSubtagNames(id, o); - olangs.AddPair(id, o); + o := TJSONObject.Create; + try + for i := 0 to alangs.Count - 1 do + begin + id := alangs.Items[i].Value; + if id = '' then + continue; + AddSubtagNames(id, o); + olangs.AddPair(id, o as TJSONValue); + end; + finally + Free; end; json.RemovePair(TKeyboardInfoFile.SLanguages); json.AddPair(TKeyboardInfoFile.SLanguages, olangs); - // Migration complete and subtags populated - Exit; - except - on E:Exception do - begin - Free; - Exit(Failed('Fatal error '+E.ClassName+': '+E.Message)); - end; + finally + Free; end; + + // Migration complete and subtags populated + Exit; end; { From 03ac003f473b3615701d956f8c48eb00e29d7625 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Sat, 15 Dec 2018 21:01:37 +0700 Subject: [PATCH 17/24] Fix check for scriptName / regionName --- .../TIKE/compile/MergeKeyboardInfo.pas | 45 +++++++++---------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas index c68c558f16..a0a82a003d 100644 --- a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas +++ b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas @@ -436,29 +436,27 @@ begin TLanguageCodeUtils.BCP47Languages.TryGetValue(Language, languageName); TLanguageCodeUtils.BCP47Scripts.TryGetValue(Script, scriptName); TLanguageCodeUtils.BCP47Regions.TryGetValue(Region, regionName); - - displayName := TLanguageCodeUtils.LanguageName(languageName, scriptName, regionName); - - v := o.Values[TKeyboardInfoFile.SDisplayName]; - if not Assigned(v) then - o.AddPair(TKeyboardInfoFile.SDisplayName, displayName); - - v := o.Values[TKeyboardInfoFile.SLanguageName]; - if not Assigned(v) then - o.AddPair(TKeyboardInfoFile.SLanguageName, languageName); - - v := o.Values[TKeyboardInfoFile.SScriptName]; - if not Assigned(v) and (Script <> '') then - o.AddPair(TKeyboardInfoFile.SScriptName, scriptName); - - v := o.Values[TKeyboardInfoFile.SRegionName]; - if not Assigned(v) and (Region <> '') then - o.AddPair(TKeyboardInfoFile.SRegionName, regionName); - finally Free; end; + displayName := TLanguageCodeUtils.LanguageName(languageName, scriptName, regionName); + + v := o.Values[TKeyboardInfoFile.SDisplayName]; + if not Assigned(v) then + o.AddPair(TKeyboardInfoFile.SDisplayName, displayName); + + v := o.Values[TKeyboardInfoFile.SLanguageName]; + if not Assigned(v) then + o.AddPair(TKeyboardInfoFile.SLanguageName, languageName); + + v := o.Values[TKeyboardInfoFile.SScriptName]; + if not Assigned(v) and (scriptName <> '') then + o.AddPair(TKeyboardInfoFile.SScriptName, scriptName); + + v := o.Values[TKeyboardInfoFile.SRegionName]; + if not Assigned(v) and (regionName <> '') then + o.AddPair(TKeyboardInfoFile.SRegionName, regionName); end; function TMergeKeyboardInfo.CheckOrMigrateLanguages: Boolean; @@ -488,15 +486,14 @@ begin if id = '' then continue; AddSubtagNames(id, o); - olangs.AddPair(id, o as TJSONValue); + olangs.AddPair(id, o); end; + + json.RemovePair(TKeyboardInfoFile.SLanguages); + json.AddPair(TKeyboardInfoFile.SLanguages, olangs); finally Free; end; - - json.RemovePair(TKeyboardInfoFile.SLanguages); - json.AddPair(TKeyboardInfoFile.SLanguages, olangs); - finally Free; end; From 846c05693bdf077f8effaaf24d569d333b41203a Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Sat, 15 Dec 2018 22:09:07 +0700 Subject: [PATCH 18/24] Tweak Free --- .../TIKE/compile/MergeKeyboardInfo.pas | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas index a0a82a003d..5b43b4a7c5 100644 --- a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas +++ b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas @@ -432,12 +432,12 @@ begin if id = '' then Exit; with TBCP47Tag.Create(id) do - try - TLanguageCodeUtils.BCP47Languages.TryGetValue(Language, languageName); - TLanguageCodeUtils.BCP47Scripts.TryGetValue(Script, scriptName); - TLanguageCodeUtils.BCP47Regions.TryGetValue(Region, regionName); - finally - Free; + try + TLanguageCodeUtils.BCP47Languages.TryGetValue(Language, languageName); + TLanguageCodeUtils.BCP47Scripts.TryGetValue(Script, scriptName); + TLanguageCodeUtils.BCP47Regions.TryGetValue(Region, regionName); + finally + Free; end; displayName := TLanguageCodeUtils.LanguageName(languageName, scriptName, regionName); @@ -449,7 +449,7 @@ begin v := o.Values[TKeyboardInfoFile.SLanguageName]; if not Assigned(v) then o.AddPair(TKeyboardInfoFile.SLanguageName, languageName); - + { v := o.Values[TKeyboardInfoFile.SScriptName]; if not Assigned(v) and (scriptName <> '') then o.AddPair(TKeyboardInfoFile.SScriptName, scriptName); @@ -457,6 +457,7 @@ begin v := o.Values[TKeyboardInfoFile.SRegionName]; if not Assigned(v) and (regionName <> '') then o.AddPair(TKeyboardInfoFile.SRegionName, regionName); + } end; function TMergeKeyboardInfo.CheckOrMigrateLanguages: Boolean; @@ -478,28 +479,29 @@ begin alangs := v as TJSONArray; olangs := TJSONObject.Create; try - o := TJSONObject.Create; - try - for i := 0 to alangs.Count - 1 do - begin - id := alangs.Items[i].Value; - if id = '' then - continue; - AddSubtagNames(id, o); + for i := 0 to alangs.Count - 1 do + begin + id := alangs.Items[i].Value; + if id = '' then + continue; + + o := TJSONObject.Create; + try olangs.AddPair(id, o); + AddSubtagNames(id, o); + finally + o.Free; end; - - json.RemovePair(TKeyboardInfoFile.SLanguages); - json.AddPair(TKeyboardInfoFile.SLanguages, olangs); - finally - Free; end; - finally - Free; - end; - // Migration complete and subtags populated - Exit; + json.RemovePair(TKeyboardInfoFile.SLanguages); + json.AddPair(TKeyboardInfoFile.SLanguages, olangs); + + // Migration complete and subtags populated + Exit; + finally + olangs.Free; + end; end; { From 7ceb1f175fa81b3f41a8eb0d0fae1ed5f503b288 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Sat, 15 Dec 2018 22:12:12 +0700 Subject: [PATCH 19/24] Add Script/Region back --- windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas index 5b43b4a7c5..a709e8243a 100644 --- a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas +++ b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas @@ -449,7 +449,7 @@ begin v := o.Values[TKeyboardInfoFile.SLanguageName]; if not Assigned(v) then o.AddPair(TKeyboardInfoFile.SLanguageName, languageName); - { + v := o.Values[TKeyboardInfoFile.SScriptName]; if not Assigned(v) and (scriptName <> '') then o.AddPair(TKeyboardInfoFile.SScriptName, scriptName); @@ -457,7 +457,6 @@ begin v := o.Values[TKeyboardInfoFile.SRegionName]; if not Assigned(v) and (regionName <> '') then o.AddPair(TKeyboardInfoFile.SRegionName, regionName); - } end; function TMergeKeyboardInfo.CheckOrMigrateLanguages: Boolean; From f55a9477e3636dc345981d23d547e183845138f1 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Mon, 17 Dec 2018 09:00:00 +0700 Subject: [PATCH 20/24] Address review comments Cleanup and fixes null pointer exception --- .../TIKE/compile/MergeKeyboardInfo.pas | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas index a709e8243a..14199e4c14 100644 --- a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas +++ b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas @@ -87,7 +87,7 @@ type procedure AddHelpLink; procedure AddPlatformSupport; procedure CheckOrAddVersion; - procedure AddSubtagNames(id: String; var o: TJSONObject); + procedure AddSubtagNames(id: String; o: TJSONObject); function CheckOrMigrateLanguages: Boolean; function SaveJsonFile: Boolean; procedure CheckPackageKeyboardFilenames; @@ -424,20 +424,21 @@ begin json.AddPair('id', FID); end; -procedure TMergeKeyboardInfo.AddSubtagNames(id: String; var o: TJSONObject); +procedure TMergeKeyboardInfo.AddSubtagNames(id: String; o: TJSONObject); var displayName, languageName, scriptName, regionName: String; v: TJSONValue; + bcp47Tag: TBCP47Tag; begin if id = '' then Exit; - with TBCP47Tag.Create(id) do + bcp47Tag := TBCP47Tag.Create(id); try - TLanguageCodeUtils.BCP47Languages.TryGetValue(Language, languageName); - TLanguageCodeUtils.BCP47Scripts.TryGetValue(Script, scriptName); - TLanguageCodeUtils.BCP47Regions.TryGetValue(Region, regionName); + TLanguageCodeUtils.BCP47Languages.TryGetValue(bcp47Tag.Language, languageName); + TLanguageCodeUtils.BCP47Scripts.TryGetValue(bcp47Tag.Script, scriptName); + TLanguageCodeUtils.BCP47Regions.TryGetValue(bcp47Tag.Region, regionName); finally - Free; + bcp47Tag.Free; end; displayName := TLanguageCodeUtils.LanguageName(languageName, scriptName, regionName); @@ -450,13 +451,19 @@ begin if not Assigned(v) then o.AddPair(TKeyboardInfoFile.SLanguageName, languageName); - v := o.Values[TKeyboardInfoFile.SScriptName]; - if not Assigned(v) and (scriptName <> '') then - o.AddPair(TKeyboardInfoFile.SScriptName, scriptName); + if scriptName <> '' then + begin + v := o.Values[TKeyboardInfoFile.SScriptName]; + if not Assigned(v) and (scriptName <> '') then + o.AddPair(TKeyboardInfoFile.SScriptName, scriptName); + end; - v := o.Values[TKeyboardInfoFile.SRegionName]; - if not Assigned(v) and (regionName <> '') then - o.AddPair(TKeyboardInfoFile.SRegionName, regionName); + if regionName <> '' then + begin + v := o.Values[TKeyboardInfoFile.SRegionName]; + if not Assigned(v) and (regionName <> '') then + o.AddPair(TKeyboardInfoFile.SRegionName, regionName); + end; end; function TMergeKeyboardInfo.CheckOrMigrateLanguages: Boolean; @@ -485,22 +492,18 @@ begin continue; o := TJSONObject.Create; - try - olangs.AddPair(id, o); - AddSubtagNames(id, o); - finally - o.Free; - end; + olangs.AddPair(id, o); + AddSubtagNames(id, o); end; json.RemovePair(TKeyboardInfoFile.SLanguages); json.AddPair(TKeyboardInfoFile.SLanguages, olangs); - // Migration complete and subtags populated - Exit; finally - olangs.Free; end; + + // Migration complete and subtags populated + Exit; end; { From 19db11bc6ab27d0b0fd2d08b9cea9161b4f2722a Mon Sep 17 00:00:00 2001 From: glasseyes Date: Mon, 17 Dec 2018 09:05:42 +0700 Subject: [PATCH 21/24] put underlying base "us" keyboard into LDML for any unshifted or shifted key that isn't specified in the kvk --- linux/keyman-config/keyman_config/kvk2ldml.py | 147 ++++++++++-------- 1 file changed, 86 insertions(+), 61 deletions(-) diff --git a/linux/keyman-config/keyman_config/kvk2ldml.py b/linux/keyman-config/keyman_config/kvk2ldml.py index fcd93e9f0b..9d1bf78741 100755 --- a/linux/keyman-config/keyman_config/kvk2ldml.py +++ b/linux/keyman-config/keyman_config/kvk2ldml.py @@ -100,65 +100,64 @@ KVKS_RALT= b'\x40' # from web/source/kmwosk.ts VKey_to_Iso = { - 90 : "B01", # Z - 88 : "B02", # X - 67 : "B03", # C - 86 : "B04", # V - 66 : "B05", # B - 78 : "B06", # N - 77 : "B07", # M - 188 : "B08", # , - 190 : "B09", # . - 191 : "B10", # / - 65 : "C01", # A - 83 : "C02", # S - 68 : "C03", # D - 70 : "C04", # F - 71 : "C05", # G - 72 : "C06", # H - 74 : "C07", # J - 75 : "C08", # K - 76 : "C09", # L - 186 : "C10", # ; - 222 : "C11", # ' - 81 : "D01", # Q - 87 : "D02", # W - 69 : "D03", # E - 82 : "D04", # R - 84 : "D05", # T - 89 : "D06", # Y - 85 : "D07", # U - 73 : "D08", # I - 79 : "D09", # O - 80 : "D10", # P - 219 : "D11", # [ - 221: "D12", # ] - 49 : "E01", # 1 - 50 : "E02", # 2 - 51 : "E03", # 3 - 52 : "E04", # 4 - 53 : "E05", # 5 - 54 : "E06", # 6 - 55 : "E07", # 7 - 56 : "E08", # 8 - 57 : "E09", # 9 - 48 : "E10", # 0 - 189 : "E11", # - - 187 : "E12", # = - 192 : "E00", # ` - 220 : "B00", # \ - 226 : "C12", # extra key on european keyboards - 32 : "A03", # space - 96 : "A51", # "K_NP0" - 97 : "B51", # "K_NP1" - 98 : "B52", # "K_NP2" - 99 : "B53", # "K_NP3" - 100 : "C51", # "K_NP4" - 101 : "C52", # "K_NP5" - 102 : "C53", # "K_NP6" - 103 : "D51", # "K_NP7" - 104 : "D52", # "K_NP8" - 105 : "D53" # "K_NP9" + 90 : { "code": "B01", "base" : "z", "shift" : "Z" }, # Z + 88 : { "code": "B02", "base" : "x", "shift" : "X" }, # X + 67 : { "code": "B03", "base" : "c", "shift" : "C" }, # C + 86 : { "code": "B04", "base" : "v", "shift" : "V" }, # V + 66 : { "code": "B05", "base" : "b", "shift" : "B" }, # B + 78 : { "code": "B06", "base" : "n", "shift" : "N" }, # N + 77 : { "code": "B07", "base" : "m", "shift" : "M" }, # M + 188 : { "code": "B08", "base" : ",", "shift" : "<" }, # , + 190 : { "code": "B09", "base" : ".", "shift" : ">" }, # . + 191 : { "code": "B10", "base" : "/", "shift" : "?" }, # / + 65 : { "code": "C01", "base" : "a", "shift" : "A" }, # A + 83 : { "code": "C02", "base" : "s", "shift" : "S" }, # S + 68 : { "code": "C03", "base" : "d", "shift" : "D" }, # D + 70 : { "code": "C04", "base" : "f", "shift" : "F" }, # F + 71 : { "code": "C05", "base" : "g", "shift" : "G" }, # G + 72 : { "code": "C06", "base" : "h", "shift" : "H" }, # H + 74 : { "code": "C07", "base" : "j", "shift" : "J" }, # J + 75 : { "code": "C08", "base" : "k", "shift" : "K" }, # K + 76 : { "code": "C09", "base" : "l", "shift" : "L" }, # L + 186 : { "code": "C10", "base" : ";", "shift" : ":" }, # ; + 222 : { "code": "C11", "base" : "'", "shift" : '"' }, # ' + 81 : { "code": "D01", "base" : "q", "shift" : "Q" }, # Q + 87 : { "code": "D02", "base" : "w", "shift" : "W" }, # W + 69 : { "code": "D03", "base" : "e", "shift" : "E" }, # E + 82 : { "code": "D04", "base" : "r", "shift" : "R" }, # R + 84 : { "code": "D05", "base" : "t", "shift" : "T" }, # T + 89 : { "code": "D06", "base" : "y", "shift" : "Y" }, # Y + 85 : { "code": "D07", "base" : "u", "shift" : "U" }, # U + 73 : { "code": "D08", "base" : "i", "shift" : "I" }, # I + 79 : { "code": "D09", "base" : "o", "shift" : "O" }, # O + 80 : { "code": "D10", "base" : "p", "shift" : "P" }, # P + 219 : { "code": "D11", "base" : "[", "shift" : "{" }, # [ + 221 : { "code": "D12", "base" : "]", "shift" : "}" }, # ] + 49 : { "code": "E01", "base" : "1", "shift" : "!" }, # 1 + 50 : { "code": "E02", "base" : "2", "shift" : "@" }, # 2 + 51 : { "code": "E03", "base" : "3", "shift" : "#" }, # 3 + 52 : { "code": "E04", "base" : "4", "shift" : "$" }, # 4 + 53 : { "code": "E05", "base" : "5", "shift" : "%" }, # 5 + 54 : { "code": "E06", "base" : "6", "shift" : "^" }, # 6 + 55 : { "code": "E07", "base" : "7", "shift" : "&" }, # 7 + 56 : { "code": "E08", "base" : "8", "shift" : "*" }, # 8 + 57 : { "code": "E09", "base" : "9", "shift" : "(" }, # 9 + 48 : { "code": "E10", "base" : "0", "shift" : ")" }, # 0 + 189 : { "code": "E11", "base" : "-", "shift" : "_" }, # - + 187 : { "code": "E12", "base" : "=", "shift" : "+" }, # = + 192 : { "code": "E00", "base" : "`", "shift" : "~" }, # ` + 220 : { "code": "C12", "base" : "\\","shift" : "|" }, # \ + 226 : { "code": "B00", "base" : "<", "shift" : ">" }, # extra key on european keyboards + 32 : { "code": "A03", "base" : " ", "shift" : " " }, # space + 97 : { "code": "B51", "base" : "1", "shift" : "1" }, # "K_NP1" + 98 : { "code": "B52", "base" : "2", "shift" : "2" }, # "K_NP2" + 99 : { "code": "B53", "base" : "3", "shift" : "3" }, # "K_NP3" + 100 : { "code": "C51", "base" : "4", "shift" : "4" }, # "K_NP4" + 101 : { "code": "C52", "base" : "5", "shift" : "5" }, # "K_NP5" + 102 : { "code": "C53", "base" : "6", "shift" : "6" }, # "K_NP6" + 103 : { "code": "D51", "base" : "7", "shift" : "7" }, # "K_NP7" + 104 : { "code": "D52", "base" : "8", "shift" : "8" }, # "K_NP8" + 105 : { "code": "D53", "base" : "9", "shift" : "9" }, # "K_NP9" } @@ -309,8 +308,34 @@ def convert_ldml(kvkData): else: keymaps[modifier] = (key,) + for vkey in VKey_to_Iso: + alreadyused = False + for key in keymaps["None"]: + if key.VKey == vkey: + alreadyused = True + if not alreadyused and vkey != 226: + uskey = NKey() + uskey.VKey = vkey + uskey.text = VKey_to_Iso[vkey]["base"] + if "None" in keymaps: + keymaps["None"] = keymaps["None"] + (uskey,) + else: + keymaps["None"] = (uskey,) + alreadyused = False + for key in keymaps["shift"]: + if key.VKey == vkey: + alreadyused = True + if not alreadyused and vkey != 226: + uskey = NKey() + uskey.VKey = vkey + uskey.text = VKey_to_Iso[vkey]["shift"] + if "shift" in keymaps: + keymaps["shift"] = keymaps["shift"] + (uskey,) + else: + keymaps["shift"] = (uskey,) + ldml = etree.Element("keyboard", locale = "zzz-keyman") - etree.SubElement(ldml, "version", platform = "10") + etree.SubElement(ldml, "version", platform = "11") names = etree.SubElement(ldml, "names") names.append( etree.Element("name", value = "ZZZ") ) @@ -321,7 +346,7 @@ def convert_ldml(kvkData): keymap = etree.SubElement(ldml, "keyMap", modifiers = modifier) for key in keymaps[modifier]: if key.VKey in VKey_to_Iso: - iso_key = VKey_to_Iso[key.VKey] + iso_key = VKey_to_Iso[key.VKey]["code"] keymap.append( etree.Element("map", iso = iso_key, to = key.text) ) else: logging.warning("Unknown vkey: %s", key.VKey) From 5dd0737185da26050957719d463da29189208780 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 17 Dec 2018 13:45:19 +1100 Subject: [PATCH 22/24] [Windows] Use RegisterHotkey to (a) simplify hotkeys and (b) avoid serialization issues with modifiers --- windows/src/engine/keyman/UfrmKeyman7Main.pas | 156 ++++++++++++++---- .../keyman32/k32_lowlevelkeyboardhook.cpp | 43 +---- .../delphi/general/KeymanControlMessages.pas | 2 +- .../global/delphi/general/UserMessages.pas | 1 + windows/src/global/inc/keymancontrol.h | 4 +- 5 files changed, 131 insertions(+), 75 deletions(-) diff --git a/windows/src/engine/keyman/UfrmKeyman7Main.pas b/windows/src/engine/keyman/UfrmKeyman7Main.pas index af78a66f57..8c198f699f 100644 --- a/windows/src/engine/keyman/UfrmKeyman7Main.pas +++ b/windows/src/engine/keyman/UfrmKeyman7Main.pas @@ -155,6 +155,7 @@ uses //TOUCH UfrmTouchKeyboard, GlobalKeyboardChangeManager, UfrmVisualKeyboard, + IntegerList, KeymanTrayIcon, KeymanMenuItem, custinterfaces, @@ -220,6 +221,8 @@ type FGlobalKeyboardChangeManager: TGlobalKeyboardChangeManager; // I4271 FActiveHKL: Integer; FTrayIcon: TIcon; // I4359 + FHotkeyWindow: HWND; + FHotkeys: TIntegerList; //TOUCH FCurrentContext: string; function AddTaskbarIcon: Boolean; @@ -236,7 +239,7 @@ type procedure TrayIconMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure ShowBalloon(Value: Integer); - procedure DoHotkey(Target: Integer); + procedure DoInterfaceHotkey(Target: Integer); procedure WMUserStart(var Message: TMessage); message WM_USER_Start; procedure WMUserParameterPass(var Message: TMessage); message WM_USER_ParameterPass; @@ -286,8 +289,11 @@ type procedure UpdateFocusInfo; // I4731 procedure UnregisterControllerWindows; // I4731 function IsSysTrayWindow(AHandle: THandle): Boolean; - procedure HandleLanguageHotkey(HotkeyValue: Integer); procedure GetTrayIconHandle; // I4731 + procedure RegisterHotkeys; + procedure UnregisterHotkeys; + procedure HotkeyWndProc(var Message: TMessage); + procedure DoLanguageHotkey(Index: Integer); protected procedure Notification(AComponent: TComponent; Operation: TOperation); override; @@ -399,7 +405,6 @@ uses Vcl.AxCtrls, Vcl.Buttons, Vcl.ComCtrls, - IntegerList, InterfaceHotkeys, utilhotkey, MessageIdentifiers, @@ -493,7 +498,6 @@ begin FLangSwitchManager := TLangSwitchManager.Create; // I3933 - //FLastKeymanID := -1; FLastHKL := GetKeyboardLayout(0); @@ -533,6 +537,8 @@ procedure TfrmKeyman7Main.FormDestroy(Sender: TObject); begin ClosePlatformComms64; + UnregisterHotkeys; + with TRegistryErrorControlled.Create do // I2890 try if OpenKey(SRegKey_KeymanOSK_CU, True) then @@ -560,6 +566,7 @@ begin kmint.KeymanEngineControl.ShutdownKeyman32Engine; FreeAndNil(FLangSwitchManager); // I3933 + //Windows.MessageBox(Handle, PChar(IntToStr(kmcom._AddRef)), 'RefCount+1', MB_OK); kmint.kmcom := nil; // I5132 @@ -785,13 +792,9 @@ begin UpdateFocusInfo; // I4731 RequestCurrentActiveKeyboard(0); // I3961 end; - KMC_LANGUAGEHOTKEY: // I4451 - begin - HandleLanguageHotkey(lParam); - end; KMC_INTERFACEHOTKEY: begin - DoHotkey(wParam); + DoInterfaceHotkey(wParam); end; KMC_ONSCREENKEYBOARD: begin @@ -815,6 +818,7 @@ begin end; FOSKManuallyClosedThisSession := False; FRunningProduct.FLangSwitchConfiguration.Refresh; + RegisterHotkeys; end; KMC_NOTIFYWELCOME: // I1248 - Redesigned welcome begin @@ -844,7 +848,24 @@ begin end; end; -procedure TfrmKeyman7Main.DoHotkey(Target: Integer); +procedure TfrmKeyman7Main.DoLanguageHotkey(Index: Integer); +var + FKeyboard: TLangSwitchKeyboard; +begin + if (Index >= 0) and (Index < kmcom.Languages.Count) then + begin + FKeyboard := FLangSwitchManager.FindKeyboard(kmcom.Languages[Index].HKL, kmcom.Languages[Index].ProfileGUID); + if not Assigned(FKeyboard) then Exit; + + // Handle toggle hotkey + if (FKeyboard = FLangSwitchManager.ActiveKeyboard) and (kmcom.Options['koKeyboardHotkeysAreToggle'].Value) then + FKeyboard := FLangSwitchManager.Languages[0].Keyboards[0]; + + ActivateKeyboard(FKeyboard); + end; +end; + +procedure TfrmKeyman7Main.DoInterfaceHotkey(Target: Integer); begin if not Assigned(FRunningProduct) then Exit; @@ -1227,6 +1248,8 @@ begin kmint.KeymanEngineControl.RestartEngine; // I1486 StartPlatformComms64; + + RegisterHotkeys; end; procedure TfrmKeyman7Main.RequestCurrentActiveKeyboard(Command: WORD); // I3961 @@ -1585,26 +1608,6 @@ begin inherited Notification(AComponent, Operation); end; -procedure TfrmKeyman7Main.HandleLanguageHotkey(HotkeyValue: Integer); -var - i: Integer; - FKeyboard: TLangSwitchKeyboard; -begin - for i := 0 to kmcom.Languages.Count - 1 do - if kmcom.Languages[i].Hotkey.RawValue = HotkeyValue then - begin - FKeyboard := FLangSwitchManager.FindKeyboard(kmcom.Languages[i].HKL, kmcom.Languages[i].ProfileGUID); - if not Assigned(FKeyboard) then Exit; - - // Handle toggle hotkey - if (FKeyboard = FLangSwitchManager.ActiveKeyboard) and (kmcom.Options['koKeyboardHotkeysAreToggle'].Value) then - FKeyboard := FLangSwitchManager.Languages[0].Keyboards[0]; - - ActivateKeyboard(FKeyboard); - Exit; - end; -end; - procedure TfrmKeyman7Main.HideVisualKeyboard; begin if not Assigned(kmcom) and not StartKeymanEngine then Exit; @@ -1835,4 +1838,97 @@ begin if not ShellExecuteExW(@sei) then Exit; // log end; +procedure TfrmKeyman7Main.HotkeyWndProc(var Message: TMessage); +begin + if Message.Msg = WM_HOTKEY then + begin + KL.Log('Hotkey %d', [Message.WParam]); + if Message.WParam > kh__High + then DoLanguageHotkey(Message.WParam - kh__High - 1) + else DoInterfaceHotkey(Message.WParam); + end; + Message.Result := DefWindowProc(FHotkeyWindow, Message.Msg, Message.WParam, Message.LParam); +end; + +function KeymanHotkeyModifiersToWindowsHotkeyModifiers(v: KeymanHotkeyModifiers): Integer; +begin + Result := 0; + if (v and HK_SHIFT) = HK_SHIFT then Result := Result or MOD_SHIFT; + if (v and HK_CTRL) = HK_CTRL then Result := Result or MOD_CONTROL; + if (v and HK_ALT) = HK_ALT then Result := Result or MOD_ALT; +end; + +procedure TfrmKeyman7Main.RegisterHotkeys; +var + hk: IKeymanHotkey; + i: Integer; + language: IKeymanLanguage; + id: Integer; +begin + TDebugLogClient.Instance.WriteMessage('Enter RegisterHotkeys', []); + + if FHotkeyWindow = 0 then + FHotkeyWindow := AllocateHWnd(HotkeyWndProc); + + if not Assigned(FHotkeys) then + FHotkeys := TIntegerList.Create; + + UnregisterHotkeys; + + for i := 0 to kmcom.Hotkeys.Count - 1 do + begin + hk := kmcom.Hotkeys[i]; + if not hk.IsEmpty and (hk.VirtualKey <> 0) then + begin + // Note, if hk.VirtualKey is 0, this indicates a modifier-only hotkey such + // as Alt+Left Shift. These are handled in keyman32 k32_lowlevelkeyboardhook + // because RegisterHotkey cannot handle modifier-only hotkeys. + if RegisterHotkey(FHotkeyWindow, hk.Target, KeymanHotkeyModifiersToWindowsHotkeyModifiers(hk.Modifiers), hk.VirtualKey) then + begin + TDebugLogClient.Instance.WriteMessage('Added hotkey %d -> %x %x', [hk.Target, + KeymanHotkeyModifiersToWindowsHotkeyModifiers(hk.Modifiers), hk.VirtualKey]); + FHotkeys.Add(hk.Target) + end + else + TDebugLogClient.Instance.WriteLastError('RegisterHotkeys', 'RegisterHotkey', 'Failed to register hotkey '+IntToStr(hk.Target)); + end; + end; + + for i := 0 to kmcom.Languages.Count - 1 do + begin + language := kmcom.Languages[i]; + hk := language.Hotkey; + if Assigned(hk) and not hk.IsEmpty and (hk.VirtualKey <> 0) then + begin + id := kh__High + 1 + i; + if RegisterHotkey(FHotkeyWindow, id, KeymanHotkeyModifiersToWindowsHotkeyModifiers(hk.Modifiers), hk.VirtualKey) then + begin + TDebugLogClient.Instance.WriteMessage('Added hotkey for language %s [%d] -> %x %x', [language.LocaleName, id, + KeymanHotkeyModifiersToWindowsHotkeyModifiers(hk.Modifiers), hk.VirtualKey]); + FHotkeys.Add(id); + end + else + TDebugLogClient.Instance.WriteLastError('RegisterHotkeys', 'RegisterHotkey', 'Failed to register hotkey '+IntToStr(id)); + end; + end; +end; + +procedure TfrmKeyman7Main.UnregisterHotkeys; +var + i, hk: Integer; +begin + TDebugLogClient.Instance.WriteMessage('Enter UnregisterHotkeys', []); + + if not Assigned(FHotkeys) then + Exit; + + for i := 0 to FHotkeys.Count - 1 do + begin + hk := FHotkeys[i]; + if not UnregisterHotKey(FHotkeyWindow, hk) then + TDebugLogClient.Instance.WriteLastError('UnregisterHotkeys', 'UnregisterHotkey', 'Failed to unregister hotkey '+IntToStr(hk)); + end; + FHotkeys.Clear; +end; + end. diff --git a/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp b/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp index 79f478ce33..b50e0486e4 100644 --- a/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp +++ b/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp @@ -143,51 +143,10 @@ LRESULT _kmnLowLevelKeyboardProc( else if (KeyLanguageSwitchPress(hs->vkCode, extended, isUp, FHotkeyShiftState)) { if (ProcessLanguageSwitchShiftKey(hs->vkCode, isUp) == 1) return 1; } - else { - - /* - Process keyboard hotkeys - */ - - DWORD hk = (DWORD)hs->vkCode | FHotkeyShiftState; - - Hotkeys *hotkeys = Hotkeys::Instance(); // I4641 - - /* - Search for an interface or language hotkey - */ - - // TODO: deprecate KeymanUIDisabled, FSingleThread - - if (hotkeys) { // I4641 - Hotkey *hotkey = hotkeys->GetHotkey(hk); - if (hotkey) { - if (isUp) { - if (hotkey->HotkeyType == hktInterface) { - SendDebugMessageFormat(0, sdmGlobal, 0, "Hotkey matched = {HotkeyValue: %x, Target: %d}", - hotkey->HotkeyValue, - hotkey->Target); - Globals::PostMasterController(wm_keyman_control, MAKELONG(KMC_INTERFACEHOTKEY, hotkey->Target), 0); - } - else { - SendDebugMessageFormat(0, sdmGlobal, 0, "Hotkey matched = {HotkeyValue: %x, hkl: %x}", - hotkey->HotkeyValue, - hotkey->hkl); - - // Send the hotkey value to the master controller, rather than the language HKL or profile GUID, because - // this is cheaper than constructing a string and posting it across. - - Globals::PostMasterController(wm_keyman_control, MAKELONG(KMC_LANGUAGEHOTKEY, 0), (LPARAM)hotkey->HotkeyValue); // I4451 - } - } - return 1; - } - } - } /* - Not a hotkey, so we will use the serialized input model + Not the language switch hotkey, so we will use the serialized input model */ diff --git a/windows/src/global/delphi/general/KeymanControlMessages.pas b/windows/src/global/delphi/general/KeymanControlMessages.pas index f8f9fa5f8f..443b333dda 100644 --- a/windows/src/global/delphi/general/KeymanControlMessages.pas +++ b/windows/src/global/delphi/general/KeymanControlMessages.pas @@ -53,7 +53,7 @@ const KMC_PROFILECHANGED = 18; // 9.0.426.0 // I3933 // KMC_KEYBOARDHOTKEY = 19; // 9.0.459.0 // I4326 Deprecated in favour of language hotkeys - KMC_LANGUAGEHOTKEY = 20; // 9.0.471.0 // I4451 + // KMC_LANGUAGEHOTKEY = 20; // 9.0.471.0 // I4451 Now using keyman.exe RegisterHotkey //TOUCH KMC_CONTEXT = 19; diff --git a/windows/src/global/delphi/general/UserMessages.pas b/windows/src/global/delphi/general/UserMessages.pas index 41ec91720e..14987304ce 100644 --- a/windows/src/global/delphi/general/UserMessages.pas +++ b/windows/src/global/delphi/general/UserMessages.pas @@ -87,3 +87,4 @@ const implementation end. + diff --git a/windows/src/global/inc/keymancontrol.h b/windows/src/global/inc/keymancontrol.h index 063f059a65..fe6b119988 100644 --- a/windows/src/global/inc/keymancontrol.h +++ b/windows/src/global/inc/keymancontrol.h @@ -47,8 +47,8 @@ #define KMC_PROFILECHANGED 18 // 9.0.426.0 // I3933 -#define KMC_KEYBOARDHOTKEY 19 // 9.0.459.0 // I4326 -#define KMC_LANGUAGEHOTKEY 20 // 9.0.460.0 // I4451 +//#define KMC_KEYBOARDHOTKEY 19 // 9.0.459.0 // I4326 Deprecated in favour of language hotkeys +//#define KMC_LANGUAGEHOTKEY 20 // 9.0.460.0 // I4451 Now using keyman.exe RegisterHotkey //TOUCH #define KMC_CONTEXT 19 // 9.0.450.0 #define RWM_KEYMAN_CONTROL "WM_KEYMAN_CONTROL" From e8ba0944ea7dfd10c9e64f6223faf6761321729f Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Mon, 17 Dec 2018 09:55:46 +0700 Subject: [PATCH 23/24] Update languages objects to add subtag names --- .../TIKE/compile/MergeKeyboardInfo.pas | 60 ++++++++----------- windows/src/developer/history.md | 1 + 2 files changed, 25 insertions(+), 36 deletions(-) diff --git a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas index 14199e4c14..7ebf7699b7 100644 --- a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas +++ b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas @@ -26,6 +26,9 @@ # version -- from kmp.inf, js # minKeymanVersion -- from kmp.inf, kmx, js # platformSupport -- deduce from whether kmp exists, js exists + # languages -- given the BCP 47 ids, generate the subtag names: + # displayName, languageName - (required) + # scriptName, regionName - if not blank } unit MergeKeyboardInfo; @@ -88,7 +91,7 @@ type procedure AddPlatformSupport; procedure CheckOrAddVersion; procedure AddSubtagNames(id: String; o: TJSONObject); - function CheckOrMigrateLanguages: Boolean; + procedure CheckOrMigrateLanguages; function SaveJsonFile: Boolean; procedure CheckPackageKeyboardFilenames; procedure AddIsRTL; @@ -466,22 +469,20 @@ begin end; end; -function TMergeKeyboardInfo.CheckOrMigrateLanguages: Boolean; +procedure TMergeKeyboardInfo.CheckOrMigrateLanguages; var v: TJSONValue; alangs: TJSONArray; - olangs: TJSONObject; - o: TJSONObject; + olangs, o: TJSONObject; + pair: TJSONPair; i: Integer; id: string; begin - Result := True; - v := json.GetValue(TKeyboardInfoFile.SLanguages); - // Migrate languages[] array to Object if v is TJSONArray then begin + // Migrate languages[] array to Object alangs := v as TJSONArray; olangs := TJSONObject.Create; try @@ -491,6 +492,7 @@ begin if id = '' then continue; + // Populate subtag names o := TJSONObject.Create; olangs.AddPair(id, o); AddSubtagNames(id, o); @@ -498,41 +500,27 @@ begin json.RemovePair(TKeyboardInfoFile.SLanguages); json.AddPair(TKeyboardInfoFile.SLanguages, olangs); - finally end; - - // Migration complete and subtags populated - Exit; - end; - - { - // Populate subtag names if needed - v := json.GetValue(TKeyboardInfoFile.SLanguages); - - if v is TJSONObject then + end + else if v is TJSONObject then begin - olang := lang as TJsonObject; - if (olang <> nil) and (olang.GetValue(TKeyboardInfoFile.SDisplayName) = nil) then - begin - olang.AddPair(TKeyboardInfoFile.SDisplayName, TJSONString.Create('displayName1')); - nameAdded := True; - end; + olangs := v as TJsonObject; + try + for i := 0 to olangs.Count - 1 do + begin + pair := olangs.Pairs[i]; + id := pair.JSONString.Value; + if id = '' then + continue; - if (olang <> nil) and (olang.GetValue(TKeyboardInfoFile.SLanguageName) = nil) then - begin - olang.AddPair(TKeyboardInfoFile.SLanguageName, TJSONString.Create('languageName1')); - nameAdded := True; + // Populate subtag names + o := pair.JsonValue as TJSONObject; + AddSubtagNames(id, o); + end; + finally end; - - if nameAdded then - begin - olangs.RemovePair(olangs.Pairs[i].JsonString.Value); - olangs.AddPair(olangs.Pairs[i].JsonString.Value, olang); - end; - end; - } end; // diff --git a/windows/src/developer/history.md b/windows/src/developer/history.md index f62d6485d5..872906b86d 100644 --- a/windows/src/developer/history.md +++ b/windows/src/developer/history.md @@ -10,6 +10,7 @@ * Opening or creating a project now closes current editor files (#1242) * Projects can now include other related files such as history.md (#1243) * Keyman Developer now treats files as UTF-8 by default (#1244) +* Update kmcomp to add language subtag names to keyboard_info files (#1426) ## 2018-11-28 10.0.1206 stable * Add parameter `-add-help-link` to kmcomp (#1346) From 52f9bc0589759b0a94a434f41841bdac2c55ce45 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Mon, 17 Dec 2018 12:04:38 +0700 Subject: [PATCH 24/24] More cleanup for review comments --- .../TIKE/compile/MergeKeyboardInfo.pas | 52 ++++++++----------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas index 7ebf7699b7..5b8614ce6d 100644 --- a/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas +++ b/windows/src/developer/TIKE/compile/MergeKeyboardInfo.pas @@ -457,14 +457,14 @@ begin if scriptName <> '' then begin v := o.Values[TKeyboardInfoFile.SScriptName]; - if not Assigned(v) and (scriptName <> '') then + if not Assigned(v) then o.AddPair(TKeyboardInfoFile.SScriptName, scriptName); end; if regionName <> '' then begin v := o.Values[TKeyboardInfoFile.SRegionName]; - if not Assigned(v) and (regionName <> '') then + if not Assigned(v) then o.AddPair(TKeyboardInfoFile.SRegionName, regionName); end; end; @@ -485,40 +485,34 @@ begin // Migrate languages[] array to Object alangs := v as TJSONArray; olangs := TJSONObject.Create; - try - for i := 0 to alangs.Count - 1 do - begin - id := alangs.Items[i].Value; - if id = '' then - continue; + for i := 0 to alangs.Count - 1 do + begin + id := alangs.Items[i].Value; + if id = '' then + continue; - // Populate subtag names - o := TJSONObject.Create; - olangs.AddPair(id, o); - AddSubtagNames(id, o); - end; - - json.RemovePair(TKeyboardInfoFile.SLanguages); - json.AddPair(TKeyboardInfoFile.SLanguages, olangs); - finally + // Populate subtag names + o := TJSONObject.Create; + olangs.AddPair(id, o); + AddSubtagNames(id, o); end; + + json.RemovePair(TKeyboardInfoFile.SLanguages); + json.AddPair(TKeyboardInfoFile.SLanguages, olangs); end else if v is TJSONObject then begin olangs := v as TJsonObject; - try - for i := 0 to olangs.Count - 1 do - begin - pair := olangs.Pairs[i]; - id := pair.JSONString.Value; - if id = '' then - continue; + for i := 0 to olangs.Count - 1 do + begin + pair := olangs.Pairs[i]; + id := pair.JSONString.Value; + if id = '' then + continue; - // Populate subtag names - o := pair.JsonValue as TJSONObject; - AddSubtagNames(id, o); - end; - finally + // Populate subtag names + o := pair.JsonValue as TJSONObject; + AddSubtagNames(id, o); end; end; end;