Scroll inputs in Mini Micro
Tags: miniscript, minimicro, programming, beginners
Published: 2023-04-16 21:40:16

If you do any UI programming, or even many kinds of games, sooner or later you're likely to need to react to the scroll wheel (or for trackpad users, the scroll gesture).  This is a somewhat thornier issue than it may seem, because there are multiple levels — including the hardware, the OS, and the middleware layers between those and your code — which all modify the scroll values you see.  Even I have been thoroughly confused by this.

So, this week I sat down, and with the help of some of the amazing MiniScript community on Discord, got it all sorted out.  Read on to see how you can use the scroll wheel in your own [Mini Micro](https://miniscript.org/MiniMicro) code!

## Reading the scroll wheel

First, the easy part: you can read the value reported by the scroll wheel using `key.axis("Mouse ScrollWheel")`.  Here's a simple loop you can try, just to see what values you get:

```
while true
    x = key.axis("Mouse ScrollWheel")
    if x then print x
    yield
end while
```

Run this code in Mini Micro, and then use the scroll wheel on your mouse, or the scroll gesture on your trackpad.  If you're using a mouse with a "clicky" scroll wheel, you'll probably find that a single notch on this wheel produces a value of about 0.1 or -0.1.  On some systems it might instead be about 0.05 (or -0.05).

On a trackpad, there are of course no "clicks," but the smallest value you can produce is probably still about 0.1 (or -0.1).

As you scroll faster, you'll see larger numbers.  Exactly what you get in this case depends greatly on your system software and settings.  This is the part that confused my for a long time:

> If you have "Mouse scroll speed" set to a higher value at the OS level, then the scroll wheel will report values that scale _nonlinearly_ with the amount of actual input.

So for example, I would see absolute values of 0.1 for a single click, but if I moved the wheel faster, causing maybe 5 or 10 clicks in a single frame, I would not see 0.5 or 1.0.  Instead I would see much larger values, like 11.4 or even 20.7.

On the same machine, if I used the track pad instead of the mouse wheel, I would see much more reasonable values: fast scrolling could cause values around 1 or 2, but never much higher than that.

## Interpreting the values

So, how are we to interpret the values returned from the Mouse ScrollWheel axis?  The short answer is, **multiply the scroll wheel value by 10 to get the number of rows to scroll**.  This will make a single tick of the scroll wheel, or a smallish use of the scroll gesture, move your content by 1 line, as the user would expect.

When the user is scrolling faster, you will always see bigger numbers — possibly _much_ bigger, when the user has their mouse scroll wheel set to a high speed.  But that's OK; presumably the user has done this because they prefer to scroll through content faster.  So again, just multiplying whatever value you get by 10 will produce a reasonable number of lines to scroll.

If you're scrolling by pixels rather than lines, then it's up to you to define how much "one line" of scrolling actually is, in pixels.  For example, instead of just multiplying by 10, you might want to multiply by 100.  Then a single click of the scroll wheel will move the content by 10 pixels, which will probably feel about right.

There is an important caveat, particularly if you are scrolling by row or line rather than by pixel.  That is, on some systems, **users may scroll by values smaller than 0.1**.  If you were to take the scroll input times ten, and immediately floor or round this to the nearest whole number, you would never see these small increments.  So instead you should add up the scroll values you get, _as floating-point values_, and only round or truncate for display.  (This is illustrated in the demo below.)

## Pay attention to direction

Be careful with how you interpret the sign of the scroll wheel values.  There is no fixed rule here, since this is a matter of preference (particularly since Apple threw a monkey wrench into the world back in 2011, by [suddenly reversing the default convention](https://slate.com/technology/2011/09/apple-s-mousetrap-why-did-apple-reverse-the-way-we-scroll-up-and-down.html)).  

For me, when I scroll the wheel "up" I expect to see content higher up in my document, meaning that any particular bit I keep my eye on moves down.  So when I scroll up, `key.axis("Mouse ScrollWheel")` reports positive values, and when I scroll down, it reports negative ones.  But for you, it could be the opposite.

So, just test it against your favorite text editor, or even this web browser, and make sure your content scrolls the same way.

## Demo

Here's a simple program you can use to test your scroll wheel or gesture — as well as your own understanding.

```
clear
scroll = 0

draw = function
    text.row = 25
    value = floor(scroll)
    for i in range(0, 24)
        text.inverse = ((value+i) % 10 == 0)
        print ((value + i) + " "*8)[:8]
    end for
end function

draw

while true
    prevScroll = scroll
    scroll = scroll - key.axis("Mouse ScrollWheel") * 10
    if scroll < 0 then scroll = 0
    if floor(scroll) != floor(prevScroll) then draw
    yield
end while
```

Here our content is a list of numbers, starting at 0 on the small end, but essentially unlimited on the high end.  Every 10th number is drawn in inverse text.  Using the scroll wheel or gesture, you can scroll this content just as if it were a giant text document or web page.  You should find that that the scroll speed and direction feel similar to other apps on your system.

Notice how the main loop works: our `scroll` variable is a sum of the scroll wheel values, not truncated in any way except to prevent them from going less than zero.  But we redraw only when the `floor` of this value is different from what it was on the previous frame.  (The `draw` method also floors the scroll value when drawing.)

Also note that what I'm actually adding up is the _negative_ of the scroll wheel value.  That's because when we scroll "down" in this document, the number at the top of the screen — which is what `scroll` actually controls — should get larger.  Scrolling "down" produces negative numbers, so we want to invert those to get larger top-number values.

(I explain this as if I'd worked it out in logic, but in fact I just tried it with `+` first, noticed the scrolling was backwards, and changed it to `-` to fix it.  This is a perfectly cromulent approach!)

## Conclusion

Now you know how to read the scroll wheel in Mini Micro, and how to interpret the results.  I hope this gives you a new tool to apply in your own programs!