Skip to content

Commit 3949466

Browse files
committed
impr(tape mode): support RTL languages (@NadAlaba)
1 parent 1efd175 commit 3949466

File tree

5 files changed

+102
-76
lines changed

5 files changed

+102
-76
lines changed

Diff for: frontend/src/ts/controllers/input-controller.ts

+32-25
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
}
@@ -1141,7 +1144,7 @@ $(document).on("keydown", async (event) => {
11411144
await funbox.functions.preventDefaultEvent(event as JQuery.KeyDownEvent)
11421145
) {
11431146
event.preventDefault();
1144-
handleChar(event.key, TestInput.input.current.length);
1147+
await handleChar(event.key, TestInput.input.current.length);
11451148
updateUI();
11461149
setWordsInput(" " + TestInput.input.current);
11471150
}
@@ -1157,7 +1160,7 @@ $(document).on("keydown", async (event) => {
11571160
const char: string | null = await LayoutEmulator.getCharFromEvent(event);
11581161
if (char !== null) {
11591162
event.preventDefault();
1160-
handleChar(char, TestInput.input.current.length);
1163+
await handleChar(char, TestInput.input.current.length);
11611164
updateUI();
11621165
setWordsInput(" " + TestInput.input.current);
11631166
}
@@ -1243,7 +1246,7 @@ $("#wordsInput").on("beforeinput", (event) => {
12431246
}
12441247
});
12451248

1246-
$("#wordsInput").on("input", (event) => {
1249+
$("#wordsInput").on("input", async (event) => {
12471250
if (!event.originalEvent?.isTrusted || TestUI.testRestarting) {
12481251
(event.target as HTMLInputElement).value = " ";
12491252
return;
@@ -1312,7 +1315,7 @@ $("#wordsInput").on("input", (event) => {
13121315

13131316
if (realInputValue.length === 0 && currTestInput.length === 0) {
13141317
// fallback for when no Backspace keydown event (mobile)
1315-
backspaceToPrevious();
1318+
await backspaceToPrevious();
13161319
} else if (inputValue.length < currTestInput.length) {
13171320
if (containsChinese) {
13181321
if (
@@ -1332,7 +1335,11 @@ $("#wordsInput").on("input", (event) => {
13321335
iOffset = inputValue.indexOf(" ") + 1;
13331336
}
13341337
for (let i = diffStart; i < inputValue.length; i++) {
1335-
handleChar(inputValue[i] as string, i - iOffset, realInputValue);
1338+
await handleChar(
1339+
inputValue[i] as string,
1340+
i - iOffset,
1341+
realInputValue
1342+
);
13361343
}
13371344
}
13381345
} else if (containsKorean) {
@@ -1369,7 +1376,7 @@ $("#wordsInput").on("input", (event) => {
13691376
}
13701377
for (let i = diffStart; i < inputValue.length; i++) {
13711378
// passing realInput to allow for correct Korean character compilation
1372-
handleChar(inputValue[i] as string, i - iOffset, realInputValue);
1379+
await handleChar(inputValue[i] as string, i - iOffset, realInputValue);
13731380
}
13741381
}
13751382

Diff for: frontend/src/ts/test/caret.ts

+8-2
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
}

Diff for: frontend/src/ts/test/test-logic.ts

+1-8
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)