linebender / parley

Rich text layout library
Apache License 2.0
228 stars 28 forks source link

Text editing behavior in BiDi is different between LeftArrow and Ctrl+LeftArrow #118

Open PoignardAzur opened 2 months ago

PoignardAzur commented 2 months ago

The easiest way to describe the problem is to show this snippet from the vello_editor example:

KeyCode::ArrowLeft => {
    self.selection = if ctrl {
        self.selection.previous_word(&self.layout, shift)
    } else {
        self.selection.previous_visual(
            &self.layout,
            self.cursor_mode,
            shift,
        )
    };
}

By default, pressing an arrow key moves your cursor in the visual direction of your key. But if you're doing ctrl+arrow, now the editor switches to "left means upstream, right means downstream". Both behaviors are mostly fine on their own (though "left means upstream" is latin-centric), but they're inconsistent with each other.

This behavior doesn't match either Chrome or Firefox.

(Parley's behavior with Home and End is also different from both Chrome and Firefox, but there's less of an inconsistency there, and Home/End support in BiDi is bad in both Chrome and Firefox anyway.)

nicoburns commented 2 months ago

What is the behavious in Chrome/Firefox/other text engines?

PoignardAzur commented 2 months ago

Chrome

Chrome fully commits to "The dir attribute is always right". This value is usually inherited from the <html> element. Which means if your page has a LTR direction, LeftArrow is always upstream and RightArrow is downstream. The opposite if your page has a RTL direction.

If you're selecting across elements with different directions, the anchor wins out.

Home and End go to the logical start and end no matter what.

Firefox

Firefox matches LeftArrow with visual left movements and vice versa. Doing so means the cursor's byte position may jump wildly when crossing BiDi boundaries.

This still applies when holding shift, but only in non-editable text.

For some reason, if you're holding Shift in editable text, Firefox goes "Screw you, left is upstream and right is downstream". As far as I can tell (from playing around with Yiddish Wikipedia) this applies no matter the page's default language and direction.

In all those cases, Ctrl+LeftArrow will go in the same direction as LeftArrow.

Here too, Home and End go to the logical start and end no matter what.


Now that I've taken the time to enumerate these behaviors, they don't seem that inconsistent.

Except for editing in Firefox. I suspect many RTL users just don't bother with Firefox, because this is strikingly annoying behavior.

xorgy commented 2 months ago

I think that the reason shift selection forces upstream/downstream in some browsers is a limitation around non-contiguous selections; or alternatively if you go the other way around, there is a case where a visual cursor at a direction boundary would result in a circumstance where none of the inner directionality text would be selected.

xorgy commented 2 months ago

Chromium's behavior makes sense I think, though the visual behavior is attractive for implementation reasons.

In the application case we would go with the locale default directionality, but there's also a case that overriding it should be an option (though this gets complicated when locale data come from the environment, and may not be present for). This is a benefit of using the visual direction, because we already have the visual order of the text figured out for layout, without locale knowledge.

PoignardAzur commented 2 months ago

I think that the reason shift selection forces upstream/downstream in some browsers is a limitation around non-contiguous selections; or alternatively if you go the other way around, there is a case where a visual cursor at a direction boundary would result in a circumstance where none of the inner directionality text would be selected.

I don't think so. Shift+Arrow works reasonably well in Firefox outside of editable text. I think this is a straight up bug.

dfrg commented 1 month ago

I believe the current state roughly matches the behavior of pango (or gedit). I have no objections to changing this.

This might be a good opportunity to discuss/document what our behavior should be and why we’ve decided as such.