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

Use click event to determine modifier keys #22988

Merged
merged 2 commits into from
Jan 31, 2025

Conversation

smoores-dev
Copy link
Contributor

@smoores-dev smoores-dev commented Jan 10, 2025

Previously, editor elements had to listen for mouse_up events to determine when a click had completed. This meant that they only had access to modifier keys that were pressed during the mouse_up event.

This led to some incorrect user experiences, such as executing a ctrl+click if the user pressed ctrl after pressing the mouse button, but before releasing it.

This change adds a click event handler to EditorElement, and adds a modifier() method to the ClickEvent, which only includes the modifier keys that were pressed during both mouse down and mouse up. The code for handling link clicks has been moved into the click event handler, so that it's only triggered when the non-multi-cursor modifier was held for both the mouse down and mouse up events.

Closes #12752, #16074, #17892 (the latter two seem to be duplicates of the former!)

Release Notes:

  • Fixed a bug where pressing ctrl/cmd (or other modifiers) after mouse down but before mouse up still triggered ctrl/cmd+click behavior (e.g. "go to definition")

This comment was marked as resolved.

@maxdeviant maxdeviant changed the title Adds a platform click event with better semantics Add a platform click event with better semantics Jan 10, 2025
@smoores-dev

This comment was marked as resolved.

@cla-bot cla-bot bot added the cla-signed The user has signed the Contributor License Agreement label Jan 10, 2025

This comment was marked as resolved.

@smoores-dev
Copy link
Contributor Author

To reviewers:

  • Does this make sense as a strategy? It seemed like the most straightforward thing, to me: We were missing proper "click" semantics, so I've added a new (essentially, synthetic) click event at the platform level so that consumers don't have to add additional state management
  • I am not super familiar with this codebase — it's very possible that I did something silly or missed something obvious! Apologies in advance
  • I've only implemented the new event in the X11 and Wayland platforms because... well I couldn't figure out exactly how Windows or macOS were wired up? If someone can point me in the right direction, I'm happy to add those changes as well!

Copy link
Contributor

@mikayla-maki mikayla-maki left a comment

Choose a reason for hiding this comment

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

It seems like the issue is that the existing click event forces you to pick either the down or up events to work with, and so there's bugs in existing code due to modifiers present in one or the other, is that correct?

If so, I think a simpler solution would be to create a method on ClickEvent, maybe just modifiers() and click_count(), that computes this combined event that you are synthesizing here, and then fix the bugs upstream by calling this method. I don't think we want to hide the underlying nature of click events as being composed of two MouseEvents, as done in this PR.

@smoores-dev
Copy link
Contributor Author

smoores-dev commented Jan 10, 2025

It's not only that — the ClickEvent before this change was only ever emitted, manually, by the div element. So no click events were emitted at all if a user clicks on text in the editor, for example.

But that makes sense, I'm definitely not opposed to keeping the up and down properties on the ClickEvent and just adding modifiers() and click_count()! I think we would then need to track the entire previous MouseDownEvent (rather than just its modifiers) in the client state, which seems fine to me.

The approach here is modeled after web browser's click event semantics, which doesn't contain any references to the associated mouse down or up events!

@mikayla-maki
Copy link
Contributor

mikayla-maki commented Jan 10, 2025

It's not only that — the ClickEvent before this change was only ever emitted, manually, by the div element. So no click events were emitted at all if a user clicks on text in the editor, for example.

True, it is a tradeoff of this approach. I think generally we prefer to push decisions like this up to client APIs though, and so you do have to re-implement this stuff more often.

The approach here is modeled after web browser's click event semantics, which doesn't contain any references to the associated mouse down or up events!

That's a sensible approach we use most of the time as well, and I think that's the model we want the div APIs to follow! But at the GPUI Element level, we are fundamentally trying to build a thinner, more DIY kind of framework than what the web offers :)

@smoores-dev
Copy link
Contributor Author

smoores-dev commented Jan 10, 2025

That's a sensible approach we use most of the time as well, and I think that's the model we want the div APIs to follow! But at the GPUI Element level, we are fundamentally trying to build a thinner, more DIY kind of framework than what the web offers :)

Ah, ok! This is very helpful to know, thank you. So then it sounds like the right approach would look more like:

  • Keep the GPUI clients as they are: only dispatch events that correspond to actual hardware events, like MouseDown and MouseUp
  • Add a "synthetic" modifiers() helper method to ClickEvent.
  • Potentially update existing uses of click_event.up.modifiers and click_event.down.modifiers to use the new click_event.modifiers()? (I would guess "yes", as this kind of thing is what's causing the issue here in the first place)
  • Add some state to track the most recent MouseDownEvent and a ClickEvent dispatcher to editor/src/element.rs, so that we can fix the titular issue

Does that seem right?

@mikayla-maki
Copy link
Contributor

That does seem correct!

@smoores-dev smoores-dev force-pushed the click-event-modifiers branch from e46e70d to 3f44a7a Compare January 11, 2025 01:10
@smoores-dev smoores-dev changed the title Add a platform click event with better semantics Use click event to determine modifier keys Jan 11, 2025
@smoores-dev
Copy link
Contributor Author

Ok, awesome,. Thanks for the guidance @mikayla-maki! I think I've implemented this as described now. I've confirmed that this approach also fixes the issue (at least for me, on Linux, with Wayland)

@smoores-dev
Copy link
Contributor Author

Howdy! This is one of those things that I'm reminded of every day because I constantly trigger it haha. Just bumping this in case it was forgotten, totally understand that you all may have other priorities and that code review is work!

@smoores-dev
Copy link
Contributor Author

smoores-dev commented Jan 16, 2025

Oh, funny.

error: unused variable: `position_map`
   --> crates/editor/src/element.rs:669:9
    |
669 |         position_map: &PositionMap,
    |         ^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_position_map`
    |
    = note: `-D unused-variables` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(unused_variables)]`

This is only failing on Windows and macOS because that position_map argument is only used inside a #[cfg(any(target_os = "linux", target_os = "freebsd"))] macro. I added an allow(unused_variables) with the inverse condition — let me know if that wasn't the right move!

@smoores-dev
Copy link
Contributor Author

I see a lot of green checkmarks! :D @mikayla-maki does this look good to you?

@maxdeviant
Copy link
Member

I see a lot of green checkmarks! :D @mikayla-maki does this look good to you?

@smoores-dev Please don’t spam the PR thread for reviews. We will get to this when we have bandwidth.

@mikayla-maki
Copy link
Contributor

Hi @smoores-dev,

I've been heads down working on #22632, and am able to review PRs again!Unfortunately, it looks like that PR has caused several conflicts with this one.

This implementation is looking really good though, I like having a proper click handler in the editor for all of that multicursor logic. Once the conflicts are resolved I'd be happy to merge this :D

@mikayla-maki mikayla-maki enabled auto-merge (squash) January 30, 2025 19:33
Previously, editor elements had to listen for mouse_up
events to determine when a click had completed. This meant
that they only had access to modifier keys that were
pressed during the mouse_up event.

This led to some incorrect user experiences, such as
executing a ctrl+click if the user pressed ctrl after
pressing the mouse button, but before releasing it.

This change adds a click event handler to EditorElement,
and adds a modifier() method to the ClickEvent, which
only includes the modifier keys that were pressed during
both mouse down and mouse up. The code for handling link
clicks has been moved into the click event handler, so that
it's only triggered when the non-multi-cursor modifier was
held for both the mouse down and mouse up events.
auto-merge was automatically disabled January 30, 2025 19:54

Head branch was pushed to by a user without write access

@smoores-dev smoores-dev force-pushed the click-event-modifiers branch from 5101a88 to c9d4bd6 Compare January 30, 2025 19:54
@smoores-dev
Copy link
Contributor Author

Yay, thanks @mikayla-maki! Just rebased onto main and pushed!

@mikayla-maki mikayla-maki merged commit c28a420 into zed-industries:main Jan 31, 2025
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla-signed The user has signed the Contributor License Agreement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

click+ctrl goes to all references/definition
3 participants