Skip to content

Conversation

@Hixie
Copy link
Contributor

@Hixie Hixie commented May 30, 2023

Previously, if you changed buttons during a long-press gesture, if it was before the gesture was accepted we would discard it, and if it was after the gesture was accepted we would silently end it without firing any of the relevant events.

This silent cancelation behavior is terrible because it means there's no way for consumers to know what state they're in, so you end up with widgets that thing they're still being long-pressed even though nothing is happening.

We could change the behavior in three ways, as far as I can tell:

  • we could send a cancel event when you change buttons. This would introduce a new kind of transition (start->cancel) which I don't think we currently require people to support. This would therefore not fix existing code and would make future code more complicated to handle a really obscure user action that it seems unlikely anyone cares about.

  • we could send an end event when you change buttons. This would mean the action commits, even though the user is still holding the mouse button down. This seems slightly better than the previous option but still not ideal as it means nudging the mouse button commits you even though you're still holding the button down.

  • we could ignore button changes after the long-press has been accepted.

I implemented the last one in this PR.

@flutter-dashboard flutter-dashboard bot added f: gestures flutter/packages/flutter/gestures repository. framework flutter/packages/flutter repository. See also f: labels. labels May 30, 2023
@github-actions github-actions bot removed f: gestures flutter/packages/flutter/gestures repository. framework flutter/packages/flutter repository. See also f: labels. labels May 30, 2023
@goderbauer goderbauer added framework flutter/packages/flutter repository. See also f: labels. f: gestures flutter/packages/flutter/gestures repository. labels Jun 1, 2023
Copy link
Contributor

@Renzo-Olivares Renzo-Olivares left a comment

Choose a reason for hiding this comment

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

I agree this is a very obscure user interaction, option 1 did sound reasonable to me and it looks like we do something similar by handling down (accepted) -> cancel in TapGestureRecognizer.

@override
void resolve(GestureDisposition disposition) {
if (_wonArenaForPrimaryPointer && disposition == GestureDisposition.rejected) {
// This can happen if the gesture has been canceled. For example, when
// the pointer has exceeded the touch slop, the buttons have been changed,
// or if the recognizer is disposed.
assert(_sentTapDown);
_checkCancel(null, 'spontaneous');
_reset();
}
super.resolve(disposition);
}

But I agree with the point that it wouldn't fix current users. I wonder if in the future we want to provide the buttons in the gesture details object so a user could potentially handle this use-case by checking if the initial down buttons are the same as the end buttons.

Copy link
Contributor

Choose a reason for hiding this comment

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

From testing on macOS, after this first move4, when releasing the primary button, another move event is dispatched with the buttons set as 2.

Copy link
Contributor

Choose a reason for hiding this comment

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

This might be outside of the scope of this PR, but when testing on macOS and web with a local example app the buttons of this final PointerUpEvent is actually 0. Is this expected?

@Hixie
Copy link
Contributor Author

Hixie commented Jun 6, 2023

Can you elaborate on what you're seeing on macOS?

In general I would hope that we normalize our inputs so the same physical inputs turn into the same events at the Flutter level but that may be optimistic.

I'm happy to add tests for other sequences, to make sure they all make sense. Is that what you had in mind?

@Renzo-Olivares
Copy link
Contributor

Renzo-Olivares commented Jun 6, 2023

Using this example app with the change in this PR.

import 'package:flutter/material.dart';

void main() {
  runApp(const MainApp());
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: GestureDetector(
            onLongPressStart: (LongPressStartDetails details) {
              debugPrint('start');
            },
            onLongPressMoveUpdate: (LongPressMoveUpdateDetails details) {
              debugPrint('move');
            },
            onLongPressEnd: (LongPressEndDetails details) {
              debugPrint('end');
            },
            child: Container(
              color: Colors.green,
              height: 200,
              width: 200,
              child: const Text('Hello World!'),
            ),
          ),
        ),
      ),
    );
  }
}

and adding some logging in PrimaryPointerGestureRecognizer.addAllowedPointer and PrimaryPointerGestureRecognizer.handleEvent.

For the following click sequence.

  1. long press left click.
  2. sometime after long press also press the right click.
  3. release the left click.
  4. release the right click.

This is what I see in the logs for macOS native/and macOS chrome.

flutter: PrimaryPointerGestureRecognizer addAllowedPointer down _TransformedPointerDownEvent#b28e4(position: Offset(378.2, 287.3))
flutter: PrimaryPointerGestureRecognizer handleEvent _TransformedPointerDownEvent#a57f1(position: Offset(378.2, 287.3)) buttons 1
flutter: start
flutter: PrimaryPointerGestureRecognizer handleEvent _TransformedPointerMoveEvent#06cd2(position: Offset(378.2, 287.3)) buttons 3
flutter: move
flutter: PrimaryPointerGestureRecognizer handleEvent _TransformedPointerMoveEvent#fd781(position: Offset(378.2, 287.3)) buttons 2
flutter: move
flutter: PrimaryPointerGestureRecognizer handleEvent _TransformedPointerUpEvent#fad41(position: Offset(378.2, 287.3)) buttons 0
flutter: end

I was more or less wondering if we should test that extra move event when the left click is released. And if the pointer up event ending with buttons: 0 is expected. Looking at your test it looks like it should be buttons: 2.

@Hixie
Copy link
Contributor Author

Hixie commented Jun 14, 2023

I can add some tests that check that sequence and others, sure.

Previously, if you changed buttons during a long-press gesture, if it was before the gesture was accepted we would discard it, and if it was after the gesture was accepted we would silently end it without firing any of the relevant events.

This silent cancelation behavior is terrible because it means there's no way for consumers to know what state they're in, so you end up with widgets that thing they're still being long-pressed even though nothing is happening.

We could change the behavior in three ways, as far as I can tell:

- we could send a cancel event when you change buttons. This would introduce a new kind of transition (start->cancel) which I don't think we currently require people to support. This would therefore not fix existing code and would make future code more complicated to handle a really obscure user action that it seems unlikely anyone cares about.

- we could send an end event when you change buttons. This would mean the action commits, even though the user is still holding the mouse button down. This seems slightly better than the previous option but still not ideal as it means nudging the mouse button commits you even though you're still holding the button down.

- we could ignore button changes after the long-press has been accepted.

I implemented the last one in this PR.
@Hixie
Copy link
Contributor Author

Hixie commented Jun 15, 2023

@Renzo-Olivares PTAL

Copy link
Contributor

@Renzo-Olivares Renzo-Olivares left a comment

Choose a reason for hiding this comment

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

LGTM

@Renzo-Olivares
Copy link
Contributor

I think the broken google testing may have been unrelated since it was having some issues around the time when this PR was approved.

@Hixie Hixie added the autosubmit Merge PR when tree becomes green via auto submit App label Jul 5, 2023
@auto-submit auto-submit bot merged commit bc49cd1 into flutter:master Jul 6, 2023
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Jul 6, 2023
flutter/flutter@35085c3...bc49cd1

2023-07-06 [email protected] Allow long-press gestures to continue even if buttons change. (flutter/flutter#127877)
2023-07-06 [email protected] Enable unreachable_from_main lint - it is stable now!!1 (flutter/flutter#129854)
2023-07-06 [email protected] Update labeler to new label names (flutter/flutter#130040)
2023-07-05 [email protected] MergeableMaterial: Fix adding a slice and separating it (flutter/flutter#128804)
2023-07-05 [email protected] Update infrastructure issue template for new priority scheme (flutter/flutter#129741)
2023-07-05 [email protected] Fix typo in canvas example (flutter/flutter#129879)
2023-07-05 [email protected] Reland Fix AnimatedList & AnimatedGrid doesn't apply MediaQuery padding #129556 (flutter/flutter#129860)
2023-07-05 [email protected] Change from "created via performance template" to "from: performance template" (flutter/flutter#130035)
2023-07-05 [email protected] Removes deprecated APIs from v2.6 in `binding.dart` and `widget_tester.dart` (flutter/flutter#129663)
2023-07-05 [email protected] Add new hot reload case string (flutter/flutter#130008)
2023-07-05 [email protected] Manual roll Flutter Engine from 987b621eac4e to bd2e42b203e1 (32 revisions) (flutter/flutter#130023)
2023-07-05 [email protected] Add simple unit tests for annotations.dart file (flutter/flutter#128902)
2023-07-05 [email protected] fix a bug when android uses CupertinoPageTransitionsBuilder... (flutter/flutter#114303)
2023-07-05 [email protected] Add .env file support for  option `--dart-define-from-file` (flutter/flutter#128668)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autosubmit Merge PR when tree becomes green via auto submit App f: gestures flutter/packages/flutter/gestures repository. framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants