Skip to content

Fix pango_layout_move_cursor_visually()

Mashpoe requested to merge mashpoe/pango:fix-move-cursor-visually into main

This is a potential solution for #777. It has some quirks, but I think they are mostly a side-effect of the way Pango handles runs in each line.

After some additional debugging, I have figured out the exact cause of the issue. The reason it hasn't been caught by test-bidi.c is because it only occurs when a line ends in a weak run, and none of the test cases cover that situation. I would have included some additional test cases in this merge request, but the tests would have to be completely rewritten, since they also work under the assumption that every line ends in a strong run, and they don't account for some of the quirks that get introduced when a line ends with a weak run.

The aforementioned quirks seem to be a result of the way Pango handles runs in each line. In Firefox on Windows, and in Chromium-based browsers, lines within a layout appear to have a uniform strong direction, while lines within a Pango layout can each have different strong directions.

Here is an example:

daf
daf
daf
sdf
asdf
בּבּבּבּבּבּבּבּggg

Pango 1.51.1:

Pango 1.51.1

Microsoft Edge textarea:

Microsoft Edge

This distinction is important for cursor movement because the base direction of a line determines which line the cursor will move to when going off of the end of that line. When editing right-to-left text, a cursor going off of the left side of a line should be moved to the next line, but a cursor doing the same thing in left-to-right text should be moved to the previous line. This can create some goofy behavior in the presence of bidirectional text. Both Firefox (at least on Windows) and Microsoft Edge seem to handle these situations in textarea elements by taking into account the base direction of the entire layout. Firefox seems to handle it the best, and still moves the cursor in the correct direction within the weak text, while Microsoft Edge (and probably other Chromium-based browsers) only does logical cursor movement in these situations (this is handled differently in the search bar for some reason). Pango moves the cursor to the logical beginning or end of whatever line the cursor is moved to, which looks fine normally, but when that line ends with a weak run, the cursor will appear in between two runs.

The location the cursor moves to when going off of the end of a line is not addressed by this merge request because there are multiple ways to handle it, and I consider that to be beyond the scope of this issue. If the wrong approach to moving between lines is used, there are situations where a cursor won't be able to move beyond a certain point. This fix only addresses movement within a single line.

Despite how simple the changes are here, it was a major pain for me to get a working solution that didn't fail the preexisting tests, since every minor change seemed to break something.

For some reason, the code snippet I gave in #777 didn't work well with MSVC due to what appears to be an encoding issue, and I didn't want to spend too much extra time trying to figure that out. I managed to get a working test file by editing test-bidi.c, which I have provided here as an attached file. When running my test with the current version of Pango, the cursor will get stuck, and the value G_MAXINT will be printed. With my changes, the cursor will be moved to the correct position and the expected value will be printed.

test.c

I would really appreciate any feedback on this, since I spent a lot of time trying to get this working.

EDIT:

I realized that the reason text in browsers looked different from the text that Pango renders is that the auto_dir flag was set to TRUE. This made me realize how movement between lines is probably supposed to work, and it even brought some additional edge cases to my attention. I have updated this merge request to handle all cases that I could think of, and I will explain all of the additional changes in the comments.

Edited by Mashpoe

Merge request reports