Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cursor rework #170

Merged
merged 20 commits into from
Nov 27, 2024
Merged

Cursor rework #170

merged 20 commits into from
Nov 27, 2024

Conversation

dfrg
Copy link
Collaborator

@dfrg dfrg commented Nov 11, 2024

This is a draft because some functionality (mostly around word/line selection) is still missing. I also commented out the fix from #163 because the skip_mandatory_break flag is now gone and I haven't dug into the logic from that PR.

Submitting this now so that people can take a look and see if it fixes some of the known bugs, particularly around newline handling.

note: #166 should probably land first

@nicoburns
Copy link
Contributor

I also commented out the fix from #163 because the skip_mandatory_break flag is now gone and I haven't dug into the logic from that PR.

Looks like you're now appending the newline cluster to the previous line rather than waiting to append it to the next line. We'll need to test, but I suspect that means that the bug fixed in #163 won't be present. The key thing not being which line the newline cluster ends up in, but that the cluster is "committed" eagerly rather than deferred to the next iteration of the loop (the fix in #163 was handling that deferred cluster commit handling the case that the next iteration of the loop was an inline box (and thus went down a different code path)).

If you wanted to keep the "newline is at the start of the following line" behaviour then think you could probably just reverse the append_cluster_to_line and try_commit_line statements (although IMO it's more intuitive if the newline cluster is part of the line it ends).

Copy link
Contributor

@nicoburns nicoburns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left some review comments on the layout code that I'm familiar with. I haven't reviewed the selection code which I don't fully understand yet.

parley/src/layout/line/greedy.rs Outdated Show resolved Hide resolved
Comment on lines +256 to 259
let whitespace = cluster.info().whitespace();
let is_newline = whitespace == Whitespace::Newline;
let is_space = whitespace.is_space_or_nbsp();
let boundary = cluster.info().boundary();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why you have switched from Boundary::Mandatory to Whitespace::Newline here? What (if anything) is the difference?

I'm assuming the reason you (can safely) check Boundary::Line before checking is_newline is because a newline will never be a Boundary::Line (it will always be a Boundary::Mandatory)? If this is not the case then I think you need to invert the order in which you check those conditions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The full explanation is at #132 (comment)

tldr; Unicode LBA specifies a break after the newline and a trailing newline doesn’t have a following cluster so we lose that information.

Re check order: good question. I’m going to assume that a newline sequence is not marked as a break opportunity because the code appears to work as is :)

parley/src/shape.rs Outdated Show resolved Hide resolved
parley/src/layout/line/greedy.rs Outdated Show resolved Hide resolved
@spirali
Copy link
Contributor

spirali commented Nov 11, 2024

I did some quick manual testing with 220f1bc and it seems that problem from #163 is gone. The logic behind #163 was to solve the problem that newline cluster is part of the next line and it broke placing inline boxes.

@dfrg
Copy link
Collaborator Author

dfrg commented Nov 11, 2024

I did some quick manual testing with 220f1bc and it seems that problem from #163 is gone. The logic behind #163 was to solve the problem that newline cluster is part of the next line and it broke placing inline boxes.

Thanks for confirming.

@dfrg
Copy link
Collaborator Author

dfrg commented Nov 11, 2024

If you wanted to keep the "newline is at the start of the following line" behaviour then think you could probably just reverse the append_cluster_to_line and try_commit_line statements (although IMO it's more intuitive if the newline cluster is part of the line it ends).

This was initially done to force a line to be created at the end of the layout when the last character was a newline. This didn’t really work anyway and I agree that it’s more intuitive (and correct!) to keep the newline attached to the line it ends.

@mwcampbell
Copy link
Contributor

Cursor::to_access_position returns None when the cursor is at the end of the text (e.g. after pressing Ctrl+End in vello_editor). This is incorrect, as it means we can't report an accessible text selection when the cursor is at the end.

@dfrg
Copy link
Collaborator Author

dfrg commented Nov 21, 2024

Ah, I see. We won’t have a downstream cluster at the end of the text. I’ll push a fix for this in a bit.

- correct condition for empty text in shaping
- compute actual layout height based on line max coordinates
- try to handle end of text conditions in AccessKit methods
@mwcampbell
Copy link
Contributor

Just tried the latest push with a screen reader. If I go to the end of the default text in vello_editor with Ctrl+End, then the accessible selection is where it should be. But if I then press Enter to start a new blank line at the end, then the accessible selection is on the previous line, after the hard line break, rather than on the new, blank line.

- use from_byte_index in from_accesskit instead of setting index directly to capture correct affinity for trailing newline
@dfrg
Copy link
Collaborator Author

dfrg commented Nov 21, 2024

This is probably a question of affinity. I pushed an update that might address it.

@mwcampbell
Copy link
Contributor

No change to the accessibility behavior when inserting a new blank line at the end.

@DJMcNab
Copy link
Member

DJMcNab commented Nov 27, 2024

I can confirm that works. The behaviour seems a tiny bit janky, as it allows both the selections. {lorem} ipsum and {lorem }ipsum. Firefox only allows the former on my machine.

@nicoburns
Copy link
Contributor

As it allows both the selections {lorem} ipsum and {lorem }ipsum. Firefox only allows the former on my machine.

FWIW, Chrome allows both on my machine.

@xStrom
Copy link
Member

xStrom commented Nov 27, 2024

On my Windows 11 machine both Firefox and Chrome allow both selections.

@dfrg
Copy link
Collaborator Author

dfrg commented Nov 27, 2024

Word selection and movement is entirely inconsistent across platforms/editors so I chose a set of behaviors that seemed to make sense. Including space-like “words” in mouse selection was intentional because you cannot otherwise extend the selection into a space without using the keyboard, and as noted, there is precedent for this in Chrome.

- fix word navigation for single letter words in RTL runs
- add phantom text run on last empty line for AccessKit
- restore Selection::refresh() logic
@dfrg
Copy link
Collaborator Author

dfrg commented Nov 27, 2024

@mwcampbell I pushed a change to add a "phantom" text run to the last empty line. The logic is a bit janky but I believe (hope?) the mapping to AccessKit is correct.

Replaces PlainEditor::active_text with PlainEditor::selected_text for cut/copy
@mwcampbell
Copy link
Contributor

The phantom run for a blank line at the end seems to work correctly. I can find just one more accessibility problem: if the text is entirely empty, there's a phantom run, but Cursor::to_accesskit_position returns None because there's no cluster. In that case, we need to return the node ID of the one phantom run and a character index of 0.

@dfrg
Copy link
Collaborator Author

dfrg commented Nov 27, 2024

Thanks Matt. I added a special case for that.

Assuming this is okay for accessibility, I don't plan on making any further changes unless new issues are raised by others.

@mwcampbell
Copy link
Contributor

The AccessKit integration now looks good to me.

@dfrg
Copy link
Collaborator Author

dfrg commented Nov 27, 2024

The AccessKit integration now looks good to me.

Great! I appreciate your patience and your help with testing.

@nicoburns
Copy link
Contributor

We have a cursor when there's no text! (rendered with Blitz + Parley):

Screenshot 2024-11-28 at 11 07 02

Copy link
Contributor

@nicoburns nicoburns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've done some testing of the vello_editor example, and this seems to resolve all of the robustness issues around newlines I was seeing before. I've also updated Blitz to use this branch, which seems to work well.

@dfrg
Copy link
Collaborator Author

dfrg commented Nov 27, 2024

Thanks Nico. I’m going to go ahead and merge this. If there are further issues they can be addressed in a follow up.

@dfrg dfrg dismissed xorgy’s stale review November 27, 2024 22:47

Requested changes have been addressed.

@dfrg dfrg added this pull request to the merge queue Nov 27, 2024
Merged via the queue into main with commit c0d158b Nov 27, 2024
20 checks passed
@dfrg dfrg deleted the cursor2 branch November 27, 2024 22:50
DJMcNab added a commit to DJMcNab/xilem that referenced this pull request Nov 28, 2024
I've had to add a hack to workaround a (new)? Parley bug where an empty initial text causes a crash
Note that later setting the text to be empty does not reproduce the crash
github-merge-queue bot pushed a commit to linebender/xilem that referenced this pull request Nov 28, 2024
The upstream PR has now been merged:
linebender/parley#170

I've had to add a hack to workaround
linebender/parley#186. This occurs because of
the first pass of layout with zero available area.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ctrl-End followed by Ctrl-Del makes the vello_editor example panic
8 participants