The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

289 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 7 technical preview.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For the technical preview this file cannot be licensed commercially.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. //==============================================================================
  16. /**
  17. A base class for drawing a custom MIDI keyboard component.
  18. Implement the drawKeyboardBackground(), drawWhiteKey(), and drawBlackKey() methods
  19. to draw your content and this class will handle the underlying keyboard logic.
  20. The component is a ChangeBroadcaster, so if you want to be informed when the
  21. keyboard is scrolled, you can register a ChangeListener for callbacks.
  22. @tags{Audio}
  23. */
  24. class JUCE_API KeyboardComponentBase : public Component,
  25. public ChangeBroadcaster
  26. {
  27. public:
  28. //==============================================================================
  29. /** The direction of the keyboard.
  30. @see setOrientation
  31. */
  32. enum Orientation
  33. {
  34. horizontalKeyboard,
  35. verticalKeyboardFacingLeft,
  36. verticalKeyboardFacingRight,
  37. };
  38. //==============================================================================
  39. /** Constructor.
  40. @param orientation whether the keyboard is horizontal or vertical
  41. */
  42. explicit KeyboardComponentBase (Orientation orientation);
  43. /** Destructor. */
  44. ~KeyboardComponentBase() override = default;
  45. //==============================================================================
  46. /** Changes the width used to draw the white keys. */
  47. void setKeyWidth (float widthInPixels);
  48. /** Returns the width that was set by setKeyWidth(). */
  49. float getKeyWidth() const noexcept { return keyWidth; }
  50. /** Changes the width used to draw the buttons that scroll the keyboard up/down in octaves. */
  51. void setScrollButtonWidth (int widthInPixels);
  52. /** Returns the width that was set by setScrollButtonWidth(). */
  53. int getScrollButtonWidth() const noexcept { return scrollButtonWidth; }
  54. /** Changes the keyboard's current direction. */
  55. void setOrientation (Orientation newOrientation);
  56. /** Returns the keyboard's current direction. */
  57. Orientation getOrientation() const noexcept { return orientation; }
  58. /** Returns true if the keyboard's orientation is horizontal. */
  59. bool isHorizontal() const noexcept { return orientation == horizontalKeyboard; }
  60. /** Sets the range of midi notes that the keyboard will be limited to.
  61. By default the range is 0 to 127 (inclusive), but you can limit this if you
  62. only want a restricted set of the keys to be shown.
  63. Note that the values here are inclusive and must be between 0 and 127.
  64. */
  65. void setAvailableRange (int lowestNote, int highestNote);
  66. /** Returns the first note in the available range.
  67. @see setAvailableRange
  68. */
  69. int getRangeStart() const noexcept { return rangeStart; }
  70. /** Returns the last note in the available range.
  71. @see setAvailableRange
  72. */
  73. int getRangeEnd() const noexcept { return rangeEnd; }
  74. /** If the keyboard extends beyond the size of the component, this will scroll
  75. it to show the given key at the start.
  76. Whenever the keyboard's position is changed, this will use the ChangeBroadcaster
  77. base class to send a callback to any ChangeListeners that have been registered.
  78. */
  79. void setLowestVisibleKey (int noteNumber);
  80. /** Returns the number of the first key shown in the component.
  81. @see setLowestVisibleKey
  82. */
  83. int getLowestVisibleKey() const noexcept { return (int) firstKey; }
  84. /** Returns the absolute length of the white notes.
  85. This will be their vertical or horizontal length, depending on the keyboard's orientation.
  86. */
  87. float getWhiteNoteLength() const noexcept;
  88. /** Sets the length of the black notes as a proportion of the white note length. */
  89. void setBlackNoteLengthProportion (float ratio) noexcept;
  90. /** Returns the length of the black notes as a proportion of the white note length. */
  91. float getBlackNoteLengthProportion() const noexcept { return blackNoteLengthRatio; }
  92. /** Returns the absolute length of the black notes.
  93. This will be their vertical or horizontal length, depending on the keyboard's orientation.
  94. */
  95. float getBlackNoteLength() const noexcept;
  96. /** Sets the width of the black notes as a proportion of the white note width. */
  97. void setBlackNoteWidthProportion (float ratio) noexcept;
  98. /** Returns the width of the black notes as a proportion of the white note width. */
  99. float getBlackNoteWidthProportion() const noexcept { return blackNoteWidthRatio; }
  100. /** Returns the absolute width of the black notes.
  101. This will be their vertical or horizontal width, depending on the keyboard's orientation.
  102. */
  103. float getBlackNoteWidth() const noexcept { return keyWidth * blackNoteWidthRatio; }
  104. /** If set to true, then scroll buttons will appear at either end of the keyboard
  105. if there are too many notes to fit them all in the component at once.
  106. */
  107. void setScrollButtonsVisible (bool canScroll);
  108. //==============================================================================
  109. /** Colour IDs to use to change the colour of the octave scroll buttons.
  110. These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
  111. methods.
  112. @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
  113. */
  114. enum ColourIds
  115. {
  116. upDownButtonBackgroundColourId = 0x1004000,
  117. upDownButtonArrowColourId = 0x1004001
  118. };
  119. /** Returns the position within the component of the left-hand edge of a key.
  120. Depending on the keyboard's orientation, this may be a horizontal or vertical
  121. distance, in either direction.
  122. */
  123. float getKeyStartPosition (int midiNoteNumber) const;
  124. /** Returns the total width needed to fit all the keys in the available range. */
  125. float getTotalKeyboardWidth() const noexcept;
  126. /** This structure is returned by the getNoteAndVelocityAtPosition() method.
  127. */
  128. struct JUCE_API NoteAndVelocity
  129. {
  130. int note;
  131. float velocity;
  132. };
  133. /** Returns the note number and velocity for a given position within the component.
  134. If includeChildComponents is true then this will return a key obscured by any child
  135. components.
  136. */
  137. NoteAndVelocity getNoteAndVelocityAtPosition (Point<float> position, bool includeChildComponents = false);
  138. #ifndef DOXYGEN
  139. /** Returns the key at a given coordinate, or -1 if the position does not intersect a key. */
  140. [[deprecated ("This method has been deprecated in favour of getNoteAndVelocityAtPosition.")]]
  141. int getNoteAtPosition (Point<float> p) { return getNoteAndVelocityAtPosition (p).note; }
  142. #endif
  143. /** Returns the rectangle for a given key. */
  144. Rectangle<float> getRectangleForKey (int midiNoteNumber) const;
  145. //==============================================================================
  146. /** This sets the octave number which is shown as the octave number for middle C.
  147. This affects only the default implementation of getWhiteNoteText(), which
  148. passes this octave number to MidiMessage::getMidiNoteName() in order to
  149. get the note text. See MidiMessage::getMidiNoteName() for more info about
  150. the parameter.
  151. By default this value is set to 3.
  152. @see getOctaveForMiddleC
  153. */
  154. void setOctaveForMiddleC (int octaveNumForMiddleC);
  155. /** This returns the value set by setOctaveForMiddleC().
  156. @see setOctaveForMiddleC
  157. */
  158. int getOctaveForMiddleC() const noexcept { return octaveNumForMiddleC; }
  159. //==============================================================================
  160. /** Use this method to draw the background of the keyboard that will be drawn under
  161. the white and black notes. This can also be used to draw any shadow or outline effects.
  162. */
  163. virtual void drawKeyboardBackground (Graphics& g, Rectangle<float> area) = 0;
  164. /** Use this method to draw a white key of the keyboard in a given rectangle.
  165. When doing this, be sure to note the keyboard's orientation.
  166. */
  167. virtual void drawWhiteKey (int midiNoteNumber, Graphics& g, Rectangle<float> area) = 0;
  168. /** Use this method to draw a black key of the keyboard in a given rectangle.
  169. When doing this, be sure to note the keyboard's orientation.
  170. */
  171. virtual void drawBlackKey (int midiNoteNumber, Graphics& g, Rectangle<float> area) = 0;
  172. /** This can be overridden to draw the up and down buttons that scroll the keyboard
  173. up/down in octaves.
  174. */
  175. virtual void drawUpDownButton (Graphics& g, int w, int h, bool isMouseOver, bool isButtonPressed, bool movesOctavesUp);
  176. /** Calculates the position of a given midi-note.
  177. This can be overridden to create layouts with custom key-widths.
  178. @param midiNoteNumber the note to find
  179. @param keyWidth the desired width in pixels of one key - see setKeyWidth()
  180. @returns the start and length of the key along the axis of the keyboard
  181. */
  182. virtual Range<float> getKeyPosition (int midiNoteNumber, float keyWidth) const;
  183. //==============================================================================
  184. /** @internal */
  185. void paint (Graphics&) override;
  186. /** @internal */
  187. void resized() override;
  188. /** @internal */
  189. void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
  190. private:
  191. //==============================================================================
  192. struct UpDownButton;
  193. Range<float> getKeyPos (int midiNoteNumber) const;
  194. NoteAndVelocity remappedXYToNote (Point<float>) const;
  195. void setLowestVisibleKeyFloat (float noteNumber);
  196. //==============================================================================
  197. Orientation orientation;
  198. float blackNoteLengthRatio = 0.7f, blackNoteWidthRatio = 0.7f;
  199. float xOffset = 0.0f;
  200. float keyWidth = 16.0f;
  201. float firstKey = 12 * 4.0f;
  202. int scrollButtonWidth = 12;
  203. int rangeStart = 0, rangeEnd = 127;
  204. int octaveNumForMiddleC = 3;
  205. bool canScroll = true;
  206. std::unique_ptr<Button> scrollDown, scrollUp;
  207. //==============================================================================
  208. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KeyboardComponentBase)
  209. };
  210. } // namespace juce