Skip to content

Commit

Permalink
Merge pull request #11217 from keymanapp/chore/merge-beta-to-master-b…
Browse files Browse the repository at this point in the history
…17s5

chore(common): Merge beta to master for Sprint B17S5
  • Loading branch information
darcywong00 committed Apr 12, 2024
2 parents 93dc41e + 97e90c9 commit 1f3ef92
Show file tree
Hide file tree
Showing 150 changed files with 3,233 additions and 986 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/deb-packaging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,16 @@ jobs:
ref: '${{ github.event.client_payload.buildSha }}'
sparse-checkout: '.github/actions/'

binary_packages_unreleased:
name: Build binary packages for next Ubuntu version
needs: sourcepackage
strategy:
fail-fast: true
matrix:
dist: [noble]

runs-on: ubuntu-latest
steps:
- name: Build
uses: ./.github/actions/build-binary-packages
with:
Expand Down
75 changes: 75 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,81 @@
* chore(common): move to 18.0 alpha (#10713)
* chore: move to 18.0 alpha

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

* fix(core): skip leading trail surrogate char in km_core_state_context_set_if_needed() (#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)
* fix(web): fix crash in nearest-key row lookup when touch moves out-of-bounds (#11178)

## 17.0.303 beta 2024-04-05

* fix(windows): decode uri for Package ID and filename (#11152)
* fix(common/models): suggestion stability after multiple whitespaces (#11164)

## 17.0.302 beta 2024-04-04

* fix(mac): load only 80 characters from context when processing keystrokes (#11141)

## 17.0.301 beta 2024-04-03

* feat(core): support modifiers=other (#11118)
* chore(core): dx better err message on embedded test vkeys (#11119)
* fix(web): key preview stickiness 🪠 (#10778)
* fix(web): early gesture-match abort when unable to extend existing gestures 🪠 (#10836)
* fix(web): infinite model-match replacement looping 🪠 (#10838)
* fix(web): proper gesture-match sequencing 🪠 (#10840)
* change(web): input-event sequentialization 🪠 (#10843)
* fix(web): proper linkage of sources to events 🪠 (#10960)
* fix(developer): handle buffer boundaries in four cases (#11137)
* chore(linux): Build packages for next Ubuntu version separately (#11153)
* fix(common): upgrade sentry-cli to 2.31.0 (#11151)
* fix(android/app): Track previous device orientation for SystemKeyboard (#11134)
* change(web): reworks nearest-key detection to avoid layout reflow (#11129)

## 17.0.300 beta 2024-04-02

* change(web): keyboard swaps keep original keyboards active until fully ready (#11108)
* fix(android/engine): Swap selection range if reversed (#11127)
* test(developer): keyboard info compiler unit tests (#11000)

## 17.0.299 beta 2024-04-01

* fix(ios): address crash by reading full code point rather than code unit when trimming initial directional-mark (#11113)
* fix(mac): delete correct number of characters from current context when processing BMP or SMP deletes (#11086)
* feat(developer): disallow stray dollarsign in from pattern (#11117)

## 17.0.298 beta 2024-03-29

* chore(linux): Update debian changelog (#11096)
* fix(web): prevent layer switch key from erasing selection (#11032)
* fix(developer): prevent error when scrolling touch layout editor with no selected key (#11109)
* fix(common): make `isEmptyTransform` return true if passed a nullish transform (#11110)

## 17.0.297 beta 2024-03-28

* fix(common): properly handle illegal UnicodeSets to prevent crash in kmc-ldml compiler (#11065)
* fix(core,developer): variable/marker substitution in sets and strings (#11059)
* fix(developer): in ldml compiler, generate compiler error if `from=` regex matches empty string (#11070)
* fix(core): calculate offset correctly when replacing marker in transform (fixes crash) (#11071)
* feat(developer): support comma in modifiers (#11075)
* fix(core): actions_normalize() length and dead store fix (#11100)
* chore(core): optimize ldml_event_state::emit_difference() when no diff (#11094)
* fix(ios): bad initial in-app layout, delayed banner, deprecated banner toggle (#10929)
* feat(developer/compilers): better unit test for suggestion accessibility (#11085)
* fix(core): fix pointer math in actions_normalize() (#11101)


## 17.0.296 beta 2024-03-27

* fix(developer): in model compiler, give correct key to shorter prefix words when a longer, higher-frequency word is also present (#11074)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
private static View inputView = null;
private static ExtractedText exText = null;
private KMHardwareKeyboardInterpreter interpreter = null;
private int lastOrientation = Configuration.ORIENTATION_UNDEFINED;

private static final String TAG = "SystemKeyboard";

Expand Down Expand Up @@ -129,7 +130,7 @@ public View onCreateInputView() {
@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);
}

/**
Expand Down Expand Up @@ -169,9 +170,7 @@ public void onStartInput(EditorInfo attribute, boolean restarting) {
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;
}
Expand All @@ -197,7 +196,10 @@ public void onUpdateExtractingVisibility(EditorInfo ei) {
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
KMManager.onConfigurationChanged(newConfig);
if (newConfig.orientation != lastOrientation) {
lastOrientation = newConfig.orientation;
KMManager.onConfigurationChanged(newConfig);
}
}

@Override
Expand All @@ -220,8 +222,9 @@ public void onComputeInsets(InputMethodService.Insets outInsets) {
wm.getDefaultDisplay().getSize(size);

int inputViewHeight = 0;
if (inputView != null)
if (inputView != null) {
inputViewHeight = inputView.getHeight();
}

int bannerHeight = KMManager.getBannerHeight(this);
int kbHeight = KMManager.getKeyboardHeight(this);
Expand Down
14 changes: 12 additions & 2 deletions android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ protected boolean updateText(String text) {
return result;
}

protected boolean updateSelectionRange(int selStart, int selEnd) {
protected boolean updateSelectionRange() {
boolean result = false;
InputConnection ic = KMManager.getInputConnection(this.keyboardType);
if (ic != null) {
Expand All @@ -175,6 +175,16 @@ protected boolean updateSelectionRange(int selStart, int selEnd) {
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.
Expand All @@ -193,8 +203,8 @@ protected boolean updateSelectionRange(int selStart, int selEnd) {

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;
Expand Down
27 changes: 25 additions & 2 deletions android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -2137,17 +2137,40 @@ public static boolean updateText(KeyboardType kbType, String text) {
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;

if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) {
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP) && !InAppKeyboard.shouldIgnoreSelectionChange()) {
result = InAppKeyboard.updateSelectionRange(selStart, selEnd);
result = InAppKeyboard.updateSelectionRange();
}

InAppKeyboard.setShouldIgnoreSelectionChange(false);
} else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM) && !SystemKeyboard.shouldIgnoreSelectionChange()) {
result = SystemKeyboard.updateSelectionRange(selStart, selEnd);
result = SystemKeyboard.updateSelectionRange();
}

SystemKeyboard.setShouldIgnoreSelectionChange(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,8 @@ public KMTextView(Context context, AttributeSet attrs, int defStyle) {
*/
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);
}
}
Expand Down Expand Up @@ -167,7 +165,7 @@ protected void onTextChanged(CharSequence text, int start, int lengthBefore, int
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);
}
}
Expand Down
3 changes: 3 additions & 0 deletions common/include/kmx_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,9 @@ namespace kmx {

#define K_MODIFIERFLAG 0x007F
#define K_NOTMODIFIERFLAG 0xFF00 // I4548
// Note: OTHER_MODIFIER = 0x10000, used by KMX+ for the
// other modifier flag in layers, > 16 bit so not available here.
// See keys_mod_other in keyman_core_ldml.ts

struct COMP_STORE {
KMX_DWORD_unaligned dwSystemID;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { timedPromise } from "@keymanapp/web-utils";
import { reportError } from "../reportError.js";

export type QueueClosure = () => (Promise<any> | void);

/**
This class is modeled somewhat after Swift's `DispatchQueue` class, but with
the twist that each closure may return a `Promise` (in Swift: a `Future`) to
lock out further closure processing until the `Promise` resolves.
*/
export class AsyncClosureDispatchQueue {
private queue: QueueClosure[];
private waitLock: Promise<any>;
private defaultWaitFactory: () => Promise<any>;

/**
*
* @param defaultWaitFactory A factory returning Promises to use for default
* delays between tasks. If not specified, Promises corresponding to
* setTimeout(0) will be used, allowing the microqueue task to flush between
* tasks.
*/
constructor(defaultWaitFactory?: () => Promise<any>) {
// We only need to trigger events if the queue has no prior entries and there isn't an
// active wait-lock; for the latter, we'll auto-trigger the next function when it unlocks.
this.queue = [];

this.defaultWaitFactory = defaultWaitFactory || (() => { return timedPromise(0) });
}

get defaultWait() {
return this.defaultWaitFactory();
}

get ready() {
return this.queue.length == 0 && !this.waitLock;
}

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 result;
} catch(err) {
reportError('Async error from queued closure', err);
}

this.waitLock = null;
// if queue is length zero, auto-returns.
this.triggerNextClosure();
}

runAsync(closure: QueueClosure) {
// Check before putting the closure on the internal queue; the check is based in part
// upon the existing queue length.
const isReady = this.ready;

this.queue.push(closure);

// If `!isReady`, the next closure will automatically be triggered when possible.
if(isReady) {
this.triggerNextClosure();
}
}
}
Loading

0 comments on commit 1f3ef92

Please sign in to comment.