Skip to content

Commit 7b14235

Browse files
committed
impr(tape mode): support RTL languages (@NadAlaba)
1 parent c34d109 commit 7b14235

File tree

5 files changed

+127
-98
lines changed

5 files changed

+127
-98
lines changed

frontend/src/ts/controllers/input-controller.ts

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ function updateUI(): void {
110110
}
111111
}
112112

113-
function backspaceToPrevious(): void {
113+
async function backspaceToPrevious(): Promise<void> {
114114
if (!TestState.isActive) return;
115115

116116
if (
@@ -152,7 +152,7 @@ function backspaceToPrevious(): void {
152152
}
153153
TestWords.words.decreaseCurrentIndex();
154154
TestUI.setActiveWordElementIndex(TestUI.activeWordElementIndex - 1);
155-
TestUI.updateActiveElement(true);
155+
await TestUI.updateActiveElement(true);
156156
Funbox.toggleScript(TestWords.words.getCurrent());
157157
void TestUI.updateActiveWordLetters();
158158

@@ -337,7 +337,7 @@ async function handleSpace(): Promise<void> {
337337
}
338338
}
339339
TestUI.setActiveWordElementIndex(TestUI.activeWordElementIndex + 1);
340-
TestUI.updateActiveElement();
340+
await TestUI.updateActiveElement();
341341
void Caret.updatePosition();
342342

343343
if (
@@ -363,7 +363,7 @@ async function handleSpace(): Promise<void> {
363363
}
364364

365365
if (nextTop > currentTop) {
366-
TestUI.lineJump(currentTop);
366+
await TestUI.lineJump(currentTop);
367367
}
368368
} //end of line wrap
369369

@@ -461,32 +461,32 @@ function isCharCorrect(char: string, charIndex: number): boolean {
461461
return false;
462462
}
463463

464-
function handleChar(
464+
async function handleChar(
465465
char: string,
466466
charIndex: number,
467467
realInputValue?: string
468-
): void {
468+
): Promise<void> {
469469
if (TestUI.resultCalculating || TestUI.resultVisible) {
470470
return;
471471
}
472472

473473
if (char === "…" && TestWords.words.getCurrent()[charIndex] !== "…") {
474474
for (let i = 0; i < 3; i++) {
475-
handleChar(".", charIndex + i);
475+
await handleChar(".", charIndex + i);
476476
}
477477

478478
return;
479479
}
480480

481481
if (char === "œ" && TestWords.words.getCurrent()[charIndex] !== "œ") {
482-
handleChar("o", charIndex);
483-
handleChar("e", charIndex + 1);
482+
await handleChar("o", charIndex);
483+
await handleChar("e", charIndex + 1);
484484
return;
485485
}
486486

487487
if (char === "æ" && TestWords.words.getCurrent()[charIndex] !== "æ") {
488-
handleChar("a", charIndex);
489-
handleChar("e", charIndex + 1);
488+
await handleChar("a", charIndex);
489+
await handleChar("e", charIndex + 1);
490490
return;
491491
}
492492

@@ -745,7 +745,7 @@ function handleChar(
745745
TestInput.input.current.length > 1
746746
) {
747747
if (Config.mode === "zen") {
748-
if (!Config.showAllLines) TestUI.lineJump(activeWordTopBeforeJump);
748+
if (!Config.showAllLines) await TestUI.lineJump(activeWordTopBeforeJump);
749749
} else {
750750
TestInput.input.current = TestInput.input.current.slice(0, -1);
751751
void TestUI.updateActiveWordLetters();
@@ -785,7 +785,10 @@ function handleChar(
785785
}
786786
}
787787

788-
function handleTab(event: JQuery.KeyDownEvent, popupVisible: boolean): void {
788+
async function handleTab(
789+
event: JQuery.KeyDownEvent,
790+
popupVisible: boolean
791+
): Promise<void> {
789792
if (TestUI.resultCalculating) {
790793
event.preventDefault();
791794
return;
@@ -813,7 +816,7 @@ function handleTab(event: JQuery.KeyDownEvent, popupVisible: boolean): void {
813816
event.preventDefault();
814817
// insert tab character if needed (only during the test)
815818
if (!TestUI.resultVisible && shouldInsertTabCharacter) {
816-
handleChar("\t", TestInput.input.current.length);
819+
await handleChar("\t", TestInput.input.current.length);
817820
setWordsInput(" " + TestInput.input.current);
818821
return;
819822
}
@@ -840,7 +843,7 @@ function handleTab(event: JQuery.KeyDownEvent, popupVisible: boolean): void {
840843
// insert tab character if needed (only during the test)
841844
if (!TestUI.resultVisible && shouldInsertTabCharacter) {
842845
event.preventDefault();
843-
handleChar("\t", TestInput.input.current.length);
846+
await handleChar("\t", TestInput.input.current.length);
844847
setWordsInput(" " + TestInput.input.current);
845848
return;
846849
}
@@ -859,7 +862,7 @@ function handleTab(event: JQuery.KeyDownEvent, popupVisible: boolean): void {
859862
// insert tab character if needed
860863
if (shouldInsertTabCharacter) {
861864
event.preventDefault();
862-
handleChar("\t", TestInput.input.current.length);
865+
await handleChar("\t", TestInput.input.current.length);
863866
setWordsInput(" " + TestInput.input.current);
864867
return;
865868
}
@@ -936,7 +939,7 @@ $(document).on("keydown", async (event) => {
936939

937940
//tab
938941
if (event.key === "Tab") {
939-
handleTab(event, popupVisible);
942+
await handleTab(event, popupVisible);
940943
}
941944

942945
//esc
@@ -1050,7 +1053,7 @@ $(document).on("keydown", async (event) => {
10501053
Monkey.type();
10511054

10521055
if (event.key === "Backspace" && TestInput.input.current.length === 0) {
1053-
backspaceToPrevious();
1056+
await backspaceToPrevious();
10541057
if (TestInput.input.current) {
10551058
setWordsInput(" " + TestInput.input.current + " ");
10561059
}
@@ -1088,7 +1091,7 @@ $(document).on("keydown", async (event) => {
10881091
}
10891092
}
10901093
} else {
1091-
handleChar("\n", TestInput.input.current.length);
1094+
await handleChar("\n", TestInput.input.current.length);
10921095
setWordsInput(" " + TestInput.input.current);
10931096
}
10941097
}
@@ -1145,7 +1148,7 @@ $(document).on("keydown", async (event) => {
11451148
)
11461149
) {
11471150
event.preventDefault();
1148-
handleChar(event.key, TestInput.input.current.length);
1151+
await handleChar(event.key, TestInput.input.current.length);
11491152
updateUI();
11501153
setWordsInput(" " + TestInput.input.current);
11511154
}
@@ -1161,7 +1164,7 @@ $(document).on("keydown", async (event) => {
11611164
const char: string | null = await LayoutEmulator.getCharFromEvent(event);
11621165
if (char !== null) {
11631166
event.preventDefault();
1164-
handleChar(char, TestInput.input.current.length);
1167+
await handleChar(char, TestInput.input.current.length);
11651168
updateUI();
11661169
setWordsInput(" " + TestInput.input.current);
11671170
}
@@ -1247,7 +1250,7 @@ $("#wordsInput").on("beforeinput", (event) => {
12471250
}
12481251
});
12491252

1250-
$("#wordsInput").on("input", (event) => {
1253+
$("#wordsInput").on("input", async (event) => {
12511254
if (!event.originalEvent?.isTrusted || TestUI.testRestarting) {
12521255
(event.target as HTMLInputElement).value = " ";
12531256
return;
@@ -1316,7 +1319,7 @@ $("#wordsInput").on("input", (event) => {
13161319

13171320
if (realInputValue.length === 0 && currTestInput.length === 0) {
13181321
// fallback for when no Backspace keydown event (mobile)
1319-
backspaceToPrevious();
1322+
await backspaceToPrevious();
13201323
} else if (inputValue.length < currTestInput.length) {
13211324
if (containsChinese) {
13221325
if (
@@ -1336,7 +1339,11 @@ $("#wordsInput").on("input", (event) => {
13361339
iOffset = inputValue.indexOf(" ") + 1;
13371340
}
13381341
for (let i = diffStart; i < inputValue.length; i++) {
1339-
handleChar(inputValue[i] as string, i - iOffset, realInputValue);
1342+
await handleChar(
1343+
inputValue[i] as string,
1344+
i - iOffset,
1345+
realInputValue
1346+
);
13401347
}
13411348
}
13421349
} else if (containsKorean) {
@@ -1373,7 +1380,7 @@ $("#wordsInput").on("input", (event) => {
13731380
}
13741381
for (let i = diffStart; i < inputValue.length; i++) {
13751382
// passing realInput to allow for correct Korean character compilation
1376-
handleChar(inputValue[i] as string, i - iOffset, realInputValue);
1383+
await handleChar(inputValue[i] as string, i - iOffset, realInputValue);
13771384
}
13781385
}
13791386

frontend/src/ts/test/caret.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,17 @@ function getTargetPositionLeft(
110110

111111
if (Config.tapeMode === "word" && inputLen > 0) {
112112
let currentWordWidth = 0;
113+
let lastPositiveLetterWidth = 0;
113114
for (let i = 0; i < inputLen; i++) {
114115
if (invisibleExtraLetters && i >= wordLen) break;
115-
currentWordWidth +=
116-
$(currentWordNodeList[i] as HTMLElement).outerWidth(true) ?? 0;
116+
const letterOuterWidth =
117+
$(currentWordNodeList[i] as Element).outerWidth(true) ?? 0;
118+
currentWordWidth += letterOuterWidth;
119+
if (letterOuterWidth > 0) lastPositiveLetterWidth = letterOuterWidth;
117120
}
121+
// if current letter has zero width move the caret to previous positive width letter
122+
if ($(currentWordNodeList[inputLen] as Element).outerWidth(true) === 0)
123+
currentWordWidth -= lastPositiveLetterWidth;
118124
if (isLanguageRightToLeft) currentWordWidth *= -1;
119125
result += currentWordWidth;
120126
}

frontend/src/ts/test/test-logic.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -418,13 +418,6 @@ export async function init(): Promise<void> {
418418
}
419419
}
420420

421-
if (Config.tapeMode !== "off" && language.rightToLeft) {
422-
Notifications.add("This language does not support tape mode.", 0, {
423-
important: true,
424-
});
425-
UpdateConfig.setTapeMode("off");
426-
}
427-
428421
const allowLazyMode = !language.noLazyMode || Config.mode === "custom";
429422
if (Config.lazyMode && !allowLazyMode) {
430423
rememberLazyMode = true;
@@ -507,7 +500,7 @@ export async function init(): Promise<void> {
507500
Funbox.toggleScript(TestWords.words.getCurrent());
508501
TestUI.setRightToLeft(language.rightToLeft);
509502
TestUI.setLigatures(language.ligatures ?? false);
510-
TestUI.showWords();
503+
await TestUI.showWords();
511504
console.debug("Test initialized with words", generatedWords);
512505
console.debug(
513506
"Test initialized with section indexes",

0 commit comments

Comments
 (0)