912 lines
24 KiB
C++
912 lines
24 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 "cknob.h"
|
|
#include "../cbitmap.h"
|
|
#include "../cdrawcontext.h"
|
|
#include "../cframe.h"
|
|
#include "../cgraphicspath.h"
|
|
#include "../cvstguitimer.h"
|
|
#include "../events.h"
|
|
#include <cmath>
|
|
|
|
namespace VSTGUI {
|
|
#if TARGET_OS_IPHONE
|
|
static const float kCKnobRangeDefault = 300.f;
|
|
#else
|
|
static const float kCKnobRangeDefault = 200.f;
|
|
#endif
|
|
|
|
static constexpr CViewAttributeID kCKnobMouseStateAttribute = 'knms';
|
|
//------------------------------------------------------------------------
|
|
struct CKnobBase::MouseEditingState
|
|
{
|
|
CPoint firstPoint;
|
|
CPoint lastPoint;
|
|
float startValue;
|
|
float entryState;
|
|
float range;
|
|
float coef;
|
|
CButtonState oldButton;
|
|
bool modeLinear;
|
|
};
|
|
|
|
//------------------------------------------------------------------------
|
|
CKnobBase::CKnobBase (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background)
|
|
: CControl (size, listener, tag, background)
|
|
{
|
|
rangeAngle = 1.f;
|
|
setStartAngle ((float)(3.f * Constants::quarter_pi));
|
|
setRangeAngle ((float)(3.f * Constants::half_pi));
|
|
zoomFactor = 1.5f;
|
|
knobRange = kCKnobRangeDefault;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
CKnobBase::CKnobBase (const CKnobBase& k)
|
|
: CControl (k)
|
|
, startAngle (k.startAngle)
|
|
, rangeAngle (k.rangeAngle)
|
|
, zoomFactor (k.zoomFactor)
|
|
, inset (k.inset)
|
|
{
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnobBase::setViewSize (const CRect &rect, bool invalid)
|
|
{
|
|
CControl::setViewSize (rect, invalid);
|
|
compute ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool CKnobBase::sizeToFit ()
|
|
{
|
|
if (getDrawBackground ())
|
|
{
|
|
CRect vs (getViewSize ());
|
|
vs.setWidth (getDrawBackground ()->getWidth ());
|
|
vs.setHeight (getDrawBackground ()->getHeight ());
|
|
setViewSize (vs);
|
|
setMouseableArea (vs);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
auto CKnobBase::getMouseEditingState () -> MouseEditingState&
|
|
{
|
|
MouseEditingState* state = nullptr;
|
|
if (!getAttribute (kCKnobMouseStateAttribute, state))
|
|
{
|
|
state = new MouseEditingState;
|
|
setAttribute (kCKnobMouseStateAttribute, state);
|
|
}
|
|
return *state;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnobBase::clearMouseEditingState ()
|
|
{
|
|
MouseEditingState* state = nullptr;
|
|
if (!getAttribute (kCKnobMouseStateAttribute, state))
|
|
return;
|
|
delete state;
|
|
removeAttribute (kCKnobMouseStateAttribute);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
CMouseEventResult CKnobBase::onMouseDown (CPoint& where, const CButtonState& buttons)
|
|
{
|
|
if (!buttons.isLeftButton ())
|
|
return kMouseEventNotHandled;
|
|
|
|
invalidMouseWheelEditTimer (this);
|
|
beginEdit ();
|
|
|
|
auto& mouseState = getMouseEditingState ();
|
|
mouseState.firstPoint = where;
|
|
mouseState.lastPoint (-1, -1);
|
|
mouseState.startValue = getOldValue ();
|
|
|
|
mouseState.modeLinear = false;
|
|
mouseState.entryState = value;
|
|
mouseState.range = knobRange;
|
|
mouseState.coef = (getMax () - getMin ()) / mouseState.range;
|
|
mouseState.oldButton = buttons;
|
|
|
|
int32_t mode = kCircularMode;
|
|
int32_t newMode = getFrame ()->getKnobMode ();
|
|
if (kLinearMode == newMode)
|
|
{
|
|
if (!(buttons & kAlt))
|
|
mode = newMode;
|
|
}
|
|
else if (buttons & kAlt)
|
|
{
|
|
mode = kLinearMode;
|
|
}
|
|
|
|
if (mode == kLinearMode)
|
|
{
|
|
if (buttons & kZoomModifier)
|
|
mouseState.range *= zoomFactor;
|
|
mouseState.lastPoint = where;
|
|
mouseState.modeLinear = true;
|
|
mouseState.coef = (getMax () - getMin ()) / mouseState.range;
|
|
}
|
|
else
|
|
{
|
|
CPoint where2 (where);
|
|
where2.offset (-getViewSize ().left, -getViewSize ().top);
|
|
mouseState.startValue = valueFromPoint (where2);
|
|
mouseState.lastPoint = where;
|
|
}
|
|
|
|
return onMouseMoved (where, buttons);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
CMouseEventResult CKnobBase::onMouseUp (CPoint& where, const CButtonState& buttons)
|
|
{
|
|
if (isEditing ())
|
|
{
|
|
endEdit ();
|
|
clearMouseEditingState ();
|
|
}
|
|
return kMouseEventHandled;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
CMouseEventResult CKnobBase::onMouseCancel ()
|
|
{
|
|
if (isEditing ())
|
|
{
|
|
auto& mouseState = getMouseEditingState ();
|
|
value = mouseState.startValue;
|
|
if (isDirty ())
|
|
{
|
|
valueChanged ();
|
|
invalid ();
|
|
}
|
|
endEdit ();
|
|
clearMouseEditingState ();
|
|
}
|
|
return kMouseEventHandled;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
CMouseEventResult CKnobBase::onMouseMoved (CPoint& where, const CButtonState& buttons)
|
|
{
|
|
if (buttons.isLeftButton () && isEditing ())
|
|
{
|
|
auto& mouseState = getMouseEditingState ();
|
|
|
|
float middle = (getMax () - getMin ()) * 0.5f;
|
|
|
|
if (where != mouseState.lastPoint)
|
|
{
|
|
mouseState.lastPoint = where;
|
|
if (mouseState.modeLinear)
|
|
{
|
|
CCoord diff = (mouseState.firstPoint.y - where.y) + (where.x - mouseState.firstPoint.x);
|
|
if (buttons != mouseState.oldButton)
|
|
{
|
|
mouseState.range = knobRange;
|
|
if (buttons & kZoomModifier)
|
|
mouseState.range *= zoomFactor;
|
|
|
|
float coef2 = (getMax () - getMin ()) / mouseState.range;
|
|
mouseState.entryState += (float)(diff * (mouseState.coef - coef2));
|
|
mouseState.coef = coef2;
|
|
mouseState.oldButton = buttons;
|
|
}
|
|
value = (float)(mouseState.entryState + diff * mouseState.coef);
|
|
bounceValue ();
|
|
}
|
|
else
|
|
{
|
|
where.offset (-getViewSize ().left, -getViewSize ().top);
|
|
value = valueFromPoint (where);
|
|
if (mouseState.startValue - value > middle)
|
|
value = getMax ();
|
|
else if (value - mouseState.startValue > middle)
|
|
value = getMin ();
|
|
else
|
|
mouseState.startValue = value;
|
|
}
|
|
if (value != getOldValue ())
|
|
valueChanged ();
|
|
if (isDirty ())
|
|
invalid ();
|
|
}
|
|
return kMouseEventHandled;
|
|
}
|
|
return kMouseEventNotHandled;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnobBase::onMouseWheelEvent (MouseWheelEvent& event)
|
|
{
|
|
onMouseWheelEditing (this);
|
|
|
|
float v = getValueNormalized ();
|
|
if (buttonStateFromEventModifiers (event.modifiers) & kZoomModifier)
|
|
v += 0.1f * static_cast<float> (event.deltaY) * getWheelInc ();
|
|
else
|
|
v += static_cast<float> (event.deltaY) * getWheelInc ();
|
|
setValueNormalized (v);
|
|
|
|
if (isDirty ())
|
|
{
|
|
invalid ();
|
|
valueChanged ();
|
|
}
|
|
event.consumed = true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnobBase::onKeyboardEvent (KeyboardEvent& event)
|
|
{
|
|
if (event.type != EventType::KeyDown)
|
|
return;
|
|
switch (event.virt)
|
|
{
|
|
case VirtualKey::Up :
|
|
case VirtualKey::Right :
|
|
case VirtualKey::Down :
|
|
case VirtualKey::Left :
|
|
{
|
|
float distance = 1.f;
|
|
if (event.virt == VirtualKey::Down || event.virt == VirtualKey::Left)
|
|
distance = -distance;
|
|
|
|
float v = getValueNormalized ();
|
|
if (buttonStateFromEventModifiers (event.modifiers) & kZoomModifier)
|
|
v += 0.1f * distance * getWheelInc ();
|
|
else
|
|
v += distance * getWheelInc ();
|
|
setValueNormalized (v);
|
|
|
|
if (isDirty ())
|
|
{
|
|
invalid ();
|
|
beginEdit ();
|
|
valueChanged ();
|
|
endEdit ();
|
|
}
|
|
event.consumed = true;
|
|
}
|
|
case VirtualKey::Escape:
|
|
{
|
|
if (isEditing ())
|
|
{
|
|
onMouseCancel ();
|
|
event.consumed = true;
|
|
}
|
|
break;
|
|
}
|
|
default: return;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnobBase::setStartAngle (float val)
|
|
{
|
|
startAngle = val;
|
|
compute ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnobBase::setRangeAngle (float val)
|
|
{
|
|
rangeAngle = val;
|
|
compute ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnobBase::compute ()
|
|
{
|
|
setDirty ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnobBase::valueToPoint (CPoint &point) const
|
|
{
|
|
float alpha = (value - getMin()) / (getMax() - getMin());
|
|
alpha = startAngle + alpha*rangeAngle;
|
|
|
|
CPoint c (getViewSize ().getWidth () / 2., getViewSize ().getHeight () / 2.);
|
|
double xradius = c.x - inset;
|
|
double yradius = c.y - inset;
|
|
|
|
point.x = (CCoord)(c.x + cosf (alpha) * xradius + 0.5f);
|
|
point.y = (CCoord)(c.y + sinf (alpha) * yradius + 0.5f);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
float CKnobBase::valueFromPoint (CPoint &point) const
|
|
{
|
|
float v;
|
|
double d = rangeAngle * 0.5;
|
|
double a = startAngle + d;
|
|
|
|
CPoint c (getViewSize ().getWidth () / 2., getViewSize ().getHeight () / 2.);
|
|
double xradius = c.x - inset;
|
|
double yradius = c.y - inset;
|
|
|
|
double dx = (point.x - c.x) / xradius;
|
|
double dy = (point.y - c.y) / yradius;
|
|
|
|
double alpha = atan2 (dy, dx) - a;
|
|
while (alpha >= Constants::pi)
|
|
alpha -= Constants::double_pi;
|
|
while (alpha < -Constants::pi)
|
|
alpha += Constants::double_pi;
|
|
|
|
if (d < 0.0)
|
|
alpha = -alpha;
|
|
|
|
if (alpha > d)
|
|
v = getMax ();
|
|
else if (alpha < -d)
|
|
v = getMin ();
|
|
else
|
|
{
|
|
v = float (0.5 + alpha / rangeAngle);
|
|
v = getMin () + (v * getRange ());
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnobBase::setMin (float val)
|
|
{
|
|
CControl::setMin (val);
|
|
if (getValue () < val)
|
|
setValue (val);
|
|
compute ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnobBase::setMax (float val)
|
|
{
|
|
CControl::setMax (val);
|
|
if (getValue () > val)
|
|
setValue (val);
|
|
compute ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// CKnob
|
|
//------------------------------------------------------------------------
|
|
/*! @class CKnob
|
|
Define a knob with a given background and foreground handle.
|
|
The handle describes a circle over the background (between -45deg and +225deg).
|
|
By clicking alt modifier and left mouse button the default value is used.
|
|
By clicking alt modifier and left mouse button the value changes with a vertical move (version 2.1)
|
|
*/
|
|
//------------------------------------------------------------------------
|
|
/**
|
|
* CKnob constructor.
|
|
* @param size the size of this view
|
|
* @param listener the listener
|
|
* @param tag the control tag
|
|
* @param background background bitmap
|
|
* @param handle handle bitmap
|
|
* @param offset offset of background bitmap
|
|
* @param drawStyle draw style
|
|
*/
|
|
//------------------------------------------------------------------------
|
|
CKnob::CKnob (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background, CBitmap* handle, const CPoint& offset, int32_t drawStyle)
|
|
: CKnobBase (size, listener, tag, background)
|
|
, offset (offset)
|
|
, drawStyle (drawStyle)
|
|
, handleLineWidth (1.)
|
|
, coronaInset (0)
|
|
, coronaOutlineWidthAdd (2.)
|
|
, pHandle (handle)
|
|
{
|
|
if (pHandle)
|
|
{
|
|
pHandle->remember ();
|
|
inset = (CCoord)((float)pHandle->getWidth () / 2.f + 2.5f);
|
|
}
|
|
else
|
|
{
|
|
inset = 3;
|
|
}
|
|
|
|
colorShadowHandle = kGreyCColor;
|
|
colorHandle = kWhiteCColor;
|
|
coronaLineStyle = kLineOnOffDash;
|
|
coronaLineStyle.getDashLengths ()[1] = 2.;
|
|
|
|
setWantsFocus (true);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
CKnob::CKnob (const CKnob& v)
|
|
: CKnobBase (v)
|
|
, offset (v.offset)
|
|
, drawStyle (v.drawStyle)
|
|
, colorHandle (v.colorHandle)
|
|
, colorShadowHandle (v.colorShadowHandle)
|
|
, handleLineWidth (v.handleLineWidth)
|
|
, coronaInset (v.coronaInset)
|
|
, coronaOutlineWidthAdd (v.coronaInset)
|
|
, coronaLineStyle (v.coronaLineStyle)
|
|
, pHandle (v.pHandle)
|
|
{
|
|
if (pHandle)
|
|
pHandle->remember ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
CKnob::~CKnob () noexcept
|
|
{
|
|
if (pHandle)
|
|
pHandle->forget ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool CKnob::drawFocusOnTop ()
|
|
{
|
|
if (drawStyle & kCoronaDrawing && wantsFocus ())
|
|
{
|
|
return false;
|
|
}
|
|
return CKnobBase::drawFocusOnTop ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool CKnob::getFocusPath (CGraphicsPath &outPath)
|
|
{
|
|
if (drawStyle & kCoronaDrawing && wantsFocus ())
|
|
{
|
|
CRect corona (getViewSize ());
|
|
corona.inset (coronaInset, coronaInset);
|
|
corona.inset (handleLineWidth/2., handleLineWidth/2.);
|
|
outPath.addEllipse (corona);
|
|
return true;
|
|
}
|
|
return CKnobBase::getFocusPath (outPath);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::draw (CDrawContext *pContext)
|
|
{
|
|
if (getDrawBackground ())
|
|
{
|
|
getDrawBackground ()->draw (pContext, getViewSize (), offset);
|
|
}
|
|
if (pHandle)
|
|
drawHandle (pContext);
|
|
else
|
|
{
|
|
if (drawStyle & kCoronaOutline)
|
|
drawCoronaOutline (pContext);
|
|
if (drawStyle & kCoronaDrawing)
|
|
drawCorona (pContext);
|
|
if (!(drawStyle & kSkipHandleDrawing))
|
|
{
|
|
if (drawStyle & kHandleCircleDrawing)
|
|
drawHandleAsCircle (pContext);
|
|
else
|
|
drawHandleAsLine (pContext);
|
|
}
|
|
}
|
|
setDirty (false);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::addArc (CGraphicsPath* path, const CRect& r, double startAngle, double sweepAngle)
|
|
{
|
|
CCoord w = r.getWidth ();
|
|
CCoord h = r.getHeight ();
|
|
double endAngle = startAngle + sweepAngle;
|
|
if (w != h)
|
|
{
|
|
startAngle = atan2 (sin (startAngle) * h, cos (startAngle) * w);
|
|
endAngle = atan2 (sin (endAngle) * h, cos (endAngle) * w);
|
|
}
|
|
path->addArc (r, startAngle / Constants::pi * 180, endAngle / Constants::pi * 180, sweepAngle >= 0);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::drawCoronaOutline (CDrawContext* pContext) const
|
|
{
|
|
auto path = owned (pContext->createGraphicsPath ());
|
|
if (path == nullptr)
|
|
return;
|
|
CRect corona (getViewSize ());
|
|
corona.inset (coronaInset, coronaInset);
|
|
auto start = startAngle;
|
|
auto range = rangeAngle;
|
|
if (coronaOutlineWidthAdd && (drawStyle & kCoronaLineCapButt))
|
|
{
|
|
auto a = static_cast<float> (coronaOutlineWidthAdd / getWidth ());
|
|
start -= a;
|
|
range += a * 2.f;
|
|
}
|
|
addArc (path, corona, start, range);
|
|
pContext->setFrameColor (colorShadowHandle);
|
|
CLineStyle lineStyle (kLineSolid);
|
|
if (!(drawStyle & kCoronaLineCapButt))
|
|
lineStyle.setLineCap (CLineStyle::kLineCapRound);
|
|
pContext->setLineStyle (lineStyle);
|
|
pContext->setLineWidth (handleLineWidth+coronaOutlineWidthAdd);
|
|
pContext->setDrawMode (kAntiAliasing | kNonIntegralMode);
|
|
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::drawCorona (CDrawContext* pContext) const
|
|
{
|
|
auto path = owned (pContext->createGraphicsPath ());
|
|
if (path == nullptr)
|
|
return;
|
|
float coronaValue = getValueNormalized ();
|
|
if (drawStyle & kCoronaInverted)
|
|
coronaValue = 1.f - coronaValue;
|
|
CRect corona (getViewSize ());
|
|
corona.inset (coronaInset, coronaInset);
|
|
if (drawStyle & kCoronaFromCenter)
|
|
addArc (path, corona, 1.5 * Constants::pi, rangeAngle * (coronaValue - 0.5));
|
|
else
|
|
{
|
|
if (drawStyle & kCoronaInverted)
|
|
addArc (path, corona, startAngle + rangeAngle, -rangeAngle * coronaValue);
|
|
else
|
|
addArc (path, corona, startAngle, rangeAngle * coronaValue);
|
|
}
|
|
pContext->setFrameColor (coronaColor);
|
|
if (!(drawStyle & kCoronaLineCapButt))
|
|
{
|
|
CLineStyle lineStyle (kLineSolid);
|
|
lineStyle.setLineCap (CLineStyle::kLineCapRound);
|
|
pContext->setLineStyle (lineStyle);
|
|
}
|
|
else if (drawStyle & kCoronaLineDashDot)
|
|
pContext->setLineStyle (coronaLineStyle);
|
|
else
|
|
pContext->setLineStyle (kLineSolid);
|
|
|
|
pContext->setLineWidth (handleLineWidth);
|
|
pContext->setDrawMode (kAntiAliasing | kNonIntegralMode);
|
|
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::drawHandleAsCircle (CDrawContext* pContext) const
|
|
{
|
|
CPoint where;
|
|
valueToPoint (where);
|
|
|
|
where.offset (getViewSize ().left, getViewSize ().top);
|
|
CRect r (where.x - 0.5, where.y - 0.5, where.x + 0.5, where.y + 0.5);
|
|
r.extend (handleLineWidth, handleLineWidth);
|
|
pContext->setDrawMode (kAntiAliasing);
|
|
pContext->setFrameColor (colorShadowHandle);
|
|
pContext->setFillColor (colorHandle);
|
|
pContext->setLineWidth (0.5);
|
|
pContext->setLineStyle (kLineSolid);
|
|
pContext->setDrawMode (kAntiAliasing | kNonIntegralMode);
|
|
pContext->drawEllipse (r, kDrawFilledAndStroked);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::drawHandleAsLine (CDrawContext* pContext) const
|
|
{
|
|
CPoint where;
|
|
valueToPoint (where);
|
|
|
|
CPoint origin (getViewSize ().getWidth () / 2, getViewSize ().getHeight () / 2);
|
|
where.offset (getViewSize ().left - 1, getViewSize ().top);
|
|
origin.offset (getViewSize ().left - 1, getViewSize ().top);
|
|
pContext->setFrameColor (colorShadowHandle);
|
|
pContext->setLineWidth (handleLineWidth);
|
|
pContext->setLineStyle (CLineStyle (CLineStyle::kLineCapRound));
|
|
pContext->setDrawMode (kAntiAliasing | kNonIntegralMode);
|
|
pContext->drawLine (where, origin);
|
|
|
|
where.offset (1, -1);
|
|
origin.offset (1, -1);
|
|
pContext->setFrameColor (colorHandle);
|
|
pContext->drawLine (where, origin);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::drawHandle (CDrawContext *pContext)
|
|
{
|
|
CPoint where;
|
|
valueToPoint (where);
|
|
|
|
CCoord width = pHandle->getWidth ();
|
|
CCoord height = pHandle->getHeight ();
|
|
where.offset (getViewSize ().left - width / 2, getViewSize ().top - height / 2);
|
|
|
|
where.x = floor (where.x);
|
|
where.y = floor (where.y);
|
|
|
|
CRect handleSize (0, 0, width, height);
|
|
handleSize.offset (where.x, where.y);
|
|
pHandle->draw (pContext, handleSize);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::setCoronaInset (CCoord inset)
|
|
{
|
|
if (inset != coronaInset)
|
|
{
|
|
coronaInset = inset;
|
|
setDirty ();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::setCoronaColor (CColor color)
|
|
{
|
|
if (color != coronaColor)
|
|
{
|
|
coronaColor = color;
|
|
setDirty ();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::setColorShadowHandle (CColor color)
|
|
{
|
|
if (color != colorShadowHandle)
|
|
{
|
|
colorShadowHandle = color;
|
|
setDirty ();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::setColorHandle (CColor color)
|
|
{
|
|
if (color != colorHandle)
|
|
{
|
|
colorHandle = color;
|
|
setDirty ();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::setHandleLineWidth (CCoord width)
|
|
{
|
|
if (width != handleLineWidth)
|
|
{
|
|
handleLineWidth = width;
|
|
setDirty ();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::setCoronaOutlineWidthAdd (CCoord width)
|
|
{
|
|
if (width != coronaOutlineWidthAdd)
|
|
{
|
|
coronaOutlineWidthAdd = width;
|
|
setDirty ();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
const CLineStyle::CoordVector& CKnob::getCoronaDashDotLengths () const
|
|
{
|
|
return coronaLineStyle.getDashLengths ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::setCoronaDashDotLengths (const CLineStyle::CoordVector& lengths)
|
|
{
|
|
if (coronaLineStyle.getDashLengths () != lengths)
|
|
{
|
|
coronaLineStyle.getDashLengths () = lengths;
|
|
setDirty ();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::setDrawStyle (int32_t style)
|
|
{
|
|
if (style != drawStyle)
|
|
{
|
|
drawStyle = style;
|
|
setDirty ();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CKnob::setHandleBitmap (CBitmap* bitmap)
|
|
{
|
|
if (pHandle)
|
|
{
|
|
pHandle->forget ();
|
|
pHandle = nullptr;
|
|
}
|
|
|
|
if (bitmap)
|
|
{
|
|
pHandle = bitmap;
|
|
pHandle->remember ();
|
|
inset = (CCoord)((float)pHandle->getWidth () / 2.f + 2.5f);
|
|
}
|
|
setDirty ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// CAnimKnob
|
|
//------------------------------------------------------------------------
|
|
/*! @class CAnimKnob
|
|
Such as a CKnob control object, but there is a unique bitmap which contains different views
|
|
(subbitmaps) of this knob. According to the value, a specific subbitmap is displayed. Use a
|
|
CMultiFrameBitmap for its background bitmap.
|
|
*/
|
|
//------------------------------------------------------------------------
|
|
/**
|
|
* CAnimKnob constructor.
|
|
* @param size the size of this view
|
|
* @param listener the listener
|
|
* @param tag the control tag
|
|
* @param background the background bitmap
|
|
*/
|
|
//------------------------------------------------------------------------
|
|
CAnimKnob::CAnimKnob (const CRect& size, IControlListener* listener, int32_t tag,
|
|
CBitmap* background)
|
|
: CKnobBase (size, listener, tag, background), bInverseBitmap (false)
|
|
{
|
|
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
|
heightOfOneImage = size.getHeight ();
|
|
setNumSubPixmaps (0);
|
|
if (background)
|
|
{
|
|
if (auto frameBitmap = dynamic_cast<CMultiFrameBitmap*> (background))
|
|
{
|
|
heightOfOneImage = frameBitmap->getFrameSize ().y;
|
|
setNumSubPixmaps (frameBitmap->getNumFrames ());
|
|
}
|
|
else
|
|
{
|
|
setNumSubPixmaps ((int32_t)(background->getHeight () / heightOfOneImage));
|
|
}
|
|
}
|
|
#endif
|
|
inset = 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
CAnimKnob::CAnimKnob (const CAnimKnob& v)
|
|
: CKnobBase (v)
|
|
, bInverseBitmap (v.bInverseBitmap)
|
|
{
|
|
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
|
setNumSubPixmaps (v.subPixmaps);
|
|
setHeightOfOneImage (v.heightOfOneImage);
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------------
|
|
bool CAnimKnob::sizeToFit ()
|
|
{
|
|
if (auto bitmap = getDrawBackground ())
|
|
{
|
|
CRect vs (getViewSize ());
|
|
if (auto frameBitmap = dynamic_cast<CMultiFrameBitmap*> (bitmap))
|
|
{
|
|
vs.setSize (frameBitmap->getFrameSize ());
|
|
}
|
|
else
|
|
{
|
|
vs.setWidth (bitmap->getWidth ());
|
|
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
|
vs.setHeight (getHeightOfOneImage ());
|
|
#else
|
|
vs.setHeight (bitmap->getHeight ());
|
|
#endif
|
|
}
|
|
setViewSize (vs);
|
|
setMouseableArea (vs);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
|
//------------------------------------------------------------------------
|
|
/**
|
|
* CAnimKnob constructor.
|
|
* @param size the size of this view
|
|
* @param listener the listener
|
|
* @param tag the control tag
|
|
* @param subPixmaps number of sub bitmaps in background
|
|
* @param heightOfOneImage the height of one sub bitmap
|
|
* @param background the background bitmap
|
|
* @param offset unused
|
|
*/
|
|
//------------------------------------------------------------------------
|
|
CAnimKnob::CAnimKnob (const CRect& size, IControlListener* listener, int32_t tag,
|
|
int32_t subPixmaps, CCoord heightOfOneImage, CBitmap* background,
|
|
const CPoint& offset)
|
|
: CKnobBase (size, listener, tag, background), bInverseBitmap (false)
|
|
{
|
|
vstgui_assert (background && !dynamic_cast<CMultiFrameBitmap*> (background),
|
|
"Use the other constrcutor when using a CMultiFrameBitmap");
|
|
setNumSubPixmaps (subPixmaps);
|
|
setHeightOfOneImage (heightOfOneImage);
|
|
inset = 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------------
|
|
void CAnimKnob::setHeightOfOneImage (const CCoord& height)
|
|
{
|
|
if (dynamic_cast<CMultiFrameBitmap*> (getDrawBackground ()))
|
|
return;
|
|
IMultiBitmapControl::setHeightOfOneImage (height);
|
|
if (getDrawBackground () && heightOfOneImage > 0)
|
|
setNumSubPixmaps ((int32_t)(getDrawBackground ()->getHeight () / heightOfOneImage));
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------------------------
|
|
void CAnimKnob::setBackground (CBitmap *background)
|
|
{
|
|
CKnobBase::setBackground (background);
|
|
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
|
if (auto frameBitmap = dynamic_cast<CMultiFrameBitmap*> (background))
|
|
{
|
|
heightOfOneImage = frameBitmap->getFrameSize ().y;
|
|
setNumSubPixmaps (frameBitmap->getNumFrames ());
|
|
return;
|
|
}
|
|
|
|
if (heightOfOneImage == 0)
|
|
heightOfOneImage = getViewSize ().getHeight ();
|
|
if (background && heightOfOneImage > 0)
|
|
setNumSubPixmaps ((int32_t)(background->getHeight () / heightOfOneImage));
|
|
#endif
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void CAnimKnob::draw (CDrawContext *pContext)
|
|
{
|
|
if (auto bitmap = getDrawBackground ())
|
|
{
|
|
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
|
|
{
|
|
auto frameIndex = getMultiFrameBitmapIndex (*mfb, getValueNormalized ());
|
|
if (bInverseBitmap)
|
|
frameIndex = getInverseIndex (*mfb, frameIndex);
|
|
mfb->drawFrame (pContext, frameIndex, getViewSize ().getTopLeft ());
|
|
}
|
|
else
|
|
{
|
|
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
|
CPoint where (0, 0);
|
|
float val = getValueNormalized ();
|
|
if (val >= 0.f && heightOfOneImage > 0.)
|
|
{
|
|
CCoord tmp = heightOfOneImage * (getNumSubPixmaps () - 1);
|
|
if (bInverseBitmap)
|
|
where.y = floor ((1. - val) * tmp);
|
|
else
|
|
where.y = floor (val * tmp);
|
|
where.y -= (int32_t)where.y % (int32_t)heightOfOneImage;
|
|
}
|
|
bitmap->draw (pContext, getViewSize (), where);
|
|
#else
|
|
CView::draw (pContext);
|
|
#endif
|
|
}
|
|
}
|
|
setDirty (false);
|
|
}
|
|
|
|
} // VSTGUI
|