MidiKeyboardComponent: Why are the "draw" functions not in the look and feel?


#1

I’m wondering why the MidiKeyboardComponent component doesn’t use the LookAndFeel class to draw the white and black notes? Everything else seems to use the LookAndFeel to “paint”, but for some reason the MidiKeyboardComponent doesn’t follow the same design principle.

Is there a particular reason, or was this a simple oversight?


#2

if you look at the length of the paint() methods in that class, they’re really long. It’s probably a pain in the ass to write several versions that are all vector-based too.


#3

That may be true, but that doesn’t explain the inconsistency at all. Are you arguing that nobody would bother coding several look and feels, thus the function to draw them is in a different class? What does the location of these functions have to do with their customization?

All this means is that somebody who wants to customize it, has to create a new class, inherit from the MidiKeyboardComponent and override it there. Then, when switching LookAndFeel, one would also need to remove the MidiKeyboardComponent from the parent and add another one, making this unnecessarily complicated and confusing.

When I went through the tutorials etc. I was under the impression that the LookAndFeel is responsible for all rendering. Everything else is in there, so why not this? :confused:


#4

Which I just did… :frowning:


#5

So did I, but I think it’s inconsistent and unnecessary. We already have a better, easier, more customizable option. The LookAndFeel class. Why not utilize it? :confused:


#6

The drawBlackNote and drawWhiteNote functions are not very long at all… :confused:


#7

this is not very long??


void MidiKeyboardComponent::paint (Graphics& g)
{
    g.fillAll (findColour (whiteNoteColourId));

    const Colour lineColour (findColour (keySeparatorLineColourId));
    const Colour textColour (findColour (textLabelColourId));

    for (int octave = 0; octave < 128; octave += 12)
    {
        for (int white = 0; white < 7; ++white)
        {
            const int noteNum = octave + whiteNotes [white];

            if (noteNum >= rangeStart && noteNum <= rangeEnd)
            {
                Rectangle<int> pos = getRectangleForKey (noteNum);

                                {
                    Colour c (Colours::transparentWhite);

                    if (isDown)  c = findColour (keyDownOverlayColourId);
                    if (isOver)  c = c.overlaidWith (findColour (mouseOverKeyOverlayColourId));

                    g.setColour (c);
                    g.fillRect (x, y, w, h);

                    const String text (getWhiteNoteText (midiNoteNumber));

                    if (text.isNotEmpty())
                    {
                        const float fontHeight = jmin (12.0f, keyWidth * 0.9f);

                        g.setColour (textColour);
                        g.setFont (Font (fontHeight).withHorizontalScale (0.8f));

                        switch (orientation)
                        {
                            case horizontalKeyboard:            g.drawText (text, x + 1, y,     w - 1, h - 2, Justification::centredBottom, false); break;
                            case verticalKeyboardFacingLeft:    g.drawText (text, x + 2, y + 2, w - 4, h - 4, Justification::centredLeft,   false); break;
                            case verticalKeyboardFacingRight:   g.drawText (text, x + 2, y + 2, w - 4, h - 4, Justification::centredRight,  false); break;
                            default: break;
                        }
                    }

                    if (! lineColour.isTransparent())
                    {
                        g.setColour (lineColour);

                        switch (orientation)
                        {
                            case horizontalKeyboard:            g.fillRect (x, y, 1, h); break;
                            case verticalKeyboardFacingLeft:    g.fillRect (x, y, w, 1); break;
                            case verticalKeyboardFacingRight:   g.fillRect (x, y + h - 1, w, 1); break;
                            default: break;
                        }

                        if (midiNoteNumber == rangeEnd)
                        {
                            switch (orientation)
                            {
                                case horizontalKeyboard:            g.fillRect (x + w, y, 1, h); break;
                                case verticalKeyboardFacingLeft:    g.fillRect (x, y + h, w, 1); break;
                                case verticalKeyboardFacingRight:   g.fillRect (x, y - 1, w, 1); break;
                                default: break;
                            }
                        }
                    }
                }
            }
        }
    }

    float x1 = 0.0f, y1 = 0.0f, x2 = 0.0f, y2 = 0.0f;
    const int width = getWidth();
    const int height = getHeight();

    if (orientation == verticalKeyboardFacingLeft)
    {
        x1 = width - 1.0f;
        x2 = width - 5.0f;
    }
    else if (orientation == verticalKeyboardFacingRight)
        x2 = 5.0f;
    else
        y2 = 5.0f;

    int x, w;
    getKeyPos (rangeEnd, x, w);
    x += w;

    const Colour shadowCol (findColour (shadowColourId));

    if (! shadowCol.isTransparent())
    {
        g.setGradientFill (ColourGradient (shadowCol, x1, y1, shadowCol.withAlpha (0.0f), x2, y2, false));

        switch (orientation)
        {
            case horizontalKeyboard:            g.fillRect (0, 0, x, 5); break;
            case verticalKeyboardFacingLeft:    g.fillRect (width - 5, 0, 5, x); break;
            case verticalKeyboardFacingRight:   g.fillRect (0, 0, 5, x); break;
            default: break;
        }
    }

    if (! lineColour.isTransparent())
    {
        g.setColour (lineColour);

        switch (orientation)
        {
            case horizontalKeyboard:            g.fillRect (0, height - 1, x, 1); break;
            case verticalKeyboardFacingLeft:    g.fillRect (0, 0, 1, x); break;
            case verticalKeyboardFacingRight:   g.fillRect (width - 1, 0, 1, x); break;
            default: break;
        }
    }

    const Colour blackNoteColour (findColour (blackNoteColourId));

    for (int octave = 0; octave < 128; octave += 12)
    {
        for (int black = 0; black < 5; ++black)
        {
            const int noteNum = octave + blackNotes [black];

            if (noteNum >= rangeStart && noteNum <= rangeEnd)
            {
                Rectangle<int> pos = getRectangleForKey (noteNum);

                {
                    Colour c (noteFillColour);

                    if (isDown)  c = c.overlaidWith (findColour (keyDownOverlayColourId));
                    if (isOver)  c = c.overlaidWith (findColour (mouseOverKeyOverlayColourId));

                    g.setColour (c);
                    g.fillRect (x, y, w, h);

                    if (isDown)
                    {
                        g.setColour (noteFillColour);
                        g.drawRect (x, y, w, h);
                    }
                    else
                    {
                        g.setColour (c.brighter());
                        const int xIndent = jmax (1, jmin (w, h) / 8);

                        switch (orientation)
                        {
                            case horizontalKeyboard:            g.fillRect (x + xIndent, y, w - xIndent * 2, 7 * h / 8); break;
                            case verticalKeyboardFacingLeft:    g.fillRect (x + w / 8, y + xIndent, w - w / 8, h - xIndent * 2); break;
                            case verticalKeyboardFacingRight:   g.fillRect (x, y + xIndent, 7 * w / 8, h - xIndent * 2); break;
                            default: break;
                        }
                    }
                }
            }
        }
    }
}

#8

I think you misunderstand me:

  1. It doesn’t matter how long it is. It’s length should have no impact on it’s location. Nothing would be duplicated. The code would just move.
  2. I’m talking about the “drawWhiteNote” and “drawBlackNote” methods, not the “paint” method.
  3. But even the “paint” method should be moved, in case people don’t want to “default” LookAndFeel…

#9

I had to change some private class members to protected as well… as well as also make a small code change to the base class to create my own keyboard look:

Rail


#10

awwww snap, I knew you were a big time developer!!!


#11

Not really, I’m actually a recording engineer :slight_smile:

Cheers,

Rail


#12

that plugin gui says otherwise!


#13

Thanks :sunglasses:

Rail