Merge branch 'beta' into fix/core/10955-reset-on-frame

This commit is contained in:
Steven R. Loomis 2024-04-11 16:43:47 -05:00 • committed by GitHub
commit d0ca92a250
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 213 additions and 131 deletions

View file

@ -1,5 +1,22 @@
# Keyman Version History
## 17.0.306 beta 2024-04-11
* docs(ios): updates iOS app help for 17.0 banner changes (#11200)
* chore(oem/fv): Add fv sguuxs and update keyboard versions (#11198)
## 17.0.305 beta 2024-04-10
* (#11169)
* change(web): merges split async method in gesture engine 🪠 (#11142)
* fix(web): blocks nextLayer for keys quickly typed when multitapping to new layer when final tap is held (#11189)
* refactor(web): OSK spacebar-label updates now managed by layer object 🪠 (#11175)
## 17.0.304 beta 2024-04-09
* fix(android): atomically updates selection with text (#11188)
* (#11178)
## 17.0.303 beta 2024-04-05
* fix(windows): decode uri for Package ID and filename (#11152)

View file

@ -1 +1 @@
17.0.304
17.0.307

View file

@ -130,7 +130,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
@Override
public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd);
KMManager.updateSelectionRange(KMManager.KeyboardType.KEYBOARD_TYPE_SYSTEM, newSelStart, newSelEnd);
KMManager.updateSelectionRange(KMManager.KeyboardType.KEYBOARD_TYPE_SYSTEM);
}
/**
@ -170,9 +170,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0);
if (icText != null) {
boolean didUpdateText = KMManager.updateText(KeyboardType.KEYBOARD_TYPE_SYSTEM, icText.text.toString());
int selStart = icText.startOffset + icText.selectionStart;
int selEnd = icText.startOffset + icText.selectionEnd;
boolean didUpdateSelection = KMManager.updateSelectionRange(KeyboardType.KEYBOARD_TYPE_SYSTEM, selStart, selEnd);
boolean didUpdateSelection = KMManager.updateSelectionRange(KeyboardType.KEYBOARD_TYPE_SYSTEM);
if (!didUpdateText || !didUpdateSelection)
exText = icText;
}

View file

@ -163,7 +163,7 @@ final class KMKeyboard extends WebView {
return result;
}
protected boolean updateSelectionRange(int selStart, int selEnd) {
protected boolean updateSelectionRange() {
boolean result = false;
InputConnection ic = KMManager.getInputConnection(this.keyboardType);
if (ic != null) {
@ -175,6 +175,16 @@ final class KMKeyboard extends WebView {
String rawText = icText.text.toString();
updateText(rawText.toString());
int selStart = icText.selectionStart;
int selEnd = icText.selectionEnd;
int selMin = selStart, selMax = selEnd;
if (selStart > selEnd) {
// Selection is reversed so "swap"
selMin = selEnd;
selMax = selStart;
}
/*
The values of selStart & selEnd provided by the system are in code units,
not code-points. We need to account for surrogate pairs here.
@ -193,8 +203,8 @@ final class KMKeyboard extends WebView {
selStart -= pairsAtStart;
selEnd -= (pairsAtStart + pairsSelected);
this.loadJavascript(KMString.format("updateKMSelectionRange(%d,%d)", selStart, selEnd));
}
this.loadJavascript(KMString.format("updateKMSelectionRange(%d,%d)", selStart, selEnd));
result = true;
return result;

View file

@ -2137,23 +2137,40 @@ public final class KMManager {
return result;
}
/**
* Updates the active range for selected text.
* @deprecated
* This method no longer needs the `selStart` and `selEnd` parameters.
* <p>Use {@link KMManager#updateSelectionRange(KeyboardType)} instead.</p>
*
* @param kbType A value indicating if this request is for the in-app keyboard or the system keyboard
* @param selStart (deprecated) the start index for the range
* @param selEnd (deprecated) the end index for the selected range
* @return
*/
@Deprecated
public static boolean updateSelectionRange(KeyboardType kbType, int selStart, int selEnd) {
return updateSelectionRange(kbType);
}
/**
* Performs a synchronization check for the active range for selected text,
* ensuring it matches the text-editor's current state.
* @param kbType A value indicating if this request is for the in-app or system keyboard.
* @return
*/
public static boolean updateSelectionRange(KeyboardType kbType) {
boolean result = false;
int selMin = selStart, selMax = selEnd;
if (selStart > selEnd) {
// Selection is reversed so "swap"
selMin = selEnd;
selMax = selStart;
}
if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) {
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP) && !InAppKeyboard.shouldIgnoreSelectionChange()) {
result = InAppKeyboard.updateSelectionRange(selMin, selMax);
result = InAppKeyboard.updateSelectionRange();
}
InAppKeyboard.setShouldIgnoreSelectionChange(false);
} else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM) && !SystemKeyboard.shouldIgnoreSelectionChange()) {
result = SystemKeyboard.updateSelectionRange(selMin, selMax);
result = SystemKeyboard.updateSelectionRange();
}
SystemKeyboard.setShouldIgnoreSelectionChange(false);

View file

@ -63,10 +63,8 @@ public final class KMTextView extends AppCompatEditText {
*/
public static void updateTextContext() {
KMTextView textView = (KMTextView) activeView;
int selStart = textView.getSelectionStart();
int selEnd = textView.getSelectionEnd();
KMManager.updateText(KeyboardType.KEYBOARD_TYPE_INAPP, textView.getText().toString());
if (KMManager.updateSelectionRange(KeyboardType.KEYBOARD_TYPE_INAPP, selStart, selEnd)) {
if (KMManager.updateSelectionRange(KeyboardType.KEYBOARD_TYPE_INAPP)) {
KMManager.resetContext(KeyboardType.KEYBOARD_TYPE_INAPP);
}
}
@ -167,7 +165,7 @@ public final class KMTextView extends AppCompatEditText {
protected void onSelectionChanged(int selStart, int selEnd) {
super.onSelectionChanged(selStart, selEnd);
if (activeView != null && activeView.equals(this)) {
if (KMManager.updateSelectionRange(KMManager.KeyboardType.KEYBOARD_TYPE_INAPP, selStart, selEnd)) {
if (KMManager.updateSelectionRange(KMManager.KeyboardType.KEYBOARD_TYPE_INAPP)) {
KMManager.resetContext(KeyboardType.KEYBOARD_TYPE_INAPP);
}
}

View file

@ -36,51 +36,53 @@ export class AsyncClosureDispatchQueue {
return this.queue.length == 0 && !this.waitLock;
}
private async setWaitLock(promise: Promise<any>) {
this.waitLock = promise;
private async triggerNextClosure() {
if(this.queue.length == 0) {
return;
}
const functor = this.queue.shift();
// A stand-in so that `ready` doesn't report true while the closure runs.
this.waitLock = Promise.resolve();
/*
It is imperative that any errors triggered by the functor do not prevent this method from setting
the wait lock that will trigger the following event (if it exists). Failure to do so will
result in all further queued closures never getting the opportunity to run!
*/
let result: undefined | Promise<any>;
try {
// Is either undefined (return type: void) or is a Promise.
result = functor() as undefined | Promise<any>;
/* c8 ignore start */
} catch (err) {
reportError('Error from queued closure', err);
}
/* c8 ignore end */
/*
Replace the stand-in with the _true_ post-closure wait.
If the closure returns a Promise, the implication is that the further processing of queued
functions should be blocked until that Promise is fulfilled.
If not, we just add a default delay.
*/
result = result ?? this.defaultWaitFactory();
this.waitLock = result;
try {
await promise;
await result;
} catch(err) {
reportError('Async error from queued closure', err);
}
this.waitLock = null;
// if queue is length zero, auto-returns.
this.triggerNextClosure();
}
private async triggerNextClosure() {
if(this.queue.length > 0) {
const functor = this.queue.shift();
// A stand-in so that `ready` doesn't report true while the closure runs.
this.waitLock = Promise.resolve();
/*
It is imperative that any errors triggered by the functor do not prevent this method from setting
the wait lock that will trigger the following event (if it exists). Failure to do so will
result in all further queued closures never getting the opportunity to run!
*/
let result: undefined | Promise<any>;
try {
// Is either undefined (return type: void) or is a Promise.
result = functor() as undefined | Promise<any>;
/* c8 ignore start */
} catch (err) {
reportError('Error from queued closure', err);
}
/* c8 ignore end */
/*
If the closure returns a Promise, the implication is that the further processing of queued
functions should be blocked until that Promise is fulfilled.
If not, we still add a delay according to the specified default.
*/
this.setWaitLock(result ?? this.defaultWaitFactory());
}
}
runAsync(closure: QueueClosure) {
// Check before putting the closure on the internal queue; the check is based in part
// upon the existing queue length.

View file

@ -157,14 +157,14 @@ export class GestureMatcher<Type, StateToken = any> implements PredecessorMatch<
return;
case 'full':
contact = srcContact.constructSubview(false, true);
this.addContactInternal(contact, srcContact.path.stats);
this.addContactInternal(contact, srcContact.path.stats, true);
continue;
case 'partial':
preserveBaseItem = true;
// Intentional fall-through
case 'chop':
contact = srcContact.constructSubview(true, preserveBaseItem);
this.addContactInternal(contact, srcContact.path.stats);
this.addContactInternal(contact, srcContact.path.stats, true);
break;
}
}
@ -367,7 +367,7 @@ export class GestureMatcher<Type, StateToken = any> implements PredecessorMatch<
return this._result;
}
private addContactInternal(simpleSource: GestureSourceSubview<Type>, basePathStats: CumulativePathStats<Type>) {
private addContactInternal(simpleSource: GestureSourceSubview<Type>, basePathStats: CumulativePathStats<Type>, whileInitializing?: boolean) {
// The number of already-active contacts tracked for this gesture
const existingContacts = this.pathMatchers.length;
@ -491,16 +491,22 @@ export class GestureMatcher<Type, StateToken = any> implements PredecessorMatch<
const result = contactModel.update();
if(result?.type == 'reject') {
/*
Refer to the earlier comment in this method re: use of 'cancelled'; we need to
prevent any and all further attempts to match against this model We'd
instantly reject it anyway due to its rejected initial state. Failing to do so
can cause an infinite async loop.
Refer to the earlier comment in this method re: use of 'cancelled'; we
need to prevent any and all further attempts to match against this model
We'd instantly reject it anyway due to its rejected initial state.
Failing to do so can cause an infinite async loop.
If we weren't using 'cancelled', 'path' would correspond best with a rejection
here, as the decision is made due to the GestureSource's current path being
rejected by one of the `PathModel`s comprising the `GestureModel`.
If we weren't using 'cancelled', 'path' would correspond best with a
rejection here, as the decision is made due to the GestureSource's
current path being rejected by one of the `PathModel`s comprising the
`GestureModel`.
If the model's already been initialized, it's possible that a _new_
incoming touch needs special handling. We'll allow one reset. In the
case that it would try to restart itself, the restarted model will
instantly fail and thus cancel.
*/
this.finalize(false, 'cancelled');
this.finalize(false, whileInitializing ? 'cancelled' : 'path');
}
// Standard path: trigger either resolution or rejection when the contact model signals either.

View file

@ -26,17 +26,6 @@ screen where you may view the languages you have installed and, by clicking any
Click on this to [search for a keyboard or language](../../start/searching-for-keyboards).
## Show Banner
This adds a banner to the top of the keyboard to allow space for pop-up keys to render above the top row of keys.
![](../../ios_images/banner.png)
When this is off,
![](../../ios_images/no-banner.png)
pop-up keys in the top row render horizontally.
## Show "Get Started" on startup
When enabled, the Keyman app will display the 'Get Started' screen on app startup.

View file

@ -1,6 +1,7 @@
---
title: Basic Help
---
* [Using the Banner on the Keyboard](using-the-banner)
* [Switching Between Keyboards](switching-between-keyboards)

View file

@ -22,5 +22,7 @@ The Gestures:
* **Flick:** hold and slide in various directions on a key to reach alternate outputs. The key will animate to show you the expected output. For example, a keyboard could support sliding down on keys in the top row to output numerals.
![](../ios_images/down-flick-i.gif)
* **Multitap:** some keys produce alternate outputs with rapid, repeated taps. For example, tapping rapidly twice on <kbd>shift</kbd> will activate <kbd>caps lock</kbd> on many keyboards.

View file

@ -0,0 +1,29 @@
---
title: Using the Banner on the Keyboard - Keyman for iPhone and iPad Help
---
## About the Keyboard Banner
Keyman keyboards now always display a banner above the keyboard for one of the following functionalities:
* Display suggestions (See "Using the Suggestion Banner" below)
* Display a Keyman-themed banner so popups and gestures for the top row of keys are visible
* Reserved for future functionality
## Using the Suggestion Banner
If a [dictionary is installed](installing-custom-keyboards-dictionaries) and enabled for the active Keyman
keyboard, the banner will display suggestions that can be selected.
![](../ios_images/settings-suggestions-i.png)
* Drag the banner horizontally to see more suggestions
* Overly-long suggestions are partially hidden, but expand when a finger is held on them
* The banner will display up to 8 suggestions at a time.
## The Keyman-Themed Banner
When suggestions are disabled or unavailable, the Keyman-themed banner will display instead.
This is displayed so that key-popups and gestures for the top row of keys are visible.
![](../ios_images/themed-banner.png)

View file

@ -15,6 +15,7 @@ title: Keyman for iPhone and iPad 17.0 Help
## [Using Keyman for iPhone and iPad](basic/)
* [Using the Banner on the Keyboard](basic/using-the-banner)
* [Switching Between Keyboards](basic/switching-between-keyboards)
* [Using a Keyboard](basic/keyboard-usage)
* [Using the Menu](context/)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View file

@ -11,11 +11,11 @@ fv,fv_uwikala,'Uwik̓ala,BC Coast,fv_uwikala_kmw-9.0.js,9.3,hei,Heiltsuk
fv,fv_dexwlesucid,Dəxʷləšucid,BC Coast,fv_dexwlesucid_kmw-9.0.js,9.2.1,lut-Latn,Lushootseed (Latin)
fv,fv_diitiidatx,Diidiitidq,BC Coast,fv_diitiidatx_kmw-9.0.js,9.1.3,nuk-Latn,Nuu-chah-nulth (Latin)
fv,fv_gitsenimx,Gitsenimx̱,BC Coast,fv_gitsenimx_kmw-9.0.js,10.0.1,git,Gitxsan (Latin)
fv,fv_hailzaqvla,Haiɫzaqvla,BC Coast,fv_hailzaqvla_kmw-9.0.js,9.5,hei,Heiltsuk (Latin)
fv,fv_haisla,Haisla,BC Coast,fv_haisla.js,1.0,has-Latn,Haisla (Latin)
fv,fv_hailzaqvla,Haiɫzaqvla,BC Coast,fv_hailzaqvla_kmw-9.0.js,9.5.1,hei,Heiltsuk (Latin)
fv,fv_haisla,Haisla,BC Coast,fv_haisla.js,2.0.1,has-Latn,Haisla (Latin)
fv,fv_halqemeylem,Halq'eméylem,BC Coast,fv_halqemeylem_kmw-9.0.js,9.1.3,hur-Latn,Halkomelem (Latin)
fv,fv_henqeminem,Hǝn̓q̓ǝmin̓ǝm,BC Coast,fv_henqeminem_kmw-9.0.js,9.1.3,hur-Latn,Halkomelem (Latin)
fv,fv_klahoose,Homalco-Klahoose-Sliammon,BC Coast,fv_klahoose_kmw-9.0.js,10.0,coo,Comox
fv,fv_henqeminem,Hǝn̓q̓ǝmin̓ǝm,BC Coast,fv_henqeminem_kmw-9.0.js,10.0.1,hur-Latn,Halkomelem (Latin)
fv,fv_klahoose,Homalco-Klahoose-Sliammon,BC Coast,fv_klahoose_kmw-9.0.js,10.1,coo,Comox
fv,fv_hulquminum,Hul’q’umi’num’,BC Coast,fv_hulquminum_kmw-9.0.js,9.1,hur-Latn,Halkomelem (Latin)
fv,fv_hulquminum_combine,Hul̓q̓umin̓um̓,BC Coast,fv_hulquminum_combine_kmw-9.0.js,1.0,hur-Latn,Halkomelem (Latin)
fv,fv_kwakwala_liqwala,Kʷak̓ʷala,BC Coast,fv_kwakwala_liqwala_kmw-9.0.js,9.2.5,kwk-Latn,Kwakiutl (Latin)
@ -25,8 +25,9 @@ fv,fv_nisgaa,Nisg̱a'a,BC Coast,fv_nisgaa_kmw-9.0.js,9.1.2,ncg-Latn,Nisga'a (Lat
fv,fv_nuucaanul,Nuučaan̓uł,BC Coast,fv_nuucaanul_kmw-9.0.js,9.1.4,nuk-Latn,Nuu-chah-nulth (Latin)
fv,fv_nuxalk,Nuxalk,BC Coast,fv_nuxalk_kmw-9.0.js,10.0,blc-Latn,Bella Coola (Latin)
fv,fv_sencoten,SENĆOŦEN,BC Coast,fv_sencoten_kmw-9.0.js,9.2.1,str,Straits Salish
fv,fv_sguuxs,Sgüüx̱s,BC Coast,fv_sguuxs.js,1.0,tsi,Tsimshian
fv,fv_shashishalhem,Shashishalhem,BC Coast,fv_shashishalhem_kmw-9.0.js,9.1.3,sec-Latn,Sechelt (Latin)
fv,fv_skwxwumesh_snichim,Sḵwx̱wúmesh sníchim,BC Coast,fv_skwxwumesh_snichim_kmw-9.0.js,9.2.1,squ-Latn,Squamish (Latin)
fv,fv_skwxwumesh_snichim,Sḵwx̱wúmesh sníchim,BC Coast,fv_skwxwumesh_snichim_kmw-9.0.js,9.3,squ-Latn,Squamish (Latin)
fv,fv_smalgyax,Sm'algya̱x,BC Coast,fv_smalgyax_kmw-9.0.js,9.1.3,tsi-Latn,Tsimshian (Latin)
fv,fv_xaislakala,X̄a'ʼislak̓ala,BC Coast,fv_xaislakala_kmw-9.0.js,9.1.1,has-Latn,Haisla (Latin)
fv,fv_hlgaagilda_xaayda_kil,X̱aayda-X̱aad Kil,BC Coast,fv_hlgaagilda_xaayda_kil_kmw-9.0.js,9.3,hax,Southern Haida
@ -37,13 +38,13 @@ fv,fv_natwits,Nedut’en-Witsuwit'en,BC Interior,fv_natwits_kmw-9.0.js,9.1.3,caf
fv,fv_nlekepmxcin,Nłeʔkepmxcin,BC Interior,fv_nlekepmxcin_kmw-9.0.js,9.2.3,thp-Latn,Thompson (Latin)
fv,fv_nlha7kapmxtsin,Nlha7kapmxtsin,BC Interior,fv_nlha7kapmxtsin_kmw-9.0.js,9.1.1,thp-Latn,Thompson (Latin)
fv,fv_nsilxcen,Nsilxcən,BC Interior,fv_nsilxcen_kmw-9.0.js,9.3,oka,Okanagan
fv,fv_secwepemctsin,Secwepemctsín,BC Interior,fv_secwepemctsin_kmw-9.0.js,9.1.2,shs-Latn,Shuswap (Latin)
fv,fv_secwepemctsin,Secwepemctsín,BC Interior,fv_secwepemctsin_kmw-9.0.js,9.2,shs-Latn,Shuswap (Latin)
fv,fv_stlatlimxec,Sƛ̓aƛ̓imxəc,BC Interior,fv_stlatlimxec_kmw-9.0.js,9.2.3,lil-Latn,Lillooet (Latin)
fv,fv_statimcets,St̓át̓imcets,BC Interior,fv_statimcets_kmw-9.0.js,9.1.3,lil-Latn,Lillooet (Latin)
fv,fv_statimcets,St̓át̓imcets,BC Interior,fv_statimcets_kmw-9.0.js,9.1.4,lil-Latn,Lillooet (Latin)
fv,fv_taltan,Tāłtān,BC Interior,fv_taltan_kmw-9.0.js,9.1.5,tht-Latn,Tahltan (Latin)
fv,fv_tsekehne,Tsek'ehne,BC Interior,fv_tsekehne_kmw-9.0.js,9.1.2,sek-Latn,Sekani (Latin)
fv,fv_tsilhqotin,Tŝilhqot'in,BC Interior,fv_tsilhqotin_kmw-9.0.js,9.1.3,clc-Latn,Chilcotin (Latin)
fv,fv_southern_carrier,ᑐᑊᘁᗕᑋᗸ (Southern Carrier),BC Interior,fv_southern_carrier_kmw-9.0.js,9.2.1,caf-Cans,Southern Carrier (Unified Canadian Aboriginal Syllabics)
fv,fv_southern_carrier,ᑐᑊᘁᗕᑋᗸ (Southern Carrier),BC Interior,fv_southern_carrier_kmw-9.0.js,10.0,caf-Cans,Southern Carrier (Unified Canadian Aboriginal Syllabics)
fv,fv_anicinapemi8in,Anicinapemi8in/Anishinàbemiwin,Eastern Subarctic,fv_anicinapemi8in_kmw-9.0.js,9.1.1,alq-Latn,Algonquin (Latin)
fv,fv_atikamekw,Atikamekw,Eastern Subarctic,fv_atikamekw_kmw-9.0.js,9.1.1,atj-Latn,Atikamekw (Latin)
fv,fv_ilnu_innu_aimun,Ilnu-Innu Aimun,Eastern Subarctic,fv_ilnu_innu_aimun_kmw-9.0.js,9.1.1,moe-Latn,Montagnais (Latin)
@ -69,14 +70,14 @@ fv,fv_wobanakiodwawogan,Wôbanakiôdwawôgan,Great Lakes - St. Lawrence,fv_woban
fv,fv_australian,Australian,Pacific,fv_australian_kmw-9.0.js,9.3.1,pjt-Latn,Pitjantjatjara (Latin)
fv,fv_maori,Māori,Pacific,fv_maori_kmw-9.0.js,9.1.1,mi-Latn,Maori (Latin)
fv,fv_blackfoot,Blackfoot,Prairies,fv_blackfoot_kmw-9.0.js,9.2.1,bla-Latn,Siksika (Latin)
fv,fv_cree_latin,Cree - Roman Orthography,Prairies,fv_cree_latin_kmw-9.0.js,10.0,cr-Latn,Cree (Latin)
fv,fv_cree_latin,Cree - Roman Orthography,Prairies,fv_cree_latin_kmw-9.0.js,10.0.1,cr-Latn,Cree (Latin)
fv,fv_dakota_mb,Dakota,Prairies,fv_dakota_mb_kmw-9.0.js,9.1.1,dak-Latn,Dakota (Latin)
fv,fv_dakota_sk,Dakot̄a,Prairies,fV_dakota_sk_kmw-9.0.js,9.1.1,dak-Latn,Dakota (Latin)
fv,fv_isga_iabi,Isga Iʔabi,Prairies,fv_isga_iabi_kmw-9.0.js,9.1.1,sto-Latn,Stoney (Latin)
fv,fv_lakota,Lak̇ot̄a,Prairies,fv_lakota-9.0.js,9.1.1,lkt-Latn,Lakota (Latin)
fv,fv_nakoda,Nakoda,Prairies,fv_nakoda_kmw-9.0.js,9.1.1,asb-Latn,Assiniboine (Latin)
fv,fv_tsuutina,Tsúùt'ínà,Prairies,fv_tsuutina_kmw-9.0.js,9.1.1,srs-Latn,Sarsi (Latin)
fv,fv_plains_cree,ᓀᐦᐃᔭᐍᐏᐣ (Plains Cree),Prairies,fv_plains_cree_kmw-9.0.js,10.0.2,crk-Cans,ᓀᐦᐃᔭᐍᐏᐣ (Cree syllabics)
fv,fv_plains_cree,ᓀᐦᐃᔭᐍᐏᐣ (Plains Cree),Prairies,fv_plains_cree_kmw-9.0.js,11.0,crk-Cans,ᓀᐦᐃᔭᐍᐏᐣ (Cree syllabics)
fv,fv_dine_bizaad,Diné Bizaad,South West,fv_dine_bizaad_kmw-9.0.js,9.1.1,nv-Latn,Navajo (Latin)
fv,fv_dane_zaa_zaage,Dane-Z̲aa Z̲áágéʔ,Western Subarctic,fv_dane_zaa_zaage_kmw-9.0.js,9.3,bea,Beaver
fv,fv_dene_dzage,Dene Dzage,Western Subarctic,fv_dene_dzage_kmw-9.0.js,9.1.1,kkz-Latn,Kaska (Latin)

1 Shortname ID Name Region 9.0 Web Keyboard Version Language ID Language Name
11 fv fv_dexwlesucid Dəxʷləšucid BC Coast fv_dexwlesucid_kmw-9.0.js 9.2.1 lut-Latn Lushootseed (Latin)
12 fv fv_diitiidatx Diidiitidq BC Coast fv_diitiidatx_kmw-9.0.js 9.1.3 nuk-Latn Nuu-chah-nulth (Latin)
13 fv fv_gitsenimx Gitsenimx̱ BC Coast fv_gitsenimx_kmw-9.0.js 10.0.1 git Gitxsan (Latin)
14 fv fv_hailzaqvla Haiɫzaqvla BC Coast fv_hailzaqvla_kmw-9.0.js 9.5 9.5.1 hei Heiltsuk (Latin)
15 fv fv_haisla Haisla BC Coast fv_haisla.js 1.0 2.0.1 has-Latn Haisla (Latin)
16 fv fv_halqemeylem Halq'eméylem BC Coast fv_halqemeylem_kmw-9.0.js 9.1.3 hur-Latn Halkomelem (Latin)
17 fv fv_henqeminem Hǝn̓q̓ǝmin̓ǝm BC Coast fv_henqeminem_kmw-9.0.js 9.1.3 10.0.1 hur-Latn Halkomelem (Latin)
18 fv fv_klahoose Homalco-Klahoose-Sliammon BC Coast fv_klahoose_kmw-9.0.js 10.0 10.1 coo Comox
19 fv fv_hulquminum Hul’q’umi’num’ BC Coast fv_hulquminum_kmw-9.0.js 9.1 hur-Latn Halkomelem (Latin)
20 fv fv_hulquminum_combine Hul̓q̓umin̓um̓ BC Coast fv_hulquminum_combine_kmw-9.0.js 1.0 hur-Latn Halkomelem (Latin)
21 fv fv_kwakwala_liqwala Kʷak̓ʷala BC Coast fv_kwakwala_liqwala_kmw-9.0.js 9.2.5 kwk-Latn Kwakiutl (Latin)
25 fv fv_nuucaanul Nuučaan̓uł BC Coast fv_nuucaanul_kmw-9.0.js 9.1.4 nuk-Latn Nuu-chah-nulth (Latin)
26 fv fv_nuxalk Nuxalk BC Coast fv_nuxalk_kmw-9.0.js 10.0 blc-Latn Bella Coola (Latin)
27 fv fv_sencoten SENĆOŦEN BC Coast fv_sencoten_kmw-9.0.js 9.2.1 str Straits Salish
28 fv fv_sguuxs Sgüüx̱s BC Coast fv_sguuxs.js 1.0 tsi Tsimshian
29 fv fv_shashishalhem Shashishalhem BC Coast fv_shashishalhem_kmw-9.0.js 9.1.3 sec-Latn Sechelt (Latin)
30 fv fv_skwxwumesh_snichim Sḵwx̱wúmesh sníchim BC Coast fv_skwxwumesh_snichim_kmw-9.0.js 9.2.1 9.3 squ-Latn Squamish (Latin)
31 fv fv_smalgyax Sm'algya̱x BC Coast fv_smalgyax_kmw-9.0.js 9.1.3 tsi-Latn Tsimshian (Latin)
32 fv fv_xaislakala X̄a'ʼislak̓ala BC Coast fv_xaislakala_kmw-9.0.js 9.1.1 has-Latn Haisla (Latin)
33 fv fv_hlgaagilda_xaayda_kil X̱aayda-X̱aad Kil BC Coast fv_hlgaagilda_xaayda_kil_kmw-9.0.js 9.3 hax Southern Haida
38 fv fv_nlekepmxcin Nłeʔkepmxcin BC Interior fv_nlekepmxcin_kmw-9.0.js 9.2.3 thp-Latn Thompson (Latin)
39 fv fv_nlha7kapmxtsin Nlha7kapmxtsin BC Interior fv_nlha7kapmxtsin_kmw-9.0.js 9.1.1 thp-Latn Thompson (Latin)
40 fv fv_nsilxcen Nsilxcən BC Interior fv_nsilxcen_kmw-9.0.js 9.3 oka Okanagan
41 fv fv_secwepemctsin Secwepemctsín BC Interior fv_secwepemctsin_kmw-9.0.js 9.1.2 9.2 shs-Latn Shuswap (Latin)
42 fv fv_stlatlimxec Sƛ̓aƛ̓imxəc BC Interior fv_stlatlimxec_kmw-9.0.js 9.2.3 lil-Latn Lillooet (Latin)
43 fv fv_statimcets St̓át̓imcets BC Interior fv_statimcets_kmw-9.0.js 9.1.3 9.1.4 lil-Latn Lillooet (Latin)
44 fv fv_taltan Tāłtān BC Interior fv_taltan_kmw-9.0.js 9.1.5 tht-Latn Tahltan (Latin)
45 fv fv_tsekehne Tsek'ehne BC Interior fv_tsekehne_kmw-9.0.js 9.1.2 sek-Latn Sekani (Latin)
46 fv fv_tsilhqotin Tŝilhqot'in BC Interior fv_tsilhqotin_kmw-9.0.js 9.1.3 clc-Latn Chilcotin (Latin)
47 fv fv_southern_carrier ᑐᑊᘁᗕᑋᗸ (Southern Carrier) BC Interior fv_southern_carrier_kmw-9.0.js 9.2.1 10.0 caf-Cans Southern Carrier (Unified Canadian Aboriginal Syllabics)
48 fv fv_anicinapemi8in Anicinapemi8in/Anishinàbemiwin Eastern Subarctic fv_anicinapemi8in_kmw-9.0.js 9.1.1 alq-Latn Algonquin (Latin)
49 fv fv_atikamekw Atikamekw Eastern Subarctic fv_atikamekw_kmw-9.0.js 9.1.1 atj-Latn Atikamekw (Latin)
50 fv fv_ilnu_innu_aimun Ilnu-Innu Aimun Eastern Subarctic fv_ilnu_innu_aimun_kmw-9.0.js 9.1.1 moe-Latn Montagnais (Latin)
70 fv fv_australian Australian Pacific fv_australian_kmw-9.0.js 9.3.1 pjt-Latn Pitjantjatjara (Latin)
71 fv fv_maori Māori Pacific fv_maori_kmw-9.0.js 9.1.1 mi-Latn Maori (Latin)
72 fv fv_blackfoot Blackfoot Prairies fv_blackfoot_kmw-9.0.js 9.2.1 bla-Latn Siksika (Latin)
73 fv fv_cree_latin Cree - Roman Orthography Prairies fv_cree_latin_kmw-9.0.js 10.0 10.0.1 cr-Latn Cree (Latin)
74 fv fv_dakota_mb Dakota Prairies fv_dakota_mb_kmw-9.0.js 9.1.1 dak-Latn Dakota (Latin)
75 fv fv_dakota_sk Dakot̄a Prairies fV_dakota_sk_kmw-9.0.js 9.1.1 dak-Latn Dakota (Latin)
76 fv fv_isga_iabi Isga Iʔabi Prairies fv_isga_iabi_kmw-9.0.js 9.1.1 sto-Latn Stoney (Latin)
77 fv fv_lakota Lak̇ot̄a Prairies fv_lakota-9.0.js 9.1.1 lkt-Latn Lakota (Latin)
78 fv fv_nakoda Nakoda Prairies fv_nakoda_kmw-9.0.js 9.1.1 asb-Latn Assiniboine (Latin)
79 fv fv_tsuutina Tsúùt'ínà Prairies fv_tsuutina_kmw-9.0.js 9.1.1 srs-Latn Sarsi (Latin)
80 fv fv_plains_cree ᓀᐦᐃᔭᐍᐏᐣ (Plains Cree) Prairies fv_plains_cree_kmw-9.0.js 10.0.2 11.0 crk-Cans ᓀᐦᐃᔭᐍᐏᐣ (Cree syllabics)
81 fv fv_dine_bizaad Diné Bizaad South West fv_dine_bizaad_kmw-9.0.js 9.1.1 nv-Latn Navajo (Latin)
82 fv fv_dane_zaa_zaage Dane-Z̲aa Z̲áágéʔ Western Subarctic fv_dane_zaa_zaage_kmw-9.0.js 9.3 bea Beaver
83 fv fv_dene_dzage Dene Dzage Western Subarctic fv_dene_dzage_kmw-9.0.js 9.1.1 kkz-Latn Kaska (Latin)

View file

@ -4,6 +4,11 @@ import OSKRow from './oskRow.js';
import OSKBaseKey from './oskBaseKey.js';
import VisualKeyboard from '../visualKeyboard.js';
export interface LayerLayoutParams {
keyboardHeight: number;
spacebarText: string;
}
export default class OSKLayer {
public readonly element: HTMLDivElement;
public readonly rows: OSKRow[];
@ -76,6 +81,21 @@ export default class OSKLayer {
this.capsKey = this.findKey('K_CAPS');
this.numKey = this.findKey('K_NUMLOCK');
this.scrollKey = this.findKey('K_SCROLL');
if(this.spaceBarKey) {
const spacebarLabel = this.spaceBarKey.label;
let tButton = this.spaceBarKey.btn;
if (typeof (tButton.className) == 'undefined' || tButton.className == '') {
tButton.className = 'kmw-spacebar';
} else if (tButton.className.indexOf('kmw-spacebar') == -1) {
tButton.className += ' kmw-spacebar';
}
if (spacebarLabel.className != 'kmw-spacebar-caption') {
spacebarLabel.className = 'kmw-spacebar-caption';
}
}
}
/**
@ -96,7 +116,35 @@ export default class OSKLayer {
return null;
}
public refreshLayout(vkbd: VisualKeyboard, layerHeight: number) {
/**
* Indicate the current language and keyboard on the space bar
**/
showLanguage(displayName: string) {
if(!this.spaceBarKey) {
return () => {};
}
try {
const spacebarLabel = this.spaceBarKey.label;
// The key can read the text from here during the display update without us
// needing to trigger a reflow by running the closure below early.
this.spaceBarKey.spec.text = displayName;
// It sounds redundant, but this dramatically cuts down on browser DOM processing;
// but sometimes innerText is reported empty when it actually isn't, so set it
// anyway in that case (Safari, iOS 14.4)
if (spacebarLabel.innerText != displayName || displayName == '') {
spacebarLabel.innerText = displayName;
}
}
catch (ex) { }
}
public refreshLayout(vkbd: VisualKeyboard, layoutParams: LayerLayoutParams) {
const layerHeight = layoutParams.keyboardHeight;
this.showLanguage(layoutParams.spacebarText);
// Check the heights of each row, in case different layers have different row counts.
const nRows = this.rows.length;
const rowHeight = this._rowHeight = Math.floor(layerHeight/(nRows == 0 ? 1 : nRows));

View file

@ -198,7 +198,7 @@ export default class OSKLayerGroup {
Assumes there is no fine-tuning of the row ranges to be done - each takes a perfect
fraction of the overall layer height without any padding above or below.
*/
const rowIndex = Math.floor(proportionalCoords.y * layer.rows.length);
const rowIndex = Math.max(0, Math.min(layer.rows.length-1, Math.floor(proportionalCoords.y * layer.rows.length)));
const row = layer.rows[rowIndex];
// Assertion: row no longer `null`.

View file

@ -658,6 +658,7 @@ export default abstract class OSKView
if(this.bannerView.height > 0) {
availableHeight -= this.bannerView.height + 5;
}
// Triggers the VisualKeyboard.refreshLayout() method, which includes a showLanguage() call.
this.vkbd.setSize(this.computedWidth, availableHeight, pending);
const bs = this._Box.style;
@ -665,9 +666,6 @@ export default abstract class OSKView
// visualizations, not to help text or empty views.
bs.width = bs.maxWidth = this.computedWidth + 'px';
bs.height = bs.maxHeight = this.computedHeight + 'px';
// Ensure that the layer's spacebar is properly captioned.
this.vkbd.showLanguage();
} else {
const bs = this._Box.style;
bs.width = 'auto';
@ -855,9 +853,8 @@ export default abstract class OSKView
// Also, only change the layer ID itself if there is an actual corresponding layer
// in the OSK.
if(this.vkbd?.layerGroup.layers[newValue] && !this.vkbd?.layerLocked) {
// triggers state-update + layer refresh automatically.
this.vkbd.layerId = newValue;
// Ensure that the layer's spacebar is properly captioned.
this.vkbd.showLanguage();
}
// Ensure the keyboard view is modeling the correct state. (Correct layer, etc.)
@ -892,10 +889,6 @@ export default abstract class OSKView
// First thing after it's made visible.
this.refreshLayoutIfNeeded();
if(this.keyboardView instanceof VisualKeyboard) {
this.keyboardView.showLanguage();
}
this._Visible=true;
/* In case it's still '0' from a hide() operation.

View file

@ -1115,38 +1115,6 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
//#endregion
/**
* Indicate the current language and keyboard on the space bar
**/
showLanguage() {
let activeStub = this.layoutKeyboardProperties;
let displayName: string = activeStub?.displayName ?? '(System keyboard)';
try {
var t = <HTMLElement>this.spaceBar.key.label;
let tParent = <HTMLElement>t.parentNode;
if (typeof (tParent.className) == 'undefined' || tParent.className == '') {
tParent.className = 'kmw-spacebar';
} else if (tParent.className.indexOf('kmw-spacebar') == -1) {
tParent.className += ' kmw-spacebar';
}
if (t.className != 'kmw-spacebar-caption') {
t.className = 'kmw-spacebar-caption';
}
// It sounds redundant, but this dramatically cuts down on browser DOM processing;
// but sometimes innerText is reported empty when it actually isn't, so set it
// anyway in that case (Safari, iOS 14.4)
if (t.innerText != displayName || displayName == '') {
t.innerText = displayName;
}
this.spaceBar.key.refreshLayout(this);
}
catch (ex) { }
}
/**
* Add or remove a class from a keyboard key (when touched or clicked)
* or add a key preview for phone devices
@ -1269,7 +1237,6 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
const isInDOM = computedStyle.height != '' && computedStyle.height != 'auto';
// Step 2: determine basic layout geometry, refresh things that might update.
this.showLanguage(); // In case the spacebar-text mode setting has changed.
if (fixedSize) {
this._computedWidth = this.width;
@ -1307,7 +1274,10 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
// Step 4: perform layout operations.
// Needs the refreshed layout info to work correctly.
if(this.currentLayer) {
this.currentLayer.refreshLayout(this, this._computedHeight - this.getVerticalLayerGroupPadding());
this.currentLayer.refreshLayout(this, {
keyboardHeight: this._computedHeight - this.getVerticalLayerGroupPadding(),
spacebarText: this.layoutKeyboardProperties?.displayName ?? '(System keyboard)'
});
}
}