diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java index 5c1590dd6b..eec7a8a53a 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java +++ b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java @@ -103,6 +103,8 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven attribute.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_FULLSCREEN; super.onStartInput(attribute, restarting); KMManager.onStartInput(attribute, restarting); + KMManager.resetContext(KeyboardType.KEYBOARD_TYPE_SYSTEM); + //Log.i("SystemKeyboard", "onStartInput"); InputConnection ic = getCurrentInputConnection(); if (ic != null) { diff --git a/android/KMEA/app/src/main/assets/keyboard.html b/android/KMEA/app/src/main/assets/keyboard.html index 413311ce40..b2301c4a71 100644 --- a/android/KMEA/app/src/main/assets/keyboard.html +++ b/android/KMEA/app/src/main/assets/keyboard.html @@ -90,6 +90,10 @@ window.jsInterface.insertText(dn, s); } + function resetContext() { + tavultesoft.keymanweb.resetContext(); + } + function updateKMText(text) { if(text == undefined) { text = ''; diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index 832af6e297..5bfc4c8eca 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -1730,6 +1730,18 @@ public final class KMManager { return result; } + public static void resetContext(KeyboardType kbType) { + if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) { + if (InAppKeyboard != null && InAppKeyboardLoaded) { + InAppKeyboard.loadUrl("javascript:resetContext()"); + } + } else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { + if (SystemKeyboard != null && SystemKeyboardLoaded) { + SystemKeyboard.loadUrl("javascript:resetContext()"); + } + } + } + public static int getCurrentKeyboardIndex(Context context) { return KeyboardPickerActivity.getCurrentKeyboardIndex(context); } diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java index 6fbf59324f..e4ab1c5149 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java @@ -111,6 +111,7 @@ public final class KMTextView extends EditText { int selEnd = textView.getSelectionEnd(); KMManager.InAppKeyboard.loadUrl(String.format("javascript:updateKMText('%s')", textView.getText().toString())); KMManager.InAppKeyboard.loadUrl(String.format("javascript:updateKMSelectionRange(%d,%d)", selStart, selEnd)); + KMManager.resetContext(KeyboardType.KEYBOARD_TYPE_INAPP); } showKeyboard(); } else { diff --git a/android/history.md b/android/history.md index 654f8a5a00..1792b1f15a 100644 --- a/android/history.md +++ b/android/history.md @@ -1,8 +1,9 @@ # Keyman for Android ## 10.0 alpha -* Removed "Share to Facebook" feature (#156) -* Fix dual keyboards that appear when closing Keyman Browser (#220) +* Add feature to reset keyboard to default layer when new input field focused (keymanapp/keyman#288) +* Removed "Share to Facebook" feature (keymanapp/keyman#156) +* Fix dual keyboards that appear when closing Keyman Browser (keymanapp/keyman#220) ## 2017-08-10 2.8.300 stable * No changes, just published latest beta as stable diff --git a/web/.editorconfig b/web/.editorconfig new file mode 100644 index 0000000000..5e07bc9613 --- /dev/null +++ b/web/.editorconfig @@ -0,0 +1,10 @@ +# top-most EditorConfig file +root = true + +; applies to all files +[*] +indent_style = space + +[*.{java, js, sh, css, html, xml}] +indent_size = 2 + diff --git a/web/history.md b/web/history.md index 78caae1d4d..c70dc43eab 100644 --- a/web/history.md +++ b/web/history.md @@ -10,7 +10,7 @@ * Refactored code paths relating to KMW's embedding in the iOS and Android apps. (#211) * Added support for L/R Alt and Ctrl modifiers for keyboards. (#9) (#52) * Added support for use of the Caps Lock state within keyboards if specified by a keyboard designer. - +* Fixed next-layer processing (#116) ## 2017-07-10 2.0.473 stable * 2.0 stable release build. diff --git a/web/samples/index.html b/web/samples/index.html index 139331eae4..6f7b38576f 100644 --- a/web/samples/index.html +++ b/web/samples/index.html @@ -26,9 +26,12 @@

Uncompiled Keymanweb - test desktop MutationObserver functionality

Uncompiled Keymanweb - light test for touch-based MutationObserver functionality

Uncompiled Keymanweb - test/stress-test touch-based MutationObserver functionality

-

Uncompiled Keymanweb - test touch-based keyboards with heterogenous layer structures/row counts.

-

Uncompiled KeymanWeb - tests keyboard error-handling functionality.

-

Uncompiled KeymanWeb - tests the new Attachment/Enablement API functionality.

-

Uncompiled KeymanWeb - chirality testing.

+

Uncompiled KeymanWeb - tests keyboard error-handling functionality

+

Uncompiled KeymanWeb - tests the new Attachment/Enablement API functionality

+

Uncompiled KeymanWeb - chirality testing

+

Issue Testing

+

Long-press on numeric and symbolic layer testing

+

Next-layer processing testing

+

Uxxxx code testing

diff --git a/web/samples/issue115/index.html b/web/samples/issue115/index.html index d46c564f5a..803dc2b839 100644 --- a/web/samples/issue115/index.html +++ b/web/samples/issue115/index.html @@ -46,7 +46,8 @@ + + + + + + + + + + + + + + + + + + + + +

KeymanWeb Sample Page - Uncompiled Source

+ +
+ +
+ +

Type in your language in this text area:

+ + +

or in this input field:

+ + +

Return to testing home page

+
+ + + + + diff --git a/web/samples/issue116/next_layer_tests-1.0.js b/web/samples/issue116/next_layer_tests-1.0.js new file mode 100644 index 0000000000..daacb4a487 --- /dev/null +++ b/web/samples/issue116/next_layer_tests-1.0.js @@ -0,0 +1 @@ +KeymanWeb.KR(new Keyboard_next_layer_tests());function Keyboard_next_layer_tests(){this.KI="Keyboard_next_layer_tests";this.KN="Issue 116";this.KV=null;this.KH='';this.KM=0;this.KBVER="1.0";this.KVKD="T_CAPS_Q T_CAPS_W T_CAPS_E T_CAPS_R T_CAPS_T T_CAPS_Y T_CAPS_U T_CAPS_I T_CAPS_O T_CAPS_P T_CAPS_A T_CAPS_S T_CAPS_D T_CAPS_F T_CAPS_G T_CAPS_H T_CAPS_J T_CAPS_K T_CAPS_L T_CAPS_Z T_CAPS_X T_CAPS_C T_CAPS_V T_CAPS_B T_CAPS_N T_CAPS_M";this.KVKL={"tablet":{"font":"Tahoma","layer":[{"id":"default","row":[{"id":1,"key":[{"id":"K_Q","text":"q"},{"id":"K_W","text":"w"},{"id":"K_E","text":"e"},{"id":"K_R","text":"r"},{"id":"K_T","text":"t"},{"id":"K_Y","text":"y"},{"id":"K_U","text":"u"},{"id":"K_I","text":"i"},{"id":"K_O","text":"o"},{"id":"K_P","text":"p"}]},{"id":2,"key":[{"id":"K_A","text":"a","pad":70},{"id":"K_S","text":"s"},{"id":"K_D","text":"d"},{"id":"K_F","text":"f"},{"id":"K_G","text":"g"},{"id":"K_H","text":"h"},{"id":"K_J","text":"j"},{"id":"K_K","text":"k"},{"id":"K_L","text":"l"},{"id":"T_new_20","text":"","width":"10","sp":"10"}]},{"id":3,"key":[{"id":"K_SHIFT","text":"*Shift*","width":"110","sp":"1","nextlayer":"shift","sk":[{"text":"*Caps*","id":"T_new_409","sp":"1","nextlayer":"CAPS"}]},{"id":"K_Z","text":"z"},{"id":"K_X","text":"x"},{"id":"K_C","text":"c"},{"id":"K_V","text":"v"},{"id":"K_B","text":"b"},{"id":"K_N","text":"n"},{"id":"K_M","text":"m"},{"id":"K_PERIOD","text":".","sk":[{"text":",","id":"K_COMMA"},{"text":"!","id":"K_1","layer":"shift"},{"text":"?","id":"K_SLASH","layer":"shift"},{"text":"'","id":"K_QUOTE"},{"text":"\"","id":"K_QUOTE","layer":"shift"},{"text":"\\","id":"K_BKSLASH"},{"text":":","id":"K_COLON","layer":"shift"},{"text":";","id":"K_COLON"}]},{"id":"K_BKSP","text":"*BkSp*","width":"90","sp":"1"}]},{"id":4,"key":[{"id":"K_NUMLOCK","text":"*123*","width":"140","sp":"1","nextlayer":"numeric"},{"id":"K_LOPT","text":"*Menu*","width":"120","sp":"1"},{"id":"K_SPACE","text":"","width":"630","sp":"0"},{"id":"K_ENTER","text":"*Enter*","width":"140","sp":"1"}]}]},{"id":"shift","row":[{"id":1,"key":[{"id":"K_Q","text":"Q","nextlayer":"default"},{"id":"K_W","text":"W","nextlayer":"default"},{"id":"K_E","text":"E","nextlayer":"default"},{"id":"K_R","text":"R","nextlayer":"default"},{"id":"K_T","text":"T","nextlayer":"default"},{"id":"K_Y","text":"Y","nextlayer":"default"},{"id":"K_U","text":"U","nextlayer":"default"},{"id":"K_I","text":"I","nextlayer":"default"},{"id":"K_O","text":"O","nextlayer":"default"},{"id":"K_P","text":"P","nextlayer":"default"}]},{"id":2,"key":[{"id":"K_A","text":"A","pad":70,"nextlayer":"default"},{"id":"K_S","text":"S","nextlayer":"default"},{"id":"K_D","text":"D","nextlayer":"default"},{"id":"K_F","text":"F","nextlayer":"default"},{"id":"K_G","text":"G","nextlayer":"default"},{"id":"K_H","text":"H","nextlayer":"default"},{"id":"K_J","text":"J","nextlayer":"default"},{"id":"K_K","text":"K","nextlayer":"default"},{"id":"K_L","text":"L","nextlayer":"default"},{"id":"T_new_54","text":"","width":"10","sp":"10"}]},{"id":3,"key":[{"id":"K_SHIFT","text":"*Shift*","width":"110","sp":"2","nextlayer":"default","sk":[{"text":"*Caps*","id":"T_new_205","sp":"1","nextlayer":"CAPS"}]},{"id":"K_Z","text":"Z","nextlayer":"default"},{"id":"K_X","text":"X","nextlayer":"default"},{"id":"K_C","text":"C","nextlayer":"default"},{"id":"K_V","text":"V","nextlayer":"default"},{"id":"K_B","text":"B","nextlayer":"default"},{"id":"K_N","text":"N","nextlayer":"default"},{"id":"K_M","text":"M","nextlayer":"default"},{"id":"K_PERIOD","text":".","nextlayer":"default","sk":[{"text":",","id":"K_COMMA","nextlayer":"default","layer":"default"},{"text":"!","id":"K_1","nextlayer":"default","layer":"shift"},{"text":"?","id":"K_SLASH","nextlayer":"default","layer":"shift"},{"text":"'","id":"K_QUOTE","nextlayer":"default","layer":"default"},{"text":"\"","id":"K_QUOTE","nextlayer":"default","layer":"shift"},{"text":"\\","id":"K_BKSLASH","nextlayer":"default","layer":"default"},{"text":":","id":"K_COLON","nextlayer":"default","layer":"shift"},{"text":";","id":"K_COLON","nextlayer":"default","layer":"default"}]},{"id":"K_BKSP","text":"*BkSp*","width":"90","sp":"1"}]},{"id":4,"key":[{"id":"K_NUMLOCK","text":"*123*","width":"140","sp":"1","nextlayer":"numeric"},{"id":"K_LOPT","text":"*Menu*","width":"120","sp":"1"},{"id":"K_SPACE","text":"","width":"630","sp":"0"},{"id":"K_ENTER","text":"*Enter*","width":"140","sp":"1"}]}]},{"id":"numeric","row":[{"id":1,"key":[{"id":"K_1","text":"1"},{"id":"K_2","text":"2"},{"id":"K_3","text":"3"},{"id":"K_4","text":"4"},{"id":"K_5","text":"5"},{"id":"K_6","text":"6"},{"id":"K_7","text":"7"},{"id":"K_8","text":"8"},{"id":"K_9","text":"9"},{"id":"K_0","text":"0"}]},{"id":2,"key":[{"id":"K_4","layer":"shift","pad":70,"text":"$"},{"id":"K_2","layer":"shift","text":"@"},{"id":"K_3","layer":"shift","text":"#"},{"id":"K_5","layer":"shift","text":"%"},{"id":"K_7","layer":"shift","text":"&"},{"id":"K_HYPHEN","layer":"shift","text":"_"},{"id":"K_EQUAL","layer":"default","text":"="},{"id":"K_BKSLASH","layer":"shift","text":"|"},{"id":"K_BKSLASH","layer":"default","text":"\\"},{"text":"","width":"10","sp":"10"}]},{"id":3,"key":[{"id":"K_SHIFT","text":"*Shift*","width":"110","sp":"1"},{"id":"K_LBRKT","sk":[{"id":"U_00AB","text":"\u00AB"},{"id":"K_COMMA","text":"<","layer":"shift"},{"id":"K_LBRKT","text":"{","layer":"shift"}],"text":"["},{"id":"K_9","layer":"shift","text":"("},{"id":"K_0","layer":"shift","text":")"},{"id":"K_RBRKT","sk":[{"id":"U_00BB","text":"\u00BB"},{"id":"K_PERIOD","text":">","layer":"shift"},{"id":"K_RBRKT","text":"}","layer":"shift"}],"text":"]"},{"id":"K_EQUAL","layer":"shift","text":"+"},{"id":"K_HYPHEN","layer":"default","text":"-"},{"id":"K_8","layer":"shift","text":"*"},{"id":"K_SLASH","layer":"default","text":"\/"},{"id":"K_BKSP","text":"*BkSp*","width":"90","sp":"1"}]},{"id":4,"key":[{"id":"K_LOWER","text":"*abc*","width":"140","sp":"1","nextlayer":"default"},{"id":"K_LOPT","text":"*Menu*","width":"120","sp":"1"},{"id":"K_SPACE","width":"630","sp":"0","text":""},{"id":"K_ENTER","text":"*Enter*","width":"140","sp":"1"}]}]},{"id":"CAPS","row":[{"id":1,"key":[{"id":"T_CAPS_Q","text":"Q"},{"id":"T_CAPS_W","text":"W"},{"id":"T_CAPS_E","text":"E"},{"id":"T_CAPS_R","text":"R"},{"id":"T_CAPS_T","text":"T"},{"id":"T_CAPS_Y","text":"Y"},{"id":"T_CAPS_U","text":"U"},{"id":"T_CAPS_I","text":"I"},{"id":"T_CAPS_O","text":"O"},{"id":"T_CAPS_P","text":"P"}]},{"id":2,"key":[{"id":"T_CAPS_A","text":"A","pad":70},{"id":"T_CAPS_S","text":"S"},{"id":"T_CAPS_D","text":"D"},{"id":"T_CAPS_F","text":"F"},{"id":"T_CAPS_G","text":"G"},{"id":"T_CAPS_H","text":"H"},{"id":"T_CAPS_J","text":"J"},{"id":"T_CAPS_K","text":"K"},{"id":"T_CAPS_L","text":"L"},{"id":"T_new_54","text":"","width":"10","sp":"10"}]},{"id":3,"key":[{"id":"K_SHIFT","text":"*Caps*","width":"110","sp":"2","nextlayer":"default"},{"id":"T_CAPS_Z","text":"Z"},{"id":"T_CAPS_X","text":"X"},{"id":"T_CAPS_C","text":"C"},{"id":"T_CAPS_V","text":"V"},{"id":"T_CAPS_B","text":"B"},{"id":"T_CAPS_N","text":"N"},{"id":"T_CAPS_M","text":"M"},{"id":"K_PERIOD","text":".","sk":[{"text":",","id":"K_COMMA","layer":"default"},{"text":"!","id":"K_1","layer":"shift"},{"text":"?","id":"K_SLASH","layer":"shift"},{"text":"'","id":"K_QUOTE","layer":"default"},{"text":"\"","id":"K_QUOTE","layer":"shift"},{"text":"\\","id":"K_BKSLASH","layer":"default"},{"text":":","id":"K_COLON","layer":"shift"},{"text":";","id":"K_COLON","layer":"default"}]},{"id":"K_BKSP","text":"*BkSp*","width":"90","sp":"1"}]},{"id":4,"key":[{"id":"K_NUMLOCK","text":"*123*","width":"140","sp":"1","nextlayer":"numeric"},{"id":"K_LOPT","text":"*Menu*","width":"120","sp":"1"},{"id":"K_SPACE","text":"","width":"630","sp":"0"},{"id":"K_ENTER","text":"*Enter*","width":"140","sp":"1"}]}]}]},"phone":{"font":"Tahoma","layer":[{"id":"default","row":[{"id":1,"key":[{"id":"K_Q","text":"q"},{"id":"K_W","text":"w"},{"id":"K_E","text":"e"},{"id":"K_R","text":"r"},{"id":"K_T","text":"t"},{"id":"K_Y","text":"y"},{"id":"K_U","text":"u"},{"id":"K_I","text":"i"},{"id":"K_O","text":"o"},{"id":"K_P","text":"p"}]},{"id":2,"key":[{"id":"K_A","text":"a","pad":"50"},{"id":"K_S","text":"s"},{"id":"K_D","text":"d"},{"id":"K_F","text":"f"},{"id":"K_G","text":"g"},{"id":"K_H","text":"h"},{"id":"K_J","text":"j"},{"id":"K_K","text":"k"},{"id":"K_L","text":"l"},{"id":"T_new_122","text":"","width":"10","sp":"10"}]},{"id":3,"key":[{"id":"K_SHIFT","text":"*Shift*","sp":"1","nextlayer":"shift","sk":[{"text":"*Caps*","id":"T_new_754","sp":"1","nextlayer":"CAPS"}]},{"id":"K_Z","text":"z"},{"id":"K_X","text":"x"},{"id":"K_C","text":"c"},{"id":"K_V","text":"v"},{"id":"K_B","text":"b"},{"id":"K_N","text":"n"},{"id":"K_M","text":"m"},{"id":"K_PERIOD","text":".","sk":[{"text":",","id":"K_COMMA"},{"text":"!","id":"K_1","layer":"shift"},{"text":"?","id":"K_SLASH","layer":"shift"},{"text":"'","id":"K_QUOTE"},{"text":"\"","id":"K_QUOTE","layer":"shift"},{"text":"\\","id":"K_BKSLASH"},{"text":":","id":"K_COLON","layer":"shift"},{"text":";","id":"K_COLON"}]},{"id":"K_BKSP","text":"*BkSp*","width":"100","sp":"1"}]},{"id":4,"key":[{"id":"K_NUMLOCK","text":"*123*","width":"150","sp":"1","nextlayer":"numeric"},{"id":"K_LOPT","text":"*Menu*","width":"120","sp":"1"},{"id":"K_SPACE","text":"","width":"610","sp":"0"},{"id":"K_ENTER","text":"*Enter*","width":"150","sp":"1"}]}]},{"id":"shift","row":[{"id":1,"key":[{"id":"K_Q","text":"Q","nextlayer":"default"},{"id":"K_W","text":"W","nextlayer":"default"},{"id":"K_E","text":"E","nextlayer":"default"},{"id":"K_R","text":"R","nextlayer":"default"},{"id":"K_T","text":"T","nextlayer":"default"},{"id":"K_Y","text":"Y","nextlayer":"default"},{"id":"K_U","text":"U","nextlayer":"default"},{"id":"K_I","text":"I","nextlayer":"default"},{"id":"K_O","text":"O","nextlayer":"default"},{"id":"K_P","text":"P","nextlayer":"default"}]},{"id":2,"key":[{"id":"K_A","text":"A","pad":"50","nextlayer":"default"},{"id":"K_S","text":"S","nextlayer":"default"},{"id":"K_D","text":"D","nextlayer":"default"},{"id":"K_F","text":"F","nextlayer":"default"},{"id":"K_G","text":"G","nextlayer":"default"},{"id":"K_H","text":"H","nextlayer":"default"},{"id":"K_J","text":"J","nextlayer":"default"},{"id":"K_K","text":"K","nextlayer":"default"},{"id":"K_L","text":"L","nextlayer":"default"},{"id":"T_new_535","text":"","width":"10","sp":"10"}]},{"id":3,"key":[{"id":"K_SHIFT","text":"*Shift*","sp":"2","nextlayer":"default"},{"id":"K_Z","text":"Z","nextlayer":"default"},{"id":"K_X","text":"X","nextlayer":"default"},{"id":"K_C","text":"C","nextlayer":"default"},{"id":"K_V","text":"V","nextlayer":"default"},{"id":"K_B","text":"B","nextlayer":"default"},{"id":"K_N","text":"N","nextlayer":"default"},{"id":"K_M","text":"M","nextlayer":"default"},{"id":"K_PERIOD","text":".","nextlayer":"default","sk":[{"text":",","id":"K_COMMA","nextlayer":"default","layer":"default"},{"text":"!","id":"K_1","nextlayer":"default","layer":"shift"},{"text":"?","id":"K_SLASH","nextlayer":"default","layer":"shift"},{"text":"'","id":"K_QUOTE","nextlayer":"default","layer":"default"},{"text":"\"","id":"K_QUOTE","nextlayer":"default","layer":"shift"},{"text":"\\","id":"K_BKSLASH","nextlayer":"default","layer":"default"},{"text":":","id":"K_COLON","nextlayer":"default","layer":"shift"},{"text":";","id":"K_COLON","nextlayer":"default","layer":"default"}]},{"id":"K_BKSP","text":"*BkSp*","sp":"1"}]},{"id":4,"key":[{"id":"K_NUMLOCK","text":"*123*","width":"150","sp":"1","nextlayer":"numeric"},{"id":"K_LOPT","text":"*Menu*","width":"120","sp":"1"},{"id":"K_SPACE","text":"","width":"610","sp":"0"},{"id":"K_ENTER","text":"*Enter*","width":"150","sp":"1"}]}]},{"id":"numeric","row":[{"id":1,"key":[{"id":"K_1","text":"1"},{"id":"K_2","text":"2"},{"id":"K_3","text":"3"},{"id":"K_4","text":"4"},{"id":"K_5","text":"5"},{"id":"K_6","text":"6"},{"id":"K_7","text":"7"},{"id":"K_8","text":"8"},{"id":"K_9","text":"9"},{"id":"K_0","text":"0"}]},{"id":2,"key":[{"id":"K_4","layer":"shift","pad":"50","text":"$"},{"id":"K_2","layer":"shift","text":"@"},{"id":"K_3","layer":"shift","text":"#"},{"id":"K_5","layer":"shift","text":"%"},{"id":"K_6","layer":"shift","text":"&"},{"id":"K_HYPHEN","layer":"shift","text":"_"},{"id":"K_EQUAL","layer":"default","text":"="},{"id":"K_BKSLASH","layer":"shift","text":"|"},{"id":"K_BKSLASH","layer":"default","text":"\\"},{"text":"","width":"10","sp":"10"}]},{"id":3,"key":[{"id":"K_LBRKT","pad":"110","sk":[{"id":"U_00AB","text":"\u00AB"},{"id":"K_COMMA","text":"<","layer":"shift"},{"id":"K_LBRKT","text":"{","layer":"shift"}],"text":"["},{"id":"K_9","layer":"shift","text":"("},{"id":"K_0","layer":"shift","text":")"},{"id":"K_RBRKT","sk":[{"id":"U_00BB","text":"\u00BB"},{"id":"K_PERIOD","text":">","layer":"shift"},{"id":"K_RBRKT","text":"}","layer":"shift"}],"text":"]"},{"id":"K_EQUAL","layer":"shift","text":"+"},{"id":"K_HYPHEN","text":"-"},{"id":"K_8","layer":"shift","text":"*"},{"id":"K_SLASH","text":"\/"},{"id":"K_BKSP","text":"*BkSp*","width":"100","sp":"1"}]},{"id":4,"key":[{"id":"K_LOWER","text":"*abc*","width":"150","sp":"1","nextlayer":"default"},{"id":"K_LOPT","text":"*Menu*","width":"120","sp":"1"},{"id":"K_SPACE","width":"610","sp":"0","text":""},{"id":"K_ENTER","text":"*Enter*","width":"150","sp":"1"}]}]},{"id":"CAPS","row":[{"id":1,"key":[{"id":"T_CAPS_Q","text":"Q","layer":"default"},{"id":"T_CAPS_W","text":"W"},{"id":"T_CAPS_E","text":"E"},{"id":"T_CAPS_R","text":"R"},{"id":"T_CAPS_T","text":"T"},{"id":"T_CAPS_Y","text":"Y"},{"id":"T_CAPS_U","text":"U"},{"id":"T_CAPS_I","text":"I"},{"id":"T_CAPS_O","text":"O"},{"id":"T_CAPS_P","text":"P"}]},{"id":2,"key":[{"id":"T_CAPS_A","text":"A","pad":"50"},{"id":"T_CAPS_U","text":"S"},{"id":"T_CAPS_D","text":"D"},{"id":"T_CAPS_F","text":"F"},{"id":"T_CAPS_G","text":"G"},{"id":"T_CAPS_H","text":"H"},{"id":"T_CAPS_J","text":"J"},{"id":"T_CAPS_K","text":"K"},{"id":"T_CAPS_L","text":"L"},{"id":"T_new_535","text":"","width":"10","sp":"10"}]},{"id":3,"key":[{"id":"K_SHIFT","text":"*Shift*","sp":"2","nextlayer":"default"},{"id":"T_CAPS_Z","text":"Z"},{"id":"T_CAPS_X","text":"X"},{"id":"T_CAPS_C","text":"C"},{"id":"T_CAPS_V","text":"V"},{"id":"T_CAPS_B","text":"B"},{"id":"T_CAPS_N","text":"N"},{"id":"T_CAPS_M","text":"M"},{"id":"K_PERIOD","text":".","sk":[{"text":",","id":"K_COMMA","layer":"default"},{"text":"!","id":"K_1","layer":"shift"},{"text":"?","id":"K_SLASH","layer":"shift"},{"text":"'","id":"K_QUOTE","layer":"default"},{"text":"\"","id":"K_QUOTE","layer":"shift"},{"text":"\\","id":"K_BKSLASH","layer":"default"},{"text":":","id":"K_COLON","layer":"shift"},{"text":";","id":"K_COLON","layer":"default"}]},{"id":"K_BKSP","text":"*BkSp*","sp":"1"}]},{"id":4,"key":[{"id":"K_NUMLOCK","text":"*123*","width":"150","sp":"1","nextlayer":"numeric"},{"id":"K_LOPT","text":"*Menu*","width":"120","sp":"1"},{"id":"K_SPACE","text":"","width":"610","sp":"0"},{"id":"K_ENTER","text":"*Enter*","width":"150","sp":"1"}]}]}]}};this.KVER="10.0.870.0";this.gs=function(t,e) {return this.g0(t,e);};this.g0=function(t,e) {var k=KeymanWeb,r=0,m=0;if(k.KKM(e,16384,256)) {r=m=1;k.KO(0,t,"CAPS MODE Q");}else if(k.KKM(e,16384,257)) {r=m=1;k.KO(0,t,"CAPS MODE W");}else if(k.KKM(e,16384,258)) {r=m=1;k.KO(0,t,"CAPS MODE E");}else if(k.KKM(e,16384,259)) {r=m=1;k.KO(0,t,"CAPS MODE R");}else if(k.KKM(e,16384,260)) {r=m=1;k.KO(0,t,"CAPS MODE T");}else if(k.KKM(e,16384,261)) {r=m=1;k.KO(0,t,"CAPS MODE Y");}else if(k.KKM(e,16384,262)) {r=m=1;k.KO(0,t,"CAPS MODE U");}else if(k.KKM(e,16384,263)) {r=m=1;k.KO(0,t,"CAPS MODE I");}else if(k.KKM(e,16384,264)) {r=m=1;k.KO(0,t,"CAPS MODE O");}else if(k.KKM(e,16384,265)) {r=m=1;k.KO(0,t,"CAPS MODE P");}else if(k.KKM(e,16384,266)) {r=m=1;k.KO(0,t,"CAPS MODE A");}else if(k.KKM(e,16384,267)) {r=m=1;k.KO(0,t,"CAPS MODE S");}else if(k.KKM(e,16384,268)) {r=m=1;k.KO(0,t,"CAPS MODE D");}else if(k.KKM(e,16384,269)) {r=m=1;k.KO(0,t,"CAPS MODE F");}else if(k.KKM(e,16384,270)) {r=m=1;k.KO(0,t,"CAPS MODE G");}else if(k.KKM(e,16384,271)) {r=m=1;k.KO(0,t,"CAPS MODE H");}else if(k.KKM(e,16384,272)) {r=m=1;k.KO(0,t,"CAPS MODE J");}else if(k.KKM(e,16384,273)) {r=m=1;k.KO(0,t,"CAPS MODE K");}else if(k.KKM(e,16384,274)) {r=m=1;k.KO(0,t,"CAPS MODE L");}else if(k.KKM(e,16384,275)) {r=m=1;k.KO(0,t,"CAPS MODE Z");}else if(k.KKM(e,16384,276)) {r=m=1;k.KO(0,t,"CAPS MODE X");}else if(k.KKM(e,16384,277)) {r=m=1;k.KO(0,t,"CAPS MODE C");}else if(k.KKM(e,16384,278)) {r=m=1;k.KO(0,t,"CAPS MODE V");}else if(k.KKM(e,16384,279)) {r=m=1;k.KO(0,t,"CAPS MODE B");}else if(k.KKM(e,16384,280)) {r=m=1;k.KO(0,t,"CAPS MODE N");}else if(k.KKM(e,16384,281)) {r=m=1;k.KO(0,t,"CAPS MODE M");}return r;};} \ No newline at end of file diff --git a/web/samples/issue116/next_layer_tests-1.0.json b/web/samples/issue116/next_layer_tests-1.0.json new file mode 100644 index 0000000000..d02a2de16d --- /dev/null +++ b/web/samples/issue116/next_layer_tests-1.0.json @@ -0,0 +1,20 @@ +{ + "options" : { + "device" : "any", + "keyboardBaseUri" : "http://your-host-here/keyboard-path/", + "fontBaseUri" : "http://your-host-here/font-path/" + }, + "keyboard" : { + "id" : "next_layer_tests", + "name" : "Issue 116", + "filename" : "next_layer_tests-1.0.js", + "version" : "1.0", + "lastModified" : "2017-09-15T08:13:57.822+07:00", + "languages" : [ + { + "id" : "eng", + "name" : "English" + } + ] + } +} diff --git a/web/samples/issue116/next_layer_tests-1.0_load.js b/web/samples/issue116/next_layer_tests-1.0_load.js new file mode 100644 index 0000000000..b37b56ff58 --- /dev/null +++ b/web/samples/issue116/next_layer_tests-1.0_load.js @@ -0,0 +1 @@ +KeymanWeb.KRS(new Stub_Keyboard_next_layer_tests()); function Stub_Keyboard_next_layer_tests() {this.KF="next_layer_tests-1.0.js";this.KI="Keyboard_next_layer_tests";this.KN="Issue 116";} \ No newline at end of file diff --git a/web/samples/issue116/next_layer_tests-layout.js b/web/samples/issue116/next_layer_tests-layout.js new file mode 100644 index 0000000000..c1dbed862d --- /dev/null +++ b/web/samples/issue116/next_layer_tests-layout.js @@ -0,0 +1,1853 @@ +{ + "tablet": { + "font": "Tahoma", + "layer": [ + { + "id": "default", + "row": [ + { + "id": 1, + "key": [ + { + "id": "K_Q", + "text": "q" + }, + { + "id": "K_W", + "text": "w" + }, + { + "id": "K_E", + "text": "e" + }, + { + "id": "K_R", + "text": "r" + }, + { + "id": "K_T", + "text": "t" + }, + { + "id": "K_Y", + "text": "y" + }, + { + "id": "K_U", + "text": "u" + }, + { + "id": "K_I", + "text": "i" + }, + { + "id": "K_O", + "text": "o" + }, + { + "id": "K_P", + "text": "p" + } + ] + }, + { + "id": 2, + "key": [ + { + "id": "K_A", + "text": "a", + "pad": 70 + }, + { + "id": "K_S", + "text": "s" + }, + { + "id": "K_D", + "text": "d" + }, + { + "id": "K_F", + "text": "f" + }, + { + "id": "K_G", + "text": "g" + }, + { + "id": "K_H", + "text": "h" + }, + { + "id": "K_J", + "text": "j" + }, + { + "id": "K_K", + "text": "k" + }, + { + "id": "K_L", + "text": "l" + }, + { + "id": "T_new_20", + "text": "", + "width": "10", + "sp": "10" + } + ] + }, + { + "id": 3, + "key": [ + { + "id": "K_SHIFT", + "text": "*Shift*", + "width": "110", + "sp": "1", + "nextlayer": "shift", + "sk": [ + { + "text": "*Caps*", + "id": "T_new_409", + "sp": "1", + "nextlayer": "CAPS" + } + ] + }, + { + "id": "K_Z", + "text": "z" + }, + { + "id": "K_X", + "text": "x" + }, + { + "id": "K_C", + "text": "c" + }, + { + "id": "K_V", + "text": "v" + }, + { + "id": "K_B", + "text": "b" + }, + { + "id": "K_N", + "text": "n" + }, + { + "id": "K_M", + "text": "m" + }, + { + "id": "K_PERIOD", + "text": ".", + "sk": [ + { + "text": ",", + "id": "K_COMMA" + }, + { + "text": "!", + "id": "K_1", + "layer": "shift" + }, + { + "text": "?", + "id": "K_SLASH", + "layer": "shift" + }, + { + "text": "'", + "id": "K_QUOTE" + }, + { + "text": "\"", + "id": "K_QUOTE", + "layer": "shift" + }, + { + "text": "\\", + "id": "K_BKSLASH" + }, + { + "text": ":", + "id": "K_COLON", + "layer": "shift" + }, + { + "text": ";", + "id": "K_COLON" + } + ] + }, + { + "id": "K_BKSP", + "text": "*BkSp*", + "width": "90", + "sp": "1" + } + ] + }, + { + "id": 4, + "key": [ + { + "id": "K_NUMLOCK", + "text": "*123*", + "width": "140", + "sp": "1", + "nextlayer": "numeric" + }, + { + "id": "K_LOPT", + "text": "*Menu*", + "width": "120", + "sp": "1" + }, + { + "id": "K_SPACE", + "text": "", + "width": "630", + "sp": "0" + }, + { + "id": "K_ENTER", + "text": "*Enter*", + "width": "140", + "sp": "1" + } + ] + } + ] + }, + { + "id": "shift", + "row": [ + { + "id": 1, + "key": [ + { + "id": "K_Q", + "text": "Q", + "nextlayer": "default" + }, + { + "id": "K_W", + "text": "W", + "nextlayer": "default" + }, + { + "id": "K_E", + "text": "E", + "nextlayer": "default" + }, + { + "id": "K_R", + "text": "R", + "nextlayer": "default" + }, + { + "id": "K_T", + "text": "T", + "nextlayer": "default" + }, + { + "id": "K_Y", + "text": "Y", + "nextlayer": "default" + }, + { + "id": "K_U", + "text": "U", + "nextlayer": "default" + }, + { + "id": "K_I", + "text": "I", + "nextlayer": "default" + }, + { + "id": "K_O", + "text": "O", + "nextlayer": "default" + }, + { + "id": "K_P", + "text": "P", + "nextlayer": "default" + } + ] + }, + { + "id": 2, + "key": [ + { + "id": "K_A", + "text": "A", + "pad": 70, + "nextlayer": "default" + }, + { + "id": "K_S", + "text": "S", + "nextlayer": "default" + }, + { + "id": "K_D", + "text": "D", + "nextlayer": "default" + }, + { + "id": "K_F", + "text": "F", + "nextlayer": "default" + }, + { + "id": "K_G", + "text": "G", + "nextlayer": "default" + }, + { + "id": "K_H", + "text": "H", + "nextlayer": "default" + }, + { + "id": "K_J", + "text": "J", + "nextlayer": "default" + }, + { + "id": "K_K", + "text": "K", + "nextlayer": "default" + }, + { + "id": "K_L", + "text": "L", + "nextlayer": "default" + }, + { + "id": "T_new_54", + "text": "", + "width": "10", + "sp": "10" + } + ] + }, + { + "id": 3, + "key": [ + { + "id": "K_SHIFT", + "text": "*Shift*", + "width": "110", + "sp": "2", + "nextlayer": "default", + "sk": [ + { + "text": "*Caps*", + "id": "T_new_205", + "sp": "1", + "nextlayer": "CAPS" + } + ] + }, + { + "id": "K_Z", + "text": "Z", + "nextlayer": "default" + }, + { + "id": "K_X", + "text": "X", + "nextlayer": "default" + }, + { + "id": "K_C", + "text": "C", + "nextlayer": "default" + }, + { + "id": "K_V", + "text": "V", + "nextlayer": "default" + }, + { + "id": "K_B", + "text": "B", + "nextlayer": "default" + }, + { + "id": "K_N", + "text": "N", + "nextlayer": "default" + }, + { + "id": "K_M", + "text": "M", + "nextlayer": "default" + }, + { + "id": "K_PERIOD", + "text": ".", + "nextlayer": "default", + "sk": [ + { + "text": ",", + "id": "K_COMMA", + "nextlayer": "default", + "layer": "default" + }, + { + "text": "!", + "id": "K_1", + "nextlayer": "default", + "layer": "shift" + }, + { + "text": "?", + "id": "K_SLASH", + "nextlayer": "default", + "layer": "shift" + }, + { + "text": "'", + "id": "K_QUOTE", + "nextlayer": "default", + "layer": "default" + }, + { + "text": "\"", + "id": "K_QUOTE", + "nextlayer": "default", + "layer": "shift" + }, + { + "text": "\\", + "id": "K_BKSLASH", + "nextlayer": "default", + "layer": "default" + }, + { + "text": ":", + "id": "K_COLON", + "nextlayer": "default", + "layer": "shift" + }, + { + "text": ";", + "id": "K_COLON", + "nextlayer": "default", + "layer": "default" + } + ] + }, + { + "id": "K_BKSP", + "text": "*BkSp*", + "width": "90", + "sp": "1" + } + ] + }, + { + "id": 4, + "key": [ + { + "id": "K_NUMLOCK", + "text": "*123*", + "width": "140", + "sp": "1", + "nextlayer": "numeric" + }, + { + "id": "K_LOPT", + "text": "*Menu*", + "width": "120", + "sp": "1" + }, + { + "id": "K_SPACE", + "text": "", + "width": "630", + "sp": "0" + }, + { + "id": "K_ENTER", + "text": "*Enter*", + "width": "140", + "sp": "1" + } + ] + } + ] + }, + { + "id": "numeric", + "row": [ + { + "id": 1, + "key": [ + { + "id": "K_1", + "text": "1" + }, + { + "id": "K_2", + "text": "2" + }, + { + "id": "K_3", + "text": "3" + }, + { + "id": "K_4", + "text": "4" + }, + { + "id": "K_5", + "text": "5" + }, + { + "id": "K_6", + "text": "6" + }, + { + "id": "K_7", + "text": "7" + }, + { + "id": "K_8", + "text": "8" + }, + { + "id": "K_9", + "text": "9" + }, + { + "id": "K_0", + "text": "0" + } + ] + }, + { + "id": 2, + "key": [ + { + "id": "K_4", + "layer": "shift", + "pad": 70, + "text": "$" + }, + { + "id": "K_2", + "layer": "shift", + "text": "@" + }, + { + "id": "K_3", + "layer": "shift", + "text": "#" + }, + { + "id": "K_5", + "layer": "shift", + "text": "%" + }, + { + "id": "K_7", + "layer": "shift", + "text": "&" + }, + { + "id": "K_HYPHEN", + "layer": "shift", + "text": "_" + }, + { + "id": "K_EQUAL", + "layer": "default", + "text": "=" + }, + { + "id": "K_BKSLASH", + "layer": "shift", + "text": "|" + }, + { + "id": "K_BKSLASH", + "layer": "default", + "text": "\\" + }, + { + "text": "", + "width": "10", + "sp": "10" + } + ] + }, + { + "id": 3, + "key": [ + { + "id": "K_SHIFT", + "text": "*Shift*", + "width": "110", + "sp": "1" + }, + { + "id": "K_LBRKT", + "sk": [ + { + "id": "U_00AB", + "text": "«" + }, + { + "id": "K_COMMA", + "text": "<", + "layer": "shift" + }, + { + "id": "K_LBRKT", + "text": "{", + "layer": "shift" + } + ], + "text": "[" + }, + { + "id": "K_9", + "layer": "shift", + "text": "(" + }, + { + "id": "K_0", + "layer": "shift", + "text": ")" + }, + { + "id": "K_RBRKT", + "sk": [ + { + "id": "U_00BB", + "text": "»" + }, + { + "id": "K_PERIOD", + "text": ">", + "layer": "shift" + }, + { + "id": "K_RBRKT", + "text": "}", + "layer": "shift" + } + ], + "text": "]" + }, + { + "id": "K_EQUAL", + "layer": "shift", + "text": "+" + }, + { + "id": "K_HYPHEN", + "layer": "default", + "text": "-" + }, + { + "id": "K_8", + "layer": "shift", + "text": "*" + }, + { + "id": "K_SLASH", + "layer": "default", + "text": "/" + }, + { + "id": "K_BKSP", + "text": "*BkSp*", + "width": "90", + "sp": "1" + } + ] + }, + { + "id": 4, + "key": [ + { + "id": "K_LOWER", + "text": "*abc*", + "width": "140", + "sp": "1", + "nextlayer": "default" + }, + { + "id": "K_LOPT", + "text": "*Menu*", + "width": "120", + "sp": "1" + }, + { + "id": "K_SPACE", + "width": "630", + "sp": "0", + "text": "" + }, + { + "id": "K_ENTER", + "text": "*Enter*", + "width": "140", + "sp": "1" + } + ] + } + ] + }, + { + "id": "CAPS", + "row": [ + { + "id": 1, + "key": [ + { + "id": "T_CAPS_Q", + "text": "Q" + }, + { + "id": "T_CAPS_W", + "text": "W" + }, + { + "id": "T_CAPS_E", + "text": "E" + }, + { + "id": "T_CAPS_R", + "text": "R" + }, + { + "id": "T_CAPS_T", + "text": "T" + }, + { + "id": "T_CAPS_Y", + "text": "Y" + }, + { + "id": "T_CAPS_U", + "text": "U" + }, + { + "id": "T_CAPS_I", + "text": "I" + }, + { + "id": "T_CAPS_O", + "text": "O" + }, + { + "id": "T_CAPS_P", + "text": "P" + } + ] + }, + { + "id": 2, + "key": [ + { + "id": "T_CAPS_A", + "text": "A", + "pad": 70 + }, + { + "id": "T_CAPS_S", + "text": "S" + }, + { + "id": "T_CAPS_D", + "text": "D" + }, + { + "id": "T_CAPS_F", + "text": "F" + }, + { + "id": "T_CAPS_G", + "text": "G" + }, + { + "id": "T_CAPS_H", + "text": "H" + }, + { + "id": "T_CAPS_J", + "text": "J" + }, + { + "id": "T_CAPS_K", + "text": "K" + }, + { + "id": "T_CAPS_L", + "text": "L" + }, + { + "id": "T_new_54", + "text": "", + "width": "10", + "sp": "10" + } + ] + }, + { + "id": 3, + "key": [ + { + "id": "K_SHIFT", + "text": "*Caps*", + "width": "110", + "sp": "2", + "nextlayer": "default" + }, + { + "id": "T_CAPS_Z", + "text": "Z" + }, + { + "id": "T_CAPS_X", + "text": "X" + }, + { + "id": "T_CAPS_C", + "text": "C" + }, + { + "id": "T_CAPS_V", + "text": "V" + }, + { + "id": "T_CAPS_B", + "text": "B" + }, + { + "id": "T_CAPS_N", + "text": "N" + }, + { + "id": "T_CAPS_M", + "text": "M" + }, + { + "id": "K_PERIOD", + "text": ".", + "sk": [ + { + "text": ",", + "id": "K_COMMA", + "layer": "default" + }, + { + "text": "!", + "id": "K_1", + "layer": "shift" + }, + { + "text": "?", + "id": "K_SLASH", + "layer": "shift" + }, + { + "text": "'", + "id": "K_QUOTE", + "layer": "default" + }, + { + "text": "\"", + "id": "K_QUOTE", + "layer": "shift" + }, + { + "text": "\\", + "id": "K_BKSLASH", + "layer": "default" + }, + { + "text": ":", + "id": "K_COLON", + "layer": "shift" + }, + { + "text": ";", + "id": "K_COLON", + "layer": "default" + } + ] + }, + { + "id": "K_BKSP", + "text": "*BkSp*", + "width": "90", + "sp": "1" + } + ] + }, + { + "id": 4, + "key": [ + { + "id": "K_NUMLOCK", + "text": "*123*", + "width": "140", + "sp": "1", + "nextlayer": "numeric" + }, + { + "id": "K_LOPT", + "text": "*Menu*", + "width": "120", + "sp": "1" + }, + { + "id": "K_SPACE", + "text": "", + "width": "630", + "sp": "0" + }, + { + "id": "K_ENTER", + "text": "*Enter*", + "width": "140", + "sp": "1" + } + ] + } + ] + } + ] + }, + "phone": { + "font": "Tahoma", + "layer": [ + { + "id": "default", + "row": [ + { + "id": 1, + "key": [ + { + "id": "K_Q", + "text": "q" + }, + { + "id": "K_W", + "text": "w" + }, + { + "id": "K_E", + "text": "e" + }, + { + "id": "K_R", + "text": "r" + }, + { + "id": "K_T", + "text": "t" + }, + { + "id": "K_Y", + "text": "y" + }, + { + "id": "K_U", + "text": "u" + }, + { + "id": "K_I", + "text": "i" + }, + { + "id": "K_O", + "text": "o" + }, + { + "id": "K_P", + "text": "p" + } + ] + }, + { + "id": 2, + "key": [ + { + "id": "K_A", + "text": "a", + "pad": "50" + }, + { + "id": "K_S", + "text": "s" + }, + { + "id": "K_D", + "text": "d" + }, + { + "id": "K_F", + "text": "f" + }, + { + "id": "K_G", + "text": "g" + }, + { + "id": "K_H", + "text": "h" + }, + { + "id": "K_J", + "text": "j" + }, + { + "id": "K_K", + "text": "k" + }, + { + "id": "K_L", + "text": "l" + }, + { + "id": "T_new_122", + "text": "", + "width": "10", + "sp": "10" + } + ] + }, + { + "id": 3, + "key": [ + { + "id": "K_SHIFT", + "text": "*Shift*", + "sp": "1", + "nextlayer": "shift", + "sk": [ + { + "text": "*Caps*", + "id": "T_new_754", + "sp": "1", + "nextlayer": "CAPS" + } + ] + }, + { + "id": "K_Z", + "text": "z" + }, + { + "id": "K_X", + "text": "x" + }, + { + "id": "K_C", + "text": "c" + }, + { + "id": "K_V", + "text": "v" + }, + { + "id": "K_B", + "text": "b" + }, + { + "id": "K_N", + "text": "n" + }, + { + "id": "K_M", + "text": "m" + }, + { + "id": "K_PERIOD", + "text": ".", + "sk": [ + { + "text": ",", + "id": "K_COMMA" + }, + { + "text": "!", + "id": "K_1", + "layer": "shift" + }, + { + "text": "?", + "id": "K_SLASH", + "layer": "shift" + }, + { + "text": "'", + "id": "K_QUOTE" + }, + { + "text": "\"", + "id": "K_QUOTE", + "layer": "shift" + }, + { + "text": "\\", + "id": "K_BKSLASH" + }, + { + "text": ":", + "id": "K_COLON", + "layer": "shift" + }, + { + "text": ";", + "id": "K_COLON" + } + ] + }, + { + "id": "K_BKSP", + "text": "*BkSp*", + "width": "100", + "sp": "1" + } + ] + }, + { + "id": 4, + "key": [ + { + "id": "K_NUMLOCK", + "text": "*123*", + "width": "150", + "sp": "1", + "nextlayer": "numeric" + }, + { + "id": "K_LOPT", + "text": "*Menu*", + "width": "120", + "sp": "1" + }, + { + "id": "K_SPACE", + "text": "", + "width": "610", + "sp": "0" + }, + { + "id": "K_ENTER", + "text": "*Enter*", + "width": "150", + "sp": "1" + } + ] + } + ] + }, + { + "id": "shift", + "row": [ + { + "id": 1, + "key": [ + { + "id": "K_Q", + "text": "Q", + "nextlayer": "default" + }, + { + "id": "K_W", + "text": "W", + "nextlayer": "default" + }, + { + "id": "K_E", + "text": "E", + "nextlayer": "default" + }, + { + "id": "K_R", + "text": "R", + "nextlayer": "default" + }, + { + "id": "K_T", + "text": "T", + "nextlayer": "default" + }, + { + "id": "K_Y", + "text": "Y", + "nextlayer": "default" + }, + { + "id": "K_U", + "text": "U", + "nextlayer": "default" + }, + { + "id": "K_I", + "text": "I", + "nextlayer": "default" + }, + { + "id": "K_O", + "text": "O", + "nextlayer": "default" + }, + { + "id": "K_P", + "text": "P", + "nextlayer": "default" + } + ] + }, + { + "id": 2, + "key": [ + { + "id": "K_A", + "text": "A", + "pad": "50", + "nextlayer": "default" + }, + { + "id": "K_S", + "text": "S", + "nextlayer": "default" + }, + { + "id": "K_D", + "text": "D", + "nextlayer": "default" + }, + { + "id": "K_F", + "text": "F", + "nextlayer": "default" + }, + { + "id": "K_G", + "text": "G", + "nextlayer": "default" + }, + { + "id": "K_H", + "text": "H", + "nextlayer": "default" + }, + { + "id": "K_J", + "text": "J", + "nextlayer": "default" + }, + { + "id": "K_K", + "text": "K", + "nextlayer": "default" + }, + { + "id": "K_L", + "text": "L", + "nextlayer": "default" + }, + { + "id": "T_new_535", + "text": "", + "width": "10", + "sp": "10" + } + ] + }, + { + "id": 3, + "key": [ + { + "id": "K_SHIFT", + "text": "*Shift*", + "sp": "2", + "nextlayer": "default" + }, + { + "id": "K_Z", + "text": "Z", + "nextlayer": "default" + }, + { + "id": "K_X", + "text": "X", + "nextlayer": "default" + }, + { + "id": "K_C", + "text": "C", + "nextlayer": "default" + }, + { + "id": "K_V", + "text": "V", + "nextlayer": "default" + }, + { + "id": "K_B", + "text": "B", + "nextlayer": "default" + }, + { + "id": "K_N", + "text": "N", + "nextlayer": "default" + }, + { + "id": "K_M", + "text": "M", + "nextlayer": "default" + }, + { + "id": "K_PERIOD", + "text": ".", + "nextlayer": "default", + "sk": [ + { + "text": ",", + "id": "K_COMMA", + "nextlayer": "default", + "layer": "default" + }, + { + "text": "!", + "id": "K_1", + "nextlayer": "default", + "layer": "shift" + }, + { + "text": "?", + "id": "K_SLASH", + "nextlayer": "default", + "layer": "shift" + }, + { + "text": "'", + "id": "K_QUOTE", + "nextlayer": "default", + "layer": "default" + }, + { + "text": "\"", + "id": "K_QUOTE", + "nextlayer": "default", + "layer": "shift" + }, + { + "text": "\\", + "id": "K_BKSLASH", + "nextlayer": "default", + "layer": "default" + }, + { + "text": ":", + "id": "K_COLON", + "nextlayer": "default", + "layer": "shift" + }, + { + "text": ";", + "id": "K_COLON", + "nextlayer": "default", + "layer": "default" + } + ] + }, + { + "id": "K_BKSP", + "text": "*BkSp*", + "sp": "1" + } + ] + }, + { + "id": 4, + "key": [ + { + "id": "K_NUMLOCK", + "text": "*123*", + "width": "150", + "sp": "1", + "nextlayer": "numeric" + }, + { + "id": "K_LOPT", + "text": "*Menu*", + "width": "120", + "sp": "1" + }, + { + "id": "K_SPACE", + "text": "", + "width": "610", + "sp": "0" + }, + { + "id": "K_ENTER", + "text": "*Enter*", + "width": "150", + "sp": "1" + } + ] + } + ] + }, + { + "id": "numeric", + "row": [ + { + "id": 1, + "key": [ + { + "id": "K_1", + "text": "1" + }, + { + "id": "K_2", + "text": "2" + }, + { + "id": "K_3", + "text": "3" + }, + { + "id": "K_4", + "text": "4" + }, + { + "id": "K_5", + "text": "5" + }, + { + "id": "K_6", + "text": "6" + }, + { + "id": "K_7", + "text": "7" + }, + { + "id": "K_8", + "text": "8" + }, + { + "id": "K_9", + "text": "9" + }, + { + "id": "K_0", + "text": "0" + } + ] + }, + { + "id": 2, + "key": [ + { + "id": "K_4", + "layer": "shift", + "pad": "50", + "text": "$" + }, + { + "id": "K_2", + "layer": "shift", + "text": "@" + }, + { + "id": "K_3", + "layer": "shift", + "text": "#" + }, + { + "id": "K_5", + "layer": "shift", + "text": "%" + }, + { + "id": "K_6", + "layer": "shift", + "text": "&" + }, + { + "id": "K_HYPHEN", + "layer": "shift", + "text": "_" + }, + { + "id": "K_EQUAL", + "layer": "default", + "text": "=" + }, + { + "id": "K_BKSLASH", + "layer": "shift", + "text": "|" + }, + { + "id": "K_BKSLASH", + "layer": "default", + "text": "\\" + }, + { + "text": "", + "width": "10", + "sp": "10" + } + ] + }, + { + "id": 3, + "key": [ + { + "id": "K_LBRKT", + "pad": "110", + "sk": [ + { + "id": "U_00AB", + "text": "«" + }, + { + "id": "K_COMMA", + "text": "<", + "layer": "shift" + }, + { + "id": "K_LBRKT", + "text": "{", + "layer": "shift" + } + ], + "text": "[" + }, + { + "id": "K_9", + "layer": "shift", + "text": "(" + }, + { + "id": "K_0", + "layer": "shift", + "text": ")" + }, + { + "id": "K_RBRKT", + "sk": [ + { + "id": "U_00BB", + "text": "»" + }, + { + "id": "K_PERIOD", + "text": ">", + "layer": "shift" + }, + { + "id": "K_RBRKT", + "text": "}", + "layer": "shift" + } + ], + "text": "]" + }, + { + "id": "K_EQUAL", + "layer": "shift", + "text": "+" + }, + { + "id": "K_HYPHEN", + "text": "-" + }, + { + "id": "K_8", + "layer": "shift", + "text": "*" + }, + { + "id": "K_SLASH", + "text": "/" + }, + { + "id": "K_BKSP", + "text": "*BkSp*", + "width": "100", + "sp": "1" + } + ] + }, + { + "id": 4, + "key": [ + { + "id": "K_LOWER", + "text": "*abc*", + "width": "150", + "sp": "1", + "nextlayer": "default" + }, + { + "id": "K_LOPT", + "text": "*Menu*", + "width": "120", + "sp": "1" + }, + { + "id": "K_SPACE", + "width": "610", + "sp": "0", + "text": "" + }, + { + "id": "K_ENTER", + "text": "*Enter*", + "width": "150", + "sp": "1" + } + ] + } + ] + }, + { + "id": "CAPS", + "row": [ + { + "id": 1, + "key": [ + { + "id": "T_CAPS_Q", + "text": "Q", + "layer": "default" + }, + { + "id": "T_CAPS_W", + "text": "W" + }, + { + "id": "T_CAPS_E", + "text": "E" + }, + { + "id": "T_CAPS_R", + "text": "R" + }, + { + "id": "T_CAPS_T", + "text": "T" + }, + { + "id": "T_CAPS_Y", + "text": "Y" + }, + { + "id": "T_CAPS_U", + "text": "U" + }, + { + "id": "T_CAPS_I", + "text": "I" + }, + { + "id": "T_CAPS_O", + "text": "O" + }, + { + "id": "T_CAPS_P", + "text": "P" + } + ] + }, + { + "id": 2, + "key": [ + { + "id": "T_CAPS_A", + "text": "A", + "pad": "50" + }, + { + "id": "T_CAPS_U", + "text": "S" + }, + { + "id": "T_CAPS_D", + "text": "D" + }, + { + "id": "T_CAPS_F", + "text": "F" + }, + { + "id": "T_CAPS_G", + "text": "G" + }, + { + "id": "T_CAPS_H", + "text": "H" + }, + { + "id": "T_CAPS_J", + "text": "J" + }, + { + "id": "T_CAPS_K", + "text": "K" + }, + { + "id": "T_CAPS_L", + "text": "L" + }, + { + "id": "T_new_535", + "text": "", + "width": "10", + "sp": "10" + } + ] + }, + { + "id": 3, + "key": [ + { + "id": "K_SHIFT", + "text": "*Shift*", + "sp": "2", + "nextlayer": "default" + }, + { + "id": "T_CAPS_Z", + "text": "Z" + }, + { + "id": "T_CAPS_X", + "text": "X" + }, + { + "id": "T_CAPS_C", + "text": "C" + }, + { + "id": "T_CAPS_V", + "text": "V" + }, + { + "id": "T_CAPS_B", + "text": "B" + }, + { + "id": "T_CAPS_N", + "text": "N" + }, + { + "id": "T_CAPS_M", + "text": "M" + }, + { + "id": "K_PERIOD", + "text": ".", + "sk": [ + { + "text": ",", + "id": "K_COMMA", + "layer": "default" + }, + { + "text": "!", + "id": "K_1", + "layer": "shift" + }, + { + "text": "?", + "id": "K_SLASH", + "layer": "shift" + }, + { + "text": "'", + "id": "K_QUOTE", + "layer": "default" + }, + { + "text": "\"", + "id": "K_QUOTE", + "layer": "shift" + }, + { + "text": "\\", + "id": "K_BKSLASH", + "layer": "default" + }, + { + "text": ":", + "id": "K_COLON", + "layer": "shift" + }, + { + "text": ";", + "id": "K_COLON", + "layer": "default" + } + ] + }, + { + "id": "K_BKSP", + "text": "*BkSp*", + "sp": "1" + } + ] + }, + { + "id": 4, + "key": [ + { + "id": "K_NUMLOCK", + "text": "*123*", + "width": "150", + "sp": "1", + "nextlayer": "numeric" + }, + { + "id": "K_LOPT", + "text": "*Menu*", + "width": "120", + "sp": "1" + }, + { + "id": "K_SPACE", + "text": "", + "width": "610", + "sp": "0" + }, + { + "id": "K_ENTER", + "text": "*Enter*", + "width": "150", + "sp": "1" + } + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/web/samples/issue116/next_layer_tests.kmn b/web/samples/issue116/next_layer_tests.kmn new file mode 100644 index 0000000000..2d17d72a15 --- /dev/null +++ b/web/samples/issue116/next_layer_tests.kmn @@ -0,0 +1,40 @@ +store(&VERSION) '9.0' +store(&TARGETS) 'web' +store(ÐNOLOGUECODE) 'eng' +store(©RIGHT) '2017 SIL International' +store(&LAYOUTFILE) 'next_layer_tests-layout.js' +store(&NAME) 'Issue 116' +begin Unicode > use(main) + + +group(main) using keys + ++ [T_CAPS_Q] > "CAPS MODE Q" ++ [T_CAPS_W] > "CAPS MODE W" ++ [T_CAPS_E] > "CAPS MODE E" ++ [T_CAPS_R] > "CAPS MODE R" ++ [T_CAPS_T] > "CAPS MODE T" ++ [T_CAPS_Y] > "CAPS MODE Y" ++ [T_CAPS_U] > "CAPS MODE U" ++ [T_CAPS_I] > "CAPS MODE I" ++ [T_CAPS_O] > "CAPS MODE O" ++ [T_CAPS_P] > "CAPS MODE P" + ++ [T_CAPS_A] > "CAPS MODE A" ++ [T_CAPS_S] > "CAPS MODE S" ++ [T_CAPS_D] > "CAPS MODE D" ++ [T_CAPS_F] > "CAPS MODE F" ++ [T_CAPS_G] > "CAPS MODE G" ++ [T_CAPS_H] > "CAPS MODE H" ++ [T_CAPS_J] > "CAPS MODE J" ++ [T_CAPS_K] > "CAPS MODE K" ++ [T_CAPS_L] > "CAPS MODE L" + ++ [T_CAPS_Z] > "CAPS MODE Z" ++ [T_CAPS_X] > "CAPS MODE X" ++ [T_CAPS_C] > "CAPS MODE C" ++ [T_CAPS_V] > "CAPS MODE V" ++ [T_CAPS_B] > "CAPS MODE B" ++ [T_CAPS_N] > "CAPS MODE N" ++ [T_CAPS_M] > "CAPS MODE M" + diff --git a/web/source/keymanweb.js b/web/source/keymanweb.js index fbab6edd83..9392c150ce 100644 --- a/web/source/keymanweb.js +++ b/web/source/keymanweb.js @@ -140,26 +140,26 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { keymanweb._ActiveElement=null; keymanweb.hideCaret(); osk.hideNow(); - } - + }; + /** * Handle losing focus from simulated input field - * + * * @param {Event} e event - */ + */ keymanweb.setBlur = function(e) - { + { // This works OK for iOS, but may need something else for other platforms if(('relatedTarget' in e) && e.relatedTarget) - { + { if(e.relatedTarget.nodeName != 'DIV' || e.relatedTarget.className.indexOf('keymanweb-input') == -1) - { - keymanweb.cancelInput(); return; + { + keymanweb.cancelInput(); return; } - } - //Hide the OSK - if(!keymanweb.focusing) keymanweb.cancelInput(); - } + } + //Hide the OSK + if(!keymanweb.focusing) keymanweb.cancelInput(); + } /** * Handle receiving focus by simulated input field @@ -170,7 +170,6 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { { //e.stopPropagation(); // not doing anything useful, no child elements //e.preventDefault(); // prevents user selection or scrolling, may be better if they are allowed? - keymanweb.focusing=true; keymanweb.focusTimer=window.setTimeout(function(){keymanweb.focusing=false;},1000); @@ -207,10 +206,10 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { // Move the caret and refocus if necessary if(keymanweb._ActiveElement != target) - { + { // Hide the KMW caret keymanweb.hideCaret(); - keymanweb._ActiveElement=target; + keymanweb._ActiveElement=target; // The issue here is that touching a DIV does not actually set the focus for iOS, even when enabled to accept focus (by setting tabIndex=0) // We must explicitly set the focus in order to remove focus from any non-KMW input target.focus(); //Android native browsers may not like this, but it is needed for Chrome, Safari @@ -1318,7 +1317,23 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { keymanweb.updateInput=function(x){}; // End of I3363 (Build 301) additions - + + /** + * Function resetContext + * Scope Public + * Description Revert OSK to default layer and clear any deadkeys and modifiers + */ + keymanweb['resetContext'] = keymanweb.resetContext = function() { + osk.layerId = 'default'; + + keymanweb._DeadkeyResetMatched(); + keymanweb._DeadkeyDeleteMatched(); + keymanweb.cachedContext.reset(); + keymanweb._ResetVKShift(); + + osk._Show(); + }; + /** * Exposed function to load keyboards by name. One or more arguments may be used * @@ -2358,8 +2373,8 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { * @return {boolean} always true (?) */ keymanweb._ControlFocus = function(e) - { - var Ltarg, Ln; + { + var Ltarg, Ln; if(!keymanweb._Enabled) return true; e = keymanweb._GetEventObject(e); // I2404 - Manage IE events in IFRAMEs if(!e) return true; @@ -2644,7 +2659,7 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { } /** - * Execute external (UI) code needed on laoding keyboard + * Execute external (UI) code needed on loading keyboard * * @param {string} _internalName * @return {boolean} diff --git a/web/source/kmwembedded.js b/web/source/kmwembedded.js index 7205958071..ee0a960950 100644 --- a/web/source/kmwembedded.js +++ b/web/source/kmwembedded.js @@ -32,39 +32,39 @@ if(device.app.indexOf('Mobile') >= 0) device.formFactor='phone'; if(device.app.indexOf('Tablet') >= 0) device.formFactor='tablet'; device.browser='native'; - } + }; // Get default style sheet path keymanweb.getStyleSheetPath = function(ssName) { return keymanweb.rootPath+ssName; - } + }; // Get KMEI, KMEA keyboard path (overrides default function) keymanweb.getKeyboardPath = function(Lfilename) { return keymanweb.rootPath+'languages/' + Lfilename + "?v=" + (new Date()).getTime(); /*cache buster*/ - } + }; /** * Force reload of resource * * @param {string} s unmodified URL - * @return modified URL + * @return modified URL */ util.unCached = function(s) { var t=(new Date().getTime()); s = s + '?v=' + t; return s; - } + }; util.wait = function() { // Empty stub - this function should not be implemented or used within embedded code routes. console.warn("util.wait() call attempted in embedded mode!"); // Sends log message to embedding app. - } + }; util.alert = function() { // Empty stub - this function should not be implemented or used within embedded code routes. console.warn("util.alert() call attempted in embedded mode!"); // Sends log message to embedding app. - } + }; // TODO: This needs to be discussed with Serkan - can possibly get context without any reference to Pelem?? @@ -83,8 +83,8 @@ */ keymanweb.KC_ = function(n, ln, Pelem) { - var Ldv; - if(Pelem.body) var Ldoc=Pelem; else var Ldoc=Pelem.ownerDocument; // I1481 - use Ldoc to get the ownerDocument when no selection is found + var Ldv, Ldoc; + if(Pelem.body) Ldoc=Pelem; else Ldoc=Pelem.ownerDocument; // I1481 - use Ldoc to get the ownerDocument when no selection is found if(keymanweb.legacy) { @@ -92,7 +92,7 @@ } else if(Ldoc && (Ldv=Ldoc.defaultView) && Ldv.getSelection && (Ldoc.designMode.toLowerCase() == 'on' || Pelem.contentEditable == 'true' || Pelem.contentEditable == 'plaintext-only' || Pelem.contentEditable === '')) // && Pelem.tagName == 'HTML') && Pelem.tagName == 'HTML') - // I2457 - support contentEditable elements in mozilla, webkit + // I2457 - support contentEditable elements in mozilla, webkit { /* Mozilla midas html editor and editable elements */ var Lsel = Ldv.getSelection(); @@ -150,7 +150,7 @@ } return ""; - } + }; /** * Refresh element content after change of text (if required) @@ -160,27 +160,27 @@ keymanweb.refreshElementContent = function(Pelem) { if('ontextchange' in keymanweb) keymanweb['ontextchange'](Pelem); - } + }; /** * Set target element text direction (LTR or RTL): not functional for KMEI, KMEA * * @param {Object} Ptarg Target element */ - keymanweb._SetTargDir = function(Ptarg){} + keymanweb._SetTargDir = function(Ptarg){}; -/** + /** * Align input fields (should not be needed with KMEI, KMEA) * * @param {boolean} align align and make visible, else hide * **/ - keymanweb.alignInputs = function(align) {} + keymanweb.alignInputs = function(align) {}; /** * Use rotation events to adjust OSK element positions and scaling if necessary */ - keymanweb.handleRotationEvents = function() {} + keymanweb.handleRotationEvents = function() {}; /** * Adjust the absolute height of each keyboard element after a rotation - modified for KMEI, 13/11/14 @@ -239,7 +239,7 @@ } return true; - } + }; /** * Caret position always determined from the active (but hidden) element @@ -249,17 +249,17 @@ keymanweb.isPositionSynthesized = function() { return false; - } + }; /** * correctOSKTextSize handles rotation event -- currently rebuilds keyboard and adjusts font sizes - */ + */ keymanweb['correctOSKTextSize']=function() { if(osk.adjustHeights()) { osk._Load(); } - } - + }; + // Send the subkey array to iOS, with centre,top of base key position /** * Create a popup key array natively @@ -269,15 +269,15 @@ osk.touchHold = function(key) { if(key.subKeys && (typeof(window['oskCreatePopup']) == 'function')) - { + { var xBase=util._GetAbsoluteX(key)-util._GetAbsoluteX(osk._DivVKbd)+key.offsetWidth/2, yBase=util._GetAbsoluteY(key)-util._GetAbsoluteY(osk._DivVKbd); if(device.formFactor == 'phone') osk.prependBaseKey(key); osk.popupBaseKey = key; osk.popupPending=true; window['oskCreatePopup'](key.subKeys,xBase,yBase,key.offsetWidth,key.offsetHeight); - } - } + } + }; /** * Function called by iOS when a device-implemented keyboard popup is displayed or hidden @@ -288,16 +288,16 @@ keymanweb['popupVisible'] = function(isVisible) { osk.popupVisible = isVisible; - } + }; // Popup key highlighting (managed by device, dummy call) - osk.highlightSubKeys = function(k,x,y){} + osk.highlightSubKeys = function(k,x,y){}; // Create a keytip (dummy call - actual keytip handled by native code) osk.createKeyTip = function() { if(device.formFactor == 'phone') osk.keytip = {key:null,state:false}; - } + }; // Send the key details to KMEI or KMEA for showing or hiding the native-code keytip osk.showKeyTip = function(key,on) @@ -328,7 +328,7 @@ if(osk.touchCount == 0 || key == null) clearPreview(); } tip.key = key; tip.state = on; - } + }; osk.menuEvent = null; osk.optionKey = function(e,keyName,keyDown) @@ -355,17 +355,18 @@ if('hideKeyboard' in keymanweb) keymanweb['hideKeyboard'](); } } - } + }; // For KMEI and KMEA will always assume fonts are already installed - osk.waitForFonts = function(kfd,ofd){return true;} + osk.waitForFonts = function(kfd,ofd){return true;}; + /** * Return position of language menu key to KeymanTouch * * @return {string} comma-separated x,y,w,h of language menu key * - **/ + **/ keymanweb['touchMenuPos'] = function() { if(osk.lgKey == null) return ''; @@ -381,7 +382,7 @@ x=util._GetAbsoluteX(key) - util._GetAbsoluteX(osk._DivVKbd) + w/2, y=util._GetAbsoluteY(key) - util._GetAbsoluteY(osk._DivVKbd); return x+','+y+','+w+','+h; - } + }; /** * Accept an external key ID (from KeymanTouch) and pass to the keyboard mapping @@ -445,10 +446,10 @@ // Use default mapping only if necessary (last resort) var ch = osk.defaultKeyOutput(keyName,Lkc.Lcode,keyShiftState); - if(ch) keymanweb.KO(0, Lelem, ch); - - return true; - } + if(ch) keymanweb.KO(0, Lelem, ch); + + return true; + }; /** * API endpoint for hardware keystroke events from Android external keyboards diff --git a/web/source/kmwosk.js b/web/source/kmwosk.js index 047c09c4fc..e809e3c392 100644 --- a/web/source/kmwosk.js +++ b/web/source/kmwosk.js @@ -851,11 +851,12 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { // Turn off key highlighting (or preview) osk.highlightKey(e,false); - // Process modifier key action - // TODO: Fixup the duplication of nextlayer processing here and below. - // This breaks the `nextlayer` property on keys with output - // even though it does the right thing for modifier keys. - if(osk.selectLayer(keyName,nextLayer)) return true; + // The default OSK layout for desktop devices does not include nextlayer info, relying on modifier detection here. + if(device.formFactor == 'desktop') { + if(osk.selectLayer(keyName, nextLayer)){ + return true; + } + } // Prevent any output from 'ghost' (unmapped) keys if(keyName != 'K_SPACE') diff --git a/windows/src/Defines.mak b/windows/src/Defines.mak index f1c4a5040a..818754d649 100644 --- a/windows/src/Defines.mak +++ b/windows/src/Defines.mak @@ -25,6 +25,10 @@ BUILD=$(ROOT)\build DEBUGPATH=$(ROOT)\debug OUTLIB=$(ROOT)\lib +INSTALLPATH_KEYMANDESKTOP="%ProgramFiles(X86)%\Keyman\Keyman Desktop 10.0" +INSTALLPATH_KEYMANDEVELOPER="%ProgramFiles(X86)%\Keyman\Keyman Developer 10.0" +INSTALLPATH_KEYMANENGINE="%CommonProgramFiles(X86)%\Keyman\Keyman Engine 10.0" + !IFDEF DEBUG MAKEFLAG_DEBUG=-DDEBUG !ENDIF @@ -198,8 +202,7 @@ SC_PFX_SHA256="$(ROOT)\src\buildtools\certificates\keymantest-sha256.pfx" SC_PFX_SHA1="$(ROOT)\src\buildtools\certificates\keymantest-sha1.pfx" SC_PWD="" -SIGNTOOL="C:\Program Files (x86)\Windows Kits\8.0\bin\x86\signtool.exe" -SIGNCODE=@$(ROOT)\src\buildtools\signtime.bat $(SIGNTOOL) $(SC_PFX_SHA1) $(SC_PFX_SHA256) $(SC_URL) $(SC_PWD) +SIGNCODE=@$(ROOT)\src\buildtools\signtime.bat signtool.exe $(SC_PFX_SHA1) $(SC_PFX_SHA256) $(SC_URL) $(SC_PWD) !ifdef USERDEFINES !include $(ROOT)\src\UserDefines.mak diff --git a/windows/src/Makefile b/windows/src/Makefile index f57f61a19b..4c10fefa26 100644 --- a/windows/src/Makefile +++ b/windows/src/Makefile @@ -12,6 +12,7 @@ default: @echo     release               make a release @echo     web-install           sends the documentation to the website @echo     test-certificate      create a test certificate + CA (and install test CA) + @echo     signcode              sign the build artifacts @echo. @echo     Build flags: @echo           DEBUG           Include debug information diff --git a/windows/src/README.md b/windows/src/README.md index e2cb212180..46abdd4728 100644 --- a/windows/src/README.md +++ b/windows/src/README.md @@ -1,31 +1,28 @@ -Keyman Desktop 10.0 and Keyman Developer 10.0 -============================================= +# Keyman Desktop 10.0 and Keyman Developer 10.0 -Build Prerequisites -=================== +## Build Prerequisites -1. Install VS2017 Community Edition (see below for details) -2. Install Delphi 10.2 Berlin (if Starter Edition, see below for additional notes) -3. Install git https://git-scm.com/download/win -4. Add Keyman root folder to antivirus exclusions (performance and file lock reasons) [optional - but highly recommended] -5. Start Delphi 10.2 Berlin IDE once after installation to create default environment files and ensure registration is complete. -6. Set environment variables per notes below: `KEYMAN_ROOT`, `DELPHI_STARTER`, `USERDEFINES`, (`USE_PLUSMEMO`, `KEYMAN_ENCUMBERED_ROOT`) -7. Add the `windows/lib` folder in the Keyman repository to your `PATH` environment variable (required for packages in Delphi) +1. Install [VS2017 Community Edition](#visual-studio-2017-community-edition-setup-requirements). +2. Install [Delphi 10.2](#delphi-setup-requirements). +3. Install [git](https://git-scm.com/download/win). +4. Add the Keyman root folder to antivirus exclusions for performance and file lock reasons (optional - but highly recommended). +5. Start Delphi 10.2 IDE once after installation to create default environment files and ensure registration is complete. +6. Set environment variables per [notes below](#environment-variables): `KEYMAN_ROOT`, `DELPHI_STARTER`, `USERDEFINES`, (`USE_PLUSMEMO`, `KEYMAN_ENCUMBERED_ROOT`). +7. Add the **windows/lib** folder in the Keyman repository to your `PATH` environment variable (required for packages in Delphi) -Release build prerequisites ---------------------------- +### Release build prerequisites -1. Install 7-Zip 64-bit (or 32-bit on x86 Windows) http://www.7-zip.org/ (used for archiving build files -- may be eliminated in future) -2. Install HTML Help Workshop from https://www.microsoft.com/en-us/download/details.aspx?id=21138 -3. Run `make test-certificate` to create a test certificate, or else create `UserDefines.mak` to locate code signing certificates -4. Install WiX to c:\program files (x86)\windows installer xml v3.5 https://wix.codeplex.com/releases/view/60102 +For local development you do not need to perform a release build so these are optional. -Building Keyman Desktop and Keyman Developer -============================================ +1. Install [7-Zip](http://www.7-zip.org/) 64-bit (or 32-bit on x86 Windows). 7-Zip is used for archiving build files -- may be eliminated in future. +2. Install [HTML Help Workshop](https://www.microsoft.com/en-us/download/details.aspx?id=21138) +4. Install [WiX](https://wix.codeplex.com/releases/view/60102) to **C:\Program Files (x86)\Windows Installer XML v3.5** -1. Start a `VS2017 x64_x86 Cross Tools` command prompt. -2. Run `make build` from the folder `windows/src` folder. -3. Artifacts from a successful build will be placed in `windows/bin` folder. +## Building Keyman Desktop and Keyman Developer + +1. Start 'x64_x86 Cross Tools Command Prompt for VS 2017'. +2. Run `make build` from the **windows/src** folder. +3. Artifacts from a successful build will be placed in **windows/bin** folder. Type `make` to see build targets. Common build targets are: @@ -33,53 +30,80 @@ Type `make` to see build targets. Common build targets are: `make clean` <-- remove temporary files and build artifacts `make release` <-- makes a release of all Keyman Windows projects -Release build -------------- +By default the version generated will be 10.0.700.0. -1. Start a `VS2017 x64_x86 Cross Tools` command prompt. -2. Run `make release` from the folder `windows/src` folder. -3. Artifacts from a successful build will be placed in `windows/release` folder. +### Release build + +To perform a release build, you will need to obtain valid certificates. A release build is +unnecessary for local development. + +1. Start 'x64_x86 Cross Tools Command Prompt for VS 2017'. +2. Run `make release` from the **windows/src** folder. +3. Artifacts from a successful build will be placed in **windows/release** folder. Note: by default, the version generated will be 10.0.700.0. You will not be able to install it over a later version of Keyman, and will need to uninstall and reinstall. -Detailed Preqrequisite Notes -============================ +## Installing Keyman -Visual Studio 2017 Community Edition setup requirements -------------------------------------------------------- +These steps are only required the first time you install Keyman: +1. Install release versions of Keyman 10.0 and Keyman Developer 10.0. + * Download the [latest official builds (alpha)](https://keyman.com/beta/) and run the installers. +2. Install the Keyman test certificates. Do the following for each KeymanTestCA cert in +**windows/src/buildtools/certificates**: + 1. Open the certificate and click 'Install certificate...' to open the Certificate Import Wizard. + 2. Select either 'Current User' or 'Local Machine' for 'Store Location' depending on your preference and + click Next. + 3. Select 'Place all certificates in the following store' and 'Browse...' to select + 'Trusted Root Certification Authorities'. Click Next and finish the wizard. + +To deploy a development build of Keyman and Keyman Developer, +1. Start 'x64_x86 Cross Tools Command Prompt for VS 2017'. +2. Run `make signcode` from the **windows/src** folder. +3. Ensure that Keyman and Keyman Developer are not running. +4. Run `make install` in **windows/src/engine**, **windows/src/desktop** and **windows/src/developer**. + +## Detailed Prerequisite Notes + +### Visual Studio 2017 Community Edition setup requirements In Visual Studio 2017, you need to have the following components installed: * Universal Windows Platform development + * Desktop development with C++ > Windows 8.1 SDK and UCRT SDK * Desktop development with C++ -* 'Windows 8.1 SDK and UCRT SDK' -* Universal CRT SDK -* Two-space tab stops (spaces, not tabs). Some existing code has tabs rather than spaces +Configure Visual Studio to use two-space tab stops: +1. Open the options dialog: Tools > Options +2. Navigate to Text Editor > All Languages > Tabs +3. Change 'Tab size' to 2 and 'Indent size' to 2 +4. Select 'Insert spaces' -Delphi setup requirements -------------------------- +### Delphi setup requirements -* Windows 32 and 64 bit (note, only 32 bit on Delphi Starter) +Delphi Starter Edition is free and can be downloaded [here](https://www.embarcadero.com/products/delphi/starter). +Some features of Keyman are [limited](#delphi_starter---building-with-delphi-starter-edition) when built using +the Starter Edition. You must set the `DELPHI_STARTER` environment variable when using the Starter Edition. + +Install Delphi using the following options: +* Windows 32 and 64 bit (note: Delphi Starter only has 32 bit) * No 3rd party components required * No Interbase components required -Environment Variables -===================== +## Environment Variables -KEYMAN_ROOT - Locating the source ---------------------------------- +To check whether these variables are set, run `SET NAME_OF_VAR` in command prompt. + +### KEYMAN_ROOT - Locating the source If you pull the entire `keyman.git` repo to `c:\keyman`, then the paths by default will work without changes. Otherwise, you will need to set an environment variable `KEYMAN_ROOT` to the root path of the Keyman repo. For example: -```` +``` SET KEYMAN_ROOT=c:\projects\keyman -```` +``` -DELPHI_STARTER - Building with Delphi Starter Edition ------------------------------------------------------ +### DELPHI_STARTER - Building with Delphi Starter Edition Keyman can be built from the command line with Delphi Starter Edition. You will need to set the environment variable `DELPHI_STARTER` to enable a command line build with @@ -100,8 +124,7 @@ However, there are three limitations: 3. The encumbered components cannot be built with Delphi Starter Edition. -USERDEFINES - User Defines --------------------------- +### USERDEFINES - User Defines You can specify defines that will not be added to the git repository and will be used in the build in the UserDefines.mak file in the root folder. This is used mostly for @@ -111,8 +134,7 @@ executables when you build a release. To include UserDefines.mak in the build, use the command line parameter `-DUSERDEFINES`. You can also set an environment variable `USERDEFINES=1` to get the same result. -KEYMAN_ENCUMBERED_ROOT and USE_PLUSMEMO - Encumbered components ---------------------------------------------------------------- +### KEYMAN_ENCUMBERED_ROOT and USE_PLUSMEMO - Encumbered components Keyman Developer can be built with an encumbered text editor, `TPlusMemo`. The standard open source release uses a very basic `TMemo` without syntax highlighting and lots of diff --git a/windows/src/desktop/insthelp/Makefile b/windows/src/desktop/insthelp/Makefile index b022d2c0e5..afd7fb10aa 100644 --- a/windows/src/desktop/insthelp/Makefile +++ b/windows/src/desktop/insthelp/Makefile @@ -23,4 +23,7 @@ signcode: backup: $(WZZIP) $(BUILD)\desktop\insthelp.zip $(BACKUPDEFAULTS) insthelp.exe +install: + rem + !include ..\..\Target.mak diff --git a/windows/src/desktop/kmshell/Makefile b/windows/src/desktop/kmshell/Makefile index 95da53320e..6b4b9b62f1 100644 --- a/windows/src/desktop/kmshell/Makefile +++ b/windows/src/desktop/kmshell/Makefile @@ -36,7 +36,7 @@ test-manifest: $(MT) -nologo -inputresource:$(PROGRAM)\desktop\kmshell.exe -validate_manifest install: - copy $(ROOT)\bin\desktop\kmshell.exe "c:\program files (x86)\tavultesoft\keyman desktop 9.0" - copy $(ROOT)\bin\desktop\xml\* "c:\program files (x86)\tavultesoft\keyman desktop 9.0\xml\" + copy $(ROOT)\bin\desktop\kmshell.exe $(INSTALLPATH_KEYMANDESKTOP) + copy $(ROOT)\bin\desktop\xml\* $(INSTALLPATH_KEYMANDESKTOP)\xml\ !include ..\..\Target.mak diff --git a/windows/src/desktop/setup/Makefile b/windows/src/desktop/setup/Makefile index c9dd2c79b2..c5b2853e49 100644 --- a/windows/src/desktop/setup/Makefile +++ b/windows/src/desktop/setup/Makefile @@ -29,4 +29,7 @@ test-manifest: # test that (a) linked manifest exists and correct, and (b) has uiAccess=true $(MT) -nologo -inputresource:$(PROGRAM)\desktop\setup.exe -validate_manifest +install: + rem + !include ..\..\Target.mak diff --git a/windows/src/developer/TIKE/Makefile b/windows/src/developer/TIKE/Makefile index 3b4fc905a6..8f28225bb3 100644 --- a/windows/src/developer/TIKE/Makefile +++ b/windows/src/developer/TIKE/Makefile @@ -40,7 +40,7 @@ backup: install: - $(COPY) $(PROGRAM)\developer\tike.exe "c:\Program Files (x86)\Tavultesoft\Keyman Developer 9.0\tike.exe" + $(COPY) $(PROGRAM)\developer\tike.exe $(INSTALLPATH_KEYMANDEVELOPER)\tike.exe test-manifest: # test that linked manifest exists and correct diff --git a/windows/src/developer/TIKE/main/UframeOnScreenKeyboardEditor.pas b/windows/src/developer/TIKE/main/UframeOnScreenKeyboardEditor.pas index a78161891d..81fedb2ae5 100644 --- a/windows/src/developer/TIKE/main/UframeOnScreenKeyboardEditor.pas +++ b/windows/src/developer/TIKE/main/UframeOnScreenKeyboardEditor.pas @@ -298,16 +298,9 @@ begin end; procedure TframeOnScreenKeyboardEditor.tmrUpdateCharacterMapTimer(Sender: TObject); -var - token: WideString; - x, token_start: Integer; - //prev_token: WideString; begin tmrUpdateCharacterMap.Enabled := False; - x := editVKKeyText.SelStart+editVKKeyText.SelLength; - token_start := 0; - token := FormatUnicode(Copy(editVKKeyText.Text, x, 1)); - UpdateCharacterMap(False, token, x, token_start, False); // I4807 + UpdateCharacterMap(False, editVKKeyText.Text, 0, 0, True); end; { ---------------------------------------------------------------------------- } diff --git a/windows/src/developer/kmcmpdll/Makefile b/windows/src/developer/kmcmpdll/Makefile index b1cd9211d1..f2bff68dc8 100644 --- a/windows/src/developer/kmcmpdll/Makefile +++ b/windows/src/developer/kmcmpdll/Makefile @@ -24,6 +24,6 @@ test-manifest: @rem This target needed as dependency for TIKE and KMCMPDLL install: - $(COPY) $(PROGRAM)\developer\kmcmpdll.dll "c:\program files (x86)\tavultesoft\keyman developer 9.0\kmcmpdll.dll" + $(COPY) $(PROGRAM)\developer\kmcmpdll.dll $(INSTALLPATH_KEYMANDEVELOPER)\kmcmpdll.dll !include ..\..\Target.mak diff --git a/windows/src/developer/kmcomp/Makefile b/windows/src/developer/kmcomp/Makefile index 93c36a9f84..4b5b1c2897 100644 --- a/windows/src/developer/kmcomp/Makefile +++ b/windows/src/developer/kmcomp/Makefile @@ -28,6 +28,6 @@ test-manifest: $(MT) -nologo -inputresource:$(PROGRAM)\developer\kmcomp.exe -validate_manifest install: - $(COPY) $(PROGRAM)\developer\kmcomp.exe "c:\program files (x86)\tavultesoft\keyman developer 9.0\kmcomp.exe" + $(COPY) $(PROGRAM)\developer\kmcomp.exe $(INSTALLPATH_KEYMANDEVELOPER)\kmcomp.exe !include ..\..\Target.mak diff --git a/windows/src/engine/keyman/Makefile b/windows/src/engine/keyman/Makefile index d1b006b14d..4e291b73b1 100644 --- a/windows/src/engine/keyman/Makefile +++ b/windows/src/engine/keyman/Makefile @@ -37,8 +37,8 @@ debug-manifest: copy std-manifest.xml manifest.xml install: - $(COPY) $(PROGRAM)\engine\keyman.exe "C:\Program Files (x86)\Common Files\Keyman\Keyman Engine 10.0" - $(COPY) keyman.rsm "C:\Program Files (x86)\Common Files\Keyman\Keyman Engine 10.0" + $(COPY) $(PROGRAM)\engine\keyman.exe $(INSTALLPATH_KEYMANENGINE) + $(COPY) keyman.rsm $(INSTALLPATH_KEYMANENGINE) test-uiaccess: grep -c "uiAccess=\"true\"" manifest.xml diff --git a/windows/src/engine/keyman32/Makefile b/windows/src/engine/keyman32/Makefile index 2483916fb4..0be75a5ce4 100644 --- a/windows/src/engine/keyman32/Makefile +++ b/windows/src/engine/keyman32/Makefile @@ -25,6 +25,6 @@ backup: $(WZZIP) $(BUILD)\engine\keyman32.zip $(BACKUPDEFAULTS) keyman32.dll install: - $(COPY) $(PROGRAM)\engine\keyman32.dll "C:\Program Files (x86)\Common Files\Keyman\Keyman Engine 10.0\keyman32.dll" + $(COPY) $(PROGRAM)\engine\keyman32.dll $(INSTALLPATH_KEYMANENGINE)\keyman32.dll !include ..\..\Target.mak diff --git a/windows/src/engine/keyman32/kmhook_keyboard.cpp b/windows/src/engine/keyman32/kmhook_keyboard.cpp index 93e440455c..db55ab5b09 100644 --- a/windows/src/engine/keyman32/kmhook_keyboard.cpp +++ b/windows/src/engine/keyman32/kmhook_keyboard.cpp @@ -173,6 +173,8 @@ int ProcessLanguageSwitchShiftKey(WPARAM wParam, BOOL isUp) switch(wParam) // I4548 { + case VK_LSHIFT: + case VK_RSHIFT: case VK_SHIFT: case VK_CONTROL: case VK_LCONTROL: // I4124 diff --git a/windows/src/engine/keyman64/Makefile b/windows/src/engine/keyman64/Makefile index 09c9e11172..4af3309f7d 100644 --- a/windows/src/engine/keyman64/Makefile +++ b/windows/src/engine/keyman64/Makefile @@ -25,6 +25,6 @@ backup: $(WZZIP) $(BUILD)\engine\keyman64.zip $(BACKUPDEFAULTS) keyman64.dll install: - $(COPY) $(PROGRAM)\engine\keyman64.dll "C:\Program Files (x86)\Common Files\Keyman\Keyman Engine 10.0\keyman64.dll" + $(COPY) $(PROGRAM)\engine\keyman64.dll $(INSTALLPATH_KEYMANENGINE)\keyman64.dll !include ..\..\Target.mak diff --git a/windows/src/engine/keymanx64/Makefile b/windows/src/engine/keymanx64/Makefile index 7393a3e205..ee5e2ec000 100644 --- a/windows/src/engine/keymanx64/Makefile +++ b/windows/src/engine/keymanx64/Makefile @@ -26,7 +26,7 @@ backup: $(WZZIP) $(BUILD)\engine\keymanx64.exe $(BACKUPDEFAULTS) keymanx64.exe install: - $(COPY) $(PROGRAM)\engine\keymanx64.exe "C:\Program Files (x86)\Common Files\Tavultesoft\Keyman Engine 9.0" + $(COPY) $(PROGRAM)\engine\keymanx64.exe $(INSTALLPATH_KEYMANENGINE) test-uiaccess: grep -c "uiAccess=\"true\"" manifest.xml diff --git a/windows/src/engine/kmcomapi/Makefile b/windows/src/engine/kmcomapi/Makefile index d7435ae25b..7988940f2b 100644 --- a/windows/src/engine/kmcomapi/Makefile +++ b/windows/src/engine/kmcomapi/Makefile @@ -23,9 +23,9 @@ signcode: $(SIGNCODE) /d "Keyman Engine" $(PROGRAM)\engine\kmcomapi.dll install: - regsvr32 /s /u "C:\Program Files (x86)\Common Files\Keyman\Keyman Engine 10.0\kmcomapi.dll" - $(COPY) $(PROGRAM)\engine\kmcomapi.dll "C:\Program Files (x86)\Common Files\Keyman\Keyman Engine 10.0" - regsvr32 "C:\Program Files (x86)\Common Files\Keyman\Keyman Engine 10.0\kmcomapi.dll" + regsvr32 /s /u $(INSTALLPATH_KEYMANENGINE)\kmcomapi.dll + $(COPY) $(PROGRAM)\engine\kmcomapi.dll $(INSTALLPATH_KEYMANENGINE) + regsvr32 $(INSTALLPATH_KEYMANENGINE)\kmcomapi.dll backup: $(WZZIP) $(BUILD)\engine\kmcomapi.zip $(BACKUPDEFAULTS) kmcomapi.dll diff --git a/windows/src/engine/kmtip/Makefile b/windows/src/engine/kmtip/Makefile index 3610ee3917..a797e67d1c 100644 --- a/windows/src/engine/kmtip/Makefile +++ b/windows/src/engine/kmtip/Makefile @@ -25,11 +25,11 @@ backup: $(WZZIP) $(BUILD)\engine\kmtip64.zip x64\kmtip64.pdb $(BACKUPDEFAULTS) x64\kmtip64.dll install: - cd c:\program files (x86)\common files\keyman\keyman engine 10.0 + cd $(INSTALLPATH_KEYMANENGINE) #regsvr32 /s /u kmtip.dll #regsvr32 /s /u kmtip64.dll - $(COPY) $(PROGRAM)\engine\kmtip.dll "c:\program files (x86)\common files\keyman\keyman engine 10.0\kmtip.dll" - $(COPY) $(PROGRAM)\engine\kmtip64.dll "c:\program files (x86)\common files\keyman\keyman engine 10.0\kmtip64.dll" + $(COPY) $(PROGRAM)\engine\kmtip.dll $(INSTALLPATH_KEYMANENGINE)\kmtip.dll + $(COPY) $(PROGRAM)\engine\kmtip64.dll $(INSTALLPATH_KEYMANENGINE)\kmtip64.dll #regsvr32 /s kmtip.dll #regsvr32 /s kmtip64.dll cd $(ROOT)\src\engine\kmtip diff --git a/windows/src/engine/mcompile/Makefile b/windows/src/engine/mcompile/Makefile index 6ab84abf5e..84260e52bb 100644 --- a/windows/src/engine/mcompile/Makefile +++ b/windows/src/engine/mcompile/Makefile @@ -22,7 +22,7 @@ backup: $(WZZIP) $(BUILD)\engine\mcompile.exe $(BACKUPDEFAULTS) mcompile.exe install: - $(COPY) $(PROGRAM)\engine\mcompile.exe "C:\Program Files (x86)\Common Files\Tavultesoft\Keyman Engine 9.0" + $(COPY) $(PROGRAM)\engine\mcompile.exe $(INSTALLPATH_KEYMANENGINE) test-manifest: # test that linked manifest exists and correct diff --git a/windows/src/engine/tsysinfo/Makefile b/windows/src/engine/tsysinfo/Makefile index 834705ee3d..d698c9becf 100644 --- a/windows/src/engine/tsysinfo/Makefile +++ b/windows/src/engine/tsysinfo/Makefile @@ -32,8 +32,8 @@ backup: $(WZZIP) $(BUILD)\engine\tsysinfo.zip $(BACKUPDEFAULTS) tsysinfo.exe install: - $(COPY) $(PROGRAM)\engine\tsysinfo.exe "C:\Program Files (x86)\Common Files\Tavultesoft\Keyman Engine 9.0" - $(COPY) $(ROOT)\src\engine\tsysinfo\*.xsl "C:\Program Files (x86)\Common Files\Tavultesoft\Keyman Engine 9.0" + $(COPY) $(PROGRAM)\engine\tsysinfo.exe $(INSTALLPATH_KEYMANENGINE) + $(COPY) $(ROOT)\src\engine\tsysinfo\*.xsl $(INSTALLPATH_KEYMANENGINE) test-manifest: # test that linked manifest exists and correct diff --git a/windows/src/global/delphi/visualkeyboard/VisualKeyboardLoaderBinary.pas b/windows/src/global/delphi/visualkeyboard/VisualKeyboardLoaderBinary.pas index 77465d294c..9b1e61770e 100644 --- a/windows/src/global/delphi/visualkeyboard/VisualKeyboardLoaderBinary.pas +++ b/windows/src/global/delphi/visualkeyboard/VisualKeyboardLoaderBinary.pas @@ -1,130 +1,135 @@ -unit VisualKeyboardLoaderBinary; - -interface - -uses - System.SysUtils, - System.Classes, - Vcl.Graphics, - Winapi.Windows, - - VisualKeyboard; - -// -// This originated with the TVisualKeyboardImportXML code but has now diverged for -// UI-clean single-file round-trippable consistency. -// - -type - EVisualKeyboardLoaderBinary = class(EVisualKeyboardLoader); - - TVisualKeyboardLoaderBinary = class(TVisualKeyboardLoader) - private - procedure LoadKey(Key: TVisualKeyboardKey; Stream: TStream); - procedure LoadHeader(Header: TVisualKeyboardHeader; Stream: TStream); - function ReadBitmap(Stream: TStream): Vcl.Graphics.TBitmap; - procedure ReadFont(Stream: TStream; FFont: TFont); - procedure LoadKeys(Keys: TVisualKeyboardKeyList; Stream: TStream); - function ReadString(Stream: TStream): string; - public - procedure LoadFromStream(Stream: TStream); override; - end; - -implementation - -{ TVisualKeyboardLoaderBinary } - -function TVisualKeyboardLoaderBinary.ReadString(Stream: TStream): string; -var - w: Word; - str: string; -begin - Stream.Read(w, sizeof(w)); - SetLength(str, w); - Stream.Read(PChar(str)^, w*2); - Result := str; -end; - -procedure TVisualKeyboardLoaderBinary.ReadFont(Stream: TStream; FFont: TFont); -var - n: Integer; -begin - FFont.Name := ReadString(Stream); - Stream.Read(n, sizeof(n)); - FFont.Size := n; - Stream.Read(n, sizeof(n)); - FFont.Color := TColor(n); -end; - -function TVisualKeyboardLoaderBinary.ReadBitmap(Stream: TStream): Vcl.Graphics.TBitmap; -var - n: DWord; - mem: TMemoryStream; -begin - Stream.Read(n, Sizeof(n)); - if n > 0 then - begin - mem := TMemoryStream.Create; - try - mem.CopyFrom(Stream, n); - mem.Position := 0; - Result := Vcl.Graphics.TBitmap.Create; - Result.LoadFromStream(mem); - finally - mem.Free; - end; - end - else - Result := nil; -end; - -procedure TVisualKeyboardLoaderBinary.LoadHeader(Header: TVisualKeyboardHeader; Stream: TStream); -var - ch: array[0..4] of ansichar; // I3310 - version: Integer; - f: TVisualKeyboardHeaderFlags; -begin - Stream.Read(ch, 4); ch[4] := #0; - if ch <> 'KVKF' then raise EVisualKeyboardLoader.Create('Not a valid Keyman Visual Keyboard File'); - Stream.Read(version, 4); - if version <> $0600 then raise EVisualKeyboardLoader.Create('Version number not recognised'); - - Stream.Read(f, sizeof(f)); Header.Flags := f; - Header.AssociatedKeyboard := ReadString(Stream); - ReadFont(Stream, Header.ANSIFont); - ReadFont(Stream, Header.UnicodeFont); -end; - -procedure TVisualKeyboardLoaderBinary.LoadKeys(Keys: TVisualKeyboardKeyList; Stream: TStream); -var - i, n: Integer; - item: TVisualKeyboardKey; -begin - Stream.Read(n, Sizeof(n)); - for i := 0 to n - 1 do - begin - item := TVisualKeyboardKey.Create; - LoadKey(item, Stream); - Keys.Add(item); - end; -end; - -procedure TVisualKeyboardLoaderBinary.LoadKey(Key: TVisualKeyboardKey; Stream: TStream); -var - Flags: TVisualKeyboardKeyFlags; - Shift, VKey: Word; -begin - Stream.Read(Flags, sizeof(Flags)); Key.Flags := Flags; - Stream.Read(Shift, sizeof(Shift)); Key.Shift := Shift; - Stream.Read(VKey, sizeof(VKey)); Key.VKey := VKey; - Key.Text := ReadString(Stream); - Key.Bitmap := ReadBitmap(Stream); -end; - -procedure TVisualKeyboardLoaderBinary.LoadFromStream(Stream: TStream); -begin - LoadHeader(FKbd.Header, Stream); - LoadKeys(FKbd.Keys, Stream); -end; - -end. +unit VisualKeyboardLoaderBinary; + +interface + +uses + System.SysUtils, + System.Classes, + Vcl.Graphics, + Winapi.Windows, + + VisualKeyboard; + +// +// This originated with the TVisualKeyboardImportXML code but has now diverged for +// UI-clean single-file round-trippable consistency. +// + +type + EVisualKeyboardLoaderBinary = class(EVisualKeyboardLoader); + + TVisualKeyboardLoaderBinary = class(TVisualKeyboardLoader) + private + procedure LoadKey(Key: TVisualKeyboardKey; Stream: TStream); + procedure LoadHeader(Header: TVisualKeyboardHeader; Stream: TStream); + function ReadBitmap(Stream: TStream): Vcl.Graphics.TBitmap; + procedure ReadFont(Stream: TStream; FFont: TFont); + procedure LoadKeys(Keys: TVisualKeyboardKeyList; Stream: TStream); + function ReadString(Stream: TStream): string; + public + procedure LoadFromStream(Stream: TStream); override; + end; + +implementation + +{ TVisualKeyboardLoaderBinary } + +function TVisualKeyboardLoaderBinary.ReadString(Stream: TStream): string; +var + w: Word; + str: string; +begin + Stream.Read(w, sizeof(w)); + SetLength(str, w); + Stream.Read(PChar(str)^, w*2); + // The string read is a C-style null terminated string. We need + // to remove the null character because Pascal strings don't + // use null termination in the same way. Best way to do this is + // to cast it as a C-style string, which reliably terminates at + // first null byte. + Result := PWideChar(str); +end; + +procedure TVisualKeyboardLoaderBinary.ReadFont(Stream: TStream; FFont: TFont); +var + n: Integer; +begin + FFont.Name := ReadString(Stream); + Stream.Read(n, sizeof(n)); + FFont.Size := n; + Stream.Read(n, sizeof(n)); + FFont.Color := TColor(n); +end; + +function TVisualKeyboardLoaderBinary.ReadBitmap(Stream: TStream): Vcl.Graphics.TBitmap; +var + n: DWord; + mem: TMemoryStream; +begin + Stream.Read(n, Sizeof(n)); + if n > 0 then + begin + mem := TMemoryStream.Create; + try + mem.CopyFrom(Stream, n); + mem.Position := 0; + Result := Vcl.Graphics.TBitmap.Create; + Result.LoadFromStream(mem); + finally + mem.Free; + end; + end + else + Result := nil; +end; + +procedure TVisualKeyboardLoaderBinary.LoadHeader(Header: TVisualKeyboardHeader; Stream: TStream); +var + ch: array[0..4] of ansichar; // I3310 + version: Integer; + f: TVisualKeyboardHeaderFlags; +begin + Stream.Read(ch, 4); ch[4] := #0; + if ch <> 'KVKF' then raise EVisualKeyboardLoader.Create('Not a valid Keyman Visual Keyboard File'); + Stream.Read(version, 4); + if version <> $0600 then raise EVisualKeyboardLoader.Create('Version number not recognised'); + + Stream.Read(f, sizeof(f)); Header.Flags := f; + Header.AssociatedKeyboard := ReadString(Stream); + ReadFont(Stream, Header.ANSIFont); + ReadFont(Stream, Header.UnicodeFont); +end; + +procedure TVisualKeyboardLoaderBinary.LoadKeys(Keys: TVisualKeyboardKeyList; Stream: TStream); +var + i, n: Integer; + item: TVisualKeyboardKey; +begin + Stream.Read(n, Sizeof(n)); + for i := 0 to n - 1 do + begin + item := TVisualKeyboardKey.Create; + LoadKey(item, Stream); + Keys.Add(item); + end; +end; + +procedure TVisualKeyboardLoaderBinary.LoadKey(Key: TVisualKeyboardKey; Stream: TStream); +var + Flags: TVisualKeyboardKeyFlags; + Shift, VKey: Word; +begin + Stream.Read(Flags, sizeof(Flags)); Key.Flags := Flags; + Stream.Read(Shift, sizeof(Shift)); Key.Shift := Shift; + Stream.Read(VKey, sizeof(VKey)); Key.VKey := VKey; + Key.Text := ReadString(Stream); + Key.Bitmap := ReadBitmap(Stream); +end; + +procedure TVisualKeyboardLoaderBinary.LoadFromStream(Stream: TStream); +begin + LoadHeader(FKbd.Header, Stream); + LoadKeys(FKbd.Keys, Stream); +end; + +end.