-
Notifications
You must be signed in to change notification settings - Fork 28
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
base: main
Are you sure you want to change the base?
Cursor rework #170
Conversation
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 |
There was a problem hiding this 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.
let whitespace = cluster.info().whitespace(); | ||
let is_newline = whitespace == Whitespace::Newline; | ||
let is_space = whitespace.is_space_or_nbsp(); | ||
let boundary = cluster.info().boundary(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 :)
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. |
|
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
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
This is probably a question of affinity. I pushed an update that might address it. |
No change to the accessibility behavior when inserting a new blank line at the end. |
I wonder if that problem is that there's no actual character on that line and AccessKit doesn't have a text run node associated with it? |
When inserting a blank line at the end of the text, is there a Parley run for that blank line at the end? It appears that there is now parley run, and an AccessKit text run node, for the one blank line if the text is empty. |
I think there's one if the text is empty but there's no run for a final blank line in non-empty text. I guess we need to add one somehow and make sure it's mapped properly in the to/from acessskit methods. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Glad to see this coming along. :+ )
// let range = self | ||
// .selection | ||
// .focus() | ||
// .cluster_path() | ||
// .cluster(&self.layout) | ||
// .map(|c| c.text_range()) | ||
// .unwrap_or_default(); | ||
ActiveText::FocusedCluster(self.selection.focus().affinity(), "") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👁️ 👁️ Needs reintroduced.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some new debugging code that is more useful so I'll come up with a way to fit that in.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've ported to this in linebender/xilem#755. Overall, this works well, except for the mentioned privacy concerns.
Additionally, there seems to be a new crash if the layout
/buffer
is exactly empty when layout is called, but only the first time.
That is, having an empty initial text crashes, but editing so that the text is empty does not. I have not dug into this very far.
// For newline sequences and emoji, delete the previous cluster | ||
range.start | ||
} else { | ||
// Otherwise, delete the previous character |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Otherwise, delete the previous character | |
// Otherwise, delete the previous character (unicode scalar value) |
self.editor.cursor_mode, | ||
false, | ||
)); | ||
self.editor.set_selection( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a pre-existing concern, but I'll raise it now; should these methods call refresh_layout
? That would be necessary if specifically the text has changed since the last relayout, and I don't think anything protects against that.
} | ||
} | ||
|
||
pub(crate) fn maybe_extend(&self, focus: Cursor, extend: bool) -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A function of this name used to be public; it's used within editor.rs
(with extend: true
). Can we just expose an extend
function?
Is the fix for the problem with accessibility on the last line waiting on something from me? |
No, I plan on adding an empty run to the last line to see if that helps. Just haven’t gotten to it yet. I’ll ping here when the PR is updated. |
Pressing |
... also various fixes from review feedback
@@ -394,10 +372,12 @@ impl<'a, B: Brush> Cluster<'a, B> { | |||
/// Determines how a cursor attaches to a cluster. | |||
#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)] | |||
pub enum Affinity { | |||
/// Left side for LTR clusters and right side for RTL clusters. | |||
/// Cursor is attached to the character that is logically following in the | |||
/// text stream. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! This is so much better.
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