Some caret navigation shortcuts in the editor are reversed for RTL languages

Some keyboard shortcuts like Ctrl+Right Arrow, and Ctrl+Left Arrow (along with the Ctrl+Shift combinations) don’t match the visual order of text in RTL languages.
For example, the right arrow combinations navigate to the word on the left instead of the right.

character-by-character navigation using the left and right arrows works as expected.

This happens for RTL fields regardless of the interface language.

Curiously, when RTL text is typed in a LTR field, the Ctrl+Arrow combinations works, but not other combinations.

So it seems each operation is handled differently somehow.

It may be understandable that RTL text in a LTR field does not work properly and vice versa due to complex directionality issues,
but for purely RTL text in RTL fields, this should work.

This is a video to demonstrate the problem:

https://drive.google.com/file/d/1t5GwNhfIk20MwwSW7WBU5zfjkYo2Cp43/view

I figured out a solution, though I’m not sure if this is the right way to go about it.
This makes caret navigation works as expected on RTL fields with RTL text.

--- a/qt/ts/src/editor.ts
+++ b/qt/ts/src/editor.ts
@@ -47,6 +47,10 @@ function triggerKeyTimer() {
     }, 600);
 }

+interface Selection {
+    modify(s: string, t: string, u: string): void;
+}
+
 function onKey(evt: KeyboardEvent) {
     // esc clears focus, allowing dialog to close
     if (evt.which === 27) {
@@ -59,6 +63,27 @@ function onKey(evt: KeyboardEvent) {
         focusPrevious();
         return;
     }
+
+    const selection = window.getSelection();
+    let granularity = 'character';
+    let alter = 'move';
+    if(evt.ctrlKey) {
+       granularity = 'word';
+    }
+    if(evt.shiftKey) {
+       alter = 'extend';
+    }
+    if(evt.which === 39) {
+        selection.modify(alter, 'right', granularity);
+        evt.preventDefault();
+        return;
+    }
+    else if(evt.which === 37) {
+        selection.modify(alter, 'left', granularity);
+        evt.preventDefault();
+        return;
+    }
+
     triggerKeyTimer();
}

Note that the Selection interface should be augmented to allow using the nonstandard function modify
as described here.

1 Like

I don’t know enough about RTL text to know what the correct behaviour here is - perhaps some other RTL users could chime in as well? Presumably this code needs to check for the direction the field and/or body direction is set to?

Added to https://github.com/ankitects/help-wanted/issues/14

I don’t think the code needs to check for direction.
This is because the right and left arguments in Selection.modify() move selection right and left regardless of the direction.of the field.
In contrast, there are also forward and backward as valid arguments that depend on direction.

see https://developer.mozilla.org/en-US/docs/Web/API/Selection/modify

I’m unsure how selection should behave exactly when text with different directions is mixed. My fix just ensures that keyboard navigation for pure RTL text in RTL fields matches visual order (as it’s the case with LTR text in LTR fields), though it needs further testing by more users.

I was just concerned about potential issues this change might unintentionally cause - by limiting it to RTL fields, if it does cause problems the number of affected users would be limited. It does sound like it’s something that would benefit from a longer beta testing period (ie not something for 2.1.29)

I agree - no need to mess with LTR fields.
I presume if(currentField.dir === "rtl") is necessary.

This seems to be an issue in Qt (not limited to Anki).

@mmdj2, since you use a right-to-left language, is this a problem you encounter?

It’s pretty a minor issue, but it will be nice to see it fixed.

I do have that problem.
right/left arrows work fine whether I’m writing in rtl or not.
but ctrl + right/left is reversed.
it just happens in card editor, I tested this in browser search bar and it was working fine.