mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-26 17:47:40 +00:00
Merge branch 'epic/mac-config' into feat/mac/kmp-inf
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled
This commit is contained in:
commit
b7eb4dc9d6
279 changed files with 11878 additions and 11508 deletions
71
HISTORY.md
71
HISTORY.md
|
|
@ -1,5 +1,76 @@
|
|||
# Keyman Version History
|
||||
|
||||
## 19.0.285 alpha 2026-09-11
|
||||
|
||||
* fix(web): handle 'unload' message while attempting model 'load' (#16548)
|
||||
* fix(android): load KMP files from app-external sources (#16392)
|
||||
|
||||
## 19.0.284 alpha 2026-09-10
|
||||
|
||||
* maint(developer): remove unused ajv dependency from kmc-convert (#16539)
|
||||
* fix(web): remove scroll-to-top on page load (#16531)
|
||||
|
||||
## 19.0.283 alpha 2026-09-09
|
||||
|
||||
* change(web): allow autocorrection on input of basic whitespaces (#16541)
|
||||
|
||||
## 19.0.282 alpha 2026-09-08
|
||||
|
||||
* refactor(web): clarify function scope and update some function comments (#16532)
|
||||
|
||||
## 19.0.281 alpha 2026-09-07
|
||||
|
||||
* chore(deps): bump fast-uri from 3.1.5 to 3.1.7 (#16511)
|
||||
* chore(deps): bump nanoid from 3.3.11 to 3.3.18 (#16512)
|
||||
* chore(deps): bump qs and express (#16513)
|
||||
* chore(web): adjust types in guide-examples tests (#16523)
|
||||
|
||||
## 19.0.280 alpha 2026-09-05
|
||||
|
||||
* fix(windows): open chm external links in user's browser (#16515)
|
||||
|
||||
## 19.0.279 alpha 2026-09-03
|
||||
|
||||
* chore(web): tweak error message (#16495)
|
||||
* change(web): stage construction of CorrectionPredictionTuple instances (#16429)
|
||||
* change(web): narrow the range of suggestions examined to determine autocompletion (#16436)
|
||||
* fix(web): remove deprecated `onunload` handler (#16504)
|
||||
* docs(developer): add comment to test fixtures to clarify expectations (#16505)
|
||||
* fix(developer): hide keyboard uninstall confirmation (#16506)
|
||||
* fix(developer): remember presentation and layer when switching platforms in touch layout editor (#16509)
|
||||
* fix(developer): ensure JSON regenerated after undo in touch layout editor (#16516)
|
||||
* fix(developer): improve validation of minDeviceWidth to match spec (#16501)
|
||||
* chore(developer): improve kmc-ldml test messages (#16502)
|
||||
* chore(web): add const for cookie name (#16519)
|
||||
* refactor(web): clarify scope of PageContextAttachment functions (#16521)
|
||||
|
||||
## 19.0.278 alpha 2026-09-02
|
||||
|
||||
* feat(developer): support output folder and `--continue-on-error` (#16453)
|
||||
* fix(developer): validate the number of hardware layers only in Layr compiler (#16455)
|
||||
* fix(developer): validate id and modifiers attributes in Layr compiler (#16456)
|
||||
* fix(developer): disable Copy Link in Model Editor if no link to copy (#16475)
|
||||
* fix(developer): open context help when F1 pressed in Project window (#16481)
|
||||
* chore(web): update file headers (#16497)
|
||||
|
||||
## 19.0.277 alpha 2026-09-01
|
||||
|
||||
* fix(developer): handle comment without trailing whitespace (#16479)
|
||||
* fix(developer): improve scroll wheel behavior in Character Map (#16474)
|
||||
* fix(developer): call 'Show Console' twice to make the console actually show on first use (#16483)
|
||||
* fix(developer): use path not dependent on pwd for starting Server (#16476)
|
||||
* chore(developer): remove duplicated versioning information from Server (#16478)
|
||||
* fix(developer): handle leading delimiters correctly in `u16tok()` (#16446)
|
||||
* chore(android): upload webview source map during android and ios build (#16449)
|
||||
* refactor(windows): rename `GetCapsAndNumlockState()` to `RefreshModifierState()` (#16444)
|
||||
* chore(developer): strip out defunct printing support (#16480)
|
||||
* fix(developer): make tool windows non-topmost when TIKE is deactivated (#16482)
|
||||
* fix(windows): simplify keyboard refresh and make it more on-demand (#16435)
|
||||
* refactor(windows): cleanup unused wm_keyman_control values (#16439)
|
||||
* maint(windows): remove `koSwitchLanguageForAllApplications`, `koAutoSwitchOSKPages`, osVista, osWin7, osWin8, cleanup (#16443)
|
||||
* change(developer): deprecate `fix`, `clearcontext`, bump max .kmx version to 19.0 (#16445)
|
||||
* fix(developer): add missing message parameters and cleanup kmc-kmn messages further (#16493)
|
||||
|
||||
## 19.0.276 alpha 2026-08-28
|
||||
|
||||
* fix(windows): add manifest to insthelper and set trustinfo to asInvoker (#16430)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
19.0.277
|
||||
19.0.286
|
||||
|
|
@ -85,42 +85,33 @@
|
|||
|
||||
|
||||
<!--
|
||||
Capture content by MIME type, which is how Gmail broadcasts
|
||||
attachment open requests. pathPattern and file extensions
|
||||
are ignored, so the MIME type *MUST* be explicit, otherwise
|
||||
we will match absolutely every file opened.
|
||||
-->
|
||||
Capture content by MIME type, which is how Gmail broadcasts
|
||||
attachment open requests. pathPattern and file extensions
|
||||
are ignored, so the MIME type *MUST* be explicit, otherwise
|
||||
we will match absolutely every file opened.
|
||||
|
||||
See https://developer.android.com/guide/components/intents-filters#DataTest, item 3.
|
||||
-->
|
||||
<intent-filter android:priority="50">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<!-- needed for properly formatted email messages -->
|
||||
<data
|
||||
android:mimeType="application/vnd.keyman"
|
||||
android:scheme="content" />
|
||||
<data android:scheme="content" />
|
||||
<data android:host="*" />
|
||||
|
||||
<!-- needed for mangled email messages -->
|
||||
<data
|
||||
android:mimeType="application/keyman"
|
||||
android:scheme="content" />
|
||||
<!-- needed for mangled email messages -->
|
||||
<data
|
||||
android:mimeType="application/octet-stream"
|
||||
android:scheme="content" />
|
||||
<data
|
||||
android:mimeType="application/x-keyman-package"
|
||||
android:scheme="content" />
|
||||
<data android:mimeType="application/vnd.keyman.kmp+zip"/>
|
||||
<data android:mimeType="application/octet-stream"/>
|
||||
</intent-filter>
|
||||
|
||||
<!--
|
||||
Capture file open requests (pathPattern is honoured) where no
|
||||
MIME type is provided in the Intent. An Intent with a null
|
||||
MIME type will never be matched by a filter with a set MIME
|
||||
type, so we need a second intent-filter if we wish to also
|
||||
match files with this extension and a non-null MIME type
|
||||
(even if it is non-null but zero length).
|
||||
-->
|
||||
Capture open requests (pathPattern is honoured) where MIME
|
||||
type is provided in the Intent and a URI is provided.
|
||||
|
||||
See https://developer.android.com/guide/components/intents-filters#DataTest, item 4.
|
||||
-->
|
||||
<intent-filter android:priority="50">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
|
|
@ -128,19 +119,28 @@
|
|||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:scheme="file" />
|
||||
<data android:scheme="content" />
|
||||
|
||||
<data android:host="*" />
|
||||
<data android:pathPattern="/.*\\.kmp" />
|
||||
<data android:pathPattern=".*\\.kmp" />
|
||||
|
||||
<data android:mimeType="application/vnd.keyman.kmp+zip" />
|
||||
<!--
|
||||
Backup MIME intents
|
||||
-->
|
||||
<data android:mimeType="application/octet-stream" />
|
||||
</intent-filter>
|
||||
|
||||
<!--
|
||||
Capture file open requests (pathPattern is honoured) where a
|
||||
(possibly blank) MIME type is provided in the Intent. This
|
||||
filter may only be necessary for supporting ES File Explorer,
|
||||
which has the probably buggy behaviour of using an Intent
|
||||
with a MIME type that is set but zero-length. It's
|
||||
impossible to match such a type except by using a global
|
||||
wildcard.
|
||||
-->
|
||||
Capture file open requests (pathPattern is honoured) where no
|
||||
MIME type is provided in the Intent. An Intent with a null
|
||||
MIME type will never be matched by a filter with a set MIME
|
||||
type, so we need a second intent-filter if we wish to also
|
||||
match files with this extension and a non-null MIME type
|
||||
(even if it is non-null but zero length).
|
||||
|
||||
See https://developer.android.com/guide/components/intents-filters#DataTest, item 2.
|
||||
-->
|
||||
<intent-filter android:priority="50">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
|
|
@ -148,25 +148,10 @@
|
|||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:scheme="file" />
|
||||
<data android:scheme="content" />
|
||||
|
||||
<data android:host="*" />
|
||||
<data android:mimeType="*/*" />
|
||||
<data android:pathPattern="/.*\\.kmp" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<!-- http:// and https:// protocols -->
|
||||
<data
|
||||
android:host="*"
|
||||
android:pathPattern="/.*\\.kmp"
|
||||
android:scheme="http" />
|
||||
<data
|
||||
android:host="*"
|
||||
android:pathPattern="/.*\\.kmp"
|
||||
android:scheme="https" />
|
||||
<data android:pathPattern=".*\\.kmp" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
|
|
@ -192,35 +177,14 @@
|
|||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:host="keyman-staging.com"
|
||||
android:scheme="http"
|
||||
android:pathPrefix="/go/package/download" />
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
|
||||
<data
|
||||
android:host="keyman.com"
|
||||
android:scheme="https"
|
||||
android:pathPrefix="/go/package/download" />
|
||||
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:priority="50">
|
||||
<!-- KMAPro should also be able to handle /keyboards/install links and convert to /go/package/download -->
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:host="keyman-staging.com"
|
||||
android:scheme="http"
|
||||
android:pathPrefix="/keyboards/install" />
|
||||
|
||||
<data
|
||||
android:host="keyman.com"
|
||||
android:scheme="https"
|
||||
android:pathPrefix="/keyboards/install" />
|
||||
<data android:host="keyman-staging.com" />
|
||||
<data android:host="keyman.com" />
|
||||
|
||||
<data android:pathPrefix="/go/package/download" />
|
||||
<data android:pathPrefix="/keyboards/install" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
|
|
|||
|
|
@ -33,13 +33,8 @@ public class CheckPermissions {
|
|||
// API 30-32
|
||||
permissionsOK = Environment.isExternalStorageManager() ||
|
||||
checkPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
} else {
|
||||
// API 33+
|
||||
// We had to remove these MEDIA permissions from AndroidManifest.xml so these will end up failing
|
||||
// https://support.google.com/googleplay/android-developer/answer/14115180?hl=en
|
||||
permissionsOK = permissionsOK && checkPermission(activity, Manifest.permission.READ_MEDIA_IMAGES);
|
||||
permissionsOK = permissionsOK && checkPermission(activity, Manifest.permission.READ_MEDIA_VIDEO);
|
||||
}
|
||||
// API 33+ does not need any special permissions.
|
||||
|
||||
return permissionsOK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
. "${KEYMAN_ROOT}/resources/build/ci/sentry-control.inc.sh"
|
||||
|
||||
# ################################ Main script ################################
|
||||
|
||||
|
|
@ -19,6 +20,7 @@ builder_describe "Builds Keyman Engine for Android." \
|
|||
"configure" \
|
||||
"build" \
|
||||
"test Runs lint and unit tests." \
|
||||
"publish-symbols Publishes symbols to Sentry." \
|
||||
":engine Builds Engine"
|
||||
|
||||
builder_parse "$@"
|
||||
|
|
@ -79,6 +81,13 @@ do_test() {
|
|||
./gradlew $GRADLE_DAEMON $TEST_FLAGS
|
||||
}
|
||||
|
||||
builder_run_action clean:engine rm -rf build app/build
|
||||
builder_run_action build:engine do_build
|
||||
builder_run_action test:engine do_test
|
||||
do_publish_symbols() {
|
||||
if builder_is_ci_build && builder_is_ci_build_level_release; then
|
||||
sentry_upload_web "${KEYMAN_ROOT}/web/build/app/webview/release/"
|
||||
fi
|
||||
}
|
||||
|
||||
builder_run_action clean:engine rm -rf build app/build
|
||||
builder_run_action build:engine do_build
|
||||
builder_run_action test:engine do_test
|
||||
builder_run_action publish-symbols do_publish_symbols
|
||||
|
|
|
|||
|
|
@ -326,10 +326,20 @@ int u16ncmp(const KMX_WCHAR* p, const KMX_WCHAR* q, size_t count) {
|
|||
* @return Pointer to the first token in p
|
||||
*/
|
||||
KMX_WCHAR* u16tok(KMX_WCHAR* p, const KMX_WCHAR ch, KMX_WCHAR** ctx) {
|
||||
if(ch == 0 || !ctx) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!p) {
|
||||
p = *ctx;
|
||||
if (!p)
|
||||
if (!p) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// skip initial delimiter
|
||||
while (*p == ch) {
|
||||
p++;
|
||||
}
|
||||
|
||||
KMX_WCHAR* q = p;
|
||||
|
|
@ -356,10 +366,20 @@ KMX_WCHAR* u16tok(KMX_WCHAR* p, const KMX_WCHAR ch, KMX_WCHAR** ctx) {
|
|||
* @return Pointer to the first token in p
|
||||
*/
|
||||
KMX_WCHAR* u16tok(KMX_WCHAR* p, const KMX_WCHAR* delimiters, KMX_WCHAR** ctx) {
|
||||
if(!ctx || !delimiters || !(*delimiters)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!p) {
|
||||
p = *ctx;
|
||||
if (!p)
|
||||
if (!p) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// skip initial delimiters
|
||||
while (*p && u16chr(delimiters, *p)) {
|
||||
p++;
|
||||
}
|
||||
|
||||
KMX_WCHAR* q = p;
|
||||
|
|
|
|||
|
|
@ -51,9 +51,10 @@ namespace kmx {
|
|||
|
||||
#define VERSION_160 0x00001000
|
||||
#define VERSION_170 0x00001100
|
||||
#define VERSION_190 0x00001300
|
||||
|
||||
#define VERSION_MIN VERSION_50
|
||||
#define VERSION_MAX VERSION_170
|
||||
#define VERSION_MAX VERSION_190
|
||||
|
||||
//
|
||||
// Backspace types
|
||||
|
|
@ -201,7 +202,7 @@ namespace kmx {
|
|||
//#define CODE_EXTENDEDEND 0x0B deprecated
|
||||
#define CODE_SWITCH 0x0C
|
||||
#define CODE_KEY 0x0D
|
||||
#define CODE_CLEARCONTEXT 0x0E
|
||||
#define CODE_CLEARCONTEXT 0x0E // deprecated in 19.0
|
||||
#define CODE_CALL 0x0F
|
||||
// UC_SENTINEL_EXTENDEDEND 0x10
|
||||
#define CODE_CONTEXTEX 0x11
|
||||
|
|
@ -234,7 +235,7 @@ namespace kmx {
|
|||
#define U_CODE_DEADKEY u"\u0008"
|
||||
#define U_CODE_EXTENDED u"\u000A"
|
||||
#define U_CODE_SWITCH u"\u000C"
|
||||
#define U_CODE_CLEARCONTEXT u"\u000E"
|
||||
#define U_CODE_CLEARCONTEXT u"\u000E" // deprecated in 19.0
|
||||
#define U_CODE_CALL u"\u000F"
|
||||
#define U_CODE_EXTENDEDEND u"\u0010"
|
||||
#define U_CODE_CONTEXTEX u"\u0011"
|
||||
|
|
@ -256,7 +257,7 @@ namespace kmx {
|
|||
#define C_CODE_DEADKEY(deadkey) U_UC_SENTINEL U_CODE_DEADKEY deadkey
|
||||
#define C_CODE_EXTENDED(varargs) U_UC_SENTINEL U_CODE_EXTENDED varargs
|
||||
#define C_CODE_SWITCH(val) U_UC_SENTINEL U_CODE_SWITCH val
|
||||
#define C_CODE_CLEARCONTEXT() U_UC_SENTINEL U_CODE_CLEARCONTEXT
|
||||
#define C_CODE_CLEARCONTEXT() U_UC_SENTINEL U_CODE_CLEARCONTEXT // deprecated in 19.0
|
||||
#define C_CODE_CALL(val) U_UC_SENTINEL U_CODE_CALL val
|
||||
#define C_CODE_CONTEXTEX(val) U_UC_SENTINEL U_CODE_CONTEXTEX val
|
||||
#define C_CODE_NOTANY(val) U_UC_SENTINEL U_CODE_NOTANY val
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ export enum KMX_Version {
|
|||
VERSION_140 = 0x00000E00,
|
||||
VERSION_150 = 0x00000F00,
|
||||
VERSION_160 = 0x00001000,
|
||||
VERSION_170 = 0x00001100
|
||||
VERSION_170 = 0x00001100,
|
||||
VERSION_190 = 0x00001300,
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -165,9 +166,10 @@ export class KMXFile {
|
|||
public static readonly VERSION_150 = KMX_Version.VERSION_150;
|
||||
public static readonly VERSION_160 = KMX_Version.VERSION_160;
|
||||
public static readonly VERSION_170 = KMX_Version.VERSION_170;
|
||||
public static readonly VERSION_190 = KMX_Version.VERSION_190;
|
||||
|
||||
public static readonly VERSION_MIN = this.VERSION_50;
|
||||
public static readonly VERSION_MAX = this.VERSION_170;
|
||||
public static readonly VERSION_MAX = this.VERSION_190;
|
||||
|
||||
//
|
||||
// Backspace types
|
||||
|
|
@ -308,7 +310,7 @@ export class KMXFile {
|
|||
//public static readonly CODE_EXTENDEDEND = 0x0B; deprecated
|
||||
public static readonly CODE_SWITCH = 0x0C;
|
||||
public static readonly CODE_KEY = 0x0D;
|
||||
public static readonly CODE_CLEARCONTEXT = 0x0E;
|
||||
public static readonly CODE_CLEARCONTEXT = 0x0E; // deprecated in 19.0
|
||||
public static readonly CODE_CALL = 0x0F;
|
||||
// UC_SENTINEL_EXTENDEDEND 0x10
|
||||
public static readonly CODE_CONTEXTEX = 0x11;
|
||||
|
|
|
|||
|
|
@ -75,8 +75,9 @@
|
|||
#define VERSION_150 0x00000F00
|
||||
#define VERSION_160 0x00001000
|
||||
#define VERSION_170 0x00001100
|
||||
#define VERSION_190 0x00001300
|
||||
#define VERSION_MIN VERSION_50
|
||||
#define VERSION_MAX VERSION_170
|
||||
#define VERSION_MAX VERSION_190
|
||||
|
||||
/*
|
||||
Special flag for WM_CHAR/WM_KEY???/WM_SYSKEY???: says that key has been
|
||||
|
|
@ -257,7 +258,7 @@
|
|||
//#define CODE_EXTENDEDEND 0x0B deprecated
|
||||
#define CODE_SWITCH 0x0C
|
||||
#define CODE_KEY 0x0D
|
||||
#define CODE_CLEARCONTEXT 0x0E
|
||||
#define CODE_CLEARCONTEXT 0x0E // deprecated in 19.0
|
||||
#define CODE_CALL 0x0F
|
||||
// UC_SENTINEL_EXTENDEDEND 0x10
|
||||
#define CODE_CONTEXTEX 0x11
|
||||
|
|
|
|||
|
|
@ -896,22 +896,36 @@ end;
|
|||
procedure TfrmCharacterMapNew.gridMouseWheelDown(Sender: TObject;
|
||||
Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
|
||||
begin
|
||||
Handled := True;
|
||||
if ssCtrl in Shift then
|
||||
begin
|
||||
Handled := True;
|
||||
// Zoom
|
||||
if tbSize.Position < tbSize.Max then
|
||||
tbSize.Position := tbSize.Position + tbSize.PageSize;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Scroll
|
||||
if grid.TopRow < grid.RowCount - grid.VisibleRowCount then
|
||||
grid.TopRow := grid.TopRow + 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TfrmCharacterMapNew.gridMouseWheelUp(Sender: TObject;
|
||||
Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
|
||||
begin
|
||||
Handled := True;
|
||||
if ssCtrl in Shift then
|
||||
begin
|
||||
Handled := True;
|
||||
// Zoom
|
||||
if tbSize.Position > tbSize.Min then
|
||||
tbSize.Position := tbSize.Position - tbSize.PageSize;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Scroll
|
||||
if grid.TopRow > 0 then
|
||||
grid.TopRow := grid.TopRow - 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
(*
|
||||
Name: GetOsVersion
|
||||
Copyright: Copyright (C) SIL International.
|
||||
Documentation:
|
||||
Description:
|
||||
Documentation:
|
||||
Description:
|
||||
Create Date: 4 Dec 2006
|
||||
|
||||
Modified Date: 28 May 2014
|
||||
Authors: mcdurdin
|
||||
Related Files:
|
||||
Dependencies:
|
||||
Related Files:
|
||||
Dependencies:
|
||||
|
||||
Bugs:
|
||||
Todo:
|
||||
Notes:
|
||||
Bugs:
|
||||
Todo:
|
||||
Notes:
|
||||
History: 04 Dec 2006 - mcdurdin - Add osVista
|
||||
04 May 2012 - mcdurdin - I3306 - V9.0 - Remove TntControls + Win9x support
|
||||
24 Oct 2012 - mcdurdin - I3487 - V9.0 - Add detection of Win8
|
||||
|
|
@ -27,7 +27,7 @@ uses
|
|||
Winapi.Windows;
|
||||
|
||||
type
|
||||
TOS = (osLegacy, osVista, osWin7, osWin8, osWin10, osOther); // I3669 // I4222
|
||||
TOS = (osLegacy, osWin10, osOther); // I3669 // I4222
|
||||
|
||||
function GetOs: TOS;
|
||||
|
||||
|
|
@ -46,12 +46,7 @@ begin
|
|||
begin
|
||||
case osv.dwMajorVersion of
|
||||
5: Result := osLegacy;
|
||||
6: case osv.dwMinorVersion of
|
||||
0: Result := osVista;
|
||||
1: Result := osWin7;
|
||||
2: Result := osWin8; // I3487
|
||||
else Result := osWin8; // I3669
|
||||
end;
|
||||
6: Result := osLegacy;
|
||||
10: Result := osWin10;
|
||||
else
|
||||
Result := osWin10;
|
||||
|
|
|
|||
|
|
@ -35,12 +35,6 @@ var
|
|||
t: TBCP47Tag;
|
||||
LangTag: TLangTag;
|
||||
begin
|
||||
// We do not try and canonicalize language tags on Windows 7, because it does
|
||||
// not follow the same patterns as for later versions of Windows. For example,
|
||||
// zh-CN is not canonicalized to zh-Hans-CN
|
||||
if GetOs = osWin7 then
|
||||
Exit(Tag);
|
||||
|
||||
if Tag = '' then
|
||||
Exit('');
|
||||
|
||||
|
|
|
|||
|
|
@ -127,9 +127,6 @@ const
|
|||
SRegValue_ProxyLogin = 'proxy login';
|
||||
|
||||
SRegValue_AutoOpenOSK = 'auto open osk'; // CU, default true
|
||||
SRegValue_AutoSwitchOSKPages = 'auto switch osk pages'; // CU, default true
|
||||
SRegValue_SwitchLanguageWithKeyboard = 'switch language with keyboard'; // CU, default true
|
||||
SRegValue_SwitchLanguageForAllApplications = 'switch language for all applications'; // CU, default true
|
||||
|
||||
SRegValue_DeepTSFIntegration = 'deep tsf integration'; // LM, 0=disable, 1=enable, 2=default
|
||||
SRegKey_AppIntegration = SRegKey_KeymanEngine_LM + '\App Integration'; //KM
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ const
|
|||
//#define CODE_EXTENDEDEND = $0B deprecated
|
||||
CODE_SWITCH = $0C;
|
||||
CODE_KEY = $0D;
|
||||
CODE_CLEARCONTEXT = $0E;
|
||||
CODE_CLEARCONTEXT = $0E; // deprecated in 19.0
|
||||
CODE_CALL = $0F;
|
||||
// UC_SENTINEL_EXTENDEDEND 0x10
|
||||
CODE_CONTEXTEX = $11;
|
||||
|
|
@ -99,9 +99,10 @@ const
|
|||
VERSION_150 = $00000F00;
|
||||
VERSION_160 = $00001000;
|
||||
VERSION_170 = $00001100;
|
||||
VERSION_190 = $00001300;
|
||||
|
||||
VERSION_MIN = VERSION_50;
|
||||
VERSION_MAX = VERSION_170;
|
||||
VERSION_MAX = VERSION_190;
|
||||
|
||||
VERSION_MASK_MINOR = $00FF;
|
||||
VERSION_MASK_MAJOR = $FF00;
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ begin
|
|||
CODE_CONTEXTEX: Inc(Result, 2);
|
||||
CODE_NOTANY: Inc(Result, 2);
|
||||
|
||||
CODE_CLEARCONTEXT: Inc(Result, 2); // I3442
|
||||
CODE_CLEARCONTEXT: Inc(Result, 2); // I3442 // deprecated in 19.0
|
||||
CODE_IFOPT: Inc(Result, 4); // I3442
|
||||
CODE_IFSYSTEMSTORE: Inc(Result, 4); // I3442
|
||||
CODE_SETOPT: Inc(Result, 3); // I3442
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const int km::core::kmx::CODE__SIZE[] = {
|
|||
-1, // CODE_EXTENDEDEND 0x0B (unused)
|
||||
1, // CODE_SWITCH 0x0C
|
||||
-1, // CODE_KEY 0x0D (never used)
|
||||
0, // CODE_CLEARCONTEXT 0x0E
|
||||
0, // CODE_CLEARCONTEXT 0x0E (deprecated in 19.0)
|
||||
1, // CODE_CALL 0x0F
|
||||
-1, // UC_SENTINEL_EXTENDEDEND 0x10 (not valid with UC_SENTINEL)
|
||||
1, // CODE_CONTEXTEX 0x11
|
||||
|
|
|
|||
|
|
@ -495,6 +495,7 @@ int KMX_ProcessEvent::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR end
|
|||
FoundUse = TRUE;
|
||||
break;
|
||||
case CODE_CLEARCONTEXT:
|
||||
// statement deprecated in 19.0
|
||||
// no longer supported, no-op
|
||||
break;
|
||||
case CODE_INDEX:
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ working on ‘layr’, using ‘disp’ as a model from https://github.com/keyma
|
|||
|
||||
- update basic.xml and basic.txt
|
||||
- Tweak `eveloper/src/kmc-ldml/test/fixtures/basic.xml` as needed
|
||||
- You can use `developer/src/kmc-ldml/build.sh build-fixtures` which will generate these. The two .kmx files are supposed to match: if not, fix `basic.txt` or fix other bugs.
|
||||
- You can use `developer/src/kmc-ldml/build.sh fixtures-build` which will generate these. The two .kmx files are supposed to match: if not, fix `basic.txt` or fix other bugs.
|
||||
- `developer/src/kmc-ldml/build/test/fixtures/basic-txt.kmx` - KMX generated from basic.txt.
|
||||
- `developer/src/kmc-ldml/build/test/fixtures/basic-xml.kmx` - KMX generated from basic.xml.
|
||||
- `developer/src/kmc-ldml/build/test/fixtures/basic-xml.kvk` - KVK generated from basic.xml.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="a" /> <!-- number row -->
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ Keys that don't show up in a row don't generate an output
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="1" />
|
||||
<row keys="q" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="hmaqtugha that" /> <!-- number row -->
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="hmaqtugha that screamcat" /> <!-- number row -->
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ from https://github.com/unicode-org/cldr/blob/keyboard-preview/docs/ldml/tr35-ke
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer modifiers="none" id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="hat 1 2 3 4 5 6 7 8 9 0 hyphen equal" />
|
||||
<row keys="q w e r t y u i o p" />
|
||||
<row keys="a s d f g h j k l" />
|
||||
<row keys="z x c v b n m" />
|
||||
<row keys="space" />
|
||||
</layer>
|
||||
<layer modifiers="shift" id="shift">
|
||||
<layer modifiers="shift">
|
||||
<row keys="untransformed-hat 1 2 3 4 5 6 7 8 9 0 hyphen equal" />
|
||||
<row keys="Q W E R T Y U I O P" />
|
||||
<row keys="A S D F G H J K L" />
|
||||
|
|
|
|||
|
|
@ -18,16 +18,16 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base" modifiers="none">
|
||||
<layer modifiers="none">
|
||||
<row keys="hmaqtugha that" /> <!-- number row -->
|
||||
</layer>
|
||||
<layer id="shift" modifiers="shift">
|
||||
<layer modifiers="shift">
|
||||
<row keys="seven eee" /> <!-- number row -->
|
||||
</layer>
|
||||
<layer id="control" modifiers="ctrl">
|
||||
<layer modifiers="ctrl">
|
||||
<row keys="that gap" /> <!-- number row -->
|
||||
</layer>
|
||||
<layer id="catchall" modifiers="other">
|
||||
<layer modifiers="other">
|
||||
<row keys="eee gap" /> <!-- number row -->
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<keys/> <!-- implied only -->
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base" modifiers="none">
|
||||
<layer modifiers="none">
|
||||
<row keys="a" /> <!-- K_BKQUOTE -->
|
||||
</layer>
|
||||
<layer modifiers="shift">
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer modifiers="none" id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="hat 1 2 3 4 5 6 7 8 9 0 hyphen equal" />
|
||||
<row keys="q w e r t y u i o p" />
|
||||
<row keys="a s d f g h j k l" />
|
||||
<row keys="z x c v b n m" />
|
||||
<row keys="space" />
|
||||
</layer>
|
||||
<layer modifiers="shift" id="shift">
|
||||
<layer modifiers="shift">
|
||||
<row keys="untransformed-hat 1 2 3 4 5 6 7 8 9 0 hyphen equal" />
|
||||
<row keys="Q W E R T Y U I O P" />
|
||||
<row keys="A S D F G H J K L" />
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ from https://github.com/unicode-org/cldr/blob/keyboard-preview/docs/ldml/tr35-ke
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer modifiers="none" id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="grave 1 2 3 4 5 6 7 8 9 0" />
|
||||
<row keys="q w e r t y u i o p" />
|
||||
<row keys="a s d f g h j k l" />
|
||||
<row keys="z x c v b n m" />
|
||||
<row keys="space" />
|
||||
</layer>
|
||||
<layer modifiers="shift" id="shift">
|
||||
<layer modifiers="shift">
|
||||
<row keys="grave 1 2 3 4 5 6 7 8 9 0" />
|
||||
<row keys="Q W E R T Y U I O P" />
|
||||
<row keys="A S D F G H J K L" />
|
||||
|
|
|
|||
|
|
@ -21,14 +21,14 @@ https://github.com/unicode-org/cldr/blob/keyboard-preview/docs/ldml/tr35-keyboar
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer modifiers="none" id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="u-0320 1 2 3 4 5 6 7 8 9 0" />
|
||||
<row keys="q w e r t y u i o p" />
|
||||
<row keys="a s d f g h j k l" />
|
||||
<row keys="z x c v b n m" />
|
||||
<row keys="space" />
|
||||
</layer>
|
||||
<layer modifiers="shift" id="shift">
|
||||
<layer modifiers="shift">
|
||||
<row keys="u-0300 u-00e8 nfd nfc not-nfd stampy lgtm u-0344" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -21,14 +21,14 @@ like k_008 but in NFC (and other normalizations)
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer modifiers="none" id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="u-0320 1 2 3 4 5 6 7 8 9 0" />
|
||||
<row keys="q w e r t y u i o p" />
|
||||
<row keys="a s d f g h j k l" />
|
||||
<row keys="z x c v b n m" />
|
||||
<row keys="space" />
|
||||
</layer>
|
||||
<layer modifiers="shift" id="shift">
|
||||
<layer modifiers="shift">
|
||||
<row keys="u-0300 u-00e8 nfd nfc not-nfd stampy lgtm u-0344" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -14,13 +14,13 @@ will match the default layer
|
|||
<keys/>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base" modifiers="none">
|
||||
<layer modifiers="none">
|
||||
<row keys="b" /> <!-- number row -->
|
||||
</layer>
|
||||
<layer id="shift" modifiers="shift">
|
||||
<layer modifiers="shift">
|
||||
<row keys="s" /> <!-- number row -->
|
||||
</layer>
|
||||
<layer id="other" modifiers="other">
|
||||
<layer modifiers="other">
|
||||
<row keys="d" /> <!-- number row -->
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="a gap"/>
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="a" /> <!-- number row -->
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ Comment: enter=not mappable, causes ctx reset.
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="a gap" /> <!-- number row -->
|
||||
<row keys="q gap" /> <!-- q w ... -->
|
||||
<row keys="gap" />
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer modifiers="none" id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="roast" />
|
||||
<row keys="gap wa gap gap t2 gap gap gap o gap" />
|
||||
<row keys="gap sakot gap gap gap gap gap kha gap" />
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer modifiers="none" id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="gap overbar underbar circumflex" />
|
||||
<row keys="gap gap e gap gap y u i o gap" />
|
||||
<row keys="a" />
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer modifiers="none" id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="grave acute caret hacek squiggle" />
|
||||
<row keys="q w e" /> <!-- etc -->
|
||||
<row keys="a s d" /> <!-- etc -->
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer modifiers="none" id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="B 1 2 3" />
|
||||
<row keys="q w e" /> <!-- etc -->
|
||||
<row keys="a s d" /> <!-- etc -->
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer modifiers="none" id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="caret umlaut" />
|
||||
<row keys="q w e r t y" />
|
||||
<row keys="a" />
|
||||
|
|
|
|||
|
|
@ -185,9 +185,24 @@ The following parameters are available:
|
|||
|
||||
`-o <filename>`, `--out-file <filename>`
|
||||
|
||||
: Overrides the default path and filename for the output file(s). Note that
|
||||
some compilers emit multiple files, in which case, the output filenames
|
||||
will vary by file extension.
|
||||
: Overrides the default path and filename for the output file(s). Note that some
|
||||
compilers emit multiple files, in which case, the output filenames will vary
|
||||
by file extension.
|
||||
|
||||
If the output filename ends in a forward slash (`/`) or backslash (`\\`), or
|
||||
if multiple input files are specified, or if the output filename exists and is
|
||||
already a folder, then the filename will be treated as a folder, and all
|
||||
output files will be written within that.
|
||||
|
||||
An error will be raised if an output file is specified and already exists, and
|
||||
is a regular file, but is expected to be a folder.
|
||||
|
||||
`--continue-on-error`
|
||||
|
||||
: When building multiple input files, continue building subsequent files even if
|
||||
a file fails to build. Note that `kmc` will still abort if an internal error
|
||||
is encountered.
|
||||
|
||||
|
||||
## `kmc build file` additional options
|
||||
|
||||
|
|
|
|||
|
|
@ -45,17 +45,6 @@ type
|
|||
function CanClearSelection: Boolean;
|
||||
end;
|
||||
|
||||
IKMDPrintActions = interface
|
||||
['{C6F05AD6-42A3-404F-944C-904393A51837}']
|
||||
//function PageSetup: Boolean;
|
||||
function PrintFile: Boolean;
|
||||
end;
|
||||
|
||||
IKMDPrintPreviewActions = interface(IKMDPrintActions)
|
||||
['{B5879BC4-7A0C-4E00-AAE3-E2D48489152A}']
|
||||
function PrintPreview: Boolean;
|
||||
end;
|
||||
|
||||
IKMDViewExpandEditorActions = interface
|
||||
['{680A172E-A221-4CDA-B68C-1E2AF8487510}']
|
||||
procedure ExpandContractEditor;
|
||||
|
|
|
|||
|
|
@ -285,6 +285,8 @@ namespace KmnCompilerMessages {
|
|||
ERROR_NameMustNotContainParentheses = SevError | 0x0B9,
|
||||
ERROR_NameMustNotContainSquareBrackets = SevError | 0x0BA,
|
||||
|
||||
WARN_DeprecatedStatement = SevWarn | 0x0BB,
|
||||
|
||||
FATAL_BufferOverflow = SevFatal | 0x0C0
|
||||
// FATAL_Break = SevFatal | 0x0C1, unused
|
||||
};
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ export enum CompilerErrorMask {
|
|||
Severity = 0x00F00000, // includes reserved bits, 16 possible severity levels
|
||||
Error = 0x000FFFFF, // error | namespace
|
||||
Namespace = 0x000FF000, // 256 possible namespaces
|
||||
BaseError = 0x00000FFF, // error code, 2,048 possible error codes per namespace
|
||||
BaseError = 0x00000FFF, // error code, 4,096 possible error codes per namespace
|
||||
Reserved = 0xFF000000, // do not use these error values at this time
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ export interface KL_Key {
|
|||
* Ether the action attribute or the output attribute must be specified for a key, but not both.
|
||||
* kmc-convert does not use the concept of 'anonymous actions' at present.
|
||||
*/
|
||||
action?: string; //TODO-KMC-CONVERT: Support <action> sub-element 'anonymous actions' in the future
|
||||
action?: string;
|
||||
/**
|
||||
* The output attribute specifies the character that is produced when a key is pressed.
|
||||
* Ether the action attribute or the output attribute must be specified for a key, but not both.
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
<key id="that" output="" /> <!-- illegal NCR (single char)-->
|
||||
</keys>
|
||||
|
||||
<layers formId="us" minDeviceWidth="123">
|
||||
<layers formId="touch" minDeviceWidth="123">
|
||||
<layer id="">
|
||||
<row keys="hmaqtugha that" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
]
|
||||
},
|
||||
"layers": {
|
||||
"formId": "us",
|
||||
"formId": "touch",
|
||||
"minDeviceWidth": "123",
|
||||
"layer": {
|
||||
"id": "",
|
||||
|
|
|
|||
|
|
@ -38,9 +38,6 @@
|
|||
"@keymanapp/resources-gosh": "*",
|
||||
"@types/node": "^20.4.1",
|
||||
"@types/semver": "^7.3.12",
|
||||
"ajv": "^8.12.0",
|
||||
"ajv-cli": "^5.0.0",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"c8": "^7.12.0",
|
||||
"chalk": "^2.4.2",
|
||||
"typescript": "^5.4.5"
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -248,7 +248,7 @@ export function incxstr(p: string, x: number): number {
|
|||
case KMX.KMXFile.CODE_CONTEXTEX: x += 2; break;
|
||||
case KMX.KMXFile.CODE_NOTANY: x += 2; break;
|
||||
|
||||
case KMX.KMXFile.CODE_CLEARCONTEXT: x += 2; break;
|
||||
case KMX.KMXFile.CODE_CLEARCONTEXT: x += 2; break; // deprecated in 19.0
|
||||
case KMX.KMXFile.CODE_IFOPT: x += 4; break;
|
||||
case KMX.KMXFile.CODE_IFSYSTEMSTORE: x += 4; break;
|
||||
case KMX.KMXFile.CODE_SETOPT: x += 3; break;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*/
|
||||
import { dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import fs from 'node:fs';
|
||||
import 'mocha';
|
||||
import { assert } from 'chai';
|
||||
import { KmnCompiler } from '../src/main.js';
|
||||
import { dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import fs from 'fs';
|
||||
import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
|
||||
import { KmxFileReader } from '@keymanapp/common-types';
|
||||
import { compileTestKeyboard } from './helpers/index.js';
|
||||
import { KmnCompiler } from '../src/main.js';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url)).replace(/\\/g, '/');
|
||||
const keyboardsDir = __dirname + '/../../../../../common/test/keyboards/';
|
||||
|
|
@ -13,6 +18,16 @@ const baselineDir = keyboardsDir + 'baseline/';
|
|||
describe('Compiler class', function() {
|
||||
const callbacks = new TestCompilerCallbacks(this);
|
||||
|
||||
this.beforeEach(function() {
|
||||
callbacks.clear();
|
||||
});
|
||||
|
||||
this.afterEach(function() {
|
||||
if(this.currentTest?.isFailed()) {
|
||||
callbacks.printMessages();
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw on failure', async function() {
|
||||
const compiler = new KmnCompiler();
|
||||
const callbacks : any = null; // ERROR
|
||||
|
|
@ -129,4 +144,20 @@ describe('Compiler class', function() {
|
|||
assert.deepEqual(kvkData, kvkFixtureData);
|
||||
});
|
||||
|
||||
it('should trim all whitespace for `&targets` store', async function() {
|
||||
const result = await compileTestKeyboard(callbacks, ['keyboards', 'targets-with-whitespace.kmn']);
|
||||
assert.isNotNull(result);
|
||||
|
||||
// Verify implictly that `&targets` store was interpreted correctly as 'any'
|
||||
// because the compiler generated both JS and KMX targets (#13721)
|
||||
assert.isNotNull(result.artifacts.js);
|
||||
assert.isNotNull(result.artifacts.kmx);
|
||||
|
||||
// Then verify directly that the `&targets` store was trimmed by looking at
|
||||
// the final value in the kmx data
|
||||
const reader = new KmxFileReader();
|
||||
const keyboard = reader.read(result.artifacts.kmx.data);
|
||||
assert.equal(keyboard.targets, 'any');
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
11
developer/src/kmc-kmn/test/fixtures/keyboards/targets-with-whitespace.kmn
vendored
Normal file
11
developer/src/kmc-kmn/test/fixtures/keyboards/targets-with-whitespace.kmn
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
store(&NAME) 'targets_with_whitespace'
|
||||
store(&VERSION) '10.0'
|
||||
|
||||
c this used to generate KM0207B: At least one compile target must be specified
|
||||
store(&TARGETS) ' any'
|
||||
|
||||
begin unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
+ 'x' > 'y'
|
||||
9
developer/src/kmc-kmn/test/fixtures/keyboards/warn_deprecated_statement-clearcontext-17.kmn
vendored
Normal file
9
developer/src/kmc-kmn/test/fixtures/keyboards/warn_deprecated_statement-clearcontext-17.kmn
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'warn_deprecated_statement-clearcontext-17'
|
||||
store(&VERSION) '17.0'
|
||||
|
||||
begin unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c NOTE: using `clearcontext` should not generate a warning in v17 target version compile (unlike v19 target version)
|
||||
+ 'x' > clearcontext
|
||||
9
developer/src/kmc-kmn/test/fixtures/keyboards/warn_deprecated_statement-clearcontext-19.kmn
vendored
Normal file
9
developer/src/kmc-kmn/test/fixtures/keyboards/warn_deprecated_statement-clearcontext-19.kmn
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'warn_deprecated_statement-clearcontext-19'
|
||||
store(&VERSION) '19.0'
|
||||
|
||||
begin unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c NOTE: using `clearcontext` SHOULD generate a warning in v19 target version compile (unlike v17 target version)
|
||||
+ 'x' > clearcontext
|
||||
9
developer/src/kmc-kmn/test/fixtures/keyboards/warn_deprecated_statement-fix-17.kmn
vendored
Normal file
9
developer/src/kmc-kmn/test/fixtures/keyboards/warn_deprecated_statement-fix-17.kmn
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'warn_deprecated_statement-fix-17'
|
||||
store(&VERSION) '17.0'
|
||||
|
||||
begin unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c NOTE: using `fix` should not generate a warning in v17 target version compile (unlike v19 target version)
|
||||
+ 'x' > fix
|
||||
9
developer/src/kmc-kmn/test/fixtures/keyboards/warn_deprecated_statement-fix-19.kmn
vendored
Normal file
9
developer/src/kmc-kmn/test/fixtures/keyboards/warn_deprecated_statement-fix-19.kmn
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'warn_deprecated_statement-fix-19'
|
||||
store(&VERSION) '19.0'
|
||||
|
||||
begin unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c NOTE: using `fix` SHOULD generate a warning in v19 target version compile (unlike v17 target version)
|
||||
+ 'x' > fix
|
||||
|
|
@ -1,8 +1,13 @@
|
|||
/**
|
||||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*
|
||||
* Helpers and utilities for the Mocha tests.
|
||||
*/
|
||||
import * as path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import * as path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { assert } from 'chai';
|
||||
import { CompilerCallbacks } from '@keymanapp/developer-utils';
|
||||
import { KmnCompiler } from '../../src/compiler/compiler.js';
|
||||
|
||||
/**
|
||||
* Builds a path to the fixture with the given path components.
|
||||
|
|
@ -15,3 +20,13 @@ import { fileURLToPath } from 'url';
|
|||
export function makePathToFixture(...components: string[]): string {
|
||||
return fileURLToPath(new URL(path.join('..', '..', '..', 'test', 'fixtures', ...components), import.meta.url));
|
||||
}
|
||||
|
||||
export async function compileTestKeyboard(callbacks: CompilerCallbacks, fixture: string[]) {
|
||||
const compiler = new KmnCompiler();
|
||||
assert(await compiler.init(callbacks, {saveDebug: true, shouldAddCompilerVersion: false}));
|
||||
assert(compiler.verifyInitialized());
|
||||
|
||||
const kmnPath = makePathToFixture(...fixture);
|
||||
|
||||
return await compiler.run(kmnPath, null);
|
||||
}
|
||||
|
|
@ -3,9 +3,24 @@ import { assert } from 'chai';
|
|||
import { CompilerErrorMask, CompilerErrorNamespace } from '@keymanapp/developer-utils';
|
||||
import { TestCompilerCallbacks, verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers';
|
||||
import { KmnCompiler } from '../src/main.js';
|
||||
import { KmnCompilerMessageRanges, KmnCompilerMessages } from '../src/compiler/kmn-compiler-messages.js';
|
||||
import { KmcKmnCompilerEvent, KmnCompilerMessageRanges, KmnCompilerMessages } from '../src/compiler/kmn-compiler-messages.js';
|
||||
import { makePathToFixture } from './helpers/index.js';
|
||||
|
||||
|
||||
// Source - https://stackoverflow.com/a/9924463
|
||||
// Posted by Jack Allan, modified by community. See post 'Timeline' for change history
|
||||
// Retrieved 2026-09-01, License - CC BY-SA 3.0
|
||||
function getParamNames(func: any) {
|
||||
const STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,\)]*))/mg;
|
||||
const ARGUMENT_NAMES = /([^\s,]+)/g;
|
||||
const fnStr = func.toString().replace(STRIP_COMMENTS, '');
|
||||
let result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
|
||||
if(result === null)
|
||||
result = [];
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
describe('KmnCompilerMessages', function () {
|
||||
const callbacks = new TestCompilerCallbacks(this);
|
||||
|
||||
|
|
@ -13,6 +28,53 @@ describe('KmnCompilerMessages', function () {
|
|||
return verifyCompilerMessagesObject(KmnCompilerMessages, CompilerErrorNamespace.KmnCompiler);
|
||||
});
|
||||
|
||||
it('should have correct parameter types and numeric ranges for kmcmplib vs kmw vs kmc-kmn messages', function() {
|
||||
// See comment in kmn-compiler-messages.ts for logic.
|
||||
// This function follows the same pattern as `verifyCompilerMessagesObject` but with specific tests for
|
||||
// this more constrained object
|
||||
const toTitleCase = (s: string) => s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
|
||||
const keys = Object.keys(KmnCompilerMessages);
|
||||
const m = KmnCompilerMessages as Record<string,any>;
|
||||
|
||||
for(const key of keys) {
|
||||
if(typeof m[key] == 'number') {
|
||||
const o = /^(DEBUG|VERBOSE|INFO|HINT|WARN|ERROR|FATAL)_([A-Za-z0-9_]+)$/.exec(key);
|
||||
const f = toTitleCase(o[1]) + '_' + o[2];
|
||||
// note: validation done in verifyCompilerMessagesObject, don't repeat it here
|
||||
|
||||
const v: KmcKmnCompilerEvent = m[f]('','','','','','','','','','','','' /* ignore arguments*/);
|
||||
|
||||
const code = m[key] & CompilerErrorMask.BaseError;
|
||||
const hex = code.toString(16).padStart(3, '0');
|
||||
|
||||
if(code >= KmnCompilerMessageRanges.RANGE_CompilerMessage_Min &&code <= KmnCompilerMessageRanges.RANGE_CompilerMessage_Max) {
|
||||
// kmc-kmn messages, should not be using mw() function or mc() function
|
||||
//
|
||||
assert.equal(v.kmcKmnSource, 'kmc-kmn', `expected ${v.kmcKmnSource} to equal 'kmc-kmn' for ${hex}, '${key}'.`);
|
||||
} else if(code >= KmnCompilerMessageRanges.RANGE_KMN_COMPILER_MIN && code <= KmnCompilerMessageRanges.RANGE_KMN_COMPILER_MAX) {
|
||||
// kmcmplib or kmw compiler messages, should not be using m() function or mx() function
|
||||
assert.oneOf(v.kmcKmnSource, ['kmcmplib', 'kmw-compiler'], `expected ${v.kmcKmnSource} to equal 'kmcmplib' or 'kmw-compiler for ${hex}, '${key}'.`);
|
||||
if(v.kmcKmnSource == 'kmcmplib') {
|
||||
// The only allowed param is an object o with property p. We
|
||||
// cannot test parameter shape directly, so we will compare
|
||||
// outputs of calling if o.p is set, which should not match what
|
||||
// we get when o.p is not set.
|
||||
const params = getParamNames(m[f]);
|
||||
if(params.length) {
|
||||
const v2: KmcKmnCompilerEvent = m[f]({p:['1','2','3','4','5','6','7','8','9']});
|
||||
assert.notEqual(v.message, v2.message, `Message '${key}' (${hex}) must use KmcmpLibMessageParameters pattern`);
|
||||
}
|
||||
}
|
||||
} else if(code >= KmnCompilerMessageRanges.RANGE_LEXICAL_MODEL_MIN && code <= KmnCompilerMessageRanges.RANGE_LEXICAL_MODEL_MAX) {
|
||||
assert.fail(`Message identifier ${hex} for '${key}' is reserved and should not be used (LEXICAL_MODEL range)`);
|
||||
} else {
|
||||
assert.fail(`Message identifier ${hex} for '${key}' is out of range`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
it('should have a 1:1 correspondence with kmn_compiler_errors.h', function() {
|
||||
|
||||
const headerFilename = 'kmn_compiler_errors.h';
|
||||
|
|
@ -285,4 +347,13 @@ describe('KmnCompilerMessages', function () {
|
|||
await testForMessage(this, ['invalid-keyboards', 'error_name_must_not_contain_square_brackets-deadkey.kmn'], [KmnCompilerMessages.ERROR_NameMustNotContainSquareBrackets, KmnCompilerMessages.ERROR_InvalidDeadkey]);
|
||||
});
|
||||
|
||||
// WARN_DeprecatedStatement
|
||||
|
||||
it('should generate WARN_DeprecatedStatement if the file has `clearcontext` or `fix` statements and is 19.0', async function() {
|
||||
await testForMessage(this, ['keyboards', 'warn_deprecated_statement-clearcontext-19.kmn'], KmnCompilerMessages.WARN_DeprecatedStatement);
|
||||
await testForMessage(this, ['keyboards', 'warn_deprecated_statement-fix-19.kmn'], KmnCompilerMessages.WARN_DeprecatedStatement);
|
||||
await testForMessage(this, ['keyboards', 'warn_deprecated_statement-clearcontext-17.kmn']);
|
||||
await testForMessage(this, ['keyboards', 'warn_deprecated_statement-fix-17.kmn']);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ builder_describe "Keyman kmc Keyboard Compiler module" \
|
|||
"api analyze API and prepare API documentation" \
|
||||
"clean" \
|
||||
"test" \
|
||||
"build-fixtures builds test fixtures for manual examination"
|
||||
"fixtures-build builds test fixtures for manual examination"
|
||||
|
||||
builder_describe_outputs \
|
||||
configure /developer/src/kmc-ldml/src/util/abnf/46/transform-from-required.js \
|
||||
|
|
@ -84,6 +84,6 @@ function do_build_fixtures() {
|
|||
builder_run_action clean do_clean
|
||||
builder_run_action configure do_configure
|
||||
builder_run_action build do_build
|
||||
builder_run_action build-fixtures do_build_fixtures
|
||||
builder_run_action fixtures-build do_build_fixtures
|
||||
builder_run_action api typescript_run_api_extractor developer/src/kmc-ldml main.d.ts
|
||||
builder_run_action test typescript_run_eslint_mocha_tests 90
|
||||
builder_run_action test typescript_run_eslint_mocha_tests
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import Keys = KMXPlus.Keys;
|
|||
import KeysKeys = KMXPlus.KeysKeys;
|
||||
import ListItem = KMXPlus.ListItem;
|
||||
import KeysFlicks = KMXPlus.KeysFlicks;
|
||||
import { allUsedKeyIdsInFlick, allUsedKeyIdsInKey, allUsedKeyIdsInLayers, calculateUniqueKeys, hashFlicks, hashKeys, translateLayerAttrToModifier, validModifier } from '../util/util.js';
|
||||
import { allUsedKeyIdsInFlick, allUsedKeyIdsInKey, allUsedKeyIdsInLayers, calculateUniqueKeys, hashFlicks, hashKeys, translateLayerAttrToModifier } from '../util/util.js';
|
||||
import { SubstitutionUse, Substitutions } from './substitution-tracker.js';
|
||||
|
||||
/** reserved name for the special gap key. space is not allowed in key ids. */
|
||||
|
|
@ -225,13 +225,9 @@ export class KeysCompiler extends SectionCompiler {
|
|||
// Finally, kmap
|
||||
// Use LayerMap + keys to generate compiled keys for hardware
|
||||
const hardwareLayers = this.hardwareLayers();
|
||||
/* c8 ignore next 3 */
|
||||
if (hardwareLayers.length > 1) {
|
||||
// validation should have already caught this
|
||||
throw Error(
|
||||
`Internal error: Expected 0 or 1 hardware layer, not ${hardwareLayers.length}`
|
||||
);
|
||||
} else if (hardwareLayers.length === 1) {
|
||||
if (hardwareLayers.length >= 1) {
|
||||
// Only 1 hardware layer is supported; however, `LayrCompiler` will report
|
||||
// on this error, so we can just process the first one here
|
||||
const theLayers = hardwareLayers[0];
|
||||
const { formId } = theLayers;
|
||||
for (const layer of theLayers.layer) {
|
||||
|
|
@ -461,12 +457,6 @@ export class KeysCompiler extends SectionCompiler {
|
|||
let valid = true;
|
||||
|
||||
const { modifiers } = layer;
|
||||
if (!validModifier(modifiers)) {
|
||||
this.callbacks.reportMessage(
|
||||
LdmlCompilerMessages.Error_InvalidModifier({ modifiers }, layer)
|
||||
);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (layer.row.length > keymap.length) {
|
||||
this.callbacks.reportMessage(
|
||||
|
|
@ -484,7 +474,7 @@ export class KeysCompiler extends SectionCompiler {
|
|||
LdmlCompilerMessages.Error_RowOnHardwareLayerHasTooManyKeys({
|
||||
row: y + 1,
|
||||
hardware: layers.formId,
|
||||
modifiers: modifiers || 'none',
|
||||
modifiers,
|
||||
}, row)
|
||||
);
|
||||
valid = false;
|
||||
|
|
@ -501,7 +491,7 @@ export class KeysCompiler extends SectionCompiler {
|
|||
keyId: key,
|
||||
col: x + 1,
|
||||
row: y + 1,
|
||||
layer: layer.id,
|
||||
layer: layer.id ?? layer.modifiers, // just to give a useful reference point in the error message
|
||||
form: "hardware",
|
||||
}, row)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -20,18 +20,26 @@ export class LayrCompiler extends SectionCompiler {
|
|||
let valid = true;
|
||||
let totalLayerCount = 0;
|
||||
let hardwareLayers = 0;
|
||||
let touchLayers = 0;
|
||||
let touchLayerCount = 0;
|
||||
let hasNullDeviceWidth = false;
|
||||
const deviceWidths = new Set<number>();
|
||||
this.keyboard3.layers?.forEach((layers) => {
|
||||
const { formId } = layers;
|
||||
if (formId === 'touch') {
|
||||
touchLayers++;
|
||||
touchLayerCount++;
|
||||
totalLayerCount += layers.layer?.length;
|
||||
const { minDeviceWidth } = layers;
|
||||
if (!minDeviceWidth ||
|
||||
if (minDeviceWidth === undefined || minDeviceWidth === null) {
|
||||
if(hasNullDeviceWidth) {
|
||||
this.callbacks.reportMessage(LdmlCompilerMessages.Hint_MultipleTouchFormsWithoutMinDeviceWidth(layers));
|
||||
}
|
||||
hasNullDeviceWidth = true;
|
||||
} else if (
|
||||
(typeof minDeviceWidth === 'string' && (<string>minDeviceWidth).trim() === '') ||
|
||||
Number.isNaN(Number(minDeviceWidth)) ||
|
||||
minDeviceWidth < constants.layr_min_minDeviceWidth ||
|
||||
minDeviceWidth > constants.layr_max_minDeviceWidth ||
|
||||
Number.isNaN(Number(minDeviceWidth))) {
|
||||
minDeviceWidth > constants.layr_max_minDeviceWidth
|
||||
) {
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(LdmlCompilerMessages.Error_InvalidLayerWidth({minDeviceWidth}, layers));
|
||||
} else if (deviceWidths.has(minDeviceWidth)) {
|
||||
|
|
@ -40,6 +48,17 @@ export class LayrCompiler extends SectionCompiler {
|
|||
} else {
|
||||
deviceWidths.add(minDeviceWidth);
|
||||
}
|
||||
// For touch layers, id attr must exist, and modifiers attribute should not
|
||||
layers.layer.forEach(layer => {
|
||||
if(typeof layer.id === 'undefined') {
|
||||
this.callbacks.reportMessage(LdmlCompilerMessages.Error_TouchLayerRequiresId({minDeviceWidth}, layer));
|
||||
valid = false;
|
||||
}
|
||||
if(typeof layer.modifiers !== 'undefined') {
|
||||
this.callbacks.reportMessage(LdmlCompilerMessages.Hint_TouchLayerHasModifiers({minDeviceWidth, id: layer.id}, layer));
|
||||
}
|
||||
totalLayerCount++;
|
||||
});
|
||||
} else {
|
||||
// hardware
|
||||
hardwareLayers++;
|
||||
|
|
@ -47,15 +66,22 @@ export class LayrCompiler extends SectionCompiler {
|
|||
valid = false;
|
||||
this.callbacks.reportMessage(LdmlCompilerMessages.Error_ExcessHardware({formId}, layers));
|
||||
}
|
||||
layers.layer.forEach(layer => {
|
||||
const { modifiers } = layer;
|
||||
if(typeof modifiers === 'undefined' || modifiers == '') {
|
||||
this.callbacks.reportMessage(LdmlCompilerMessages.Error_HardwareLayerRequiresModifiers({ formId }, layer));
|
||||
valid = false;
|
||||
}
|
||||
else if (!validModifier(modifiers)) {
|
||||
this.callbacks.reportMessage(LdmlCompilerMessages.Error_InvalidModifier({ modifiers }, layer));
|
||||
valid = false;
|
||||
}
|
||||
if(typeof layer.id !== 'undefined') {
|
||||
this.callbacks.reportMessage(LdmlCompilerMessages.Hint_HardwareLayerHasId({formId, id: layer.id}, layer));
|
||||
}
|
||||
totalLayerCount++;
|
||||
});
|
||||
}
|
||||
layers.layer.forEach((layer) => {
|
||||
const { modifiers } = layer;
|
||||
totalLayerCount++;
|
||||
if (!validModifier(modifiers)) {
|
||||
this.callbacks.reportMessage(LdmlCompilerMessages.Error_InvalidModifier({ modifiers }, layer));
|
||||
valid = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
if (totalLayerCount === 0) { // TODO-LDML: does not validate touch layers yet
|
||||
// no layers seen anywhere
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export class LdmlCompilerMessages {
|
|||
static ERROR_KeyNotFoundInKeyBag = SevError | 0x0005;
|
||||
static Error_KeyNotFoundInKeyBag = (o: { keyId: string, col: number, row: number, layer: string, form: string }, compileContext?: ObjectWithCompileContext) => mx(
|
||||
this.ERROR_KeyNotFoundInKeyBag, compileContext,
|
||||
`Key '${def(o.keyId)}' in position #${def(o.col)} on row #${def(o.row)} of layer ${def(o.layer)}, form '${def(o.form)}' not found in key bag`,
|
||||
`Key '${def(o.keyId)}' in position #${def(o.col)} on row #${def(o.row)} of layer '${def(o.layer)}', form '${def(o.form)}' not found in key bag`,
|
||||
);
|
||||
|
||||
static HINT_OneOrMoreRepeatedLocales = SevHint | 0x0006;
|
||||
|
|
@ -277,7 +277,7 @@ export class LdmlCompilerMessages {
|
|||
);
|
||||
|
||||
static ERROR_InvalidLayerWidth = SevError | 0x002D;
|
||||
static Error_InvalidLayerWidth = (o: { minDeviceWidth: number }, compileContext?: ObjectWithCompileContext) => mx(
|
||||
static Error_InvalidLayerWidth = (o: { minDeviceWidth: number | string }, compileContext?: ObjectWithCompileContext) => mx(
|
||||
this.ERROR_InvalidLayerWidth, compileContext,
|
||||
`Invalid Layers minDeviceWidth=${def(o.minDeviceWidth)}`,
|
||||
`Width must be between 1-999 (millimeters), inclusive.` // sync with layr_max_minDeviceWidth / layr_max_maxDeviceWidth (from spec)
|
||||
|
|
@ -292,6 +292,50 @@ export class LdmlCompilerMessages {
|
|||
`**Hint**: Use "${def(o.recommended)}"`,
|
||||
);
|
||||
|
||||
static ERROR_TouchLayerRequiresId = SevError | 0x0031;
|
||||
static Error_TouchLayerRequiresId = (o: { minDeviceWidth: number }, compileContext?: ObjectWithCompileContext) => mx(
|
||||
this.ERROR_TouchLayerRequiresId, compileContext,
|
||||
`Layer for touch form with minDeviceWidth=${def(o.minDeviceWidth)} requires an "id" attribute`, `
|
||||
Touch layers must have an \`id\` attribute, but should not have a \`modifiers\`
|
||||
attribute, and conversely, hardware layers must have a \`modifiers\` attribute
|
||||
and should not have an \`id\` attribute.
|
||||
`);
|
||||
|
||||
static HINT_TouchLayerHasModifiers = SevHint | 0x0032;
|
||||
static Hint_TouchLayerHasModifiers = (o: { minDeviceWidth: number, id: string }, compileContext?: ObjectWithCompileContext) => mx(
|
||||
this.HINT_TouchLayerHasModifiers, compileContext,
|
||||
`Touch layer with id "${def(o.id)}" for touch form with minDeviceWidth=${def(o.minDeviceWidth)} should not have a "modifiers" attribute`, `
|
||||
Touch layers must have an \`id\` attribute, but should not have a \`modifiers\`
|
||||
attribute, and conversely, hardware layers must have a \`modifiers\` attribute
|
||||
and should not have an \`id\` attribute.
|
||||
`);
|
||||
|
||||
static HINT_HardwareLayerHasId = SevHint | 0x0033;
|
||||
static Hint_HardwareLayerHasId = (o: { formId: string, id: string }, compileContext?: ObjectWithCompileContext) => mx(
|
||||
this.HINT_HardwareLayerHasId, compileContext,
|
||||
`Layer for hardware form "${def(o.formId)}" should not have an "id" attribute (currently "${def(o.id)}")`, `
|
||||
Touch layers must have an \`id\` attribute, but should not have a \`modifiers\`
|
||||
attribute, and conversely, hardware layers must have a \`modifiers\` attribute
|
||||
and should not have an \`id\` attribute.
|
||||
`);
|
||||
|
||||
static ERROR_HardwareLayerRequiresModifiers = SevError | 0x0034;
|
||||
static Error_HardwareLayerRequiresModifiers = (o: { formId: string }, compileContext?: ObjectWithCompileContext) => mx(
|
||||
this.ERROR_HardwareLayerRequiresModifiers, compileContext,
|
||||
`Layers for hardware form "${def(o.formId)}" require a "modifiers" attribute`, `
|
||||
Touch layers must have an \`id\` attribute, but should not have a \`modifiers\`
|
||||
attribute, and conversely, hardware layers must have a \`modifiers\` attribute
|
||||
and should not have an \`id\` attribute.
|
||||
`);
|
||||
|
||||
static HINT_MultipleTouchFormsWithoutMinDeviceWidth = SevHint | 0x0035;
|
||||
static Hint_MultipleTouchFormsWithoutMinDeviceWidth = (compileContext?: ObjectWithCompileContext) => mx(
|
||||
this.HINT_MultipleTouchFormsWithoutMinDeviceWidth, compileContext,
|
||||
`When multiple touch forms are present, 'minDeviceWidth' is required to differentiate them`, `
|
||||
Touch forms are differentiated by their minimum device width, so when there is
|
||||
more than one, at most one form may omit the \`minDeviceWidth\` attribute.
|
||||
`);
|
||||
|
||||
//
|
||||
// Transform syntax errors begin at ...F00 (SevErrorTransform)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
import { ModifierKeyConstants, KMXPlus, VisualKeyboard } from "@keymanapp/common-types";
|
||||
import { CompilerCallbacks } from "@keymanapp/developer-utils";
|
||||
import { LdmlCompilerMessages } from "./ldml-compiler-messages.js";
|
||||
import { modifiersToString } from "../util/util.js";
|
||||
|
||||
// This is a partial polyfill for findLast, so not polluting Array.prototype
|
||||
// https://medium.com/@stheodorejohn/findlast-method-polyfill-in-javascript-bridging-browser-gaps-c3baf6aabae1
|
||||
|
|
@ -93,7 +94,7 @@ export class LdmlKeyboardVisualKeyboardCompiler {
|
|||
layer: KMXPlus.LayrEntry,
|
||||
hardware: string,
|
||||
) {
|
||||
const layerId = layer.id.value;
|
||||
const layerId = layer.id.value ?? modifiersToString(layer.mod); // used only for reference in error messages
|
||||
|
||||
hardware = 'us'; // TODO-LDML: US Only. We need to clean this up for other hardware forms
|
||||
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ export function kmxToXml(kmx: KMXPlus.KMXPlusFile): string {
|
|||
return {
|
||||
layers: layr.lists.map(({ hardware, minDeviceWidth, layers }) => ({
|
||||
...stringToAttr('formId', hardware),
|
||||
...numberToAttr('minDeviceWidth', minDeviceWidth),
|
||||
...numberToAttr('minDeviceWidth', minDeviceWidth === 0 ? undefined : minDeviceWidth),
|
||||
layer: layers.map(({ id, mod, rows }) => ({
|
||||
...stringToAttr('id', id),
|
||||
...asAttr('modifiers', modToString(mod)),
|
||||
|
|
|
|||
|
|
@ -164,6 +164,15 @@ export function translateLayerAttrToModifier(layer: LDMLKeyboard.LKLayer) : numb
|
|||
return modifiers.split(',').map(m => translateModifierSubsetToLayer(m)).sort();
|
||||
}
|
||||
|
||||
export function modifiersToString(modifiers: number) : string {
|
||||
if (!modifiers) return 'none';
|
||||
const result: string[] = [];
|
||||
for(const mod of constants.keys_mod_map) {
|
||||
if(mod[1] != constants.keys_mod_none && modifiers & mod[1]) result.push(mod[0]);
|
||||
}
|
||||
return result.join(' ');
|
||||
}
|
||||
|
||||
function translateModifierSubsetToLayer(modifiers: string) : number {
|
||||
// TODO-LDML: Default #11072
|
||||
if (modifiers) {
|
||||
|
|
@ -186,7 +195,7 @@ function translateModifierSubsetToLayer(modifiers: string) : number {
|
|||
* @returns true if valid
|
||||
*/
|
||||
export function validModifier(modifier?: string) : boolean {
|
||||
if (!modifier) return true; // valid to have no modifier, == none
|
||||
if (!modifier) return false; // hardware layer must have a modifier
|
||||
// TODO-LDML: enforce illegal combinations per spec.
|
||||
for (const sub of modifier.trim().split(',')) {
|
||||
for (const str of sub.trim().split(' ')) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<key id="hmaqtugha" output="ħ" longPressKeyIds="a e"/>
|
||||
<key id="that" output="ថា"/>
|
||||
</keys>
|
||||
<layers formId="us" minDeviceWidth="123">
|
||||
<layers formId="us">
|
||||
<layer modifiers="none">
|
||||
<row keys="hmaqtugha that"/>
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
#
|
||||
# cd developer/src/kmc
|
||||
# ./build.sh configure build # if needed
|
||||
# ./build.sh build-fixtures
|
||||
# ./build.sh fixtures-build
|
||||
#
|
||||
# This will compile both the .xml and the .txt to build/test/fixtures and also emit the
|
||||
# checksum for basic-xml.kmx so you can patch that into this file.
|
||||
|
|
@ -408,7 +408,7 @@ block(layr) # struct COMP_KMXPLUS_LAYR {
|
|||
index(strNull,strUs,2) # KMXPLUS_STR hardware = 'us'
|
||||
00 00 00 00 # KMX_DWORD layer;
|
||||
01 00 00 00 # count
|
||||
7B 00 00 00 # KMX_DWORD minDeviceWidth; // 123
|
||||
00 00 00 00 # KMX_DWORD minDeviceWidth; // 0
|
||||
# layers 0
|
||||
00 00 00 00 # KMXPLUS_STR id;
|
||||
00 00 00 00 # KMX_DWORD mod
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@
|
|||
<key id="that" output="ថា" />
|
||||
</keys>
|
||||
|
||||
<layers formId="us" minDeviceWidth="123">
|
||||
<!-- TODO-LDML: unit test for <layers formId="us" with <layer id="base" should be illegal -->
|
||||
<layers formId="us">
|
||||
<layer modifiers="none">
|
||||
<row keys="hmaqtugha that" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -30,11 +30,11 @@
|
|||
</layer>
|
||||
</layers>
|
||||
<layers formId="iso">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="Q W" />
|
||||
</layer>
|
||||
<layer id="shift">
|
||||
<layer modifiers="shift">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="q w" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="grave" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="Q W" />
|
||||
</layer>
|
||||
<layer id="shift">
|
||||
<layer modifiers="shift">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="q w" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base" modifiers="none">
|
||||
<layer modifiers="none">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="qqq www" />
|
||||
</layer>
|
||||
<layer id="shift" modifiers="shift">
|
||||
<layer modifiers="shift">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="QQQ WWW" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="iso">
|
||||
<layer id="base" modifiers="none">
|
||||
<layer modifiers="none">
|
||||
<row keys="grave 1 2 3 4 5 6 7 8 9 0 hyphen equal" />
|
||||
<row keys="q w e r t y u i o p open-square close-square" />
|
||||
<row keys="a s d f g h j k l semi-colon apos" />
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base" modifiers="none">
|
||||
<layer modifiers="none">
|
||||
<row keys="grave 1 2 3 4 5 6 7 8 9 0 hyphen equal" />
|
||||
<row keys="q w e r t y u i o p open-square close-square backslash" />
|
||||
<row keys="a s d f g h j k l semi-colon apos" />
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="snail interrobang" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="grave one two three four five six seven eight nine ten eleven minus equal" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="Q W" />
|
||||
<row keys="Q W" />
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<!-- beware: this is mapping `! -->
|
||||
<row keys="Q" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
</flick>
|
||||
</flicks>
|
||||
<layers formId="iso">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="Q W" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="foo" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="5" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="7" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="iso">
|
||||
<layer id="base" modifiers="none">
|
||||
<layer modifiers="none">
|
||||
<row keys="a" />
|
||||
</layer>
|
||||
<layer modifiers="altR, ctrl shift">
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
</flicks>
|
||||
|
||||
<layers formId="iso">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="ww" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -38,11 +38,11 @@
|
|||
</layer>
|
||||
</layers>
|
||||
<layers formId="iso">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="Q W" />
|
||||
</layer>
|
||||
<layer id="shift" modifiers="shift">
|
||||
<layer modifiers="shift">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="q w amarker" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -36,11 +36,11 @@
|
|||
</layer>
|
||||
</layers>
|
||||
<layers formId="iso">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="Q W" />
|
||||
</layer>
|
||||
<layer id="shift" modifiers="shift">
|
||||
<layer modifiers="shift">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="q w amarker" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="grave mistake" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
</displays>
|
||||
|
||||
<layers formId="iso">
|
||||
<layer id="base" modifiers="none">
|
||||
<layer modifiers="none">
|
||||
<row keys="mark-dotbelow mark-dotabove mark-dotabovebelow mark-dotbelowabove" />
|
||||
<row keys="base rebase"/>
|
||||
<row keys="space" />
|
||||
|
|
|
|||
22
developer/src/kmc-ldml/test/fixtures/sections/layr/error-bad-width-.xml
vendored
Normal file
22
developer/src/kmc-ldml/test/fixtures/sections/layr/error-bad-width-.xml
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<keyboard3 xmlns="https://schemas.unicode.org/cldr/45/keyboard3" locale="und" conformsTo="45">
|
||||
<info name="layr-error-custom-form"/>
|
||||
<keys>
|
||||
<key id="one" output="1" />
|
||||
<key id="two" output="2" />
|
||||
<key id="three" output="3" />
|
||||
</keys>
|
||||
<layers formId="iso">
|
||||
<layer modifiers="none">
|
||||
<row keys="one two three" />
|
||||
</layer>
|
||||
</layers>
|
||||
<layers formId="touch" minDeviceWidth=""> <!-- a blank value is not permitted (instead, omit the attribute entirely) -->
|
||||
<layer id="base">
|
||||
<row keys="one" />
|
||||
<row keys="two" />
|
||||
<row keys="three" />
|
||||
</layer>
|
||||
</layers>
|
||||
</keyboard3>
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
<key id="three" output="3" />
|
||||
</keys>
|
||||
<layers formId="iso">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="one two three" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<row keys="three" />
|
||||
</layer>
|
||||
</layers>
|
||||
<layers formId="touch" minDeviceWidth="0"> <!-- dup -->
|
||||
<layers formId="touch" minDeviceWidth="0"> <!-- width 0 not permitted -->
|
||||
<layer id="base">
|
||||
<row keys="one" />
|
||||
<row keys="two" />
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<key id="three" output="3" />
|
||||
</keys>
|
||||
<layers formId="iso">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="one two three" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<row keys="three" />
|
||||
</layer>
|
||||
</layers>
|
||||
<layers formId="touch" minDeviceWidth="1024"> <!-- dup -->
|
||||
<layers formId="touch" minDeviceWidth="1024"> <!-- max width 999mm -->
|
||||
<layer id="base">
|
||||
<row keys="one" />
|
||||
<row keys="two" />
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<key id="three" output="3" />
|
||||
</keys>
|
||||
<layers formId="iso">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="one two three" />
|
||||
</layer>
|
||||
</layers>
|
||||
<layers formId="touch" minDeviceWidth="1500">
|
||||
<layers formId="touch" minDeviceWidth="1500"> <!-- max width 999mm -->
|
||||
<layer id="base">
|
||||
<row keys="one" />
|
||||
<row keys="two" />
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<key id="three" output="3" />
|
||||
</keys>
|
||||
<layers formId="iso">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="one two three" />
|
||||
</layer>
|
||||
</layers>
|
||||
<layers formId="touch" minDeviceWidth="x">
|
||||
<layers formId="touch" minDeviceWidth="x"> <!-- not a number -->
|
||||
<layer id="base">
|
||||
<row keys="one" />
|
||||
<row keys="two" />
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
</form>
|
||||
</forms>
|
||||
<layers formId="us">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="one two three" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
</form>
|
||||
</forms>
|
||||
<layers formId="zzz">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="one two three" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<key id="three" output="3" />
|
||||
</keys>
|
||||
<layers formId="iso">
|
||||
<layer id="base">
|
||||
<layer modifiers="none">
|
||||
<row keys="one two three" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
22
developer/src/kmc-ldml/test/fixtures/sections/layr/error-hardware-layer-requires-modifiers.xml
vendored
Normal file
22
developer/src/kmc-ldml/test/fixtures/sections/layr/error-hardware-layer-requires-modifiers.xml
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<keyboard3 xmlns="https://schemas.unicode.org/cldr/45/keyboard3" locale="und" conformsTo="45">
|
||||
<info name="error-hardware-layer-requires-modifiers"/>
|
||||
<keys>
|
||||
<key id="one" output="1" />
|
||||
<key id="two" output="2" />
|
||||
<key id="three" output="3" />
|
||||
</keys>
|
||||
<layers formId="us">
|
||||
<layer> <!-- modifiers is missing -->
|
||||
<row keys="one two three" />
|
||||
</layer>
|
||||
</layers>
|
||||
<layers formId="touch" minDeviceWidth="120">
|
||||
<layer id="base">
|
||||
<row keys="one" />
|
||||
<row keys="two" />
|
||||
<row keys="three" />
|
||||
</layer>
|
||||
</layers>
|
||||
</keyboard3>
|
||||
22
developer/src/kmc-ldml/test/fixtures/sections/layr/error-touch-layer-requires-id.xml
vendored
Normal file
22
developer/src/kmc-ldml/test/fixtures/sections/layr/error-touch-layer-requires-id.xml
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<keyboard3 xmlns="https://schemas.unicode.org/cldr/45/keyboard3" locale="und" conformsTo="45">
|
||||
<info name="error-touch-layer-requires-id"/>
|
||||
<keys>
|
||||
<key id="one" output="1" />
|
||||
<key id="two" output="2" />
|
||||
<key id="three" output="3" />
|
||||
</keys>
|
||||
<layers formId="iso">
|
||||
<layer modifiers="none">
|
||||
<row keys="one two three" />
|
||||
</layer>
|
||||
</layers>
|
||||
<layers formId="touch" minDeviceWidth="120">
|
||||
<layer> <!-- id is missing -->
|
||||
<row keys="one" />
|
||||
<row keys="two" />
|
||||
<row keys="three" />
|
||||
</layer>
|
||||
</layers>
|
||||
</keyboard3>
|
||||
22
developer/src/kmc-ldml/test/fixtures/sections/layr/hint-hardware-layer-has-id.xml
vendored
Normal file
22
developer/src/kmc-ldml/test/fixtures/sections/layr/hint-hardware-layer-has-id.xml
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<keyboard3 xmlns="https://schemas.unicode.org/cldr/45/keyboard3" locale="und" conformsTo="45">
|
||||
<info name="hint-hardware-layer-has-id"/>
|
||||
<keys>
|
||||
<key id="one" output="1" />
|
||||
<key id="two" output="2" />
|
||||
<key id="three" output="3" />
|
||||
</keys>
|
||||
<layers formId="iso">
|
||||
<layer modifiers="none" id="base"> <!-- hardware form should not have id -->
|
||||
<row keys="one two three" />
|
||||
</layer>
|
||||
</layers>
|
||||
<layers formId="touch" minDeviceWidth="120">
|
||||
<layer id="base">
|
||||
<row keys="one" />
|
||||
<row keys="two" />
|
||||
<row keys="three" />
|
||||
</layer>
|
||||
</layers>
|
||||
</keyboard3>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<keyboard3 xmlns="https://schemas.unicode.org/cldr/45/keyboard3" locale="und" conformsTo="45">
|
||||
<info name="layr-hint-multiple-touch-forms-without-min-device-width"/>
|
||||
<keys>
|
||||
<key id="one" output="1" />
|
||||
<key id="two" output="2" />
|
||||
<key id="three" output="3" />
|
||||
</keys>
|
||||
<layers formId="iso">
|
||||
<layer modifiers="none">
|
||||
<row keys="one two three" />
|
||||
</layer>
|
||||
</layers>
|
||||
<layers formId="touch">
|
||||
<layer id="base">
|
||||
<row keys="one" />
|
||||
<row keys="two" />
|
||||
<row keys="three" />
|
||||
</layer>
|
||||
</layers>
|
||||
<layers formId="touch"> <!-- two forms without minDeviceWidth -->
|
||||
<layer id="base">
|
||||
<row keys="one" />
|
||||
<row keys="two" />
|
||||
<row keys="three" />
|
||||
</layer>
|
||||
</layers>
|
||||
</keyboard3>
|
||||
22
developer/src/kmc-ldml/test/fixtures/sections/layr/hint-touch-layer-has-modifiers.xml
vendored
Normal file
22
developer/src/kmc-ldml/test/fixtures/sections/layr/hint-touch-layer-has-modifiers.xml
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<keyboard3 xmlns="https://schemas.unicode.org/cldr/45/keyboard3" locale="und" conformsTo="45">
|
||||
<info name="hint-touch-layer-has-modifiers"/>
|
||||
<keys>
|
||||
<key id="one" output="1" />
|
||||
<key id="two" output="2" />
|
||||
<key id="three" output="3" />
|
||||
</keys>
|
||||
<layers formId="iso">
|
||||
<layer modifiers="none">
|
||||
<row keys="one two three" />
|
||||
</layer>
|
||||
</layers>
|
||||
<layers formId="touch" minDeviceWidth="120">
|
||||
<layer id="base" modifiers="shift"> <!-- touch layer has modifiers -->
|
||||
<row keys="one" />
|
||||
<row keys="two" />
|
||||
<row keys="three" />
|
||||
</layer>
|
||||
</layers>
|
||||
</keyboard3>
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
<keys />
|
||||
|
||||
<layers formId="us">
|
||||
<layer id="base" modifiers="altR-shift"> <!-- invalid, should be "altR shift" -->
|
||||
<layer modifiers="altR-shift"> <!-- invalid, should be "altR shift" -->
|
||||
<row keys="a b c" />
|
||||
</layer>
|
||||
</layers>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue