Merge remote-tracking branch 'upstream/master' into feat/developer/10622-xml-parse-line-numbers-epic-ldml
2
.github/workflows/deb-packaging.README.md
vendored
|
|
@ -1,4 +1,4 @@
|
|||
# Debian Packaging GitHub Action
|
||||
# Ubuntu Packaging GitHub Action
|
||||
|
||||
You can manually trigger a deb-packaging action on GitHub, e.g. to test changes:
|
||||
|
||||
|
|
|
|||
15
.github/workflows/deb-packaging.yml
vendored
|
|
@ -1,3 +1,14 @@
|
|||
#
|
||||
# This workflow builds the source package for Keyman for Linux, and then builds
|
||||
# the binary packages for the released versions of Ubuntu. It also signs the
|
||||
# packages. Released Keyman versions get uploaded to the LLSO repository.
|
||||
# It is triggered by a repository_dispatch event with the
|
||||
# types `deb-release-packaging:*` and `deb-pr-packaging:*`.
|
||||
#
|
||||
# We use the filename `deb-packaging.yml` even though we only do Ubuntu
|
||||
# packaging here, because we're packaging the .deb format, and because it's shorter.
|
||||
#
|
||||
|
||||
name: "Ubuntu packaging"
|
||||
run-name: "Ubuntu packaging - ${{ github.event.client_payload.branch }} (branch ${{ github.event.client_payload.baseBranch }}), by @${{ github.event.client_payload.user }}, testbuild: ${{ github.event.client_payload.isTestBuild }}"
|
||||
on:
|
||||
|
|
@ -16,7 +27,7 @@ on:
|
|||
env:
|
||||
COLOR_GREEN: "\e[32m"
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
STATUS_CONTEXT: 'Debian Packaging'
|
||||
STATUS_CONTEXT: 'Ubuntu Packaging'
|
||||
DEBFULLNAME: 'Keyman GHA packager'
|
||||
DEBEMAIL: 'support@keyman.com'
|
||||
|
||||
|
|
@ -45,7 +56,7 @@ jobs:
|
|||
/repos/$GITHUB_REPOSITORY/statuses/${{ github.event.client_payload.buildSha }} \
|
||||
-f state='pending' \
|
||||
-f target_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
|
||||
-f description='Debian packaging started' \
|
||||
-f description='Ubuntu packaging started' \
|
||||
-f context="$STATUS_CONTEXT"
|
||||
|
||||
- name: Install devscripts
|
||||
|
|
|
|||
57
HISTORY.md
|
|
@ -1,5 +1,9 @@
|
|||
# Keyman Version History
|
||||
|
||||
## 19.0.19 alpha 2025-03-31
|
||||
|
||||
* chore: merge beta to master B18S4 (#13606)
|
||||
|
||||
## 19.0.18 alpha 2025-03-25
|
||||
|
||||
* chore(developer): improve ldml code coverage (#13516)
|
||||
|
|
@ -80,6 +84,59 @@
|
|||
* refactor(windows): rename `TKeymanMutex.MutexOwned` to `TakeOwnership` and add `ReleaseOwnership` (#13168)
|
||||
* chore: increment to alpha 19.0 (#13187)
|
||||
|
||||
## 18.0.222 beta 2025-03-28
|
||||
|
||||
* fix(android): Handle banner overrides independently when device is locked and unlocked (#13546)
|
||||
|
||||
## 18.0.221 beta 2025-03-27
|
||||
|
||||
* fix(windows): configuration update tab text changes (#13555)
|
||||
* chore(common): update language-subtag-registry to 2025-03-10 release (#13577)
|
||||
* docs(mac): update help documentation due to changes in Settings in macOS 13.0 Ventura (#13501)
|
||||
* fix(linux): improve emitting keystrokes (#13558)
|
||||
* fix(linux): improve key mapping to allow Core to reset context (#13372)
|
||||
|
||||
## 18.0.220 beta 2025-03-24
|
||||
|
||||
* fix(windows,mac): Update Spanish configuration string and mac strings (#13564)
|
||||
* chore(developer): use 'statement' instead of 'command' for messages about kmn statements (#13562)
|
||||
|
||||
## 18.0.219 beta 2025-03-21
|
||||
|
||||
* fix(windows): add exception handling for loading xml file in XSL Rendering (#13530)
|
||||
* refactor(linux): rename `initialize_queue` method (#13554)
|
||||
|
||||
## 18.0.218 beta 2025-03-19
|
||||
|
||||
* fix(developer): add validation of `nul` usage in output part of rule (#13532)
|
||||
* fix(web): model unload and load should use consistent, canEnable check (#13534)
|
||||
* feat(web): adds gesture 'traces' for warning/error logging (#13360)
|
||||
* fix(linux): fix building in Docker with different Ubuntu/Debian version (#13537)
|
||||
* chore(linux): replace deprecated setup.py (#13524)
|
||||
* chore(linux): rename "Debian packaging" to "Ubuntu packaging" (#13523)
|
||||
|
||||
## 18.0.217 beta 2025-03-18
|
||||
|
||||
* fix(windows): cherry pick hard code key cap (#13526)
|
||||
* fix(android,windows): Update Russian language strings to use Cyrillic script (#13527)
|
||||
* docs(web): add internal documentation for context state and keystroke processing (#13520)
|
||||
* docs(common): add link to schemas (#13521)
|
||||
* chore(linux): update URL for lcov package (#13535)
|
||||
* chore(linux): allow to build and use Debian docker image (#13536)
|
||||
|
||||
## 18.0.216 beta 2025-03-17
|
||||
|
||||
* change(android): re-order host page script load so Sentry can report script load failures (#13333)
|
||||
* chore(web): remove dead osk code (#13358)
|
||||
* fix(web): cancel held backspace on new key input (#13359)
|
||||
* fix(developer): handle non bmp numeric entities in xml reader (#13365)
|
||||
|
||||
## 18.0.215 beta 2025-03-14
|
||||
|
||||
* docs(common): Update Sentry links to keyman.sentry.io (#13491)
|
||||
* chore(android,windows): Update Crowdin for Spanish and Spanish (Latin America) (#13499)
|
||||
* chore(common): update crowdin strings for Italian (#13500)
|
||||
|
||||
## 18.0.214 beta 2025-03-13
|
||||
|
||||
* fix(mac): change text in privacy alert for macOS 13.0 and later (#13480)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
19.0.19
|
||||
19.0.20
|
||||
|
|
@ -159,9 +159,11 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
|
|||
Context appContext = getApplicationContext();
|
||||
// Temporarily disable predictions on certain fields (e.g. hidden password field or numeric)
|
||||
inputType = attribute.inputType;
|
||||
KMManager.setMayPredictOverride(inputType);
|
||||
if (KMManager.getMayPredictOverride()) {
|
||||
KMManager.setPredictionsSuspended(inputType, KeyboardType.KEYBOARD_TYPE_SYSTEM);
|
||||
if (KMManager.getPredictionsSuspended(KeyboardType.KEYBOARD_TYPE_SYSTEM)) {
|
||||
KMManager.setBannerOptions(false);
|
||||
// Set the system keyboard HTML banner
|
||||
BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM);
|
||||
} else if (KMManager.isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM)){
|
||||
// Check if predictions needs to be re-enabled per Settings preference
|
||||
Keyboard kbInfo = KMManager.getCurrentKeyboardInfo(appContext);
|
||||
|
|
|
|||
|
|
@ -244,6 +244,12 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
|
|||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (textView != null) {
|
||||
// Reset inAppPredictionsSuspendedForSensitiveInput flag
|
||||
KMManager.setPredictionsSuspended(textView.getInputType(), KeyboardType.KEYBOARD_TYPE_INAPP);
|
||||
}
|
||||
|
||||
KMManager.onResume();
|
||||
KMManager.hideSystemKeyboard();
|
||||
|
||||
|
|
|
|||
|
|
@ -126,11 +126,11 @@
|
|||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Restaurar valores predeterminados</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="longpress_delay_time" comment="Current longpress delay time (seconds)">Delay Time: %1$.1f seconds</string>
|
||||
<string name="longpress_delay_time" comment="Current longpress delay time (seconds)">Tiempo de retraso: %1$.1f segundos</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="ic_delay_time_up" comment="Make longpress delay longer">Delay time longer</string>
|
||||
<string name="ic_delay_time_up" comment="Make longpress delay longer">Tiempo de retraso más largo</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="ic_delay_time_down" comment="Make longpress delay shorter">Delay time shorter</string>
|
||||
<string name="ic_delay_time_down" comment="Make longpress delay shorter">Tiempo de retraso más corto</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="ic_longpress_delay_slider" comment="Adjust longpress delay time">Longpress delay time slider</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
<title>Keyman</title>
|
||||
<!-- DOMRect only exists as of Chrome 61... thus coming later than ES6. -->
|
||||
<script src="other-polyfills.js"></script>
|
||||
<script src="keymanweb-webview.js"></script>
|
||||
<script src="sentry.min.js"></script>
|
||||
<script src="keyman-sentry.js"></script>
|
||||
<script src="keymanweb-webview.js"></script>
|
||||
<script src="android-host.js"></script>
|
||||
<style type="text/css">
|
||||
body {background-color:#000000;}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ public class DisplayLanguages {
|
|||
new DisplayLanguageType("el", "Polytonic Greek"),
|
||||
new DisplayLanguageType("pt-PT", "Português de Portugal"),
|
||||
new DisplayLanguageType("ff-ZA", "Pulaar-Fulfulde"), // or Fulah
|
||||
new DisplayLanguageType("ru-RU", "Pyccĸий (Russian)"),
|
||||
new DisplayLanguageType("ru-RU", "Русский (Russian)"),
|
||||
new DisplayLanguageType("shu-latn", "Shuwa (Latin)"),
|
||||
new DisplayLanguageType("sv-SE", "svenska (Swedish)"),
|
||||
new DisplayLanguageType("vi-VN", "Tiếng Việt (Vietnamese)"),
|
||||
|
|
|
|||
|
|
@ -165,12 +165,12 @@ public final class KMKeyboardWebViewClient extends WebViewClient {
|
|||
// appContext instead of context?
|
||||
SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
|
||||
boolean modelPredictionPref = false;
|
||||
if (!KMManager.getMayPredictOverride() && KMManager.currentLexicalModel != null) {
|
||||
if (!KMManager.getPredictionsSuspended(keyboardType) && KMManager.currentLexicalModel != null) {
|
||||
modelPredictionPref = prefs.getInt(KMManager.getLanguageAutoCorrectionPreferenceKey(
|
||||
KMManager.currentLexicalModel.get(KMManager.KMKey_LanguageID)), KMManager.KMDefault_Suggestion)
|
||||
!= SuggestionType.SUGGESTIONS_DISABLED.toInt();
|
||||
}
|
||||
KMManager.setBannerOptions(modelPredictionPref);
|
||||
KMManager.setBannerOptions(modelPredictionPref, keyboardType);
|
||||
RelativeLayout.LayoutParams params = KMManager.getKeyboardLayoutParams();
|
||||
kmKeyboard.setLayoutParams(params);
|
||||
} else if (url.indexOf("suggestPopup") >= 0) {
|
||||
|
|
|
|||
|
|
@ -257,10 +257,13 @@ public final class KMManager {
|
|||
// haptic feedback disabled for hardware keystrokes
|
||||
private static boolean mayHaveHapticFeedback = false;
|
||||
|
||||
// Special override for when keyboard is entering a password text field.
|
||||
// When mayPredictOverride is true, the option {'mayPredict' = false} is set in the lm-layer
|
||||
// Special flags to temporarily disable predictions when editing a password field.
|
||||
// These are maintained independently for inapp keyboard and system keyboard.
|
||||
// When true, the suggestion banner passes the option {'mayPredict' = false} to KeymanWeb in the lm-layer
|
||||
// regardless what the Settings preference is.
|
||||
private static boolean mayPredictOverride = false;
|
||||
// API added Keyman 18.0
|
||||
private static boolean inAppPredictionsSuspendedForSensitiveInput = false;
|
||||
private static boolean systemPredictionsSuspendedForSensitiveInput = false;
|
||||
|
||||
// Determine how system keyboard handles ENTER key
|
||||
public static EnterModeType enterMode = EnterModeType.DEFAULT;
|
||||
|
|
@ -1409,25 +1412,49 @@ public final class KMManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets mayPredictOverride true if the InputType field is a hidden password text field
|
||||
* (either TYPE_TEXT_VARIATION_PASSWORD or TYPE_TEXT_VARIATION_WEB_PASSWORD
|
||||
* but not TYPE_TEXT_VARIATION_VISIBLE_PASSWORD)
|
||||
* Determine the predictions should be suspended for a given InputType field: hidden password text field
|
||||
* (but not TYPE_TEXT_VARIATION_VISIBLE_PASSWORD).
|
||||
* Also true for numeric text fields
|
||||
* @param inputType android.text.InputType
|
||||
* @return boolean
|
||||
*/
|
||||
public static void setMayPredictOverride(int inputType) {
|
||||
mayPredictOverride =
|
||||
((inputType == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD)) ||
|
||||
(inputType == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD)) ||
|
||||
isNumericField(inputType));
|
||||
private static boolean shouldSuspendPredictions(int inputType) {
|
||||
return ((inputType == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD)) ||
|
||||
(inputType == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD)) ||
|
||||
isNumericField(inputType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of mayPredictOverride
|
||||
* @return boolean
|
||||
* Sets PredictionsSuspended true if the given InputType field for the keyboard is a hidden password text field
|
||||
* (either TYPE_TEXT_VARIATION_PASSWORD or TYPE_TEXT_VARIATION_WEB_PASSWORD
|
||||
* but not TYPE_TEXT_VARIATION_VISIBLE_PASSWORD)
|
||||
* Also true for numeric text fields.
|
||||
* This is to temporarily disable suggestions in these text fields.
|
||||
* API added Keyman 18.0
|
||||
* @param inputType android.text.InputType
|
||||
* @param {KeyboardType} keyboard
|
||||
*/
|
||||
public static boolean getMayPredictOverride() {
|
||||
return mayPredictOverride;
|
||||
public static void setPredictionsSuspended(int inputType, KeyboardType keyboard) {
|
||||
if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP) {
|
||||
inAppPredictionsSuspendedForSensitiveInput = shouldSuspendPredictions(inputType);
|
||||
} else if (keyboard == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
systemPredictionsSuspendedForSensitiveInput = shouldSuspendPredictions(inputType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of PredictionsSuspendedForSensitiveInput based on KeyboardType.
|
||||
* API added in Keyman 18.0
|
||||
* @param {KeyboardType} keyboard
|
||||
* @return boolean of inAppPredictionsSuspendedForSensitiveInput or systemPredictionsSuspendedForSensitiveInput
|
||||
*/
|
||||
public static boolean getPredictionsSuspended(KeyboardType keyboard) {
|
||||
if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP) {
|
||||
return inAppPredictionsSuspendedForSensitiveInput;
|
||||
} else if (keyboard == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
return systemPredictionsSuspendedForSensitiveInput;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1556,20 +1583,23 @@ public final class KMManager {
|
|||
|
||||
// When entering password field, maySuggest should override to disabled
|
||||
SharedPreferences prefs = appContext.getSharedPreferences(appContext.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
|
||||
int maySuggest = mayPredictOverride ? SuggestionType.SUGGESTIONS_DISABLED.toInt() :
|
||||
prefs.getInt(getLanguageAutoCorrectionPreferenceKey(languageID), KMDefault_Suggestion);
|
||||
int suggestionPreference = prefs.getInt(getLanguageAutoCorrectionPreferenceKey(languageID), KMDefault_Suggestion);
|
||||
|
||||
RelativeLayout.LayoutParams params;
|
||||
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP) && !InAppKeyboard.shouldIgnoreTextChange() && modelFileExists) {
|
||||
params = getKeyboardLayoutParams();
|
||||
|
||||
// Do NOT re-layout here; it'll be triggered once the banner loads.
|
||||
InAppKeyboard.loadJavascript(KMString.format("enableSuggestions(%s, %d)", model, maySuggest));
|
||||
int inappMaySuggest = inAppPredictionsSuspendedForSensitiveInput ? SuggestionType.SUGGESTIONS_DISABLED.toInt() :
|
||||
suggestionPreference;
|
||||
InAppKeyboard.loadJavascript(KMString.format("enableSuggestions(%s, %d)", model, inappMaySuggest));
|
||||
}
|
||||
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM) && !SystemKeyboard.shouldIgnoreTextChange() && modelFileExists) {
|
||||
params = getKeyboardLayoutParams();
|
||||
|
||||
// Do NOT re-layout here; it'll be triggered once the banner loads.
|
||||
int maySuggest = systemPredictionsSuspendedForSensitiveInput ? SuggestionType.SUGGESTIONS_DISABLED.toInt() :
|
||||
suggestionPreference;
|
||||
SystemKeyboard.loadJavascript(KMString.format("enableSuggestions(%s, %d)", model, maySuggest));
|
||||
}
|
||||
return true;
|
||||
|
|
@ -1621,6 +1651,25 @@ public final class KMManager {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* setBannerOptions - Update KMW for inapp/system keyboard whether to generate predictions.
|
||||
* For now, also display banner
|
||||
* @param mayPredict - boolean whether KMW should generate predictions
|
||||
* @param {KeyboardType} keyboard
|
||||
* @return boolean - Success
|
||||
*/
|
||||
public static boolean setBannerOptions(boolean mayPredict, KeyboardType keyboard) {
|
||||
String url = KMString.format("setBannerOptions(%s)", mayPredict);
|
||||
if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP && InAppKeyboard != null) {
|
||||
InAppKeyboard.loadJavascript(url);
|
||||
}
|
||||
|
||||
if (keyboard == KeyboardType.KEYBOARD_TYPE_SYSTEM && SystemKeyboard != null) {
|
||||
SystemKeyboard.loadJavascript(url);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Update KeymanWeb banner type
|
||||
* @param {KeyboardType} keyboard
|
||||
|
|
|
|||
|
|
@ -82,6 +82,14 @@
|
|||
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Error fatal del teclado con %1$s:%2$s para el idioma %3$s. Cargando teclado predeterminado.</string>
|
||||
<!-- Context: Fatal keyboard error. -->
|
||||
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Error en el teclado %1$s:%2$s para el idioma %3$s.</string>
|
||||
<!-- Context: Alert Dialog body: System WebView not installed -->
|
||||
<string name="body_install_webview" comment="Notification when WebView not installed"> Keyman requiere que WebView esté instalado.\"</string>
|
||||
<!-- Context: Alert Dialog body: System WebView installed but not enabled -->
|
||||
<string name="body_enable_webview" comment="Notification when WebView installed but not enabled">Keyman requiere que WebView esté habilitado.\"</string>
|
||||
<!-- Context Button label: Install WebView -->
|
||||
<string name="label_install_webview" comment="Button label to install webview">Instalar WebView</string>
|
||||
<!-- Context Button label: Enable WebView -->
|
||||
<string name="label_enable_webview" comment="Button label to enable webview">Habilitar WebView</string>
|
||||
<!-- Context: Query for associated dictionary -->
|
||||
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Buscando diccionario asociado para descargar</string>
|
||||
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> No se pudo conectar al servidor de Keyman para buscar un diccionario asociado para descargar</string>
|
||||
|
|
@ -115,6 +123,8 @@
|
|||
<string name="failed_to_retrieve_file" comment="Failed to retrieve downloaded file">Error al recuperar el archivo descargado</string>
|
||||
<!-- Context: General Updates -->
|
||||
<string name="update_check_unavailable" comment="Error message when a Keyman server can't be reached">¡Error al acceder al servidor!</string>
|
||||
<!-- Context: Update-check failure notification -->
|
||||
<string name="update_check_download_manager_disabled" comment="Notification that a system service needed for updates is disabled">DownloadManager deshabilitado - No se pueden buscar actualizaciones</string>
|
||||
<!-- Context: General Updates -->
|
||||
<string name="update_check_current" comment="Notification that all resources are up to date">"¡Todos los recursos están actualizados!"</string>
|
||||
<!-- Context: General Updates -->
|
||||
|
|
|
|||
|
|
@ -82,6 +82,14 @@
|
|||
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Error de teclado fatal con %1$s:%2$s para %3$s idioma. Cargando teclado predeterminado.</string>
|
||||
<!-- Context: Fatal keyboard error. -->
|
||||
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Error en el teclado %1$s:%2$s para %3$s idioma.</string>
|
||||
<!-- Context: Alert Dialog body: System WebView not installed -->
|
||||
<string name="body_install_webview" comment="Notification when WebView not installed"> Keyman requires WebView to be installed.\"</string>
|
||||
<!-- Context: Alert Dialog body: System WebView installed but not enabled -->
|
||||
<string name="body_enable_webview" comment="Notification when WebView installed but not enabled"> Keyman requires WebView to be enabled.\"</string>
|
||||
<!-- Context Button label: Install WebView -->
|
||||
<string name="label_install_webview" comment="Button label to install webview">Install WebView</string>
|
||||
<!-- Context Button label: Enable WebView -->
|
||||
<string name="label_enable_webview" comment="Button label to enable webview">Enable WebView</string>
|
||||
<!-- Context: Query for associated dictionary -->
|
||||
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Comprobando si hay un diccionario asociado para descargar</string>
|
||||
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> No se puede conectar al servidor Keyman para comprobar si hay un diccionario asociado para descargar</string>
|
||||
|
|
@ -115,6 +123,8 @@
|
|||
<string name="failed_to_retrieve_file" comment="Failed to retrieve downloaded file">Falló al recuperar el archivo descargado</string>
|
||||
<!-- Context: General Updates -->
|
||||
<string name="update_check_unavailable" comment="Error message when a Keyman server can't be reached">¡Falló al acceder al servidor!</string>
|
||||
<!-- Context: Update-check failure notification -->
|
||||
<string name="update_check_download_manager_disabled" comment="Notification that a system service needed for updates is disabled">DownloadManager disabled - cannot check for updates</string>
|
||||
<!-- Context: General Updates -->
|
||||
<string name="update_check_current" comment="Notification that all resources are up to date">"¡Todos los recursos están actualizados!"</string>
|
||||
<!-- Context: General Updates -->
|
||||
|
|
|
|||
|
|
@ -82,6 +82,14 @@
|
|||
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Errore fatale della tastiera con %1$s:%2$s per %3$s lingua. Caricamento tastiera predefinita.</string>
|
||||
<!-- Context: Fatal keyboard error. -->
|
||||
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Errore nella tastiera %1$s:%2$s per %3$s lingua.</string>
|
||||
<!-- Context: Alert Dialog body: System WebView not installed -->
|
||||
<string name="body_install_webview" comment="Notification when WebView not installed"> Keyman richiede che WebView sia installato.\"</string>
|
||||
<!-- Context: Alert Dialog body: System WebView installed but not enabled -->
|
||||
<string name="body_enable_webview" comment="Notification when WebView installed but not enabled"> Keyman richiede che WebView sia abilitato.\"</string>
|
||||
<!-- Context Button label: Install WebView -->
|
||||
<string name="label_install_webview" comment="Button label to install webview">Installa WebView</string>
|
||||
<!-- Context Button label: Enable WebView -->
|
||||
<string name="label_enable_webview" comment="Button label to enable webview">Abilita WebView</string>
|
||||
<!-- Context: Query for associated dictionary -->
|
||||
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Controllo del dizionario associato da scaricare</string>
|
||||
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Non è possibile connettersi al server Keyman per verificare dizionari associati da scaricare</string>
|
||||
|
|
@ -115,6 +123,8 @@
|
|||
<string name="failed_to_retrieve_file" comment="Failed to retrieve downloaded file">Recupero del file scaricato non riuscito</string>
|
||||
<!-- Context: General Updates -->
|
||||
<string name="update_check_unavailable" comment="Error message when a Keyman server can't be reached">Accesso al server non riuscito!</string>
|
||||
<!-- Context: Update-check failure notification -->
|
||||
<string name="update_check_download_manager_disabled" comment="Notification that a system service needed for updates is disabled">DownloadManager disabilitato - impossibile controllare aggiornamenti</string>
|
||||
<!-- Context: General Updates -->
|
||||
<string name="update_check_current" comment="Notification that all resources are up to date">"Tutte le risorse sono aggiornate!"</string>
|
||||
<!-- Context: General Updates -->
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ title: KMManager.getMaySendCrashReport()
|
|||
---
|
||||
|
||||
## Summary
|
||||
The **getMaySendCrashReport()** method returns whether Keyman Engine is allowed to send crash reports over the network to sentry.keyman.com.
|
||||
The **getMaySendCrashReport()** method returns whether Keyman Engine is allowed to send crash reports over the network to Sentry.
|
||||
|
||||
## Syntax
|
||||
```java
|
||||
|
|
@ -11,7 +11,7 @@ KMManager.getMaySendCrashReport()
|
|||
```
|
||||
|
||||
### Returns
|
||||
Returns `true` if crash reports can be sent over the network to sentry.keyman.com, `false` otherwise.
|
||||
Returns `true` if crash reports can be sent over the network to Sentry, `false` otherwise.
|
||||
|
||||
## Description
|
||||
Use this method to check if Keyman Engine will be accessing the network to send crash reports.
|
||||
|
|
|
|||
29
android/docs/engine/KMManager/getPredictionsSuspended.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
title: KMManager.getPredictionsSuspended()
|
||||
---
|
||||
|
||||
## Summary
|
||||
The **getPredictionsSuspended()** method returns a flag that determines whether predictions are temporarily disabled because the currently selected text field is a hidden password text field or a numeric text field.
|
||||
|
||||
## Syntax
|
||||
```java
|
||||
KMManager.getPredictionsSuspended(KeyboardType keyboardType)
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
`keyboardType`
|
||||
: The keyboard type. `KEYBOARD_TYPE_INAPP` or
|
||||
`KEYBOARD_TYPE_SYSTEM`.
|
||||
|
||||
### Returns
|
||||
Returns `true` if predictions are temporarily disabled because the currently selected text field is a hidden password text field or a numeric text field. `false` otherwise.
|
||||
|
||||
## Description
|
||||
Use this method to check if predictions are temporarily disabled because of the type of currently selected text field. This value takes precedence over the user preference in the LanguageSettings menu.
|
||||
|
||||
## History
|
||||
Added syntax in Keyman Engine for Android 18.0.
|
||||
|
||||
## See also
|
||||
* [setPredictionsSuspended](setPredictionsSuspended)
|
||||
|
|
@ -147,11 +147,14 @@ The KMManager is the core class which provides most of the methods and constants
|
|||
: returns from stored preference the number of milliseconds to trigger a longpress gesture
|
||||
|
||||
[`getMaySendCrashReport()`](getMaySendCrashReport)
|
||||
: returns whether Keyman Engine is allowed to send crash reports over the network to sentry.keyman.com
|
||||
: returns whether Keyman Engine is allowed to send crash reports over the network to Sentry
|
||||
|
||||
[`getOrientation()`](getOrientation)
|
||||
: returns the device's current orientation (Portrait vs Landscape)
|
||||
|
||||
[`getPredictionsSuspended()`](getPredictionsSuspended)
|
||||
: returns whether predictions are temporarily disabled because the currently selected text field is a hidden password text field or a numeric text field.
|
||||
|
||||
[`getSpacebarText()`](getSpacebarText)
|
||||
: returns the current text display pattern for the spacebar
|
||||
|
||||
|
|
@ -255,7 +258,10 @@ The KMManager is the core class which provides most of the methods and constants
|
|||
: stores the longpress delay in milliseconds as a preference.
|
||||
|
||||
[`setMaySendCrashReport()`](setMaySendCrashReport)
|
||||
: sets whether Keyman Engine can send crash reports over the network to sentry.keyman.com
|
||||
: sets whether Keyman Engine can send crash reports over the network to Sentry
|
||||
|
||||
[`setPredictionsSuspended()`](setPredictionsSuspended)
|
||||
: sets whether predictions are temporarily disabled because the currently selected text field is a hidden password text field or a numeric text field.
|
||||
|
||||
[`setShouldAllowSetKeyboard()`](setShouldAllowSetKeyboard)
|
||||
: sets whether Keyman Engine allows setting a keyboard other than the default keyboard
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ title: KMManager.setMaySendCrashReport()
|
|||
|
||||
## Summary
|
||||
The **setMaySendCrashReport()** enables or disables whether Keyman Engine can send crash reports over the
|
||||
network to sentry.keyman.com.
|
||||
network to Sentry.
|
||||
|
||||
## Syntax
|
||||
```java
|
||||
|
|
|
|||
36
android/docs/engine/KMManager/setPredictionsSuspended.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
title: KMManager.setPredictionsSuspended()
|
||||
---
|
||||
|
||||
## Summary
|
||||
The **setPredictionsSuspended()** method sets a flag to temporarily disable predictions when the currently selected text field is a hidden password text field or a numeric text field.
|
||||
|
||||
## Syntax
|
||||
```java
|
||||
KMManager.setPredictionsSuspended(int inputType, KeyboardType keyboardType)
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
[`inputType`](https://developer.android.com/reference/android/text/InputType)
|
||||
: An integer defining the basic context type of text being edited. If the text is one of the following InputTypes, this methods returns `true`:
|
||||
|
||||
* TYPE_CLASS_TEXT
|
||||
* TYPE_TEXT_VARIATION_PASSWORD
|
||||
* TYPE_CLASS_TEXT
|
||||
* TYPE_TEXT_VARIATION_WEB_PASSWORD
|
||||
* TYPE_CLASS_NUMBER
|
||||
* TYPE_CLASS_PHONE
|
||||
|
||||
`keyboardType`
|
||||
: The keyboard type. `KEYBOARD_TYPE_INAPP` or
|
||||
`KEYBOARD_TYPE_SYSTEM`.
|
||||
|
||||
## Description
|
||||
Use this method to temporarily disable predictions because of the currently selected text field. This value takes precedence over the user preference in the LanguageSettings menu.
|
||||
|
||||
## History
|
||||
Added syntax in Keyman Engine for Android 18.0.
|
||||
|
||||
## See also
|
||||
* [getPredictionsSuspended](getPredictionsSuspended)
|
||||
|
|
@ -4,7 +4,7 @@ title: Data Privacy Policy
|
|||
|
||||
## Privacy Policy for Keyman
|
||||
|
||||
This application will send crash reporting information to https://sentry.keyman.com. No personally identifiable information or keyboard strokes are recorded or shared.
|
||||
This application will send crash reporting information to https://keyman.sentry.io. No personally identifiable information or keyboard strokes are recorded or shared.
|
||||
|
||||
You can disable crash reporting on the Keyman
|
||||
[settings menu](../basic/config/)
|
||||
|
|
|
|||
|
|
@ -59,4 +59,4 @@ When enabled, the Keyman keyboard will provide haptic feedback (vibrate) when th
|
|||
When enabled, the Keyman app will display the 'Get Started' screen on app startup.
|
||||
|
||||
## Allow sending crash reports over network
|
||||
When enabled, the Keyman app will send crash reporting information to https://sentry.keyman.com. No personally identifiable information or keyboard strokes are recorded.
|
||||
When enabled, the Keyman app will send crash reporting information to [Sentry](https://keyman.sentry.io). No personally identifiable information or keyboard strokes are recorded.
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
store(&NAME) '050 - deadkey cancelled by arrow'
|
||||
c Description: Tests that context gets reset if we encounter a right-arrow
|
||||
c after a deadkey (#12968)
|
||||
c keys: [SHIFT K_6][K_RIGHT][K_E][SHIFT K_6][K_LEFT][K_E][SHIFT K_6][K_UP][K_E][SHIFT K_6][K_DOWN][K_E]
|
||||
c expected: PASS.PASS.PASS.PASS.
|
||||
|
||||
store(&version) '10.0'
|
||||
|
||||
begin Unicode > use(Main)
|
||||
|
||||
group(Main) using keys
|
||||
|
||||
+ '^' > dk(1)
|
||||
dk(1) + 'e' > 'FAIL.'
|
||||
+ 'e' > 'PASS.'
|
||||
|
||||
|
|
@ -53,261 +53,265 @@ enum km_core_modifier_mask {
|
|||
};
|
||||
|
||||
// These are Windows API VKEYs, using Keyman VKEY names.
|
||||
// Underlying values from winuser.h (https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes)
|
||||
// Note, some codes are defined here for parity with winuser.h, but are
|
||||
// not available in .kmn language. These codes are marked as "internal"
|
||||
// by using `__` in the name: `KM_CORE_VKEY__<name>__`
|
||||
enum km_kpb_virtual_key {
|
||||
KM_CORE_VKEY__00,
|
||||
KM_CORE_VKEY_LBUTTON,
|
||||
KM_CORE_VKEY_RBUTTON,
|
||||
KM_CORE_VKEY_CANCEL,
|
||||
KM_CORE_VKEY_MBUTTON,
|
||||
KM_CORE_VKEY__05,
|
||||
KM_CORE_VKEY__06,
|
||||
KM_CORE_VKEY__07,
|
||||
KM_CORE_VKEY_BKSP,
|
||||
KM_CORE_VKEY_TAB,
|
||||
KM_CORE_VKEY__0A,
|
||||
KM_CORE_VKEY__0B,
|
||||
KM_CORE_VKEY_KP5,
|
||||
KM_CORE_VKEY_ENTER,
|
||||
KM_CORE_VKEY__0E,
|
||||
KM_CORE_VKEY__0F,
|
||||
KM_CORE_VKEY_SHIFT,
|
||||
KM_CORE_VKEY_CONTROL,
|
||||
KM_CORE_VKEY_ALT,
|
||||
KM_CORE_VKEY_PAUSE,
|
||||
KM_CORE_VKEY_CAPS,
|
||||
KM_CORE_VKEY__15,
|
||||
KM_CORE_VKEY__16,
|
||||
KM_CORE_VKEY__17,
|
||||
KM_CORE_VKEY__18,
|
||||
KM_CORE_VKEY__19,
|
||||
KM_CORE_VKEY__1A,
|
||||
KM_CORE_VKEY_ESC,
|
||||
KM_CORE_VKEY__1C,
|
||||
KM_CORE_VKEY__1D,
|
||||
KM_CORE_VKEY__1E,
|
||||
KM_CORE_VKEY__1F,
|
||||
KM_CORE_VKEY_SPACE,
|
||||
KM_CORE_VKEY_PGUP,
|
||||
KM_CORE_VKEY_PGDN,
|
||||
KM_CORE_VKEY_END,
|
||||
KM_CORE_VKEY_HOME,
|
||||
KM_CORE_VKEY_LEFT,
|
||||
KM_CORE_VKEY_UP,
|
||||
KM_CORE_VKEY_RIGHT,
|
||||
KM_CORE_VKEY_DOWN,
|
||||
KM_CORE_VKEY_SEL,
|
||||
KM_CORE_VKEY_PRINT,
|
||||
KM_CORE_VKEY_EXEC,
|
||||
KM_CORE_VKEY_PRTSCN,
|
||||
KM_CORE_VKEY_INS,
|
||||
KM_CORE_VKEY_DEL,
|
||||
KM_CORE_VKEY_HELP,
|
||||
KM_CORE_VKEY_0,
|
||||
KM_CORE_VKEY_1,
|
||||
KM_CORE_VKEY_2,
|
||||
KM_CORE_VKEY_3,
|
||||
KM_CORE_VKEY_4,
|
||||
KM_CORE_VKEY_5,
|
||||
KM_CORE_VKEY_6,
|
||||
KM_CORE_VKEY_7,
|
||||
KM_CORE_VKEY_8,
|
||||
KM_CORE_VKEY_9,
|
||||
KM_CORE_VKEY__3A,
|
||||
KM_CORE_VKEY__3B,
|
||||
KM_CORE_VKEY__3C,
|
||||
KM_CORE_VKEY__3D,
|
||||
KM_CORE_VKEY__3E,
|
||||
KM_CORE_VKEY__3F,
|
||||
KM_CORE_VKEY__40,
|
||||
KM_CORE_VKEY_A,
|
||||
KM_CORE_VKEY_B,
|
||||
KM_CORE_VKEY_C,
|
||||
KM_CORE_VKEY_D,
|
||||
KM_CORE_VKEY_E,
|
||||
KM_CORE_VKEY_F,
|
||||
KM_CORE_VKEY_G,
|
||||
KM_CORE_VKEY_H,
|
||||
KM_CORE_VKEY_I,
|
||||
KM_CORE_VKEY_J,
|
||||
KM_CORE_VKEY_K,
|
||||
KM_CORE_VKEY_L,
|
||||
KM_CORE_VKEY_M,
|
||||
KM_CORE_VKEY_N,
|
||||
KM_CORE_VKEY_O,
|
||||
KM_CORE_VKEY_P,
|
||||
KM_CORE_VKEY_Q,
|
||||
KM_CORE_VKEY_R,
|
||||
KM_CORE_VKEY_S,
|
||||
KM_CORE_VKEY_T,
|
||||
KM_CORE_VKEY_U,
|
||||
KM_CORE_VKEY_V,
|
||||
KM_CORE_VKEY_W,
|
||||
KM_CORE_VKEY_X,
|
||||
KM_CORE_VKEY_Y,
|
||||
KM_CORE_VKEY_Z,
|
||||
KM_CORE_VKEY__5B,
|
||||
KM_CORE_VKEY__5C,
|
||||
KM_CORE_VKEY__5D,
|
||||
KM_CORE_VKEY__5E,
|
||||
KM_CORE_VKEY__5F,
|
||||
KM_CORE_VKEY_NP0,
|
||||
KM_CORE_VKEY_NP1,
|
||||
KM_CORE_VKEY_NP2,
|
||||
KM_CORE_VKEY_NP3,
|
||||
KM_CORE_VKEY_NP4,
|
||||
KM_CORE_VKEY_NP5,
|
||||
KM_CORE_VKEY_NP6,
|
||||
KM_CORE_VKEY_NP7,
|
||||
KM_CORE_VKEY_NP8,
|
||||
KM_CORE_VKEY_NP9,
|
||||
KM_CORE_VKEY_NPSTAR,
|
||||
KM_CORE_VKEY_NPPLUS,
|
||||
KM_CORE_VKEY_SEPARATOR,
|
||||
KM_CORE_VKEY_NPMINUS,
|
||||
KM_CORE_VKEY_NPDOT,
|
||||
KM_CORE_VKEY_NPSLASH,
|
||||
KM_CORE_VKEY_F1,
|
||||
KM_CORE_VKEY_F2,
|
||||
KM_CORE_VKEY_F3,
|
||||
KM_CORE_VKEY_F4,
|
||||
KM_CORE_VKEY_F5,
|
||||
KM_CORE_VKEY_F6,
|
||||
KM_CORE_VKEY_F7,
|
||||
KM_CORE_VKEY_F8,
|
||||
KM_CORE_VKEY_F9,
|
||||
KM_CORE_VKEY_F10,
|
||||
KM_CORE_VKEY_F11,
|
||||
KM_CORE_VKEY_F12,
|
||||
KM_CORE_VKEY_F13,
|
||||
KM_CORE_VKEY_F14,
|
||||
KM_CORE_VKEY_F15,
|
||||
KM_CORE_VKEY_F16,
|
||||
KM_CORE_VKEY_F17,
|
||||
KM_CORE_VKEY_F18,
|
||||
KM_CORE_VKEY_F19,
|
||||
KM_CORE_VKEY_F20,
|
||||
KM_CORE_VKEY_F21,
|
||||
KM_CORE_VKEY_F22,
|
||||
KM_CORE_VKEY_F23,
|
||||
KM_CORE_VKEY_F24,
|
||||
KM_CORE_VKEY__88,
|
||||
KM_CORE_VKEY__89,
|
||||
KM_CORE_VKEY__8A,
|
||||
KM_CORE_VKEY__8B,
|
||||
KM_CORE_VKEY__8C,
|
||||
KM_CORE_VKEY__8D,
|
||||
KM_CORE_VKEY__8E,
|
||||
KM_CORE_VKEY__8F,
|
||||
KM_CORE_VKEY_NUMLOCK,
|
||||
KM_CORE_VKEY_SCROLL,
|
||||
KM_CORE_VKEY__92,
|
||||
KM_CORE_VKEY__93,
|
||||
KM_CORE_VKEY__94,
|
||||
KM_CORE_VKEY__95,
|
||||
KM_CORE_VKEY__96,
|
||||
KM_CORE_VKEY__97,
|
||||
KM_CORE_VKEY__98,
|
||||
KM_CORE_VKEY__99,
|
||||
KM_CORE_VKEY__9A,
|
||||
KM_CORE_VKEY__9B,
|
||||
KM_CORE_VKEY__9C,
|
||||
KM_CORE_VKEY__9D,
|
||||
KM_CORE_VKEY__9E,
|
||||
KM_CORE_VKEY__9F,
|
||||
KM_CORE_VKEY__A0,
|
||||
KM_CORE_VKEY__A1,
|
||||
KM_CORE_VKEY__A2,
|
||||
KM_CORE_VKEY__A3,
|
||||
KM_CORE_VKEY__A4,
|
||||
KM_CORE_VKEY__A5,
|
||||
KM_CORE_VKEY__A6,
|
||||
KM_CORE_VKEY__A7,
|
||||
KM_CORE_VKEY__A8,
|
||||
KM_CORE_VKEY__A9,
|
||||
KM_CORE_VKEY__AA,
|
||||
KM_CORE_VKEY__AB,
|
||||
KM_CORE_VKEY__AC,
|
||||
KM_CORE_VKEY__AD,
|
||||
KM_CORE_VKEY__AE,
|
||||
KM_CORE_VKEY__AF,
|
||||
KM_CORE_VKEY__B0,
|
||||
KM_CORE_VKEY__B1,
|
||||
KM_CORE_VKEY__B2,
|
||||
KM_CORE_VKEY__B3,
|
||||
KM_CORE_VKEY__B4,
|
||||
KM_CORE_VKEY__B5,
|
||||
KM_CORE_VKEY__B6,
|
||||
KM_CORE_VKEY__B7,
|
||||
KM_CORE_VKEY__B8,
|
||||
KM_CORE_VKEY__B9,
|
||||
KM_CORE_VKEY_COLON,
|
||||
KM_CORE_VKEY_EQUAL,
|
||||
KM_CORE_VKEY_COMMA,
|
||||
KM_CORE_VKEY_HYPHEN,
|
||||
KM_CORE_VKEY_PERIOD,
|
||||
KM_CORE_VKEY_SLASH,
|
||||
KM_CORE_VKEY_BKQUOTE,
|
||||
KM_CORE_VKEY__C1,
|
||||
KM_CORE_VKEY__C2,
|
||||
KM_CORE_VKEY__C3,
|
||||
KM_CORE_VKEY__C4,
|
||||
KM_CORE_VKEY__C5,
|
||||
KM_CORE_VKEY__C6,
|
||||
KM_CORE_VKEY__C7,
|
||||
KM_CORE_VKEY__C8,
|
||||
KM_CORE_VKEY__C9,
|
||||
KM_CORE_VKEY__CA,
|
||||
KM_CORE_VKEY__CB,
|
||||
KM_CORE_VKEY__CC,
|
||||
KM_CORE_VKEY__CD,
|
||||
KM_CORE_VKEY__CE,
|
||||
KM_CORE_VKEY__CF,
|
||||
KM_CORE_VKEY__D0,
|
||||
KM_CORE_VKEY__D1,
|
||||
KM_CORE_VKEY__D2,
|
||||
KM_CORE_VKEY__D3,
|
||||
KM_CORE_VKEY__D4,
|
||||
KM_CORE_VKEY__D5,
|
||||
KM_CORE_VKEY__D6,
|
||||
KM_CORE_VKEY__D7,
|
||||
KM_CORE_VKEY__D8,
|
||||
KM_CORE_VKEY__D9,
|
||||
KM_CORE_VKEY__DA,
|
||||
KM_CORE_VKEY_LBRKT,
|
||||
KM_CORE_VKEY_BKSLASH,
|
||||
KM_CORE_VKEY_RBRKT,
|
||||
KM_CORE_VKEY_QUOTE,
|
||||
KM_CORE_VKEY_oDF,
|
||||
KM_CORE_VKEY_oE0,
|
||||
KM_CORE_VKEY_oE1,
|
||||
KM_CORE_VKEY_oE2, // 102nd key on European layouts
|
||||
KM_CORE_VKEY_oE3,
|
||||
KM_CORE_VKEY_oE4,
|
||||
KM_CORE_VKEY__E5,
|
||||
KM_CORE_VKEY_oE6,
|
||||
KM_CORE_VKEY__E7,
|
||||
KM_CORE_VKEY__E8,
|
||||
KM_CORE_VKEY_oE9,
|
||||
KM_CORE_VKEY_oEA,
|
||||
KM_CORE_VKEY_oEB,
|
||||
KM_CORE_VKEY_oEC,
|
||||
KM_CORE_VKEY_oED,
|
||||
KM_CORE_VKEY_oEE,
|
||||
KM_CORE_VKEY_oEF,
|
||||
KM_CORE_VKEY_oF0,
|
||||
KM_CORE_VKEY_oF1,
|
||||
KM_CORE_VKEY_oF2,
|
||||
KM_CORE_VKEY_oF3,
|
||||
KM_CORE_VKEY_oF4,
|
||||
KM_CORE_VKEY_oF5,
|
||||
KM_CORE_VKEY__F6,
|
||||
KM_CORE_VKEY__F7,
|
||||
KM_CORE_VKEY__F8,
|
||||
KM_CORE_VKEY__F9,
|
||||
KM_CORE_VKEY__FA,
|
||||
KM_CORE_VKEY__FB,
|
||||
KM_CORE_VKEY__FC,
|
||||
KM_CORE_VKEY__FD,
|
||||
KM_CORE_VKEY__FE,
|
||||
KM_CORE_VKEY__FF,
|
||||
KM_CORE_VKEY_LBUTTON, // 0x01
|
||||
KM_CORE_VKEY_RBUTTON, // 0x02
|
||||
KM_CORE_VKEY_CANCEL, // 0x03
|
||||
KM_CORE_VKEY_MBUTTON, // 0x04
|
||||
KM_CORE_VKEY__XBUTTON1__, // 0x05
|
||||
KM_CORE_VKEY__XBUTTON2__, // 0x06
|
||||
KM_CORE_VKEY__07, // 0x07 - Reserved
|
||||
KM_CORE_VKEY_BKSP, // 0x08 - VK_BACK
|
||||
KM_CORE_VKEY_TAB, // 0x09
|
||||
KM_CORE_VKEY__0A, // 0x0a - Reserved
|
||||
KM_CORE_VKEY__0B, // 0x0b - Reserved
|
||||
KM_CORE_VKEY_KP5, // 0x0c - VK_CLEAR
|
||||
KM_CORE_VKEY_ENTER, // 0x0d - VK_RETURN
|
||||
KM_CORE_VKEY__0E, // 0x0e - Reserved
|
||||
KM_CORE_VKEY__0F, // 0x0f - Reserved
|
||||
KM_CORE_VKEY_SHIFT, // 0x10
|
||||
KM_CORE_VKEY_CONTROL, // 0x11
|
||||
KM_CORE_VKEY_ALT, // 0x12 - VK_MENU
|
||||
KM_CORE_VKEY_PAUSE, // 0x13
|
||||
KM_CORE_VKEY_CAPS, // 0x14 - VK_CAPITAL
|
||||
KM_CORE_VKEY__KANA__, // 0x15
|
||||
KM_CORE_VKEY__HANGUL__, // 0x16
|
||||
KM_CORE_VKEY__JUNJA__, // 0x17
|
||||
KM_CORE_VKEY__FINAL__, // 0x18
|
||||
KM_CORE_VKEY__HANJA__, // 0x19 - or VK_KANJI
|
||||
KM_CORE_VKEY__IMEOFF__, // 0x1a - VK_IME_OFF
|
||||
KM_CORE_VKEY_ESC, // 0x1b - VK_ESCAPE
|
||||
KM_CORE_VKEY__CONVERT__, // 0x1c
|
||||
KM_CORE_VKEY__NONCONVERT__, // 0x1d
|
||||
KM_CORE_VKEY__ACCEPT__, // 0x1e
|
||||
KM_CORE_VKEY__MODECHANGE__, // 0x1f
|
||||
KM_CORE_VKEY_SPACE, // 0x20
|
||||
KM_CORE_VKEY_PGUP, // 0x21 - VK_PRIOR
|
||||
KM_CORE_VKEY_PGDN, // 0x22 - VK_NEXT
|
||||
KM_CORE_VKEY_END, // 0x23
|
||||
KM_CORE_VKEY_HOME, // 0x24
|
||||
KM_CORE_VKEY_LEFT, // 0x25
|
||||
KM_CORE_VKEY_UP, // 0x26
|
||||
KM_CORE_VKEY_RIGHT, // 0x27
|
||||
KM_CORE_VKEY_DOWN, // 0x28
|
||||
KM_CORE_VKEY_SEL, // 0x29 - VK_SELECT
|
||||
KM_CORE_VKEY_PRINT, // 0x2a
|
||||
KM_CORE_VKEY_EXEC, // 0x2b - VK_EXECUTE
|
||||
KM_CORE_VKEY_PRTSCN, // 0x2c - VK_SNAPSHOT
|
||||
KM_CORE_VKEY_INS, // 0x2d - VK_INSERT
|
||||
KM_CORE_VKEY_DEL, // 0x2e - VK_DELETE
|
||||
KM_CORE_VKEY_HELP, // 0x2f
|
||||
KM_CORE_VKEY_0, // 0x30
|
||||
KM_CORE_VKEY_1, // 0x31
|
||||
KM_CORE_VKEY_2, // 0x32
|
||||
KM_CORE_VKEY_3, // 0x33
|
||||
KM_CORE_VKEY_4, // 0x34
|
||||
KM_CORE_VKEY_5, // 0x35
|
||||
KM_CORE_VKEY_6, // 0x36
|
||||
KM_CORE_VKEY_7, // 0x37
|
||||
KM_CORE_VKEY_8, // 0x38
|
||||
KM_CORE_VKEY_9, // 0x39
|
||||
KM_CORE_VKEY__3A, // 0x3a - undefined
|
||||
KM_CORE_VKEY__3B, // 0x3b - undefined
|
||||
KM_CORE_VKEY__3C, // 0x3c - undefined
|
||||
KM_CORE_VKEY__3D, // 0x3d - undefined
|
||||
KM_CORE_VKEY__3E, // 0x3e - undefined
|
||||
KM_CORE_VKEY__3F, // 0x3f - undefined
|
||||
KM_CORE_VKEY__40, // 0x40 - undefined
|
||||
KM_CORE_VKEY_A, // 0x41
|
||||
KM_CORE_VKEY_B, // 0x42
|
||||
KM_CORE_VKEY_C, // 0x43
|
||||
KM_CORE_VKEY_D, // 0x44
|
||||
KM_CORE_VKEY_E, // 0x45
|
||||
KM_CORE_VKEY_F, // 0x46
|
||||
KM_CORE_VKEY_G, // 0x47
|
||||
KM_CORE_VKEY_H, // 0x48
|
||||
KM_CORE_VKEY_I, // 0x49
|
||||
KM_CORE_VKEY_J, // 0x4a
|
||||
KM_CORE_VKEY_K, // 0x4b
|
||||
KM_CORE_VKEY_L, // 0x4c
|
||||
KM_CORE_VKEY_M, // 0x4d
|
||||
KM_CORE_VKEY_N, // 0x4e
|
||||
KM_CORE_VKEY_O, // 0x4f
|
||||
KM_CORE_VKEY_P, // 0x50
|
||||
KM_CORE_VKEY_Q, // 0x51
|
||||
KM_CORE_VKEY_R, // 0x52
|
||||
KM_CORE_VKEY_S, // 0x53
|
||||
KM_CORE_VKEY_T, // 0x54
|
||||
KM_CORE_VKEY_U, // 0x55
|
||||
KM_CORE_VKEY_V, // 0x56
|
||||
KM_CORE_VKEY_W, // 0x57
|
||||
KM_CORE_VKEY_X, // 0x58
|
||||
KM_CORE_VKEY_Y, // 0x59
|
||||
KM_CORE_VKEY_Z, // 0x5a
|
||||
KM_CORE_VKEY__LWIN__, // 0x5b
|
||||
KM_CORE_VKEY__RWIN__, // 0x5c
|
||||
KM_CORE_VKEY__APPS__, // 0x5d
|
||||
KM_CORE_VKEY__5E, // 0x5e - reserved
|
||||
KM_CORE_VKEY__SLEEP__, // 0x5f
|
||||
KM_CORE_VKEY_NP0, // 0x60 - VK_NUMPAD0
|
||||
KM_CORE_VKEY_NP1, // 0x61 - VK_NUMPAD1
|
||||
KM_CORE_VKEY_NP2, // 0x62 - VK_NUMPAD2
|
||||
KM_CORE_VKEY_NP3, // 0x63 - VK_NUMPAD3
|
||||
KM_CORE_VKEY_NP4, // 0x64 - VK_NUMPAD4
|
||||
KM_CORE_VKEY_NP5, // 0x65 - VK_NUMPAD5
|
||||
KM_CORE_VKEY_NP6, // 0x66 - VK_NUMPAD6
|
||||
KM_CORE_VKEY_NP7, // 0x67 - VK_NUMPAD7
|
||||
KM_CORE_VKEY_NP8, // 0x68 - VK_NUMPAD8
|
||||
KM_CORE_VKEY_NP9, // 0x69 - VK_NUMPAD9
|
||||
KM_CORE_VKEY_NPSTAR, // 0x6a - VK_MULTIPLY
|
||||
KM_CORE_VKEY_NPPLUS, // 0x6b - VK_ADD
|
||||
KM_CORE_VKEY_SEPARATOR, // 0x6c
|
||||
KM_CORE_VKEY_NPMINUS, // 0x6d - VK_SUBTRACT
|
||||
KM_CORE_VKEY_NPDOT, // 0x6e - VK_DECIMAL
|
||||
KM_CORE_VKEY_NPSLASH, // 0x6f - VK_DIVIDE
|
||||
KM_CORE_VKEY_F1, // 0x70
|
||||
KM_CORE_VKEY_F2, // 0x71
|
||||
KM_CORE_VKEY_F3, // 0x72
|
||||
KM_CORE_VKEY_F4, // 0x73
|
||||
KM_CORE_VKEY_F5, // 0x74
|
||||
KM_CORE_VKEY_F6, // 0x75
|
||||
KM_CORE_VKEY_F7, // 0x76
|
||||
KM_CORE_VKEY_F8, // 0x77
|
||||
KM_CORE_VKEY_F9, // 0x78
|
||||
KM_CORE_VKEY_F10, // 0x79
|
||||
KM_CORE_VKEY_F11, // 0x7a
|
||||
KM_CORE_VKEY_F12, // 0x7b
|
||||
KM_CORE_VKEY_F13, // 0x7c
|
||||
KM_CORE_VKEY_F14, // 0x7d
|
||||
KM_CORE_VKEY_F15, // 0x7e
|
||||
KM_CORE_VKEY_F16, // 0x7f
|
||||
KM_CORE_VKEY_F17, // 0x80
|
||||
KM_CORE_VKEY_F18, // 0x81
|
||||
KM_CORE_VKEY_F19, // 0x82
|
||||
KM_CORE_VKEY_F20, // 0x83
|
||||
KM_CORE_VKEY_F21, // 0x84
|
||||
KM_CORE_VKEY_F22, // 0x85
|
||||
KM_CORE_VKEY_F23, // 0x86
|
||||
KM_CORE_VKEY_F24, // 0x87
|
||||
KM_CORE_VKEY__88, // 0x88 - reserved
|
||||
KM_CORE_VKEY__89, // 0x89 - reserved
|
||||
KM_CORE_VKEY__8A, // 0x8a - reserved
|
||||
KM_CORE_VKEY__8B, // 0x8b - reserved
|
||||
KM_CORE_VKEY__8C, // 0x8c - reserved
|
||||
KM_CORE_VKEY__8D, // 0x8d - reserved
|
||||
KM_CORE_VKEY__8E, // 0x8e - reserved
|
||||
KM_CORE_VKEY__8F, // 0x8f - reserved
|
||||
KM_CORE_VKEY_NUMLOCK, // 0x90
|
||||
KM_CORE_VKEY_SCROLL, // 0x91
|
||||
KM_CORE_VKEY__92, // 0x92 - OEM specific
|
||||
KM_CORE_VKEY__93, // 0x93 - OEM specific
|
||||
KM_CORE_VKEY__94, // 0x94 - OEM specific
|
||||
KM_CORE_VKEY__95, // 0x95 - OEM specific
|
||||
KM_CORE_VKEY__96, // 0x96 - OEM specific
|
||||
KM_CORE_VKEY__97, // 0x97 - unassigned
|
||||
KM_CORE_VKEY__98, // 0x98 - unassigned
|
||||
KM_CORE_VKEY__99, // 0x99 - unassigned
|
||||
KM_CORE_VKEY__9A, // 0x9a - unassigned
|
||||
KM_CORE_VKEY__9B, // 0x9b - unassigned
|
||||
KM_CORE_VKEY__9C, // 0x9c - unassigned
|
||||
KM_CORE_VKEY__9D, // 0x9d - unassigned
|
||||
KM_CORE_VKEY__9E, // 0x9e - unassigned
|
||||
KM_CORE_VKEY__9F, // 0x9f - unassigned
|
||||
KM_CORE_VKEY__LSHIFT__, // 0xa0
|
||||
KM_CORE_VKEY__RSHIFT__, // 0xa1
|
||||
KM_CORE_VKEY__LCONTROL__, // 0xa2
|
||||
KM_CORE_VKEY__RCONTROL__, // 0xa3
|
||||
KM_CORE_VKEY__LMENU__, // 0xa4
|
||||
KM_CORE_VKEY__RMENU__, // 0xa5
|
||||
KM_CORE_VKEY__BROWSERBACK__, // 0xa6 - VK_BROWSER_BACK
|
||||
KM_CORE_VKEY__BROWSERFORWARD__, // 0xa7 - VK_BROWSER_FORWARD
|
||||
KM_CORE_VKEY__BROWSERREFRESH__, // 0xa8 - VK_BROWSER_REFRESH
|
||||
KM_CORE_VKEY__BROWSERSTOP__, // 0xa9 - VK_BROWSER_STOP
|
||||
KM_CORE_VKEY__BROWSERSEARCH__, // 0xaa - VK_BROWSER_SEARCH
|
||||
KM_CORE_VKEY_BROWSERFAVORITES, // 0xab - VK_BROWSER_FAVORITES
|
||||
KM_CORE_VKEY__BROWSERHOME__, // 0xac - VK_BROWSER_HOME
|
||||
KM_CORE_VKEY__VOLUMEMUTE__, // 0xad - VK_VOLUME_MUTE
|
||||
KM_CORE_VKEY__VOLUMEDOWN__, // 0xae - VK_VOLUME_DOWN
|
||||
KM_CORE_VKEY__VOLUMEUP__, // 0xaf - VK_VOLUME_UP
|
||||
KM_CORE_VKEY__MEDIANEXT__, // 0xb0 - VK_MEDIA_NEXT_TRACK
|
||||
KM_CORE_VKEY__MEDIAPREV__, // 0xb1 - VK_MEDIA_PREV_TRACK
|
||||
KM_CORE_VKEY__MEDIASTOP__, // 0xb2 - VK_MEDIA_STOP
|
||||
KM_CORE_VKEY__MEDIAPLAY__, // 0xb3 - VK_MEDIA_PLAY_PAUSE
|
||||
KM_CORE_VKEY__MAIL__, // 0xb4 - VK_LAUNCH_MAIL
|
||||
KM_CORE_VKEY__MEDIA__, // 0xb5 - VK_LAUNCH_MEDIA_SELECT
|
||||
KM_CORE_VKEY__APP1__, // 0xb6 - VK_LAUNCH_APP1
|
||||
KM_CORE_VKEY__APP2__, // 0xb7 - VK_LAUNCH_APP2
|
||||
KM_CORE_VKEY__B8, // 0xb8 - reserved
|
||||
KM_CORE_VKEY__B9, // 0xb9 - reserved
|
||||
KM_CORE_VKEY_COLON, // 0xba - VK_OEM_1
|
||||
KM_CORE_VKEY_EQUAL, // 0xbb - VK_OEM_PLUS
|
||||
KM_CORE_VKEY_COMMA, // 0xbc - VK_OEM_COMMA
|
||||
KM_CORE_VKEY_HYPHEN, // 0xbd - VK_OEM_MINUS
|
||||
KM_CORE_VKEY_PERIOD, // 0xbe - VK_OEM_PERIOD
|
||||
KM_CORE_VKEY_SLASH, // 0xbf - VK_OEM_2
|
||||
KM_CORE_VKEY_BKQUOTE, // 0xc0 - VK_OEM_3
|
||||
KM_CORE_VKEY__C1, // 0xc1 - reserved
|
||||
KM_CORE_VKEY__C2, // 0xc2 - reserved
|
||||
KM_CORE_VKEY__C3, // 0xc3 - reserved
|
||||
KM_CORE_VKEY__C4, // 0xc4 - reserved
|
||||
KM_CORE_VKEY__C5, // 0xc5 - reserved
|
||||
KM_CORE_VKEY__C6, // 0xc6 - reserved
|
||||
KM_CORE_VKEY__C7, // 0xc7 - reserved
|
||||
KM_CORE_VKEY__C8, // 0xc8 - reserved
|
||||
KM_CORE_VKEY__C9, // 0xc9 - reserved
|
||||
KM_CORE_VKEY__CA, // 0xca - reserved
|
||||
KM_CORE_VKEY__CB, // 0xcb - reserved
|
||||
KM_CORE_VKEY__CC, // 0xcc - reserved
|
||||
KM_CORE_VKEY__CD, // 0xcd - reserved
|
||||
KM_CORE_VKEY__CE, // 0xce - reserved
|
||||
KM_CORE_VKEY__CF, // 0xcf - reserved
|
||||
KM_CORE_VKEY__D0, // 0xd0 - reserved
|
||||
KM_CORE_VKEY__D1, // 0xd1 - reserved
|
||||
KM_CORE_VKEY__D2, // 0xd2 - reserved
|
||||
KM_CORE_VKEY__D3, // 0xd3 - reserved
|
||||
KM_CORE_VKEY__D4, // 0xd4 - reserved
|
||||
KM_CORE_VKEY__D5, // 0xd5 - reserved
|
||||
KM_CORE_VKEY__D6, // 0xd6 - reserved
|
||||
KM_CORE_VKEY__D7, // 0xd7 - reserved
|
||||
KM_CORE_VKEY__D8, // 0xd8 - reserved
|
||||
KM_CORE_VKEY__D9, // 0xd9 - reserved
|
||||
KM_CORE_VKEY__DA, // 0xda - reserved
|
||||
KM_CORE_VKEY_LBRKT, // 0xdb - VK_OEM_4
|
||||
KM_CORE_VKEY_BKSLASH, // 0xdc - VK_OEM_5
|
||||
KM_CORE_VKEY_RBRKT, // 0xdd - VK_OEM_6
|
||||
KM_CORE_VKEY_QUOTE, // 0xde - VK_OEM_7
|
||||
KM_CORE_VKEY_oDF, // 0xdf - VK_OEM_8
|
||||
KM_CORE_VKEY_oE0, // 0xe0 - reserved
|
||||
KM_CORE_VKEY_oE1, // 0xe1 - reserved
|
||||
KM_CORE_VKEY_oE2, // 0xe2 - VK_OEM_102 - 102nd key on European layouts
|
||||
KM_CORE_VKEY_oE3, // 0xe3 - OEM specific
|
||||
KM_CORE_VKEY_oE4, // 0xe4 - OEM specific
|
||||
KM_CORE_VKEY__PROCESSKEY__, // 0xe5
|
||||
KM_CORE_VKEY_oE6, // 0xe6 - OEM specific
|
||||
KM_CORE_VKEY__PACKET__, // 0xe7
|
||||
KM_CORE_VKEY__E8, // 0xe8 - unassigned
|
||||
KM_CORE_VKEY_oE9, // 0xe9 - OEM specific
|
||||
KM_CORE_VKEY_oEA, // 0xea - OEM specific
|
||||
KM_CORE_VKEY_oEB, // 0xeb - OEM specific
|
||||
KM_CORE_VKEY_oEC, // 0xec - OEM specific
|
||||
KM_CORE_VKEY_oED, // 0xed - OEM specific
|
||||
KM_CORE_VKEY_oEE, // 0xee - OEM specific
|
||||
KM_CORE_VKEY_oEF, // 0xef - OEM specific
|
||||
KM_CORE_VKEY_oF0, // 0xf0 - OEM specific
|
||||
KM_CORE_VKEY_oF1, // 0xf1 - OEM specific
|
||||
KM_CORE_VKEY_oF2, // 0xf2 - OEM specific
|
||||
KM_CORE_VKEY_oF3, // 0xf3 - OEM specific
|
||||
KM_CORE_VKEY_oF4, // 0xf4 - OEM specific
|
||||
KM_CORE_VKEY_oF5, // 0xf5 - OEM specific
|
||||
KM_CORE_VKEY__ATTN__, // 0xf6
|
||||
KM_CORE_VKEY__CRSEL__, // 0xf7
|
||||
KM_CORE_VKEY__EXSEL__, // 0xf8
|
||||
KM_CORE_VKEY__EREOF__, // 0xf9
|
||||
KM_CORE_VKEY__PLAY__, // 0xfa
|
||||
KM_CORE_VKEY__ZOOM__, // 0xfb
|
||||
KM_CORE_VKEY__NONAME__, // 0xfc
|
||||
KM_CORE_VKEY__PA1__, // 0xfd
|
||||
KM_CORE_VKEY__OEMCLEAR__, // 0xfe
|
||||
KM_CORE_VKEY__FF, // 0xff
|
||||
};
|
||||
|
|
|
|||
|
|
@ -275,8 +275,10 @@ namespace KmnCompilerMessages {
|
|||
ERROR_ContextExCannotReferenceIf = SevError | 0x0B1,
|
||||
ERROR_ContextExCannotReferenceNul = SevError | 0x0B2,
|
||||
|
||||
FATAL_BufferOverflow = SevFatal | 0x0C0
|
||||
// FATAL_Break = SevFatal | 0x0C1, unused
|
||||
ERROR_TextBeforeOrAfterNulInOutput = SevError | 0x0B3,
|
||||
|
||||
FATAL_BufferOverflow = SevFatal | 0x0C0
|
||||
// FATAL_Break = SevFatal | 0x0C1, unused
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,17 +24,30 @@ export type KeymanXMLType =
|
|||
|
||||
/** Bag of options, maximally one for each KeymanXMLType */
|
||||
type KeymanXMLParserOptionsBag = {
|
||||
[key in KeymanXMLType]?: X2jOptions
|
||||
[key in KeymanXMLType]?: X2jOptions;
|
||||
};
|
||||
|
||||
const PARSER_COMMON_OPTIONS: X2jOptions = {
|
||||
attributeNamePrefix: '$', // causes remapping into $: { … } objects
|
||||
htmlEntities: true,
|
||||
ignoreAttributes: false,
|
||||
ignorePiTags: true,
|
||||
numberParseOptions: { // TODO: query is this option really necessary?
|
||||
eNotation: null,
|
||||
hex: null,
|
||||
leadingZeros: null,
|
||||
skipLike: /(?:)/, // parse numbers as strings
|
||||
},
|
||||
textNodeName: '_',
|
||||
};
|
||||
|
||||
/** map of options for the XML parser */
|
||||
const PARSER_OPTIONS: KeymanXMLParserOptionsBag = {
|
||||
'keyboard3': {
|
||||
ignoreAttributes: false, // We'd like attributes, please
|
||||
attributeNamePrefix: '@__', // We'll use this to convert attributes to strings and subobjects to arrays, when empty.
|
||||
trimValues: false, // preserve spaces, but:
|
||||
htmlEntities: true,
|
||||
tagValueProcessor: (tagName: string, tagValue: string /*, jPath, hasAttributes, isLeafNode*/) => {
|
||||
ignoreAttributes: false, // We'd like attributes, please
|
||||
tagValueProcessor: (_tagName: string, tagValue: string /*, jPath, hasAttributes, isLeafNode*/) => {
|
||||
// since trimValues: false, we need to zap any element values that would be trimmed.
|
||||
// currently, the LDML spec doesn't have any element values, but this
|
||||
// future-proofs us a little in that element values are allowed, just trimmed.
|
||||
|
|
@ -42,60 +55,32 @@ const PARSER_OPTIONS: KeymanXMLParserOptionsBag = {
|
|||
return tagValue?.trim();
|
||||
},
|
||||
captureMetaData: true,
|
||||
trimValues: false, // preserve spaces, but see tagValueProcessor
|
||||
},
|
||||
'keyboardTest3': {
|
||||
ignorePiTags: true,
|
||||
attributeNamePrefix: '', // avoid @_
|
||||
htmlEntities: true,
|
||||
ignoreAttributes: false, // We'd like attributes, please
|
||||
attributeNamePrefix: '', // avoid @_
|
||||
ignorePiTags: true,
|
||||
preserveOrder: true, // Gives us a 'special' format
|
||||
},
|
||||
'kps': {
|
||||
ignorePiTags: true,
|
||||
ignoreAttributes: false,
|
||||
htmlEntities: true,
|
||||
attributeNamePrefix: '$', // causes remapping into $: { … } objects
|
||||
textNodeName: '_',
|
||||
numberParseOptions: {
|
||||
skipLike: /(?:)/, // parse numbers as strings
|
||||
hex: null,
|
||||
leadingZeros: null,
|
||||
eNotation: null,
|
||||
},
|
||||
...PARSER_COMMON_OPTIONS,
|
||||
},
|
||||
'kpj': {
|
||||
ignorePiTags: true,
|
||||
textNodeName: '_',
|
||||
htmlEntities: true,
|
||||
ignoreAttributes: false, // We'd like attributes, please
|
||||
...PARSER_COMMON_OPTIONS,
|
||||
attributeNamePrefix: '', // to avoid '@_' prefixes
|
||||
numberParseOptions: {
|
||||
skipLike: /(?:)/, // parse numbers as strings
|
||||
hex: null,
|
||||
leadingZeros: null,
|
||||
eNotation: null,
|
||||
},
|
||||
},
|
||||
'kvks': {
|
||||
ignorePiTags: true,
|
||||
textNodeName: '_',
|
||||
htmlEntities: true,
|
||||
ignoreAttributes: false, // We'd like attributes, please
|
||||
attributeNamePrefix: '$', // causes remapping into $: { … } objects
|
||||
numberParseOptions: {
|
||||
skipLike: /(?:)/, // parse numbers as strings
|
||||
hex: null,
|
||||
leadingZeros: null,
|
||||
eNotation: null,
|
||||
},
|
||||
trimValues: false, // preserve spaces, but:
|
||||
tagValueProcessor: (tagName: string, tagValue: string, jPath: string, hasAttributes: boolean, isLeafNode: boolean) : string | undefined => {
|
||||
...PARSER_COMMON_OPTIONS,
|
||||
tagValueProcessor: (_tagName: string, tagValue: string, _jPath: string, _hasAttributes: boolean, isLeafNode: boolean) : string | undefined => {
|
||||
if (!isLeafNode) {
|
||||
return tagValue?.trim(); // trimmed value
|
||||
} else {
|
||||
return null; // no change to leaf nodes
|
||||
}
|
||||
},
|
||||
trimValues: false, // preserve spaces
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -103,27 +88,23 @@ type KeymanXMLGeneratorOptionsBag = {
|
|||
[key in KeymanXMLType]?: XmlBuilderOptions
|
||||
};
|
||||
|
||||
const GENERATOR_COMMON_OPTIONS: XmlBuilderOptions = {
|
||||
attributeNamePrefix: '$',
|
||||
ignoreAttributes: false,
|
||||
format: true,
|
||||
textNodeName: '_',
|
||||
suppressEmptyNode: true,
|
||||
};
|
||||
|
||||
const GENERATOR_OPTIONS: KeymanXMLGeneratorOptionsBag = {
|
||||
kvks: {
|
||||
attributeNamePrefix: '$',
|
||||
ignoreAttributes: false,
|
||||
format: true,
|
||||
textNodeName: '_',
|
||||
suppressEmptyNode: true,
|
||||
...GENERATOR_COMMON_OPTIONS,
|
||||
},
|
||||
kpj: {
|
||||
attributeNamePrefix: '$',
|
||||
ignoreAttributes: false,
|
||||
format: true,
|
||||
textNodeName: '_',
|
||||
suppressEmptyNode: true,
|
||||
...GENERATOR_COMMON_OPTIONS,
|
||||
},
|
||||
kps: {
|
||||
attributeNamePrefix: '$',
|
||||
ignoreAttributes: false,
|
||||
format: true,
|
||||
textNodeName: '_',
|
||||
suppressEmptyNode: true,
|
||||
...GENERATOR_COMMON_OPTIONS,
|
||||
},
|
||||
keyboard3: {
|
||||
attributeNamePrefix: '$',
|
||||
|
|
|
|||
16
developer/src/common/web/utils/test/fixtures/kvks/hex_escape.kvks
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<visualkeyboard>
|
||||
<header>
|
||||
<version>10.0</version>
|
||||
<kbdname>hex_escape</kbdname>
|
||||
<flags/>
|
||||
</header>
|
||||
<encoding name="unicode" fontname="arial" fontsize="-12">
|
||||
<layer shift="">
|
||||
<key vkey="K_1">ሴ</key>
|
||||
<key vkey="K_2">Ā</key>
|
||||
<key vkey="K_3">𐀀</key>
|
||||
<key vkey="K_4">𒍅</key>
|
||||
</layer>
|
||||
</encoding>
|
||||
</visualkeyboard>
|
||||
10
developer/src/common/web/utils/test/fixtures/ldml-keyboard/numeric-id.xml
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<keyboard3 xmlns="https://schemas.unicode.org/cldr/45/keyboard3" locale="mt" conformsTo="46">
|
||||
<info author="srl295" indicator="🙀" layout="qwerty" name="TestKbd"/>
|
||||
|
||||
<keys>
|
||||
<key id="1" output="ħ" />
|
||||
<key id="2" output="ថា" />
|
||||
</keys>
|
||||
|
||||
</keyboard3>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
|
||||
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
|
||||
<keyboardTest3 conformsTo="techpreview">
|
||||
<!--
|
||||
Read by:
|
||||
|
|
|
|||
|
|
@ -41,6 +41,21 @@ describe('kvks-file-reader', function() {
|
|||
const reader = new KvksFileReader();
|
||||
assert.throws(() => reader.read(input), 'File appears to be a binary .kvk file');
|
||||
});
|
||||
|
||||
it('should read non-bmp hex escapes correctly', function() {
|
||||
const path = makePathToFixture('kvks', 'hex_escape.kvks');
|
||||
const input = fs.readFileSync(path);
|
||||
|
||||
const reader = new KvksFileReader();
|
||||
const kvks = reader.read(input);
|
||||
const invalidVkeys: string[] = [];
|
||||
const vk = reader.transform(kvks, invalidVkeys);
|
||||
assert.isEmpty(invalidVkeys);
|
||||
assert.equal(vk.keys[0].text, '\u{1234}');
|
||||
assert.equal(vk.keys[1].text, '\u{100}');
|
||||
assert.equal(vk.keys[2].text, String.fromCodePoint(0x10000));
|
||||
assert.equal(vk.keys[3].text, String.fromCodePoint(0x12345));
|
||||
});
|
||||
});
|
||||
|
||||
describe('kvks-file-writer', function() {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { constants } from '@keymanapp/ldml-keyboard-constants';
|
|||
import { assert } from 'chai';
|
||||
import 'mocha';
|
||||
import { testTestdataReaderCases } from '../helpers/reader-callback-test.js';
|
||||
import { LKTAnyAction } from '../../src/types/ldml-keyboard/ldml-keyboard-testdata-xml.js';
|
||||
import { LKTAnyAction } from '../types/ldml-keyboard/ldml-keyboard-testdata-xml.js';
|
||||
|
||||
describe('ldml keyboard xml reader tests', function () {
|
||||
this.slow(500); // 0.5 sec -- json schema validation takes a while
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import { LKKey, ImportStatus } from '../../src/types/ldml-keyboard/ldml-keyboard-xml.js';
|
||||
import 'mocha';
|
||||
import {assert} from 'chai';
|
||||
import { CommonTypesMessages } from '../../src/common-messages.js';
|
||||
import { testReaderCases } from '../helpers/reader-callback-test.js';
|
||||
import { Constants } from '@keymanapp/common-types';
|
||||
import { CommonTypesMessages } from '../../src/common-messages.js';
|
||||
import { LKKey, ImportStatus } from '../../src/types/ldml-keyboard/ldml-keyboard-xml.js';
|
||||
import { testReaderCases } from '../helpers/reader-callback-test.js';
|
||||
|
||||
import CLDRScanToVkey = Constants.CLDRScanToVkey;
|
||||
import CLDRScanToKeyMap = Constants.CLDRScanToKeyMap;
|
||||
|
|
@ -210,6 +210,25 @@ describe('ldml keyboard xml reader tests', function () {
|
|||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
subpath: 'numeric-id.xml',
|
||||
callback: (data, source, subpath, callbacks) => {
|
||||
assert.ok(source?.keyboard3?.keys);
|
||||
const k = pluckKeysFromKeybag(source?.keyboard3?.keys.key, ['1', '2']);
|
||||
assert.sameDeepOrderedMembers(k.map((entry) => {
|
||||
// Drop the Symbol members from the returned keys; assertions may expect their presence.
|
||||
return {
|
||||
id: entry.id,
|
||||
output: entry.output
|
||||
};
|
||||
}), [
|
||||
{id: '1', output: '1'}, //default import
|
||||
{id: '2', output: '2'}, //default import
|
||||
{id: '1', output: 'ħ'}, //override
|
||||
{id: '2', output: 'ថា'}, //override
|
||||
]);
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
@ -227,40 +227,40 @@ export class KmnCompilerMessages {
|
|||
static ERROR_InvalidToken = SevError | 0x00A;
|
||||
static Error_InvalidToken = () => m(this.ERROR_InvalidToken, `Invalid token found`);
|
||||
|
||||
// kmcmplib: static Error_InvalidBegin = () => m(this.ERROR_InvalidBegin, `Invalid 'begin' command`);
|
||||
// kmcmplib: static Error_InvalidBegin = () => m(this.ERROR_InvalidBegin, `Invalid 'begin' statement`);
|
||||
static ERROR_InvalidBegin = SevError | 0x00B;
|
||||
static Error_InvalidBegin = () => mw(this.ERROR_InvalidBegin,
|
||||
`A "begin unicode" statement is required to compile a KeymanWeb keyboard`);
|
||||
|
||||
static ERROR_InvalidName = SevError | 0x00C;
|
||||
static Error_InvalidName = () => m(this.ERROR_InvalidName, `Invalid 'name' command`);
|
||||
static Error_InvalidName = () => m(this.ERROR_InvalidName, `Invalid 'name' statement`);
|
||||
|
||||
static ERROR_InvalidVersion = SevError | 0x00D;
|
||||
static Error_InvalidVersion = () => m(this.ERROR_InvalidVersion, `Invalid 'version' command`);
|
||||
static Error_InvalidVersion = () => m(this.ERROR_InvalidVersion, `Invalid 'version' statement`);
|
||||
|
||||
static ERROR_InvalidLanguageLine = SevError | 0x00E;
|
||||
static Error_InvalidLanguageLine = () => m(this.ERROR_InvalidLanguageLine, `Invalid 'language' command`);
|
||||
static Error_InvalidLanguageLine = () => m(this.ERROR_InvalidLanguageLine, `Invalid 'language' statement`);
|
||||
|
||||
static ERROR_LayoutButNoLanguage = SevError | 0x00F;
|
||||
static Error_LayoutButNoLanguage = () => m(this.ERROR_LayoutButNoLanguage, `Layout command found but no language command`);
|
||||
static Error_LayoutButNoLanguage = () => m(this.ERROR_LayoutButNoLanguage, `Layout statement found but no language statement`);
|
||||
|
||||
static ERROR_InvalidLayoutLine = SevError | 0x010;
|
||||
static Error_InvalidLayoutLine = () => m(this.ERROR_InvalidLayoutLine, `Invalid 'layout' command`);
|
||||
static Error_InvalidLayoutLine = () => m(this.ERROR_InvalidLayoutLine, `Invalid 'layout' statement`);
|
||||
|
||||
static ERROR_NoVersionLine = SevError | 0x011;
|
||||
static Error_NoVersionLine = () => m(this.ERROR_NoVersionLine, `No version line found for file`);
|
||||
|
||||
static ERROR_InvalidGroupLine = SevError | 0x012;
|
||||
static Error_InvalidGroupLine = () => m(this.ERROR_InvalidGroupLine, `Invalid 'group' command`);
|
||||
static Error_InvalidGroupLine = () => m(this.ERROR_InvalidGroupLine, `Invalid 'group' statement`);
|
||||
|
||||
static ERROR_InvalidStoreLine = SevError | 0x013;
|
||||
static Error_InvalidStoreLine = () => m(this.ERROR_InvalidStoreLine, `Invalid 'store' command`);
|
||||
static Error_InvalidStoreLine = () => m(this.ERROR_InvalidStoreLine, `Invalid 'store' statement`);
|
||||
|
||||
static ERROR_InvalidCodeInKeyPartOfRule = SevError | 0x014;
|
||||
static Error_InvalidCodeInKeyPartOfRule = () => m(this.ERROR_InvalidCodeInKeyPartOfRule, `Invalid command or code found in key part of rule`);
|
||||
static Error_InvalidCodeInKeyPartOfRule = () => m(this.ERROR_InvalidCodeInKeyPartOfRule, `Invalid statement or code found in key part of rule`);
|
||||
|
||||
static ERROR_InvalidDeadkey = SevError | 0x015;
|
||||
static Error_InvalidDeadkey = () => m(this.ERROR_InvalidDeadkey, `Invalid 'deadkey' or 'dk' command`);
|
||||
static Error_InvalidDeadkey = () => m(this.ERROR_InvalidDeadkey, `Invalid 'deadkey' or 'dk' statement`);
|
||||
|
||||
static ERROR_InvalidValue = SevError | 0x016;
|
||||
static Error_InvalidValue = () => m(this.ERROR_InvalidValue, `Invalid value in extended string`);
|
||||
|
|
@ -269,7 +269,7 @@ export class KmnCompilerMessages {
|
|||
static Error_ZeroLengthString = () => m(this.ERROR_ZeroLengthString, `A string of zero characters was found`);
|
||||
|
||||
static ERROR_TooManyIndexToKeyRefs = SevError | 0x018;
|
||||
static Error_TooManyIndexToKeyRefs = () => m(this.ERROR_TooManyIndexToKeyRefs, `Too many index commands refering to key string`);
|
||||
static Error_TooManyIndexToKeyRefs = () => m(this.ERROR_TooManyIndexToKeyRefs, `Too many index statements refering to key string`);
|
||||
|
||||
static ERROR_UnterminatedString = SevError | 0x019;
|
||||
static Error_UnterminatedString = () => m(this.ERROR_UnterminatedString, `Unterminated string in line`);
|
||||
|
|
@ -278,34 +278,34 @@ export class KmnCompilerMessages {
|
|||
// static Error_StringInVirtualKeySection = () => m(this.ERROR_StringInVirtualKeySection, `extend string illegal in virtual key section`);
|
||||
|
||||
// static ERROR_AnyInVirtualKeySection = SevError | 0x01B; // no longer used, see #12612
|
||||
// static Error_AnyInVirtualKeySection = () => m(this.ERROR_AnyInVirtualKeySection, `'any' command is illegal in virtual key section`);
|
||||
// static Error_AnyInVirtualKeySection = () => m(this.ERROR_AnyInVirtualKeySection, `'any' statement is illegal in virtual key section`);
|
||||
|
||||
static ERROR_InvalidAny = SevError | 0x01C;
|
||||
static Error_InvalidAny = () => m(this.ERROR_InvalidAny, `Invalid 'any' command`);
|
||||
static Error_InvalidAny = () => m(this.ERROR_InvalidAny, `Invalid 'any' statement`);
|
||||
|
||||
static ERROR_StoreDoesNotExist = SevError | 0x01D;
|
||||
static Error_StoreDoesNotExist = () => m(this.ERROR_StoreDoesNotExist, `Store referenced does not exist`);
|
||||
|
||||
// static ERROR_BeepInVirtualKeySection = SevError | 0x01E; // no longer used, see #12612
|
||||
// static Error_BeepInVirtualKeySection = () => m(this.ERROR_BeepInVirtualKeySection, `'beep' command is illegal in virtual key section`);
|
||||
// static Error_BeepInVirtualKeySection = () => m(this.ERROR_BeepInVirtualKeySection, `'beep' statement is illegal in virtual key section`);
|
||||
|
||||
// static ERROR_IndexInVirtualKeySection = SevError | 0x01F; // no longer used, see #12612
|
||||
// static Error_IndexInVirtualKeySection = () => m(this.ERROR_IndexInVirtualKeySection, `'index' command is illegal in virtual key section`);
|
||||
// static Error_IndexInVirtualKeySection = () => m(this.ERROR_IndexInVirtualKeySection, `'index' statement is illegal in virtual key section`);
|
||||
|
||||
static ERROR_InvalidIndex = SevError | 0x020;
|
||||
static Error_InvalidIndex = () => m(this.ERROR_InvalidIndex, `Invalid 'index' command`);
|
||||
static Error_InvalidIndex = () => m(this.ERROR_InvalidIndex, `Invalid 'index' statement`);
|
||||
|
||||
// static ERROR_OutsInVirtualKeySection = SevError | 0x021; // no longer used, see #12612
|
||||
// static Error_OutsInVirtualKeySection = () => m(this.ERROR_OutsInVirtualKeySection, `'outs' command is illegal in virtual key section`);
|
||||
// static Error_OutsInVirtualKeySection = () => m(this.ERROR_OutsInVirtualKeySection, `'outs' statement is illegal in virtual key section`);
|
||||
|
||||
static ERROR_InvalidOuts = SevError | 0x022;
|
||||
static Error_InvalidOuts = () => m(this.ERROR_InvalidOuts, `Invalid 'outs' command`);
|
||||
static Error_InvalidOuts = () => m(this.ERROR_InvalidOuts, `Invalid 'outs' statement`);
|
||||
|
||||
// static ERROR_ContextInVirtualKeySection = SevError | 0x024; // no longer used, see #12612
|
||||
// static Error_ContextInVirtualKeySection = () => m(this.ERROR_ContextInVirtualKeySection, `'context' command is illegal in virtual key section`);
|
||||
// static Error_ContextInVirtualKeySection = () => m(this.ERROR_ContextInVirtualKeySection, `'context' statement is illegal in virtual key section`);
|
||||
|
||||
static ERROR_InvalidUse = SevError | 0x025;
|
||||
static Error_InvalidUse = () => m(this.ERROR_InvalidUse, `Invalid 'use' command`);
|
||||
static Error_InvalidUse = () => m(this.ERROR_InvalidUse, `Invalid 'use' statement`);
|
||||
|
||||
static ERROR_GroupDoesNotExist = SevError | 0x026;
|
||||
static Error_GroupDoesNotExist = () => m(this.ERROR_GroupDoesNotExist, `Group does not exist`);
|
||||
|
|
@ -314,7 +314,7 @@ export class KmnCompilerMessages {
|
|||
static Error_VirtualKeyNotAllowedHere = () => m(this.ERROR_VirtualKeyNotAllowedHere, `Virtual key is not allowed here`);
|
||||
|
||||
static ERROR_InvalidSwitch = SevError | 0x028;
|
||||
static Error_InvalidSwitch = () => m(this.ERROR_InvalidSwitch, `Invalid 'switch' command`);
|
||||
static Error_InvalidSwitch = () => m(this.ERROR_InvalidSwitch, `Invalid 'switch' statement`);
|
||||
|
||||
static ERROR_NoTokensFound = SevError | 0x029;
|
||||
static Error_NoTokensFound = () => m(this.ERROR_NoTokensFound, `No tokens found in line`);
|
||||
|
|
@ -326,19 +326,19 @@ export class KmnCompilerMessages {
|
|||
static Error_LineTooLong = () => m(this.ERROR_LineTooLong, `Line too long`);
|
||||
|
||||
static ERROR_InvalidCopyright = SevError | 0x02C;
|
||||
static Error_InvalidCopyright = () => m(this.ERROR_InvalidCopyright, `Invalid 'copyright' command`);
|
||||
static Error_InvalidCopyright = () => m(this.ERROR_InvalidCopyright, `Invalid 'copyright' statement`);
|
||||
|
||||
static ERROR_CodeInvalidInThisSection = SevError | 0x02D;
|
||||
static Error_CodeInvalidInThisSection = () => m(this.ERROR_CodeInvalidInThisSection, `This line is invalid in this section of the file`);
|
||||
|
||||
static ERROR_InvalidMessage = SevError | 0x02E;
|
||||
static Error_InvalidMessage = () => m(this.ERROR_InvalidMessage, `Invalid 'message' command`);
|
||||
static Error_InvalidMessage = () => m(this.ERROR_InvalidMessage, `Invalid 'message' statement`);
|
||||
|
||||
static ERROR_InvalidLanguageName = SevError | 0x02F;
|
||||
static Error_InvalidLanguageName = () => m(this.ERROR_InvalidLanguageName, `Invalid 'languagename' command`);
|
||||
static Error_InvalidLanguageName = () => m(this.ERROR_InvalidLanguageName, `Invalid 'languagename' statement`);
|
||||
|
||||
static ERROR_InvalidBitmapLine = SevError | 0x030;
|
||||
static Error_InvalidBitmapLine = () => m(this.ERROR_InvalidBitmapLine, `Invalid 'bitmaps' command`);
|
||||
static Error_InvalidBitmapLine = () => m(this.ERROR_InvalidBitmapLine, `Invalid 'bitmaps' statement`);
|
||||
|
||||
static ERROR_CannotReadBitmapFile = SevError | 0x031;
|
||||
static Error_CannotReadBitmapFile = () => m(this.ERROR_CannotReadBitmapFile, `Cannot open the bitmap or icon file for reading`);
|
||||
|
|
@ -353,13 +353,13 @@ export class KmnCompilerMessages {
|
|||
static Error_InvalidCharacter = () => m(this.ERROR_InvalidCharacter, `A character was found that is outside the valid Unicode range (U+0000 - U+10FFFF)`);
|
||||
|
||||
static ERROR_InvalidCall = SevError | 0x035;
|
||||
static Error_InvalidCall = () => m(this.ERROR_InvalidCall, `The 'call' command is invalid`);
|
||||
static Error_InvalidCall = () => m(this.ERROR_InvalidCall, `The 'call' statement is invalid`);
|
||||
|
||||
// static ERROR_CallInVirtualKeySection = SevError | 0x036; // no longer used, see #12612
|
||||
// static Error_CallInVirtualKeySection = () => m(this.ERROR_CallInVirtualKeySection, `'call' command is illegal in virtual key section`);
|
||||
// static Error_CallInVirtualKeySection = () => m(this.ERROR_CallInVirtualKeySection, `'call' statement is illegal in virtual key section`);
|
||||
|
||||
static ERROR_CodeInvalidInKeyStore = SevError | 0x037;
|
||||
static Error_CodeInvalidInKeyStore = () => m(this.ERROR_CodeInvalidInKeyStore, `The command is invalid inside a store that is used in a key part of the rule`);
|
||||
static Error_CodeInvalidInKeyStore = () => m(this.ERROR_CodeInvalidInKeyStore, `The statement is invalid inside a store that is used in a key part of the rule`);
|
||||
|
||||
static ERROR_CannotLoadIncludeFile = SevError | 0x038;
|
||||
static Error_CannotLoadIncludeFile = () => m(this.ERROR_CannotLoadIncludeFile, `Cannot load the included file: it is either invalid or does not exist`);
|
||||
|
|
@ -747,11 +747,30 @@ export class KmnCompilerMessages {
|
|||
);
|
||||
|
||||
static ERROR_ContextExCannotReferenceNul = SevError | 0x0B2;
|
||||
static Error_ContextExCannotReferenceNul = () => m(
|
||||
static Error_ContextExCannotReferenceNul = () => m(
|
||||
this.ERROR_ContextExCannotReferenceNul,
|
||||
`The offset in context() points to a non-character nul statement in the context`,
|
||||
);
|
||||
|
||||
static ERROR_TextBeforeOrAfterNulInOutput = SevError | 0x0B3;
|
||||
static Error_TextBeforeOrAfterNulInOutput = () => m(
|
||||
this.ERROR_TextBeforeOrAfterNulInOutput,
|
||||
`A rule with a nul statement in the output cannot also output text or text-emitting statements`,
|
||||
`If a rule has [\`nul\`](https://help.keyman.com/developer/language/reference/nul)
|
||||
in the output, it means that it deletes any context referenced on the left-hand
|
||||
side of the rule, and has no other output, so it does not make sense to have any
|
||||
content apart from the \`nul\` statement in the output.
|
||||
|
||||
The following statements can still be used with \`nul\` in the output:
|
||||
* [\`beep()\`](https://help.keyman.com/developer/language/reference/beep)
|
||||
* [\`call()\`](https://help.keyman.com/developer/language/reference/call)
|
||||
* [\`reset()\`](https://help.keyman.com/developer/language/reference/reset)
|
||||
* [\`return\`](https://help.keyman.com/developer/language/reference/return)
|
||||
* [\`save()\`](https://help.keyman.com/developer/language/reference/save)
|
||||
* [\`set()\`](https://help.keyman.com/developer/language/reference/set)
|
||||
* [\`use()\`](https://help.keyman.com/developer/language/reference/use)`
|
||||
);
|
||||
|
||||
static FATAL_BufferOverflow = SevFatal | 0x0C0;
|
||||
static Fatal_BufferOverflow = () => m(this.FATAL_BufferOverflow, `The compiler memory buffer overflowed`);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'ERROR_TextBeforeOrAfterNulInOutput'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_TextBeforeOrAfterNulInOutput after nul
|
||||
+ 'a' > nul 'a'
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
store(&NAME) 'ERROR_TextBeforeOrAfterNulInOutput'
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_TextBeforeOrAfterNulInOutput before nul
|
||||
+ 'b' > 'b' nul
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import 'mocha';
|
||||
import { assert } from 'chai';
|
||||
import { KmnCompilerMessageRanges, KmnCompilerMessages } from '../src/compiler/kmn-compiler-messages.js';
|
||||
import { TestCompilerCallbacks, verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers';
|
||||
import { makePathToFixture } from './helpers/index.js';
|
||||
import { KmnCompiler } from '../src/main.js';
|
||||
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 { makePathToFixture } from './helpers/index.js';
|
||||
|
||||
describe('KmnCompilerMessages', function () {
|
||||
const callbacks = new TestCompilerCallbacks();
|
||||
|
|
@ -216,4 +216,11 @@ describe('KmnCompilerMessages', function () {
|
|||
await testForMessage(this, ['invalid-keyboards', 'error_contextex_cannot_reference_nul.kmn'], KmnCompilerMessages.ERROR_ContextExCannotReferenceNul);
|
||||
});
|
||||
|
||||
// ERROR_TextBeforeOrAfterNulInOutput
|
||||
|
||||
it('should generate ERROR_TextBeforeOrAfterNulInOutput if text before or after nul in output', async function() {
|
||||
await testForMessage(this, ['invalid-keyboards', 'error_text_before_or_after_nul_in_output_before.kmn'], KmnCompilerMessages.ERROR_TextBeforeOrAfterNulInOutput);
|
||||
await testForMessage(this, ['invalid-keyboards', 'error_text_before_or_after_nul_in_output_after.kmn'], KmnCompilerMessages.ERROR_TextBeforeOrAfterNulInOutput);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -88,7 +88,7 @@ describe('KmwCompilerMessages', function () {
|
|||
|
||||
// ERROR_NotSupportedInKeymanWebOutput
|
||||
|
||||
it('should generate ERROR_NotSupportedInKeymanWebOutput if the command is not supported in output for KeymanWeb', async function() {
|
||||
it('should generate ERROR_NotSupportedInKeymanWebOutput if the statement is not supported in output for KeymanWeb', async function() {
|
||||
await testForMessage(this, ['kmw', 'error_not_supported_in_keyman_web_output.kmn'], KmwCompilerMessages.ERROR_NotSupportedInKeymanWebOutput);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -62,8 +62,18 @@ export class LdmlCompilerMessages {
|
|||
m(this.HINT_NoDisplayForMarker, `Key element with id "${def(o.id)}" has only marker output, but there is no matching display element by output or keyId. Keycap may be blank.`);
|
||||
|
||||
static ERROR_InvalidVersion = SevError | 0x000D;
|
||||
static Error_InvalidVersion = (o:{version: string}) =>
|
||||
m(this.ERROR_InvalidVersion, `Version number '${def(o.version)}' must be a semantic version format string.`);
|
||||
static Error_InvalidVersion = (o:{version: string}) => m(
|
||||
this.ERROR_InvalidVersion,
|
||||
`Version number '${def(o.version)}' must be a semantic version format string with 'major.minor.patch' components.`,
|
||||
`The version number in the LDML keyboard file must be a [semantic
|
||||
version](https://semver.org) (semver) string. This string has a format of three
|
||||
integers representing major, minor, and patch versions, separated by periods. In
|
||||
the LDML keyboard specification, the full semver format is permitted, including
|
||||
pre-release suffix strings, but the Keyman toolchain currently restricts the
|
||||
format to the three integer components.
|
||||
|
||||
Example: \`"1.12.3"\``
|
||||
);
|
||||
|
||||
static ERROR_MustBeAtLeastOneLayerElement = SevError | 0x000E;
|
||||
static Error_MustBeAtLeastOneLayerElement = () =>
|
||||
|
|
|
|||
|
|
@ -1556,6 +1556,71 @@ KMX_DWORD CheckVirtualKeysInOutput(PKMX_WCHAR output) {
|
|||
return STATUS_Success;
|
||||
}
|
||||
|
||||
const KMX_BOOL CODE__IS_TEXTUAL[] = {
|
||||
-1, // undefined 0x00
|
||||
TRUE, // CODE_ANY 0x01
|
||||
TRUE, // CODE_INDEX 0x02
|
||||
TRUE, // CODE_CONTEXT 0x03
|
||||
FALSE, // CODE_NUL 0x04
|
||||
FALSE, // CODE_USE 0x05 // may trigger text effects but indirectly
|
||||
FALSE, // CODE_RETURN 0x06
|
||||
FALSE, // CODE_BEEP 0x07
|
||||
TRUE, // CODE_DEADKEY 0x08
|
||||
-1, // unused 0x09
|
||||
TRUE, // CODE_EXTENDED 0x0A
|
||||
-1, // CODE_EXTENDEDEND 0x0B (unused)
|
||||
FALSE, // CODE_SWITCH 0x0C
|
||||
-1, // CODE_KEY 0x0D (never used)
|
||||
FALSE, // CODE_CLEARCONTEXT 0x0E
|
||||
FALSE, // CODE_CALL 0x0F // may trigger text effects but indirectly
|
||||
-1, // UC_SENTINEL_EXTENDEDEND 0x10 (not valid with UC_SENTINEL)
|
||||
TRUE, // CODE_CONTEXTEX 0x11
|
||||
TRUE, // CODE_NOTANY 0x12
|
||||
FALSE, // CODE_SETOPT 0x13
|
||||
FALSE, // CODE_IFOPT 0x14
|
||||
FALSE, // CODE_SAVEOPT 0x15
|
||||
FALSE, // CODE_RESETOPT 0x16
|
||||
FALSE, // CODE_IFSYSTEMSTORE 0x17
|
||||
FALSE // CODE_SETSYSTEMSTORE 0x18
|
||||
};
|
||||
|
||||
static_assert(sizeof(CODE__IS_TEXTUAL) / sizeof(CODE__IS_TEXTUAL[0]) == (CODE_LASTCODE + 1), "Size of array CODE__EMITS_TEXT not correct");
|
||||
|
||||
KMX_BOOL IsTextualCode(KMX_WORD code) {
|
||||
assert(code >= CODE_ANY && code <= CODE_LASTCODE);
|
||||
if(!(code >= CODE_ANY && code <= CODE_LASTCODE)) {
|
||||
return FALSE;
|
||||
}
|
||||
return CODE__IS_TEXTUAL[code];
|
||||
}
|
||||
|
||||
/**
|
||||
* Warn if output has text before or after a nul statement, which
|
||||
* doesn't make sense -- if nul is used, it indicates no output
|
||||
*/
|
||||
KMX_DWORD CheckNulUsageInOutput(PKMX_WCHAR output) {
|
||||
PKMX_WCHAR p;
|
||||
KMX_BOOL hasNul = FALSE, hasOther = FALSE;
|
||||
for (p = output; *p; p = incxstr(p)) {
|
||||
if (*p == UC_SENTINEL) {
|
||||
auto code = *(p+1);
|
||||
if(code == CODE_NUL) {
|
||||
hasNul = TRUE;
|
||||
} else if(IsTextualCode(code)) {
|
||||
hasOther = TRUE;
|
||||
}
|
||||
} else {
|
||||
hasOther = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(hasNul && hasOther) {
|
||||
return KmnCompilerMessages::ERROR_TextBeforeOrAfterNulInOutput;
|
||||
}
|
||||
|
||||
return STATUS_Success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds implicit `context` to start of output of rules for readonly groups
|
||||
*/
|
||||
|
|
@ -1694,6 +1759,10 @@ KMX_DWORD ProcessKeyLineImpl(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_BOOL IsUnico
|
|||
return msg;
|
||||
}
|
||||
|
||||
if ((msg = CheckNulUsageInOutput(pklOut)) != STATUS_Success) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (gp->fReadOnly) {
|
||||
// Ensure no output is made from the rule, and that
|
||||
// use() statements meet required readonly semantics
|
||||
|
|
|
|||
2
docs/build/sentry-cli.md
vendored
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Contact the Keyman team if you need access to sentry.keyman.com for development.
|
||||
You will also need to install [sentry-cli](https://docs.sentry.io/cli/installation/) for uploading Debug symbols.
|
||||
After setting up your personal [Auth token](http://sentry.keyman.com/settings/account/api/auth-tokens/), add the following to **~/.bashrc**
|
||||
After setting up your personal [Auth token](http://keyman.sentry.io/settings/account/api/auth-tokens/), add the following to **~/.bashrc**
|
||||
|
||||
```bash
|
||||
export SENTRY_AUTH_TOKEN={your Sentry auth token}
|
||||
|
|
|
|||
4
docs/file-formats/README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Description of file formats
|
||||
|
||||
In addition to the file formats described in this folder, see also
|
||||
the schema files in `common/schemas` for additional file formats.
|
||||
|
|
@ -72,7 +72,7 @@ https://help.keyman.com/developer/engine/android/latest-version/
|
|||
| KEYMAN_VERSION_ISO639_3 | 2024-05-22 |
|
||||
| KEYMAN_VERSION_JAVA | 11 |
|
||||
| KEYMAN_VERSION_LANGTAGS | 2025-02-18 |
|
||||
| KEYMAN_VERSION_LANGUAGE_SUBTAG_REGISTRY | 2024-05-16 |
|
||||
| KEYMAN_VERSION_LANGUAGE_SUBTAG_REGISTRY | 2025-03-10 |
|
||||
| KEYMAN_VERSION_UNICODE | 16.0.0 |
|
||||
|
||||
> ### This file is auto-generated by publish-minimum-versions.sh. Do not modify
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ Click on this to [search for a keyboard or language](../../start/searching-for-k
|
|||
When enabled, the Keyman app will display the 'Get Started' screen on app startup.
|
||||
|
||||
## Allow error reporting
|
||||
When enabled, the Keyman app will send crash reporting information to https://sentry.keyman.com. No personally identifiable information or keyboard strokes are recorded.
|
||||
When enabled, the Keyman app will send crash reporting information to Sentry. No personally identifiable information or keyboard strokes are recorded.
|
||||
|
||||
## Spacebar Caption
|
||||
Click on this to [change the displayed keyboard name](spacebar-caption) on the spacebar.
|
||||
|
|
|
|||
|
|
@ -226,6 +226,24 @@
|
|||
/* Text showing name of spacebar caption - language + keyboard */
|
||||
"menu-settings-spacebar-item-languageKeyboard" = "Lingua e tastiera";
|
||||
|
||||
/* Label for the "Adjust Keyboard Height" item on the main settings screen */
|
||||
"menu-settings-adjust-keyboard-height" = "Regola altezza tastiera";
|
||||
|
||||
/* Title for the "Adjust Keyboard Height" settings child screen */
|
||||
"adjust-keyboard-height-title" = "Regola tastiera";
|
||||
|
||||
/* Label for "Reset to Default Keyboard Height" button on the adjust height screen */
|
||||
"button-label-reset-default-keyboard-height" = "Reimposta l'altezza predefinita tastiera";
|
||||
|
||||
/* Instruction text to drag keyboard to resize */
|
||||
"keyboard-drag-instructions" = "Trascina il controllo freccia per regolare l'altezza della tastiera.";
|
||||
|
||||
/* Instruction to rotate to adjust landscape keyboard height (displayed when device is portrait) */
|
||||
"portrait-keyboard-rotate-instructions" = "Ruota il dispositivo per regolarlo in orizzontale.";
|
||||
|
||||
/* Instruction to rotate to adjust portrait keyboard height (displayed when device is landscape) */
|
||||
"landscape-keyboard-rotate-instructions" = "Ruota il dispositivo per regolarlo in verticale.";
|
||||
|
||||
/* Short text for notification: download failure for keyboard */
|
||||
"notification-download-failure-keyboard" = "Download tastiera non riuscito";
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ Build-Depends:
|
|||
perl,
|
||||
pkgconf,
|
||||
python3-all (>= 3.5),
|
||||
python3-build,
|
||||
python3-dbus,
|
||||
python3-fonttools,
|
||||
python3-gi,
|
||||
|
|
@ -39,6 +40,7 @@ Build-Depends:
|
|||
python3-requests,
|
||||
python3-sentry-sdk (>= 1.1),
|
||||
python3-setuptools,
|
||||
python3-venv,
|
||||
python3-xdg,
|
||||
xserver-xephyr,
|
||||
xvfb,
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ get_context_debug(IBusEngine *engine) {
|
|||
static gchar *
|
||||
debug_utf8_with_codepoints(const gchar *utf8) {
|
||||
GString *output = g_string_new("");
|
||||
g_string_append_printf(output, "|%s| len:%ld) [", utf8, g_utf8_strlen(utf8, -1));
|
||||
g_string_append_printf(output, "|%s| (len:%ld) [", utf8, g_utf8_strlen(utf8, -1));
|
||||
gunichar2 *utf16 = g_utf8_to_utf16(utf8, -1, NULL, NULL, NULL);
|
||||
for (int i = 0; utf16[i] != '\0'; i++) {
|
||||
g_string_append_printf(output, "U+%04x ", utf16[i]);
|
||||
|
|
@ -290,7 +290,7 @@ set_context_if_needed(IBusEngine *engine) {
|
|||
}
|
||||
|
||||
static void
|
||||
initialize_queue(IBusKeymanEngine *keyman, int index, int count) {
|
||||
initialize_queue_items(IBusKeymanEngine *keyman, int index, int count) {
|
||||
g_assert(keyman != NULL);
|
||||
g_assert(index >= 0 && index < MAX_QUEUE_SIZE);
|
||||
g_assert(count > 0 && count <= MAX_QUEUE_SIZE);
|
||||
|
|
@ -463,7 +463,7 @@ ibus_keyman_engine_constructor(
|
|||
keyman->lctrl_pressed = FALSE;
|
||||
keyman->ralt_pressed = FALSE;
|
||||
keyman->rctrl_pressed = FALSE;
|
||||
initialize_queue(keyman, 0, MAX_QUEUE_SIZE);
|
||||
initialize_queue_items(keyman, 0, MAX_QUEUE_SIZE);
|
||||
keyman->commit_item = &keyman->commit_queue[0];
|
||||
gchar **split_name = g_strsplit(engine_name, ":", 2);
|
||||
if (split_name[0] == NULL)
|
||||
|
|
@ -703,18 +703,19 @@ process_persist_action(IBusEngine *engine, km_core_option_item *persist_options)
|
|||
|
||||
static void
|
||||
process_emit_keystroke_action(IBusEngine *engine, km_core_bool emit_keystroke) {
|
||||
if (!emit_keystroke) {
|
||||
return;
|
||||
}
|
||||
IBusKeymanEngine *keyman = (IBusKeymanEngine *)engine;
|
||||
if (client_supports_surrounding_text(engine)) {
|
||||
// compliant app
|
||||
if (!emit_keystroke) {
|
||||
return;
|
||||
}
|
||||
ibus_engine_forward_key_event(engine, keyman->commit_item->keyval,
|
||||
keyman->commit_item->keycode, keyman->commit_item->state);
|
||||
return;
|
||||
}
|
||||
|
||||
// non-compliant app
|
||||
keyman->commit_item->emitting_keystroke = TRUE;
|
||||
keyman->commit_item->emitting_keystroke = emit_keystroke;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -768,7 +769,7 @@ commit_current_queue_item(IBusKeymanEngine *keyman) {
|
|||
}
|
||||
keyman->commit_item--;
|
||||
memmove(keyman->commit_queue, &keyman->commit_queue[1], sizeof(commit_queue_item) * MAX_QUEUE_SIZE - 1);
|
||||
initialize_queue(keyman, MAX_QUEUE_SIZE - 1, 1);
|
||||
initialize_queue_items(keyman, MAX_QUEUE_SIZE - 1, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -7,109 +7,255 @@
|
|||
// uses kernel keycodes which are (X11 keycode - 8)
|
||||
// see /usr/include/linux/input-event-codes.h
|
||||
static km_core_virtual_key const keycode_to_vk[256] = {
|
||||
0, // KEY_RESERVED = 0x00;
|
||||
KM_CORE_VKEY_ESC, // KEY_ESC = 0x01;
|
||||
KM_CORE_VKEY_1, // KEY_1 = 0x02;
|
||||
KM_CORE_VKEY_2, // KEY_2 = 0x03;
|
||||
KM_CORE_VKEY_3, // KEY_3 = 0x04;
|
||||
KM_CORE_VKEY_4, // KEY_4 = 0x05;
|
||||
KM_CORE_VKEY_5, // KEY_5 = 0x06;
|
||||
KM_CORE_VKEY_6, // KEY_6 = 0x07;
|
||||
KM_CORE_VKEY_7, // KEY_7 = 0x08;
|
||||
KM_CORE_VKEY_8, // KEY_8 = 0x09;
|
||||
KM_CORE_VKEY_9, // KEY_9 = 0x0A;
|
||||
KM_CORE_VKEY_0, // KEY_0 = 0x0B;
|
||||
KM_CORE_VKEY_HYPHEN, // KEY_MINUS = 0x0C;
|
||||
KM_CORE_VKEY_EQUAL, // KEY_EQUALS = 0x0D;
|
||||
KM_CORE_VKEY_BKSP, // KEY_BACKSPACE = 0x0E;
|
||||
KM_CORE_VKEY_TAB, // KEY_TAB = 0x0F;
|
||||
KM_CORE_VKEY_Q, // KEY_Q = 0x10;
|
||||
KM_CORE_VKEY_W, // KEY_W = 0x11;
|
||||
KM_CORE_VKEY_E, // KEY_E = 0x12;
|
||||
KM_CORE_VKEY_R, // KEY_R = 0x13;
|
||||
KM_CORE_VKEY_T, // KEY_T = 0x14;
|
||||
KM_CORE_VKEY_Y, // KEY_Y = 0x15;
|
||||
KM_CORE_VKEY_U, // KEY_U = 0x16;
|
||||
KM_CORE_VKEY_I, // KEY_I = 0x17;
|
||||
KM_CORE_VKEY_O, // KEY_O = 0x18;
|
||||
KM_CORE_VKEY_P, // KEY_P = 0x19;
|
||||
KM_CORE_VKEY_LBRKT, // KEY_LEFTBRACE = 0x1A;
|
||||
KM_CORE_VKEY_RBRKT, // KEY_RIGHTBRACE = 0x1B;
|
||||
KM_CORE_VKEY_ENTER, // KEY_ENTER = 0x1C;
|
||||
KM_CORE_VKEY_CONTROL, // KEY_LEFTCTRL = 0x1D;
|
||||
KM_CORE_VKEY_A, // KEY_A = 0x1E;
|
||||
KM_CORE_VKEY_S, // KEY_S = 0x1F;
|
||||
KM_CORE_VKEY_D, // KEY_D = 0x20;
|
||||
KM_CORE_VKEY_F, // KEY_F = 0x21;
|
||||
KM_CORE_VKEY_G, // KEY_G = 0x22;
|
||||
KM_CORE_VKEY_H, // KEY_H = 0x23;
|
||||
KM_CORE_VKEY_J, // KEY_J = 0x24;
|
||||
KM_CORE_VKEY_K, // KEY_K = 0x25;
|
||||
KM_CORE_VKEY_L, // KEY_L = 0x26;
|
||||
KM_CORE_VKEY_COLON, // KEY_SEMICOLON = 0x27;
|
||||
KM_CORE_VKEY_QUOTE, // KEY_APOSTROPHE = 0x28;
|
||||
KM_CORE_VKEY_BKQUOTE, // KEY_GRAVE = 0x29;
|
||||
KM_CORE_VKEY_SHIFT, // KEY_LEFTSHIFT = 0x2A;
|
||||
KM_CORE_VKEY_BKSLASH, // KEY_BACKSLASH = 0x2B;
|
||||
KM_CORE_VKEY_Z, // KEY_Z = 0x2C;
|
||||
KM_CORE_VKEY_X, // KEY_X = 0x2D;
|
||||
KM_CORE_VKEY_C, // KEY_C = 0x2E;
|
||||
KM_CORE_VKEY_V, // KEY_V = 0x2F;
|
||||
KM_CORE_VKEY_B, // KEY_B = 0x30;
|
||||
KM_CORE_VKEY_N, // KEY_N = 0x31;
|
||||
KM_CORE_VKEY_M, // KEY_M = 0x32;
|
||||
KM_CORE_VKEY_COMMA, // KEY_COMMA = 0x33;
|
||||
KM_CORE_VKEY_PERIOD, // KEY_DOT = 0x34;
|
||||
KM_CORE_VKEY_SLASH, // KEY_SLASH = 0x35;
|
||||
KM_CORE_VKEY_SHIFT, // KEY_RIGHTSHIFT = 0x36;
|
||||
KM_CORE_VKEY_NPSTAR, // KEY_KPASTERISK = 0x37;
|
||||
KM_CORE_VKEY_ALT, // KEY_LEFTALT = 0x38;
|
||||
KM_CORE_VKEY_SPACE, // KEY_SPACE = 0x39;
|
||||
KM_CORE_VKEY_CAPS, // KEY_CAPSLOCK = 0x3A;
|
||||
KM_CORE_VKEY_F1, // KEY_F1 = 0x3B;
|
||||
KM_CORE_VKEY_F2, // KEY_F2 = 0x3C;
|
||||
KM_CORE_VKEY_F3, // KEY_F3 = 0x3D;
|
||||
KM_CORE_VKEY_F4, // KEY_F4 = 0x3E;
|
||||
KM_CORE_VKEY_F5, // KEY_F5 = 0x3F;
|
||||
KM_CORE_VKEY_F6, // KEY_F6 = 0x40;
|
||||
KM_CORE_VKEY_F7, // KEY_F7 = 0x41;
|
||||
KM_CORE_VKEY_F8, // KEY_F8 = 0x42;
|
||||
KM_CORE_VKEY_F9, // KEY_F9 = 0x43;
|
||||
KM_CORE_VKEY_F10, // KEY_F10 = 0x44;
|
||||
0, // KEY_NUMLOCK = 0x45;
|
||||
0, // KEY_SCROLLLOCK = 0x46;
|
||||
KM_CORE_VKEY_NP7, // KEY_KP7 = 0x47;
|
||||
KM_CORE_VKEY_NP8, // KEY_KP8 = 0x48;
|
||||
KM_CORE_VKEY_NP9, // KEY_KP9 = 0x49;
|
||||
KM_CORE_VKEY_NPMINUS, // KEY_KPMINUS = 0x4A;
|
||||
KM_CORE_VKEY_NP4, // KEY_KP4 = 0x4B;
|
||||
KM_CORE_VKEY_NP5, // KEY_KP5 = 0x4C;
|
||||
KM_CORE_VKEY_NP6, // KEY_KP6 = 0x4D;
|
||||
KM_CORE_VKEY_NPPLUS, // KEY_KPPLUS = 0x4E;
|
||||
KM_CORE_VKEY_NP1, // KEY_KP1 = 0x4F;
|
||||
KM_CORE_VKEY_NP2, // KEY_KP2 = 0x50;
|
||||
KM_CORE_VKEY_NP3, // KEY_KP3 = 0x51;
|
||||
KM_CORE_VKEY_NP0, // KEY_KP0 = 0x52;
|
||||
KM_CORE_VKEY_NPDOT, // KEY_KPDOT = 0x53;
|
||||
0, // padding 0x54;
|
||||
0, // KEY_ZENKAKUHANKAKU = 0x55;
|
||||
KM_CORE_VKEY_oE2, // KEY_102ND = 0x56;
|
||||
KM_CORE_VKEY_F11, // KEY_F11 = 0x57;
|
||||
KM_CORE_VKEY_F12, // KEY_F12 = 0x58;
|
||||
0, // KEY_RO = 0x59;
|
||||
0, // KEY_KATAKANA = 0x5a;
|
||||
0, // KEY_HIRAGANA = 0x5b;
|
||||
0, // KEY_HENKAN = 0x5c;
|
||||
0, // KEY_KATAKANAHIRAGANA = 0x5d;
|
||||
0, // KEY_MUHENKAN = 0x5e;
|
||||
0, // KEY_KPJPCOMMA = 0x5f;
|
||||
0, // KEY_KPENTER = 0x60;
|
||||
KM_CORE_VKEY_CONTROL, // KEY_RIGHTCTRL = 0x61;
|
||||
0, // KEY_KPSLASH = 0x62;
|
||||
0, // KEY_SYSRQ = 0x63;
|
||||
KM_CORE_VKEY_ALT // KEY_RIGHTALT = 0x64;
|
||||
|
||||
// Many more KEYS currently not used by KMW...
|
||||
0, // 0x00 - KEY_RESERVED
|
||||
KM_CORE_VKEY_ESC, // 0x01 - KEY_ESC
|
||||
KM_CORE_VKEY_1, // 0x02 - KEY_1
|
||||
KM_CORE_VKEY_2, // 0x03 - KEY_2
|
||||
KM_CORE_VKEY_3, // 0x04 - KEY_3
|
||||
KM_CORE_VKEY_4, // 0x05 - KEY_4
|
||||
KM_CORE_VKEY_5, // 0x06 - KEY_5
|
||||
KM_CORE_VKEY_6, // 0x07 - KEY_6
|
||||
KM_CORE_VKEY_7, // 0x08 - KEY_7
|
||||
KM_CORE_VKEY_8, // 0x09 - KEY_8
|
||||
KM_CORE_VKEY_9, // 0x0A - KEY_9
|
||||
KM_CORE_VKEY_0, // 0x0B - KEY_0
|
||||
KM_CORE_VKEY_HYPHEN, // 0x0C - KEY_MINUS
|
||||
KM_CORE_VKEY_EQUAL, // 0x0D - KEY_EQUALS
|
||||
KM_CORE_VKEY_BKSP, // 0x0E - KEY_BACKSPACE
|
||||
KM_CORE_VKEY_TAB, // 0x0F - KEY_TAB
|
||||
KM_CORE_VKEY_Q, // 0x10 - KEY_Q
|
||||
KM_CORE_VKEY_W, // 0x11 - KEY_W
|
||||
KM_CORE_VKEY_E, // 0x12 - KEY_E
|
||||
KM_CORE_VKEY_R, // 0x13 - KEY_R
|
||||
KM_CORE_VKEY_T, // 0x14 - KEY_T
|
||||
KM_CORE_VKEY_Y, // 0x15 - KEY_Y
|
||||
KM_CORE_VKEY_U, // 0x16 - KEY_U
|
||||
KM_CORE_VKEY_I, // 0x17 - KEY_I
|
||||
KM_CORE_VKEY_O, // 0x18 - KEY_O
|
||||
KM_CORE_VKEY_P, // 0x19 - KEY_P
|
||||
KM_CORE_VKEY_LBRKT, // 0x1A - KEY_LEFTBRACE
|
||||
KM_CORE_VKEY_RBRKT, // 0x1B - KEY_RIGHTBRACE
|
||||
KM_CORE_VKEY_ENTER, // 0x1C - KEY_ENTER
|
||||
KM_CORE_VKEY_CONTROL, // 0x1D - KEY_LEFTCTRL
|
||||
KM_CORE_VKEY_A, // 0x1E - KEY_A
|
||||
KM_CORE_VKEY_S, // 0x1F - KEY_S
|
||||
KM_CORE_VKEY_D, // 0x20 - KEY_D
|
||||
KM_CORE_VKEY_F, // 0x21 - KEY_F
|
||||
KM_CORE_VKEY_G, // 0x22 - KEY_G
|
||||
KM_CORE_VKEY_H, // 0x23 - KEY_H
|
||||
KM_CORE_VKEY_J, // 0x24 - KEY_J
|
||||
KM_CORE_VKEY_K, // 0x25 - KEY_K
|
||||
KM_CORE_VKEY_L, // 0x26 - KEY_L
|
||||
KM_CORE_VKEY_COLON, // 0x27 - KEY_SEMICOLON
|
||||
KM_CORE_VKEY_QUOTE, // 0x28 - KEY_APOSTROPHE
|
||||
KM_CORE_VKEY_BKQUOTE, // 0x29 - KEY_GRAVE
|
||||
KM_CORE_VKEY_SHIFT, // 0x2A - KEY_LEFTSHIFT
|
||||
KM_CORE_VKEY_BKSLASH, // 0x2B - KEY_BACKSLASH
|
||||
KM_CORE_VKEY_Z, // 0x2C - KEY_Z
|
||||
KM_CORE_VKEY_X, // 0x2D - KEY_X
|
||||
KM_CORE_VKEY_C, // 0x2E - KEY_C
|
||||
KM_CORE_VKEY_V, // 0x2F - KEY_V
|
||||
KM_CORE_VKEY_B, // 0x30 - KEY_B
|
||||
KM_CORE_VKEY_N, // 0x31 - KEY_N
|
||||
KM_CORE_VKEY_M, // 0x32 - KEY_M
|
||||
KM_CORE_VKEY_COMMA, // 0x33 - KEY_COMMA
|
||||
KM_CORE_VKEY_PERIOD, // 0x34 - KEY_DOT
|
||||
KM_CORE_VKEY_SLASH, // 0x35 - KEY_SLASH
|
||||
KM_CORE_VKEY_SHIFT, // 0x36 - KEY_RIGHTSHIFT
|
||||
KM_CORE_VKEY_NPSTAR, // 0x37 - KEY_KPASTERISK
|
||||
KM_CORE_VKEY_ALT, // 0x38 - KEY_LEFTALT
|
||||
KM_CORE_VKEY_SPACE, // 0x39 - KEY_SPACE
|
||||
KM_CORE_VKEY_CAPS, // 0x3A - KEY_CAPSLOCK
|
||||
KM_CORE_VKEY_F1, // 0x3B - KEY_F1
|
||||
KM_CORE_VKEY_F2, // 0x3C - KEY_F2
|
||||
KM_CORE_VKEY_F3, // 0x3D - KEY_F3
|
||||
KM_CORE_VKEY_F4, // 0x3E - KEY_F4
|
||||
KM_CORE_VKEY_F5, // 0x3F - KEY_F5
|
||||
KM_CORE_VKEY_F6, // 0x40 - KEY_F6
|
||||
KM_CORE_VKEY_F7, // 0x41 - KEY_F7
|
||||
KM_CORE_VKEY_F8, // 0x42 - KEY_F8
|
||||
KM_CORE_VKEY_F9, // 0x43 - KEY_F9
|
||||
KM_CORE_VKEY_F10, // 0x44 - KEY_F10
|
||||
KM_CORE_VKEY_NUMLOCK, // 0x45 - KEY_NUMLOCK
|
||||
KM_CORE_VKEY_SCROLL, // 0x46 - KEY_SCROLLLOCK
|
||||
KM_CORE_VKEY_NP7, // 0x47 - KEY_KP7
|
||||
KM_CORE_VKEY_NP8, // 0x48 - KEY_KP8
|
||||
KM_CORE_VKEY_NP9, // 0x49 - KEY_KP9
|
||||
KM_CORE_VKEY_NPMINUS, // 0x4A - KEY_KPMINUS
|
||||
KM_CORE_VKEY_NP4, // 0x4B - KEY_KP4
|
||||
KM_CORE_VKEY_NP5, // 0x4C - KEY_KP5
|
||||
KM_CORE_VKEY_NP6, // 0x4D - KEY_KP6
|
||||
KM_CORE_VKEY_NPPLUS, // 0x4E - KEY_KPPLUS
|
||||
KM_CORE_VKEY_NP1, // 0x4F - KEY_KP1
|
||||
KM_CORE_VKEY_NP2, // 0x50 - KEY_KP2
|
||||
KM_CORE_VKEY_NP3, // 0x51 - KEY_KP3
|
||||
KM_CORE_VKEY_NP0, // 0x52 - KEY_KP0
|
||||
KM_CORE_VKEY_NPDOT, // 0x53 - KEY_KPDOT
|
||||
0, // padding 0x54;
|
||||
KM_CORE_VKEY__KANA__, // 0x55 - KEY_ZENKAKUHANKAKU
|
||||
KM_CORE_VKEY_oE2, // 0x56 - KEY_102ND
|
||||
KM_CORE_VKEY_F11, // 0x57 - KEY_F11
|
||||
KM_CORE_VKEY_F12, // 0x58 - KEY_F12
|
||||
0, // 0x59 - KEY_RO - ?
|
||||
KM_CORE_VKEY__KANA__, // 0x5a - KEY_KATAKANA
|
||||
KM_CORE_VKEY__KANA__, // 0x5b - KEY_HIRAGANA
|
||||
KM_CORE_VKEY__KANA__, // 0x5c - KEY_HENKAN
|
||||
KM_CORE_VKEY__KANA__, // 0x5d - KEY_KATAKANAHIRAGANA
|
||||
KM_CORE_VKEY__KANA__, // 0x5e - KEY_MUHENKAN
|
||||
KM_CORE_VKEY_COMMA, // 0x5f - KEY_KPJPCOMMA
|
||||
KM_CORE_VKEY_ENTER, // 0x60 - KEY_KPENTER
|
||||
KM_CORE_VKEY_CONTROL, // 0x61 - KEY_RIGHTCTRL
|
||||
KM_CORE_VKEY_SLASH, // 0x62 - KEY_KPSLASH
|
||||
KM_CORE_VKEY_PRTSCN, // 0x63 - KEY_SYSRQ
|
||||
KM_CORE_VKEY_ALT, // 0x64 - KEY_RIGHTALT
|
||||
0, // 0x65 - KEY_LINEFEED - ?
|
||||
KM_CORE_VKEY_HOME, // 0x66 - KEY_HOME
|
||||
KM_CORE_VKEY_UP, // 0x67 - KEY_UP
|
||||
KM_CORE_VKEY_PGUP, // 0x68 - KEY_PAGEUP
|
||||
KM_CORE_VKEY_LEFT, // 0x69 - KEY_LEFT
|
||||
KM_CORE_VKEY_RIGHT, // 0x6A - KEY_RIGHT
|
||||
KM_CORE_VKEY_END, // 0x6B - KEY_END
|
||||
KM_CORE_VKEY_DOWN, // 0x6C - KEY_DOWN
|
||||
KM_CORE_VKEY_PGDN, // 0x6D - KEY_PAGEDOWN
|
||||
KM_CORE_VKEY_INS, // 0x6E - KEY_INSERT
|
||||
KM_CORE_VKEY_DEL, // 0x6F - KEY_DELETE
|
||||
0, // 0x70 - KEY_MACRO - ?
|
||||
KM_CORE_VKEY__VOLUMEMUTE__, // 0x71 - KEY_MUTE
|
||||
KM_CORE_VKEY__VOLUMEDOWN__, // 0x72 - KEY_VOLUMEDOWN
|
||||
KM_CORE_VKEY__VOLUMEUP__, // 0x73 - KEY_VOLUMEUP
|
||||
0, // 0x74 - KEY_POWER
|
||||
KM_CORE_VKEY_EQUAL, // 0x75 - KEY_KPEQUAL
|
||||
KM_CORE_VKEY_NPPLUS, // 0x76 - KEY_KPPLUSMINUS
|
||||
KM_CORE_VKEY_PAUSE, // 0x77 - KEY_PAUSE
|
||||
KM_CORE_VKEY__ZOOM__, // 0x78 - KEY_SCALE
|
||||
KM_CORE_VKEY_NPDOT, // 0x79 - KEY_KPCOMMA
|
||||
KM_CORE_VKEY__HANGUL__, // 0x7a - KEY_HANGEUL
|
||||
KM_CORE_VKEY__HANJA__, // 0x7b - KEY_HANJA
|
||||
0, // 0x7c - KEY_YEN - ?
|
||||
KM_CORE_VKEY__LWIN__, // 0x7d - KEY_LEFTMETA
|
||||
KM_CORE_VKEY__RWIN__, // 0x7e - KEY_RIGHTMETA
|
||||
0, // 0x7f - KEY_COMPOSE - ?
|
||||
KM_CORE_VKEY__BROWSERSTOP__, // 0x80 - KEY_STOP
|
||||
KM_CORE_VKEY__BROWSERREFRESH__,// 0x81 - KEY_AGAIN
|
||||
0, // 0x82 - KEY_PROPS - ?
|
||||
0, // 0x83 - KEY_UNDO - ?
|
||||
0, // 0x84 - KEY_FRONT - ?
|
||||
0, // 0x85 - KEY_COPY - ?
|
||||
0, // 0x86 - KEY_OPEN - ?
|
||||
0, // 0x87 - KEY_PASTE - ?
|
||||
KM_CORE_VKEY__BROWSERSEARCH__, // 0x88 - KEY_FIND
|
||||
0, // 0x89 - KEY_CUT - ?
|
||||
KM_CORE_VKEY_HELP, // 0x8a - KEY_HELP
|
||||
KM_CORE_VKEY_ALT, // 0x8b - KEY_MENU
|
||||
0, // 0x8c - KEY_CALC - ?
|
||||
0, // 0x8d - KEY_SETUP - ?
|
||||
KM_CORE_VKEY__SLEEP__, // 0x8e - KEY_SLEEP
|
||||
0, // 0x8f - KEY_WAKEUP - ?
|
||||
0, // 0x90 - KEY_FILE - ?
|
||||
0, // 0x91 - KEY_SENDFILE - ?
|
||||
0, // 0x92 - KEY_DELETEFILE - ?
|
||||
0, // 0x93 - KEY_XFER - ?
|
||||
KM_CORE_VKEY__APP1__, // 0x94 - KEY_PROG1
|
||||
KM_CORE_VKEY__APP2__, // 0x95 - KEY_PROG2
|
||||
KM_CORE_VKEY__BROWSERHOME__, // 0x96 - KEY_WWW
|
||||
0, // 0x97 - KEY_MSDOS - ?
|
||||
0, // 0x98 - KEY_COFFEE, KEY_SCREENLOCK - ?
|
||||
0, // 0x99 - KEY_ROTATE_DISPLAY, KEY_DIRECTION - ?
|
||||
0, // 0x9a - KEY_CYCLEWINDOWS - ?
|
||||
KM_CORE_VKEY__MAIL__, // 0x9b - KEY_MAIL
|
||||
0, // 0x9c - KEY_BOOKMARKS - ?
|
||||
0, // 0x9d - KEY_COMPUTER - ?
|
||||
KM_CORE_VKEY__BROWSERBACK__, // 0x9e - KEY_BACK
|
||||
KM_CORE_VKEY__BROWSERFORWARD__,// 0x9f - KEY_FORWARD
|
||||
0, // 0xa0 - KEY_CLOSECD - ?
|
||||
0, // 0xa1 - KEY_EJECTCD - ?
|
||||
0, // 0xa2 - KEY_EJECTCLOSECD - ?
|
||||
KM_CORE_VKEY__MEDIANEXT__, // 0xa3 - KEY_NEXTSONG
|
||||
KM_CORE_VKEY__MEDIAPLAY__, // 0xa4 - KEY_PLAYPAUSE
|
||||
KM_CORE_VKEY__MEDIAPREV__, // 0xa5 - KEY_PREVIOUSSONG
|
||||
0, // 0xa6 - KEY_STOPCD - ?
|
||||
0, // 0xa7 - KEY_RECORD - ?
|
||||
0, // 0xa8 - KEY_REWIND - ?
|
||||
KM_CORE_VKEY__MEDIA__, // 0xa9 - KEY_PHONE
|
||||
0, // 0xaa - KEY_ISO - ?
|
||||
0, // 0xab - KEY_CONFIG - ?
|
||||
KM_CORE_VKEY__BROWSERHOME__, // 0xac - KEY_HOMEPAGE
|
||||
KM_CORE_VKEY__BROWSERREFRESH__,// 0xad - KEY_REFRESH
|
||||
0, // 0xae - KEY_EXIT - ?
|
||||
0, // 0xaf - KEY_MOVE - ?
|
||||
0, // 0xb0 - KEY_EDIT - ?
|
||||
0, // 0xb1 - KEY_SCROLLUP - ?
|
||||
0, // 0xb2 - KEY_SCROLLDOWN - ?
|
||||
0, // 0xb3 - KEY_KPLEFTPAREN - ?
|
||||
0, // 0xb4 - KEY_KPRIGHTPAREN - ?
|
||||
0, // 0xb5 - KEY_NEW - ?
|
||||
0, // 0xb6 - KEY_REDO - ?
|
||||
KM_CORE_VKEY_F13, // 0xb7 - KEY_F13
|
||||
KM_CORE_VKEY_F14, // 0xb8 - KEY_F14
|
||||
KM_CORE_VKEY_F15, // 0xb9 - KEY_F15
|
||||
KM_CORE_VKEY_F16, // 0xba - KEY_F16
|
||||
KM_CORE_VKEY_F17, // 0xbb - KEY_F17
|
||||
KM_CORE_VKEY_F18, // 0xbc - KEY_F18
|
||||
KM_CORE_VKEY_F19, // 0xbd - KEY_F19
|
||||
KM_CORE_VKEY_F20, // 0xbe - KEY_F20
|
||||
KM_CORE_VKEY_F21, // 0xbf - KEY_F21
|
||||
KM_CORE_VKEY_F22, // 0xc0 - KEY_F22
|
||||
KM_CORE_VKEY_F23, // 0xc1 - KEY_F23
|
||||
KM_CORE_VKEY_F24, // 0xc2 - KEY_F24
|
||||
0, // 0xc3 - unassigned
|
||||
0, // 0xc4 - unassigned
|
||||
0, // 0xc5 - unassigned
|
||||
0, // 0xc6 - unassigned
|
||||
0, // 0xc7 - unassigned
|
||||
KM_CORE_VKEY__MEDIAPLAY__, // 0xc8 - KEY_PLAYCD
|
||||
KM_CORE_VKEY__MEDIASTOP__, // 0xc9 - KEY_PAUSECD
|
||||
0, // 0xca - KEY_PROG3 - ?
|
||||
0, // 0xcb - KEY_PROG4 - ?
|
||||
0, // 0xcc - KEY_ALL_APPLICATIONS, KEY_DASHBOARD - ?
|
||||
0, // 0xcd - KEY_SUSPEND - ?
|
||||
0, // 0xce - KEY_CLOSE - ?
|
||||
KM_CORE_VKEY__MEDIAPLAY__, // 0xcf - KEY_PLAY
|
||||
0, // 0xd0 - KEY_FASTFORWARD - ?
|
||||
0, // 0xd1 - KEY_BASSBOOST - ?
|
||||
KM_CORE_VKEY_PRINT, // 0xd2 - KEY_PRINT
|
||||
0, // 0xd3 - KEY_HP - ?
|
||||
0, // 0xd4 - KEY_CAMERA - ?
|
||||
0, // 0xd5 - KEY_SOUND - ?
|
||||
0, // 0xd6 - KEY_QUESTION - ?
|
||||
KM_CORE_VKEY__MAIL__, // 0xd7 - KEY_EMAIL
|
||||
0, // 0xd8 - KEY_CHAT - ?
|
||||
0, // 0xd9 - KEY_SEARCH - ?
|
||||
0, // 0xda - KEY_CONNECT - ?
|
||||
0, // 0xdb - KEY_FINANCE - ?
|
||||
0, // 0xdc - KEY_SPORT - ?
|
||||
0, // 0xdd - KEY_SHOP - ?
|
||||
0, // 0xde - KEY_ALTERASE - ?
|
||||
0, // 0xdf - KEY_CANCEL - ?
|
||||
0, // 0xe0 - KEY_BRIGHTNESSDOWN - ?
|
||||
0, // 0xe1 - KEY_BRIGHTNESSUP - ?
|
||||
KM_CORE_VKEY__MEDIA__, // 0xe2 - KEY_MEDIA
|
||||
0, // 0xe3 - KEY_SWITCHVIDEOMODE - ?
|
||||
0, // 0xe4 - KEY_KBDILLUMTOGGLE - ?
|
||||
0, // 0xe5 - KEY_KBDILLUMDOWN - ?
|
||||
0, // 0xe6 - KEY_KBDILLUMUP - ?
|
||||
0, // 0xe7 - KEY_SEND - ?
|
||||
0, // 0xe8 - KEY_REPLY - ?
|
||||
0, // 0xe9 - KEY_FORWARDMAIL - ?
|
||||
0, // 0xea - KEY_SAVE - ?
|
||||
0, // 0xeb - KEY_DOCUMENTS - ?
|
||||
0, // 0xec - KEY_BATTERY - ?
|
||||
0, // 0xed - KEY_BLUETOOTH - ?
|
||||
0, // 0xee - KEY_WLAN - ?
|
||||
0, // 0xef - KEY_UWB - ?
|
||||
0, // 0xf0 - KEY_UNKNOWN - ?
|
||||
0, // 0xf1 - KEY_VIDEO_NEXT - ?
|
||||
0, // 0xf2 - KEY_VIDEO_PREV - ?
|
||||
0, // 0xf3 - KEY_BRIGHTNESS_CYCLE - ?
|
||||
0, // 0xf4 - KEY_BRIGHTNESS_AUTO, KEY_BRIGHTNESS_ZERO - ?
|
||||
0, // 0xf5 - KEY_DISPLAY_OFF - ?
|
||||
0, // 0xf6 - KEY_WWAN, KEY_WIMAX - ?
|
||||
0, // 0xf7 - KEY_RFKILL - ?
|
||||
0, // 0xf8 - KEY_MICMUTE - ?
|
||||
};
|
||||
|
||||
#endif // __KEYCODES_H__
|
||||
|
|
|
|||
|
|
@ -15,11 +15,13 @@ clean:
|
|||
check:
|
||||
./build.sh test
|
||||
|
||||
devdist: version
|
||||
python3 setup.py egg_info -b.`TZ=UTC git log -1 --pretty=format:%cd --date=format-local:%Y%m%d%H%M` sdist
|
||||
|
||||
dist: clean version
|
||||
python3 setup.py sdist
|
||||
# TODO: remove this if when we no longer support Ubuntu 22.04 Jammy
|
||||
if dpkg --compare-versions "$(lsb_release -r -s)" lt 24.04; then \
|
||||
python3 setup.py sdist ; \
|
||||
else \
|
||||
python3 -m build --outdir build --sdist ; \
|
||||
fi
|
||||
|
||||
deb: dist
|
||||
@VERSION=$(shell echo `basename dist/*.gz .tar.gz|cut -d "-" -f2` > /tmp/keyman_version)
|
||||
|
|
|
|||
|
|
@ -83,7 +83,14 @@ build_action() {
|
|||
builder_echo "Building man and help pages"
|
||||
build_man_and_help_pages
|
||||
builder_echo "Building keyman-config"
|
||||
python3 setup.py build
|
||||
# we use `dpkg --compare-versions` to compare the current Ubuntu version
|
||||
if dpkg --compare-versions "$(lsb_release -r -s)" lt 24.04; then
|
||||
# Ubuntu 22.04 Jammy has a buggy version of python3-build which doesn't work
|
||||
# TODO: remove once we drop support for Ubuntu 22.04 Jammy
|
||||
python3 setup.py build
|
||||
else
|
||||
python3 -m build --outdir build .
|
||||
fi
|
||||
}
|
||||
|
||||
test_action() {
|
||||
|
|
@ -106,8 +113,7 @@ install_action() {
|
|||
if [ -n "${SUDO_USER:-}" ]; then
|
||||
# with sudo install into /usr/local
|
||||
pip3 install qrcode sentry-sdk
|
||||
# eventually change this to: pip3 install .
|
||||
python3 setup.py install
|
||||
pip3 install .
|
||||
# install icons
|
||||
mkdir -p /usr/local/share/keyman/icons
|
||||
cp keyman_config/icons/* /usr/local/share/keyman/icons
|
||||
|
|
@ -117,9 +123,7 @@ install_action() {
|
|||
else
|
||||
# without sudo install into /tmp/keyman (or $DESTDIR)
|
||||
mkdir -p "/tmp/keyman/$(python3 -c 'import sys;import os;pythonver="python%d.%d" % (sys.version_info[0], sys.version_info[1]);sitedir = os.path.join("lib", pythonver, "site-packages");print(sitedir)')"
|
||||
# when we no longer have to support old pip version (python > 3.6) change this to:
|
||||
# pip3 install --prefix /tmp/keyman .
|
||||
PYTHONUSERBASE=${DESTDIR:-/tmp/keyman} python3 setup.py install --user
|
||||
pip3 install --prefix /tmp/keyman .
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,10 +68,10 @@
|
|||
/* copyright label in the Package Information window */
|
||||
"copyright-label" = "Copyright:";
|
||||
|
||||
/* message displayed to alert user to need grant accessibility permission in Preferences, pre-Ventura */
|
||||
/* message displayed to alert user to grant accessibility permission in Preferences, pre-Ventura */
|
||||
"privacy-alert-text" = "To function properly, Keyman requires accessibility features:\n\nGrant access in System Preferences, Security & Privacy.\nRestart your system.";
|
||||
|
||||
/* message displayed to alert user to need grant accessibility permission in Settings, for macOS Ventura and later */
|
||||
/* message displayed to alert user to grant accessibility permission in Settings, for macOS Ventura and later */
|
||||
"privacy-alert-text-ventura" = "To function properly, Keyman requires accessibility features:\n\nGrant access in System Settings, Privacy & Security.\nRestart your system.";
|
||||
|
||||
/* Text of menu item in Input Menu when no Keyboards are configured -- include parentheses */
|
||||
|
|
|
|||
|
|
@ -67,8 +67,11 @@
|
|||
/* copyright label in the Package Information window */
|
||||
"copyright-label" = "Derechos de autor:";
|
||||
|
||||
/* message displayed to alert user to need grant accessibility permission */
|
||||
/* message displayed to alert user to grant accessibility permission in Preferences, pre-Ventura */
|
||||
"privacy-alert-text" = "Para funcionar correctamente, Keyman requiere las siguientes características de accesibilidad:\nel acceso en Preferencias del Sistema, Seguridad y Privacidad.\nel reinicio del sistema.";
|
||||
|
||||
/* message displayed to alert user to grant accessibility permission in Settings, for macOS Ventura and later */
|
||||
"privacy-alert-text-ventura" = "To function properly, Keyman requires accessibility features:\n\nGrant access in System Settings, Privacy & Security.\nRestart your system.";
|
||||
|
||||
/* Text of menu item in Input Menu when no Keyboards are configured -- include parentheses */
|
||||
"no-keyboard-configured-menu-placeholder" = "(Sin teclado configurado)";
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Keyman works, as well as access information about installed keyboards.
|
|||
## Opening Keyman Configuration
|
||||
To open Keyman Configuration:
|
||||
|
||||
1. 
|
||||
1. 
|
||||
|
||||
Make sure that Keyman for macOS is the active input source (as shown above).
|
||||
For more information, see [Getting Started with Keyman for macOS](../../start/tutorial).
|
||||
|
|
@ -16,16 +16,15 @@ To open Keyman Configuration:
|
|||
3. On the Input menu, in the section for Keyman, click **Configuration**. Note: If you just
|
||||
installed Keyman and the Configuration menu is not available, please restart the computer.
|
||||
|
||||

|
||||

|
||||
|
||||
4. The Keyman Configuration window appears:
|
||||

|
||||

|
||||
|
||||
## Using Keyman Configuration
|
||||
|
||||
Following are instructions for performing various tasks in the Configuration window:
|
||||
|
||||
- [Keyman Configuration - Keyboard Layouts Tab](keyboards)
|
||||
- [Keyman Configuration - Options Tab](options)
|
||||
- [Keyman Configuration - Support Tab](support)
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 27 KiB |
BIN
mac/docs/help/mac_images/input-menu-keyman-selected.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 28 KiB |
BIN
mac/docs/help/mac_images/keyboard-add-input-source.png
Normal file
|
After Width: | Height: | Size: 266 KiB |
BIN
mac/docs/help/mac_images/keyboard-input-sources-annotated.png
Normal file
|
After Width: | Height: | Size: 223 KiB |
BIN
mac/docs/help/mac_images/keyboard-settings-annotated.png
Normal file
|
After Width: | Height: | Size: 206 KiB |
BIN
mac/docs/help/mac_images/keyman-configuration-menu.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
mac/docs/help/mac_images/keyman-configuration-window.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
mac/docs/help/mac_images/keyman-keyboard-selected.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 42 KiB |
BIN
mac/docs/help/mac_images/menu-bar-with-keyman.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |
|
|
@ -17,7 +17,7 @@ Here's how to download and install a Keyman keyboard within Keyman for macOS:
|
|||
Note: If you just installed Keyman and the Configuration menu is not available, please
|
||||
restart the computer.
|
||||
|
||||

|
||||

|
||||
|
||||
3. Select the Keyboard Layouts tab.
|
||||
4. Click **Download keyboard…**.
|
||||
|
|
@ -39,7 +39,7 @@ window. Here's how:
|
|||
1. Choose Keyman from the Input menu in the menu bar.
|
||||
2. Open Keyman Configuration, from the Configuration link under Keyman in the Input menu.
|
||||
|
||||

|
||||

|
||||
|
||||
3. Select the Keyboard Layouts tab.
|
||||
4. Back in Finder, find the keyboard package on your computer. Keyman keyboard packages
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ Apple Disk Image (.dmg) file. It should be installed according to the following
|
|||
4. Keyman should now be an option in the Input Sources menu towards the right hand
|
||||
side of the menu bar.
|
||||
|
||||

|
||||

|
||||
|
||||
**Note**: On recent versions of macOS, the first time you enable Keyman by selecting Keyman
|
||||
from the menu, you will be prompted to give certain permissions to Keyman. It is
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ installing *Keyman for macOS*, please see: [Install Keyman for macOS](install-ke
|
|||
|
||||
## Step 1: Is Keyman active?
|
||||
|
||||

|
||||

|
||||
|
||||
Check the menu bar near the date and time to see if Keyman for macOS is
|
||||
active. If it is active you will see the Keyman icon: 
|
||||
|
|
@ -21,38 +21,45 @@ active. If it is active you will see the Keyman icon: 
|
||||

|
||||
|
||||
The Input menu appears in the menu bar only if you enable at least two input
|
||||
sources in Keyboard preferences. The Input menu is identified by a flag or character
|
||||
that represents the input source currently in use.
|
||||
The Input menu appears in the menu bar only if you enable 'Show Input menu in menu bar'
|
||||
under Keyboard settings > Input Sources > Edit. The Input source currently in use is represented in the menu bar by an icon or a symbol containing one or more characters.
|
||||
|
||||
If Keyman is not in the Input menu, Keyman for macOS is not added as an input source.
|
||||
If Keyman is not listed in the Input menu, then Keyman for macOS has not been added as an input source.
|
||||
Follow **Step 3** to add Keyman for macOS as an input source.
|
||||
|
||||
## Step 3: Add Keyman for macOS
|
||||
|
||||
To add Keyman for macOS as an input source:
|
||||
|
||||
1. On the Apple menu, click System Preferences.
|
||||
1. On the Apple menu, click System Settings
|
||||
|
||||

|
||||

|
||||
|
||||
2. Click Keyboard, select the **Input Sources** tab, and then click .
|
||||
2. Click Keyboard, go to the **Text Input** section and then click the Edit... button
|
||||
|
||||
3. In the left-hand pane, select **Multiple Languages**, and then select **Keyman** in the
|
||||
right-hand pane.
|
||||

|
||||
|
||||

|
||||
3. In the Input Sources window, click the plus sign to add a keyboard
|
||||
|
||||
4. Click Add.
|
||||

|
||||
|
||||
4. In the left-hand pane, select **Multiple Languages**, and then select **Keyman** in the
|
||||
right-hand pane and click the Add button.
|
||||
|
||||

|
||||
|
||||
Now, return to **Step 2**.
|
||||
|
||||
## Step 4: Select a Keyman keyboard
|
||||
|
||||
On the Input menu, point to Keyboards, and then select a Keyman keyboard. Note: If
|
||||
the Keyman menu items are not available, please restart the computer.
|
||||
With Keyman selected in the Input menu, select a Keyman keyboard.
|
||||
|
||||
Note: If the Keyman menu items are not available, please restart the computer.
|
||||
|
||||

|
||||
|
||||
|
||||
**Tip:**: If there are no Keyman keyboards in the Keyboards menu, you can learn
|
||||
how to install one here: [Install a keyboard](../start/install-keyboard).
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ title: Uninstalling Keyman for macOS
|
|||
|
||||
To uninstall Keyman for macOS:
|
||||
|
||||
1. Choose Apple menu > System Preferences.
|
||||
1. Choose Apple menu > System Settings.
|
||||
|
||||

|
||||

|
||||
|
||||
2. Click Keyboard, then click Input Sources.
|
||||
|
||||
|
|
|
|||
|
|
@ -9,4 +9,4 @@ This keyboard is used only to test the crash reporting.
|
|||
5. Type **Sentry force now**
|
||||
- With each keystroke, Console messages should indicate that the character is being added to the Easter egg string.
|
||||
- When the final **w** is typed, a Console message should indicate: **Forcing crash now!**
|
||||
- After some delay (you may need to switch Keyman off and on again in order for the report to upload), the new crash should appear in the console on sentry.keyman.com.
|
||||
- After some delay (you may need to switch Keyman off and on again in order for the report to upload), the new crash should appear in the console on Sentry.
|
||||
|
|
|
|||
|
|
@ -135,9 +135,9 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
|
|||
KMManager.setNumericLayer(KeyboardType.KEYBOARD_TYPE_SYSTEM);
|
||||
}
|
||||
|
||||
// Temporarily disable predictions if entering a hidden password field
|
||||
KMManager.setMayPredictOverride(inputType);
|
||||
if (KMManager.getMayPredictOverride()) {
|
||||
// Temporarily disable predictions on certain fields (e.g. hidden password field or numeric)
|
||||
KMManager.setPredictionsSuspended(inputType, KeyboardType.KEYBOARD_TYPE_SYSTEM);
|
||||
if (KMManager.getPredictionsSuspended(KeyboardType.KEYBOARD_TYPE_SYSTEM)) {
|
||||
KMManager.setBannerOptions(false);
|
||||
} else if (KMManager.isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM)){
|
||||
// Check if predictions needs to be re-enabled per Settings preference
|
||||
|
|
|
|||
|
|
@ -15,5 +15,6 @@ builder_parse "$@"
|
|||
|
||||
if builder_start_action test; then
|
||||
./build/test/test.sh
|
||||
./build/publish-minimum-versions.sh test
|
||||
builder_finish_action success test
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ KEYMAN_MIN_VERSION_ANDROID_SDK=21
|
|||
KEYMAN_DEFAULT_VERSION_UBUNTU_CONTAINER=noble # Ubuntu 24.04 Noble
|
||||
|
||||
# Data versions -- see resources/standards-data/readme.md
|
||||
KEYMAN_VERSION_CLDR=45 # LDML Keyboards version
|
||||
KEYMAN_VERSION_ISO639_3=2024-05-22 # Date of last import
|
||||
KEYMAN_VERSION_LANGTAGS=2025-02-18 # Date of last import
|
||||
KEYMAN_VERSION_LANGUAGE_SUBTAG_REGISTRY=2024-05-16 # Date of last import
|
||||
KEYMAN_VERSION_UNICODE=16.0.0 # UCD + related data
|
||||
KEYMAN_VERSION_CLDR=45 # LDML Keyboards version
|
||||
KEYMAN_VERSION_ISO639_3=2024-05-22 # Date of last import
|
||||
KEYMAN_VERSION_LANGTAGS=2025-02-18 # Date of last import
|
||||
KEYMAN_VERSION_LANGUAGE_SUBTAG_REGISTRY=2025-03-10 # Date from first line of language-subtag-registry
|
||||
KEYMAN_VERSION_UNICODE=16.0.0 # UCD + related data
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -u
|
||||
|
||||
#
|
||||
# Usage: publish-minimum-versions.sh
|
||||
# Usage: ./publish-minimum-versions.sh build test
|
||||
#
|
||||
# Parses minimum-versions.inc.sh to update template file /docs/minimum-versions.md.in.
|
||||
# If /docs/minimum-versions.md is changed, commit and push changes
|
||||
|
|
@ -13,12 +8,17 @@ set -u
|
|||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "${THIS_SCRIPT%/*}/build-utils.sh"
|
||||
. "${THIS_SCRIPT%/*}/builder.inc.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
. "${KEYMAN_ROOT}/resources/build/minimum-versions.inc.sh"
|
||||
|
||||
function generate_table {
|
||||
builder_describe "Build and test minimum-versions" \
|
||||
clean build test
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
generate_table() {
|
||||
echo ""
|
||||
echo "## minimum-versions.inc.sh Variables"
|
||||
echo ""
|
||||
|
|
@ -37,5 +37,34 @@ function generate_table {
|
|||
echo "> ### This file is auto-generated by publish-minimum-versions.sh. Do not modify"
|
||||
}
|
||||
|
||||
cp "$KEYMAN_ROOT/docs/minimum-versions.md.in" "$KEYMAN_ROOT/docs/minimum-versions.md"
|
||||
generate_table >> "$KEYMAN_ROOT/docs/minimum-versions.md"
|
||||
do_build() {
|
||||
cp "$KEYMAN_ROOT/docs/minimum-versions.md.in" "$KEYMAN_ROOT/docs/minimum-versions.md"
|
||||
generate_table >> "$KEYMAN_ROOT/docs/minimum-versions.md"
|
||||
}
|
||||
|
||||
do_test() {
|
||||
# Verify that minimum-versions.md matches the generated data
|
||||
|
||||
local new_table="$(mktemp)"
|
||||
cp "$KEYMAN_ROOT/docs/minimum-versions.md.in" "$new_table"
|
||||
generate_table >> "$new_table"
|
||||
diff --suppress-common-lines -yw "$KEYMAN_ROOT/docs/minimum-versions.md" "$new_table" || ( \
|
||||
rm "$new_table"
|
||||
builder_die "minimum-versions.md is not up to date. Please run 'publish-minimum-versions.sh build'"
|
||||
)
|
||||
|
||||
rm "$new_table"
|
||||
|
||||
# TODO: this should have a test for each of the known machine-extractable versions.
|
||||
|
||||
# language-subtag-registry
|
||||
|
||||
local NEW_KEYMAN_VERSION_LANGUAGE_SUBTAG_REGISTRY="$(head ../standards-data/language-subtag-registry/language-subtag-registry -n 1 | cut -d" " -f 2 -)"
|
||||
if [[ "$NEW_KEYMAN_VERSION_LANGUAGE_SUBTAG_REGISTRY" != "$KEYMAN_VERSION_LANGUAGE_SUBTAG_REGISTRY" ]]; then
|
||||
builder_die "language-subtag-registry is version '$NEW_KEYMAN_VERSION_LANGUAGE_SUBTAG_REGISTRY' but minimum-version.inc.sh has version '$KEYMAN_VERSION_LANGUAGE_SUBTAG_REGISTRY'"
|
||||
fi
|
||||
}
|
||||
|
||||
builder_run_action clean rm -f "$KEYMAN_ROOT/docs/minimum-versions.md"
|
||||
builder_run_action build do_build
|
||||
builder_run_action test do_test
|
||||
|
|
@ -31,7 +31,7 @@ and are named e.g. `keyman-core-ci:default`.
|
|||
The versions can be changed, e.g.
|
||||
|
||||
```shell
|
||||
resources/docker-images/build.sh --ubuntu-version jammy --node 20
|
||||
resources/docker-images/build.sh --distro-version jammy --node 20
|
||||
```
|
||||
|
||||
This will create an image named e.g. `keyman-core-ci:jammy-node20`.
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ LABEL org.opencontainers.image.authors="SIL Global."
|
|||
LABEL org.opencontainers.image.url="https://github.com/keymanapp/keyman.git"
|
||||
LABEL org.opencontainers.image.title="Keyman Build Base Image"
|
||||
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
# We will switch to a build user after some installation
|
||||
USER root
|
||||
ENV HOME=/home/build
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
################################ Main script ################################
|
||||
|
||||
. "${KEYMAN_ROOT}/resources/build/minimum-versions.inc.sh"
|
||||
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
|
||||
. "${KEYMAN_ROOT}/resources/shellHelperFunctions.sh"
|
||||
. "${KEYMAN_ROOT}/resources/docker-images/docker-build.inc.sh"
|
||||
|
||||
builder_describe \
|
||||
"Build docker images" \
|
||||
|
|
@ -18,7 +19,8 @@ builder_describe \
|
|||
":core" \
|
||||
":linux" \
|
||||
":web" \
|
||||
"--distro=DISTRO The distribution to use for the base image (debian or ubuntu, default: ubuntu)" \
|
||||
"--distro=DISTRO The distribution to use for the base image "\
|
||||
" (debian or ubuntu, default: ubuntu)" \
|
||||
"--distro-version=DISTRO_VERSION The Ubuntu/Debian version (default: ${KEYMAN_DEFAULT_VERSION_UBUNTU_CONTAINER})" \
|
||||
"--no-cache Force rebuild of docker images" \
|
||||
"build Build docker images" \
|
||||
|
|
@ -97,7 +99,7 @@ build_action() {
|
|||
docker build ${OPTION_NO_CACHE:-} -t "keymanapp/keyman-${platform}-ci:${build_version}" "${build_args[@]}" .
|
||||
# If the user didn't specify particular versions we will additionaly create an image
|
||||
# with the tag 'default'.
|
||||
if _is_default_values; then
|
||||
if is_default_values; then
|
||||
builder_echo debug "Setting default tag for ${platform}"
|
||||
docker build -t "keymanapp/keyman-${platform}-ci:default" "${build_args[@]}" .
|
||||
fi
|
||||
|
|
@ -119,7 +121,6 @@ _convert_parameters_to_build_args
|
|||
|
||||
if builder_has_action build; then
|
||||
build_action base
|
||||
BASE_VERSION="${build_version}"
|
||||
builder_run_action build:android build_action android
|
||||
builder_run_action build:core build_action core
|
||||
builder_run_action build:linux build_action linux
|
||||
|
|
|
|||
61
resources/docker-images/docker-build.inc.sh
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# shellcheck shell=bash
|
||||
# no hashbang for .inc.sh
|
||||
|
||||
. "${KEYMAN_ROOT}/resources/shellHelperFunctions.sh"
|
||||
|
||||
_add_build_args() {
|
||||
local var=$1
|
||||
local default_var=$2
|
||||
local name=$3
|
||||
local value
|
||||
|
||||
value="${!var:=${!default_var:-}}"
|
||||
|
||||
build_args+=(--build-arg="${var}=${value}")
|
||||
|
||||
if [[ -n "${build_version:-}" ]]; then
|
||||
build_version="${build_version}-${name:-}${value}"
|
||||
else
|
||||
build_version="${name}${value}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Convert the parameters passed in to the script to build arguments
|
||||
# that form the docker image name
|
||||
# Returns:
|
||||
# build_args: array of build arguments
|
||||
# build_version: version string of the docker image
|
||||
convert_parameters_to_args() {
|
||||
build_args=()
|
||||
build_version=
|
||||
local required_node_version keyman_default_distro
|
||||
# shellcheck disable=SC2034
|
||||
required_node_version="$(_print_expected_node_version)"
|
||||
# shellcheck disable=SC2034
|
||||
keyman_default_distro="ubuntu"
|
||||
|
||||
_add_build_args DISTRO keyman_default_distro ""
|
||||
_add_build_args DISTRO_VERSION KEYMAN_DEFAULT_VERSION_UBUNTU_CONTAINER ""
|
||||
_add_build_args JAVA_VERSION KEYMAN_VERSION_JAVA java
|
||||
_add_build_args REQUIRED_NODE_VERSION required_node_version node
|
||||
_add_build_args REQUIRED_EMSCRIPTEN_VERSION KEYMAN_MIN_VERSION_EMSCRIPTEN emsdk
|
||||
|
||||
if [[ -n "${BASE_VERSION:-}" ]]; then
|
||||
build_args+=(--build-arg="BASE_VERSION=${BASE_VERSION}")
|
||||
else
|
||||
build_args+=(--build-arg="BASE_VERSION=${build_version}")
|
||||
fi
|
||||
}
|
||||
|
||||
check_for_default_values() {
|
||||
if [[ -z "${DISTRO_VERSION:-}" ]] && [[ -z "${JAVA_VERSION:-}" ]]; then
|
||||
is_default_values=true
|
||||
else
|
||||
is_default_values=false
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if a specific Ubuntu or Java version is specified
|
||||
is_default_values() {
|
||||
${is_default_values}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
. "${KEYMAN_ROOT}/resources/build/minimum-versions.inc.sh"
|
||||
. "${KEYMAN_ROOT}/resources/docker-images/docker-build.inc.sh"
|
||||
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
|
||||
|
||||
################################ Main script ################################
|
||||
|
|
@ -22,6 +23,17 @@ builder_describe \
|
|||
|
||||
builder_parse "$@"
|
||||
|
||||
check_for_default_values
|
||||
convert_parameters_to_args
|
||||
|
||||
if is_default_values; then
|
||||
image_version=default
|
||||
build_dir=default
|
||||
else
|
||||
image_version=${build_version:-}
|
||||
build_dir="${DISTRO:-}-${DISTRO_VERSION:-}"
|
||||
fi
|
||||
|
||||
run_android() {
|
||||
docker run -it --rm -v "${KEYMAN_ROOT}":/home/build/build \
|
||||
-v "${KEYMAN_ROOT}/core/build/docker-core/${build_dir}":/home/build/build/core/build \
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
File-Date: 2024-05-16
|
||||
File-Date: 2025-03-10
|
||||
%%
|
||||
Type: language
|
||||
Subtag: aa
|
||||
|
|
@ -5950,6 +5950,7 @@ Added: 2009-07-29
|
|||
%%
|
||||
Type: language
|
||||
Subtag: bql
|
||||
Description: Karian
|
||||
Description: Bilakura
|
||||
Added: 2009-07-29
|
||||
%%
|
||||
|
|
@ -9083,6 +9084,7 @@ Scope: collection
|
|||
%%
|
||||
Type: language
|
||||
Subtag: daz
|
||||
Description: Moi-Wadea
|
||||
Description: Dao
|
||||
Added: 2009-07-29
|
||||
%%
|
||||
|
|
@ -9290,6 +9292,8 @@ Type: language
|
|||
Subtag: dek
|
||||
Description: Dek
|
||||
Added: 2009-07-29
|
||||
Deprecated: 2024-12-12
|
||||
Preferred-Value: sqm
|
||||
%%
|
||||
Type: language
|
||||
Subtag: del
|
||||
|
|
@ -14082,6 +14086,12 @@ Added: 2009-07-29
|
|||
Macrolanguage: hmn
|
||||
%%
|
||||
Type: language
|
||||
Subtag: hnm
|
||||
Description: Hainanese
|
||||
Added: 2024-12-12
|
||||
Macrolanguage: zh
|
||||
%%
|
||||
Type: language
|
||||
Subtag: hnn
|
||||
Description: Hanunoo
|
||||
Added: 2009-07-29
|
||||
|
|
@ -21081,6 +21091,12 @@ Description: Laua
|
|||
Added: 2009-07-29
|
||||
%%
|
||||
Type: language
|
||||
Subtag: luh
|
||||
Description: Leizhou Chinese
|
||||
Added: 2024-12-12
|
||||
Macrolanguage: zh
|
||||
%%
|
||||
Type: language
|
||||
Subtag: lui
|
||||
Description: Luiseno
|
||||
Added: 2005-10-16
|
||||
|
|
@ -22850,6 +22866,8 @@ Added: 2009-07-29
|
|||
%%
|
||||
Type: language
|
||||
Subtag: mmi
|
||||
Description: Hember Avu
|
||||
Description: Amben
|
||||
Description: Musar
|
||||
Added: 2009-07-29
|
||||
%%
|
||||
|
|
@ -26641,6 +26659,8 @@ Type: language
|
|||
Subtag: nte
|
||||
Description: Nathembo
|
||||
Added: 2009-07-29
|
||||
Deprecated: 2024-12-12
|
||||
Preferred-Value: eko
|
||||
%%
|
||||
Type: language
|
||||
Subtag: ntg
|
||||
|
|
@ -32147,6 +32167,12 @@ Description: Sajau Basap
|
|||
Added: 2009-07-29
|
||||
%%
|
||||
Type: language
|
||||
Subtag: sjc
|
||||
Description: Shaojiang Chinese
|
||||
Added: 2024-12-12
|
||||
Macrolanguage: zh
|
||||
%%
|
||||
Type: language
|
||||
Subtag: sjd
|
||||
Description: Kildin Sami
|
||||
Added: 2009-07-29
|
||||
|
|
@ -41302,6 +41328,11 @@ Description: Aluo
|
|||
Added: 2009-07-29
|
||||
%%
|
||||
Type: language
|
||||
Subtag: ynb
|
||||
Description: Yamben
|
||||
Added: 2025-02-06
|
||||
%%
|
||||
Type: language
|
||||
Subtag: ynd
|
||||
Description: Yandruwandha
|
||||
Added: 2009-07-29
|
||||
|
|
@ -43616,6 +43647,14 @@ Preferred-Value: hks
|
|||
Prefix: sgn
|
||||
%%
|
||||
Type: extlang
|
||||
Subtag: hnm
|
||||
Description: Hainanese
|
||||
Added: 2024-12-12
|
||||
Preferred-Value: hnm
|
||||
Prefix: zh
|
||||
Macrolanguage: zh
|
||||
%%
|
||||
Type: extlang
|
||||
Subtag: hos
|
||||
Description: Ho Chi Minh City Sign Language
|
||||
Added: 2009-07-29
|
||||
|
|
@ -43958,6 +43997,14 @@ Prefix: lv
|
|||
Macrolanguage: lv
|
||||
%%
|
||||
Type: extlang
|
||||
Subtag: luh
|
||||
Description: Leizhou Chinese
|
||||
Added: 2024-12-12
|
||||
Preferred-Value: luh
|
||||
Prefix: zh
|
||||
Macrolanguage: zh
|
||||
%%
|
||||
Type: extlang
|
||||
Subtag: lvs
|
||||
Description: Standard Latvian
|
||||
Added: 2010-03-11
|
||||
|
|
@ -44393,6 +44440,14 @@ Prefix: ar
|
|||
Macrolanguage: ar
|
||||
%%
|
||||
Type: extlang
|
||||
Subtag: sjc
|
||||
Description: Shaojiang Chinese
|
||||
Added: 2024-12-12
|
||||
Preferred-Value: sjc
|
||||
Prefix: zh
|
||||
Macrolanguage: zh
|
||||
%%
|
||||
Type: extlang
|
||||
Subtag: slf
|
||||
Description: Swiss-Italian Sign Language
|
||||
Added: 2009-07-29
|
||||
|
|
@ -44844,6 +44899,11 @@ Description: Bangla
|
|||
Added: 2005-10-16
|
||||
%%
|
||||
Type: script
|
||||
Subtag: Berf
|
||||
Description: Beria Erfe
|
||||
Added: 2025-02-06
|
||||
%%
|
||||
Type: script
|
||||
Subtag: Bhks
|
||||
Description: Bhaiksuki
|
||||
Added: 2015-07-24
|
||||
|
|
@ -47919,6 +47979,12 @@ Comments: Written standard developed by Romanilha in 1853 and used by
|
|||
dóu Po, Escolo Gaston Febus, and others
|
||||
%%
|
||||
Type: variant
|
||||
Subtag: hanoi
|
||||
Description: The Hà Nội variant of Vietnamese
|
||||
Added: 2025-03-10
|
||||
Prefix: vi
|
||||
%%
|
||||
Type: variant
|
||||
Subtag: hepburn
|
||||
Description: Hepburn romanization
|
||||
Added: 2009-10-01
|
||||
|
|
@ -47949,6 +48015,12 @@ Added: 2017-03-14
|
|||
Prefix: eo
|
||||
%%
|
||||
Type: variant
|
||||
Subtag: huett
|
||||
Description: The Huế (province Thừa Thiên) variant of Vietnamese
|
||||
Added: 2025-03-10
|
||||
Prefix: vi
|
||||
%%
|
||||
Type: variant
|
||||
Subtag: ijekavsk
|
||||
Description: Serbian with Ijekavian pronunciation
|
||||
Prefix: sr
|
||||
|
|
@ -47991,6 +48063,16 @@ Added: 2008-10-14
|
|||
Prefix: kw
|
||||
%%
|
||||
Type: variant
|
||||
Subtag: kleinsch
|
||||
Description: Kleinschmidt orthography
|
||||
Description: Allattaasitaamut
|
||||
Added: 2024-07-20
|
||||
Prefix: kl
|
||||
Prefix: kl-tunumiit
|
||||
Comments: Orthography for Greenlandic designed by Samuel Kleinschmidt,
|
||||
used from 1851 to 1973.
|
||||
%%
|
||||
Type: variant
|
||||
Subtag: kociewie
|
||||
Description: The Kociewie dialect of Polish
|
||||
Added: 2014-11-27
|
||||
|
|
@ -48009,7 +48091,16 @@ Type: variant
|
|||
Subtag: laukika
|
||||
Description: Classical Sanskrit
|
||||
Added: 2010-07-28
|
||||
Deprecated: 2024-06-08
|
||||
Prefix: sa
|
||||
Comments: Preferred tag is cls
|
||||
%%
|
||||
Type: variant
|
||||
Subtag: leidentr
|
||||
Description: Ancient Egyptian in Leiden Unified Transliteration
|
||||
Added: 2025-02-06
|
||||
Prefix: egy
|
||||
Comments: Recommended by the International Association of Egyptologists
|
||||
%%
|
||||
Type: variant
|
||||
Subtag: lemosin
|
||||
|
|
@ -48056,6 +48147,19 @@ Comments: Russian orthography as established by the 1917/1918
|
|||
orthographic reforms
|
||||
%%
|
||||
Type: variant
|
||||
Subtag: mdcegyp
|
||||
Description: Ancient Egyptian hieroglyphs encoded in Manuel de Codage
|
||||
Added: 2025-02-06
|
||||
Prefix: egy
|
||||
%%
|
||||
Type: variant
|
||||
Subtag: mdctrans
|
||||
Description: Ancient Egyptian transliteration encoded in Manuel de
|
||||
Codage
|
||||
Added: 2025-02-06
|
||||
Prefix: egy
|
||||
%%
|
||||
Type: variant
|
||||
Subtag: metelko
|
||||
Description: Slovene in Metelko alphabet
|
||||
Added: 2012-06-27
|
||||
|
|
@ -48243,6 +48347,12 @@ Prefix: rm
|
|||
Comments: Supraregional Romansh written standard
|
||||
%%
|
||||
Type: variant
|
||||
Subtag: saigon
|
||||
Description: The Sài Gòn variant of Vietnamese
|
||||
Added: 2025-03-10
|
||||
Prefix: vi
|
||||
%%
|
||||
Type: variant
|
||||
Subtag: scotland
|
||||
Description: Scottish Standard English
|
||||
Added: 2007-08-31
|
||||
|
|
@ -48385,9 +48495,11 @@ Type: variant
|
|||
Subtag: vaidika
|
||||
Description: Vedic Sanskrit
|
||||
Added: 2010-07-28
|
||||
Deprecated: 2024-06-08
|
||||
Prefix: sa
|
||||
Comments: The most ancient dialect of Sanskrit used in verse and prose
|
||||
composed until about the 4th century B.C.E.
|
||||
Comments: Preferred tag is vsn
|
||||
%%
|
||||
Type: variant
|
||||
Subtag: valbadia
|
||||
|
|
|
|||
72
web/docs/internal/context-state-management.md
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# Context State Management
|
||||
|
||||
## The `OutputTarget` Abstraction
|
||||
|
||||
The `OutputTarget` abstraction and its associated types and classes exist to facilitate handling different types of context sources within Keyman Engine for Web through a common interface. In essence, any implementing type is valid within the engine as a "target" for "output" from any existing keyboard supported by the engine. Through 18.0, only JS-based keyboards were supported due to lack of implementation of alternate keystroke-processing engines.
|
||||
|
||||
At the most basic level, the abstraction is defined at [web/src/engine/keyboard/outputTarget.interface.ts](https://github.com/keymanapp/keyman/blob/master/web/src/engine/keyboard/src/outputTarget.interface.ts). The methods defined therein support properties and methods for fetching, setting, and manipulating deadkey markers, text-selection, and text within whatever context source it represents. JS-keyboard keystroke processing directly uses these methods during operation.
|
||||
- `epic/web-core` note: moved to `outputTarget.ts` in the same folder, dropping the `.interface` component.
|
||||
|
||||
The base implementation for this type may be found at [web/src/engine/js-processor/src/outputTarget.ts](https://github.com/keymanapp/keyman/blob/master/web/src/engine/js-processor/src/outputTarget.ts). This implementation provides common support for deadkey tracking, matching, and handling - the same strategy is used regardless of context-source type. A few additional methods are provided to assist with comparison and contrast between two different context states and restoration of a prior context state.
|
||||
- `epic/web-core` note: renamed `OutputTargetBase`, moved to `outputTargetBase.ts` in the same folder
|
||||
|
||||
### Deadkey management
|
||||
|
||||
Specifics for the implementation of JS-keyboard deadkeys can be found here: https://github.com/keymanapp/keyman/blob/b4df4ab80862bc90da42bcdbd333df0a14da01ca/web/src/engine/js-processor/src/deadkeys.ts#L2-L6
|
||||
- `epic/web-core` note: unaltered.
|
||||
|
||||
`ordinal`: if two deadkeys are in the same "virtual" position, this field resolves which came first.
|
||||
`matched`: used during keyboard-rule processing.
|
||||
|
||||
Note that we do NOT actually insert the deadkeys into the raw text-context! Web's pattern here is different than that of Keyman Core.
|
||||
|
||||
## Manipulating context-states
|
||||
|
||||
Initially used to support predictive-text, the `Transform` type aims to encapsulate the minimal information necessary to transition from one context-state to another. It is both selection-agnostic and deadkey-agnostic. `Transform`s are used both by predictive-text and by the webview-embedded build of the engine in order to succinctly communicate the data needed to update context upon receiving keystrokes.
|
||||
- `Transform`'s specification may be found at [common/web/types/src/lexical-model-types.ts](https://github.com/keymanapp/keyman/blob/master/common/web/types/src/lexical-model-types.ts).
|
||||
|
||||
`Transform`s consist of three values:
|
||||
- `deleteLeft` - the number of codepoints prior to the caret/selection that should be deleted
|
||||
- `insert` - the text to insert at the caret and/or replace currently-selected text
|
||||
- `deleteRight` - the number of codepoints _after_ the caret/selection that should be deleted
|
||||
- Note that `deleteRight` does not currently see actual use due to iOS platform limitations.
|
||||
|
||||
This type may then be used as an argument to `OutputTarget.apply()` (defined on `OutputTarget` (`epic/web-core`: `OutputTargetBase`)) to update any context source accordingly.
|
||||
|
||||
It is possible to determine the `Transform` needed to transition from one `OutputTarget` to another using `OutputTarget.buildTransformFrom` (defined on `OutputTarget` (`epic/web-core`: `OutputTargetBase`)).
|
||||
|
||||
### The `Mock` - representing context-state
|
||||
|
||||
The comparison and contrast methods mentioned above for `OutputTarget` are of particular use for predictive text, which usually operates with a headless implementation of the type, termed a `Mock`. This class may be found in [web/src/engine/js-processor/src/mock.ts](https://github.com/keymanapp/keyman/blob/master/web/src/engine/js-processor/src/mock.ts). It is a fully-featured implementation of the `OutputTarget` interface.
|
||||
- `epic/web-core` note: unaltered.
|
||||
|
||||
It is possible to make a `Mock`-based clone of any `OutputTarget`-derived type - a feature leveraged significantly within the inner workings of Keyman Engine for Web. As JS keyboards can have side effects beyond text-manipulation, predictive text generally operates by first _cloning_ the "true" context source. `Mock`s are also used when saving context states within the engine for later reference and/or reuse - a feature also utilized significantly for multitap support.
|
||||
|
||||
`Mock`s can also easily be constructed from scratch for a simple string. Optionally, caret position or selection data may be specified at construction time as well. `epic/web-core`: in theory, this should make them easy to utilize for integration with Keyman Core.
|
||||
|
||||
### The `Transcription` - representing context-state transitions
|
||||
|
||||
The `Transcription` class (defined within [web/src/engine/js-processor/src/outputTarget.ts](https://github.com/keymanapp/keyman/blob/master/web/src/engine/js-processor/src/outputTarget.ts)) is the type within the engine that most closely matches a sense of transition in context state. These are generally constructed by comparing two `OutputTarget` instances to each other via `OutputTarget.buildTranscriptionFrom`, with the base instance corresponding to the "new" state and the first parameter matching the original state before transition.
|
||||
- `epic/web-core` note: moved to `outputTargetBase.ts` in the same folder; the method is on `OutputTargetBase`.
|
||||
|
||||
`Transcription`s are granted unique identifiers and are used within the engine for preservation of recent context states. These identifiers are currently generated within the class's constructor and are internally set. When predictive-text generates new suggestions or a multitap needs to revert to a prior context, both will use the `Transcription`'s unique identifier in order to find the corresponding context state and leverage it as needed for their operations.
|
||||
|
||||
Important fields:
|
||||
- `keystroke` - the keystroke that triggered the context change corresponding to this `Transcription`
|
||||
- `transform` - the direct effects of the keystroke
|
||||
- This uses a specialized variant that also notes if the transition destroyed previously-existing selected text.
|
||||
- `preInput` - the state of the context immediately before the keystroke was processed
|
||||
|
||||
Note that the transition metadata does not include deadkeys generated by its triggering `keystroke`. The decision was made long ago to forgo directly recording deadkey changes when recording `Transcriptions`, as any operation that restores an old context also seeks to apply deadkey-destroying operations immediately afterward. Should we ever need to do so, PR #1611 contains code that was originally designed for actively detecting and recording deadkey transition data in `Transcription`s.
|
||||
|
||||
A cache of recent context-state transitions is stored at `keyman.core.contextCache`, with `keyman.core` being an instance of `InputProcessor` ([web/src/engine/main/src/headless/inputProcessor.ts](https://github.com/keymanapp/keyman/blob/master/web/src/engine/main/src/headless/inputProcessor.ts)), the component responsible for linking keystroke processing with predictive-text support and restoration of context-state for multitap-generated keystrokes.
|
||||
|
||||
### JS-keyboard keystroke processing
|
||||
|
||||
For JS-keyboard keystroke processing, a `Mock` clone of the context is generated before any actual keyboard rule checks are applied. This provides a clear "before" state (eventually saved at `Transcription.preInput`) useful for determining the scope of the keystroke's changes once processing is completed via `buildTranscriptionFrom`.
|
||||
|
||||
Once keystroke processing is completed by a JS-keyboard, the JS-processor constructs a `RuleBehavior` object describing all primary and side effects of the keystroke. Defined at [web/src/engine/js-processor/src/ruleBehavior.ts](https://github.com/keymanapp/keyman/blob/master/web/src/engine/js-processor/src/ruleBehavior.ts), all of its fields aside from `transcription` are specific to JS-keyboard side effects, some of which do need special handling and support outside of the keystroke processor. None of these side effects apply for common-case keystrokes and so have default handling in place within the engine for cases where they are not needed.
|
||||
|
||||
---
|
||||
|
||||
In case referenced classes/files have moved: this doc was last updated in 18.0-beta, based upon commit d1e45a0df49f81597887577a8c83af36d4a85283
|
||||
81
web/docs/internal/keystroke-processing.md
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# Keystroke Processing
|
||||
|
||||
In addition to handling keystroke events produced from hardware keyboards, Keyman Engine for Web manages an on-screen keyboard that can also be used to generate such events. This document will enumerate the various components of data management and control flow used to handle and process keystrokes.
|
||||
|
||||
## Input: KeyEvent - the pre-processed keystroke
|
||||
|
||||
Defined at [web/src/engine/keyboard/src/keyEvent.ts](https://github.com/keymanapp/keyman/blob/master/web/src/engine/keyboard/src/keyEvent.ts), the `KeyEvent` is used to represent incoming _and_ potential keystrokes. This type is what the JS-keyboard processor references when evaluating keyboard rules during its keystroke processing. For versions of the engine that support AltGr aliasing, such aliasing will be applied during generation of `KeyEvent` objects.
|
||||
|
||||
Note that for predictive-text's fat-finger correction functionality, the engine will also generate versions of this type for nearby but _unpressed_ keys into the keystroke processing engine as well in order to facilitate predictions that follow context manipulations that could have resulted from specialized rules or reorders on neighboring keys. `Mock`-cloned copies of the active context-state will be leveraged to prevent unwanted manipulation of the true context source.
|
||||
|
||||
### `isSynthetic`
|
||||
`isSynthetic` should be set to `true` if generated through interaction with an on-screen-keyboard or for fat-finger simulation. It should only be set to `false` if sending the basic key-event data through to the destination, without rule processing, leads to default handling picking up the slack. Browsers provide default handling of keystrokes not directly defined within keyboards, but this is not available for keystrokes against the engine's OSK without internal support.
|
||||
|
||||
For other applications of `false`, consider a hardware K_ENTER keystroke - this could be interpreted as a "submit form" / "search" command depending on the context.
|
||||
|
||||
When `true`, the keystroke processor will need to simulate the results of "standard" default key-event handling, as nothing else can handle it. At present, we don't attempt to force / synthesize an event that could trigger the "true" default.
|
||||
|
||||
### `on('keyevent')`
|
||||
|
||||
On-screen keyboard modules and hardware-keyboard event interception modules will produce instances of `KeyEvent` as noted above. Once this is completed, both module types will _signal_ the keystroke event by raising an event with ID `keyevent` and provide the `KeyEvent` instance as its first parameter. The main module of the engine will then forward that `KeyEvent` object to the input processing pipeline via `InputProcessor.processKeyEvent` (see below).
|
||||
|
||||
## Output: RuleBehavior
|
||||
|
||||
Once keystroke processing is completed, the outward-facing components of the engine expect to receive a `RuleBehavior` object describing all primary and side effects of the keystroke. Defined at [web/src/engine/js-processor/src/ruleBehavior.ts](https://github.com/keymanapp/keyman/blob/master/web/src/engine/js-processor/src/ruleBehavior.ts), all of its fields aside from `transcription` are specific to existing JS-keyboard side effects. (Certain Keyman language features can make permanent side-effect changes to state that should only be persisted for the "true" keystroke - not for any predictive-text 'alternative' keys.)
|
||||
|
||||
See also: [context-state-management.md](context-state-management.md#js-keyboard-keystroke-processing)
|
||||
|
||||
## Processing Pipeline
|
||||
|
||||
### Input Processor
|
||||
|
||||
The "first stop" for incoming keystrokes is the `InputProcessor`, found at [web/src/engine/main/src/headless/inputProcessor.ts](https://github.com/keymanapp/keyman/blob/master/web/src/engine/main/src/headless/.inputProcessor.ts), through its `processKeyEvent` method. This class manages higher-level functionality triggered by keystroke events while deferring actual interpretation of the incoming keystroke further down the line. Of particular note is that it also handles control-flows that require restoration of previously-occuring contexts.
|
||||
|
||||
This class is the connection point for generating prediction requests and receiving corresponding suggestions. In order to facilitate higher-quality predictive-text when enabled, the `InputProcessor` will _also_ generate and trigger processing for nearby keys. This process allows transforms, reorders, and KMN keyboard rules to take effect and be used as alternative context roots for predictions. These are generally run against `Mock`-based clones of the true context source and are additionally prevented from triggering long-term side-effects, such as changes to KMN-keyboard variable stores, by only calling `RuleBehavior.finalize` for the true input keystroke's result object.
|
||||
|
||||
Keys generated by OSK multitap need special handling here as well; they should always be applied to the context state as it existed at the time of the initial tap. To facilitate this, the `InputProcessor` will directly rewind the active context-source to match the corresponding context state before requesting that the `KeyEvent` be processed.
|
||||
|
||||
Due to the role `InputProcessor` plays, it is intended that _all_ keystroke processing variants leverage this class in order to maintain integration with such higher-level functionality.
|
||||
|
||||
#### The "context cache"
|
||||
|
||||
As both predictive-text and multitaps require the ability to reference old context states, the `InputProcessor` maintains a cache of recently-occurring context-state transitions - of [`Transcription`](./context-state-management.md#the-transcription---representing-context-state-transitions)s. This cache is kept reasonably small to prevent memory bloat, so functionality referencing the cache should verify the results of any related lookup operation.
|
||||
|
||||
Thanks to the `Transcription.preInput`, it is possible to rewind the current context to its state at the time the `Transcription` was generated - a feature quite useful for applying predictive-text suggestions and for handling multitap keystrokes. Predictive-text reversions may even re-use cached predictions associated with the `Transcription`, avoiding the need to recompute them.
|
||||
|
||||
#### LanguageProcessor
|
||||
|
||||
The `LanguageProcessor` class, whose instances are owned within the engine by `InputProcessor`, are the anchor point for connecting rest of Keyman Engine for Web with the predictive-text web-worker and its functionalities.
|
||||
|
||||
### KeyboardProcessor
|
||||
- `epic/web-core` note: renamed to `JSKeyboardProcessor`.
|
||||
|
||||
Found within [web/src/engine/js-processor/src/keyboardProcessor.ts](https://github.com/keymanapp/keyman/blob/master/web/src/engine/js-processor/src/keyboardProcessor.ts), the `KeyboardProcessor` class is the top-level class responsible for processing keystrokes targeting JS-based keyboards. It is also responsible for providing default output for OSK-based keystrokes not explicitly handled by the keyboard that should receive browser-default handling (when JS-based keyboards are active).
|
||||
- `epic/web-core` note: renamed to `JSKeyboardProcessor`, moved to `jsKeyboardProcessor.ts` within the same folder.
|
||||
|
||||
This is likely the top-level class that `epic/web-core` would likely need to replace wholesale with a Keyman Core wrapper - hopefully with a mutual abstraction providing a similar interface for both. (`KeyboardProcessor` was originally named after Core's equivalent class, as it pretty much fills the same role.) `InputProcessor` could then leverage that mutual abstraction for its functions without needing a specialized version per keystroke interpreting engine.
|
||||
|
||||
#### The `processKeystroke` method
|
||||
|
||||
https://github.com/keymanapp/keyman/blob/b4df4ab80862bc90da42bcdbd333df0a14da01ca/common/web/keyboard-processor/src/text/keyboardProcessor.ts#L217
|
||||
|
||||
The method linked above is the primary entrypoint for rule processing of individual, preprocessed `KeyEvent`s. There's a little infrastructure between it and the actual keyboard-script for JS keyboards, but it is the sole entry point in `KeyboardProcessor` responsible for keyboard rule evaluation.
|
||||
- `epic/web-core` note: renamed to `JSKeyboardProcessor`.
|
||||
|
||||
#### JS-keyboard interfacing
|
||||
|
||||
Certain Keyman language features can make permanent side-effect changes to state. In order to prevent these from taking place for every keystroke, the method that interfaces with JS keyboards - `KeyboardInterface.process` - saves the context state (as a `Mock`) and current variable store values, then prepares a fresh `RuleBehavior` instance, before passing control off to the keyboard's backing script. (Note that `KeyboardInterface` itself primarily consists of keyboard-script API called by JS-keyboard script.)
|
||||
- `epic/web-core` note: renamed to `JSKeyboardInterface`.
|
||||
|
||||
A few of the keyboard-script API methods will mark `RuleBehavior` properties directly when called, but the bulk of its data will be set once the keyboard-script returns control to Keyman Engine for Web. At this time, variable store values will also be reverted to prevent possible cross-contamination effects when predictive text is active - they're reapplied later if `RuleBehavior.finalize` is leveraged on the resulting instance. Components documented in [context-state-management.md](./context-state-management.md) are then leveraged to determine the total change to context caused by the keystroke.
|
||||
|
||||
Note that for JS-keyboards, in 18.0 and before the true keystroke is processed against the _true_ context state, not a cloned copy, and thus its changes are applied immediately.
|
||||
|
||||
#### Keystroke-default emulation
|
||||
|
||||
Note that browser-default keystroke processing is defined within the `DefaultRules` class found at [web/src/engine/keyboard/src/defaultRules.ts](https://github.com/keymanapp/keyman/blob/master/web/src/engine/keyboard/src/defaultRules.ts). It currently produces a (possibly `null`) string output rather than a `RuleBehavior`, but the latter can be easily constructed based on the returned string and the existing context state.
|
||||
- For `epic/web-core`, it may be wise to spin the `DefaultRules` component off as its own processor, serving as a backup for _all_ keystroke processing variants.
|
||||
|
||||
----
|
||||
|
||||
In case referenced classes/files have moved: this doc was last updated in 18.0-beta, based upon commit d1e45a0df49f81597887577a8c83af36d4a85283
|
||||
|
|
@ -92,6 +92,15 @@ export default class KeyEvent implements KeyEventSpec {
|
|||
// Holds a generated fat-finger distribution (when appropriate)
|
||||
keyDistribution?: KeyDistribution;
|
||||
|
||||
/**
|
||||
* Information provided here may be used for breadcrumbs regarding internal engine errors and warnings.
|
||||
* It should be scrubbed of info that could be used to directly reproduce what the user has typed.
|
||||
*
|
||||
* One great use is to identify scenarios such as how the current state of an OSK gesture was reached and
|
||||
* culminated in this key event.
|
||||
*/
|
||||
inputBreadcrumb?: string;
|
||||
|
||||
/**
|
||||
* The device model for web-core to follow when processing the keystroke.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -120,7 +120,9 @@ export class InputProcessor {
|
|||
if this is a purely-layer-switching multitap. (#11230)
|
||||
*/
|
||||
} else {
|
||||
console.warn('The base context for the multitap could not be found');
|
||||
console.warn('The base context for the multitap could not be found!\n\n' + this.contextCache.buildLog());
|
||||
// would be lovely to report a desire for gesture debug output
|
||||
// maybe add something to RuleBehavior?
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -242,7 +244,7 @@ export class InputProcessor {
|
|||
|
||||
// Multitaps operate in part by referencing 'committed' Transcriptions to rewind
|
||||
// the context as necessary.
|
||||
this.contextCache.save(ruleBehavior.transcription);
|
||||
this.contextCache.save(ruleBehavior.transcription); //
|
||||
|
||||
// The keyboard may want to take an action after all other keystroke processing is
|
||||
// finished, for example to switch layers. This action may not have any output
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ export class LanguageProcessor extends EventEmitter<LanguageProcessorEventMap> {
|
|||
}
|
||||
|
||||
public unloadModel() {
|
||||
if(!this.isActive) {
|
||||
if(!this.canEnable) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,4 +34,11 @@ export class TranscriptionCache {
|
|||
this.map.delete(this.map.keys().next().value);
|
||||
}
|
||||
}
|
||||
|
||||
public buildLog(): string {
|
||||
const entries = [...this.map.entries()];
|
||||
return entries
|
||||
.map(([key, entry]) => `Context state ${key}'s keystroke:\n${entry.keystroke.inputBreadcrumb ?? ''}`)
|
||||
.join('\n');
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,9 @@ export class GestureStageReport<Type, StateToken = any> {
|
|||
*/
|
||||
public readonly sources: GestureSourceSubview<Type>[];
|
||||
|
||||
public get currentSourceIds(): string[] {
|
||||
return this.sources.map((entry) => entry.identifier);
|
||||
}
|
||||
public readonly allSourceIds: string[];
|
||||
|
||||
constructor(selection: MatcherSelection<Type, StateToken>, gestureSetId: string) {
|
||||
|
|
@ -66,6 +69,32 @@ export class GestureStageReport<Type, StateToken = any> {
|
|||
|
||||
this.allSourceIds = matcher?.allSourceIds || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time interval covered by recorded touch events during this stage and its
|
||||
* matched gesture model.
|
||||
*
|
||||
* May return `undefined` if no actual touches occurred during this time.
|
||||
*/
|
||||
public get interval() {
|
||||
if(this.sources.length == 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const timestamps = this.sources.map((entry => {
|
||||
return {
|
||||
start: entry.path.stats.initialSample.t,
|
||||
end: entry.path.stats.lastTimestamp
|
||||
}
|
||||
}));
|
||||
|
||||
return timestamps.reduce((prior, current) => {
|
||||
return {
|
||||
start: prior.start > current.start ? current.start : prior.start,
|
||||
end: prior.end < current.end ? current.end : prior.end
|
||||
}
|
||||
}, { start: Number.POSITIVE_INFINITY, end: Number.NEGATIVE_INFINITY });
|
||||
}
|
||||
}
|
||||
|
||||
interface PushConfig<Type> {
|
||||
|
|
@ -83,10 +112,30 @@ interface PopConfig {
|
|||
export type ConfigChangeClosure<Type> = (configStackCommand: PushConfig<Type> | PopConfig) => void;
|
||||
|
||||
interface EventMap<Type, StateToken> {
|
||||
/**
|
||||
* This event is raised when a new, valid gesture component for the sequence has been matched and
|
||||
* committed.
|
||||
*
|
||||
* It is always called synchronously with the match, before any further matches are possible.
|
||||
* - The sole exception to the "synchronous" aspect is if this gesture enacted a 'pushed' state
|
||||
* that has active 'nested' gestures that need to be sustained.
|
||||
* - Even in the case above, no further 'stage' events for this GestureSequence will occur.
|
||||
* @param stageReport
|
||||
* @param changeConfiguration
|
||||
* @returns
|
||||
*/
|
||||
stage: (
|
||||
stageReport: GestureStageReport<Type, StateToken>,
|
||||
changeConfiguration: ConfigChangeClosure<Type>
|
||||
) => void;
|
||||
/**
|
||||
* This event is raised when the sequence of connected gesture components has reached its end,
|
||||
* with no further continuation possible.
|
||||
*
|
||||
* It is called synchronously with the end of the gesture sequence, though it may be 'kept alive'
|
||||
* when sustaining 'nested' gestures dependent upon a 'pushed' state.
|
||||
* @returns
|
||||
*/
|
||||
complete: () => void;
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +153,10 @@ export class GestureSequence<Type, StateToken = any> extends EventEmitter<EventM
|
|||
private pushedSelector?: MatcherSelector<Type, StateToken>;
|
||||
|
||||
private gestureConfig: GestureModelDefs<Type, StateToken>;
|
||||
private markedComplete: boolean = false;
|
||||
private completionTime: number;
|
||||
private get markedComplete(): boolean {
|
||||
return this.completionTime !== undefined;
|
||||
}
|
||||
|
||||
// Note: the first stage will be available under `stageReports` after awaiting a simple Promise.resolve().
|
||||
constructor(
|
||||
|
|
@ -119,6 +171,8 @@ export class GestureSequence<Type, StateToken = any> extends EventEmitter<EventM
|
|||
this.selector = selector;
|
||||
this.selector.on('rejectionwithaction', this.modelResetHandler);
|
||||
this.once('complete', () => {
|
||||
this.completionTime = performance.now();
|
||||
|
||||
if(this.pushedSelector) {
|
||||
// The `popSelector` method is responsible for triggering cascading cancellations if
|
||||
// there are nested GestureSequences.
|
||||
|
|
@ -200,6 +254,18 @@ export class GestureSequence<Type, StateToken = any> extends EventEmitter<EventM
|
|||
return potentialMatches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes newly-matched gesture stages and ensures that the corresponding state machines are
|
||||
* set in motion. Also processes the case where there is no longer a possible matching gesture stage.
|
||||
* - The gesture sources (touchpoints) will no longer be tracked and signal their paths' `complete` events.
|
||||
* - The gesture models for any potential followups to the completed stage will be initialized.
|
||||
* - If there are none, the `complete` event will be triggered.
|
||||
* - The `stage` event (when there is a matched stage) will be triggered before the corresponding `complete`.
|
||||
* - It will occur synchronously unless this sequence is kept alive, with no further stages possible and
|
||||
* pending completion, to 'sustain' nested gestures.
|
||||
* @param selection Data about the matching gesture and match type.
|
||||
* @returns
|
||||
*/
|
||||
private readonly selectionHandler = async (selection: MatcherSelection<Type, StateToken>) => {
|
||||
const gestureSet = this.pushedSelector?.baseGestureSetId || this.selector?.baseGestureSetId;
|
||||
const matchReport = new GestureStageReport<Type, StateToken>(selection, gestureSet);
|
||||
|
|
@ -222,13 +288,13 @@ export class GestureSequence<Type, StateToken = any> extends EventEmitter<EventM
|
|||
|
||||
if(!selection.result.matched) {
|
||||
if(!this.markedComplete) {
|
||||
this.markedComplete = true;
|
||||
this.emit('complete');
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Only source of async: actually completing a gesture that caused an alt-state.
|
||||
if(actionType == 'complete' && this.touchpointCoordinator && this.pushedSelector) {
|
||||
// Cascade-terminade all nested selectors, but don't remove / pop them yet.
|
||||
// Their selection mode remains valid while their gestures are sustained.
|
||||
|
|
@ -349,7 +415,6 @@ export class GestureSequence<Type, StateToken = any> extends EventEmitter<EventM
|
|||
} else {
|
||||
// Any extra finalization stuff should go here, before the event, if needed.
|
||||
if(!this.markedComplete) {
|
||||
this.markedComplete = true;
|
||||
this.emit('complete');
|
||||
}
|
||||
}
|
||||
|
|
@ -385,7 +450,6 @@ export class GestureSequence<Type, StateToken = any> extends EventEmitter<EventM
|
|||
const sources = this.stageReports[this.stageReports.length - 1].sources;
|
||||
sources.forEach((src) => src.baseSource.isPathComplete || src.baseSource.terminate(true));
|
||||
if(!this.markedComplete) {
|
||||
this.markedComplete = true;
|
||||
this.emit('complete');
|
||||
}
|
||||
}
|
||||
|
|
@ -393,6 +457,48 @@ export class GestureSequence<Type, StateToken = any> extends EventEmitter<EventM
|
|||
public toJSON(): any {
|
||||
return this.stageReports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints out a 'trace' of the gesture sequence in reverse chronological order. Each frame includes:
|
||||
* - the unique touch-identifiers active for each stage
|
||||
* - the matched gesture ID
|
||||
* - the time interval spanned by the frame (if at least one touch was active during it)
|
||||
* @returns
|
||||
*/
|
||||
public trace(padding?: string): string {
|
||||
padding ||= ' ';
|
||||
|
||||
const logTouches = (idArr: string[]) => {
|
||||
if(idArr.length == 0) {
|
||||
return `<empty>`;
|
||||
} else if(idArr.length == 1) {
|
||||
return idArr[0];
|
||||
} else {
|
||||
return idArr.join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
const logInterval = (interval: {start: number, end: number}) => {
|
||||
if(!interval) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if(interval.start == interval.end) {
|
||||
return `@ ${interval.start.toFixed(2)} ms`;
|
||||
} else {
|
||||
return `from ${interval.start.toFixed(2)} ms - ${interval.end.toFixed(2)} ms`
|
||||
}
|
||||
};
|
||||
|
||||
// Sanitizes the actual keystrokes involved but preserves a decent amount of gesture diagnostic information.
|
||||
let logs = this.stageReports.map((entry) => padding + `${logTouches(entry.currentSourceIds)} - ${entry.matchedId} ${logInterval(entry.interval)}`)
|
||||
// Orders each stage from most recent to least recent
|
||||
.reverse();
|
||||
if(this.markedComplete) {
|
||||
logs.unshift(padding + `complete @ ${this.completionTime}`);
|
||||
}
|
||||
return logs.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
export function modelSetForAction<Type, StateToken>(
|
||||
|
|
|
|||
|
|
@ -195,8 +195,7 @@ export class TouchpointCoordinator<HoveredItemType, StateToken=any> extends Even
|
|||
*
|
||||
* This, in turn, can affect what the initial 'item' for the new gesture will be.
|
||||
*/
|
||||
const modelingSpinupPromise = potentialSelector.matchGesture(touchpoint, getGestureModelSet(modelDefs, potentialSelector.baseGestureSetId));
|
||||
const modelingSpinupResults = await modelingSpinupPromise;
|
||||
const modelingSpinupResults = await potentialSelector.matchGesture(touchpoint, getGestureModelSet(modelDefs, potentialSelector.baseGestureSetId));
|
||||
|
||||
if(modelingSpinupResults.sustainModeWithoutMatch) {
|
||||
|
||||
|
|
|
|||
|
|
@ -119,7 +119,8 @@ export default class Flick implements GestureHandler {
|
|||
let source: GestureSource<KeyElement> = baseSource;
|
||||
|
||||
sequence.on('complete', () => {
|
||||
previewHost?.cancel()
|
||||
previewHost?.cancel();
|
||||
this.baseSpec;
|
||||
});
|
||||
|
||||
this.sequence.on('stage', (result) => {
|
||||
|
|
@ -206,6 +207,7 @@ export default class Flick implements GestureHandler {
|
|||
}
|
||||
|
||||
keyEvent.keyDistribution = this.currentStageKeyDistribution(this.baseKeyDistances);
|
||||
keyEvent.inputBreadcrumb = this.sequence.trace();
|
||||
|
||||
// emit the keystroke
|
||||
vkbd.raiseKeyEvent(keyEvent, null);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ export default class Multitap implements GestureHandler {
|
|||
public readonly hasModalVisualization = false;
|
||||
|
||||
private readonly originalLayer: string;
|
||||
// Used solely for error & warning messages.
|
||||
private readonly keyboardId: string;
|
||||
|
||||
private readonly multitaps: ActiveSubKey[];
|
||||
private tapIndex = 0;
|
||||
|
|
@ -56,6 +58,7 @@ export default class Multitap implements GestureHandler {
|
|||
}
|
||||
|
||||
this.originalLayer = vkbd.layerId;
|
||||
this.keyboardId = vkbd.layoutKeyboard.id;
|
||||
|
||||
const tapLookahead = (offset: number) => (this.tapIndex + offset) % this.multitaps.length;
|
||||
|
||||
|
|
@ -93,7 +96,7 @@ export default class Multitap implements GestureHandler {
|
|||
case 'multitap-start':
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported gesture state encountered during multitap: ${tap.matchedId}`);
|
||||
throw new Error(`Unsupported gesture state encountered during multitap: \n${this.sequence.trace()}`);
|
||||
}
|
||||
|
||||
// For rota-style behavior
|
||||
|
|
@ -104,6 +107,10 @@ export default class Multitap implements GestureHandler {
|
|||
const keyEvent = vkbd.keyEventFromSpec(selection);
|
||||
keyEvent.baseTranscriptionToken = this.baseContextToken;
|
||||
|
||||
if(this.baseContextToken == null) {
|
||||
console.warn('Multitap init warning: no associated base context (with base key '+ e.key.spec.coreID + ')');
|
||||
}
|
||||
|
||||
const coord = tap.sources[0].currentSample;
|
||||
|
||||
const baseSpec = this.baseKey.key.spec;
|
||||
|
|
@ -120,7 +127,7 @@ export default class Multitap implements GestureHandler {
|
|||
// with the multitap base key.
|
||||
const p = baseDistances.get(matchKey.key.spec);
|
||||
if(p == null) {
|
||||
console.warn("Could not find current layer's key")
|
||||
console.warn(`Could not find fat-finger probability for current layer's key: current key = ${matchKey.key.spec.elementID}, current layer = ${vkbd.layerId}, keyboard = ${this.keyboardId}`);
|
||||
} else {
|
||||
baseDistances.delete(matchKey.key.spec);
|
||||
baseDistances.set(coord.item.key.spec, p);
|
||||
|
|
@ -140,6 +147,7 @@ export default class Multitap implements GestureHandler {
|
|||
// if no such 'nextLayer' is specified by default.
|
||||
keyEvent.kNextLayer ||= this.originalLayer;
|
||||
|
||||
keyEvent.inputBreadcrumb = this.sequence.trace();
|
||||
vkbd.raiseKeyEvent(keyEvent, null);
|
||||
|
||||
// Now that the key has been processed, with a layer possibly changed as a result...
|
||||
|
|
@ -182,7 +190,7 @@ export default class Multitap implements GestureHandler {
|
|||
if(keyIndex == -1) { // also covers undefined, but does not include 0.
|
||||
// Modipress keys generally get left out of the key-correction calculations.
|
||||
if(!keySupportsModipress(this.baseKey)) {
|
||||
console.warn("Could not find base key's probability for multitap correction");
|
||||
console.warn(`Could not find base key's probability for multitap correction adjustments: ${this.baseKey.key.spec.elementID}, keyboard = ${this.keyboardId}`);
|
||||
}
|
||||
|
||||
// Decently recoverable; just use the simple-tap distances instead.
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ export default class SubkeyPopup implements GestureHandler {
|
|||
const keyEvent = vkbd.keyEventFromSpec(key.key.spec);
|
||||
keyEvent.keyDistribution = this.currentStageKeyDistribution();
|
||||
|
||||
keyEvent.inputBreadcrumb = this.source.trace();
|
||||
vkbd.raiseKeyEvent(keyEvent, key);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -522,7 +522,7 @@ export function longpressContactModel(params: FullGestureParams, enabledFlicks:
|
|||
* The 'indexOf' allows 'n', 'nw', and 'ne' - approx 67.5 degrees on
|
||||
* each side of due N in total.
|
||||
*/
|
||||
if((enabledFlicks && spec.permitsFlick(stats.lastSample.item)) && (stats.cardinalDirection?.indexOf('n') != -1 ?? false)) {
|
||||
if((enabledFlicks && spec.permitsFlick(stats.lastSample.item)) && ((stats.cardinalDirection ?? '').indexOf('n') != -1)) {
|
||||
const baseDistance = stats.netDistance;
|
||||
const angle = stats.angle; // from <0, -1> (straight up) going clockwise.
|
||||
const verticalDistance = baseDistance * Math.cos(angle);
|
||||
|
|
@ -675,6 +675,9 @@ export function specialKeyEndModel(params: FullGestureParams): GestureModel<any>
|
|||
itemChangeAction: 'resolve'
|
||||
},
|
||||
endOnResolve: true,
|
||||
}, {
|
||||
model: instantContactResolutionModel(),
|
||||
resetOnInstantFulfill: true
|
||||
}
|
||||
],
|
||||
resolutionAction: {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import { KeyEventHandler, KeyEventResultCallback } from './views/keyEventSource.
|
|||
|
||||
import GlobeHint from './globehint.interface.js';
|
||||
import KeyboardView from './components/keyboardView.interface.js';
|
||||
import { type KeyElement, getKeyFrom } from './keyElement.js';
|
||||
import { type KeyElement } from './keyElement.js';
|
||||
import KeyTip from './keytip.interface.js';
|
||||
import OSKKey from './keyboard-layout/oskKey.js';
|
||||
import OSKLayer, { LayerLayoutParams } from './keyboard-layout/oskLayer.js';
|
||||
|
|
@ -597,9 +597,7 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
|
|||
|
||||
// First, if we've configured the gesture to generate a keystroke, let's handle that.
|
||||
const gestureKey = gestureStage.item;
|
||||
|
||||
const coordSource = gestureStage.sources[0];
|
||||
const coord: InputSample<KeyElement, string> = coordSource ? coordSource.currentSample : null;
|
||||
|
||||
let keyResult: KeyRuleEffects = null;
|
||||
|
||||
|
|
@ -628,7 +626,7 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
|
|||
try {
|
||||
shouldLockLayer && this.lockLayer(true);
|
||||
// Once the best coord to use for fat-finger calculations has been determined:
|
||||
keyResult = this.modelKeyClick(gestureStage.item, coord, correctionKeyDistribution);
|
||||
keyResult = this.modelKeyClick(gestureSequence, correctionKeyDistribution); // second spot for modelKeyClick
|
||||
} finally {
|
||||
shouldLockLayer && this.lockLayer(false);
|
||||
}
|
||||
|
|
@ -660,7 +658,7 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
|
|||
//
|
||||
// Merely constructing the instance is enough; it'll link into the sequence's events and
|
||||
// handle everything that remains for the backspace from here.
|
||||
handlers = [new HeldRepeater(gestureSequence, () => this.modelKeyClick(gestureKey, coord))];
|
||||
handlers = [new HeldRepeater(gestureSequence, () => this.modelKeyClick(gestureSequence))]; // site 1 for modelKeyClick
|
||||
} else if(gestureKey.key.spec.baseKeyID == "K_LOPT") { // globe key
|
||||
gestureSequence.on('complete', () => {
|
||||
gestureKey.key.highlight(false);
|
||||
|
|
@ -944,56 +942,21 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
|
|||
const correctiveLayout = buildCorrectiveLayout(this.kbdLayout.getLayer(this.layerId), kbdAspectRatio);
|
||||
return keyTouchDistances(touchKbdPos, correctiveLayout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current key target from the touch point element within the key
|
||||
*
|
||||
* @param {Object} t element at touch point
|
||||
* @return {Object} the key element (or null)
|
||||
**/
|
||||
keyTarget(target: HTMLElement | EventTarget): KeyElement {
|
||||
let t = <HTMLElement>target;
|
||||
|
||||
try {
|
||||
if (t) {
|
||||
if (t.classList.contains('kmw-key')) {
|
||||
return getKeyFrom(t);
|
||||
}
|
||||
if (t.parentNode && (t.parentNode as HTMLElement).classList.contains('kmw-key')) {
|
||||
return getKeyFrom(t.parentNode);
|
||||
}
|
||||
if (t.firstChild && (t.firstChild as HTMLElement).classList.contains('kmw-key')) {
|
||||
return getKeyFrom(t.firstChild);
|
||||
}
|
||||
}
|
||||
} catch (ex) { }
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeat backspace as long as the backspace key is held down
|
||||
**/
|
||||
repeatDelete: () => void = function (this: VisualKeyboard) {
|
||||
if (this.deleting) {
|
||||
this.modelKeyClick(this.deleteKey);
|
||||
this.deleting = window.setTimeout(this.repeatDelete, 100);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
/**
|
||||
* Cancels any active repeatDelete() timeouts, ensuring that
|
||||
* repeating backspace operations are properly terminated.
|
||||
*/
|
||||
cancelDelete() {
|
||||
// Clears the delete-repeating timeout.
|
||||
if (this.deleting) {
|
||||
window.clearTimeout(this.deleting);
|
||||
}
|
||||
this.deleting = 0;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
modelKeyClick(e: KeyElement, input?: InputSample<KeyElement, string>, keyDistribution?: KeyDistribution) {
|
||||
// Retool: take in `gestureStage` (or its index?) and the SEQUENCE; the latter lets us log gesture breadcrumbs.
|
||||
modelKeyClick(sequence: GestureSequence<KeyElement>, keyDistribution?: KeyDistribution) {
|
||||
if(!sequence) {
|
||||
throw new Error("cannot model key event without its gesture");
|
||||
}
|
||||
const stage = sequence.stageReports[sequence.stageReports.length-1];
|
||||
const e = stage.item;
|
||||
const input = stage.sources[0]?.currentSample;
|
||||
|
||||
if(!e) {
|
||||
throw new Error(`Cannot model keypress without a selected key: \n${sequence.trace()}`);
|
||||
}
|
||||
|
||||
let keyEvent = this.initKeyEvent(e);
|
||||
|
||||
if (input) {
|
||||
|
|
@ -1003,6 +966,7 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
|
|||
keyEvent.keyDistribution = keyDistribution;
|
||||
}
|
||||
|
||||
keyEvent.inputBreadcrumb = sequence.trace();
|
||||
return this.raiseKeyEvent(keyEvent, e);
|
||||
}
|
||||
|
||||
|
|
@ -1026,12 +990,9 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
|
|||
}
|
||||
|
||||
keyEventFromSpec(keySpec: ActiveKey | ActiveSubKey) {
|
||||
//let core = com.keyman.singleton.core; // only singleton-based ref currently needed here.
|
||||
|
||||
// Start: mirrors _GetKeyEventProperties
|
||||
|
||||
// First check the virtual key, and process shift, control, alt or function keys
|
||||
//let Lkc = keySpec.constructKeyEvent(core.keyboardProcessor, this.device);
|
||||
let Lkc = this.layoutKeyboard.constructKeyEvent(keySpec, this.device, this.stateKeys);
|
||||
|
||||
/* In case of "fun" edge cases caused by JS's single-threadedness & event processing queue.
|
||||
|
|
@ -1060,7 +1021,6 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
|
|||
*/
|
||||
_UpdateVKShiftStyle(layerId?: string) {
|
||||
var i;
|
||||
//let core = com.keyman.singleton.core;
|
||||
|
||||
if (!layerId) {
|
||||
layerId = this.layerId;
|
||||
|
|
@ -1305,48 +1265,12 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
|
|||
};
|
||||
}
|
||||
|
||||
// Appears to be abandoned now - candidate for removal in future.
|
||||
/*private*/ computedAdjustedOskHeight(allottedHeight: number): number {
|
||||
if (!this.layerGroup) {
|
||||
return allottedHeight;
|
||||
}
|
||||
|
||||
/*
|
||||
Note: these may not be fully preprocessed yet!
|
||||
|
||||
However, any "empty row bug" preprocessing has been applied, and that's
|
||||
what we care about here.
|
||||
*/
|
||||
const layers = this.layerGroup.spec.layer;
|
||||
let oskHeight = 0;
|
||||
|
||||
// In case the keyboard's layers have differing row counts, we check them all for the maximum needed oskHeight.
|
||||
for (const layerID in layers) {
|
||||
const layer = layers[layerID];
|
||||
let nRows = layer.row.length;
|
||||
let rowHeight = Math.floor(allottedHeight / (nRows == 0 ? 1 : nRows));
|
||||
let layerHeight = nRows * rowHeight;
|
||||
|
||||
if (layerHeight > oskHeight) {
|
||||
oskHeight = layerHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// This isn't set anywhere else; it's a legacy part of the original methods.
|
||||
const oskPad = 0;
|
||||
let oskPaddedHeight = oskHeight + oskPad;
|
||||
|
||||
return oskPaddedHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a style sheet for the current keyboard if needed for specifying an embedded font
|
||||
* or to re-apply the default element font
|
||||
*
|
||||
**/
|
||||
appendStyleSheet() {
|
||||
//let util = com.keyman.singleton.util;
|
||||
|
||||
var activeKeyboard = this.layoutKeyboard;
|
||||
var activeStub = this.layoutKeyboardProperties;
|
||||
|
||||
|
|
|
|||
|
|
@ -182,7 +182,6 @@ uses
|
|||
Keyman.System.UpdateStateMachine in 'main\Keyman.System.UpdateStateMachine.pas',
|
||||
Keyman.System.DownloadUpdate in 'main\Keyman.System.DownloadUpdate.pas',
|
||||
Keyman.System.ExecutionHistory in '..\..\..\..\common\windows\delphi\general\Keyman.System.ExecutionHistory.pas',
|
||||
Keyman.Configuration.UI.UfrmStartInstallNow in 'main\Keyman.Configuration.UI.UfrmStartInstallNow.pas' {frmInstallNow},
|
||||
Keyman.Configuration.UI.UfrmStartInstall in 'main\Keyman.Configuration.UI.UfrmStartInstall.pas' {frmStartInstall};
|
||||
|
||||
{$R VERSION.RES}
|
||||
|
|
|
|||
|
|
@ -354,9 +354,6 @@
|
|||
<DCCReference Include="main\Keyman.System.UpdateStateMachine.pas"/>
|
||||
<DCCReference Include="main\Keyman.System.DownloadUpdate.pas"/>
|
||||
<DCCReference Include="..\..\..\..\common\windows\delphi\general\Keyman.System.ExecutionHistory.pas"/>
|
||||
<DCCReference Include="main\Keyman.Configuration.UI.UfrmStartInstallNow.pas">
|
||||
<Form>frmInstallNow</Form>
|
||||
</DCCReference>
|
||||
<DCCReference Include="main\Keyman.Configuration.UI.UfrmStartInstall.pas">
|
||||
<Form>frmStartInstall</Form>
|
||||
</DCCReference>
|
||||
|
|
|
|||
|
|
@ -465,7 +465,7 @@
|
|||
<!-- Context: Configuration Dialog - Update tab -->
|
||||
<!-- String Type: PlainText -->
|
||||
<!-- Introduced: 18.0.168 -->
|
||||
<string name="S_Updates_Available" comment="Update tab - informs user updates are available ">K dispozici jsou aktualizace, které budou aplikovány při příštím restartu Windows.</string>
|
||||
<string name="S_Updates_Available" comment="Update tab - informs user updates are available ">K dispozici jsou aktualizace, které budou aplikovány při příštím restartu Windows:</string>
|
||||
<!-- Context: Configuration Dialog - Update tab pop up -->
|
||||
<!-- String Type: PlainText -->
|
||||
<!-- Introduced: 18.0.168 -->
|
||||
|
|
|
|||
|
|
@ -465,7 +465,7 @@
|
|||
<!-- Context: Configuration Dialog - Update tab -->
|
||||
<!-- String Type: PlainText -->
|
||||
<!-- Introduced: 18.0.168 -->
|
||||
<string name="S_Updates_Available" comment="Update tab - informs user updates are available ">Ὑπάρχουν ἐνημερώσεις ποὺ θὰ ἐφαρμοσθοῦν κατὰ τὴν ἑπόμενη ἐκκίνηση τῶν Windows.</string>
|
||||
<string name="S_Updates_Available" comment="Update tab - informs user updates are available ">Ὑπάρχουν ἐνημερώσεις ποὺ θὰ ἐφαρμοσθοῦν κατὰ τὴν ἑπόμενη ἐκκίνηση τῶν Windows:</string>
|
||||
<!-- Context: Configuration Dialog - Update tab pop up -->
|
||||
<!-- String Type: PlainText -->
|
||||
<!-- Introduced: 18.0.168 -->
|
||||
|
|
|
|||