I have added some code to the MidiKeyboardComponent module, but have read that adding or modifying code directly in the modules is bad practice. And overriding is the way to go.
But I have no clue how overriding works or how to do it in Juce. I tried different tutorials I found online and have also looked into custom modules. I am just not sure what to do.
The added code works fine, but I want to do it the proper way.
It depends on the underlying class. You could simply inherit and add your own methods to your new class. Alternatively you can simply clone the entire class, rename it and go from there. I have done this on occasion when I needed to modify some of the non virtual functions. I actually had to do this with the MidiKeyboard class recently. I just cloned it and hacked it to pieces.
If I went with cloning the entire class, where would I place the files?
I tried it but if I added the new files (.h and .cpp) to the source folder it did not work.
It could not find the other Juce components that were used in the MidiKeyboardComponent.
Should i add it to the modules folder or maybe custom modules?
First have a look at the functions you modified. If they are virtual, you are better off inheriting the class and overriding the virtual methods. That is the intended use.
For sure you can add functions, but most likely you want to change how the class reacts to existing structures, which you can only accomplish by overriding virtual functions.
If you share details of your modifications it would be easier to advise
juce_MidiKeyboardComponent.h - Added two new functions
//a function that can set the notes/keys that should be highlighted.
void setHighlightedNotes(int highlightedKeys[3]);
//a function that removes all the highlighted notes/keys
void removeHighlightedNotes();
Changes to the paint function.
juce_MidiKeyboardComponent.cpp - public void paint function
void MidiKeyboardComponent::paint (Graphics& g)
{
g.fillAll (findColour (whiteNoteColourId));
auto lineColour = findColour (keySeparatorLineColourId);
auto textColour = findColour (textLabelColourId);
for (int octave = 0; octave < 128; octave += 12)
{
for (int white = 0; white < 7; ++white)
{
auto noteNum = octave + whiteNotes[white];
//NOT ORIGINAL EDIT# added bool that controls if the note/key is highlighted
bool highlight = false;
if (noteNum >= rangeStart && noteNum <= rangeEnd)
{
//NOT ORIGINAL EDIT# checks if note/key is a highlighted note/key
for each (int highlightedNote in highlightNotesForPress)
{
if (noteNum == highlightedNote) {
highlight = true;
}
}
drawWhiteNote(noteNum, g, getRectangleForKey(noteNum),
state.isNoteOnForChannels(midiInChannelMask, noteNum),
mouseOverNotes.contains(noteNum), lineColour, textColour, highlight);
}
}
}...
I have also changed something i the drawWhiteNote and drawBlackNote functions.
These functions are “protected virtual void”
Ok, so the drawWhiteNote and drawBlackNote will work with override.
The highlight: how is that different from the pressed keys? is that an additional highlight?
I think you would get away with a subclass:
class HighlightableKeyboard : public juce::MidiKeyboardComponent
{
public:
void paint (Graphics& g) override;
//a function that can set the notes/keys that should be highlighted.
void setHighlightedNotes(int highlightedKeys[3]);
//a function that removes all the highlighted notes/keys
void removeHighlightedNotes();
private:
// ...
};
If you have a referene to the actual type, you can easily call the additional functions.
The highlight is for highlighting a key that should be pressed. So C3 is red and after pressing it the highlight is removed, so the key goes back to being white. It’s used to learn the different keys on a piano.
And where would I add the new class HighlightableKeyboard? This is where I’m struggling a bit. Should it be added to the Editor file for a plugin application.
Yes exactly, just where you placed the standard MidiKeyboarComponent, replace it with the derived class.
I think for a pure GUI feature it is fine to have the information in the component.
If it was something processing related, you would need to change it in the MidiKeyboardState as well…
A drawback of this method is, if the editor is closed and reopened, the highlight is gone and will start with an initial state.
Oh dear, I never realised drawBlackNote() and drawWhiteNote() were virtual How did I miss that, it makes total sense that they would be. Too much coffee that morning I guess.
I tried doing what you wrote Daniel and it kind of works.
I have made the class HighlightableKeyboard and derived it from MidiKeyboardComponent class HighlightableKeyboard : public juce::MidiKeyboardComponent { }
Then I added and changed some of the derived functions inside the new class.
But it makes one error, i think its what you wrote about the MidiKeyboardState.
Im my PluginEditor.cpp file i have class
Does that mean I can change the function like this?
class HighlightableKeyboard : public juce::MidiKeyboardComponent {
public:
void drawWhiteNote(int midiNoteNumber,
Graphics& g, Rectangle<float> area,
bool isDown, bool isOver,
Colour lineColour, Colour textColour, bool isHighlighted) override {
//THIS CODE WOULD BE HOW THE FUNCTION WORKS?
};
void drawBlackNote(int midiNoteNumber,
Graphics& g, Rectangle<float> area,
bool isDown, bool isOver,
Colour noteFillColour, bool isHighlighted) override{
//THIS CODE WOULD BE HOW THE FUNCTION WORKS?
};
//a function that can set the notes/keys that should be highlighted.
void setHighlightedNotes(int highlightedKeys[3]) {
for (int i = 0; i < sizeof(highlightNotesForPress); i++) {
highlightNotesForPress[i] = highlightedNotes[i];
}
}
//a function that removes all the highlighted notes/keys
void removeHighlightedNotes() {
for (int i = 0; i < sizeof(highlightNotesForPress); i++) {
highlightNotesForPress[i] = -1;
}
}
enum ColourIds
{
whiteNoteColourId = 0x1005000,
blackNoteColourId = 0x1005001,
keySeparatorLineColourId = 0x1005002,
mouseOverKeyOverlayColourId = 0x1005003, /**< This colour will be overlaid on the normal note colour. */
keyDownOverlayColourId = 0x1005004, /**< This colour will be overlaid on the normal note colour. */
textLabelColourId = 0x1005005,
upDownButtonBackgroundColourId = 0x1005006,
upDownButtonArrowColourId = 0x1005007,
shadowColourId = 0x1005008,
//NOT ORIGINAL EDIT# set colour id for single notes
highlightColourId = 0x1005008,
};
private:
int highlightNotesForPress[3] = { -1, -1, -1 };
};
I tried this to be able to use key switches and it actually didn’t work… at I least I couldn’t get it to work by over riding the class. The reason being, the draw white and black notes are using constants for the color, black and white.
You have to modify the base class to make it work. So, I just copied the base class header and cpp to a new file, then I added a bool array initialized to false to represent the keys. In the draw key methods I just then test if that key set as a key switch and set the color to what I wanted.