1 //------------------------------------------------------------------------
2 // This file is part of VSTGUI. It is subject to the license terms
3 // in the LICENSE file found in the top-level directory of this
4 // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
5 // Flags : clang-format SMTGSequencer
6 
7 #pragma once
8 
9 #include "vstgui/lib/vstguifwd.h"
10 #include "vstgui/lib/ccolor.h"
11 #include "vstgui/lib/cview.h"
12 #include "vstgui/lib/dispatchlist.h"
13 #include "vstgui/lib/itouchevent.h"
14 #include <array>
15 #include <bitset>
16 #include <map>
17 
18 namespace VSTGUI {
19 
20 //------------------------------------------------------------------------
21 class KeyboardViewBase : public CView
22 {
23 public:
24 	using NoteIndex = int16_t;
25 	using NumNotes = uint8_t;
26 
27 	static constexpr NumNotes MaxNotes = 128;
28 
29 	enum class BitmapID
30 	{
31 		WhiteKeyPressed = 0,
32 		WhiteKeyUnpressed,
33 		BlackKeyPressed,
34 		BlackKeyUnpressed,
35 		WhiteKeyShadowLeft,
36 		WhiteKeyShadowRight,
37 		NumBitmaps
38 	};
39 
40 	KeyboardViewBase ();
41 
42 	void setKeyPressed (NoteIndex note, bool state);
43 
44 	virtual void setKeyRange (NoteIndex startNote, NumNotes numKeys);
getKeyRangeStart()45 	NoteIndex getKeyRangeStart () const { return startNote; }
getNumKeys()46 	NumNotes getNumKeys () const { return numKeys; }
47 	NumNotes getNumWhiteKeys () const;
48 
49 	void setWhiteKeyWidth (CCoord width);
50 	void setBlackKeyWidth (CCoord width);
51 	void setBlackKeyHeight (CCoord height);
52 	void setLineWidth (CCoord width);
getWhiteKeyWidth()53 	CCoord getWhiteKeyWidth () const { return whiteKeyWidth; }
getBlackKeyWidth()54 	CCoord getBlackKeyWidth () const { return blackKeyWidth; }
getBlackKeyHeight()55 	CCoord getBlackKeyHeight () const { return blackKeyHeight; }
getLineWidth()56 	CCoord getLineWidth () const { return lineWidth; }
57 
58 	void setFrameColor (CColor color);
59 	void setFontColor (CColor color);
60 	void setWhiteKeyColor (CColor color);
61 	void setWhiteKeyPressedColor (CColor color);
62 	void setBlackKeyColor (CColor color);
63 	void setBlackKeyPressedColor (CColor color);
getFrameColor()64 	CColor getFrameColor () const { return frameColor; }
getFontColor()65 	CColor getFontColor () const { return fontColor; }
getWhiteKeyColor()66 	CColor getWhiteKeyColor () const { return whiteKeyColor; }
getWhiteKeyPressedColor()67 	CColor getWhiteKeyPressedColor () const { return whiteKeyPressedColor; }
getBlackKeyColor()68 	CColor getBlackKeyColor () const { return blackKeyColor; }
getBlackKeyPressedColor()69 	CColor getBlackKeyPressedColor () const { return blackKeyPressedColor; }
70 
71 	void setNoteNameFont (CFontDesc* font);
getNoteNameFont()72 	CFontDesc* getNoteNameFont () const { return noteNameFont; }
73 	void setDrawNoteText (bool state);
getDrawNoteText()74 	bool getDrawNoteText () const { return drawNoteText; }
75 
76 	void setBitmap (BitmapID bID, CBitmap* bitmap);
77 	CBitmap* getBitmap (BitmapID bID) const;
78 
79 	void setWhiteKeyBitmapInset (const CRect& inset); // TODO: uidesc
80 	void setBlackKeyBitmapInset (const CRect& inset); // TODO: uidesc
81 
getNoteRect(NoteIndex note)82 	const CRect& getNoteRect (NoteIndex note) const { return noteRectCache[note]; }
83 	bool isWhiteKey (NoteIndex note) const;
84 
85 	void drawRect (CDrawContext* context, const CRect& dirtyRect) override;
86 	void setViewSize (const CRect& rect, bool invalid = true) override;
87 	bool sizeToFit () override;
88 //------------------------------------------------------------------------
89 protected:
90 	using NoteRectCache = std::array<CRect, MaxNotes>;
91 
92 	void invalidNote (NoteIndex note);
93 
94 	NoteIndex pointToNote (const CPoint& p, bool ignoreY) const;
getNoteRectCache()95 	const NoteRectCache& getNoteRectCache () const { return noteRectCache; }
96 
97 private:
98 	void drawNote (CDrawContext* context, CRect& rect, NoteIndex note, bool isWhite) const;
99 	CRect calcNoteRect (NoteIndex note) const;
100 	void updateNoteRectCache () const;
101 	void createBitmapCache ();
102 
103 	using BitmapArray =
104 	    std::array<SharedPointer<CBitmap>, static_cast<size_t> (BitmapID::NumBitmaps)>;
105 
106 	BitmapArray bitmaps;
107 	SharedPointer<CBitmap> whiteKeyBitmapCache;
108 	SharedPointer<CBitmap> blackKeyBitmapCache;
109 	SharedPointer<CFontDesc> noteNameFont;
110 
111 	CRect whiteKeyBitmapInset;
112 	CRect blackKeyBitmapInset;
113 
114 	CCoord whiteKeyWidth {30};
115 	CCoord blackKeyWidth {20};
116 	CCoord blackKeyHeight {20};
117 	CCoord lineWidth {1.};
118 
119 	CColor frameColor {kBlackCColor};
120 	CColor fontColor {kBlackCColor};
121 	CColor whiteKeyColor {kWhiteCColor};
122 	CColor whiteKeyPressedColor {kGreyCColor};
123 	CColor blackKeyColor {kBlackCColor};
124 	CColor blackKeyPressedColor {kGreyCColor};
125 
126 	NumNotes numKeys {88};
127 	NoteIndex startNote {21};
128 	bool drawNoteText {false};
129 	mutable bool noteRectCacheInvalid {true};
130 	mutable NoteRectCache noteRectCache;
131 	std::bitset<MaxNotes> keyPressed {};
132 };
133 
134 class KeyboardViewRangeSelector;
135 //------------------------------------------------------------------------
136 struct IKeyboardViewKeyRangeChangedListener
137 {
138 	virtual void onKeyRangeChanged (KeyboardViewRangeSelector*) = 0;
139 
140 	virtual ~IKeyboardViewKeyRangeChangedListener () noexcept = default;
141 };
142 
143 //------------------------------------------------------------------------
144 class KeyboardViewRangeSelector : public KeyboardViewBase
145 {
146 public:
147 	struct Range
148 	{
149 		NoteIndex position;
150 		NumNotes length;
positionRange151 		Range (NoteIndex position = 0, NumNotes length = 0) : position (position), length (length)
152 		{
153 		}
154 		bool operator!= (const Range& r) const
155 		{
156 			return position != r.position || length != r.length;
157 		}
158 	};
159 
160 	KeyboardViewRangeSelector () = default;
161 
162 	void drawRect (CDrawContext* context, const CRect& dirtyRect) override;
163 
164 	void setKeyRange (NoteIndex startNote, NumNotes numKeys) override;
165 	void setSelectionRange (const Range& range);
166 	void setSelectionMinMax (NumNotes minRange, NumNotes maxRange);
getSelectionRange()167 	const Range& getSelectionRange () const { return selectionRange; }
getSelectionMin()168 	NumNotes getSelectionMin () const { return rangeMin; }
getSelectionMax()169 	NumNotes getSelectionMax () const { return rangeMax; }
170 	NumNotes getNumWhiteKeysSelected () const;
171 
172 	void registerKeyRangeChangedListener (IKeyboardViewKeyRangeChangedListener* listener);
173 	void unregisterKeyRangeChangedListener (IKeyboardViewKeyRangeChangedListener* listener);
174 
175 //------------------------------------------------------------------------
176 private:
177 	DispatchList<IKeyboardViewKeyRangeChangedListener*> listeners;
178 
179 	Range selectionRange {0, 12};
180 	NumNotes rangeMin {12};
181 	NumNotes rangeMax {24};
182 
183 #if VSTGUI_TOUCH_EVENT_HANDLING
184 	void onTouchEvent (ITouchEvent& event) override;
185 	bool wantsMultiTouchEvents () const override;
186 	void onTouchBegin (const ITouchEvent::TouchPair& touch, ITouchEvent& event);
187 	void onTouchMove (const ITouchEvent::TouchPair& touch, ITouchEvent& event);
188 
189 	enum TouchMode {kUnknown, kMoveRange, kChangeRangeFront, kChangeRangeBack};
190 
191 	Range selectionRangeOnTouchStart;
192 	int32_t touchIds[2] {-1};
193 	TouchMode touchMode {kUnknown};
194 	NoteIndex touchStartNote[2];
195 #else
196 	CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
197 	CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
198 	CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
199 	CMouseEventResult onMouseCancel () override;
200 
201 	Range moveStartRange;
202 	NoteIndex moveStartNote {-1};
203 #endif
204 };
205 
206 //------------------------------------------------------------------------
207 struct IKeyboardViewPlayerDelegate
208 {
209 	using NoteIndex = KeyboardViewBase::NoteIndex;
210 
211 	virtual int32_t onNoteOn (NoteIndex note, double xPos, double yPos) = 0;
212 	virtual void onNoteOff (NoteIndex note, int32_t noteID) = 0;
213 
214 	virtual void onNoteModulation (int32_t noteID, double xPos, double yPos) = 0;
215 
216 	virtual ~IKeyboardViewPlayerDelegate () noexcept = default;
217 };
218 
219 //------------------------------------------------------------------------
220 struct KeyboardViewPlayerDelegate : public IKeyboardViewPlayerDelegate
221 {
onNoteOnKeyboardViewPlayerDelegate222 	int32_t onNoteOn (NoteIndex note, double xPos, double yPos) override { return -1; }
onNoteOffKeyboardViewPlayerDelegate223 	void onNoteOff (NoteIndex note, int32_t noteID) override {}
onNoteModulationKeyboardViewPlayerDelegate224 	void onNoteModulation (int32_t noteID, double xPos, double yPos) override {}
225 };
226 
227 //------------------------------------------------------------------------
228 class KeyboardView : public KeyboardViewBase
229 {
230 public:
231 	KeyboardView ();
setDelegate(IKeyboardViewPlayerDelegate * delegate)232 	void setDelegate (IKeyboardViewPlayerDelegate* delegate) { this->delegate = delegate; }
233 
234 private:
235 	double calcYParameter (NoteIndex note, CCoord y) const;
236 	double calcXParameter (NoteIndex note, CCoord x) const;
237 #if VSTGUI_TOUCH_EVENT_HANDLING
wantsMultiTouchEvents()238 	bool wantsMultiTouchEvents () const override { return true; }
239 	void onTouchEvent (ITouchEvent& event) override;
240 	void onTouchBegin (const ITouchEvent::TouchPair& touch, ITouchEvent& event);
241 	void onTouchMove (const ITouchEvent::TouchPair& touch, ITouchEvent& event);
242 	void onTouchEnd (const ITouchEvent::TouchPair& touch, ITouchEvent& event);
243 
244 	struct NoteTouch
245 	{
246 		NoteIndex note;
247 		int32_t noteID;
NoteTouchNoteTouch248 		NoteTouch (NoteIndex note) : note (note), noteID (-1) {}
249 	};
250 
251 	std::map<int32_t, NoteTouch> noteTouches;
252 #else
253 	void doNoteOff ();
254 	void doNoteOn (NoteIndex note, double yPos, double xPos);
255 
256 	CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
257 	CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
258 	CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
259 	CMouseEventResult onMouseCancel () override;
260 
261 	NoteIndex pressedNote {-1};
262 	int32_t noteID {-1};
263 #endif
264 
265 	IKeyboardViewPlayerDelegate* delegate {nullptr};
266 };
267 
268 //------------------------------------------------------------------------
269 } // VSTGUI
270