Bug 220175 - [Cocoa] MouseWheel events cannot be canceled with Table when using 2-fingers scrolling
Summary: [Cocoa] MouseWheel events cannot be canceled with Table when using 2-fingers ...
Status: CLOSED WONTFIX
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 4.8   Edit
Hardware: Macintosh Mac OS X
: P3 normal with 3 votes (vote)
Target Milestone: ---   Edit
Assignee: Platform-SWT-Inbox CLA
QA Contact: Lakshmi P Shanmugam CLA
URL:
Whiteboard: stalebug
Keywords: helpwanted, triaged
Depends on:
Blocks:
 
Reported: 2008-02-25 06:42 EST by Nicolas Richeton CLA
Modified: 2020-08-01 09:20 EDT (History)
8 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Nicolas Richeton CLA 2008-02-25 06:42:25 EST
event.doit = false; does not cancel a mousewheel with a Table widget, but it does with a Canvas. 
 
Usecase : I'd like to replace the mousewheel behavior by my own. I need to get the event and cancel it before I start my own code using the event data.

Tested only on OSX, but it's probably the same on other platforms.
Comment 1 Felipe Heidrich CLA 2008-03-19 17:17:14 EDT
Works for me with HEAD

public static void main (String [] args) {
    Display display = new Display ();
	Shell shell = new Shell (display);
    Table table = new Table(shell, SWT.BORDER);
    table.setBounds(10, 10, 400, 400);
    TableColumn c1 = new TableColumn(table, SWT.NONE); c1.setText("c1");
    TableColumn c2 = new TableColumn(table, SWT.NONE); c2.setText("c2");
    TableColumn c3 = new TableColumn(table, SWT.NONE); c3.setText("c3");
    for (int i = 0; i < 50; i++) {
            TableItem item = new TableItem(table, SWT.NONE);
            item.setText(new String[] {"a "+i, "b "+i, "c "+i});
    }
    c1.pack();
    c2.pack();
    c3.pack();
    table.addListener(SWT.MouseWheel, new Listener() {
    	public void handleEvent(Event event) {
    		event.doit = false;
    	}
    });
    shell.open ();
    while (!shell.isDisposed ()) {
            if (!display.readAndDispatch ()) display.sleep ();
    }
    display.dispose ();
 }

tested on intel mac 10.5
SWT latest from HEAD 20080319
Comment 2 Zviki Cohen CLA 2008-03-24 07:19:08 EDT
As I understand, you cannot cancel the event using a Listener. You need to use a filter for that. This is inherent in the design. Listeners are set up in the level of the display. 

In your example:
display.addFilter(SWT.MouseWheel, new Listener() {
        public void handleEvent(Event event) {
                event.doit = false;
        }
    });

Naturally, once you get the event in the display level, you will get all the events for the display. Hence, the above code will disable the mouse wheel for the entire workbench. You will need to test that this event was sent to your widget, either by testing event.widget or by checking that the event.x/y falls in the bounds of your widget.

Hence, this is not a bug, but inherent behavior. There's is a smaller bug, though. Even if you set up the filter as mentioned above, scroll events fired on the scrollbars are not caught. At least not on OS X (did not test on other platforms). This is a different issue and should be opened as a separate bug.
Comment 3 Nicolas Richeton CLA 2008-03-25 04:22:10 EDT
To Felipe
I tried your snippet with Tiger and SWT 3.3 and 3.4M5 and mouse wheel is still working. I guess this issue was fixed in HEAD and will reach in 3.4 final. Any idea of the issue # ?
Thanks

To Zviki
I'll try the code you posted, this could be a more compatible way to do it, but we should be able to cancel events from a listener, SWT is designed this way.
Thanks

Comment 4 Felipe Heidrich CLA 2008-03-25 16:08:18 EDT
>I guess this issue was fixed in HEAD and will reach in 3.4 final.

Please, test with head, make sure it works:
http://www.eclipse.org/swt/cvs.php

Sorry, I don't know what changed in the code that fixed this problem.

Comment 5 Nicolas Richeton CLA 2008-03-26 07:07:10 EDT
With the latest version of SWT, MouseWheel is still active for me. 
Macbook, Tiger 10.4.11, SWT 20080326.

I'm generating mousewheel events using the touchpad. 

Some events seem canceled, because mousewheel does not work as well as with doit=true.
Comment 6 Felipe Heidrich CLA 2008-03-26 11:02:04 EDT
Kevin, can you reproduce this problem on your macbook ?
Comment 7 Kevin Barnes CLA 2008-03-26 11:36:28 EDT
How do I generate mouse wheel events with my touchpad?

For the record I'm running Leopard on a Powerbook (PPC processor) so I may not be able to reproduce this. I'll try though.
Comment 8 Nicolas Richeton CLA 2008-03-26 12:05:17 EDT
You can use 2 fingers on the touchpad to scroll the control which is under the mouse. This will generate mousewheel events.

I'm not sure this is supported by your powerbook
Comment 9 Kevin Barnes CLA 2008-03-26 14:50:35 EDT
Thanks Nicolas.

Felipe: My hardware doesn't support this, so the answer to you question is no.
Comment 10 Nicolas Richeton CLA 2009-03-16 08:49:03 EDT
Made a new test on Leopard 10.5.6/Intel/Macbook  and Eclipse 3.4.2 
- Events are canceled when using the mousewheel with a bluetooth mouse
- Events are NOT canceled when using the 2-fingers scrolling with the touchpad.  (Some events seems canceled but not all : the table is still moving)

(I renamed this bug to match the real problem)
Comment 11 Silenio Quarti CLA 2012-06-22 12:54:28 EDT
Still a problem in cocoa.
Comment 12 Silenio Quarti CLA 2012-06-22 15:00:49 EDT
Lakshim, please investigate this one.  The problem on cocoa only happens with a trackpad or magic mouse. NSEvent.hasPreciseScrollingDeltas() and NSEvent.scrollingDeltaY() might help.
Comment 13 Markus Persson CLA 2014-06-05 07:29:09 EDT
I have investigated this. As Silenio points out, NSEvent.hasPreciseScrollingDeltas() and NSEvent.scrollingDeltaY() are good clues (although I didn't find this bug report until after my investigation).

I'll explain what happens, and partial solutions. Please note that although I mostly restrict the explanation to the Y direction, everything equally well applies to scrolling in the X direction. Gestures are included at the end.

Basically, the NSEvent property deltaY represents scrolling as a floating point value in units of a line of text (line height). But with Lion (10.7), the NSEvent property scrollingDeltaY was introduced, along with the boolean hasPreciseScrollingDeltas. If the latter is false, scrollingDeltaY has the same value as deltaY. This is typically an indication that the event source is a legacy mouse wheel.

But if hasPreciseScrollingDeltas is true, scrollingDeltaY represents scrolling in text size independent length units, most likely "points", which in Apple parlance is the same as the dimension of a pixel (unless the display is in "HiDPI" mode, in which case it is some multiple of a pixel dimension). This happens when you use two finger scrolling on a Magic Trackpad, and presumably with a Magic Mouse.

Now, what happens is that if you scroll slowly with a Magic Trackpad, most events will arrive with deltaY == 0.0, but always with scrollingDeltaY != 0.0. During my testing, on a non-HiDPI display, scrollingDeltaY was always integer values. It seems the small scrolling amounts are accumulated internally in Cocoa and delivered with a non-zero deltaY once they reach some threshold (seemingly amounting to around 4 pixels). For faster scrolling, deltaY is proportional to scrollingDeltaY (divided by line height, 10 in my case).

The problem is that SWT, in Control.scrollWheel(), only calls the event handlers if deltaY is non-zero. Thus, with slow scrolling, the event handlers get no chance to set event.doit to false. The events propagate to the native widgets, which will scroll a few pixels.

To fix this, there are a couple of ways, depending what you want to achieve. In either case, the extra properties on NSEvent needs to be added to the PI. (Pre-Lion could easily be handled too.)

If you simply want to be able to cancel the events, you could just change the condition of whether to call the handler in Control.scrollWheel() to scrollingDeltaY != 0.0. In this case, you should let Event.count be zero if deltaY is exactly zero in Control.sendMouseEvent().

However, if you want to process the scroll values, perhaps direct them to some other component, this results in jerky motion. Often more jerky than when using a scroll wheel. Even worse, since SWT currently ensures that Event.count is non-zero, some handler implementations take this for granted and scrolls in some direction even if event.count is zero. (This can be seen in the proposed workarounds to Bug 321707, which will scroll down in this case.)

So, a big stumbling block is that current SWT clients are not prepared to handle scrolling in smaller amounts than one line. Thus, I can't see a way to reliably fix this for all existing clients listening for mouse wheel events.

But, if clients opt in to it, the scrolling amount could be delivered to mouse wheel listeners in smaller units, by setting Event.detail to a new value like SWT.SCROLL_POINTS, just like it today can be SWT.SCROLL_LINE or SWT.SCROLL_PAGE for vertical scrolling (at least implemented on Windows). While that unit information today is lost when translating to a MouseEvent, it could likely be added. One additional hurdle is that since Event.count is an integer, the unit may have to be smaller than a "point", to cope with slow scrolling on HiDPI displays. Perhaps a 16th of a point?(Actual pixels cannot be used, since you might scroll across displays with different DPI.)

Alternatively, since the jerkiness might be imperceptible, sub-point scrolling may be accumulated in SWT, and delivered in whole point increments. In this case, however, it is important to always call the handler even if the count is zero, so that is may cancel the event.

For pan gestures, this is slightly different, since the documentation for GestureEvent today says that the xDirection and yDirection values are in pixels for a pan gesture. This is simply incorrect with the current implementation on Cocoa. Currently, they are actually delivered in "line height" units, although I'm not sure how that is determined for non-text widgets.

Anyway, I think the right thing here is to correct the implementation to follow the documentation, while interpreting "pixel" as "point". I suspect the clients scaling up this value are few. This is very simple, as it most likely suffices to use scrollingDeltaY (and -X) directly. It seems like a valid assumption that hasPreciseScrollingDeltas is true when a gesture is active. But even if it isn't, there isn't a reliable way to convert from "line height" units, unless you always use an arbitrary factor like 10. No opt in should be needed.

Similar to precise mouse wheel events, since the x- and yDirection fields in Event and GestureEvent are integers, sub-point scrolling may be accumulated by SWT (in the Display instance, as for rotation and magnification) and delivered in whole points.
Comment 14 Ian Pun CLA 2017-06-21 15:18:30 EDT
Bug has been triaged. Visit https://wiki.eclipse.org/SWT/Devel/Triage for more information.
Comment 15 Eclipse Genie CLA 2020-08-01 09:20:11 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet. As such, we're closing this bug.

If you have further information on the current state of the bug, please add it and reopen this bug. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.