Slider mouseWheel increment


#1

hi, 

We had some beta-testers reporting that using the mouseWheel on windows with integer sliders (interval = 1) the smallest step was sometimes 2 or 7 according to the knob range.

I just tested and the problem seems related to the way getMouseWheelDelta() is computed.

for example:

  • value = 4.0, range = [-48; 48]; wheel.deltaY = 0.2343 => delta = 1.6875, newValue = 6.0
  • value = 4.0, range = [-100; 100]; wheel.deltaY = 0.2343 => delta = 7.031, newValue = 11.0 

the hardcoded 0.15f factor multiplied by the (arbitrary?) 0.2343 mouseWheel increment means we're incrementing by ~ 3.5% of the range at each step. Which means this kind of behaviour is going to happen as soon as the range is bigger than 29.

On mac I have the following:

  • value = 4.0, range = [-48; 48]; wheel.deltaY = 0.001953 => delta = 0.014 (clipped to interval=1) , newValue = 5.0
  • value = 4.0, range = [-100; 100]; wheel.deltaY =0.001953 => delta = 0.0585 (clipped to interval=1), newValue = 5 

I'm not sure about the best way to fix that. but the ratio between windows and mac regarding the wheelIncrement makes it hard to deal with. Shouldn't we simply use interval as delta (if interval is non-zero)?


#2

It's tricky on Windows because lots of different mice produce different increment values - when I added the hard-coded bodge numbers it was based roughly on trying to make the mice I had all produce roughly similar speeds when I used them in different OSes. I'm also really not sure what I could do to make it do what you need!


#3

I think we could just clip the delta so that it's not greater than interval (if interval != 0.0). That would solve the issue on windows and make it closer to the expected behaviour.

On mac, the delta is so small it is clipped upward to interval anyway.

On Windows the situation is reversed because the delta can be 100x greater than mac so preventing it to be bigger than interval seems right and would unify behaviour accross platforms.

For float sliders (interval ~ 0.0) the situation is not easy though. It's like we don't have enough information about event sampling density, scroll speed, sensitivity and inertia to compute something really reliable accross devices. 

 


#4

I don't think clipping is the right approach here. In fact, on previous software package that I wrote, I got complaints from customers who have some fancy mice on Windows which have a deliberate button/wheel to increment two/four/etc. notches and clipping makes these mice work incorrectly.

However, after some testing with a standard external USB mouse on all OS, I agree that JUCE is inconsistent on the way it handles mouse wheel data. The correct delta for a single notch turn on a standard USB mouse should be 1/256 in JUCE. This is also what JUCE currently does on OS X. The windows code, however, currently returns 60/256 and linux does 50/256. For example, to make JUCE consistent with OS X, doMouseWheel needs to be changed in modules/juce_gui_basics/native/juce_win32_Windowing.cpp line 1817:


    static float convertWheelDeltaUnitsFromWParam (const WPARAM wParam)
    {
        const short hiword = (short) HIWORD (wParam);
        return  jlimit (-1000.0f, 1000.0f, static_cast<float> (hiword) / WHEEL_DELTA);
    }
    void doMouseWheel (const WPARAM wParam, const bool isVertical)
    {
        updateKeyModifiers();
        const float amount = convertWheelDeltaUnitsFromWParam (wParam);
        MouseWheelDetails wheel;
        wheel.deltaX = isVertical ? 0.0f : amount / -256.0f;
        wheel.deltaY = isVertical ? amount / 256.0f : 0.0f;
        wheel.isReversed = false;
        wheel.isSmooth = false;
        wheel.isInertial = false;
        Point<float> localPos;
        if (ComponentPeer* const peer = findPeerUnderMouse (localPos))
            peer->handleMouseWheel (0, localPos, getMouseEventTime(), wheel);
    }

The following ifndef is also needed somewhere at the top of juce_win32_Windowing.cpp for some older Visual Studio versions:


#ifndef WHEEL_DELTA
 #define WHEEL_DELTA                       120 // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx
#endif

On linux lines 2230/2231 of modules/juce_gui_basics/native/juce_linux_Windowing.cpp needs to be changed.

However, I fear that these changes will not only break existing code but also result in large inconsistencies when trackpad gestures are used for scrolling. Jules put in these conversions so that scrolling speed on track pads is roughly the same on all OSes. What JUCE really needs to do is to figure out if the mouse wheel change is coming from a "real" mouse wheel or if it is a two-finger scroll etc. If it is coming from a real notched mouse wheel then the above changes need to be used, otherwise the current code is the correct conversion. 

On OS X, it is possible to use hasPreciseScrollingDeltas to distinguish between the two, but I am not sure how to do this on Windows/Linux. Any suggestions?

 


#5

I agree that clipping shouldn't be the solution if possible (this is more important for float sliders)

What I was just trying to show was that the current implementation when interval=1 is always clipping upward anyway on mac so that delta is at least equal to interval whereas according to slider range delta can be much greater than interval on windows.

If we want to support all mouseWheels sensitivities properly one problem is that there's no way to know the begin/end of a mouseScroll gesture like we can for mouseDown/Drag/Up. 

In an ideal scenario rounding should use something like round(initialValue + cumulativeDelta, interval).

But since we miss the information we're constrained to perform the rounding at each step and propagate rounding error.

 


#6

FWIW, Qt uses two different values

 

http://doc.qt.io/qt-5/qwheelevent.html#pixelDelta

http://doc.qt.io/qt-5/qwheelevent.html#angleDelta


#7

On Windows, you can probably detect if the value is equal to WHEEL_DELTA

In that case, this is probably a standard stepped mouse.

 

From Microsoft documentation

The delta was set to 120 to allow Microsoft or other vendors to build finer-resolution wheels (a freely-rotating wheel with no notches) to send more messages per rotation, but with a smaller value in each message. To use this feature, you can either add the incoming delta values until WHEEL_DELTA is reached (so for a delta-rotation you get the same response), or scroll partial lines in response to the more frequent messages. You can also choose your scroll granularity and accumulate deltas until it is reached.