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