mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-09 02:15:32 +00:00
Merge branch 'master' into linux-backspace
This commit is contained in:
commit
2fda69b9f2
10 changed files with 622 additions and 355 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -7,8 +7,11 @@ namespace com.keyman {
|
|||
text?: string;
|
||||
sp?: string;
|
||||
width: string;
|
||||
layer?: string; // Added during OSK construction.
|
||||
nextlayer?: string;
|
||||
pad?: string;
|
||||
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;
|
||||
|
|
@ -19,6 +22,289 @@ namespace com.keyman {
|
|||
this.pad = pad;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
* @param {string} oldText
|
||||
* @return {string}
|
||||
**/
|
||||
protected renameSpecialKey(oldText: string): string {
|
||||
let keyman = (<KeymanBase>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.
|
||||
protected generateKeyText(): HTMLSpanElement {
|
||||
let util = (<KeymanBase>window['keyman']).util;
|
||||
let spec = this.spec;
|
||||
|
||||
// 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*' && 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
|
||||
ts.fontSize=(<KeymanBase>window['keyman']).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'];
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
export class OSKBaseKey extends OSKKey {
|
||||
constructor(spec: OSKKeySpec) {
|
||||
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.
|
||||
private generateKeyCapLabel(): HTMLDivElement {
|
||||
// Create the default key cap labels (letter keys, etc.)
|
||||
var x = (<KeymanBase>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;
|
||||
case 189: x=45; break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if(x > 0) {
|
||||
let q = (<KeymanBase>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;
|
||||
}
|
||||
}
|
||||
|
||||
private processSubkeys(btn: HTMLDivElement) {
|
||||
// Add reference to subkey array if defined
|
||||
var bsn: number, bsk=btn['subKeys'] = this.spec['sk'];
|
||||
// Transform any special keys into their PUA representations.
|
||||
for(bsn=0; bsn<bsk.length; bsn++) {
|
||||
if(bsk[bsn]['sp'] == '1' || bsk[bsn]['sp'] == '2') {
|
||||
var oldText=bsk[bsn]['text'];
|
||||
bsk[bsn]['text']=this.renameSpecialKey(oldText);
|
||||
}
|
||||
}
|
||||
|
||||
// If a subkey array is defined, add an icon
|
||||
var skIcon=(<KeymanBase>window['keyman']).util._CreateElement('div');
|
||||
skIcon.className='kmw-key-popup-icon';
|
||||
//kDiv.appendChild(skIcon);
|
||||
btn.appendChild(skIcon);
|
||||
}
|
||||
|
||||
construct(layout, layerId: string, rowStyle: CSSStyleDeclaration, totalPercent: number): {element: HTMLDivElement, percent: number} {
|
||||
let util = (<KeymanBase>window['keyman']).util;
|
||||
let osk = (<KeymanBase>window['keyman']).osk;
|
||||
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';
|
||||
|
||||
let ks=kDiv.style;
|
||||
ks.width=this.objectUnits(spec['widthpc']);
|
||||
|
||||
let originalPercent = totalPercent;
|
||||
|
||||
let btn=util._CreateElement('div');
|
||||
// Set button class
|
||||
osk.setButtonClass(spec,btn,layout);
|
||||
|
||||
// Set key and button positioning properties.
|
||||
if(!isDesktop) {
|
||||
// Regularize interkey spacing by rounding key width and padding (Build 390)
|
||||
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
|
||||
|
||||
// Set distinct phone and tablet button position properties
|
||||
btn.style.left=ks.left;
|
||||
btn.style.width=ks.width;
|
||||
} else {
|
||||
ks.marginLeft=this.objectUnits(spec['padpc']);
|
||||
}
|
||||
|
||||
totalPercent=totalPercent+spec['padpc']+spec['widthpc'];
|
||||
|
||||
// Add the (US English) keycap label for desktop OSK or if KDU flag is non-zero
|
||||
if(layout.keyLabels || isDesktop) {
|
||||
let keyCap = this.generateKeyCapLabel();
|
||||
|
||||
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=this.getId();
|
||||
// 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
|
||||
// TODO: replace inline function calls??
|
||||
if(!util.device.touchable) {
|
||||
// Highlight key while mouse down or if moving back over originally selected key
|
||||
btn.onmouseover=btn.onmousedown=osk.mouseOverMouseDownHandler; // Build 360
|
||||
|
||||
// Remove highlighting when key released or moving off selected element
|
||||
btn.onmouseup=btn.onmouseout=osk.mouseUpMouseOutHandler; //Build 360
|
||||
}
|
||||
|
||||
// Handle subkey-related tasks.
|
||||
if(typeof(spec['sk']) != 'undefined' && spec['sk'] != null) {
|
||||
this.processSubkeys(btn);
|
||||
} else {
|
||||
btn['subKeys']=null;
|
||||
}
|
||||
|
||||
// Add text to button and button to placeholder div
|
||||
btn.appendChild(this.generateKeyText());
|
||||
kDiv.appendChild(btn);
|
||||
|
||||
// Prevent user selection of key captions
|
||||
//t.style.webkitUserSelect='none';
|
||||
|
||||
// The 'return value' of this process.
|
||||
return {element: kDiv, percent: totalPercent - originalPercent};
|
||||
}
|
||||
|
||||
objectUnits(v: number) {
|
||||
if((<KeymanBase>window['keyman']).util.device.formFactor == 'desktop') {
|
||||
return v + '%';
|
||||
} else {
|
||||
return Math.round(v)+'px';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class OSKSubKey extends OSKKey {
|
||||
constructor(spec: OSKKeySpec) {
|
||||
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-' + (<KeymanBase> window['keyman']).osk.layerId + '-'+spec['id'];
|
||||
}
|
||||
}
|
||||
|
||||
construct(baseKey: HTMLDivElement, topMargin: boolean): HTMLDivElement {
|
||||
let osk = (<KeymanBase> 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);
|
||||
|
||||
btn.id = this.getId();
|
||||
|
||||
// Plan for Keyman 12: 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';
|
||||
|
||||
btn.appendChild(this.generateKeyText());
|
||||
kDiv.appendChild(btn);
|
||||
|
||||
return kDiv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
|
|
@ -588,7 +874,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();
|
||||
|
|
@ -640,72 +926,15 @@ if(!window['keyman']['initialized']) {
|
|||
|
||||
// Add nested button elements for each sub-key
|
||||
for(i=0; i<nKeys; i++) {
|
||||
sk=e.subKeys[i];
|
||||
kDiv=document.createElement('DIV');
|
||||
|
||||
for(var tp in tKey) {
|
||||
if(typeof sk[tp] != 'string') {
|
||||
sk[tp]=tKey[tp];
|
||||
}
|
||||
}
|
||||
|
||||
kDiv.className='kmw-key-square-ex';
|
||||
kDiv.keyId=sk['id'];
|
||||
ks=kDiv.style;
|
||||
nRow=Math.floor(i/nCols);
|
||||
var needsTopMargin = false;
|
||||
let nRow=Math.floor(i/nCols);
|
||||
if(nRows > 1 && nRow > 0) {
|
||||
ks.marginTop='5px';
|
||||
needsTopMargin = true;
|
||||
}
|
||||
|
||||
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(e.subKeys[i]);
|
||||
let kDiv = keyGenerator.construct(e, needsTopMargin);
|
||||
|
||||
subKeys.appendChild(kDiv);
|
||||
}
|
||||
|
||||
|
|
@ -2209,12 +2438,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();
|
||||
}
|
||||
|
||||
|
|
@ -2309,8 +2536,7 @@ if(!window['keyman']['initialized']) {
|
|||
for(j=0; j<keys.length; j++)
|
||||
{
|
||||
key=keys[j];
|
||||
for(var tp in tKey)
|
||||
{
|
||||
for(var tp in tKey) { // tKey = osk.getDefaultKeyObject();
|
||||
if(typeof key[tp] != 'string') key[tp]=tKey[tp];
|
||||
}
|
||||
|
||||
|
|
@ -2365,147 +2591,14 @@ if(!window['keyman']['initialized']) {
|
|||
|
||||
//Create the key square (an outer DIV) for each key element with padding, and an inner DIV for the button (btn)
|
||||
totalPercent=0;
|
||||
for(j=0; j<keys.length; j++)
|
||||
{
|
||||
for(j=0; j<keys.length; j++) {
|
||||
key=keys[j];
|
||||
kDiv=util._CreateElement('DIV');
|
||||
kDiv.keyId=key['id'];
|
||||
kDiv.className='kmw-key-square';
|
||||
ks=kDiv.style;
|
||||
|
||||
var keyGenerator = new com.keyman.OSKBaseKey(key);
|
||||
var keyTuple = keyGenerator.construct(layout, layer['id'], rs, totalPercent);
|
||||
|
||||
kDiv.width=ks.width=objectUnits(key['widthpc']);
|
||||
|
||||
if(formFactor != 'desktop')
|
||||
{
|
||||
// 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=objectUnits(totalPercent+keys[j]['padpc']);
|
||||
ks.bottom=rs.bottom;
|
||||
ks.height=rs.height; //must be specified in px for rest of layout to work correctly
|
||||
}
|
||||
else
|
||||
{
|
||||
ks.marginLeft=objectUnits(key['padpc']);
|
||||
}
|
||||
|
||||
totalPercent=totalPercent+keys[j]['padpc']+keys[j]['widthpc'];
|
||||
|
||||
btn=util._CreateElement('DIV');
|
||||
|
||||
// Set button class
|
||||
osk.setButtonClass(key,btn,layout);
|
||||
|
||||
// Set distinct phone and tablet button position properties
|
||||
if(formFactor != 'desktop')
|
||||
{
|
||||
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 || (formFactor == 'desktop')) //desktop or KDU flag set
|
||||
{
|
||||
// Create the default key cap labels (letter keys, etc.)
|
||||
var x=osk.keyCodes[key.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']+'-'+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<bsk.length; bsn++)
|
||||
if(bsk[bsn]['sp'] == '1' || bsk[bsn]['sp'] == '2')
|
||||
{
|
||||
var oldText=bsk[bsn]['text'];
|
||||
bsk[bsn]['text']=osk.renameSpecialKey(oldText);
|
||||
}
|
||||
}
|
||||
else btn.subKeys=null;
|
||||
|
||||
// Define callbacks to handle key touches: iOS and Android tablets and phones
|
||||
// TODO: replace inline function calls??
|
||||
if(!device.touchable)
|
||||
{
|
||||
// Highlight key while mouse down or if moving back over originally selected key
|
||||
btn.onmouseover=btn.onmousedown=osk.mouseOverMouseDownHandler; // Build 360
|
||||
|
||||
// Remove highlighting when key released or moving off selected element
|
||||
btn.onmouseup=btn.onmouseout=osk.mouseUpMouseOutHandler; //Build 360
|
||||
}
|
||||
|
||||
// Add OSK key labels
|
||||
var t=util._CreateElement('SPAN'),ts=t.style;
|
||||
if(key['text'] == null || key['text'] == '')
|
||||
{
|
||||
t.innerHTML='\xa0';
|
||||
if(typeof key['id'] == 'string')
|
||||
{
|
||||
if(/^U_[0-9A-F]{4}$/i.test(key['id']))
|
||||
t.innerHTML=String.fromCharCode(parseInt(key['id'].substr(2),16));
|
||||
}
|
||||
}
|
||||
else t.innerHTML=key['text'];
|
||||
t.className='kmw-key-text';
|
||||
|
||||
// Use special case lookup for modifier keys
|
||||
if(key['sp'] == '1' || key['sp'] == '2')
|
||||
{
|
||||
var tId=((key['text'] == '*Tab*' && n == 1) ? '*TabLeft*' : key['text']);
|
||||
t.innerHTML=osk.renameSpecialKey(tId);
|
||||
}
|
||||
|
||||
//Override font spec if set for this key in the layout
|
||||
if('font' in key) ts.fontFamily=key['font'];
|
||||
if('fontsize' in key) ts.fontSize=key['fontsize'];
|
||||
|
||||
// Add text to button and button to placeholder div
|
||||
btn.appendChild(t);
|
||||
kDiv.appendChild(btn);
|
||||
|
||||
// Prevent user selection of key captions
|
||||
//t.style.webkitUserSelect='none';
|
||||
|
||||
// If a subkey array is defined, add an icon
|
||||
if(typeof(key['sk']) != 'undefined' && key['sk'] != null)
|
||||
{
|
||||
var skIcon=util._CreateElement('DIV');
|
||||
skIcon.className='kmw-key-popup-icon';
|
||||
//kDiv.appendChild(skIcon);
|
||||
btn.appendChild(skIcon);
|
||||
}
|
||||
// Add key to row
|
||||
rDiv.appendChild(kDiv);
|
||||
rDiv.appendChild(keyTuple.element);
|
||||
totalPercent += keyTuple.percent;
|
||||
}
|
||||
// Add row to layer
|
||||
gDiv.appendChild(rDiv);
|
||||
|
|
@ -2516,17 +2609,6 @@ if(!window['keyman']['initialized']) {
|
|||
return lDiv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace default key names by special font codes for modifier keys
|
||||
*
|
||||
* @param {string} oldText
|
||||
* @return {string}
|
||||
**/
|
||||
osk.renameSpecialKey = function(oldText) {
|
||||
// If a 'special key' mapping exists for the text, replace it with its corresponding special OSK character.
|
||||
return osk.specialCharacters[oldText] ? String.fromCharCode(0XE000 + osk.specialCharacters[oldText]) : oldText;
|
||||
}
|
||||
|
||||
osk.clearPopup = function()
|
||||
{
|
||||
// Remove the displayed subkey array, if any, and cancel popup request
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
@ -87,6 +90,8 @@ type
|
|||
procedure AddHelpLink;
|
||||
procedure AddPlatformSupport;
|
||||
procedure CheckOrAddVersion;
|
||||
procedure AddSubtagNames(id: String; o: TJSONObject);
|
||||
procedure CheckOrMigrateLanguages;
|
||||
function SaveJsonFile: Boolean;
|
||||
procedure CheckPackageKeyboardFilenames;
|
||||
procedure AddIsRTL;
|
||||
|
|
@ -108,7 +113,10 @@ uses
|
|||
|
||||
Keyman.System.RegExGroupHelperRSP19902,
|
||||
|
||||
BCP47Tag,
|
||||
JsonUtil,
|
||||
Keyman.System.KeyboardInfoFile,
|
||||
Keyman.System.LanguageCodeUtils,
|
||||
utilfiletypes,
|
||||
VersionInfo;
|
||||
|
||||
|
|
@ -178,6 +186,7 @@ begin
|
|||
CheckPackageKeyboardFilenames;
|
||||
|
||||
CheckOrAddID;
|
||||
CheckOrMigrateLanguages;
|
||||
AddName;
|
||||
AddIsRTL;
|
||||
AddAuthor;
|
||||
|
|
@ -418,6 +427,96 @@ begin
|
|||
json.AddPair('id', FID);
|
||||
end;
|
||||
|
||||
procedure TMergeKeyboardInfo.AddSubtagNames(id: String; o: TJSONObject);
|
||||
var
|
||||
displayName, languageName, scriptName, regionName: String;
|
||||
v: TJSONValue;
|
||||
bcp47Tag: TBCP47Tag;
|
||||
begin
|
||||
if id = '' then
|
||||
Exit;
|
||||
bcp47Tag := TBCP47Tag.Create(id);
|
||||
try
|
||||
TLanguageCodeUtils.BCP47Languages.TryGetValue(bcp47Tag.Language, languageName);
|
||||
TLanguageCodeUtils.BCP47Scripts.TryGetValue(bcp47Tag.Script, scriptName);
|
||||
TLanguageCodeUtils.BCP47Regions.TryGetValue(bcp47Tag.Region, regionName);
|
||||
finally
|
||||
bcp47Tag.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);
|
||||
|
||||
if scriptName <> '' then
|
||||
begin
|
||||
v := o.Values[TKeyboardInfoFile.SScriptName];
|
||||
if not Assigned(v) then
|
||||
o.AddPair(TKeyboardInfoFile.SScriptName, scriptName);
|
||||
end;
|
||||
|
||||
if regionName <> '' then
|
||||
begin
|
||||
v := o.Values[TKeyboardInfoFile.SRegionName];
|
||||
if not Assigned(v) then
|
||||
o.AddPair(TKeyboardInfoFile.SRegionName, regionName);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMergeKeyboardInfo.CheckOrMigrateLanguages;
|
||||
var
|
||||
v: TJSONValue;
|
||||
alangs: TJSONArray;
|
||||
olangs, o: TJSONObject;
|
||||
pair: TJSONPair;
|
||||
i: Integer;
|
||||
id: string;
|
||||
begin
|
||||
v := json.GetValue(TKeyboardInfoFile.SLanguages);
|
||||
|
||||
if v is TJSONArray then
|
||||
begin
|
||||
// Migrate languages[] array to Object
|
||||
alangs := v as TJSONArray;
|
||||
olangs := TJSONObject.Create;
|
||||
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);
|
||||
end
|
||||
else if v is TJSONObject then
|
||||
begin
|
||||
olangs := v as TJsonObject;
|
||||
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;
|
||||
end;
|
||||
end;
|
||||
|
||||
//
|
||||
// name -- from kmp.inf, js
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -87,3 +87,4 @@ const
|
|||
implementation
|
||||
|
||||
end.
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ interface
|
|||
type
|
||||
TKeyboardInfoFile = record
|
||||
const SLanguages = 'languages';
|
||||
const SDisplayName = 'displayName';
|
||||
const SLanguageName = 'languageName';
|
||||
const SScriptName = 'scriptName';
|
||||
const SRegionName = 'regionName';
|
||||
end;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue