Merge pull request #10364 from keymanapp/feat/developer/10191-warn-on-gestures-downlevel

feat(developer): warn on gesture support for downlevel
This commit is contained in:
Marc Durdin 2024-01-15 12:34:37 +11:00 committed by GitHub
commit 779196e1fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 772 additions and 2 deletions

View file

@ -70,7 +70,7 @@ if builder_start_action test; then
copy_deps
tsc --build test/
npm run lint
readonly C8_THRESHOLD=70
readonly C8_THRESHOLD=74
c8 --reporter=lcov --reporter=text --lines $C8_THRESHOLD --statements $C8_THRESHOLD --branches $C8_THRESHOLD --functions $C8_THRESHOLD mocha
builder_echo warning "Coverage thresholds are currently $C8_THRESHOLD%, which is lower than ideal."
builder_echo warning "Please increase threshold in build.sh as test coverage improves."

View file

@ -5,7 +5,7 @@ import { kmnfile } from "./compiler-globals.js";
const Namespace = CompilerErrorNamespace.KmwCompiler;
// const SevInfo = CompilerErrorSeverity.Info | Namespace;
// const SevHint = CompilerErrorSeverity.Hint | Namespace;
// const SevWarn = CompilerErrorSeverity.Warn | Namespace;
const SevWarn = CompilerErrorSeverity.Warn | Namespace;
const SevError = CompilerErrorSeverity.Error | Namespace;
// const SevFatal = CompilerErrorSeverity.Fatal | Namespace;
@ -106,4 +106,8 @@ export class KmwCompilerMessages extends KmnCompilerMessages {
static Error_TouchLayoutFileDoesNotExist = (o:{filename:string}) => m(this.ERROR_TouchLayoutFileDoesNotExist,
`Touch layout file ${o.filename} does not exist`);
static ERROR_TouchLayoutFileDoesNotExist = SevError | 0x0004;
static Warn_TouchLayoutUsesUnsupportedGesturesDownlevel = (o:{keyId:string}) => m(this.WARN_TouchLayoutUsesUnsupportedGesturesDownlevel,
`The touch layout uses a flick or multi-tap gesture on key ${o.keyId}, which is only available on version 17.0+ of Keyman`);
static WARN_TouchLayoutUsesUnsupportedGesturesDownlevel = SevWarn | 0x0005;
};

View file

@ -237,6 +237,14 @@ export function ValidateLayoutFile(fk: KMX.KEYBOARD, FDebug: boolean, sLayoutFil
return null;
}
let hasWarnedOfGestureUseDownlevel = false;
const warnGesturesIfNeeded = function(keyId: string) {
if(!hasWarnedOfGestureUseDownlevel && !IsKeyboardVersion17OrLater()) {
hasWarnedOfGestureUseDownlevel = true;
callbacks.reportMessage(KmwCompilerMessages.Warn_TouchLayoutUsesUnsupportedGesturesDownlevel({keyId}));
}
}
let result: boolean = true;
let FTouchLayoutFont = ''; // I4872
let pid: keyof TouchLayout.TouchLayoutFile;
@ -267,6 +275,7 @@ export function ValidateLayoutFile(fk: KMX.KEYBOARD, FDebug: boolean, sLayoutFil
let direction: keyof TouchLayout.TouchLayoutFlick;
if(key.flick) {
for(direction in key.flick) {
warnGesturesIfNeeded(key.id);
result = CheckKey(pid, platform, layer, key.flick[direction].id, key.flick[direction].text,
key.flick[direction].nextlayer, key.flick[direction].sp, FRequiredKeys, FDictionary) && result;
}
@ -274,6 +283,7 @@ export function ValidateLayoutFile(fk: KMX.KEYBOARD, FDebug: boolean, sLayoutFil
if(key.multitap) {
for(let subkey of key.multitap) {
warnGesturesIfNeeded(key.id);
result = CheckKey(pid, platform, layer, subkey.id, subkey.text, subkey.nextlayer, subkey.sp, FRequiredKeys, FDictionary) && result;
}
}

View file

@ -0,0 +1,670 @@
{
"tablet": {
"font": "Tahoma",
"layer": [
{
"id": "default",
"row": [
{
"id": 1,
"key": [
{
"id": "K_Q",
"text": "q"
},
{
"id": "K_W",
"text": "w"
},
{
"id": "K_E",
"text": "e"
},
{
"id": "K_R",
"text": "r"
},
{
"id": "K_T",
"text": "t"
},
{
"id": "K_Y",
"text": "y"
},
{
"id": "K_U",
"text": "u"
},
{
"id": "K_I",
"text": "i"
},
{
"id": "K_O",
"text": "o"
},
{
"id": "K_P",
"text": "p"
}
]
},
{
"id": 2,
"key": [
{
"id": "K_A",
"text": "a",
"pad": 70,
"flick": {
"n": {
"text": "A",
"id": "U_0041"
}
}
},
{
"id": "K_S",
"text": "s"
},
{
"id": "K_D",
"text": "d"
},
{
"id": "K_F",
"text": "f"
},
{
"id": "K_G",
"text": "g"
},
{
"id": "K_H",
"text": "h"
},
{
"id": "K_J",
"text": "j"
},
{
"id": "K_K",
"text": "k"
},
{
"id": "K_L",
"text": "l"
},
{
"id": "T_new_88",
"width": 10,
"sp": 10
}
]
},
{
"id": 3,
"key": [
{
"id": "K_SHIFT",
"text": "*Shift*",
"width": 110,
"sp": 1,
"nextlayer": "shift"
},
{
"id": "K_Z",
"text": "z"
},
{
"id": "K_X",
"text": "x"
},
{
"id": "K_C",
"text": "c"
},
{
"id": "K_V",
"text": "v"
},
{
"id": "K_B",
"text": "b"
},
{
"id": "K_N",
"text": "n"
},
{
"id": "K_M",
"text": "m"
},
{
"id": "K_PERIOD",
"text": ".",
"sk": [
{
"text": ",",
"id": "K_COMMA"
},
{
"text": "!",
"id": "K_1",
"layer": "shift"
},
{
"text": "?",
"id": "K_SLASH",
"layer": "shift"
},
{
"text": "'",
"id": "K_QUOTE"
},
{
"text": "\"",
"id": "K_QUOTE",
"layer": "shift"
},
{
"text": "\\",
"id": "K_BKSLASH"
},
{
"text": ":",
"id": "K_COLON",
"layer": "shift"
},
{
"text": ";",
"id": "K_COLON"
}
]
},
{
"id": "K_BKSP",
"text": "*BkSp*",
"width": 90,
"sp": 1
}
]
},
{
"id": 4,
"key": [
{
"id": "K_NUMLOCK",
"text": "*123*",
"width": 140,
"sp": 1,
"nextlayer": "numeric"
},
{
"id": "K_LOPT",
"text": "*Menu*",
"width": 120,
"sp": 1
},
{
"id": "K_SPACE",
"text": "",
"width": 630,
"sp": 0
},
{
"id": "K_ENTER",
"text": "*Enter*",
"width": 140,
"sp": 1
}
]
}
]
},
{
"id": "shift",
"row": [
{
"id": 1,
"key": [
{
"id": "K_Q",
"text": "Q"
},
{
"id": "K_W",
"text": "W"
},
{
"id": "K_E",
"text": "E"
},
{
"id": "K_R",
"text": "R"
},
{
"id": "K_T",
"text": "T"
},
{
"id": "K_Y",
"text": "Y"
},
{
"id": "K_U",
"text": "U"
},
{
"id": "K_I",
"text": "I"
},
{
"id": "K_O",
"text": "O"
},
{
"id": "K_P",
"text": "P"
}
]
},
{
"id": 2,
"key": [
{
"id": "K_A",
"text": "A",
"pad": 70
},
{
"id": "K_S",
"text": "S"
},
{
"id": "K_D",
"text": "D"
},
{
"id": "K_F",
"text": "F"
},
{
"id": "K_G",
"text": "G"
},
{
"id": "K_H",
"text": "H"
},
{
"id": "K_J",
"text": "J"
},
{
"id": "K_K",
"text": "K"
},
{
"id": "K_L",
"text": "L"
},
{
"sp": 10,
"width": 10
}
]
},
{
"id": 3,
"key": [
{
"id": "K_SHIFT",
"text": "*Shift*",
"width": 110,
"sp": 2,
"nextlayer": "default"
},
{
"id": "K_Z",
"text": "Z"
},
{
"id": "K_X",
"text": "X"
},
{
"id": "K_C",
"text": "C"
},
{
"id": "K_V",
"text": "V"
},
{
"id": "K_B",
"text": "B"
},
{
"id": "K_N",
"text": "N"
},
{
"id": "K_M",
"text": "M"
},
{
"id": "K_PERIOD",
"text": ".",
"layer": "default",
"sk": [
{
"text": ",",
"id": "K_COMMA",
"layer": "default"
},
{
"text": "!",
"id": "K_1",
"layer": "shift"
},
{
"text": "?",
"id": "K_SLASH",
"layer": "shift"
},
{
"text": "'",
"id": "K_QUOTE",
"layer": "default"
},
{
"text": "\"",
"id": "K_QUOTE",
"layer": "shift"
},
{
"text": "\\",
"id": "K_BKSLASH",
"layer": "default"
},
{
"text": ":",
"id": "K_COLON",
"layer": "shift"
},
{
"text": ";",
"id": "K_COLON",
"layer": "default"
}
]
},
{
"id": "K_BKSP",
"text": "*BkSp*",
"width": 90,
"sp": 1
}
]
},
{
"id": 4,
"key": [
{
"id": "K_NUMLOCK",
"text": "*123*",
"width": 140,
"sp": 1,
"nextlayer": "numeric"
},
{
"id": "K_LOPT",
"text": "*Menu*",
"width": 120,
"sp": 1
},
{
"id": "K_SPACE",
"text": "",
"width": 630,
"sp": 0
},
{
"id": "K_ENTER",
"text": "*Enter*",
"width": 140,
"sp": 1
}
]
}
]
},
{
"id": "numeric",
"row": [
{
"id": 1,
"key": [
{
"id": "K_1",
"text": "1"
},
{
"id": "K_2",
"text": "2"
},
{
"id": "K_3",
"text": "3"
},
{
"id": "K_4",
"text": "4"
},
{
"id": "K_5",
"text": "5"
},
{
"id": "K_6",
"text": "6"
},
{
"id": "K_7",
"text": "7"
},
{
"id": "K_8",
"text": "8"
},
{
"id": "K_9",
"text": "9"
},
{
"id": "K_0",
"text": "0"
}
]
},
{
"id": 2,
"key": [
{
"id": "K_4",
"text": "$",
"layer": "shift",
"pad": 70
},
{
"id": "K_2",
"text": "@",
"layer": "shift"
},
{
"id": "K_3",
"text": "#",
"layer": "shift"
},
{
"id": "K_5",
"text": "%",
"layer": "shift"
},
{
"id": "K_7",
"text": "&",
"layer": "shift"
},
{
"id": "K_HYPHEN",
"text": "_",
"layer": "shift"
},
{
"id": "K_EQUAL",
"text": "=",
"layer": "default"
},
{
"id": "K_BKSLASH",
"text": "|",
"layer": "shift"
},
{
"id": "K_BKSLASH",
"text": "\\",
"layer": "default"
},
{
"text": "",
"width": 10,
"sp": 10
}
]
},
{
"id": 3,
"key": [
{
"id": "K_SHIFT",
"text": "*Shift*",
"width": 110,
"sp": 1
},
{
"id": "K_LBRKT",
"text": "[",
"sk": [
{
"id": "U_00AB",
"text": "«"
},
{
"id": "K_COMMA",
"text": "<",
"layer": "shift"
},
{
"id": "K_LBRKT",
"text": "{",
"layer": "shift"
}
]
},
{
"id": "K_9",
"text": "(",
"layer": "shift"
},
{
"id": "K_0",
"text": ")",
"layer": "shift"
},
{
"id": "K_RBRKT",
"text": "]",
"sk": [
{
"id": "U_00BB",
"text": "»"
},
{
"id": "K_PERIOD",
"text": ">",
"layer": "shift"
},
{
"id": "K_RBRKT",
"text": "}",
"layer": "shift"
}
]
},
{
"id": "K_EQUAL",
"text": "+",
"layer": "shift"
},
{
"id": "K_HYPHEN",
"text": "-",
"layer": "default"
},
{
"id": "K_8",
"text": "*",
"layer": "shift"
},
{
"id": "K_SLASH",
"text": "/",
"layer": "default"
},
{
"id": "K_BKSP",
"text": "*BkSp*",
"width": 90,
"sp": 1
}
]
},
{
"id": 4,
"key": [
{
"id": "K_LOWER",
"text": "*abc*",
"width": 140,
"sp": 1,
"nextlayer": "default"
},
{
"id": "K_LOPT",
"text": "*Menu*",
"width": 120,
"sp": 1
},
{
"id": "K_SPACE",
"text": "",
"width": 630,
"sp": 0
},
{
"id": "K_ENTER",
"text": "*Enter*",
"width": 140,
"sp": 1
}
]
}
]
}
]
}
}

View file

@ -0,0 +1,12 @@
c validate_gesture generated from template at 2024-01-11 15:15:16
c with name "validate_gesture"
store(&VERSION) '10.0'
store(&NAME) 'validate_gesture'
store(&COPYRIGHT) '©'
store(&KEYBOARDVERSION) '1.0'
store(&TARGETS) 'mobile'
store(&LAYOUTFILE) 'validate_gesture.keyman-touch-layout'
begin Unicode > use(main)
group(main) using keys

View file

@ -0,0 +1,74 @@
import 'mocha';
import { assert } from 'chai';
import { KmwCompilerMessages } from '../../src/kmw-compiler/kmw-compiler-messages.js';
import { TestCompilerCallbacks, verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers';
import { makePathToFixture } from '../helpers/index.js';
import { KmnCompiler } from '../../src/main.js';
import { CompilerErrorNamespace } from '@keymanapp/common-types';
describe('KmwCompilerMessages', function () {
const callbacks = new TestCompilerCallbacks();
it('should have a valid KmwCompilerMessages object', function() {
return verifyCompilerMessagesObject(KmwCompilerMessages, CompilerErrorNamespace.KmwCompiler);
});
//
// Message tests
//
async function testForMessage(context: Mocha.Context, fixture: string[], messageId?: number) {
context.timeout(10000);
callbacks.clear();
const compiler = new KmnCompiler();
assert(await compiler.init(callbacks, {saveDebug: true, shouldAddCompilerVersion: false}));
assert(compiler.verifyInitialized());
const kmnPath = makePathToFixture(...fixture);
// Note: throwing away compile results (just to memory)
await compiler.run(kmnPath, null);
if(messageId) {
assert.isTrue(callbacks.hasMessage(messageId), `messageId ${messageId.toString(16)} not generated, instead got: `+JSON.stringify(callbacks.messages,null,2));
assert.lengthOf(callbacks.messages, 1);
} else {
assert.lengthOf(callbacks.messages, 0, `messages should be empty, but instead got: `+JSON.stringify(callbacks.messages,null,2));
}
}
// WARN_TouchLayoutUsesUnsupportedGesturesDownlevel
it('should generate WARN_TouchLayoutUsesUnsupportedGesturesDownlevel if the touch layout has gestures but the keyboard is an old version', async function() {
await testForMessage(this, ['kmw', 'validate_gesture.kmn'], KmwCompilerMessages.WARN_TouchLayoutUsesUnsupportedGesturesDownlevel);
});
// TODO: other messages
// ERROR_NotAnyRequiresVersion14
// it('should generate ERROR_NotAnyRequiresVersion14 if ...', async function() {
// await testForMessage(this, ['kmw', '....kmn'], KmwCompilerMessages.ERROR_NotAnyRequiresVersion14);
// });
// ERROR_TouchLayoutIdentifierRequires15
// it('should generate ERROR_TouchLayoutIdentifierRequires15 if ...', async function() {
// await testForMessage(this, ['kmw', '....kmn'], KmwCompilerMessages.ERROR_TouchLayoutIdentifierRequires15);
// });
// ERROR_InvalidTouchLayoutFileFormat
// it('should generate ERROR_InvalidTouchLayoutFileFormat if ...', async function() {
// await testForMessage(this, ['kmw', '....kmn'], KmwCompilerMessages.ERROR_InvalidTouchLayoutFileFormat);
// });
// ERROR_TouchLayoutFileDoesNotExist
// it('should generate ERROR_TouchLayoutFileDoesNotExist if ...', async function() {
// await testForMessage(this, ['kmw', '....kmn'], KmwCompilerMessages.ERROR_TouchLayoutFileDoesNotExist);
// });
});