Merge branch 'master' into web-9-modifier-chirality

This commit is contained in:
Joshua Horton 2017-09-25 08:27:31 +07:00
commit e61fa5673a
38 changed files with 2371 additions and 276 deletions

View file

@ -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) {

View file

@ -90,6 +90,10 @@
window.jsInterface.insertText(dn, s);
}
function resetContext() {
tavultesoft.keymanweb.resetContext();
}
function updateKMText(text) {
if(text == undefined) {
text = '';

View file

@ -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);
}

View file

@ -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 {

View file

@ -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

10
web/.editorconfig Normal file
View file

@ -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

View file

@ -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.

View file

@ -26,9 +26,12 @@
<h2><a href="./issue-29.html">Uncompiled Keymanweb - test desktop MutationObserver functionality</a></h2>
<h2><a href="./issue-62.html">Uncompiled Keymanweb - light test for touch-based MutationObserver functionality</a></h2>
<h2><a href="./issue-63.html">Uncompiled Keymanweb - test/stress-test touch-based MutationObserver functionality</a></h2>
<h2><a href="./issue-53.html">Uncompiled Keymanweb - test touch-based keyboards with heterogenous layer structures/row counts.</a></h2>
<h2><a href="./keyboard-errors/uncompiled - error test.html">Uncompiled KeymanWeb - tests keyboard error-handling functionality.</a></h2>
<h2><a href="./attachment-api/index.html?mode=auto">Uncompiled KeymanWeb - tests the new Attachment/Enablement API functionality.</a><h2>
<h2><a href="./chirality/index.html">Uncompiled KeymanWeb - chirality testing.</a><h2>
<h2><a href="./keyboard-errors/uncompiled - error test.html">Uncompiled KeymanWeb - tests keyboard error-handling functionality</a></h2>
<h2><a href="./attachment-api/index.html?mode=auto">Uncompiled KeymanWeb - tests the new Attachment/Enablement API functionality</a><h2>
<h2><a href="./chirality/index.html">Uncompiled KeymanWeb - chirality testing</a><h2>
<h1>Issue Testing</h1>
<h2><a href="./issue-115.html">Long-press on numeric and symbolic layer testing</a></h2>
<h2><a href="./issue-116.html">Next-layer processing testing</a></h2>
<h2><a href="./issue-160.html">Uxxxx code testing</a></h2>
</body>
</html>

View file

@ -46,7 +46,8 @@
<script>
var kmw=window.tavultesoft.keymanweb;
kmw.init({
resources:'resources'
resources:'resources',
attachType:'auto'
});
kmw.addKeyboards({id:'issue115a',name:'Issue 115',language:{id:'eng',name:'English'},
filename:'./issue115a-1.0.js'});

View file

@ -0,0 +1,97 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<!-- Set the viewport width to match phone and tablet device widths -->
<meta name="viewport" content="width=device-width,user-scalable=no" />
<!-- Allow KeymanWeb to be saved to the iPhone home screen -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<!-- Enable IE9 Standards mode -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>KeymanWeb Sample Page - Uncompiled Source</title>
<!-- Your page CSS -->
<style type='text/css'>
body {font-family: Tahoma,helvetica;}
h3 {font-size: 1em;font-weight:normal;color: darkred; margin-bottom: 4px}
.test {font-size: 1.5em; width:80%; min-height:30px; border: 1px solid gray;}
#KeymanWebControl {width:50%;min-width:600px;}
</style>
<!-- Insert uncompiled KeymanWeb source scripts -->
<script src="../../source/kmwstring.js" type="application/javascript"></script>
<script src="../../source/kmwbase.js" type="application/javascript"></script>
<script src="../../source/keymanweb.js" type="application/javascript"></script>
<script src="../../source/kmwosk.js" type="application/javascript"></script>
<script src="../../source/kmwnative.js" type="application/javascript"></script>
<script src="../../source/kmwcallback.js" type="application/javascript"></script>
<script src="../../source/kmwkeymaps.js" type="application/javascript"></script>
<script src="../../source/kmwlayout.js" type="application/javascript"></script>
<script src="../../source/kmwinit.js" type="application/javascript"></script>
<!--
For desktop browsers, a script for the user interface must be inserted here.
Standard UIs are toggle, button, float and toolbar.
The toolbar UI is best for any page designed to support keyboards for
a large number of languages.
-->
<script src="../../source/kmwuitoggle.js"></script>
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.tavultesoft.keymanweb;
kmw.init({
resources:'resources',
attachType:'auto'
});
kmw.addKeyboards({id:'next_layer_tests',name:'Issue 116',language:{id:'eng',name:'English'},
filename:'./next_layer_tests-1.0.js'});
</script>
</head>
<!-- Sample page HTML -->
<body>
<h2>KeymanWeb Sample Page - Uncompiled Source</h2>
<div>
<!--
The following DIV is used to position the Button or Toolbar User Interfaces on the page.
If omitted, those User Interfaces will appear at the top of the document body.
(It is ignored by other User Interfaces.)
-->
<div id='KeymanWebControl'></div>
<h3>Type in your language in this text area:</h3>
<textarea id='ta1' class='test' placeholder='Type here'></textarea>
<h3>or in this input field:</h3>
<input class='test' value='' placeholder='or here'/>
<h3><a href="../index.html">Return to testing home page</a></h3>
</div>
</body>
<!--
*** DEVELOPER NOTE -- FIREFOX CONFIGURATION FOR TESTING ***
*
* If the URL bar starts with <b>file://</b>, Firefox may not load the font used
* to display the special characters used in the On-Screen Keyboard.
*
* To work around this Firefox bug, navigate to <b>about:config</b>
* and set <b>security.fileuri.strict_origin_policy</b> to <b>false</b>
* while testing.
*
* Firefox resolves website-based CSS URI references correctly without needing
* any configuration change, so this change should only be made for file-based testing.
*
***
-->
</html>

File diff suppressed because one or more lines are too long

View file

@ -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"
}
]
}
}

View file

@ -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";}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,40 @@
store(&VERSION) '9.0'
store(&TARGETS) 'web'
store(&ETHNOLOGUECODE) 'eng'
store(&COPYRIGHT) '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"

View file

@ -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}

View file

@ -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

View file

@ -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')

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -23,4 +23,7 @@ signcode:
backup:
$(WZZIP) $(BUILD)\desktop\insthelp.zip $(BACKUPDEFAULTS) insthelp.exe
install:
rem
!include ..\..\Target.mak

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;
{ ---------------------------------------------------------------------------- }

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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.