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

Double-click doesn't work on custom widget on OS X under Oracle Java 7 #14

Open
martinrleon opened this issue Feb 21, 2013 · 3 comments

Comments

@martinrleon
Copy link

I wish I could provide the exact cause for this but all I can do is state the problem and my workaround.

This is part of a test that has been running for a long time and as part of the migration to Java 7 stopped working (I have a customized build of fest-swing from the code as of Feb 15, 2013).

The test double-clicks on a custom widget to activate it's 'Edit' mode. This will not work with Java 7 / OS X / fest-swing. If I revert to Apple Java 6 the test works. And the original test has been running fine under fest-swing 1.2.1 / Java 6 / OS X for a very long time. Looks like the Oracle Java 7 is the root cause but I have no explanation.

I haven't ruled out that it isn't some quirk with the way our application handles mouse events that is only now coming to light as a result of the Java 7 update.

The old test did the following:

testRobot.doubleClick(wrappedWidget.asJButton())

Now the application registers these as 2 separate mouse pressed events with a click count of 1.

Anyways, I came up with a workaround, which is to send mouse-press events without the intervening mouse-release events. The application registers this as a proper double-click sequence. For what it's worth, here is the workaround:

public void clickCustomWidget(final CustomWidget wrappedWidget, int times) {
    final java.awt.Component source = wrappedWidget.asJButton();
    final java.awt.Point point = source.getLocationOnScreen();
    point.translate(source.getWidth() / 2, source.getHeight() / 2);

    testRobot.moveMouse(point);
    testRobot.pressMouse(MouseButton.LEFT_BUTTON);

    int count = 0;

    for (int x = times; x > 1; x--) {
        testRobot.pressMouse(MouseButton.LEFT_BUTTON);
        awtRobotDelay(200);
    }
}
@martinrleon
Copy link
Author

Well, the above code didn't always work, so I came up with the following (added a releaseMouse call).

    public void clickCustomWidget(final CustomWidget wrappedWidget, int times) {
        final java.awt.Component source = wrappedWidget.asJButton();
        final java.awt.Point point = source.getLocationOnScreen();
        point.translate(source.getWidth() / 2, source.getHeight() / 2);

        testRobot.moveMouse(point);
        testRobot.pressMouse(MouseButton.LEFT_BUTTON);
        testRobot.releaseMouse(MouseButton.LEFT_BUTTON);

        awtRobotDelay(100);

        for (int x = times; x > 1; x--) {
            testRobot.pressMouse(MouseButton.LEFT_BUTTON);
            testRobot.releaseMouse(MouseButton.LEFT_BUTTON);
            awtRobotDelay(100);
        }
    }

But then I HAD to ask myself why does the above code work, and not the BasicRobot doClick() method? I carefully compared and realized that the BasicRobot.doClick() method moves the mouse for every pressMouse() call because it calls pressMouse( where, mask ) instead of just clicking the mouse (pressMouse(mask)).

So I modified it to move the mouse only once before clicking and then calling pressMouse(mask) instead and all my double-clicking problems went away!

We have experience sporadic problems with double-clicking, maybe this has been the problem all along.

https://github.com/alexruiz/fest-swing-1.x/blob/master/fest-swing/src/main/java/org/fest/swing/core/BasicRobot.java#L467

I hope this helps

  private void doClick(@Nullable Component c, @Nonnull Point where, @Nonnull MouseButton button, int times) {
    int mask = button.mask;
    int modifierMask = mask & ~BUTTON_MASK;
    mask &= BUTTON_MASK;
    pressModifiers(modifierMask);
    // From Abbot: Adjust the auto-delay to ensure we actually get a multiple click
    // In general clicks have to be less than 200ms apart, although the actual setting is not readable by Java.
    int delayBetweenEvents = settings.delayBetweenEvents();
    if (shouldSetDelayBetweenEventsToZeroWhenClicking(times)) {
      settings.delayBetweenEvents(0);
    }
    if (c == null) {
      /* ONLY MOVE THE MOUSE ONCE */
      eventGenerator.moveMouse(where.x, where.y);
      eventGenerator.pressMouse(mask);
      for (int i = times; i > 1; i--) {
        eventGenerator.releaseMouse(mask);
        eventGenerator.pressMouse(where, mask);
      }
    } else {
      /* ONLY MOVE THE MOUSE ONCE */
      eventGenerator.moveMouse(c, where.x, where.y);
      eventGenerator.pressMouse(mask);
      for (int i = times; i > 1; i--) {
        eventGenerator.releaseMouse(mask);
        eventGenerator.pressMouse(mask);
      }
    }
    settings.delayBetweenEvents(delayBetweenEvents);
    eventGenerator.releaseMouse(mask);
    releaseModifiers(modifierMask);
    waitForIdle();
  }

@mperrando
Copy link

I am using version 1.2.1 on OSX 10.8.4

The proposed patch does not work for me. Maybe the issue is due to the 0 ms delay between events (see #21).

I solved simply by setting a larger timing in the BasicRobot.java file. First I tried with 10ms, but then I saw that even 1 ms is good enough.

if (shouldSetDelayBetweenEventsToZeroWhenClicking(times)) {
  settings.delayBetweenEvents(1);
}

@martinrleon, can you try this patch (I see the from the code you are using a newer version)? If it works maybe it could be considered for integration...

Hope this can help.

@mperrando
Copy link

I just realized my test were running in Java 6.
Running in Java 7, setting only the delay to 1ms didn't solve the issue.

But, inspired by @martinrleon, I've modified the pressMouse() method inside the loop, removing the "where" and the "component" parameters: the mouse is already in the right place, so it should be only necessary to perform the "click". While I left the first call as it is, without splitting in the moveMouse() and pressMouse() couple.

And, evetually, the combination of 1ms delay and the new pressMouse() call did the trick!

Here is the code that works both in Java 6 and Java 7.

    if (shouldSetDelayBetweenEventsToZeroWhenClicking(times)) settings.delayBetweenEvents(1);
    eventGenerator.pressMouse(c, where, mask);
    for (int i = times; i > 1; i--) {
      eventGenerator.releaseMouse(mask);
      eventGenerator.pressMouse(mask);
    }

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

No branches or pull requests

2 participants