Files
2026-08-16 18:24:52 +07:00

1103 lines
30 KiB
C++

// This file is part of VSTGUI. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
#include "cbuttons.h"
#include "../cdrawcontext.h"
#include "../cbitmap.h"
#include "../cframe.h"
#include "../cgraphicspath.h"
#include "../events.h"
#include "../platform/iplatformfont.h"
#include <cmath>
namespace VSTGUI {
//------------------------------------------------------------------------
// COnOffButton
//------------------------------------------------------------------------
/*! @class COnOffButton
Define a button with 2 positions.
The bitmap includes the 2 subbitmaps (i.e the rectangle used for the display of this button is half-height of the bitmap).
When its value changes, the listener is called.
*/
//------------------------------------------------------------------------
/**
* COnOffButton constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param background bitmap of the on/off button
* @param style style, currently not used
*/
//------------------------------------------------------------------------
COnOffButton::COnOffButton (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background, int32_t style)
: CControl (size, listener, tag, background)
, style (style)
{
setWantsFocus (true);
}
//------------------------------------------------------------------------
COnOffButton::COnOffButton (const COnOffButton& v)
: CControl (v)
, style (v.style)
{
setWantsFocus (true);
}
//------------------------------------------------------------------------
void COnOffButton::draw (CDrawContext *pContext)
{
if (getDrawBackground ())
{
CCoord off;
if (value == getMax ())
off = getDrawBackground ()->getHeight () / 2;
else
off = 0;
getDrawBackground ()->draw (pContext, getViewSize (), CPoint (0, off));
}
setDirty (false);
}
//------------------------------------------------------------------------
CMouseEventResult COnOffButton::onMouseDown (CPoint& where, const CButtonState& buttons)
{
if (!(buttons & kLButton))
return kMouseEventNotHandled;
beginEdit ();
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult COnOffButton::onMouseMoved (CPoint& where, const CButtonState& buttons)
{
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult COnOffButton::onMouseUp (CPoint& where, const CButtonState& buttons)
{
if (isEditing ())
{
if (getViewSize ().pointInside (where))
{
value = (value == getMax ()) ? getMin () : getMax ();
invalid ();
valueChanged ();
}
endEdit ();
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult COnOffButton::onMouseCancel ()
{
if (isEditing ())
endEdit ();
return kMouseEventHandled;
}
//------------------------------------------------------------------------
void COnOffButton::onKeyboardEvent (KeyboardEvent& event)
{
if (event.type != EventType::KeyDown)
return;
if (event.virt == VirtualKey::Return && event.modifiers.empty ())
{
value = (value == getMax ()) ? getMin () : getMax ();
invalid ();
beginEdit ();
valueChanged ();
endEdit ();
event.consumed = true;
}
}
//------------------------------------------------------------------------
bool COnOffButton::sizeToFit ()
{
if (getDrawBackground ())
{
CRect vs (getViewSize ());
vs.setWidth (getDrawBackground ()->getWidth ());
vs.setHeight (getDrawBackground ()->getHeight () / 2.);
setViewSize (vs, true);
setMouseableArea (vs);
return true;
}
return false;
}
//------------------------------------------------------------------------
// CKickButton
//------------------------------------------------------------------------
/*! @class CKickButton
Define a button with 2 states using 2 subbitmaps.
One click on it, then the second subbitmap is displayed.
When the mouse button is relaxed, the first subbitmap is framed.
Use a CMultiFrameBitmap for its background bitmap.
*/
//------------------------------------------------------------------------
/**
* CKickButton constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param background the bitmap
*/
//------------------------------------------------------------------------
CKickButton::CKickButton (const CRect& size, IControlListener* listener, int32_t tag,
CBitmap* background)
: CControl (size, listener, tag, background)
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
if (dynamic_cast<CMultiFrameBitmap*> (background) == nullptr)
{
heightOfOneImage = size.getHeight ();
}
#endif
setWantsFocus (true);
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
/**
* CKickButton constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param heightOfOneImage height of one sub bitmap in background
* @param background the bitmap
* @param offset of background
*/
//------------------------------------------------------------------------
CKickButton::CKickButton (const CRect& size, IControlListener* listener, int32_t tag, CCoord heightOfOneImage, CBitmap* background, const CPoint& offset)
: CControl (size, listener, tag, background)
, offset (offset)
{
setHeightOfOneImage (heightOfOneImage);
setWantsFocus (true);
}
#endif
//------------------------------------------------------------------------
CKickButton::CKickButton (const CKickButton& v) : CControl (v)
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
offset = v.offset;
setHeightOfOneImage (v.heightOfOneImage);
#endif
setWantsFocus (true);
}
//------------------------------------------------------------------------
void CKickButton::draw (CDrawContext *pContext)
{
bounceValue ();
if (auto bitmap = getDrawBackground ())
{
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
auto index = getMultiFrameBitmapIndex (*mfb, getValueNormalized ());
mfb->drawFrame (pContext, index, getViewSize ().getTopLeft ());
}
else
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CPoint where (offset.x, offset.y);
if (value == getMax ())
where.y += heightOfOneImage;
bitmap->draw (pContext, getViewSize (), where);
#else
bitmap->draw (pContext, getViewSize ());
#endif
}
}
setDirty (false);
}
//------------------------------------------------------------------------
CMouseEventResult CKickButton::onMouseDown (CPoint& where, const CButtonState& buttons)
{
if (!(buttons & kLButton))
return kMouseEventNotHandled;
beginEdit ();
return onMouseMoved (where, buttons);
}
//------------------------------------------------------------------------
CMouseEventResult CKickButton::onMouseCancel ()
{
if (isEditing ())
{
value = getMin ();
if (isDirty ())
{
valueChanged ();
invalid ();
}
endEdit ();
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CKickButton::onMouseUp (CPoint& where, const CButtonState& buttons)
{
if (isEditing ())
{
if (value > 0.f)
valueChanged ();
value = getMin ();
valueChanged ();
if (isDirty ())
invalid ();
endEdit ();
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CKickButton::onMouseMoved (CPoint& where, const CButtonState& buttons)
{
if (isEditing ())
{
if (where.x >= getViewSize ().left && where.y >= getViewSize ().top &&
where.x <= getViewSize ().right && where.y <= getViewSize ().bottom)
value = getMax ();
else
value = getMin ();
if (isDirty ())
invalid ();
return kMouseEventHandled;
}
return kMouseEventNotHandled;
}
//------------------------------------------------------------------------
void CKickButton::onKeyboardEvent (KeyboardEvent& event)
{
if (event.modifiers.empty () && event.virt == VirtualKey::Return)
{
if (event.type == EventType::KeyDown)
{
if (value != getMax ())
{
beginEdit ();
value = getMax ();
invalid ();
valueChanged ();
}
event.consumed = true;
}
else if (event.type == EventType::KeyUp && isEditing ())
{
value = getMin ();
invalid ();
valueChanged ();
endEdit ();
event.consumed = true;
}
}
}
//------------------------------------------------------------------------
bool CKickButton::sizeToFit ()
{
if (auto bitmap = getDrawBackground ())
{
CRect vs (getViewSize ());
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
vs.setSize (mfb->getFrameSize ());
}
else
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
vs.setHeight (heightOfOneImage);
#else
vs.setHeight (bitmap->getHeight ());
#endif
vs.setWidth (bitmap->getWidth ());
}
setViewSize (vs, true);
setMouseableArea (vs);
return true;
}
return false;
}
//------------------------------------------------------------------------
// CCheckBox
//------------------------------------------------------------------------
/*! @class CCheckBox
A checkbox control with a title and 3 states : checked, half checked, not checked
- if value is < 0.5 the checkbox is not checked
- if value is 0.5 the checkbox is half checked
- if value is > 0.5 the checkbox is checked
the user can only switch between checked and not checked state.
If the bitmap is set, the bitmap must contain 6 states of the checkbox in the following order:
- not checked
- half checked
- checked
- not checked hilighted
- half checked hilighted
- checked hilighted
*/
//------------------------------------------------------------------------
//------------------------------------------------------------------------
CCheckBox::CCheckBox (const CRect& size, IControlListener* listener, int32_t tag, UTF8StringPtr title, CBitmap* bitmap, int32_t style)
: CControl (size, listener, tag, bitmap)
, style (style)
, fontColor (kWhiteCColor)
, font (kSystemFont)
{
setTitle (title);
setBoxFillColor (kWhiteCColor);
setBoxFrameColor (kBlackCColor);
setCheckMarkColor (kRedCColor);
setWantsFocus (true);
if (style & kAutoSizeToFit)
sizeToFit ();
}
//------------------------------------------------------------------------
CCheckBox::CCheckBox (const CCheckBox& checkbox)
: CControl (checkbox)
, style (checkbox.style)
, fontColor (checkbox.fontColor)
, font (checkbox.font)
{
setTitle (checkbox.title);
setBoxFillColor (checkbox.boxFillColor);
setBoxFrameColor (checkbox.boxFrameColor);
setCheckMarkColor (checkbox.checkMarkColor);
setWantsFocus (true);
}
//------------------------------------------------------------------------
void CCheckBox::setTitle (const UTF8String& newTitle)
{
title = newTitle;
if (style & kAutoSizeToFit)
sizeToFit ();
}
//------------------------------------------------------------------------
void CCheckBox::setFont (CFontRef newFont)
{
font = newFont;
if (font && style & kAutoSizeToFit)
sizeToFit ();
}
//------------------------------------------------------------------------
void CCheckBox::setBackground (CBitmap *background)
{
CView::setBackground (background);
if (style & kAutoSizeToFit)
sizeToFit ();
}
//------------------------------------------------------------------------
void CCheckBox::setStyle (int32_t newStyle)
{
if (style != newStyle)
{
style = newStyle;
if (style & kAutoSizeToFit)
sizeToFit ();
invalid ();
}
}
//------------------------------------------------------------------------
void CCheckBox::setFrameWidth (CCoord width)
{
if (frameWidth != width)
{
frameWidth = width;
invalid ();
}
}
//------------------------------------------------------------------------
void CCheckBox::setRoundRectRadius (CCoord radius)
{
if (roundRectRadius != radius)
{
roundRectRadius = radius;
invalid ();
}
}
/// @cond ignore
//------------------------------------------------------------------------
static CCoord getFontCapHeight (CFontRef font)
{
CCoord c = font->getSize ();
IPlatformFont* pf = font->getPlatformFont ();
if (pf)
{
CCoord capHeight = pf->getCapHeight ();
if (capHeight <= 0)
capHeight = pf->getAscent ();
if (capHeight > 0)
c = capHeight;
}
return c;
}
/// @endcond
//------------------------------------------------------------------------
static CCoord kCheckBoxTitleMargin = 5;
//------------------------------------------------------------------------
bool CCheckBox::sizeToFit ()
{
if (title.empty ())
return false;
if (auto painter = font->getFontPainter ())
{
CRect fitSize (getViewSize ());
if (getDrawBackground ())
{
fitSize.setWidth (getDrawBackground ()->getWidth ());
fitSize.setHeight (getDrawBackground ()->getHeight () / 6);
}
else
{
fitSize.setWidth (fitSize.getHeight ());
}
fitSize.right += kCheckBoxTitleMargin;
fitSize.right += painter->getStringWidth (nullptr, UTF8String (title).getPlatformString (), true);
setViewSize (fitSize);
setMouseableArea (fitSize);
return true;
}
return false;
}
//------------------------------------------------------------------------
void CCheckBox::draw (CDrawContext* context)
{
float norm = getValueNormalized ();
CRect checkBoxSize (getViewSize ());
if (getDrawBackground ())
{
CPoint off;
checkBoxSize.setWidth (getDrawBackground ()->getWidth ());
checkBoxSize.setHeight (getDrawBackground ()->getHeight () / 6);
if (norm == 0.5)
off.y = checkBoxSize.getHeight ();
else if (norm > 0.5)
off.y = checkBoxSize.getHeight () * 2;
else
off.y = 0;
if (hilight)
off.y += getDrawBackground ()->getHeight () / 2.;
getDrawBackground ()->draw (context, checkBoxSize, off);
}
else
{
auto lineWidth = frameWidth;
if (lineWidth < 0)
lineWidth = context->getHairlineSize ();
if (!(style & kIgnoreCapHeightOnDraw))
checkBoxSize.setHeight (std::floor (getFontCapHeight (font) + 2.5));
else
checkBoxSize.bottom -= 2.;
checkBoxSize.setWidth (checkBoxSize.getHeight ());
checkBoxSize.offset (1., std::ceil ((getViewSize ().getHeight () - checkBoxSize.getHeight ()) / 2.));
context->setLineWidth (lineWidth);
context->setLineStyle (kLineSolid);
context->setDrawMode (kAntiAliasing);
context->setFrameColor (boxFrameColor);
context->setFillColor (boxFillColor);
if (auto path = owned (context->createRoundRectGraphicsPath (checkBoxSize, roundRectRadius)))
{
context->drawGraphicsPath (path, CDrawContext::kPathFilled);
context->drawGraphicsPath (path, CDrawContext::kPathStroked);
}
else
{
context->drawRect (checkBoxSize, kDrawFilledAndStroked);
}
if (hilight)
{
CColor hilightColor = boxFrameColor;
hilightColor.alpha /= 2;
context->setFrameColor (hilightColor);
CRect r (checkBoxSize);
r.inset (lineWidth, lineWidth);
if (auto path = owned (context->createRoundRectGraphicsPath (r, roundRectRadius)))
{
context->drawGraphicsPath (path, CDrawContext::kPathStroked);
}
else
{
context->drawRect (r, kDrawStroked);
}
}
context->setDrawMode (kAntiAliasing);
context->setFrameColor (checkMarkColor);
context->setLineWidth (2.);
const CCoord cbInset = 2.;
if (style & kDrawCrossBox)
{
if (norm == 0.5f)
{
context->drawLine (CPoint (checkBoxSize.left + cbInset, checkBoxSize.top + checkBoxSize.getHeight () / 2.), CPoint (checkBoxSize.right - cbInset, checkBoxSize.top + checkBoxSize.getHeight () / 2));
}
else if (norm > 0.5f)
{
context->drawLine (CPoint (checkBoxSize.left + cbInset, checkBoxSize.top + cbInset), CPoint (checkBoxSize.right - cbInset, checkBoxSize.bottom - cbInset));
context->drawLine (CPoint (checkBoxSize.left + cbInset, checkBoxSize.bottom - cbInset), CPoint (checkBoxSize.right - cbInset, checkBoxSize.top + cbInset));
}
}
else
{
if (norm == 0.5f)
{
context->drawLine (CPoint (checkBoxSize.left + cbInset, checkBoxSize.top + checkBoxSize.getHeight () / 2.), CPoint (checkBoxSize.right - cbInset, checkBoxSize.top + checkBoxSize.getHeight () / 2));
}
else if (norm > 0.5f)
{
SharedPointer<CGraphicsPath> path = owned (context->createGraphicsPath ());
if (path)
{
path->beginSubpath (CPoint (checkBoxSize.left + cbInset, checkBoxSize.top + checkBoxSize.getHeight () / 2.));
path->addLine (CPoint (checkBoxSize.left + checkBoxSize.getWidth () / 2, checkBoxSize.bottom - cbInset));
path->addLine (CPoint (checkBoxSize.right + 1, checkBoxSize.top - 1));
context->drawGraphicsPath (path, CDrawContext::kPathStroked);
}
else
{
context->drawLine (CPoint (checkBoxSize.left + cbInset, checkBoxSize.top + checkBoxSize.getHeight () / 2.), CPoint (checkBoxSize.left + checkBoxSize.getWidth () / 2, checkBoxSize.bottom - cbInset));
context->drawLine (CPoint (checkBoxSize.left + checkBoxSize.getWidth () / 2., checkBoxSize.bottom - cbInset), CPoint (checkBoxSize.right + 1, checkBoxSize.top - 1));
}
}
}
}
if (title.empty() == false)
{
CPoint p (checkBoxSize.getBottomRight ());
p.offset (kCheckBoxTitleMargin, -1.);
context->setFont (font);
context->setFontColor (fontColor);
context->setDrawMode (kAntiAliasing);
context->drawString (title.getPlatformString (), p, true);
}
setDirty (false);
}
//------------------------------------------------------------------------
bool CCheckBox::getFocusPath (CGraphicsPath& outPath)
{
if (wantsFocus ())
{
CCoord focusWidth = getFrame ()->getFocusWidth ();
CRect checkBoxSize (getViewSize ());
if (getDrawBackground ())
{
checkBoxSize.setWidth (getDrawBackground ()->getWidth ());
checkBoxSize.setHeight (getDrawBackground ()->getHeight () / 6);
}
else
{
if (!(style & kIgnoreCapHeightOnDraw))
checkBoxSize.setHeight (std::floor (getFontCapHeight (font) + 2.5));
else
checkBoxSize.bottom -= 2.;
checkBoxSize.setWidth (checkBoxSize.getHeight ());
checkBoxSize.offset (1, std::ceil ((getViewSize ().getHeight () - checkBoxSize.getHeight ()) / 2));
}
outPath.addRoundRect (checkBoxSize, roundRectRadius);
checkBoxSize.extend (focusWidth, focusWidth);
outPath.addRoundRect (checkBoxSize, roundRectRadius);
}
return true;
}
//------------------------------------------------------------------------
CMouseEventResult CCheckBox::onMouseDown (CPoint& where, const CButtonState& buttons)
{
if (buttons.isLeftButton ())
{
beginEdit ();
previousValue = value;
return onMouseMoved (where, buttons);
}
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
}
//------------------------------------------------------------------------
CMouseEventResult CCheckBox::onMouseMoved (CPoint& where, const CButtonState& buttons)
{
if (isEditing ())
{
bool wasHilighted = hilight;
if (getViewSize ().pointInside (where))
hilight = true;
else
hilight = false;
if (wasHilighted != hilight)
invalid ();
return kMouseEventHandled;
}
return kMouseEventNotHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CCheckBox::onMouseCancel ()
{
if (isEditing ())
{
hilight = false;
value = previousValue;
if (isDirty ())
valueChanged ();
invalid ();
endEdit ();
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CCheckBox::onMouseUp (CPoint& where, const CButtonState& buttons)
{
hilight = false;
if (getViewSize ().pointInside (where))
value = (previousValue < getMax ()) ? getMax () : getMin ();
else
value = previousValue;
if (isDirty ())
{
valueChanged ();
invalid ();
}
endEdit ();
return kMouseEventHandled;
}
//------------------------------------------------------------------------
void CCheckBox::onKeyboardEvent (KeyboardEvent& event)
{
if (event.type == EventType::KeyDown && event.virt == VirtualKey::Return &&
event.modifiers.empty ())
{
value = (value < getMax ()) ? getMax () : getMin ();
invalid ();
beginEdit ();
valueChanged ();
endEdit ();
event.consumed = true;
}
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
CTextButton::CTextButton (const CRect& size, IControlListener* listener, int32_t tag, UTF8StringPtr title, Style style)
: CControl (size, listener, tag, nullptr)
, font (kSystemFont)
, frameWidth (1.)
, roundRadius (6.)
, textMargin (0.)
, horiTxtAlign (kCenterText)
, iconPosition (CDrawMethods::kIconLeft)
, style (style)
, title (title)
{
setTextColor (kBlackCColor);
setTextColorHighlighted (kWhiteCColor);
gradient = owned (CGradient::create (0, 1, CColor (220, 220, 220, 255), CColor (180, 180, 180, 255)));
gradientHighlighted = owned (CGradient::create (0, 1, CColor (180, 180, 180, 255), CColor (100, 100, 100, 255)));
setFrameColor (kBlackCColor);
setFrameColorHighlighted (kBlackCColor);
setWantsFocus (true);
}
//------------------------------------------------------------------------
bool CTextButton::removed (CView* parent)
{
invalidPath ();
return CControl::removed (parent);
}
//------------------------------------------------------------------------
void CTextButton::setViewSize (const CRect& rect, bool invalid)
{
invalidPath ();
CControl::setViewSize (rect, invalid);
}
//------------------------------------------------------------------------
CGradient* CTextButton::getGradient () const
{
return gradient;
}
//------------------------------------------------------------------------
CGradient* CTextButton::getGradientHighlighted () const
{
return gradientHighlighted;
}
//------------------------------------------------------------------------
CBitmap* CTextButton::getIcon () const
{
return icon;
}
//------------------------------------------------------------------------
CBitmap* CTextButton::getIconHighlighted () const
{
return iconHighlighted;
}
//------------------------------------------------------------------------
void CTextButton::setTitle (const UTF8String& newTitle)
{
title = newTitle;
invalid ();
}
//------------------------------------------------------------------------
void CTextButton::setFont (CFontRef newFont)
{
font = newFont;
invalid ();
}
//------------------------------------------------------------------------
void CTextButton::setTextColor (const CColor& color)
{
textColor = color;
invalid ();
}
//------------------------------------------------------------------------
void CTextButton::setGradient (CGradient* newGradient)
{
gradient = newGradient;
invalid ();
}
//------------------------------------------------------------------------
void CTextButton::setGradientHighlighted (CGradient* newGradient)
{
gradientHighlighted = newGradient;
invalid ();
}
//------------------------------------------------------------------------
void CTextButton::setFrameColor (const CColor& color)
{
frameColor = color;
invalid ();
}
//------------------------------------------------------------------------
void CTextButton::setTextColorHighlighted (const CColor& color)
{
textColorHighlighted = color;
invalid ();
}
//------------------------------------------------------------------------
void CTextButton::setFrameColorHighlighted (const CColor& color)
{
frameColorHighlighted = color;
invalid ();
}
//------------------------------------------------------------------------
void CTextButton::setFrameWidth (CCoord width)
{
frameWidth = width;
invalid ();
}
//------------------------------------------------------------------------
void CTextButton::setRoundRadius (CCoord radius)
{
roundRadius = radius;
invalidPath ();
invalid ();
}
//------------------------------------------------------------------------
void CTextButton::setStyle (Style _style)
{
style = _style;
}
//------------------------------------------------------------------------
void CTextButton::setIcon (CBitmap* bitmap)
{
if (icon != bitmap)
{
icon = bitmap;
invalid ();
}
}
//------------------------------------------------------------------------
void CTextButton::setIconHighlighted (CBitmap* bitmap)
{
if (iconHighlighted != bitmap)
{
iconHighlighted = bitmap;
invalid ();
}
}
//------------------------------------------------------------------------
void CTextButton::setIconPosition (CDrawMethods::IconPosition pos)
{
if (iconPosition != pos)
{
iconPosition = pos;
invalid ();
}
}
//------------------------------------------------------------------------
void CTextButton::setTextMargin (CCoord margin)
{
if (textMargin != margin)
{
textMargin = margin;
invalid ();
}
}
//------------------------------------------------------------------------
void CTextButton::setTextAlignment (CHoriTxtAlign hAlign)
{
// to force the redraw
if (horiTxtAlign != hAlign)
{
horiTxtAlign = hAlign;
invalid ();
}
}
//------------------------------------------------------------------------
bool CTextButton::sizeToFit ()
{
if (title.empty ())
return false;
if (auto painter = font->getFontPainter ())
{
CRect fitSize (getViewSize ());
fitSize.right = fitSize.left + (roundRadius + 1.) * 4.;
fitSize.right += painter->getStringWidth (nullptr, title.getPlatformString (), true);
setViewSize (fitSize);
setMouseableArea (fitSize);
return true;
}
return false;
}
//------------------------------------------------------------------------
void CTextButton::draw (CDrawContext* context)
{
bool highlight = value == getMax () ? true : false;
auto lineWidth = getFrameWidth ();
if (lineWidth < 0.)
lineWidth = context->getHairlineSize ();
context->setDrawMode (kAntiAliasing);
context->setLineWidth (lineWidth);
context->setLineStyle (CLineStyle (CLineStyle::kLineCapRound, CLineStyle::kLineJoinRound));
context->setFrameColor (highlight ? frameColorHighlighted : frameColor);
CRect r (getViewSize ());
r.inset (lineWidth / 2., lineWidth / 2.);
if (gradient && gradientHighlighted)
{
CGraphicsPath* path = getPath (context, lineWidth);
if (path)
{
CGradient* drawGradient = highlight ? gradientHighlighted : gradient;
if (drawGradient)
context->fillLinearGradient (path, *drawGradient, r.getTopLeft (), r.getBottomLeft (), false);
context->drawGraphicsPath (path, CDrawContext::kPathStroked);
}
}
CRect titleRect = getViewSize ();
titleRect.inset (lineWidth / 2., lineWidth / 2.);
CBitmap* iconToDraw = nullptr;
if (!getMouseEnabled () && getDisabledBackground ())
iconToDraw = getDisabledBackground ();
else
iconToDraw = highlight ? (iconHighlighted ? iconHighlighted : icon) : (icon ? icon : iconHighlighted);
CDrawMethods::drawIconAndText (context, iconToDraw, iconPosition, getTextAlignment (), getTextMargin (), titleRect, title, getFont (), highlight ? getTextColorHighlighted () : getTextColor ());
setDirty (false);
}
//------------------------------------------------------------------------
bool CTextButton::getFocusPath (CGraphicsPath& outPath)
{
CRect r (getViewSize ());
CCoord focusWidth = getFrame ()->getFocusWidth ();
r.inset (-focusWidth, -focusWidth);
outPath.addRoundRect (r, roundRadius);
outPath.closeSubpath ();
r = getViewSize ();
outPath.addRoundRect (r, roundRadius);
return true;
}
//------------------------------------------------------------------------
bool CTextButton::drawFocusOnTop ()
{
return false;
}
//------------------------------------------------------------------------
CGraphicsPath* CTextButton::getPath (CDrawContext* context, CCoord lineWidth)
{
if (_path == nullptr)
{
CRect r (getViewSize ());
r.inset (lineWidth / 2., lineWidth / 2.);
_path = owned (context->createRoundRectGraphicsPath (r, roundRadius));
}
return _path;
}
//------------------------------------------------------------------------
void CTextButton::invalidPath ()
{
_path = nullptr;
}
//------------------------------------------------------------------------
CMouseEventResult CTextButton::onMouseDown (CPoint& where, const CButtonState& buttons)
{
if (!(buttons & kLButton))
return kMouseEventNotHandled;
fEntryState = value;
beginEdit ();
return onMouseMoved (where, buttons);
}
//------------------------------------------------------------------------
CMouseEventResult CTextButton::onMouseCancel ()
{
if (isEditing ())
{
value = fEntryState;
if (isDirty ())
invalid ();
endEdit ();
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CTextButton::onMouseUp (CPoint& where, const CButtonState& buttons)
{
if (isEditing ())
{
if (value != fEntryState)
{
valueChanged ();
if (style == kKickStyle)
{
value = getMin (); // set button to UNSELECTED state
valueChanged ();
}
if (isDirty ())
invalid ();
}
endEdit ();
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CTextButton::onMouseMoved (CPoint& where, const CButtonState& buttons)
{
if (isEditing ())
{
if (where.x >= getViewSize ().left && where.y >= getViewSize ().top &&
where.x <= getViewSize ().right && where.y <= getViewSize ().bottom)
value = fEntryState == getMin () ? getMax () : getMin ();
else
value = fEntryState == getMin () ? getMin () : getMax ();
if (isDirty ())
invalid ();
return kMouseEventHandled;
}
return kMouseEventNotHandled;
}
//------------------------------------------------------------------------
void CTextButton::onKeyboardEvent (KeyboardEvent& event)
{
if (event.type != EventType::KeyDown)
return;
if (event.modifiers.empty () && event.virt == VirtualKey::Return)
{
if (style == kKickStyle)
{
if (value != getMax ())
{
beginEdit ();
value = getMax ();
invalid ();
valueChanged ();
value = getMin ();
invalid ();
valueChanged ();
endEdit ();
}
}
else
{
beginEdit ();
if (value == getMin ())
value = getMax ();
else
value = getMin ();
invalid ();
valueChanged ();
endEdit ();
}
event.consumed = true;
}
}
} // VSTGUI