MidiKeyboardComponent GUI cut off in latest update

I just updated from 3.0.1 to 3.0.3 (Mac 10.9), and now, in both the Juce Demo and my app, the MidiKeyboardComponent's top and bottom keys are partially cut off at the ends. See attached screenshot of Juce Demo to see what I mean.

Any idea why?

 

I know you're busy Jules, but any word on this? I'm assuming it's a Juce bug, since it's happening in the Juce Demo, right?

Not really a bug.. they're just under the scroll buttons.

Yeah, but that wasn't happening before the update. The bottom key began cleanly after the scroll button. It just looks unprofessional, which in my case, for commercial software, is pretty important. Any chance we can get a fix on this so it behaves the way it did before 3.0.3?

So I went back and compared code changes in juce_MidiKeyboardComponent.cpp, found the change that caused the issue, and replaced with previous code. I'm sure you made these changes for a reason, but FYI here's what I did:

In the resized() method, from line 593 to the end of the method. I replaced this code:

        scrollDown->setVisible (canScroll && firstKey > (float) rangeStart);


        xOffset = 0;


        if (canScroll)

        {

            const int scrollButtonW = jmin (12, w / 2);

            Rectangle<int> r (getLocalBounds());


            if (orientation == horizontalKeyboard)

            {

                scrollDown->setBounds (r.removeFromLeft  (scrollButtonW));

                scrollUp  ->setBounds (r.removeFromRight (scrollButtonW));

            }

            else if (orientation == verticalKeyboardFacingLeft)

            {

                scrollDown->setBounds (r.removeFromTop    (scrollButtonW));

                scrollUp  ->setBounds (r.removeFromBottom (scrollButtonW));

            }

            else

            {

                scrollDown->setBounds (r.removeFromBottom (scrollButtonW));

                scrollUp  ->setBounds (r.removeFromTop    (scrollButtonW));

            }


            int endOfLastKey, kw;

            getKeyPos (rangeEnd, endOfLastKey, kw);

            endOfLastKey += kw;


            float mousePositionVelocity;

            const int spaceAvailable = w;

            const int lastStartKey = remappedXYToNote (Point<int> (endOfLastKey - spaceAvailable, 0), mousePositionVelocity) + 1;


            if (lastStartKey >= 0 && ((int) firstKey) > lastStartKey)

            {

                firstKey = (float) jlimit (rangeStart, rangeEnd, lastStartKey);

                sendChangeMessage();

            }


            int newOffset = 0;

            getKeyPos ((int) firstKey, newOffset, kw);

            xOffset = newOffset;

        }

        else

        {

            firstKey = (float) rangeStart;

        }


        getKeyPos (rangeEnd, kx2, kw2);

        scrollUp->setVisible (canScroll && kx2 > w);

        repaint();

with what was previously there:

        const bool showScrollButtons = canScroll && (((int) firstKey) > rangeStart || kx2 > w + xOffset * 2);

        

        scrollDown->setVisible (showScrollButtons);

        scrollUp->setVisible (showScrollButtons);

        

        xOffset = 0;

        

        if (showScrollButtons)

        {

            const int scrollButtonW = jmin (12, w / 2);

            

            if (orientation == horizontalKeyboard)

            {

                scrollDown->setBounds (0, 0, scrollButtonW, getHeight());

                scrollUp->setBounds (getWidth() - scrollButtonW, 0, scrollButtonW, getHeight());

            }

            else if (orientation == verticalKeyboardFacingLeft)

            {

                scrollDown->setBounds (0, 0, getWidth(), scrollButtonW);

                scrollUp->setBounds (0, getHeight() - scrollButtonW, getWidth(), scrollButtonW);

            }

            else

            {

                scrollDown->setBounds (0, getHeight() - scrollButtonW, getWidth(), scrollButtonW);

                scrollUp->setBounds (0, 0, getWidth(), scrollButtonW);

            }

            

            int endOfLastKey, kw;

            getKeyPos (rangeEnd, endOfLastKey, kw);

            endOfLastKey += kw;

            

            float mousePositionVelocity;

            const int spaceAvailable = w - scrollButtonW * 2;

            const int lastStartKey = remappedXYToNote (Point<int> (endOfLastKey - spaceAvailable, 0), mousePositionVelocity) + 1;

            

            if (lastStartKey >= 0 && ((int) firstKey) > lastStartKey)

            {

                firstKey = (float) jlimit (rangeStart, rangeEnd, lastStartKey);

                sendChangeMessage();

            }

            

            int newOffset = 0;

            getKeyPos (((int) firstKey), newOffset, kw);

            xOffset = newOffset - scrollButtonW;

        }

        else

        {

            firstKey = (float) rangeStart;

        }

        

        repaint();

IIRC I made the change because it was causing havoc in various edge-case situations when the buttons were added/removed. It's difficult to make the position of the keys depend on the position of a button when the existence of that button also depends on the position of the keys!

Really the only sensible approach is what I did, which is to align the bottom key with the left of the component, and to overlay a button when necessary. Perhaps a better solution would be to simply call setLowestVisibleKey with the key before the one you actually want to be visible?

Regardless of which key you set to be the lowest, it cuts that key off. So even though,  yes I could set the next lowest key to be the lowest in order to expose the one I need, it still partially cuts off the lowest key, instead of having the clean edge. Is there a way to combine the clean edge of your former code, with the changes you made in the current version?

You’d probably have to scale the width (or height if the Keyboard is vertical) of the keys to fit the total width available… and use setKeyWidth()

Rail

Only problem is, even if I chage the key width and overall width of the keyboard, that may fix the right edge, but again the left edge is cutting off the lowest key, because of the scroll button. I could make the key widths the same width as the scroll button, but then we're forced to use that width in order to provide a clean edge, which kind of defeats the flexibility of allowing us to set key width in the first place.

Wouldn't it make more sense to always show the arrows and if they weren't needed, just show them disabled? Or hidden? Makes more sense to me than to display a key that can hardly be clicked!