1 // This file is part of VSTGUI. It is subject to the license terms
2 // in the LICENSE file found in the top-level directory of this
3 // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
4
5 #include "cbuttons.h"
6 #include "../cdrawcontext.h"
7 #include "../cbitmap.h"
8 #include "../cframe.h"
9 #include "../cgraphicspath.h"
10 #include "../platform/iplatformfont.h"
11 #include <cmath>
12
13 namespace VSTGUI {
14
15 //------------------------------------------------------------------------
16 // COnOffButton
17 //------------------------------------------------------------------------
18 /*! @class COnOffButton
19 Define a button with 2 positions.
20 The bitmap includes the 2 subbitmaps (i.e the rectangle used for the display of this button is half-height of the bitmap).
21 When its value changes, the listener is called.
22 */
23 //------------------------------------------------------------------------
24 /**
25 * COnOffButton constructor.
26 * @param size the size of this view
27 * @param listener the listener
28 * @param tag the control tag
29 * @param background bitmap of the on/off button
30 * @param style style, currently not used
31 */
32 //------------------------------------------------------------------------
COnOffButton(const CRect & size,IControlListener * listener,int32_t tag,CBitmap * background,int32_t style)33 COnOffButton::COnOffButton (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background, int32_t style)
34 : CControl (size, listener, tag, background)
35 , style (style)
36 {
37 setWantsFocus (true);
38 }
39
40 //------------------------------------------------------------------------
COnOffButton(const COnOffButton & v)41 COnOffButton::COnOffButton (const COnOffButton& v)
42 : CControl (v)
43 , style (v.style)
44 {
45 setWantsFocus (true);
46 }
47
48 //------------------------------------------------------------------------
draw(CDrawContext * pContext)49 void COnOffButton::draw (CDrawContext *pContext)
50 {
51 if (getDrawBackground ())
52 {
53 CCoord off;
54
55 if (value == getMax ())
56 off = getDrawBackground ()->getHeight () / 2;
57 else
58 off = 0;
59
60 getDrawBackground ()->draw (pContext, getViewSize (), CPoint (0, off));
61 }
62 setDirty (false);
63 }
64
65 //------------------------------------------------------------------------
onMouseDown(CPoint & where,const CButtonState & buttons)66 CMouseEventResult COnOffButton::onMouseDown (CPoint& where, const CButtonState& buttons)
67 {
68 if (!(buttons & kLButton))
69 return kMouseEventNotHandled;
70
71 beginEdit ();
72 return kMouseEventHandled;
73 }
74
75 //------------------------------------------------------------------------
onMouseMoved(CPoint & where,const CButtonState & buttons)76 CMouseEventResult COnOffButton::onMouseMoved (CPoint& where, const CButtonState& buttons)
77 {
78 return kMouseEventHandled;
79 }
80
81 //------------------------------------------------------------------------
onMouseUp(CPoint & where,const CButtonState & buttons)82 CMouseEventResult COnOffButton::onMouseUp (CPoint& where, const CButtonState& buttons)
83 {
84 if (isEditing ())
85 {
86 if (getViewSize ().pointInside (where))
87 {
88 value = (value == getMax ()) ? getMin () : getMax ();
89 invalid ();
90 valueChanged ();
91 }
92 endEdit ();
93 }
94 return kMouseEventHandled;
95 }
96
97 //------------------------------------------------------------------------
onMouseCancel()98 CMouseEventResult COnOffButton::onMouseCancel ()
99 {
100 if (isEditing ())
101 endEdit ();
102 return kMouseEventHandled;
103 }
104
105 //------------------------------------------------------------------------
onKeyDown(VstKeyCode & keyCode)106 int32_t COnOffButton::onKeyDown (VstKeyCode& keyCode)
107 {
108 if (keyCode.virt == VKEY_RETURN && keyCode.modifier == 0)
109 {
110 value = (value == getMax ()) ? getMin () : getMax ();
111 invalid ();
112 beginEdit ();
113 valueChanged ();
114 endEdit ();
115 return 1;
116 }
117 return -1;
118 }
119
120 //------------------------------------------------------------------------
sizeToFit()121 bool COnOffButton::sizeToFit ()
122 {
123 if (getDrawBackground ())
124 {
125 CRect vs (getViewSize ());
126 vs.setWidth (getDrawBackground ()->getWidth ());
127 vs.setHeight (getDrawBackground ()->getHeight () / 2.);
128 setViewSize (vs, true);
129 setMouseableArea (vs);
130 return true;
131 }
132 return false;
133 }
134
135 //------------------------------------------------------------------------
136 // CKickButton
137 //------------------------------------------------------------------------
138 /*! @class CKickButton
139 Define a button with 2 states using 2 subbitmaps.
140 One click on it, then the second subbitmap is displayed.
141 When the mouse button is relaxed, the first subbitmap is framed.
142 */
143 //------------------------------------------------------------------------
144 /**
145 * CKickButton constructor.
146 * @param size the size of this view
147 * @param listener the listener
148 * @param tag the control tag
149 * @param background the bitmap
150 * @param offset unused
151 */
152 //------------------------------------------------------------------------
CKickButton(const CRect & size,IControlListener * listener,int32_t tag,CBitmap * background,const CPoint & offset)153 CKickButton::CKickButton (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background, const CPoint& offset)
154 : CControl (size, listener, tag, background)
155 , offset (offset)
156 {
157 heightOfOneImage = size.getHeight ();
158 setWantsFocus (true);
159 }
160
161 //------------------------------------------------------------------------
162 /**
163 * CKickButton constructor.
164 * @param size the size of this view
165 * @param listener the listener
166 * @param tag the control tag
167 * @param heightOfOneImage height of one sub bitmap in background
168 * @param background the bitmap
169 * @param offset of background
170 */
171 //------------------------------------------------------------------------
CKickButton(const CRect & size,IControlListener * listener,int32_t tag,CCoord heightOfOneImage,CBitmap * background,const CPoint & offset)172 CKickButton::CKickButton (const CRect& size, IControlListener* listener, int32_t tag, CCoord heightOfOneImage, CBitmap* background, const CPoint& offset)
173 : CControl (size, listener, tag, background)
174 , offset (offset)
175 {
176 setHeightOfOneImage (heightOfOneImage);
177 setWantsFocus (true);
178 }
179
180 //------------------------------------------------------------------------
CKickButton(const CKickButton & v)181 CKickButton::CKickButton (const CKickButton& v)
182 : CControl (v)
183 , offset (v.offset)
184 {
185 setHeightOfOneImage (v.heightOfOneImage);
186 setWantsFocus (true);
187 }
188
189 //------------------------------------------------------------------------
draw(CDrawContext * pContext)190 void CKickButton::draw (CDrawContext *pContext)
191 {
192 CPoint where (offset.x, offset.y);
193
194 bounceValue ();
195
196 if (value == getMax ())
197 where.y += heightOfOneImage;
198
199 if (getDrawBackground ())
200 {
201 getDrawBackground ()->draw (pContext, getViewSize (), where);
202 }
203 setDirty (false);
204 }
205
206 //------------------------------------------------------------------------
onMouseDown(CPoint & where,const CButtonState & buttons)207 CMouseEventResult CKickButton::onMouseDown (CPoint& where, const CButtonState& buttons)
208 {
209 if (!(buttons & kLButton))
210 return kMouseEventNotHandled;
211 beginEdit ();
212 return onMouseMoved (where, buttons);
213 }
214
215 //------------------------------------------------------------------------
onMouseCancel()216 CMouseEventResult CKickButton::onMouseCancel ()
217 {
218 if (isEditing ())
219 {
220 value = getMin ();
221 if (isDirty ())
222 {
223 valueChanged ();
224 invalid ();
225 }
226 endEdit ();
227 }
228 return kMouseEventHandled;
229 }
230
231 //------------------------------------------------------------------------
onMouseUp(CPoint & where,const CButtonState & buttons)232 CMouseEventResult CKickButton::onMouseUp (CPoint& where, const CButtonState& buttons)
233 {
234 if (isEditing ())
235 {
236 if (value > 0.f)
237 valueChanged ();
238 value = getMin ();
239 valueChanged ();
240 if (isDirty ())
241 invalid ();
242 endEdit ();
243 }
244 return kMouseEventHandled;
245 }
246
247 //------------------------------------------------------------------------
onMouseMoved(CPoint & where,const CButtonState & buttons)248 CMouseEventResult CKickButton::onMouseMoved (CPoint& where, const CButtonState& buttons)
249 {
250 if (isEditing ())
251 {
252 if (where.x >= getViewSize ().left && where.y >= getViewSize ().top &&
253 where.x <= getViewSize ().right && where.y <= getViewSize ().bottom)
254 value = getMax ();
255 else
256 value = getMin ();
257
258 if (isDirty ())
259 invalid ();
260 return kMouseEventHandled;
261 }
262 return kMouseEventNotHandled;
263 }
264
265 //------------------------------------------------------------------------
onKeyDown(VstKeyCode & keyCode)266 int32_t CKickButton::onKeyDown (VstKeyCode& keyCode)
267 {
268 if (keyCode.modifier == 0 && keyCode.virt == VKEY_RETURN)
269 {
270 if (value != getMax ())
271 {
272 beginEdit ();
273 value = getMax ();
274 invalid ();
275 valueChanged ();
276 }
277 return 1;
278 }
279 return -1;
280 }
281
282 //------------------------------------------------------------------------
onKeyUp(VstKeyCode & keyCode)283 int32_t CKickButton::onKeyUp (VstKeyCode& keyCode)
284 {
285 if (keyCode.modifier == 0 && keyCode.virt == VKEY_RETURN)
286 {
287 value = getMin ();
288 invalid ();
289 valueChanged ();
290 endEdit ();
291 return 1;
292 }
293 return -1;
294 }
295
296 //------------------------------------------------------------------------
sizeToFit()297 bool CKickButton::sizeToFit ()
298 {
299 if (getDrawBackground ())
300 {
301 CRect vs (getViewSize ());
302 vs.setHeight (heightOfOneImage);
303 vs.setWidth (getDrawBackground ()->getWidth ());
304 setViewSize (vs, true);
305 setMouseableArea (vs);
306 return true;
307 }
308 return false;
309 }
310
311 //------------------------------------------------------------------------
312 // CCheckBox
313 //------------------------------------------------------------------------
314 /*! @class CCheckBox
315 A checkbox control with a title and 3 states : checked, half checked, not checked
316
317 - if value is < 0.5 the checkbox is not checked
318 - if value is 0.5 the checkbox is half checked
319 - if value is > 0.5 the checkbox is checked
320
321 the user can only switch between checked and not checked state.
322
323 If the bitmap is set, the bitmap must contain 6 states of the checkbox in the following order:
324 - not checked
325 - half checked
326 - checked
327 - not checked hilighted
328 - half checked hilighted
329 - checked hilighted
330 */
331 //------------------------------------------------------------------------
332 //------------------------------------------------------------------------
CCheckBox(const CRect & size,IControlListener * listener,int32_t tag,UTF8StringPtr title,CBitmap * bitmap,int32_t style)333 CCheckBox::CCheckBox (const CRect& size, IControlListener* listener, int32_t tag, UTF8StringPtr title, CBitmap* bitmap, int32_t style)
334 : CControl (size, listener, tag, bitmap)
335 , style (style)
336 , fontColor (kWhiteCColor)
337 , font (kSystemFont)
338 {
339 setTitle (title);
340 setBoxFillColor (kWhiteCColor);
341 setBoxFrameColor (kBlackCColor);
342 setCheckMarkColor (kRedCColor);
343 setWantsFocus (true);
344 if (style & kAutoSizeToFit)
345 sizeToFit ();
346 }
347
348 //------------------------------------------------------------------------
CCheckBox(const CCheckBox & checkbox)349 CCheckBox::CCheckBox (const CCheckBox& checkbox)
350 : CControl (checkbox)
351 , style (checkbox.style)
352 , fontColor (checkbox.fontColor)
353 , font (checkbox.font)
354 {
355 setTitle (checkbox.title);
356 setBoxFillColor (checkbox.boxFillColor);
357 setBoxFrameColor (checkbox.boxFrameColor);
358 setCheckMarkColor (checkbox.checkMarkColor);
359 setWantsFocus (true);
360 }
361
362 //------------------------------------------------------------------------
setTitle(const UTF8String & newTitle)363 void CCheckBox::setTitle (const UTF8String& newTitle)
364 {
365 title = newTitle;
366 if (style & kAutoSizeToFit)
367 sizeToFit ();
368 }
369
370 //------------------------------------------------------------------------
setFont(CFontRef newFont)371 void CCheckBox::setFont (CFontRef newFont)
372 {
373 font = newFont;
374 if (font && style & kAutoSizeToFit)
375 sizeToFit ();
376 }
377
378 //------------------------------------------------------------------------
setBackground(CBitmap * background)379 void CCheckBox::setBackground (CBitmap *background)
380 {
381 CView::setBackground (background);
382 if (style & kAutoSizeToFit)
383 sizeToFit ();
384 }
385
386 //------------------------------------------------------------------------
setStyle(int32_t newStyle)387 void CCheckBox::setStyle (int32_t newStyle)
388 {
389 if (style != newStyle)
390 {
391 style = newStyle;
392 if (style & kAutoSizeToFit)
393 sizeToFit ();
394 invalid ();
395 }
396 }
397
398 //------------------------------------------------------------------------
setFrameWidth(CCoord width)399 void CCheckBox::setFrameWidth (CCoord width)
400 {
401 if (frameWidth != width)
402 {
403 frameWidth = width;
404 invalid ();
405 }
406 }
407
408 //------------------------------------------------------------------------
setRoundRectRadius(CCoord radius)409 void CCheckBox::setRoundRectRadius (CCoord radius)
410 {
411 if (roundRectRadius != radius)
412 {
413 roundRectRadius = radius;
414 invalid ();
415 }
416 }
417
418 /// @cond ignore
419 //------------------------------------------------------------------------
getFontCapHeight(CFontRef font)420 static CCoord getFontCapHeight (CFontRef font)
421 {
422 CCoord c = font->getSize ();
423 IPlatformFont* pf = font->getPlatformFont ();
424 if (pf)
425 {
426 CCoord capHeight = pf->getCapHeight ();
427 if (capHeight <= 0)
428 capHeight = pf->getAscent ();
429 if (capHeight > 0)
430 c = capHeight;
431 }
432 return c;
433 }
434 /// @endcond
435
436 //------------------------------------------------------------------------
437 static CCoord kCheckBoxTitleMargin = 5;
438
439 //------------------------------------------------------------------------
sizeToFit()440 bool CCheckBox::sizeToFit ()
441 {
442 if (title.empty ())
443 return false;
444 if (auto painter = font->getFontPainter ())
445 {
446 CRect fitSize (getViewSize ());
447 if (getDrawBackground ())
448 {
449 fitSize.setWidth (getDrawBackground ()->getWidth ());
450 fitSize.setHeight (getDrawBackground ()->getHeight () / 6);
451 }
452 else
453 {
454 fitSize.setWidth (fitSize.getHeight ());
455 }
456 fitSize.right += kCheckBoxTitleMargin;
457 fitSize.right += painter->getStringWidth (nullptr, UTF8String (title).getPlatformString (), true);
458 setViewSize (fitSize);
459 setMouseableArea (fitSize);
460 return true;
461 }
462 return false;
463 }
464
465 //------------------------------------------------------------------------
draw(CDrawContext * context)466 void CCheckBox::draw (CDrawContext* context)
467 {
468 float norm = getValueNormalized ();
469 CRect checkBoxSize (getViewSize ());
470 if (getDrawBackground ())
471 {
472 CPoint off;
473
474 checkBoxSize.setWidth (getDrawBackground ()->getWidth ());
475 checkBoxSize.setHeight (getDrawBackground ()->getHeight () / 6);
476
477 if (norm == 0.5)
478 off.y = checkBoxSize.getHeight ();
479 else if (norm > 0.5)
480 off.y = checkBoxSize.getHeight () * 2;
481 else
482 off.y = 0;
483 if (hilight)
484 off.y += getDrawBackground ()->getHeight () / 2.;
485
486 getDrawBackground ()->draw (context, checkBoxSize, off);
487 }
488 else
489 {
490 auto lineWidth = frameWidth;
491 if (lineWidth < 0)
492 lineWidth = context->getHairlineSize ();
493 checkBoxSize.setHeight (std::floor (getFontCapHeight (font) + 2.5));
494 checkBoxSize.setWidth (checkBoxSize.getHeight ());
495 checkBoxSize.offset (1., std::ceil ((getViewSize ().getHeight () - checkBoxSize.getHeight ()) / 2.));
496 context->setLineWidth (lineWidth);
497 context->setLineStyle (kLineSolid);
498 context->setDrawMode (kAntiAliasing);
499 context->setFrameColor (boxFrameColor);
500 context->setFillColor (boxFillColor);
501 if (auto path = owned (context->createRoundRectGraphicsPath (checkBoxSize, roundRectRadius)))
502 {
503 context->drawGraphicsPath (path, CDrawContext::kPathFilled);
504 context->drawGraphicsPath (path, CDrawContext::kPathStroked);
505 }
506 else
507 {
508 context->drawRect (checkBoxSize, kDrawFilledAndStroked);
509 }
510
511 if (hilight)
512 {
513 CColor hilightColor = boxFrameColor;
514 hilightColor.alpha /= 2;
515 context->setFrameColor (hilightColor);
516 CRect r (checkBoxSize);
517 r.inset (lineWidth, lineWidth);
518 if (auto path = owned (context->createRoundRectGraphicsPath (r, roundRectRadius)))
519 {
520 context->drawGraphicsPath (path, CDrawContext::kPathStroked);
521 }
522 else
523 {
524 context->drawRect (r, kDrawStroked);
525 }
526 }
527
528 context->setDrawMode (kAntiAliasing);
529 context->setFrameColor (checkMarkColor);
530 context->setLineWidth (2.);
531
532 const CCoord cbInset = 2.;
533
534 if (style & kDrawCrossBox)
535 {
536 if (norm == 0.5f)
537 {
538 context->drawLine (CPoint (checkBoxSize.left + cbInset, checkBoxSize.top + checkBoxSize.getHeight () / 2.), CPoint (checkBoxSize.right - cbInset, checkBoxSize.top + checkBoxSize.getHeight () / 2));
539 }
540 else if (norm > 0.5f)
541 {
542 context->drawLine (CPoint (checkBoxSize.left + cbInset, checkBoxSize.top + cbInset), CPoint (checkBoxSize.right - cbInset, checkBoxSize.bottom - cbInset));
543 context->drawLine (CPoint (checkBoxSize.left + cbInset, checkBoxSize.bottom - cbInset), CPoint (checkBoxSize.right - cbInset, checkBoxSize.top + cbInset));
544 }
545 }
546 else
547 {
548 if (norm == 0.5f)
549 {
550 context->drawLine (CPoint (checkBoxSize.left + cbInset, checkBoxSize.top + checkBoxSize.getHeight () / 2.), CPoint (checkBoxSize.right - cbInset, checkBoxSize.top + checkBoxSize.getHeight () / 2));
551 }
552 else if (norm > 0.5f)
553 {
554 SharedPointer<CGraphicsPath> path = owned (context->createGraphicsPath ());
555 if (path)
556 {
557 path->beginSubpath (CPoint (checkBoxSize.left + cbInset, checkBoxSize.top + checkBoxSize.getHeight () / 2.));
558 path->addLine (CPoint (checkBoxSize.left + checkBoxSize.getWidth () / 2, checkBoxSize.bottom - cbInset));
559 path->addLine (CPoint (checkBoxSize.right + 1, checkBoxSize.top - 1));
560 context->drawGraphicsPath (path, CDrawContext::kPathStroked);
561 }
562 else
563 {
564 context->drawLine (CPoint (checkBoxSize.left + cbInset, checkBoxSize.top + checkBoxSize.getHeight () / 2.), CPoint (checkBoxSize.left + checkBoxSize.getWidth () / 2, checkBoxSize.bottom - cbInset));
565 context->drawLine (CPoint (checkBoxSize.left + checkBoxSize.getWidth () / 2., checkBoxSize.bottom - cbInset), CPoint (checkBoxSize.right + 1, checkBoxSize.top - 1));
566 }
567 }
568 }
569 }
570
571 if (title.empty() == false)
572 {
573 CPoint p (checkBoxSize.getBottomRight ());
574 p.offset (kCheckBoxTitleMargin, -1.);
575
576 context->setFont (font);
577 context->setFontColor (fontColor);
578 context->setDrawMode (kAntiAliasing);
579
580 context->drawString (title.getPlatformString (), p, true);
581 }
582
583 setDirty (false);
584 }
585
586 //------------------------------------------------------------------------
getFocusPath(CGraphicsPath & outPath)587 bool CCheckBox::getFocusPath (CGraphicsPath& outPath)
588 {
589 if (wantsFocus ())
590 {
591 CCoord focusWidth = getFrame ()->getFocusWidth ();
592 CRect checkBoxSize (getViewSize ());
593 if (getDrawBackground ())
594 {
595 checkBoxSize.setWidth (getDrawBackground ()->getWidth ());
596 checkBoxSize.setHeight (getDrawBackground ()->getHeight () / 6);
597 }
598 else
599 {
600 checkBoxSize.setHeight (std::floor (getFontCapHeight (font) + 2.5));
601 checkBoxSize.setWidth (checkBoxSize.getHeight ());
602 checkBoxSize.offset (1, std::ceil ((getViewSize ().getHeight () - checkBoxSize.getHeight ()) / 2));
603 }
604 outPath.addRoundRect (checkBoxSize, roundRectRadius);
605 checkBoxSize.extend (focusWidth, focusWidth);
606 outPath.addRoundRect (checkBoxSize, roundRectRadius);
607 }
608 return true;
609 }
610
611 //------------------------------------------------------------------------
onMouseDown(CPoint & where,const CButtonState & buttons)612 CMouseEventResult CCheckBox::onMouseDown (CPoint& where, const CButtonState& buttons)
613 {
614 if (buttons.isLeftButton ())
615 {
616 beginEdit ();
617 previousValue = value;
618 return onMouseMoved (where, buttons);
619 }
620 return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
621 }
622
623 //------------------------------------------------------------------------
onMouseMoved(CPoint & where,const CButtonState & buttons)624 CMouseEventResult CCheckBox::onMouseMoved (CPoint& where, const CButtonState& buttons)
625 {
626 if (isEditing ())
627 {
628 bool wasHilighted = hilight;
629 if (getViewSize ().pointInside (where))
630 hilight = true;
631 else
632 hilight = false;
633 if (wasHilighted != hilight)
634 invalid ();
635 return kMouseEventHandled;
636 }
637 return kMouseEventNotHandled;
638 }
639
640 //------------------------------------------------------------------------
onMouseCancel()641 CMouseEventResult CCheckBox::onMouseCancel ()
642 {
643 if (isEditing ())
644 {
645 hilight = false;
646 value = previousValue;
647 if (isDirty ())
648 valueChanged ();
649 invalid ();
650 endEdit ();
651 }
652 return kMouseEventHandled;
653 }
654
655 //------------------------------------------------------------------------
onMouseUp(CPoint & where,const CButtonState & buttons)656 CMouseEventResult CCheckBox::onMouseUp (CPoint& where, const CButtonState& buttons)
657 {
658 hilight = false;
659 if (getViewSize ().pointInside (where))
660 value = (previousValue < getMax ()) ? getMax () : getMin ();
661 else
662 value = previousValue;
663 if (isDirty ())
664 {
665 valueChanged ();
666 invalid ();
667 }
668 endEdit ();
669 return kMouseEventHandled;
670 }
671
672 //------------------------------------------------------------------------
onKeyDown(VstKeyCode & keyCode)673 int32_t CCheckBox::onKeyDown (VstKeyCode& keyCode)
674 {
675 if (keyCode.virt == VKEY_RETURN && keyCode.modifier == 0)
676 {
677 value = (value < getMax ()) ? getMax () : getMin ();
678 invalid ();
679 beginEdit ();
680 valueChanged ();
681 endEdit ();
682 return 1;
683 }
684 return -1;
685 }
686
687 //------------------------------------------------------------------------
688 //------------------------------------------------------------------------
689 //------------------------------------------------------------------------
CTextButton(const CRect & size,IControlListener * listener,int32_t tag,UTF8StringPtr title,Style style)690 CTextButton::CTextButton (const CRect& size, IControlListener* listener, int32_t tag, UTF8StringPtr title, Style style)
691 : CControl (size, listener, tag, nullptr)
692 , font (kSystemFont)
693 , frameWidth (1.)
694 , roundRadius (6.)
695 , textMargin (0.)
696 , horiTxtAlign (kCenterText)
697 , iconPosition (CDrawMethods::kIconLeft)
698 , style (style)
699 , title (title)
700 {
701 setTextColor (kBlackCColor);
702 setTextColorHighlighted (kWhiteCColor);
703
704 gradient = owned (CGradient::create (0, 1, CColor (220, 220, 220, 255), CColor (180, 180, 180, 255)));
705 gradientHighlighted = owned (CGradient::create (0, 1, CColor (180, 180, 180, 255), CColor (100, 100, 100, 255)));
706
707 setFrameColor (kBlackCColor);
708 setFrameColorHighlighted (kBlackCColor);
709 setWantsFocus (true);
710 }
711
712 //------------------------------------------------------------------------
removed(CView * parent)713 bool CTextButton::removed (CView* parent)
714 {
715 invalidPath ();
716 return CControl::removed (parent);
717 }
718
719 //------------------------------------------------------------------------
setViewSize(const CRect & rect,bool invalid)720 void CTextButton::setViewSize (const CRect& rect, bool invalid)
721 {
722 invalidPath ();
723 CControl::setViewSize (rect, invalid);
724 }
725
726 //------------------------------------------------------------------------
getGradient() const727 CGradient* CTextButton::getGradient () const
728 {
729 return gradient;
730 }
731
732 //------------------------------------------------------------------------
getGradientHighlighted() const733 CGradient* CTextButton::getGradientHighlighted () const
734 {
735 return gradientHighlighted;
736 }
737
738 //------------------------------------------------------------------------
getIcon() const739 CBitmap* CTextButton::getIcon () const
740 {
741 return icon;
742 }
743
744 //------------------------------------------------------------------------
getIconHighlighted() const745 CBitmap* CTextButton::getIconHighlighted () const
746 {
747 return iconHighlighted;
748 }
749
750 //------------------------------------------------------------------------
setTitle(const UTF8String & newTitle)751 void CTextButton::setTitle (const UTF8String& newTitle)
752 {
753 title = newTitle;
754 invalid ();
755 }
756
757 //------------------------------------------------------------------------
setFont(CFontRef newFont)758 void CTextButton::setFont (CFontRef newFont)
759 {
760 font = newFont;
761 invalid ();
762 }
763
764 //------------------------------------------------------------------------
setTextColor(const CColor & color)765 void CTextButton::setTextColor (const CColor& color)
766 {
767 textColor = color;
768 invalid ();
769 }
770
771 //------------------------------------------------------------------------
setGradient(CGradient * newGradient)772 void CTextButton::setGradient (CGradient* newGradient)
773 {
774 gradient = newGradient;
775 invalid ();
776 }
777
778 //------------------------------------------------------------------------
setGradientHighlighted(CGradient * newGradient)779 void CTextButton::setGradientHighlighted (CGradient* newGradient)
780 {
781 gradientHighlighted = newGradient;
782 invalid ();
783 }
784
785 //------------------------------------------------------------------------
setFrameColor(const CColor & color)786 void CTextButton::setFrameColor (const CColor& color)
787 {
788 frameColor = color;
789 invalid ();
790 }
791
792 //------------------------------------------------------------------------
setTextColorHighlighted(const CColor & color)793 void CTextButton::setTextColorHighlighted (const CColor& color)
794 {
795 textColorHighlighted = color;
796 invalid ();
797 }
798
799 //------------------------------------------------------------------------
setFrameColorHighlighted(const CColor & color)800 void CTextButton::setFrameColorHighlighted (const CColor& color)
801 {
802 frameColorHighlighted = color;
803 invalid ();
804 }
805
806 //------------------------------------------------------------------------
setFrameWidth(CCoord width)807 void CTextButton::setFrameWidth (CCoord width)
808 {
809 frameWidth = width;
810 invalid ();
811 }
812
813 //------------------------------------------------------------------------
setRoundRadius(CCoord radius)814 void CTextButton::setRoundRadius (CCoord radius)
815 {
816 roundRadius = radius;
817 invalidPath ();
818 invalid ();
819 }
820
821 //------------------------------------------------------------------------
setStyle(Style _style)822 void CTextButton::setStyle (Style _style)
823 {
824 style = _style;
825 }
826
827 //------------------------------------------------------------------------
setIcon(CBitmap * bitmap)828 void CTextButton::setIcon (CBitmap* bitmap)
829 {
830 if (icon != bitmap)
831 {
832 icon = bitmap;
833 invalid ();
834 }
835 }
836
837 //------------------------------------------------------------------------
setIconHighlighted(CBitmap * bitmap)838 void CTextButton::setIconHighlighted (CBitmap* bitmap)
839 {
840 if (iconHighlighted != bitmap)
841 {
842 iconHighlighted = bitmap;
843 invalid ();
844 }
845 }
846
847 //------------------------------------------------------------------------
setIconPosition(CDrawMethods::IconPosition pos)848 void CTextButton::setIconPosition (CDrawMethods::IconPosition pos)
849 {
850 if (iconPosition != pos)
851 {
852 iconPosition = pos;
853 invalid ();
854 }
855 }
856
857 //------------------------------------------------------------------------
setTextMargin(CCoord margin)858 void CTextButton::setTextMargin (CCoord margin)
859 {
860 if (textMargin != margin)
861 {
862 textMargin = margin;
863 invalid ();
864 }
865 }
866
867 //------------------------------------------------------------------------
setTextAlignment(CHoriTxtAlign hAlign)868 void CTextButton::setTextAlignment (CHoriTxtAlign hAlign)
869 {
870 // to force the redraw
871 if (horiTxtAlign != hAlign)
872 {
873 horiTxtAlign = hAlign;
874 invalid ();
875 }
876 }
877
878
879 //------------------------------------------------------------------------
sizeToFit()880 bool CTextButton::sizeToFit ()
881 {
882 if (title.empty ())
883 return false;
884 if (auto painter = font->getFontPainter ())
885 {
886 CRect fitSize (getViewSize ());
887 fitSize.right = fitSize.left + (roundRadius + 1.) * 4.;
888 fitSize.right += painter->getStringWidth (nullptr, title.getPlatformString (), true);
889 setViewSize (fitSize);
890 setMouseableArea (fitSize);
891 return true;
892 }
893 return false;
894 }
895
896 //------------------------------------------------------------------------
draw(CDrawContext * context)897 void CTextButton::draw (CDrawContext* context)
898 {
899 bool highlight = value > 0.5 ? true : false;
900 auto lineWidth = getFrameWidth ();
901 if (lineWidth < 0.)
902 lineWidth = context->getHairlineSize ();
903 context->setDrawMode (kAntiAliasing);
904 context->setLineWidth (lineWidth);
905 context->setLineStyle (CLineStyle (CLineStyle::kLineCapRound, CLineStyle::kLineJoinRound));
906 context->setFrameColor (highlight ? frameColorHighlighted : frameColor);
907 CRect r (getViewSize ());
908 r.inset (lineWidth / 2., lineWidth / 2.);
909 if (gradient && gradientHighlighted)
910 {
911 CGraphicsPath* path = getPath (context, lineWidth);
912 if (path)
913 {
914 CGradient* drawGradient = highlight ? gradientHighlighted : gradient;
915 if (drawGradient)
916 context->fillLinearGradient (path, *drawGradient, r.getTopLeft (), r.getBottomLeft (), false);
917 context->drawGraphicsPath (path, CDrawContext::kPathStroked);
918 }
919 }
920 CRect titleRect = getViewSize ();
921 titleRect.inset (lineWidth / 2., lineWidth / 2.);
922
923 CBitmap* iconToDraw = nullptr;
924 if (!getMouseEnabled () && getDisabledBackground ())
925 iconToDraw = getDisabledBackground ();
926 else
927 iconToDraw = highlight ? (iconHighlighted ? iconHighlighted : icon) : (icon ? icon : iconHighlighted);
928 CDrawMethods::drawIconAndText (context, iconToDraw, iconPosition, getTextAlignment (), getTextMargin (), titleRect, title, getFont (), highlight ? getTextColorHighlighted () : getTextColor ());
929 setDirty (false);
930 }
931
932 //------------------------------------------------------------------------
getFocusPath(CGraphicsPath & outPath)933 bool CTextButton::getFocusPath (CGraphicsPath& outPath)
934 {
935 CRect r (getViewSize ());
936 CCoord focusWidth = getFrame ()->getFocusWidth ();
937 r.inset (-focusWidth, -focusWidth);
938 outPath.addRoundRect (r, roundRadius);
939 outPath.closeSubpath ();
940 r = getViewSize ();
941 outPath.addRoundRect (r, roundRadius);
942 return true;
943 }
944
945 //------------------------------------------------------------------------
drawFocusOnTop()946 bool CTextButton::drawFocusOnTop ()
947 {
948 return false;
949 }
950
951 //------------------------------------------------------------------------
getPath(CDrawContext * context,CCoord lineWidth)952 CGraphicsPath* CTextButton::getPath (CDrawContext* context, CCoord lineWidth)
953 {
954 if (_path == nullptr)
955 {
956 CRect r (getViewSize ());
957 r.inset (lineWidth / 2., lineWidth / 2.);
958 _path = owned (context->createRoundRectGraphicsPath (r, roundRadius));
959 }
960 return _path;
961 }
962
963 //------------------------------------------------------------------------
invalidPath()964 void CTextButton::invalidPath ()
965 {
966 _path = nullptr;
967 }
968
969 //------------------------------------------------------------------------
onMouseDown(CPoint & where,const CButtonState & buttons)970 CMouseEventResult CTextButton::onMouseDown (CPoint& where, const CButtonState& buttons)
971 {
972 if (!(buttons & kLButton))
973 return kMouseEventNotHandled;
974 fEntryState = value;
975 beginEdit ();
976 return onMouseMoved (where, buttons);
977 }
978
979 //------------------------------------------------------------------------
onMouseCancel()980 CMouseEventResult CTextButton::onMouseCancel ()
981 {
982 if (isEditing ())
983 {
984 value = fEntryState;
985 if (isDirty ())
986 invalid ();
987 endEdit ();
988 }
989 return kMouseEventHandled;
990 }
991
992 //------------------------------------------------------------------------
onMouseUp(CPoint & where,const CButtonState & buttons)993 CMouseEventResult CTextButton::onMouseUp (CPoint& where, const CButtonState& buttons)
994 {
995 if (isEditing ())
996 {
997 if (value != fEntryState)
998 {
999 valueChanged ();
1000 if (style == kKickStyle)
1001 {
1002 value = getMin (); // set button to UNSELECTED state
1003 valueChanged ();
1004 }
1005 if (isDirty ())
1006 invalid ();
1007 }
1008 endEdit ();
1009 }
1010 return kMouseEventHandled;
1011 }
1012
1013 //------------------------------------------------------------------------
onMouseMoved(CPoint & where,const CButtonState & buttons)1014 CMouseEventResult CTextButton::onMouseMoved (CPoint& where, const CButtonState& buttons)
1015 {
1016 if (isEditing ())
1017 {
1018 if (where.x >= getViewSize ().left && where.y >= getViewSize ().top &&
1019 where.x <= getViewSize ().right && where.y <= getViewSize ().bottom)
1020 value = fEntryState == getMin () ? getMax () : getMin ();
1021 else
1022 value = fEntryState == getMin () ? getMin () : getMax ();
1023
1024 if (isDirty ())
1025 invalid ();
1026 return kMouseEventHandled;
1027 }
1028 return kMouseEventNotHandled;
1029 }
1030
1031 //------------------------------------------------------------------------
onKeyDown(VstKeyCode & keyCode)1032 int32_t CTextButton::onKeyDown (VstKeyCode& keyCode)
1033 {
1034 if (keyCode.modifier == 0 && keyCode.virt == VKEY_RETURN)
1035 {
1036 if (style == kKickStyle)
1037 {
1038 if (value != getMax ())
1039 {
1040 beginEdit ();
1041 value = getMax ();
1042 invalid ();
1043 valueChanged ();
1044 value = getMin ();
1045 invalid ();
1046 valueChanged ();
1047 endEdit ();
1048 }
1049 }
1050 else
1051 {
1052 beginEdit ();
1053 if (value == getMin ())
1054 value = getMax ();
1055 else
1056 value = getMin ();
1057 invalid ();
1058 valueChanged ();
1059 endEdit ();
1060 }
1061 return 1;
1062 }
1063 return -1;
1064 }
1065
1066 //------------------------------------------------------------------------
onKeyUp(VstKeyCode & keyCode)1067 int32_t CTextButton::onKeyUp (VstKeyCode& keyCode)
1068 {
1069 return -1;
1070 }
1071
1072 } // namespace
1073