Initial release
This commit is contained in:
@@ -0,0 +1,294 @@
|
||||
// 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 "cautoanimation.h"
|
||||
#include "../algorithm.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include "../cbitmap.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
//------------------------------------------------------------------------
|
||||
// CAutoAnimation
|
||||
//------------------------------------------------------------------------
|
||||
/*! @class CAutoAnimation
|
||||
An auto-animation control contains a given number of subbitmaps which can be displayed in loop.
|
||||
Two functions allows to get the previous or the next subbitmap (these functions increase or decrease
|
||||
the current value of this control). Use a CMultiFrameBitmap for its background bitmap.
|
||||
*/
|
||||
// displays bitmaps within a (child-) window
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CAutoAnimation constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param background the bitmap
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CAutoAnimation::CAutoAnimation (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
CBitmap* background)
|
||||
: CControl (size, listener, tag, background)
|
||||
{
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
heightOfOneImage = size.getHeight ();
|
||||
setNumSubPixmaps (background ? (int32_t)(background->getHeight () / heightOfOneImage) : 0);
|
||||
|
||||
totalHeightOfBitmap = heightOfOneImage * getNumSubPixmaps ();
|
||||
#else
|
||||
#endif
|
||||
}
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CAutoAnimation constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param background the bitmap
|
||||
* @param offset unused
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CAutoAnimation::CAutoAnimation (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
CBitmap* background, const CPoint& offset)
|
||||
: CControl (size, listener, tag, background), offset (offset)
|
||||
{
|
||||
heightOfOneImage = size.getHeight ();
|
||||
setNumSubPixmaps (background ? (int32_t)(background->getHeight () / heightOfOneImage) : 0);
|
||||
totalHeightOfBitmap = heightOfOneImage * getNumSubPixmaps ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CAutoAnimation 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 height of one sub bitmap
|
||||
* @param background the bitmap
|
||||
* @param offset unused
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CAutoAnimation::CAutoAnimation (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
int32_t subPixmaps, CCoord heightOfOneImage, CBitmap* background,
|
||||
const CPoint& offset)
|
||||
: CControl (size, listener, tag, background), offset (offset)
|
||||
{
|
||||
setNumSubPixmaps (subPixmaps);
|
||||
setHeightOfOneImage (heightOfOneImage);
|
||||
totalHeightOfBitmap = heightOfOneImage * getNumSubPixmaps ();
|
||||
setMin (0.f);
|
||||
setMax ((float)(totalHeightOfBitmap - (heightOfOneImage + 1.)));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CAutoAnimation::setBitmapOffset (const CPoint& off)
|
||||
{
|
||||
offset = off;
|
||||
invalid ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CPoint CAutoAnimation::getBitmapOffset () const { return offset; }
|
||||
|
||||
#endif // VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CAutoAnimation::CAutoAnimation (const CAutoAnimation& v)
|
||||
: CControl (v)
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
, offset (v.offset)
|
||||
, totalHeightOfBitmap (v.totalHeightOfBitmap)
|
||||
#endif
|
||||
{
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
setNumSubPixmaps (v.subPixmaps);
|
||||
setHeightOfOneImage (v.heightOfOneImage);
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CAutoAnimation::isWindowOpened () const { return bWindowOpened; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CAutoAnimation::draw (CDrawContext *pContext)
|
||||
{
|
||||
if (isWindowOpened ())
|
||||
{
|
||||
if (auto bitmap = getDrawBackground ())
|
||||
{
|
||||
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
|
||||
{
|
||||
auto frameIndex = getMultiFrameBitmapIndex (*mfb, getValueNormalized ());
|
||||
mfb->drawFrame (pContext, frameIndex, getViewSize ().getTopLeft ());
|
||||
}
|
||||
else
|
||||
{
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CPoint where;
|
||||
where.y = (int32_t)value + offset.y;
|
||||
where.x = offset.x;
|
||||
bitmap->draw (pContext, getViewSize (), where);
|
||||
#else
|
||||
CView::draw (pContext);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CAutoAnimation::onMouseDown (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (buttons & kLButton)
|
||||
{
|
||||
if (!isWindowOpened ())
|
||||
{
|
||||
value = 0;
|
||||
openWindow ();
|
||||
invalid ();
|
||||
valueChanged ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// stop info animation
|
||||
value = 0; // draw first pic of bitmap
|
||||
invalid ();
|
||||
closeWindow ();
|
||||
valueChanged ();
|
||||
}
|
||||
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
|
||||
}
|
||||
return kMouseEventNotHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CAutoAnimation::attached (CView* parent)
|
||||
{
|
||||
if (CControl::attached (parent))
|
||||
{
|
||||
if (animationFrameTime > 0 && isWindowOpened ())
|
||||
startTimer ();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CAutoAnimation::removed (CView* parent)
|
||||
{
|
||||
timer = nullptr;
|
||||
return CControl::removed (parent);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CAutoAnimation::startTimer ()
|
||||
{
|
||||
if (animationFrameTime > 0)
|
||||
{
|
||||
timer = makeOwned<CVSTGUITimer> (
|
||||
[this] (auto*) {
|
||||
nextPixmap ();
|
||||
invalid ();
|
||||
},
|
||||
animationFrameTime, true);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CAutoAnimation::openWindow ()
|
||||
{
|
||||
bWindowOpened = true;
|
||||
if (isAttached ())
|
||||
startTimer ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CAutoAnimation::closeWindow ()
|
||||
{
|
||||
bWindowOpened = false;
|
||||
timer = nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CAutoAnimation::updateMinMaxFromBackground ()
|
||||
{
|
||||
if (auto bitmap = getDrawBackground ())
|
||||
{
|
||||
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
|
||||
{
|
||||
auto numFrames = getMultiFrameBitmapRangeLength (*mfb);
|
||||
setMin (0.f);
|
||||
setMax (numFrames);
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
heightOfOneImage = mfb->getFrameSize ().y;
|
||||
totalHeightOfBitmap = heightOfOneImage * numFrames;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CAutoAnimation::setBackground (CBitmap* background)
|
||||
{
|
||||
CControl::setBackground (background);
|
||||
updateMinMaxFromBackground ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CAutoAnimation::nextPixmap ()
|
||||
{
|
||||
if (auto bitmap = getDrawBackground ())
|
||||
{
|
||||
if (dynamic_cast<CMultiFrameBitmap*> (bitmap))
|
||||
{
|
||||
if (getValue () == getMax ())
|
||||
setValue (getMin ());
|
||||
else
|
||||
setValue (getValue () + 1.f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
value += (float)heightOfOneImage;
|
||||
if (value >= (totalHeightOfBitmap - heightOfOneImage))
|
||||
value = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CAutoAnimation::previousPixmap ()
|
||||
{
|
||||
if (auto bitmap = getDrawBackground ())
|
||||
{
|
||||
if (dynamic_cast<CMultiFrameBitmap*> (bitmap))
|
||||
{
|
||||
if (getValue () == getMin ())
|
||||
setValue (getMax ());
|
||||
else
|
||||
setValue (getValue () - 1.f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
value -= (float)heightOfOneImage;
|
||||
if (value < 0.f)
|
||||
value = (float)(totalHeightOfBitmap - heightOfOneImage - 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CAutoAnimation::setAnimationTime (uint32_t animationTime)
|
||||
{
|
||||
animationFrameTime = animationTime;
|
||||
if (timer)
|
||||
startTimer ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
uint32_t CAutoAnimation::getAnimationTime () const { return animationFrameTime; }
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,90 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ccontrol.h"
|
||||
#include "../cbitmap.h"
|
||||
#include "../cvstguitimer.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CAutoAnimation Declaration
|
||||
//!
|
||||
/// @ingroup controls uses_multi_frame_bitmaps
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAutoAnimation : public CControl,
|
||||
public MultiFrameBitmapView<CAutoAnimation>
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
,
|
||||
public IMultiBitmapControl
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
CAutoAnimation (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
CBitmap* background);
|
||||
CAutoAnimation (const CAutoAnimation& autoAnimation);
|
||||
|
||||
void draw (CDrawContext*) override;
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
bool attached (CView* parent) override;
|
||||
bool removed (CView* parent) override;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CAutoAnimation Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
/** enabled drawing */
|
||||
virtual void openWindow ();
|
||||
/** disable drawing */
|
||||
virtual void closeWindow ();
|
||||
|
||||
/** the next sub bitmap should be displayed */
|
||||
virtual void nextPixmap ();
|
||||
/** the previous sub bitmap should be displayed */
|
||||
virtual void previousPixmap ();
|
||||
|
||||
bool isWindowOpened () const;
|
||||
|
||||
void setAnimationTime (uint32_t animationTime);
|
||||
uint32_t getAnimationTime () const;
|
||||
|
||||
//@}
|
||||
|
||||
void setBackground (CBitmap* background) override;
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CAutoAnimation (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background,
|
||||
const CPoint& offset);
|
||||
CAutoAnimation (const CRect& size, IControlListener* listener, int32_t tag, int32_t subPixmaps,
|
||||
CCoord heightOfOneImage, CBitmap* background,
|
||||
const CPoint& offset = CPoint (0, 0));
|
||||
void setNumSubPixmaps (int32_t numSubPixmaps) override
|
||||
{
|
||||
IMultiBitmapControl::setNumSubPixmaps (numSubPixmaps);
|
||||
invalid ();
|
||||
}
|
||||
|
||||
void setBitmapOffset (const CPoint& off);
|
||||
CPoint getBitmapOffset () const;
|
||||
#endif
|
||||
CLASS_METHODS(CAutoAnimation, CControl)
|
||||
protected:
|
||||
~CAutoAnimation () noexcept override = default;
|
||||
|
||||
void updateMinMaxFromBackground ();
|
||||
void startTimer ();
|
||||
|
||||
uint32_t animationFrameTime {0u};
|
||||
SharedPointer<CVSTGUITimer> timer;
|
||||
bool bWindowOpened {false};
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CPoint offset {};
|
||||
CCoord totalHeightOfBitmap {0};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,288 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ccontrol.h"
|
||||
#include "../cfont.h"
|
||||
#include "../ccolor.h"
|
||||
#include "../cbitmap.h"
|
||||
#include "../cgradient.h"
|
||||
#include "../cgraphicspath.h"
|
||||
#include "../cstring.h"
|
||||
#include "../cdrawmethods.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// COnOffButton Declaration
|
||||
//! @brief a button control with 2 states
|
||||
/// @ingroup controls
|
||||
//-----------------------------------------------------------------------------
|
||||
class COnOffButton : public CControl
|
||||
{
|
||||
public:
|
||||
COnOffButton (const CRect& size, IControlListener* listener = nullptr, int32_t tag = -1, CBitmap* background = nullptr, int32_t style = 0);
|
||||
COnOffButton (const COnOffButton& onOffButton);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name COnOffButton Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
virtual int32_t getStyle () const { return style; }
|
||||
virtual void setStyle (int32_t newStyle) { style = newStyle; }
|
||||
//@}
|
||||
|
||||
// overrides
|
||||
void draw (CDrawContext*) override;
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseCancel () override;
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
bool sizeToFit () override;
|
||||
|
||||
CLASS_METHODS(COnOffButton, CControl)
|
||||
protected:
|
||||
~COnOffButton () noexcept override = default;
|
||||
int32_t style;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CCheckBox Declaration
|
||||
/// @brief a check box control with a title and 3 states
|
||||
/// @ingroup controls
|
||||
/// @ingroup new_in_4_0
|
||||
//-----------------------------------------------------------------------------
|
||||
class CCheckBox : public CControl
|
||||
{
|
||||
public:
|
||||
CCheckBox (const CRect& size, IControlListener* listener = nullptr, int32_t tag = -1, UTF8StringPtr title = nullptr, CBitmap* bitmap = nullptr, int32_t style = 0);
|
||||
CCheckBox (const CCheckBox& checkbox);
|
||||
|
||||
enum Styles
|
||||
{
|
||||
/** automatically adjusts the width so that the label is completely visible */
|
||||
kAutoSizeToFit = 1 << 0,
|
||||
/** draws a crossbox instead of a checkmark if no bitmap is provided */
|
||||
kDrawCrossBox = 1 << 1,
|
||||
/** do not limit the box drawing to the cap height */
|
||||
kIgnoreCapHeightOnDraw = 1 << 2,
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CCheckBox Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
virtual void setTitle (const UTF8String& newTitle);
|
||||
const UTF8String& getTitle () const { return title; }
|
||||
|
||||
virtual void setFont (CFontRef newFont);
|
||||
const CFontRef getFont () const { return font; }
|
||||
|
||||
virtual void setFontColor (const CColor& newColor) { fontColor = newColor; invalid (); }
|
||||
const CColor& getFontColor () const { return fontColor; }
|
||||
|
||||
virtual void setBoxFrameColor (const CColor& newColor) { boxFrameColor = newColor; invalid (); }
|
||||
const CColor& getBoxFrameColor () const { return boxFrameColor; }
|
||||
virtual void setBoxFillColor (const CColor& newColor) { boxFillColor = newColor; invalid (); }
|
||||
const CColor& getBoxFillColor () const { return boxFillColor; }
|
||||
virtual void setCheckMarkColor (const CColor& newColor) { checkMarkColor = newColor; invalid (); }
|
||||
const CColor& getCheckMarkColor () const { return checkMarkColor; }
|
||||
|
||||
virtual int32_t getStyle () const { return style; }
|
||||
virtual void setStyle (int32_t newStyle);
|
||||
|
||||
CCoord getFrameWidth () const { return frameWidth; }
|
||||
virtual void setFrameWidth (CCoord width);
|
||||
CCoord getRoundRectRadius () const { return roundRectRadius; }
|
||||
virtual void setRoundRectRadius (CCoord radius);
|
||||
|
||||
//@}
|
||||
|
||||
// overrides
|
||||
void draw (CDrawContext* context) override;
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseCancel () override;
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
bool sizeToFit () override;
|
||||
void setBackground (CBitmap *background) override;
|
||||
bool getFocusPath (CGraphicsPath& outPath) override;
|
||||
|
||||
CLASS_METHODS(CCheckBox, CControl)
|
||||
protected:
|
||||
~CCheckBox () noexcept override = default;
|
||||
|
||||
UTF8String title;
|
||||
int32_t style;
|
||||
CColor fontColor;
|
||||
CColor boxFrameColor;
|
||||
CColor boxFillColor;
|
||||
CColor checkMarkColor;
|
||||
CCoord frameWidth {1};
|
||||
CCoord roundRectRadius {0};
|
||||
SharedPointer<CFontDesc> font;
|
||||
|
||||
private:
|
||||
float previousValue {0.f};
|
||||
bool hilight {false};
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CKickButton Declaration
|
||||
//!
|
||||
/// @ingroup controls uses_multi_frame_bitmaps
|
||||
//-----------------------------------------------------------------------------
|
||||
class CKickButton : public CControl,
|
||||
public MultiFrameBitmapView<CKickButton>
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
,
|
||||
public IMultiBitmapControl
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
CKickButton (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background);
|
||||
CKickButton (const CKickButton& kickButton);
|
||||
|
||||
void draw (CDrawContext*) override;
|
||||
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseCancel () override;
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
|
||||
bool sizeToFit () override;
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
void setNumSubPixmaps (int32_t numSubPixmaps) override { IMultiBitmapControl::setNumSubPixmaps (numSubPixmaps); invalid (); }
|
||||
CKickButton (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
CCoord heightOfOneImage, CBitmap* background,
|
||||
const CPoint& offset = CPoint (0, 0));
|
||||
#endif
|
||||
|
||||
CLASS_METHODS(CKickButton, CControl)
|
||||
protected:
|
||||
~CKickButton () noexcept override = default;
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CPoint offset {};
|
||||
#endif
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CTextButton Declaration
|
||||
/// @brief a button which renders without bitmaps
|
||||
/// @ingroup controls
|
||||
/// @ingroup new_in_4_0
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTextButton : public CControl
|
||||
{
|
||||
public:
|
||||
/** CTextButton style */
|
||||
enum Style
|
||||
{
|
||||
kKickStyle = 0,
|
||||
kOnOffStyle
|
||||
};
|
||||
|
||||
CTextButton (const CRect& size, IControlListener* listener = nullptr, int32_t tag = -1, UTF8StringPtr title = nullptr, Style = kKickStyle);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CTextButton Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
virtual void setTitle (const UTF8String& newTitle);
|
||||
const UTF8String& getTitle () const { return title; }
|
||||
|
||||
virtual void setFont (CFontRef newFont);
|
||||
CFontRef getFont () const { return font; }
|
||||
|
||||
virtual void setTextColor (const CColor& color);
|
||||
const CColor& getTextColor () const { return textColor; }
|
||||
virtual void setTextColorHighlighted (const CColor& color);
|
||||
const CColor& getTextColorHighlighted () const { return textColorHighlighted; }
|
||||
|
||||
virtual void setGradient (CGradient* gradient);
|
||||
CGradient* getGradient () const;
|
||||
virtual void setGradientHighlighted (CGradient* gradient);
|
||||
CGradient* getGradientHighlighted () const;
|
||||
|
||||
virtual void setFrameColor (const CColor& color);
|
||||
const CColor& getFrameColor () const { return frameColor; }
|
||||
virtual void setFrameColorHighlighted (const CColor& color);
|
||||
const CColor& getFrameColorHighlighted () const { return frameColorHighlighted; }
|
||||
|
||||
virtual void setFrameWidth (CCoord width);
|
||||
CCoord getFrameWidth () const { return frameWidth; }
|
||||
|
||||
virtual void setRoundRadius (CCoord radius);
|
||||
CCoord getRoundRadius () const { return roundRadius; }
|
||||
|
||||
virtual void setStyle (Style style);
|
||||
Style getStyle () const { return style; }
|
||||
|
||||
virtual void setIcon (CBitmap* bitmap);
|
||||
CBitmap* getIcon () const;
|
||||
|
||||
virtual void setIconHighlighted (CBitmap* bitmap);
|
||||
CBitmap* getIconHighlighted () const;
|
||||
|
||||
virtual void setIconPosition (CDrawMethods::IconPosition pos);
|
||||
CDrawMethods::IconPosition getIconPosition () const { return iconPosition; }
|
||||
|
||||
virtual void setTextMargin (CCoord margin);
|
||||
CCoord getTextMargin () const { return textMargin; }
|
||||
|
||||
virtual void setTextAlignment (CHoriTxtAlign hAlign);
|
||||
CHoriTxtAlign getTextAlignment () const { return horiTxtAlign; }
|
||||
//@}
|
||||
|
||||
// overrides
|
||||
void draw (CDrawContext* context) override;
|
||||
bool getFocusPath (CGraphicsPath& outPath) override;
|
||||
bool drawFocusOnTop () override;
|
||||
void setViewSize (const CRect& rect, bool invalid = true) override;
|
||||
bool removed (CView* parent) override;
|
||||
bool sizeToFit () override;
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseCancel () override;
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
|
||||
CLASS_METHODS_NOCOPY (CTextButton, CControl)
|
||||
protected:
|
||||
~CTextButton () noexcept override = default;
|
||||
|
||||
void invalidPath ();
|
||||
CGraphicsPath* getPath (CDrawContext* context, CCoord lineWidth);
|
||||
|
||||
SharedPointer<CFontDesc> font;
|
||||
SharedPointer<CGraphicsPath> _path;
|
||||
SharedPointer<CBitmap> icon;
|
||||
SharedPointer<CBitmap> iconHighlighted;
|
||||
SharedPointer<CGradient> gradient;
|
||||
SharedPointer<CGradient> gradientHighlighted;
|
||||
|
||||
CColor textColor;
|
||||
CColor frameColor;
|
||||
|
||||
CColor textColorHighlighted;
|
||||
CColor frameColorHighlighted;
|
||||
|
||||
CCoord frameWidth;
|
||||
CCoord roundRadius;
|
||||
CCoord textMargin;
|
||||
|
||||
CHoriTxtAlign horiTxtAlign;
|
||||
CDrawMethods::IconPosition iconPosition;
|
||||
Style style;
|
||||
UTF8String title;
|
||||
private:
|
||||
float fEntryState;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,593 @@
|
||||
// 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 "ccolorchooser.h"
|
||||
#include "cslider.h"
|
||||
#include "ctextlabel.h"
|
||||
#include "ccontrol.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include "../cframe.h"
|
||||
#include "../idatapackage.h"
|
||||
#include "../dragging.h"
|
||||
#include <string>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
/// @cond ignore
|
||||
namespace CColorChooserInternal {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class Slider : public CSlider
|
||||
{
|
||||
public:
|
||||
Slider (const CRect& size, IControlListener* listener = nullptr, int32_t tag = -1)
|
||||
: CSlider (size, listener, tag, 0, 0, nullptr, nullptr)
|
||||
{
|
||||
if (size.getWidth () > size.getHeight ())
|
||||
setHandleSizePrivate (size.getHeight (), size.getHeight ());
|
||||
else
|
||||
setHandleSizePrivate (size.getWidth (), size.getWidth ());
|
||||
const CRect& r (size);
|
||||
setViewSize (r, false);
|
||||
setWheelInc (10.f/255.f);
|
||||
}
|
||||
|
||||
void draw (CDrawContext* context) override
|
||||
{
|
||||
CColor handleFillColor (kWhiteCColor);
|
||||
CColor handleFrameColor (kBlackCColor);
|
||||
CColor backgroundFillColor (kGreyCColor);
|
||||
CColor backgroundFrameColor (kBlackCColor);
|
||||
CColor bandColor (kTransparentCColor);
|
||||
|
||||
CCoord backgroundFrameWidth = 1;
|
||||
CCoord handleFrameWidth = 1;
|
||||
|
||||
auto controlSize = getControlSizePrivate ();
|
||||
auto sliderSize = getHandleSizePrivate ();
|
||||
|
||||
CRect backgroundRect;
|
||||
backgroundRect.setSize (controlSize);
|
||||
backgroundRect.offset (getViewSize ().left, getViewSize ().top);
|
||||
context->setDrawMode (kAntiAliasing);
|
||||
context->setFillColor (backgroundFillColor);
|
||||
context->setFrameColor (backgroundFrameColor);
|
||||
context->setLineWidth (backgroundFrameWidth);
|
||||
context->setLineStyle (kLineSolid);
|
||||
context->drawRect (backgroundRect, kDrawFilledAndStroked);
|
||||
|
||||
if (getStyle () & kHorizontal)
|
||||
{
|
||||
backgroundRect.left += getOffsetHandle ().x + sliderSize.x / 2;
|
||||
backgroundRect.right -= getOffsetHandle ().x + sliderSize.x / 2;
|
||||
backgroundRect.top += controlSize.y / 2 - 2;
|
||||
backgroundRect.bottom -= controlSize.y / 2 - 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
backgroundRect.left += controlSize.x / 2 - 2;
|
||||
backgroundRect.right -= controlSize.x / 2 - 2;
|
||||
backgroundRect.top += getOffsetHandle ().y + sliderSize.y / 2;
|
||||
backgroundRect.bottom -= getOffsetHandle ().y + sliderSize.y / 2;
|
||||
}
|
||||
context->setFillColor (bandColor);
|
||||
context->drawRect (backgroundRect, kDrawFilled);
|
||||
|
||||
// calc new coords of slider
|
||||
CRect rectNew = calculateHandleRect (getValueNormalized ());
|
||||
|
||||
context->setFillColor (handleFillColor);
|
||||
context->setFrameColor (handleFrameColor);
|
||||
context->setLineWidth (handleFrameWidth);
|
||||
context->drawRect (rectNew, kDrawFilledAndStroked);
|
||||
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class ColorView : public CControl, public IDropTarget
|
||||
{
|
||||
public:
|
||||
ColorView (const CRect& r, const CColor& initialColor, IControlListener* listener = nullptr, int32_t tag = -1, bool checkerBoardBack = true, const CColor& checkerBoardColor1 = kWhiteCColor, const CColor& checkerBoardColor2 = kBlackCColor)
|
||||
: CControl (r, listener, tag)
|
||||
, color (initialColor)
|
||||
, checkerBoardColor1 (checkerBoardColor1)
|
||||
, checkerBoardColor2 (checkerBoardColor2)
|
||||
, checkerBoardBack (checkerBoardBack)
|
||||
{
|
||||
}
|
||||
|
||||
void draw (CDrawContext* context) override
|
||||
{
|
||||
context->setDrawMode (kAliasing);
|
||||
if (checkerBoardBack && color.alpha != 255)
|
||||
{
|
||||
context->setFillColor (checkerBoardColor1);
|
||||
context->drawRect (getViewSize (), kDrawFilled);
|
||||
context->setFillColor (checkerBoardColor2);
|
||||
CRect r (getViewSize ().left, getViewSize ().top, getViewSize ().left + 5, getViewSize ().top + 5);
|
||||
for (int32_t x = 0; x < getViewSize ().getWidth (); x+=5)
|
||||
{
|
||||
r.left = getViewSize ().left + x;
|
||||
r.top = (x % 2) ? getViewSize ().top : getViewSize ().top + 5;
|
||||
r.right = r.left + 5;
|
||||
r.bottom = r.top + 5;
|
||||
for (int32_t y = 0; y < getViewSize ().getHeight (); y+=10)
|
||||
{
|
||||
context->drawRect (r, kDrawFilled);
|
||||
r.offset (0, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
context->setLineWidth (1);
|
||||
context->setFillColor (color);
|
||||
context->setFrameColor (kBlackCColor);
|
||||
context->drawRect (getViewSize (), kDrawFilledAndStroked);
|
||||
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
const CColor& getColor () const { return color; }
|
||||
|
||||
void setColor (const CColor& newColor)
|
||||
{
|
||||
color = newColor;
|
||||
}
|
||||
|
||||
// we accept strings which look like : '#ff3355' (rgb) and '#ff3355bb' (rgba)
|
||||
static bool dragContainerHasColor (IDataPackage* drag, CColor* color)
|
||||
{
|
||||
for (auto item : drag)
|
||||
{
|
||||
if (item.type != IDataPackage::kText)
|
||||
continue;
|
||||
std::string colorString (static_cast<const char*> (item.data), item.dataSize);
|
||||
if (colorString.length () == 7)
|
||||
{
|
||||
if (colorString[0] == '#')
|
||||
{
|
||||
if (color)
|
||||
{
|
||||
std::string rv (colorString.substr (1, 2));
|
||||
std::string gv (colorString.substr (3, 2));
|
||||
std::string bv (colorString.substr (5, 2));
|
||||
color->red = (uint8_t)strtol (rv.c_str (), nullptr, 16);
|
||||
color->green = (uint8_t)strtol (gv.c_str (), nullptr, 16);
|
||||
color->blue = (uint8_t)strtol (bv.c_str (), nullptr, 16);
|
||||
color->alpha = 255;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (colorString.length () == 9)
|
||||
{
|
||||
if (colorString[0] == '#')
|
||||
{
|
||||
if (color)
|
||||
{
|
||||
std::string rv (colorString.substr (1, 2));
|
||||
std::string gv (colorString.substr (3, 2));
|
||||
std::string bv (colorString.substr (5, 2));
|
||||
std::string av (colorString.substr (7, 2));
|
||||
color->red = (uint8_t)strtol (rv.c_str (), nullptr, 16);
|
||||
color->green = (uint8_t)strtol (gv.c_str (), nullptr, 16);
|
||||
color->blue = (uint8_t)strtol (bv.c_str (), nullptr, 16);
|
||||
color->alpha = (uint8_t)strtol (av.c_str (), nullptr, 16);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SharedPointer<IDropTarget> getDropTarget () override { return this; }
|
||||
|
||||
bool onDrop (DragEventData data) override
|
||||
{
|
||||
CColor dragColor;
|
||||
if (dragContainerHasColor (data.drag, &dragColor))
|
||||
{
|
||||
setColor (dragColor);
|
||||
valueChanged ();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
DragOperation onDragEnter (DragEventData data) override
|
||||
{
|
||||
dragOperation =
|
||||
dragContainerHasColor (data.drag, nullptr) ? DragOperation::Copy : DragOperation::None;
|
||||
return dragOperation;
|
||||
}
|
||||
|
||||
DragOperation onDragMove (DragEventData data) override
|
||||
{
|
||||
return dragOperation;
|
||||
}
|
||||
|
||||
void onDragLeave (DragEventData data) override
|
||||
{
|
||||
dragOperation = DragOperation::None;
|
||||
}
|
||||
|
||||
CLASS_METHODS(ColorView, CControl)
|
||||
protected:
|
||||
DragOperation dragOperation {DragOperation::None};
|
||||
CColor color;
|
||||
CColor checkerBoardColor1;
|
||||
CColor checkerBoardColor2;
|
||||
bool checkerBoardBack;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void setupParamDisplay (CParamDisplay* display, const CColorChooserUISettings& settings)
|
||||
{
|
||||
display->setFont (settings.font);
|
||||
display->setFontColor (settings.fontColor);
|
||||
display->setTransparency (true);
|
||||
}
|
||||
|
||||
} // CColorChooserInternal
|
||||
|
||||
/// @endcond
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CColorChooser::convertNormalizedToString (float value, char string[256], CParamDisplay::ValueToStringUserData* userData)
|
||||
{
|
||||
snprintf (string, 255, "%.3f", value);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CColorChooser::convertColorValueToString (float value, char string[256], CParamDisplay::ValueToStringUserData* userData)
|
||||
{
|
||||
snprintf (string, 255, "%d", (int32_t)(value * 255.f));
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CColorChooser::convertAngleToString (float value, char string[256], CParamDisplay::ValueToStringUserData* userData)
|
||||
{
|
||||
snprintf (string, 255, "%d%s", (int32_t)(value * 359.f), kDegreeSymbol);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CColorChooser::convertNormalized (UTF8StringPtr string, float& output, CTextEdit::StringToValueUserData* userData)
|
||||
{
|
||||
output = UTF8StringView (string).toFloat ();
|
||||
if (output < 0.f)
|
||||
output = 0.f;
|
||||
else if (output > 1.f)
|
||||
output = 1.f;
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CColorChooser::convertColorValue (UTF8StringPtr string, float& output, CTextEdit::StringToValueUserData* userData)
|
||||
{
|
||||
output = UTF8StringView (string).toFloat ();
|
||||
if (output < 0.f)
|
||||
output = 0.f;
|
||||
else if (output > 255.f)
|
||||
output = 255.f;
|
||||
output /= 255.f;
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CColorChooser::convertAngle (UTF8StringPtr string, float& output, CTextEdit::StringToValueUserData* userData)
|
||||
{
|
||||
output = UTF8StringView (string).toFloat ();
|
||||
if (output < 0.f)
|
||||
output = 0.f;
|
||||
else if (output > 359.f)
|
||||
output = 359.f;
|
||||
output /= 359.f;
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CColorChooser::CColorChooser (IColorChooserDelegate* delegate, const CColor& initialColor, const CColorChooserUISettings& settings)
|
||||
: CViewContainer (CRect (0, 0, 0, 0))
|
||||
, delegate (delegate)
|
||||
, color (initialColor)
|
||||
, redSlider (nullptr)
|
||||
, greenSlider (nullptr)
|
||||
, blueSlider (nullptr)
|
||||
, hueSlider (nullptr)
|
||||
, saturationSlider (nullptr)
|
||||
, brightnessSlider (nullptr)
|
||||
, alphaSlider (nullptr)
|
||||
, colorView (nullptr)
|
||||
{
|
||||
setTransparency (true);
|
||||
setAutosizeFlags (kAutosizeAll);
|
||||
|
||||
const CCoord controlHeight = settings.font->getSize () + 2;
|
||||
const CCoord controlWidth = 150;
|
||||
const CCoord editWidth = 40;
|
||||
const CCoord labelWidth = 40;
|
||||
const CCoord xMargin = settings.margin.x;
|
||||
const CCoord yMargin = settings.margin.y;
|
||||
|
||||
colorView = new CColorChooserInternal::ColorView (CRect (1, 1, labelWidth + xMargin + controlWidth + xMargin + editWidth, 100), initialColor, this, kColorTag, settings.checkerBoardBack, settings.checkerBoardColor1, settings.checkerBoardColor2);
|
||||
colorView->setAutosizeFlags (kAutosizeAll);
|
||||
addView (colorView);
|
||||
CRect r (colorView->getViewSize ());
|
||||
r.offset (labelWidth + xMargin, r.bottom + yMargin);
|
||||
r.setWidth (controlWidth);
|
||||
r.setHeight (controlHeight);
|
||||
|
||||
redSlider = new CColorChooserInternal::Slider (r, this, kRedTag);
|
||||
redSlider->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeBottom);
|
||||
addView (redSlider);
|
||||
|
||||
r.offset (0, yMargin + controlHeight);
|
||||
greenSlider = new CColorChooserInternal::Slider (r, this, kGreenTag);
|
||||
greenSlider->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeBottom);
|
||||
addView (greenSlider);
|
||||
|
||||
r.offset (0, yMargin + controlHeight);
|
||||
blueSlider = new CColorChooserInternal::Slider (r, this, kBlueTag);
|
||||
blueSlider->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeBottom);
|
||||
addView (blueSlider);
|
||||
|
||||
r.offset (0, yMargin + yMargin + controlHeight);
|
||||
hueSlider = new CColorChooserInternal::Slider (r, this, kHueTag);
|
||||
hueSlider->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeBottom);
|
||||
addView (hueSlider);
|
||||
|
||||
r.offset (0, yMargin + controlHeight);
|
||||
saturationSlider = new CColorChooserInternal::Slider (r, this, kSaturationTag);
|
||||
saturationSlider->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeBottom);
|
||||
addView (saturationSlider);
|
||||
|
||||
r.offset (0, yMargin + controlHeight);
|
||||
brightnessSlider = new CColorChooserInternal::Slider (r, this, kBrightnessTag);
|
||||
brightnessSlider->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeBottom);
|
||||
addView (brightnessSlider);
|
||||
|
||||
r.offset (0, yMargin + yMargin + controlHeight);
|
||||
alphaSlider = new CColorChooserInternal::Slider (r, this, kAlphaTag);
|
||||
alphaSlider->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeBottom);
|
||||
addView (alphaSlider);
|
||||
|
||||
CRect newSize (getViewSize ());
|
||||
|
||||
newSize.bottom = r.bottom+1;
|
||||
newSize.right = colorView->getViewSize ().right+2;
|
||||
|
||||
setAutosizingEnabled (false);
|
||||
setViewSize (newSize);
|
||||
setMouseableArea (newSize);
|
||||
setAutosizingEnabled (true);
|
||||
|
||||
r = colorView->getViewSize ();
|
||||
r.offset (0, r.bottom + yMargin);
|
||||
r.setWidth (labelWidth);
|
||||
r.setHeight (controlHeight);
|
||||
auto* label = new CTextLabel (r, "Red");
|
||||
CColorChooserInternal::setupParamDisplay (label, settings);
|
||||
label->setAutosizeFlags (kAutosizeLeft|kAutosizeBottom);
|
||||
addView (label);
|
||||
|
||||
r.offset (0, yMargin + controlHeight);
|
||||
label = new CTextLabel (r, "Green");
|
||||
CColorChooserInternal::setupParamDisplay (label, settings);
|
||||
label->setAutosizeFlags (kAutosizeLeft|kAutosizeBottom);
|
||||
addView (label);
|
||||
|
||||
r.offset (0, yMargin + controlHeight);
|
||||
label = new CTextLabel (r, "Blue");
|
||||
CColorChooserInternal::setupParamDisplay (label, settings);
|
||||
label->setAutosizeFlags (kAutosizeLeft|kAutosizeBottom);
|
||||
addView (label);
|
||||
|
||||
r.offset (0, yMargin + yMargin + controlHeight);
|
||||
label = new CTextLabel (r, "Hue");
|
||||
CColorChooserInternal::setupParamDisplay (label, settings);
|
||||
label->setAutosizeFlags (kAutosizeLeft|kAutosizeBottom);
|
||||
addView (label);
|
||||
|
||||
r.offset (0, yMargin + controlHeight);
|
||||
label = new CTextLabel (r, "Sat");
|
||||
CColorChooserInternal::setupParamDisplay (label, settings);
|
||||
label->setAutosizeFlags (kAutosizeLeft|kAutosizeBottom);
|
||||
addView (label);
|
||||
|
||||
r.offset (0, yMargin + controlHeight);
|
||||
label = new CTextLabel (r, "Value");
|
||||
CColorChooserInternal::setupParamDisplay (label, settings);
|
||||
label->setAutosizeFlags (kAutosizeLeft|kAutosizeBottom);
|
||||
addView (label);
|
||||
|
||||
r.offset (0, yMargin + yMargin + controlHeight);
|
||||
label = new CTextLabel (r, "Alpha");
|
||||
CColorChooserInternal::setupParamDisplay (label, settings);
|
||||
label->setAutosizeFlags (kAutosizeLeft|kAutosizeBottom);
|
||||
addView (label);
|
||||
|
||||
r = colorView->getViewSize ();
|
||||
r.offset (labelWidth + xMargin + controlWidth + xMargin, r.bottom + yMargin);
|
||||
r.setWidth (editWidth);
|
||||
r.setHeight (controlHeight);
|
||||
editFields[0] = new CTextEdit (r, this, kRedTag, nullptr);
|
||||
CColorChooserInternal::setupParamDisplay (editFields[0], settings);
|
||||
editFields[0]->setAutosizeFlags (kAutosizeRight|kAutosizeBottom);
|
||||
editFields[0]->setStringToValueFunction (convertColorValue);
|
||||
editFields[0]->setValueToStringFunction (convertColorValueToString);
|
||||
addView (editFields[0]);
|
||||
|
||||
r.offset (0, yMargin + controlHeight);
|
||||
editFields[1] = new CTextEdit (r, this, kGreenTag, nullptr);
|
||||
CColorChooserInternal::setupParamDisplay (editFields[1], settings);
|
||||
editFields[1]->setAutosizeFlags (kAutosizeRight|kAutosizeBottom);
|
||||
editFields[1]->setStringToValueFunction (convertColorValue);
|
||||
editFields[1]->setValueToStringFunction (convertColorValueToString);
|
||||
addView (editFields[1]);
|
||||
|
||||
r.offset (0, yMargin + controlHeight);
|
||||
editFields[2] = new CTextEdit (r, this, kBlueTag, nullptr);
|
||||
CColorChooserInternal::setupParamDisplay (editFields[2], settings);
|
||||
editFields[2]->setAutosizeFlags (kAutosizeRight|kAutosizeBottom);
|
||||
editFields[2]->setStringToValueFunction (convertColorValue);
|
||||
editFields[2]->setValueToStringFunction (convertColorValueToString);
|
||||
addView (editFields[2]);
|
||||
|
||||
r.offset (0, yMargin + yMargin + controlHeight);
|
||||
editFields[3] = new CTextEdit (r, this, kHueTag, nullptr);
|
||||
CColorChooserInternal::setupParamDisplay (editFields[3], settings);
|
||||
editFields[3]->setAutosizeFlags (kAutosizeRight|kAutosizeBottom);
|
||||
editFields[3]->setStringToValueFunction (convertColorValue);
|
||||
editFields[3]->setValueToStringFunction (convertColorValueToString);
|
||||
addView (editFields[3]);
|
||||
|
||||
r.offset (0, yMargin + controlHeight);
|
||||
editFields[4] = new CTextEdit (r, this, kSaturationTag, nullptr);
|
||||
CColorChooserInternal::setupParamDisplay (editFields[4], settings);
|
||||
editFields[4]->setAutosizeFlags (kAutosizeRight|kAutosizeBottom);
|
||||
editFields[4]->setStringToValueFunction (convertColorValue);
|
||||
editFields[4]->setValueToStringFunction (convertColorValueToString);
|
||||
addView (editFields[4]);
|
||||
|
||||
r.offset (0, yMargin + controlHeight);
|
||||
editFields[5] = new CTextEdit (r, this, kBrightnessTag, nullptr);
|
||||
CColorChooserInternal::setupParamDisplay (editFields[5], settings);
|
||||
editFields[5]->setAutosizeFlags (kAutosizeRight|kAutosizeBottom);
|
||||
editFields[5]->setStringToValueFunction (convertColorValue);
|
||||
editFields[5]->setValueToStringFunction (convertColorValueToString);
|
||||
addView (editFields[5]);
|
||||
|
||||
r.offset (0, yMargin + yMargin + controlHeight);
|
||||
editFields[6] = new CTextEdit (r, this, kAlphaTag, nullptr);
|
||||
CColorChooserInternal::setupParamDisplay (editFields[6], settings);
|
||||
editFields[6]->setAutosizeFlags (kAutosizeRight|kAutosizeBottom);
|
||||
editFields[6]->setStringToValueFunction (convertColorValue);
|
||||
editFields[6]->setValueToStringFunction (convertColorValueToString);
|
||||
addView (editFields[6]);
|
||||
|
||||
updateState ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CColorChooser::valueChanged (CControl* control)
|
||||
{
|
||||
switch (control->getTag ())
|
||||
{
|
||||
case kRedTag:
|
||||
{
|
||||
color.setNormRed (control->getValue ());
|
||||
break;
|
||||
}
|
||||
case kGreenTag:
|
||||
{
|
||||
color.setNormGreen (control->getValue ());
|
||||
break;
|
||||
}
|
||||
case kBlueTag:
|
||||
{
|
||||
color.setNormBlue (control->getValue ());
|
||||
break;
|
||||
}
|
||||
case kAlphaTag:
|
||||
{
|
||||
color.setNormAlpha (control->getValue ());
|
||||
break;
|
||||
}
|
||||
case kHueTag:
|
||||
{
|
||||
double hue, saturation, value;
|
||||
color.toHSV (hue, saturation, value);
|
||||
hue = control->getValue () * 359.;
|
||||
color.fromHSV (hue, saturation, value);
|
||||
break;
|
||||
}
|
||||
case kSaturationTag:
|
||||
{
|
||||
double hue, saturation, value;
|
||||
color.toHSV (hue, saturation, value);
|
||||
saturation = control->getValue ();
|
||||
color.fromHSV (hue, saturation, value);
|
||||
break;
|
||||
}
|
||||
case kBrightnessTag:
|
||||
{
|
||||
double hue, saturation, value;
|
||||
color.toHSV (hue, saturation, value);
|
||||
value = control->getValue ();
|
||||
color.fromHSV (hue, saturation, value);
|
||||
break;
|
||||
}
|
||||
case kColorTag:
|
||||
{
|
||||
color = colorView->getColor ();
|
||||
}
|
||||
}
|
||||
updateState ();
|
||||
if (delegate)
|
||||
delegate->colorChanged (this, color);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CColorChooser::controlBeginEdit (CControl* pControl)
|
||||
{
|
||||
if (delegate)
|
||||
delegate->onBeginColorChange (this);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CColorChooser::controlEndEdit (CControl* pControl)
|
||||
{
|
||||
if (delegate)
|
||||
delegate->onEndColorChange (this);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CColorChooser::setColor (const CColor& newColor)
|
||||
{
|
||||
color = newColor;
|
||||
updateState ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CColorChooser::updateState ()
|
||||
{
|
||||
double hue, saturation, value;
|
||||
color.toHSV (hue, saturation, value);
|
||||
redSlider->setValue (color.normRed<float> ());
|
||||
greenSlider->setValue (color.normGreen<float> ());
|
||||
blueSlider->setValue (color.normBlue<float> ());
|
||||
alphaSlider->setValue (color.normAlpha<float> ());
|
||||
hueSlider->setValue ((float)(hue / 359.));
|
||||
saturationSlider->setValue ((float)saturation);
|
||||
brightnessSlider->setValue ((float)value);
|
||||
colorView->setColor (color);
|
||||
|
||||
editFields[0]->setValue (redSlider->getValue ());
|
||||
editFields[1]->setValue (greenSlider->getValue ());
|
||||
editFields[2]->setValue (blueSlider->getValue ());
|
||||
editFields[3]->setValue (hueSlider->getValue ());
|
||||
editFields[4]->setValue (saturationSlider->getValue ());
|
||||
editFields[5]->setValue (brightnessSlider->getValue ());
|
||||
editFields[6]->setValue (alphaSlider->getValue ());
|
||||
|
||||
for (int32_t i = 0; i < 7; i++)
|
||||
editFields[i]->invalid ();
|
||||
|
||||
redSlider->invalid ();
|
||||
greenSlider->invalid ();
|
||||
blueSlider->invalid ();
|
||||
alphaSlider->invalid ();
|
||||
hueSlider->invalid ();
|
||||
saturationSlider->invalid ();
|
||||
brightnessSlider->invalid ();
|
||||
colorView->invalid ();
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,95 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
#include "../cviewcontainer.h"
|
||||
#include "icontrollistener.h"
|
||||
#include "ctextedit.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
/// @cond ignore
|
||||
namespace CColorChooserInternal {
|
||||
class ColorView;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/// @ingroup new_in_4_0
|
||||
//-----------------------------------------------------------------------------
|
||||
class IColorChooserDelegate
|
||||
{
|
||||
public:
|
||||
virtual void colorChanged (CColorChooser* chooser, const CColor& color) = 0;
|
||||
virtual void onBeginColorChange (CColorChooser* chooser) = 0;
|
||||
virtual void onEndColorChange (CColorChooser* chooser) = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct CColorChooserUISettings
|
||||
{
|
||||
CFontRef font {kNormalFont};
|
||||
CColor fontColor {kWhiteCColor};
|
||||
CColor checkerBoardColor1 {kWhiteCColor};
|
||||
CColor checkerBoardColor2 {kBlackCColor};
|
||||
CPoint margin {5, 5};
|
||||
|
||||
bool checkerBoardBack {true};
|
||||
};
|
||||
|
||||
/// @ingroup new_in_4_0
|
||||
//-----------------------------------------------------------------------------
|
||||
class CColorChooser : public CViewContainer, public IControlListener
|
||||
{
|
||||
public:
|
||||
CColorChooser (IColorChooserDelegate* delegate = nullptr, const CColor& initialColor = kTransparentCColor, const CColorChooserUISettings& settings = CColorChooserUISettings ());
|
||||
~CColorChooser () noexcept override = default;
|
||||
|
||||
void setColor (const CColor& newColor);
|
||||
//-----------------------------------------------------------------------------
|
||||
protected:
|
||||
void valueChanged (CControl* pControl) override;
|
||||
void controlBeginEdit (CControl* pControl) override;
|
||||
void controlEndEdit (CControl* pControl) override;
|
||||
void updateState ();
|
||||
|
||||
/// @cond ignore
|
||||
|
||||
IColorChooserDelegate* delegate;
|
||||
CColor color;
|
||||
|
||||
CSlider* redSlider;
|
||||
CSlider* greenSlider;
|
||||
CSlider* blueSlider;
|
||||
CSlider* hueSlider;
|
||||
CSlider* saturationSlider;
|
||||
CSlider* brightnessSlider;
|
||||
CSlider* alphaSlider;
|
||||
CTextEdit* editFields[8];
|
||||
CColorChooserInternal::ColorView* colorView;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
enum {
|
||||
kRedTag = 10000,
|
||||
kGreenTag,
|
||||
kBlueTag,
|
||||
kHueTag,
|
||||
kSaturationTag,
|
||||
kBrightnessTag,
|
||||
kAlphaTag,
|
||||
kColorTag
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static bool convertNormalized (UTF8StringPtr string, float& output, CTextEdit::StringToValueUserData* userData);
|
||||
static bool convertColorValue (UTF8StringPtr string, float& output, CTextEdit::StringToValueUserData* userData);
|
||||
static bool convertAngle (UTF8StringPtr string, float& output, CTextEdit::StringToValueUserData* userData);
|
||||
static bool convertNormalizedToString (float value, char string[256], CParamDisplay::ValueToStringUserData* userData);
|
||||
static bool convertColorValueToString (float value, char string[256], CParamDisplay::ValueToStringUserData* userData);
|
||||
static bool convertAngleToString (float value, char string[256], CParamDisplay::ValueToStringUserData* userData);
|
||||
/// @endcond
|
||||
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,390 @@
|
||||
// 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 "ccontrol.h"
|
||||
#include "icontrollistener.h"
|
||||
#include "../algorithm.h"
|
||||
#include "../events.h"
|
||||
#include "../cframe.h"
|
||||
#include "../cgraphicspath.h"
|
||||
#include "../cvstguitimer.h"
|
||||
#include "../dispatchlist.h"
|
||||
#include "../iviewlistener.h"
|
||||
#include <cassert>
|
||||
|
||||
#define VSTGUI_CCONTROL_LOG_EDITING 0 //DEBUG
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct CControl::Impl : ViewEventListenerAdapter
|
||||
{
|
||||
using SubListenerDispatcher = DispatchList<IControlListener*>;
|
||||
|
||||
SubListenerDispatcher subListeners;
|
||||
float oldValue {1};
|
||||
float defaultValue {0.5};
|
||||
float vmin {0};
|
||||
float vmax {1.f};
|
||||
float wheelInc {0.1f};
|
||||
int32_t editing {0};
|
||||
|
||||
void viewOnEvent (CView* view, Event& event) override
|
||||
{
|
||||
if (event.type != EventType::MouseDown)
|
||||
return;
|
||||
auto control = static_cast<CControl*> (view);
|
||||
auto& mouseDownEvent = castMouseDownEvent (event);
|
||||
if (CControl::CheckDefaultValueEventFunc (control, mouseDownEvent))
|
||||
{
|
||||
auto defValue = control->getDefaultValue ();
|
||||
if (defValue != control->getValue ())
|
||||
{
|
||||
control->beginEdit ();
|
||||
control->setValue (defValue);
|
||||
control->valueChanged ();
|
||||
control->endEdit ();
|
||||
control->setDirty ();
|
||||
}
|
||||
mouseDownEvent.consumed = true;
|
||||
mouseDownEvent.ignoreFollowUpMoveAndUpEvents (true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CControl
|
||||
//------------------------------------------------------------------------
|
||||
/*! @class CControl
|
||||
This object manages the tag identification and the value of a control object.
|
||||
*/
|
||||
CControl::CControl (const CRect& size, IControlListener* listener, int32_t tag, CBitmap *pBackground)
|
||||
: CView (size)
|
||||
, listener (listener)
|
||||
, tag (tag)
|
||||
, value (0)
|
||||
{
|
||||
impl = std::unique_ptr<Impl> (new Impl);
|
||||
setTransparency (false);
|
||||
setMouseEnabled (true);
|
||||
setBackground (pBackground);
|
||||
registerViewEventListener (impl.get ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CControl::CControl (const CControl& c)
|
||||
: CView (c)
|
||||
, listener (c.listener)
|
||||
, tag (c.tag)
|
||||
, value (c.value)
|
||||
{
|
||||
impl = std::unique_ptr<Impl> (new Impl);
|
||||
impl->oldValue = c.impl->oldValue;
|
||||
impl->defaultValue = c.impl->defaultValue;
|
||||
impl->vmin = c.impl->vmin;
|
||||
impl->vmax = c.impl->vmax;
|
||||
impl->wheelInc = c.impl->wheelInc;
|
||||
registerViewEventListener (impl.get ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CControl::~CControl () noexcept
|
||||
{
|
||||
unregisterViewEventListener (impl.get ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CControl::registerControlListener (IControlListener* subListener)
|
||||
{
|
||||
vstgui_assert (listener != subListener, "the subListener is already the main listener");
|
||||
impl->subListeners.add (subListener);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CControl::unregisterControlListener (IControlListener* subListener)
|
||||
{
|
||||
impl->subListeners.remove (subListener);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CControl::setWheelInc (float val)
|
||||
{
|
||||
impl->wheelInc = val;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
float CControl::getWheelInc () const
|
||||
{
|
||||
return impl->wheelInc;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CControl::setMin (float val)
|
||||
{
|
||||
impl->vmin = val;
|
||||
bounceValue ();
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
float CControl::getMin () const
|
||||
{
|
||||
return impl->vmin;
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
void CControl::setMax (float val)
|
||||
{
|
||||
impl->vmax = val;
|
||||
bounceValue ();
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
float CControl::getMax () const
|
||||
{
|
||||
return impl->vmax;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CControl::setOldValue (float val)
|
||||
{
|
||||
impl->oldValue = val;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
float CControl::getOldValue (void) const
|
||||
{
|
||||
return impl->oldValue;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CControl::setDefaultValue (float val)
|
||||
{
|
||||
impl->defaultValue = val;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
float CControl::getDefaultValue (void) const
|
||||
{
|
||||
return impl->defaultValue;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CControl::setTag (int32_t val)
|
||||
{
|
||||
if (listener)
|
||||
listener->controlTagWillChange (this);
|
||||
tag = val;
|
||||
if (listener)
|
||||
listener->controlTagDidChange (this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CControl::isEditing () const
|
||||
{
|
||||
return impl->editing > 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CControl::beginEdit ()
|
||||
{
|
||||
// begin of edit parameter
|
||||
impl->editing++;
|
||||
if (impl->editing == 1)
|
||||
{
|
||||
if (listener)
|
||||
listener->controlBeginEdit (this);
|
||||
impl->subListeners.forEach ([this] (IControlListener* l) { l->controlBeginEdit (this); });
|
||||
if (getFrame ())
|
||||
getFrame ()->beginEdit (tag);
|
||||
}
|
||||
#if VSTGUI_CCONTROL_LOG_EDITING
|
||||
DebugPrint("beginEdit [%d] - %d\n", tag, impl->editing);
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CControl::endEdit ()
|
||||
{
|
||||
if (!isEditing ())
|
||||
return;
|
||||
--impl->editing;
|
||||
if (impl->editing == 0)
|
||||
{
|
||||
if (getFrame ())
|
||||
getFrame ()->endEdit (tag);
|
||||
if (listener)
|
||||
listener->controlEndEdit (this);
|
||||
impl->subListeners.forEach ([this] (IControlListener* l) { l->controlEndEdit (this); });
|
||||
}
|
||||
#if VSTGUI_CCONTROL_LOG_EDITING
|
||||
DebugPrint("endEdit [%d] - %d\n", tag, impl->editing);
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CControl::setValue (float val) { value = clamp (val, getMin (), getMax ()); }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CControl::setValueNormalized (float val)
|
||||
{
|
||||
if (getRange () == 0.f)
|
||||
{
|
||||
value = getMin ();
|
||||
return;
|
||||
}
|
||||
val = clampNorm (val);
|
||||
setValue (normalizedToPlain (val, getMin (), getMax ()));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
float CControl::getValueNormalized () const
|
||||
{
|
||||
auto range = getRange ();
|
||||
if (range == 0.f)
|
||||
return 0.f;
|
||||
return plainToNormalized<float> (value, getMin (), getMax ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CControl::valueChanged ()
|
||||
{
|
||||
if (listener)
|
||||
listener->valueChanged (this);
|
||||
impl->subListeners.forEach ([this] (IControlListener* l) { l->valueChanged (this); });
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CControl::isDirty () const
|
||||
{
|
||||
if (getOldValue () != value || CView::isDirty ())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CControl::setDirty (bool val)
|
||||
{
|
||||
CView::setDirty (val);
|
||||
if (val)
|
||||
{
|
||||
if (value != -1.f)
|
||||
setOldValue (-1.f);
|
||||
else
|
||||
setOldValue (0.f);
|
||||
}
|
||||
else
|
||||
setOldValue (value);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CControl::bounceValue () { value = clamp (value, getMin (), getMax ()); }
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
//------------------------------------------------------------------------
|
||||
CControl::CheckDefaultValueFuncT CControl::CheckDefaultValueFunc = [] (CControl*,
|
||||
CButtonState button) {
|
||||
#if TARGET_OS_IPHONE
|
||||
return button.isDoubleClick ();
|
||||
#else
|
||||
return (button.isLeftButton () && button.getModifierState () == kDefaultValueModifier);
|
||||
#endif // TARGET_OS_IPHONE
|
||||
};
|
||||
|
||||
#endif // VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CControl::CheckDefaultValueEventFuncT CControl::CheckDefaultValueEventFunc =
|
||||
[] (CControl* c, MouseDownEvent& event) {
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
if (event.buttonState.isLeft ())
|
||||
{
|
||||
return CheckDefaultValueFunc (c, buttonStateFromMouseEvent (event));
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
#if TARGET_OS_IPHONE
|
||||
return event.buttonState.isLeft () && event.clickCount == 2;
|
||||
#else
|
||||
return event.buttonState.isLeft () && event.modifiers.is (ModifierKey::Control);
|
||||
#endif // TARGET_OS_IPHONE
|
||||
#endif // VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CControl::drawFocusOnTop ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CControl::getFocusPath (CGraphicsPath& outPath)
|
||||
{
|
||||
if (wantsFocus ())
|
||||
{
|
||||
CCoord focusWidth = getFrame ()->getFocusWidth ();
|
||||
CRect r (getVisibleViewSize ());
|
||||
if (!r.isEmpty ())
|
||||
{
|
||||
outPath.addRect (r);
|
||||
r.extend (focusWidth, focusWidth);
|
||||
outPath.addRect (r);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
//-----------------------------------------------------------------------------
|
||||
int32_t CControl::mapVstKeyModifier (int32_t vstModifier)
|
||||
{
|
||||
int32_t modifiers = 0;
|
||||
if (vstModifier & MODIFIER_SHIFT)
|
||||
modifiers |= kShift;
|
||||
if (vstModifier & MODIFIER_ALTERNATE)
|
||||
modifiers |= kAlt;
|
||||
if (vstModifier & MODIFIER_COMMAND)
|
||||
modifiers |= kApple;
|
||||
if (vstModifier & MODIFIER_CONTROL)
|
||||
modifiers |= kControl;
|
||||
return modifiers;
|
||||
}
|
||||
#endif // VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
void IMultiBitmapControl::autoComputeHeightOfOneImage ()
|
||||
{
|
||||
auto* view = dynamic_cast<CView*>(this);
|
||||
if (view)
|
||||
{
|
||||
const CRect& viewSize = view->getViewSize ();
|
||||
heightOfOneImage = viewSize.getHeight ();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
void CMouseWheelEditingSupport::onMouseWheelEditing (CControl* control)
|
||||
{
|
||||
if (!control->isEditing ())
|
||||
control->beginEdit ();
|
||||
endEditTimer = makeOwned<CVSTGUITimer> (
|
||||
[control] (CVSTGUITimer* timer) {
|
||||
control->endEdit ();
|
||||
timer->stop ();
|
||||
},
|
||||
500);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMouseWheelEditingSupport::invalidMouseWheelEditTimer (CControl* control)
|
||||
{
|
||||
if (endEditTimer)
|
||||
endEditTimer = nullptr;
|
||||
if (control->isEditing ())
|
||||
control->endEdit ();
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,168 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../cview.h"
|
||||
#include "../ifocusdrawing.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
namespace Constants {
|
||||
|
||||
static constexpr auto pi = 3.14159265358979323846;
|
||||
static constexpr auto double_pi = 6.28318530717958647692;
|
||||
static constexpr auto half_pi = 1.57079632679489661923f;
|
||||
static constexpr auto quarter_pi = 0.78539816339744830962;
|
||||
static constexpr auto e = 2.7182818284590452354;
|
||||
static constexpr auto ln2 = 0.69314718055994530942;
|
||||
static constexpr auto sqrt2 = 1.41421356237309504880;
|
||||
|
||||
} // Constants
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CControl Declaration
|
||||
//! @brief base class of all VSTGUI controls
|
||||
//-----------------------------------------------------------------------------
|
||||
class CControl : public CView, public IFocusDrawing
|
||||
{
|
||||
public:
|
||||
CControl (const CRect& size, IControlListener* listener = nullptr, int32_t tag = 0, CBitmap* pBackground = nullptr);
|
||||
CControl (const CControl& c);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name Value Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
virtual void setValue (float val);
|
||||
virtual float getValue () const { return value; }
|
||||
|
||||
virtual void setValueNormalized (float val);
|
||||
virtual float getValueNormalized () const;
|
||||
|
||||
virtual void setMin (float val);
|
||||
virtual float getMin () const;
|
||||
virtual void setMax (float val);
|
||||
virtual float getMax () const;
|
||||
float getRange () const { return getMax () - getMin (); }
|
||||
|
||||
virtual void setOldValue (float val);
|
||||
virtual float getOldValue () const;
|
||||
virtual void setDefaultValue (float val);
|
||||
virtual float getDefaultValue () const;
|
||||
|
||||
virtual void bounceValue ();
|
||||
|
||||
/** notifies listener and dependent objects */
|
||||
virtual void valueChanged ();
|
||||
//@}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name Editing Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
virtual void setTag (int32_t val);
|
||||
virtual int32_t getTag () const { return tag; }
|
||||
|
||||
virtual void beginEdit ();
|
||||
virtual void endEdit ();
|
||||
bool isEditing () const;
|
||||
|
||||
/** get main listener */
|
||||
virtual IControlListener* getListener () const { return listener; }
|
||||
/** set main listener */
|
||||
virtual void setListener (IControlListener* l) { listener = l; }
|
||||
|
||||
/** register a sub listener */
|
||||
void registerControlListener (IControlListener* listener);
|
||||
/** unregister a sub listener */
|
||||
void unregisterControlListener (IControlListener* listener);
|
||||
//@}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name Misc
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
virtual void setWheelInc (float val);
|
||||
virtual float getWheelInc () const;
|
||||
//@}
|
||||
|
||||
// overrides
|
||||
void draw (CDrawContext* pContext) override = 0;
|
||||
bool isDirty () const override;
|
||||
void setDirty (bool val = true) override;
|
||||
|
||||
bool drawFocusOnTop () override;
|
||||
bool getFocusPath (CGraphicsPath& outPath) override;
|
||||
|
||||
using CheckDefaultValueEventFuncT = bool (*) (CControl*, MouseDownEvent&);
|
||||
/** Function to check if a mouse down event should reset the value to its default value for a
|
||||
*control. Per default this checks for a left mouse down button and the control modifier key. */
|
||||
static CheckDefaultValueEventFuncT CheckDefaultValueEventFunc;
|
||||
|
||||
/** zoom modifier key, per default is the shift key */
|
||||
inline static int32_t kZoomModifier = kShift;
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
/** \deprecated default value modifier key, per default is the control key */
|
||||
inline static int32_t kDefaultValueModifier = kControl;
|
||||
|
||||
using CheckDefaultValueFuncT = bool (*) (CControl*, CButtonState);
|
||||
/** \deprecated Function to check if the button state is the state to set the control value to
|
||||
* its default value. The default implementation uses the kDefaultValueModifier (see above). Use
|
||||
* this to change this to double click per example. But consider to change this to the same
|
||||
* behaviour as the host you are running in for best user experience. */
|
||||
static CheckDefaultValueFuncT CheckDefaultValueFunc;
|
||||
#endif
|
||||
|
||||
CLASS_METHODS_VIRTUAL(CControl, CView)
|
||||
protected:
|
||||
~CControl () noexcept override;
|
||||
VSTGUI_DEPRECATED (static int32_t mapVstKeyModifier (int32_t vstModifier);)
|
||||
|
||||
IControlListener* listener;
|
||||
int32_t tag;
|
||||
float value;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
//-----------------------------------------------------------------------------
|
||||
// IMultiBitmapControl Declaration
|
||||
//! @brief interface for controls with sub images
|
||||
//-----------------------------------------------------------------------------
|
||||
class IMultiBitmapControl
|
||||
{
|
||||
public:
|
||||
virtual ~IMultiBitmapControl() {}
|
||||
virtual void setHeightOfOneImage (const CCoord& height) { heightOfOneImage = height; }
|
||||
virtual CCoord getHeightOfOneImage () const { return heightOfOneImage; }
|
||||
|
||||
virtual void setNumSubPixmaps (int32_t numSubPixmaps) { subPixmaps = numSubPixmaps; }
|
||||
virtual int32_t getNumSubPixmaps () const { return subPixmaps; }
|
||||
|
||||
virtual void autoComputeHeightOfOneImage ();
|
||||
protected:
|
||||
IMultiBitmapControl () : heightOfOneImage (0), subPixmaps (0) {}
|
||||
CCoord heightOfOneImage;
|
||||
int32_t subPixmaps;
|
||||
};
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CMouseWheelEditingSupport Declaration
|
||||
//! @brief Helper class for mouse wheel editing
|
||||
//-----------------------------------------------------------------------------
|
||||
class CMouseWheelEditingSupport
|
||||
{
|
||||
protected:
|
||||
void invalidMouseWheelEditTimer (CControl* control);
|
||||
void onMouseWheelEditing (CControl* control);
|
||||
private:
|
||||
SharedPointer<CBaseObject> endEditTimer {nullptr};
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,302 @@
|
||||
// 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 "cfontchooser.h"
|
||||
#include "../cdatabrowser.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include "cbuttons.h"
|
||||
#include "ctextedit.h"
|
||||
#include "cscrollbar.h"
|
||||
#include "../cstring.h"
|
||||
#include "../platform/platformfactory.h"
|
||||
#include "../platform/iplatformfont.h"
|
||||
#include <list>
|
||||
#include <cmath>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
/// @cond ignore
|
||||
|
||||
namespace CFontChooserInternal {
|
||||
|
||||
class FontPreviewView : public CView
|
||||
{
|
||||
public:
|
||||
FontPreviewView (const CRect& size, const CColor& color = kWhiteCColor) : CView (size), font (nullptr), fontColor (color) {}
|
||||
~FontPreviewView () noexcept override { if (font) font->forget (); }
|
||||
|
||||
void setFont (CFontRef newFont)
|
||||
{
|
||||
if (font)
|
||||
font->forget ();
|
||||
font = newFont;
|
||||
if (font)
|
||||
font->remember ();
|
||||
invalid ();
|
||||
}
|
||||
|
||||
void draw (CDrawContext *context) override
|
||||
{
|
||||
context->setFontColor (fontColor);
|
||||
context->setFont (font);
|
||||
std::string text;
|
||||
char string[2];
|
||||
CRect glyphRect (getViewSize ().left, getViewSize ().top, getViewSize ().left, getViewSize ().top);
|
||||
CCoord height = ceil (font->getPlatformFont ()->getAscent () + font->getPlatformFont ()->getDescent () + font->getPlatformFont ()->getLeading () + 2.);
|
||||
glyphRect.setHeight (height);
|
||||
for (int8_t i = 33; i < 126;)
|
||||
{
|
||||
while (glyphRect.right < getViewSize ().right && i < 126)
|
||||
{
|
||||
snprintf (string, 2, "%c", i++);
|
||||
text += string;
|
||||
glyphRect.setWidth (context->getStringWidth (text.c_str ()));
|
||||
}
|
||||
context->drawString (text.c_str (), glyphRect, kLeftText);
|
||||
glyphRect.left = glyphRect.right = getViewSize ().left;
|
||||
glyphRect.offset (0, height);
|
||||
text = "";
|
||||
}
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
protected:
|
||||
CFontRef font;
|
||||
CColor fontColor;
|
||||
};
|
||||
|
||||
enum {
|
||||
kFontChooserSizeTag,
|
||||
kFontChooserBoldTag,
|
||||
kFontChooserItalicTag,
|
||||
kFontChooserUnderlineTag,
|
||||
kFontChooserStrikeoutTag
|
||||
};
|
||||
|
||||
} // CFontChooserInternal
|
||||
|
||||
/// @endcond
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
CFontChooser::CFontChooser (IFontChooserDelegate* delegate, CFontRef initialFont, const CFontChooserUIDefinition& uiDef)
|
||||
: CViewContainer (CRect (0, 0, 300, 500))
|
||||
, delegate (nullptr)
|
||||
, fontBrowser (nullptr)
|
||||
, selFont (nullptr)
|
||||
{
|
||||
std::list<std::string> fnList;
|
||||
getPlatformFactory ().getAllFontFamilies ([&fnList] (const std::string& name) {
|
||||
fnList.push_back (name);
|
||||
return true;
|
||||
});
|
||||
fnList.sort ();
|
||||
std::list<std::string>::const_iterator it = fnList.begin ();
|
||||
while (it != fnList.end ())
|
||||
{
|
||||
fontNames.emplace_back (*it);
|
||||
++it;
|
||||
}
|
||||
auto* dbSource = new GenericStringListDataBrowserSource (&fontNames, this);
|
||||
dbSource->setupUI (uiDef.selectionColor, uiDef.fontColor, uiDef.rowlineColor, uiDef.rowBackColor, uiDef.rowAlternateBackColor, uiDef.font, uiDef.rowHeight);
|
||||
int32_t dbStyle = CDataBrowser::kDrawRowLines | CScrollView::kVerticalScrollbar | CScrollView::kDontDrawFrame | CScrollView::kOverlayScrollbars;
|
||||
fontBrowser = new CDataBrowser (CRect (0, 0, 200, 500), dbSource, dbStyle, uiDef.scrollbarWidth);
|
||||
dbSource->forget ();
|
||||
fontBrowser->setAutosizeFlags (kAutosizeLeft | kAutosizeTop | kAutosizeBottom);
|
||||
fontBrowser->setTransparency (true);
|
||||
CScrollbar* scrollbar = fontBrowser->getVerticalScrollbar ();
|
||||
if (scrollbar)
|
||||
{
|
||||
scrollbar->setBackgroundColor (uiDef.scrollbarBackgroundColor);
|
||||
scrollbar->setFrameColor (uiDef.scrollbarFrameColor);
|
||||
scrollbar->setScrollerColor (uiDef.scrollbarScrollerColor);
|
||||
}
|
||||
addView (fontBrowser);
|
||||
CRect controlRect (210, 0, 300, 20);
|
||||
auto* label = new CTextLabel (controlRect, "Size:");
|
||||
label->setFont (uiDef.font);
|
||||
label->setFontColor (uiDef.fontColor);
|
||||
label->sizeToFit ();
|
||||
label->setHoriAlign (kLeftText);
|
||||
label->setTransparency (true);
|
||||
label->setAutosizeFlags (kAutosizeLeft | kAutosizeTop);
|
||||
addView (label);
|
||||
CRect teRect = label->getViewSize ();
|
||||
teRect.left = teRect.right + 5.;
|
||||
teRect.right = controlRect.right;
|
||||
sizeEdit = new CTextEdit (teRect, this, CFontChooserInternal::kFontChooserSizeTag);
|
||||
sizeEdit->setFont (uiDef.font);
|
||||
sizeEdit->setFontColor (uiDef.fontColor);
|
||||
sizeEdit->setHoriAlign (kLeftText);
|
||||
sizeEdit->setTransparency (true);
|
||||
sizeEdit->setAutosizeFlags (kAutosizeLeft | kAutosizeTop);
|
||||
sizeEdit->setMax (2000);
|
||||
sizeEdit->setMin (6);
|
||||
sizeEdit->setValue (2000);
|
||||
sizeEdit->sizeToFit ();
|
||||
sizeEdit->setStringToValueFunction ([] (UTF8StringPtr txt, float& result, CTextEdit* textEdit) { result = UTF8StringView (txt).toFloat (); return true; });
|
||||
addView (sizeEdit);
|
||||
controlRect.offset (0, 20);
|
||||
boldBox = new CCheckBox (controlRect, this, CFontChooserInternal::kFontChooserBoldTag, "Bold");
|
||||
boldBox->setFont (uiDef.font);
|
||||
boldBox->setFontColor (uiDef.fontColor);
|
||||
boldBox->setAutosizeFlags (kAutosizeLeft | kAutosizeTop);
|
||||
boldBox->sizeToFit ();
|
||||
addView (boldBox);
|
||||
controlRect.offset (0, 20);
|
||||
italicBox = new CCheckBox (controlRect, this, CFontChooserInternal::kFontChooserItalicTag, "Italic");
|
||||
italicBox->setFont (uiDef.font);
|
||||
italicBox->setFontColor (uiDef.fontColor);
|
||||
italicBox->setAutosizeFlags (kAutosizeLeft | kAutosizeTop);
|
||||
italicBox->sizeToFit ();
|
||||
addView (italicBox);
|
||||
controlRect.offset (0, 20);
|
||||
underlineBox = new CCheckBox (controlRect, this, CFontChooserInternal::kFontChooserUnderlineTag, "Underline");
|
||||
underlineBox->setFont (uiDef.font);
|
||||
underlineBox->setFontColor (uiDef.fontColor);
|
||||
underlineBox->setAutosizeFlags (kAutosizeLeft | kAutosizeTop);
|
||||
underlineBox->sizeToFit ();
|
||||
addView (underlineBox);
|
||||
controlRect.offset (0, 20);
|
||||
strikeoutBox = new CCheckBox (controlRect, this, CFontChooserInternal::kFontChooserStrikeoutTag, "Strikeout");
|
||||
strikeoutBox->setFont (uiDef.font);
|
||||
strikeoutBox->setFontColor (uiDef.fontColor);
|
||||
strikeoutBox->setAutosizeFlags (kAutosizeLeft | kAutosizeTop);
|
||||
strikeoutBox->sizeToFit ();
|
||||
addView (strikeoutBox);
|
||||
|
||||
CViewContainer* container = new CViewContainer (CRect (controlRect.left, controlRect.bottom+10, 300, 500));
|
||||
container->setBackgroundColor (uiDef.previewBackgroundColor);
|
||||
container->setAutosizeFlags (kAutosizeTop | kAutosizeBottom | kAutosizeLeft | kAutosizeRight);
|
||||
fontPreviewView = new CFontChooserInternal::FontPreviewView (CRect (10, 10, container->getWidth () - 10, container->getHeight () - 10), uiDef.previewTextColor);
|
||||
fontPreviewView->setAutosizeFlags (kAutosizeAll);
|
||||
container->addView (fontPreviewView);
|
||||
addView (container);
|
||||
|
||||
setFont (initialFont ? initialFont : kSystemFont);
|
||||
|
||||
sizeToFit ();
|
||||
|
||||
this->delegate = delegate;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CFontChooser::~CFontChooser () noexcept
|
||||
{
|
||||
if (selFont)
|
||||
selFont->forget ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CFontChooser::setFont (CFontRef font)
|
||||
{
|
||||
if (font)
|
||||
{
|
||||
if (selFont)
|
||||
selFont->forget ();
|
||||
selFont = new CFontDesc (*font);
|
||||
sizeEdit->setValue ((float)font->getSize ());
|
||||
boldBox->setValue ((font->getStyle () & kBoldFace) ? 1.f : 0.f);
|
||||
italicBox->setValue ((font->getStyle () & kItalicFace) ? 1.f : 0.f);
|
||||
underlineBox->setValue ((font->getStyle () & kUnderlineFace) ? 1.f : 0.f);
|
||||
strikeoutBox->setValue ((font->getStyle () & kStrikethroughFace) ? 1.f : 0.f);
|
||||
|
||||
auto it = fontNames.begin ();
|
||||
int32_t row = 0;
|
||||
while (it != fontNames.end ())
|
||||
{
|
||||
if (*it == font->getName ())
|
||||
{
|
||||
fontBrowser->setSelectedRow (row, true);
|
||||
break;
|
||||
}
|
||||
++it;
|
||||
row++;
|
||||
}
|
||||
static_cast<CFontChooserInternal::FontPreviewView*> (fontPreviewView)->setFont (selFont);
|
||||
}
|
||||
invalid ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CFontChooser::valueChanged (CControl* pControl)
|
||||
{
|
||||
if (selFont == nullptr)
|
||||
return;
|
||||
|
||||
switch (pControl->getTag ())
|
||||
{
|
||||
case CFontChooserInternal::kFontChooserSizeTag:
|
||||
{
|
||||
pControl->setValue (pControl->getValue ());
|
||||
selFont->setSize (pControl->getValue ());
|
||||
break;
|
||||
}
|
||||
case CFontChooserInternal::kFontChooserBoldTag:
|
||||
{
|
||||
if (pControl->getValue () == 1)
|
||||
selFont->setStyle (selFont->getStyle () | kBoldFace);
|
||||
else
|
||||
selFont->setStyle (selFont->getStyle () & ~kBoldFace);
|
||||
break;
|
||||
}
|
||||
case CFontChooserInternal::kFontChooserItalicTag:
|
||||
{
|
||||
if (pControl->getValue () == 1)
|
||||
selFont->setStyle (selFont->getStyle () | kItalicFace);
|
||||
else
|
||||
selFont->setStyle (selFont->getStyle () & ~kItalicFace);
|
||||
break;
|
||||
}
|
||||
case CFontChooserInternal::kFontChooserUnderlineTag:
|
||||
{
|
||||
if (pControl->getValue () == 1)
|
||||
selFont->setStyle (selFont->getStyle () | kUnderlineFace);
|
||||
else
|
||||
selFont->setStyle (selFont->getStyle () & ~kUnderlineFace);
|
||||
break;
|
||||
}
|
||||
case CFontChooserInternal::kFontChooserStrikeoutTag:
|
||||
{
|
||||
if (pControl->getValue () == 1)
|
||||
selFont->setStyle (selFont->getStyle () | kStrikethroughFace);
|
||||
else
|
||||
selFont->setStyle (selFont->getStyle () & ~kStrikethroughFace);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (delegate)
|
||||
delegate->fontChanged (this, selFont);
|
||||
static_cast<CFontChooserInternal::FontPreviewView*> (fontPreviewView)->setFont (selFont);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CFontChooser::dbSelectionChanged (int32_t selectedRow, GenericStringListDataBrowserSource* source)
|
||||
{
|
||||
if (selectedRow >= 0 && static_cast<size_t> (selectedRow) <= fontNames.size ())
|
||||
selFont->setName (fontNames[static_cast<size_t> (selectedRow)].data ());
|
||||
static_cast<CFontChooserInternal::FontPreviewView*> (fontPreviewView)->setFont (selFont);
|
||||
if (delegate)
|
||||
delegate->fontChanged (this, selFont);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CFontChooser::attached (CView* parent)
|
||||
{
|
||||
if (CViewContainer::attached (parent))
|
||||
{
|
||||
fontBrowser->makeRowVisible (fontBrowser->getSelectedRow ());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CFontChooser::onKeyboardEvent (KeyboardEvent& event)
|
||||
{
|
||||
fontBrowser->onKeyboardEvent (event);
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,92 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
#include "../cviewcontainer.h"
|
||||
#include "../cfont.h"
|
||||
#include "../cdatabrowser.h"
|
||||
#include "../genericstringlistdatabrowsersource.h"
|
||||
#include "icontrollistener.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
/// @ingroup new_in_4_0
|
||||
//-----------------------------------------------------------------------------
|
||||
class IFontChooserDelegate
|
||||
{
|
||||
public:
|
||||
virtual void fontChanged (CFontChooser* chooser, CFontRef newFont) = 0;
|
||||
};
|
||||
|
||||
/// @ingroup new_in_4_0
|
||||
//-----------------------------------------------------------------------------
|
||||
struct CFontChooserUIDefinition
|
||||
{
|
||||
CFontRef font;
|
||||
int32_t rowHeight;
|
||||
CColor fontColor;
|
||||
CColor selectionColor;
|
||||
CColor rowlineColor;
|
||||
CColor rowBackColor;
|
||||
CColor rowAlternateBackColor;
|
||||
CColor previewTextColor;
|
||||
CColor previewBackgroundColor;
|
||||
CColor scrollbarScrollerColor;
|
||||
CColor scrollbarFrameColor;
|
||||
CColor scrollbarBackgroundColor;
|
||||
CCoord scrollbarWidth;
|
||||
|
||||
CFontChooserUIDefinition (CFontRef font = kSystemFont,
|
||||
const CColor& fontColor = kWhiteCColor,
|
||||
const CColor& selectionColor = kBlueCColor,
|
||||
const CColor& rowlineColor = kGreyCColor,
|
||||
const CColor& rowBackColor = kTransparentCColor,
|
||||
const CColor& rowAlternateBackColor = kTransparentCColor,
|
||||
const CColor& previewTextColor = kBlackCColor,
|
||||
const CColor& previewBackgroundColor = kWhiteCColor,
|
||||
const CColor& scrollbarScrollerColor = kBlueCColor,
|
||||
const CColor& scrollbarFrameColor = kBlackCColor,
|
||||
const CColor& scrollbarBackgroundColor = kGreyCColor,
|
||||
int32_t rowHeight = -1,
|
||||
CCoord scrollbarWidth = 16)
|
||||
: font (font), rowHeight (rowHeight), fontColor (fontColor), selectionColor (selectionColor), rowlineColor (rowlineColor)
|
||||
, rowBackColor (rowBackColor), rowAlternateBackColor (rowAlternateBackColor), previewTextColor (previewTextColor), previewBackgroundColor (previewBackgroundColor)
|
||||
, scrollbarScrollerColor (scrollbarScrollerColor), scrollbarFrameColor (scrollbarFrameColor)
|
||||
, scrollbarBackgroundColor (scrollbarBackgroundColor), scrollbarWidth (scrollbarWidth)
|
||||
{}
|
||||
};
|
||||
|
||||
/// @ingroup new_in_4_0
|
||||
//-----------------------------------------------------------------------------
|
||||
class CFontChooser : public CViewContainer, public IControlListener, public GenericStringListDataBrowserSourceSelectionChanged
|
||||
{
|
||||
public:
|
||||
CFontChooser (IFontChooserDelegate* delegate, CFontRef initialFont = nullptr, const CFontChooserUIDefinition& uiDef = CFontChooserUIDefinition ());
|
||||
~CFontChooser () noexcept override;
|
||||
|
||||
void setFont (CFontRef font);
|
||||
|
||||
protected:
|
||||
void dbSelectionChanged (int32_t selectedRow, GenericStringListDataBrowserSource* source) override;
|
||||
void valueChanged (CControl* pControl) override;
|
||||
bool attached (CView* parent) override;
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
|
||||
IFontChooserDelegate* delegate;
|
||||
CDataBrowser* fontBrowser;
|
||||
CTextEdit* sizeEdit;
|
||||
CCheckBox* boldBox;
|
||||
CCheckBox* italicBox;
|
||||
CCheckBox* underlineBox;
|
||||
CCheckBox* strikeoutBox;
|
||||
CView* fontPreviewView;
|
||||
GenericStringListDataBrowserSource::StringVector fontNames;
|
||||
|
||||
CFontRef selFont;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
|
||||
@@ -0,0 +1,911 @@
|
||||
// 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
|
||||
@@ -0,0 +1,202 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ccontrol.h"
|
||||
#include "../cbitmap.h"
|
||||
#include "../ccolor.h"
|
||||
#include "../clinestyle.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CKnobBase : public CControl, protected CMouseWheelEditingSupport
|
||||
{
|
||||
public:
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CKnobBase Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
virtual void valueToPoint (CPoint& point) const;
|
||||
virtual float valueFromPoint (CPoint& point) const;
|
||||
|
||||
virtual void setStartAngle (float val);
|
||||
virtual float getStartAngle () const { return startAngle; }
|
||||
|
||||
virtual void setRangeAngle (float val);
|
||||
virtual float getRangeAngle () const { return rangeAngle; }
|
||||
|
||||
virtual void setZoomFactor (float val) { zoomFactor = val; }
|
||||
virtual float getZoomFactor () const { return zoomFactor; }
|
||||
|
||||
virtual CCoord getInsetValue () const { return inset; }
|
||||
virtual void setInsetValue (CCoord val) { inset = val; }
|
||||
|
||||
virtual void setKnobRange (float val) { if (val > 0.f) knobRange = val; }
|
||||
virtual float getKnobRange () const { return knobRange; }
|
||||
//@}
|
||||
|
||||
// overrides
|
||||
void onMouseWheelEvent (MouseWheelEvent& event) override;
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
void setViewSize (const CRect &rect, bool invalid = true) override;
|
||||
bool sizeToFit () override;
|
||||
void setMin (float val) override;
|
||||
void setMax (float val) override;
|
||||
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseCancel () override;
|
||||
|
||||
CLASS_METHODS_VIRTUAL(CKnobBase, CControl)
|
||||
protected:
|
||||
CKnobBase (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background);
|
||||
CKnobBase (const CKnobBase& knob);
|
||||
void compute ();
|
||||
|
||||
float startAngle, rangeAngle;
|
||||
float zoomFactor;
|
||||
float knobRange;
|
||||
CCoord inset;
|
||||
|
||||
private:
|
||||
struct MouseEditingState;
|
||||
|
||||
MouseEditingState& getMouseEditingState ();
|
||||
void clearMouseEditingState ();
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CKnob Declaration
|
||||
//! @brief a knob control
|
||||
/// @ingroup controls
|
||||
//-----------------------------------------------------------------------------
|
||||
class CKnob : public CKnobBase
|
||||
{
|
||||
public:
|
||||
enum DrawStyle {
|
||||
kLegacyHandleLineDrawing = 0,
|
||||
kHandleCircleDrawing = 1 << 0,
|
||||
kCoronaDrawing = 1 << 1,
|
||||
kCoronaFromCenter = 1 << 2,
|
||||
kCoronaInverted = 1 << 3,
|
||||
kCoronaLineDashDot = 1 << 4,
|
||||
kCoronaOutline = 1 << 5,
|
||||
kCoronaLineCapButt = 1 << 6,
|
||||
kSkipHandleDrawing = 1 << 7,
|
||||
};
|
||||
|
||||
CKnob (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background, CBitmap* handle, const CPoint& offset = CPoint (0, 0), int32_t drawStyle = kLegacyHandleLineDrawing);
|
||||
CKnob (const CKnob& knob);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CKnob Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
int32_t getDrawStyle () const { return drawStyle; }
|
||||
virtual void setDrawStyle (int32_t style);
|
||||
|
||||
CColor getCoronaColor () const { return coronaColor; }
|
||||
virtual void setCoronaColor (CColor color);
|
||||
|
||||
CCoord getCoronaInset () const { return coronaInset; }
|
||||
virtual void setCoronaInset (CCoord inset);
|
||||
|
||||
CColor getColorShadowHandle () const { return colorShadowHandle; }
|
||||
virtual void setColorShadowHandle (CColor color);
|
||||
|
||||
CColor getColorHandle () const { return colorHandle; }
|
||||
virtual void setColorHandle (CColor color);
|
||||
|
||||
CCoord getHandleLineWidth () const { return handleLineWidth; }
|
||||
virtual void setHandleLineWidth (CCoord width);
|
||||
|
||||
CCoord getCoronaOutlineWidthAdd () const { return coronaOutlineWidthAdd; }
|
||||
virtual void setCoronaOutlineWidthAdd (CCoord width);
|
||||
|
||||
const CLineStyle::CoordVector& getCoronaDashDotLengths () const;
|
||||
virtual void setCoronaDashDotLengths (const CLineStyle::CoordVector& lengths);
|
||||
|
||||
CBitmap* getHandleBitmap () const { return pHandle; }
|
||||
void setHandleBitmap (CBitmap* bitmap);
|
||||
//@}
|
||||
|
||||
// overrides
|
||||
void draw (CDrawContext* pContext) override;
|
||||
bool getFocusPath (CGraphicsPath& outPath) override;
|
||||
bool drawFocusOnTop () override;
|
||||
|
||||
CLASS_METHODS(CKnob, CKnobBase)
|
||||
protected:
|
||||
~CKnob () noexcept override;
|
||||
|
||||
virtual void drawHandle (CDrawContext* pContext);
|
||||
virtual void drawCoronaOutline (CDrawContext* pContext) const;
|
||||
virtual void drawCorona (CDrawContext* pContext) const;
|
||||
virtual void drawHandleAsCircle (CDrawContext* pContext) const;
|
||||
virtual void drawHandleAsLine (CDrawContext* pContext) const;
|
||||
|
||||
static void addArc (CGraphicsPath* path, const CRect& r, double startAngle, double sweepAngle);
|
||||
|
||||
CPoint offset;
|
||||
|
||||
int32_t drawStyle;
|
||||
CColor colorHandle, colorShadowHandle, coronaColor;
|
||||
CCoord handleLineWidth;
|
||||
CCoord coronaInset;
|
||||
CCoord coronaOutlineWidthAdd;
|
||||
|
||||
CLineStyle coronaLineStyle;
|
||||
CBitmap* pHandle;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CAnimKnob Declaration
|
||||
//! @brief a bitmap knob control
|
||||
/// @ingroup controls uses_multi_frame_bitmaps
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAnimKnob : public CKnobBase,
|
||||
public MultiFrameBitmapView<CAnimKnob>
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
,
|
||||
public IMultiBitmapControl
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
CAnimKnob (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background);
|
||||
CAnimKnob (const CAnimKnob& knob);
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CAnimKnob (const CRect& size, IControlListener* listener, int32_t tag, int32_t subPixmaps,
|
||||
CCoord heightOfOneImage, CBitmap* background, const CPoint& offset = CPoint (0, 0));
|
||||
void setHeightOfOneImage (const CCoord& height) override;
|
||||
void setNumSubPixmaps (int32_t numSubPixmaps) override
|
||||
{
|
||||
IMultiBitmapControl::setNumSubPixmaps (numSubPixmaps);
|
||||
invalid ();
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CAnimKnob Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
void setInverseBitmap (bool val) { bInverseBitmap = val; }
|
||||
bool getInverseBitmap () const { return bInverseBitmap; }
|
||||
//@}
|
||||
|
||||
// overrides
|
||||
void draw (CDrawContext* pContext) override;
|
||||
bool sizeToFit () override;
|
||||
void setBackground (CBitmap* background) override;
|
||||
|
||||
CLASS_METHODS(CAnimKnob, CKnobBase)
|
||||
protected:
|
||||
~CAnimKnob () noexcept override = default;
|
||||
bool bInverseBitmap;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,568 @@
|
||||
// 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 "../cbitmap.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include "../cframe.h"
|
||||
#include "../cgraphicspath.h"
|
||||
#include "../cscrollview.h"
|
||||
#include "../events.h"
|
||||
#include "clistcontrol.h"
|
||||
#include <vector>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct CListControl::Impl
|
||||
{
|
||||
SharedPointer<IListControlDrawer> drawer;
|
||||
SharedPointer<IListControlConfigurator> configurator;
|
||||
|
||||
std::vector<CListControlRowDesc> rowDescriptions;
|
||||
Optional<int32_t> hoveredRow {};
|
||||
bool doHoverCheck {false};
|
||||
CCoord minHeight {0.};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CListControl::CListControl (const CRect& size, IControlListener* listener, int32_t tag)
|
||||
: CControl (size, listener, tag)
|
||||
{
|
||||
impl = std::unique_ptr<Impl> (new Impl);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CListControl::~CListControl () = default;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CListControl::setDrawer (IListControlDrawer* d)
|
||||
{
|
||||
impl->drawer = d;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CListControl::setConfigurator (IListControlConfigurator* c)
|
||||
{
|
||||
impl->configurator = c;
|
||||
recalculateLayout ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
IListControlDrawer* CListControl::getDrawer () const
|
||||
{
|
||||
return impl->drawer;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
IListControlConfigurator* CListControl::getConfigurator () const
|
||||
{
|
||||
return impl->configurator;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Optional<int32_t> CListControl::getHoveredRow () const
|
||||
{
|
||||
if (impl->hoveredRow)
|
||||
return makeOptional (*impl->hoveredRow);
|
||||
return {};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
int32_t CListControl::getNumRows () const
|
||||
{
|
||||
auto numRows = static_cast<int32_t> (std::round (getRange ())) + 1;
|
||||
return std::max (0, numRows);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
int32_t CListControl::getIntValue () const
|
||||
{
|
||||
return static_cast<int32_t> (std::round (getValue ()));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CListControl::recalculateLayout ()
|
||||
{
|
||||
if (!impl->configurator)
|
||||
return;
|
||||
|
||||
CCoord height = 0.;
|
||||
auto numRows = getNumRows ();
|
||||
impl->rowDescriptions.resize (static_cast<size_t> (numRows));
|
||||
impl->doHoverCheck = false;
|
||||
|
||||
for (auto row = 0; row < numRows; ++row)
|
||||
{
|
||||
impl->rowDescriptions[row] = impl->configurator->getRowDesc (row);
|
||||
height += impl->rowDescriptions[row].height;
|
||||
impl->doHoverCheck |= (impl->rowDescriptions[row].flags & CListControlRowDesc::Hoverable) != 0;
|
||||
}
|
||||
|
||||
if (impl->minHeight > 0 && height < impl->minHeight)
|
||||
height = impl->minHeight;
|
||||
|
||||
auto viewSize = getViewSize ();
|
||||
if (viewSize.getHeight () != height)
|
||||
{
|
||||
viewSize.setHeight (height);
|
||||
setViewSize (viewSize);
|
||||
setMouseableArea (viewSize);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Optional<CRect> CListControl::getRowRect (int32_t row) const
|
||||
{
|
||||
if (row < getMinRowIndex () || row > getMaxRowIndex ())
|
||||
return {};
|
||||
row -= getMinRowIndex ();
|
||||
CRect rowSize;
|
||||
rowSize.setWidth (getWidth ());
|
||||
for (auto i = 0u; i < impl->rowDescriptions.size (); ++i)
|
||||
{
|
||||
rowSize.setHeight (impl->rowDescriptions[i].height);
|
||||
if (i == row)
|
||||
break;
|
||||
rowSize.offset (0, impl->rowDescriptions[i].height);
|
||||
}
|
||||
rowSize.offset (getViewSize ().getTopLeft ());
|
||||
return makeOptional (rowSize);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CListControl::invalidRow (int32_t row)
|
||||
{
|
||||
if (auto rect = getRowRect (row))
|
||||
invalidRect (*rect);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
Optional<int32_t> CListControl::getRowAtPoint (CPoint where) const
|
||||
{
|
||||
where.offsetInverse (getViewSize ().getTopLeft ());
|
||||
|
||||
auto numRows = getNumRows ();
|
||||
for (auto row = 0; row < numRows; ++row)
|
||||
{
|
||||
if (where.y < impl->rowDescriptions[row].height)
|
||||
return {row + getMinRowIndex ()};
|
||||
where.y -= impl->rowDescriptions[row].height;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CListControl::draw (CDrawContext* context)
|
||||
{
|
||||
drawRect (context, getViewSize ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CListControl::drawRect (CDrawContext* context, const CRect& updateRect)
|
||||
{
|
||||
setDirty (false);
|
||||
ConcatClip cc (*context, updateRect);
|
||||
if (cc.isEmpty ())
|
||||
return;
|
||||
|
||||
if (auto bitmap = getDrawBackground ())
|
||||
bitmap->draw (context, getViewSize ());
|
||||
|
||||
if (!impl->drawer)
|
||||
return;
|
||||
|
||||
if (!getTransparency ())
|
||||
impl->drawer->drawBackground (context, getViewSize ());
|
||||
|
||||
CRect rowSize;
|
||||
rowSize.setTopLeft (getViewSize ().getTopLeft ());
|
||||
rowSize.setWidth (getWidth ());
|
||||
rowSize.setHeight (0);
|
||||
auto numRows = getNumRows ();
|
||||
auto selectedRow = static_cast<int32_t> (getNormalizedRowIndex (getIntValue ()));
|
||||
for (auto row = 0; row < numRows; ++row)
|
||||
{
|
||||
rowSize.setHeight (impl->rowDescriptions[row].height);
|
||||
if (updateRect.rectOverlap (rowSize))
|
||||
{
|
||||
IListControlDrawer::Row::Flags flags;
|
||||
if (selectedRow == row)
|
||||
flags = IListControlDrawer::Row::Selected;
|
||||
if (impl->rowDescriptions[row].flags & CListControlRowDesc::Selectable)
|
||||
flags |= IListControlDrawer::Row::Selectable;
|
||||
if (impl->hoveredRow && *impl->hoveredRow == row + getMinRowIndex ())
|
||||
flags |= IListControlDrawer::Row::Hovered;
|
||||
if (row == numRows - 1)
|
||||
flags |= IListControlDrawer::Row::LastRow;
|
||||
impl->drawer->drawRow (context, rowSize, {row + getMinRowIndex (), flags});
|
||||
}
|
||||
rowSize.offset (0, impl->rowDescriptions[row].height);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CListControl::attached (CView* parent)
|
||||
{
|
||||
if (auto scrollView = dynamic_cast<CScrollView*> (parent->getParentView ()))
|
||||
{
|
||||
impl->minHeight = scrollView->calculateOptimalContainerSize ().getHeight ();
|
||||
struct SizeListener : ViewListenerAdapter
|
||||
{
|
||||
SizeListener (CListControl* listControl, CScrollView* scrollView)
|
||||
: control (listControl), scrollView (scrollView)
|
||||
{
|
||||
listControl->registerViewListener (this);
|
||||
scrollView->registerViewListener (this);
|
||||
}
|
||||
~SizeListener () noexcept
|
||||
{
|
||||
control->unregisterViewListener (this);
|
||||
scrollView->unregisterViewListener (this);
|
||||
}
|
||||
void viewSizeChanged (CView* view, const CRect& oldSize) override
|
||||
{
|
||||
if (view != scrollView)
|
||||
return;
|
||||
control->impl->minHeight =
|
||||
scrollView->calculateOptimalContainerSize ().getHeight ();
|
||||
control->recalculateLayout ();
|
||||
}
|
||||
void viewWillDelete (CView* view) override
|
||||
{
|
||||
if (view == control || view == scrollView)
|
||||
delete this;
|
||||
}
|
||||
void viewAttached (CView* view) override {}
|
||||
void viewRemoved (CView* view) override {}
|
||||
|
||||
CListControl* control {nullptr};
|
||||
CScrollView* scrollView {nullptr};
|
||||
};
|
||||
new SizeListener (this, scrollView);
|
||||
}
|
||||
recalculateLayout ();
|
||||
return CControl::attached (parent);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CListControl::setMin (float val)
|
||||
{
|
||||
if (getMin () != val && val < getMax ())
|
||||
{
|
||||
auto ov = getValue ();
|
||||
CControl::setMin (val);
|
||||
if (isAttached ())
|
||||
recalculateLayout ();
|
||||
if (ov != getValue ())
|
||||
valueChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CListControl::setMax (float val)
|
||||
{
|
||||
if (getMax () != val && val >= getMin ())
|
||||
{
|
||||
auto ov = getValue ();
|
||||
CControl::setMax (val);
|
||||
if (isAttached ())
|
||||
recalculateLayout ();
|
||||
if (ov != getValue ())
|
||||
valueChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
size_t CListControl::getNormalizedRowIndex (int32_t row) const
|
||||
{
|
||||
vstgui_assert (row >= getMinRowIndex ());
|
||||
return row - getMinRowIndex ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CListControl::onMouseDown (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (buttons.isLeftButton ())
|
||||
return kMouseEventHandled;
|
||||
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CListControl::clearHoveredRow ()
|
||||
{
|
||||
if (impl->hoveredRow)
|
||||
{
|
||||
invalidRow (*impl->hoveredRow);
|
||||
impl->hoveredRow.reset ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CListControl::onMouseMoved (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (impl->doHoverCheck)
|
||||
{
|
||||
auto row = getRowAtPoint (where);
|
||||
if (row)
|
||||
{
|
||||
if (impl->rowDescriptions[getNormalizedRowIndex (*row)].flags &
|
||||
CListControlRowDesc::Hoverable)
|
||||
{
|
||||
if (!impl->hoveredRow || *impl->hoveredRow != *row)
|
||||
{
|
||||
clearHoveredRow ();
|
||||
impl->hoveredRow = makeOptional (*row);
|
||||
invalidRow (*row);
|
||||
}
|
||||
}
|
||||
else
|
||||
clearHoveredRow ();
|
||||
}
|
||||
else
|
||||
clearHoveredRow ();
|
||||
}
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CListControl::onMouseUp (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (impl->rowDescriptions.empty () || !buttons.isLeftButton ())
|
||||
return kMouseEventHandled;
|
||||
|
||||
auto row = getRowAtPoint (where);
|
||||
if (row && getIntValue () != *row)
|
||||
{
|
||||
if (rowSelectable (*row))
|
||||
{
|
||||
invalidRow (getIntValue ());
|
||||
beginEdit ();
|
||||
setValue (static_cast<float> (*row));
|
||||
valueChanged ();
|
||||
endEdit ();
|
||||
invalidRow (getIntValue ());
|
||||
}
|
||||
}
|
||||
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CListControl::onMouseExited (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
clearHoveredRow ();
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
int32_t CListControl::getMinRowIndex () const
|
||||
{
|
||||
return static_cast<int32_t> (getMin ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
int32_t CListControl::getMaxRowIndex () const
|
||||
{
|
||||
return static_cast<int32_t> (getMax ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
int32_t CListControl::getNextSelectableRow (int32_t r, int32_t direction) const
|
||||
{
|
||||
auto minRowIndex = getMinRowIndex ();
|
||||
auto maxRowIndex = getMaxRowIndex ();
|
||||
int32_t row = r;
|
||||
do
|
||||
{
|
||||
row += direction;
|
||||
if (row > maxRowIndex)
|
||||
row = minRowIndex;
|
||||
else if (row < minRowIndex)
|
||||
row = maxRowIndex;
|
||||
if (rowSelectable (row))
|
||||
break;
|
||||
} while (row != r);
|
||||
return row;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CListControl::rowSelectable (int32_t row) const
|
||||
{
|
||||
return (impl->rowDescriptions[getNormalizedRowIndex (row)].flags &
|
||||
CListControlRowDesc::Selectable) != 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CListControl::onKeyboardEvent (KeyboardEvent& event)
|
||||
{
|
||||
if (event.type != EventType::KeyDown)
|
||||
return;
|
||||
if (getMouseEnabled () && event.character == 0)
|
||||
{
|
||||
int32_t newRow = getIntValue ();
|
||||
switch (event.virt)
|
||||
{
|
||||
default: return;
|
||||
case VirtualKey::Home:
|
||||
{
|
||||
if (!event.modifiers.empty ())
|
||||
break;
|
||||
newRow = getMinRowIndex ();
|
||||
if (!rowSelectable (newRow))
|
||||
newRow = getNextSelectableRow (newRow, 1);
|
||||
break;
|
||||
}
|
||||
case VirtualKey::End:
|
||||
{
|
||||
if (!event.modifiers.empty ())
|
||||
break;
|
||||
newRow = getMaxRowIndex ();
|
||||
if (!rowSelectable (newRow))
|
||||
newRow = getNextSelectableRow (newRow, -1);
|
||||
break;
|
||||
}
|
||||
case VirtualKey::Up:
|
||||
{
|
||||
if (!event.modifiers.empty ())
|
||||
break;
|
||||
newRow = getNextSelectableRow (newRow, -1);
|
||||
break;
|
||||
}
|
||||
case VirtualKey::Down:
|
||||
{
|
||||
if (!event.modifiers.empty ())
|
||||
break;
|
||||
newRow = getNextSelectableRow (newRow, 1);
|
||||
break;
|
||||
}
|
||||
case VirtualKey::PageUp:
|
||||
{
|
||||
if (!event.modifiers.empty ())
|
||||
break;
|
||||
auto vr = getVisibleViewSize ();
|
||||
auto rr = getRowRect (newRow);
|
||||
if (rr && !vr.rectOverlap (*rr))
|
||||
{
|
||||
if (auto parent = getParentView ())
|
||||
{
|
||||
if (auto scrollView = dynamic_cast<CScrollView*> (parent->getParentView ()))
|
||||
{
|
||||
scrollView->makeRectVisible (*rr);
|
||||
onKeyboardEvent (event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
vr.top += 2;
|
||||
if (auto firstVisibleRow = getRowAtPoint (vr.getTopLeft ()))
|
||||
{
|
||||
while (!rowSelectable (*firstVisibleRow))
|
||||
*firstVisibleRow += 1;
|
||||
if (*firstVisibleRow == getIntValue ())
|
||||
{
|
||||
vr.offset (0, -vr.getHeight ());
|
||||
if ((firstVisibleRow = getRowAtPoint (vr.getTopLeft ())))
|
||||
newRow = *firstVisibleRow;
|
||||
else
|
||||
newRow = getMinRowIndex ();
|
||||
}
|
||||
else
|
||||
{
|
||||
newRow = *firstVisibleRow;
|
||||
}
|
||||
}
|
||||
if (!rowSelectable (newRow))
|
||||
newRow = getNextSelectableRow (newRow, -1);
|
||||
break;
|
||||
}
|
||||
case VirtualKey::PageDown:
|
||||
{
|
||||
if (!event.modifiers.empty ())
|
||||
break;
|
||||
auto vr = getVisibleViewSize ();
|
||||
auto rr = getRowRect (newRow);
|
||||
if (rr && !vr.rectOverlap (*rr))
|
||||
{
|
||||
if (auto parent = getParentView ())
|
||||
{
|
||||
if (auto scrollView = dynamic_cast<CScrollView*> (parent->getParentView ()))
|
||||
{
|
||||
scrollView->makeRectVisible (*rr);
|
||||
onKeyboardEvent (event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
vr.bottom -= 2;
|
||||
if (auto lastVisibleRow = getRowAtPoint (vr.getBottomLeft ()))
|
||||
{
|
||||
while (!rowSelectable (*lastVisibleRow))
|
||||
*lastVisibleRow -= 1;
|
||||
if (*lastVisibleRow == getIntValue ())
|
||||
{
|
||||
vr.offset (0, vr.getHeight ());
|
||||
if ((lastVisibleRow = getRowAtPoint (vr.getBottomLeft ())))
|
||||
newRow = *lastVisibleRow;
|
||||
else
|
||||
newRow = getMaxRowIndex ();
|
||||
}
|
||||
else
|
||||
{
|
||||
newRow = *lastVisibleRow;
|
||||
}
|
||||
}
|
||||
if (!rowSelectable (newRow))
|
||||
newRow = getNextSelectableRow (newRow, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (newRow != getIntValue () && rowSelectable (newRow))
|
||||
{
|
||||
invalidRow (getIntValue ());
|
||||
beginEdit ();
|
||||
setValue (static_cast<float> (newRow));
|
||||
valueChanged ();
|
||||
endEdit ();
|
||||
if (auto rowRect = getRowRect (getIntValue ()))
|
||||
{
|
||||
invalidRect (*rowRect);
|
||||
if (auto parent = getParentView ())
|
||||
{
|
||||
if (auto scrollView = dynamic_cast<CScrollView*> (parent->getParentView ()))
|
||||
scrollView->makeRectVisible (*rowRect);
|
||||
}
|
||||
}
|
||||
event.consumed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CListControl::setViewSize (const CRect& rect, bool invalid)
|
||||
{
|
||||
CControl::setViewSize (rect, invalid);
|
||||
impl->hoveredRow.reset ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CListControl::drawFocusOnTop ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CListControl::getFocusPath (CGraphicsPath& outPath)
|
||||
{
|
||||
CRect r = getVisibleViewSize ();
|
||||
outPath.addRect (r);
|
||||
CCoord focusWidth = getFrame ()->getFocusWidth ();
|
||||
r.inset (focusWidth, focusWidth);
|
||||
outPath.addRect (r);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,197 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../optional.h"
|
||||
#include "../enumbitset.h"
|
||||
#include "ccontrol.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** Control which draws a list of configurable rows
|
||||
*
|
||||
* This control needs to be setup with an instance of a IListControlDrawer and a
|
||||
* IListControlConfigurator. The number of rows is configured via the min and max values. And the
|
||||
* selected row is the value of this control.
|
||||
* The actual drawing is done via the IListControlDrawer instance. And the row configuration is
|
||||
* handled via the IListControlConfigurator instance. Every row can have different heights and
|
||||
* flags.
|
||||
*
|
||||
* @ingroup new_in_4_9
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
class CListControl final : public CControl
|
||||
{
|
||||
public:
|
||||
CListControl (const CRect& size, IControlListener* listener = nullptr, int32_t tag = -1);
|
||||
~CListControl () override;
|
||||
|
||||
void setDrawer (IListControlDrawer* d);
|
||||
void setConfigurator (IListControlConfigurator* c);
|
||||
|
||||
IListControlDrawer* getDrawer () const;
|
||||
IListControlConfigurator* getConfigurator () const;
|
||||
|
||||
void recalculateLayout ();
|
||||
|
||||
void invalidRow (int32_t row);
|
||||
Optional<int32_t> getRowAtPoint (CPoint where) const;
|
||||
Optional<CRect> getRowRect (int32_t row) const;
|
||||
Optional<int32_t> getHoveredRow () const;
|
||||
|
||||
int32_t getIntValue () const;
|
||||
int32_t getNumRows () const;
|
||||
|
||||
// overrides
|
||||
void setMin (float val) override;
|
||||
void setMax (float val) override;
|
||||
|
||||
bool attached (CView* parent) override;
|
||||
void draw (CDrawContext* context) override;
|
||||
void drawRect (CDrawContext* context, const CRect& updateRect) override;
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseExited (CPoint& where, const CButtonState& buttons) override;
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
void setViewSize (const CRect& rect, bool invalid = true) override;
|
||||
bool drawFocusOnTop () override;
|
||||
bool getFocusPath (CGraphicsPath& outPath) override;
|
||||
|
||||
CLASS_METHODS_NOCOPY (CListControl, CControl)
|
||||
private:
|
||||
int32_t getNextSelectableRow (int32_t r, int32_t direction) const;
|
||||
int32_t getMinRowIndex () const;
|
||||
int32_t getMaxRowIndex () const;
|
||||
size_t getNormalizedRowIndex (int32_t row) const;
|
||||
bool rowSelectable (int32_t row) const;
|
||||
void clearHoveredRow ();
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** The description of one row for the CListControl
|
||||
*
|
||||
* This is returned by an instance of IListControlConfigurator for every row.
|
||||
*
|
||||
* @ingroup new_in_4_9
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
struct CListControlRowDesc
|
||||
{
|
||||
enum Flag : uint32_t
|
||||
{
|
||||
/** Indicates that the row is selectable */
|
||||
Selectable = 1 << 0,
|
||||
/** Indicates that the row should be redrawn when the mouse hovers it */
|
||||
Hoverable = 1 << 1,
|
||||
};
|
||||
using Flags = EnumBitset<Flag, true>;
|
||||
|
||||
/** The height of the row */
|
||||
CCoord height {0};
|
||||
/** The flags of the row, see the Flags enum above */
|
||||
Flags flags {Selectable};
|
||||
|
||||
CListControlRowDesc () = default;
|
||||
CListControlRowDesc (CCoord h, Flags f) : height (h), flags (f) {}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** The list control drawer interface
|
||||
*
|
||||
* This is used to do the actual drawing of the list control.
|
||||
*
|
||||
* @ingroup new_in_4_9
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
class IListControlDrawer : virtual public IReference
|
||||
{
|
||||
public:
|
||||
virtual ~IListControlDrawer () noexcept {}
|
||||
|
||||
struct Row
|
||||
{
|
||||
enum Flag : uint32_t
|
||||
{
|
||||
Selectable = 1 << 0,
|
||||
Selected = 1 << 1,
|
||||
Hovered = 1 << 2,
|
||||
LastRow = 1 << 3,
|
||||
};
|
||||
using Flags = EnumBitset<Flag, true>;
|
||||
|
||||
operator int32_t () const { return getIndex (); }
|
||||
int32_t getIndex () const { return index; }
|
||||
|
||||
bool isSelectable () const { return (flags & Selectable) != 0; }
|
||||
bool isSelected () const { return (flags & Selected) != 0; }
|
||||
bool isHovered () const { return (flags & Hovered) != 0; }
|
||||
bool isLastRow () const { return (flags & LastRow) != 0; }
|
||||
|
||||
Row (int32_t index, Flags flags) : index (index), flags (flags) {}
|
||||
|
||||
private:
|
||||
int32_t index;
|
||||
Flags flags;
|
||||
};
|
||||
|
||||
virtual void drawBackground (CDrawContext* context, CRect size) = 0;
|
||||
virtual void drawRow (CDrawContext* context, CRect size, Row row) = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** The list control configurator interface
|
||||
*
|
||||
* @ingroup new_in_4_9
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
class IListControlConfigurator : virtual public IReference
|
||||
{
|
||||
public:
|
||||
virtual ~IListControlConfigurator () noexcept {}
|
||||
|
||||
virtual CListControlRowDesc getRowDesc (int32_t row) const = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** A list control configurator implementation.
|
||||
*
|
||||
* Returns the same row description for all row indices
|
||||
*
|
||||
* @ingroup new_in_4_9
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
class StaticListControlConfigurator : public IListControlConfigurator,
|
||||
public NonAtomicReferenceCounted
|
||||
{
|
||||
public:
|
||||
using Flags = CListControlRowDesc::Flags;
|
||||
|
||||
StaticListControlConfigurator (CCoord inRowHeight,
|
||||
Flags inFlags = {CListControlRowDesc::Selectable,
|
||||
CListControlRowDesc::Hoverable})
|
||||
: rowHeight (inRowHeight), flags (inFlags)
|
||||
{
|
||||
}
|
||||
|
||||
void setRowHeight (CCoord height) { rowHeight = height; }
|
||||
void setFlags (Flags f) { flags = f; }
|
||||
CCoord getRowHeight () const { return rowHeight; }
|
||||
Flags getFlags () const { return flags; }
|
||||
|
||||
CListControlRowDesc getRowDesc (int32_t row) const override { return {rowHeight, flags}; }
|
||||
|
||||
private:
|
||||
CCoord rowHeight;
|
||||
Flags flags;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,133 @@
|
||||
// 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 "cmoviebitmap.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include "../cbitmap.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
//------------------------------------------------------------------------
|
||||
bool CMovieBitmap::useLegacyFrameCalculation = false;
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CMovieBitmap
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CMovieBitmap constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param background bitmap
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CMovieBitmap::CMovieBitmap (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
CBitmap* background)
|
||||
: CControl (size, listener, tag, background)
|
||||
{
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
setHeightOfOneImage (size.getHeight ());
|
||||
setNumSubPixmaps (background ? (int32_t)(background->getHeight () / heightOfOneImage) : 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CMovieBitmap constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param subPixmaps number of subPixmaps
|
||||
* @param heightOfOneImage height of one image in pixel
|
||||
* @param background bitmap
|
||||
* @param offset
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CMovieBitmap::CMovieBitmap (const CRect& size, IControlListener* listener, int32_t tag, int32_t subPixmaps, CCoord heightOfOneImage, CBitmap* background, const CPoint &offset)
|
||||
: CControl (size, listener, tag, background)
|
||||
, offset (offset)
|
||||
{
|
||||
setNumSubPixmaps (subPixmaps);
|
||||
setHeightOfOneImage (heightOfOneImage);
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMovieBitmap::CMovieBitmap (const CMovieBitmap& v) : CControl (v)
|
||||
{
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
offset = v.offset;
|
||||
setNumSubPixmaps (v.subPixmaps);
|
||||
setHeightOfOneImage (v.heightOfOneImage);
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMovieBitmap::draw (CDrawContext *pContext)
|
||||
{
|
||||
if (auto bitmap = getDrawBackground ())
|
||||
{
|
||||
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
|
||||
{
|
||||
auto frameIndex = getMultiFrameBitmapIndex (*mfb, getValueNormalized ());
|
||||
mfb->drawFrame (pContext, frameIndex, getViewSize ().getTopLeft ());
|
||||
}
|
||||
else
|
||||
{
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
#include "../private/disabledeprecatedmessage.h"
|
||||
CPoint where (offset.x, offset.y);
|
||||
if (useLegacyFrameCalculation)
|
||||
{
|
||||
where.y += heightOfOneImage *
|
||||
(int32_t)(getValueNormalized () * (getNumSubPixmaps () - 1) + 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto step = static_cast<int32_t> (std::min (
|
||||
getNumSubPixmaps () - 1.f, getValueNormalized () * getNumSubPixmaps ()));
|
||||
where.y += heightOfOneImage * step;
|
||||
}
|
||||
|
||||
bitmap->draw (pContext, getViewSize (), where);
|
||||
#include "../private/enabledeprecatedmessage.h"
|
||||
|
||||
#else
|
||||
bitmap->draw (pContext, getViewSize ());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool CMovieBitmap::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 (getHeightOfOneImage ());
|
||||
#else
|
||||
vs.setHeight (bitmap->getHeight ());
|
||||
#endif
|
||||
vs.setWidth (bitmap->getWidth ());
|
||||
}
|
||||
setViewSize (vs);
|
||||
setMouseableArea (vs);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ccontrol.h"
|
||||
#include "../cbitmap.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CMovieBitmap Declaration
|
||||
//! @brief a bitmap view that displays different bitmaps according to its current value
|
||||
///
|
||||
/// Use a CMultiFrameBitmap for its background bitmap.
|
||||
///
|
||||
/// @ingroup views uses_multi_frame_bitmaps
|
||||
//-----------------------------------------------------------------------------
|
||||
class CMovieBitmap : public CControl,
|
||||
public MultiFrameBitmapView<CMovieBitmap>
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
,
|
||||
public IMultiBitmapControl
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
CMovieBitmap (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background);
|
||||
CMovieBitmap (const CMovieBitmap& movieBitmap);
|
||||
|
||||
void draw (CDrawContext*) override;
|
||||
bool sizeToFit () override;
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CMovieBitmap (const CRect& size, IControlListener* listener, int32_t tag, int32_t subPixmaps,
|
||||
CCoord heightOfOneImage, CBitmap* background,
|
||||
const CPoint& offset = CPoint (0, 0));
|
||||
void setNumSubPixmaps (int32_t numSubPixmaps) override { IMultiBitmapControl::setNumSubPixmaps (numSubPixmaps); invalid (); }
|
||||
#endif
|
||||
|
||||
VSTGUI_DEPRECATED_MSG (static bool useLegacyFrameCalculation;
|
||||
, "Use CMultiFrameBitmap::normalizedValueToFrameIndex() instead")
|
||||
|
||||
CLASS_METHODS(CMovieBitmap, CControl)
|
||||
protected:
|
||||
~CMovieBitmap () noexcept override = default;
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CPoint offset {};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,189 @@
|
||||
// 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 "cmoviebutton.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include "../cbitmap.h"
|
||||
#include "../events.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CMovieButton
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CMovieButton constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param background bitmap
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CMovieButton::CMovieButton (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
CBitmap* background)
|
||||
: CControl (size, listener, tag, background), buttonState (value)
|
||||
{
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
heightOfOneImage = size.getHeight ();
|
||||
#endif
|
||||
setWantsFocus (true);
|
||||
}
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CMovieButton constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param heightOfOneImage height of one image in pixel
|
||||
* @param background bitmap
|
||||
* @param offset
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CMovieButton::CMovieButton (const CRect& size, IControlListener* listener, int32_t tag, CCoord heightOfOneImage, CBitmap* background, const CPoint &offset)
|
||||
: CControl (size, listener, tag, background)
|
||||
, offset (offset)
|
||||
, buttonState (value)
|
||||
{
|
||||
setHeightOfOneImage (heightOfOneImage);
|
||||
setWantsFocus (true);
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMovieButton::CMovieButton (const CMovieButton& v) : CControl (v), buttonState (v.buttonState)
|
||||
{
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
offset = v.offset;
|
||||
setHeightOfOneImage (v.heightOfOneImage);
|
||||
#endif
|
||||
setWantsFocus (true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMovieButton::draw (CDrawContext *pContext)
|
||||
{
|
||||
if (auto bitmap = getDrawBackground ())
|
||||
{
|
||||
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
|
||||
{
|
||||
auto frameIndex = getMultiFrameBitmapIndex (*mfb, getValueNormalized ());
|
||||
mfb->drawFrame (pContext, frameIndex, getViewSize ().getTopLeft ());
|
||||
}
|
||||
else
|
||||
{
|
||||
CPoint where {};
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
if (value == getMax ())
|
||||
where.y = heightOfOneImage;
|
||||
#endif
|
||||
bitmap->draw (pContext, getViewSize (), where);
|
||||
}
|
||||
}
|
||||
buttonState = value;
|
||||
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CMovieButton::onMouseDown (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (!(buttons & kLButton))
|
||||
return kMouseEventNotHandled;
|
||||
fEntryState = value;
|
||||
beginEdit ();
|
||||
return onMouseMoved (where, buttons);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CMovieButton::onMouseUp (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (isEditing ())
|
||||
endEdit ();
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CMovieButton::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 == getMax ()) ? getMin () : getMax ();
|
||||
else
|
||||
value = fEntryState;
|
||||
|
||||
if (isDirty ())
|
||||
{
|
||||
valueChanged ();
|
||||
invalid ();
|
||||
}
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
return kMouseEventNotHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CMovieButton::onMouseCancel ()
|
||||
{
|
||||
if (isEditing ())
|
||||
{
|
||||
value = fEntryState;
|
||||
if (isDirty ())
|
||||
{
|
||||
valueChanged ();
|
||||
invalid ();
|
||||
}
|
||||
endEdit ();
|
||||
}
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMovieButton::onKeyboardEvent (KeyboardEvent& event)
|
||||
{
|
||||
if (event.type != EventType::KeyDown || event.modifiers.empty () == false)
|
||||
return;
|
||||
if (event.virt == VirtualKey::Return)
|
||||
{
|
||||
value = (value == getMax ()) ? getMin () : getMax ();
|
||||
invalid ();
|
||||
beginEdit ();
|
||||
valueChanged ();
|
||||
endEdit ();
|
||||
event.consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool CMovieButton::sizeToFit ()
|
||||
{
|
||||
if (auto bitmap = getDrawBackground ())
|
||||
{
|
||||
CRect vs (getViewSize ());
|
||||
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
|
||||
{
|
||||
vs.setSize (mfb->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;
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,59 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ccontrol.h"
|
||||
#include "../cbitmap.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CMovieButton Declaration
|
||||
//! @brief a bi-states button with 2 subbitmaps
|
||||
///
|
||||
/// Use a CMultiFrameBitmap for its background bitmap.
|
||||
///
|
||||
/// @ingroup controls uses_multi_frame_bitmaps
|
||||
//-----------------------------------------------------------------------------
|
||||
class CMovieButton : public CControl,
|
||||
public MultiFrameBitmapView<CMovieButton>
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
,
|
||||
public IMultiBitmapControl
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
CMovieButton (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background);
|
||||
CMovieButton (const CMovieButton& movieButton);
|
||||
|
||||
void draw (CDrawContext*) override;
|
||||
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseCancel () override;
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
bool sizeToFit () override;
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CMovieButton (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
CCoord heightOfOneImage, CBitmap* background,
|
||||
const CPoint& offset = CPoint (0, 0));
|
||||
void setNumSubPixmaps (int32_t numSubPixmaps) override { IMultiBitmapControl::setNumSubPixmaps (numSubPixmaps); invalid (); }
|
||||
#endif
|
||||
|
||||
CLASS_METHODS(CMovieButton, CControl)
|
||||
protected:
|
||||
~CMovieButton () noexcept override = default;
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CPoint offset {};
|
||||
#endif
|
||||
float buttonState;
|
||||
|
||||
private:
|
||||
float fEntryState;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,918 @@
|
||||
// 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 "coptionmenu.h"
|
||||
#include "../cbitmap.h"
|
||||
#include "../cframe.h"
|
||||
#include "../cstring.h"
|
||||
#include "../events.h"
|
||||
|
||||
#include "../platform/iplatformoptionmenu.h"
|
||||
#include "../platform/iplatformframe.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct CMenuItem::Impl
|
||||
{
|
||||
UTF8String title;
|
||||
UTF8String keyCode;
|
||||
SharedPointer<COptionMenu> submenu;
|
||||
SharedPointer<CBitmap> icon;
|
||||
int32_t flags {0};
|
||||
int32_t keyModifiers {0};
|
||||
VirtualKey virtualKey {VirtualKey::None};
|
||||
int32_t tag {-1};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CMenuItem
|
||||
//------------------------------------------------------------------------
|
||||
/*! @class CMenuItem
|
||||
Defines an item of a VSTGUI::COptionMenu
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMenuItem::CMenuItem ()
|
||||
{
|
||||
impl = std::make_unique<Impl> ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMenuItem::~CMenuItem () noexcept = default;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CMenuItem constructor.
|
||||
* @param inTitle title of item
|
||||
* @param inFlags CMenuItem::Flags of item
|
||||
* @param inKeycode keycode of item
|
||||
* @param inKeyModifiers keymodifiers of item
|
||||
* @param inIcon icon of item
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CMenuItem::CMenuItem (const UTF8String& inTitle, const UTF8String& inKeycode, int32_t inKeyModifiers, CBitmap* inIcon, int32_t inFlags)
|
||||
: CMenuItem ()
|
||||
{
|
||||
impl->flags = inFlags;
|
||||
setTitle (inTitle);
|
||||
setKey (inKeycode, inKeyModifiers);
|
||||
setIcon (inIcon);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CMenuItem constructor.
|
||||
* @param inTitle title of item
|
||||
* @param inSubmenu submenu of item
|
||||
* @param inIcon icon of item
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CMenuItem::CMenuItem (const UTF8String& inTitle, COptionMenu* inSubmenu, CBitmap* inIcon)
|
||||
: CMenuItem ()
|
||||
{
|
||||
setTitle (inTitle);
|
||||
setSubmenu (inSubmenu);
|
||||
setIcon (inIcon);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CMenuItem constructor.
|
||||
* @param inTitle title of item
|
||||
* @param inTag tag of item
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CMenuItem::CMenuItem (const UTF8String& inTitle, int32_t inTag)
|
||||
: CMenuItem ()
|
||||
{
|
||||
setTitle (inTitle);
|
||||
setTag (inTag);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CMenuItem copy constructor.
|
||||
* @param item item to copy
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CMenuItem::CMenuItem (const CMenuItem& item)
|
||||
: CMenuItem ()
|
||||
{
|
||||
impl->flags = item.impl->flags;
|
||||
setTitle (item.getTitle ());
|
||||
setIcon (item.getIcon ());
|
||||
if (item.getVirtualKey () != VirtualKey::None)
|
||||
setVirtualKey (item.getVirtualKey (), item.getKeyModifiers ());
|
||||
else
|
||||
setKey (item.getKeycode (), item.getKeyModifiers ());
|
||||
setTag (item.getTag ());
|
||||
setSubmenu (item.getSubmenu ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMenuItem::setTitle (const UTF8String& inTitle)
|
||||
{
|
||||
impl->title = inTitle;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMenuItem::setKey (const UTF8String& inKeycode, int32_t inKeyModifiers)
|
||||
{
|
||||
impl->keyCode = inKeycode;
|
||||
impl->keyModifiers = inKeyModifiers;
|
||||
impl->virtualKey = VirtualKey::None;
|
||||
}
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
//------------------------------------------------------------------------
|
||||
void CMenuItem::setVirtualKey (int32_t inVirtualKeyCode, int32_t inKeyModifiers)
|
||||
{
|
||||
setKey (nullptr, inKeyModifiers);
|
||||
impl->virtualKey = fromVstVirtualKey (inVirtualKeyCode);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
int32_t CMenuItem::getVirtualKeyCode () const
|
||||
{
|
||||
return toVstVirtualKey (impl->virtualKey);
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMenuItem::setVirtualKey (VirtualKey inVirtualKey, int32_t inKeyModifiers)
|
||||
{
|
||||
setKey (nullptr, inKeyModifiers);
|
||||
impl->virtualKey = inVirtualKey;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMenuItem::setSubmenu (COptionMenu* inSubmenu)
|
||||
{
|
||||
impl->submenu = inSubmenu;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMenuItem::setIcon (CBitmap* inIcon)
|
||||
{
|
||||
impl->icon = inIcon;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMenuItem::setTag (int32_t t)
|
||||
{
|
||||
impl->tag = t;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMenuItem::setEnabled (bool state)
|
||||
{
|
||||
setBit (impl->flags, kDisabled, !state);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMenuItem::setChecked (bool state)
|
||||
{
|
||||
setBit (impl->flags, kChecked, state);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMenuItem::setIsTitle (bool state)
|
||||
{
|
||||
setBit (impl->flags, kTitle, state);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMenuItem::setIsSeparator (bool state)
|
||||
{
|
||||
setBit (impl->flags, kSeparator, state);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CMenuItem::isEnabled () const
|
||||
{
|
||||
return !hasBit (impl->flags, kDisabled);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CMenuItem::isChecked () const
|
||||
{
|
||||
return hasBit (impl->flags, kChecked);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CMenuItem::isTitle () const
|
||||
{
|
||||
return hasBit (impl->flags, kTitle);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CMenuItem::isSeparator () const
|
||||
{
|
||||
return hasBit (impl->flags, kSeparator);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const UTF8String& CMenuItem::getTitle () const
|
||||
{
|
||||
return impl->title;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
int32_t CMenuItem::getKeyModifiers () const
|
||||
{
|
||||
return impl->keyModifiers;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const UTF8String& CMenuItem::getKeycode () const
|
||||
{
|
||||
return impl->keyCode;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
VirtualKey CMenuItem::getVirtualKey () const
|
||||
{
|
||||
return impl->virtualKey;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
COptionMenu* CMenuItem::getSubmenu () const
|
||||
{
|
||||
return impl->submenu;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CBitmap* CMenuItem::getIcon () const
|
||||
{
|
||||
return impl->icon;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
int32_t CMenuItem::getTag () const
|
||||
{
|
||||
return impl->tag;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/*! @class CCommandMenuItem
|
||||
|
||||
The CCommandMenuItem supports setting a category, name and a target. The target will get a @link CBaseObject::notify notify()@endlink call before the item is
|
||||
displayed and after it was selected. @see CCommandMenuItem::kMsgMenuItemValidate and @see CCommandMenuItem::kMsgMenuItemSelected
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CCommandMenuItem::CCommandMenuItem (Desc&& args)
|
||||
: CMenuItem (args.title, args.keycode, args.keyModifiers, args.icon, args.flags)
|
||||
, commandCategory (std::move (args.commandCategory))
|
||||
, commandName (std::move (args.commandName))
|
||||
, itemTarget (std::move (args.target))
|
||||
{
|
||||
setTag (args.tag);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CCommandMenuItem::CCommandMenuItem (const Desc& args)
|
||||
: CMenuItem (args.title, args.keycode, args.keyModifiers, args.icon, args.flags)
|
||||
, commandCategory (args.commandCategory)
|
||||
, commandName (args.commandName)
|
||||
, itemTarget (args.target)
|
||||
{
|
||||
setTag (args.tag);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CCommandMenuItem::CCommandMenuItem (const CCommandMenuItem& item)
|
||||
: CMenuItem (item)
|
||||
, validateFunc (item.validateFunc)
|
||||
, selectedFunc (item.selectedFunc)
|
||||
, commandCategory (item.commandCategory)
|
||||
, commandName (item.commandName)
|
||||
{
|
||||
setItemTarget (item.itemTarget);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CCommandMenuItem::setItemTarget (ICommandMenuItemTarget* target)
|
||||
{
|
||||
itemTarget = target;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CCommandMenuItem::setCommandCategory (const UTF8String& category)
|
||||
{
|
||||
commandCategory = category;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CCommandMenuItem::isCommandCategory (const UTF8String& category) const
|
||||
{
|
||||
return commandCategory == category;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CCommandMenuItem::setCommandName (const UTF8String& name)
|
||||
{
|
||||
commandName = name;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CCommandMenuItem::isCommandName (const UTF8String& name) const
|
||||
{
|
||||
return commandName == name;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CCommandMenuItem::setActions (SelectedCallbackFunction&& selected, ValidateCallbackFunction&& validate)
|
||||
{
|
||||
selectedFunc = std::move (selected);
|
||||
validateFunc = std::move (validate);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CCommandMenuItem::execute ()
|
||||
{
|
||||
if (selectedFunc)
|
||||
selectedFunc (this);
|
||||
|
||||
if (itemTarget)
|
||||
itemTarget->onCommandMenuItemSelected (this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CCommandMenuItem::validate ()
|
||||
{
|
||||
if (validateFunc)
|
||||
validateFunc (this);
|
||||
|
||||
if (itemTarget)
|
||||
itemTarget->validateCommandMenuItem (this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// COptionMenu
|
||||
//------------------------------------------------------------------------
|
||||
/*! @class COptionMenu
|
||||
Define a rectangle view where a text-value can be displayed with a given font and color.
|
||||
The text-value is centered in the given rect.
|
||||
A bitmap can be used as background, a second bitmap can be used when the option menu is popuped.
|
||||
There are 2 styles with or without a shadowed text. When a mouse click occurs, a popup menu is displayed.
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* COptionMenu constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param background the background bitmap
|
||||
* @param bgWhenClick the background bitmap if the option menu is displayed
|
||||
* @param style the style of the display (see CParamDisplay for styles)
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
COptionMenu::COptionMenu (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background, CBitmap* bgWhenClick, const int32_t style)
|
||||
: CParamDisplay (size, background, style)
|
||||
, bgWhenClick (bgWhenClick)
|
||||
{
|
||||
this->listener = listener;
|
||||
this->tag = tag;
|
||||
|
||||
lastButton = kRButton;
|
||||
|
||||
menuItems = new CMenuItemList;
|
||||
setWantsFocus (true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
COptionMenu::COptionMenu ()
|
||||
: CParamDisplay (CRect (0, 0, 0, 0))
|
||||
{
|
||||
menuItems = new CMenuItemList;
|
||||
setWantsFocus (true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
COptionMenu::COptionMenu (const COptionMenu& v)
|
||||
: CParamDisplay (v)
|
||||
, menuItems (new CMenuItemList (*v.menuItems))
|
||||
, nbItemsPerColumn (v.nbItemsPerColumn)
|
||||
, bgWhenClick (v.bgWhenClick)
|
||||
{
|
||||
setWantsFocus (true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
COptionMenu::~COptionMenu () noexcept
|
||||
{
|
||||
removeAllEntry ();
|
||||
|
||||
delete menuItems;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void COptionMenu::registerOptionMenuListener (IOptionMenuListener* listener)
|
||||
{
|
||||
if (!listeners)
|
||||
listeners = std::unique_ptr<MenuListenerList> (new MenuListenerList ());
|
||||
listeners->add (listener);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void COptionMenu::unregisterOptionMenuListener (IOptionMenuListener* listener)
|
||||
{
|
||||
if (listeners)
|
||||
listeners->remove (listener);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void COptionMenu::onKeyboardEvent (KeyboardEvent& event)
|
||||
{
|
||||
if (event.type != EventType::KeyUp && event.modifiers.empty () && event.character == 0)
|
||||
{
|
||||
if (event.virt == VirtualKey::Return)
|
||||
{
|
||||
auto self = shared (this);
|
||||
getFrame ()->doAfterEventProcessing ([self] () {
|
||||
self->doPopup ();
|
||||
});
|
||||
event.consumed = true;
|
||||
return;
|
||||
}
|
||||
if (!(style & (kMultipleCheckStyle & ~kCheckStyle)))
|
||||
{
|
||||
if (event.virt == VirtualKey::Up)
|
||||
{
|
||||
int32_t value = (int32_t)getValue ()-1;
|
||||
if (value >= 0)
|
||||
{
|
||||
CMenuItem* entry = getEntry (value);
|
||||
while (entry && (entry->isSeparator () || entry->isTitle () || !entry->isEnabled () || entry->getSubmenu ()))
|
||||
entry = getEntry (--value);
|
||||
if (entry)
|
||||
{
|
||||
beginEdit ();
|
||||
setValue ((float)value);
|
||||
lastResult = (int32_t)getValue ();
|
||||
valueChanged ();
|
||||
endEdit ();
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
event.consumed = true;
|
||||
return;
|
||||
}
|
||||
if (event.virt == VirtualKey::Down)
|
||||
{
|
||||
int32_t value = (int32_t)getValue ()+1;
|
||||
if (value < getNbEntries ())
|
||||
{
|
||||
CMenuItem* entry = getEntry (value);
|
||||
while (entry && (entry->isSeparator () || entry->isTitle () || !entry->isEnabled () || entry->getSubmenu ()))
|
||||
entry = getEntry (++value);
|
||||
if (entry)
|
||||
{
|
||||
beginEdit ();
|
||||
setValue ((float)value);
|
||||
lastResult = (int32_t)getValue ();
|
||||
valueChanged ();
|
||||
endEdit ();
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
event.consumed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
CParamDisplay::onKeyboardEvent (event);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void COptionMenu::beforePopup ()
|
||||
{
|
||||
if (listeners)
|
||||
listeners->forEach ([this] (IOptionMenuListener* l) { l->onOptionMenuPrePopup (this); });
|
||||
for (auto& menuItem : *menuItems)
|
||||
{
|
||||
if (auto* commandItem = menuItem.cast<CCommandMenuItem> ())
|
||||
commandItem->validate ();
|
||||
if (menuItem->getSubmenu ())
|
||||
menuItem->getSubmenu ()->beforePopup ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void COptionMenu::afterPopup ()
|
||||
{
|
||||
for (auto& menuItem : *menuItems)
|
||||
{
|
||||
if (menuItem->getSubmenu ())
|
||||
menuItem->getSubmenu ()->afterPopup ();
|
||||
}
|
||||
if (listeners)
|
||||
listeners->forEach ([this] (IOptionMenuListener* l) { l->onOptionMenuPostPopup (this); });
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool COptionMenu::doPopup ()
|
||||
{
|
||||
if (bgWhenClick)
|
||||
invalid ();
|
||||
auto result = popup ();
|
||||
if (bgWhenClick)
|
||||
invalid ();
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool COptionMenu::popup (const PopupCallback& callback)
|
||||
{
|
||||
if (!getFrame ())
|
||||
return false;
|
||||
|
||||
beforePopup ();
|
||||
|
||||
lastResult = -1;
|
||||
lastMenu = nullptr;
|
||||
|
||||
if (!menuItems->empty ())
|
||||
{
|
||||
getFrame ()->onStartLocalEventLoop ();
|
||||
if (auto platformMenu = getFrame ()->getPlatformFrame ()->createPlatformOptionMenu ())
|
||||
{
|
||||
inPopup = true;
|
||||
auto self = shared (this);
|
||||
platformMenu->popup (this, [self, callback] (COptionMenu* menu, PlatformOptionMenuResult result) {
|
||||
if (result.menu != nullptr)
|
||||
{
|
||||
bool preventSettingValue = false;
|
||||
if (self->listeners)
|
||||
{
|
||||
self->listeners->forEach (
|
||||
[self, &result] (IOptionMenuListener* l) {
|
||||
return l->onOptionMenuSetPopupResult (self, result.menu,
|
||||
result.index);
|
||||
},
|
||||
[&preventSettingValue] (bool result) {
|
||||
if (result)
|
||||
preventSettingValue = true;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
if (!preventSettingValue)
|
||||
{
|
||||
self->beginEdit ();
|
||||
self->lastMenu = result.menu;
|
||||
self->lastResult = result.index;
|
||||
self->lastMenu->setValue (static_cast<float> (self->lastResult));
|
||||
self->valueChanged ();
|
||||
self->invalid ();
|
||||
if (auto commandItem = dynamic_cast<CCommandMenuItem*> (
|
||||
self->lastMenu->getEntry (self->lastResult)))
|
||||
commandItem->execute ();
|
||||
self->endEdit ();
|
||||
}
|
||||
}
|
||||
self->afterPopup ();
|
||||
if (callback)
|
||||
callback (self);
|
||||
self->inPopup = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool COptionMenu::popup (CFrame* frame, const CPoint& frameLocation, const PopupCallback& callback)
|
||||
{
|
||||
if (frame == nullptr || menuItems->empty ())
|
||||
return false;
|
||||
if (isAttached ())
|
||||
return false;
|
||||
CView* oldFocusView = frame->getFocusView ();
|
||||
CRect size (frameLocation, CPoint (0, 0));
|
||||
setViewSize (size);
|
||||
frame->addView (this);
|
||||
|
||||
auto prevFocusView = shared (oldFocusView);
|
||||
popup ([prevFocusView, callback] (COptionMenu* menu) {
|
||||
if (auto frame = menu->getFrame ())
|
||||
{
|
||||
frame->removeView (menu, false);
|
||||
frame->setFocusView (prevFocusView);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the selected menu item is a command menu and the command menu has removed this
|
||||
// option menu from the view hierarchy then we have to make sure the reference count is
|
||||
// corrected
|
||||
menu->remember ();
|
||||
}
|
||||
if (callback)
|
||||
callback (menu);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void COptionMenu::cleanupSeparators (bool deep)
|
||||
{
|
||||
if (getItems ()->empty ())
|
||||
return;
|
||||
|
||||
std::list<int32_t>indicesToRemove;
|
||||
bool lastEntryWasSeparator = true;
|
||||
for (auto i = 0; i < getNbEntries () - 1; ++i)
|
||||
{
|
||||
auto entry = getEntry (i);
|
||||
vstgui_assert (entry);
|
||||
if (!entry)
|
||||
continue;
|
||||
|
||||
if (entry->isSeparator ())
|
||||
{
|
||||
if (lastEntryWasSeparator)
|
||||
{
|
||||
indicesToRemove.push_front (i);
|
||||
}
|
||||
lastEntryWasSeparator = true;
|
||||
}
|
||||
else
|
||||
lastEntryWasSeparator = false;
|
||||
if (deep)
|
||||
{
|
||||
if (auto subMenu = entry->getSubmenu ())
|
||||
{
|
||||
subMenu->cleanupSeparators (deep);
|
||||
}
|
||||
}
|
||||
}
|
||||
auto lastIndex = getNbEntries () - 1;
|
||||
if (getEntry (lastIndex)->isSeparator ())
|
||||
{
|
||||
indicesToRemove.push_front (lastIndex);
|
||||
}
|
||||
|
||||
for (auto index : indicesToRemove)
|
||||
{
|
||||
removeEntry (index);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void COptionMenu::setPrefixNumbers (int32_t preCount)
|
||||
{
|
||||
if (preCount >= 0 && preCount <= 4)
|
||||
prefixNumbers = preCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param item menu item to add. Takes ownership of item.
|
||||
* @param index position of insertation. -1 appends the item
|
||||
*/
|
||||
//-----------------------------------------------------------------------------
|
||||
CMenuItem* COptionMenu::addEntry (CMenuItem* item, int32_t index)
|
||||
{
|
||||
if (index < 0 || index > getNbEntries ())
|
||||
menuItems->emplace_back (owned (item));
|
||||
else
|
||||
{
|
||||
menuItems->insert (menuItems->begin () + index, owned (item));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CMenuItem* COptionMenu::addEntry (COptionMenu* submenu, const UTF8String& title)
|
||||
{
|
||||
auto* item = new CMenuItem (title, submenu);
|
||||
return addEntry (item);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CMenuItem* COptionMenu::addEntry (const UTF8String& title, int32_t index, int32_t itemFlags)
|
||||
{
|
||||
if (title == "-")
|
||||
return addSeparator (index);
|
||||
auto* item = new CMenuItem (title, nullptr, 0, nullptr, itemFlags);
|
||||
return addEntry (item, index);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CMenuItem* COptionMenu::addSeparator (int32_t index)
|
||||
{
|
||||
auto* item = new CMenuItem ("", nullptr, 0, nullptr, CMenuItem::kSeparator);
|
||||
return addEntry (item, index);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CMenuItem* COptionMenu::getCurrent () const
|
||||
{
|
||||
return getEntry (currentIndex);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CMenuItem* COptionMenu::getEntry (int32_t index) const
|
||||
{
|
||||
if (index < 0 || menuItems->empty () || index >= getNbEntries ())
|
||||
return nullptr;
|
||||
|
||||
return (*menuItems)[static_cast<size_t> (index)];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
int32_t COptionMenu::getNbEntries () const
|
||||
{
|
||||
return static_cast<int32_t> (menuItems->size ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
COptionMenu* COptionMenu::getSubMenu (int32_t idx) const
|
||||
{
|
||||
CMenuItem* item = getEntry (idx);
|
||||
if (item)
|
||||
return item->getSubmenu ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
int32_t COptionMenu::getCurrentIndex (bool countSeparator) const
|
||||
{
|
||||
if (countSeparator)
|
||||
return currentIndex;
|
||||
int32_t i = 0;
|
||||
int32_t numSeparators = 0;
|
||||
for (auto& item : *menuItems)
|
||||
{
|
||||
if (item->isSeparator ())
|
||||
numSeparators++;
|
||||
if (i == currentIndex)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
return currentIndex - numSeparators;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool COptionMenu::setCurrent (int32_t index, bool countSeparator)
|
||||
{
|
||||
CMenuItem* item = nullptr;
|
||||
if (countSeparator)
|
||||
{
|
||||
item = getEntry (index);
|
||||
if (!item || item->isSeparator ())
|
||||
return false;
|
||||
currentIndex = index;
|
||||
}
|
||||
else
|
||||
{
|
||||
int32_t i = 0;
|
||||
for (auto& menuItem : *menuItems)
|
||||
{
|
||||
if (i > index)
|
||||
break;
|
||||
if (menuItem->isSeparator ())
|
||||
index++;
|
||||
i++;
|
||||
}
|
||||
currentIndex = index;
|
||||
item = getEntry (currentIndex);
|
||||
}
|
||||
if (item && style & (kMultipleCheckStyle & ~kCheckStyle))
|
||||
item->setChecked (!item->isChecked ());
|
||||
|
||||
// to force the redraw
|
||||
setDirty ();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool COptionMenu::removeEntry (int32_t index)
|
||||
{
|
||||
if (index < 0 || menuItems->empty () || index >= getNbEntries ())
|
||||
return false;
|
||||
menuItems->erase (menuItems->begin () + index);
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool COptionMenu::removeAllEntry ()
|
||||
{
|
||||
menuItems->clear ();
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool COptionMenu::checkEntry (int32_t index, bool state)
|
||||
{
|
||||
CMenuItem* item = getEntry (index);
|
||||
if (item)
|
||||
{
|
||||
item->setChecked (state);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool COptionMenu::checkEntryAlone (int32_t index)
|
||||
{
|
||||
int32_t pos = 0;
|
||||
for (auto& item : *menuItems)
|
||||
{
|
||||
item->setChecked (pos == index);
|
||||
pos++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool COptionMenu::isCheckEntry (int32_t index) const
|
||||
{
|
||||
CMenuItem* item = getEntry (index);
|
||||
if (item && item->isChecked ())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void COptionMenu::draw (CDrawContext *pContext)
|
||||
{
|
||||
CMenuItem* item = getEntry (currentIndex);
|
||||
drawBack (pContext, inPopup ? bgWhenClick : nullptr);
|
||||
if (item)
|
||||
drawPlatformText (pContext, item->getTitle ());
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult COptionMenu::onMouseDown (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
lastButton = buttons;
|
||||
if (lastButton & (kLButton|kRButton|kApple))
|
||||
{
|
||||
auto self = shared (this);
|
||||
getFrame ()->doAfterEventProcessing ([self] () {
|
||||
self->doPopup ();
|
||||
});
|
||||
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
|
||||
}
|
||||
return kMouseEventNotHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
COptionMenu *COptionMenu::getLastItemMenu (int32_t &idxInMenu) const
|
||||
{
|
||||
idxInMenu = lastMenu ? (int32_t)lastMenu->getValue (): -1;
|
||||
return lastMenu;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void COptionMenu::setValue (float val)
|
||||
{
|
||||
auto newIndex = static_cast<int32_t> (std::round (val));
|
||||
if (newIndex < 0 || newIndex >= getNbEntries ())
|
||||
return;
|
||||
|
||||
currentIndex = newIndex;
|
||||
if (style & (kMultipleCheckStyle & ~kCheckStyle))
|
||||
{
|
||||
CMenuItem* item = getCurrent ();
|
||||
if (item)
|
||||
item->setChecked (!item->isChecked ());
|
||||
}
|
||||
CParamDisplay::setValue (static_cast<float> (newIndex));
|
||||
|
||||
// to force the redraw
|
||||
setDirty ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
float COptionMenu::getMax () const
|
||||
{
|
||||
if (menuItems->empty ())
|
||||
return 0.f;
|
||||
return static_cast<float> (menuItems->size () - 1);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void COptionMenu::takeFocus ()
|
||||
{
|
||||
CParamDisplay::takeFocus ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void COptionMenu::looseFocus ()
|
||||
{
|
||||
CView* receiver = getParentView () ? getParentView () : getFrame ();
|
||||
while (receiver)
|
||||
{
|
||||
if (receiver->notify (this, kMsgLooseFocus) == kMessageNotified)
|
||||
break;
|
||||
receiver = receiver->getParentView ();
|
||||
}
|
||||
CParamDisplay::looseFocus ();
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,339 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cparamdisplay.h"
|
||||
#include "icommandmenuitemtarget.h"
|
||||
#include "ioptionmenulistener.h"
|
||||
#include "../cstring.h"
|
||||
#include "../dispatchlist.h"
|
||||
#include "../cbitmap.h"
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CMenuItem Declaration
|
||||
//! @brief a menu item
|
||||
//-----------------------------------------------------------------------------
|
||||
class CMenuItem : public CBaseObject
|
||||
{
|
||||
public:
|
||||
enum Flags {
|
||||
kNoFlags = 0,
|
||||
/** item is gray and not selectable */
|
||||
kDisabled = 1 << 0,
|
||||
/** item indicates a title and is not selectable */
|
||||
kTitle = 1 << 1,
|
||||
/** item has a checkmark */
|
||||
kChecked = 1 << 2,
|
||||
/** item is a separator */
|
||||
kSeparator = 1 << 3
|
||||
};
|
||||
|
||||
CMenuItem (const UTF8String& title, const UTF8String& keycode = "", int32_t keyModifiers = 0, CBitmap* icon = nullptr, int32_t flags = kNoFlags);
|
||||
CMenuItem (const UTF8String& title, COptionMenu* submenu, CBitmap* icon = nullptr);
|
||||
CMenuItem (const UTF8String& title, int32_t tag);
|
||||
CMenuItem (const CMenuItem& item);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CMenuItem Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
/** set title of menu item */
|
||||
virtual void setTitle (const UTF8String& title);
|
||||
/** set submenu of menu item */
|
||||
virtual void setSubmenu (COptionMenu* submenu);
|
||||
/** set keycode and key modifiers of menu item */
|
||||
virtual void setKey (const UTF8String& keyCode, int32_t keyModifiers = 0);
|
||||
/** set virtual key and key modifiers of menu item */
|
||||
virtual void setVirtualKey (VirtualKey virtualKey, int32_t keyModifiers = 0);
|
||||
/** set menu item enabled state */
|
||||
virtual void setEnabled (bool state = true);
|
||||
/** set menu item checked state */
|
||||
virtual void setChecked (bool state = true);
|
||||
/** set menu item title state */
|
||||
virtual void setIsTitle (bool state = true);
|
||||
/** set menu item separator state */
|
||||
virtual void setIsSeparator (bool state = true);
|
||||
/** set menu item icon */
|
||||
virtual void setIcon (CBitmap* icon);
|
||||
/** set menu item tag */
|
||||
virtual void setTag (int32_t tag);
|
||||
|
||||
/** returns whether the item is enabled or not */
|
||||
bool isEnabled () const;
|
||||
/** returns whether the item is checked or not */
|
||||
bool isChecked () const;
|
||||
/** returns whether the item is a title item or not */
|
||||
bool isTitle () const;
|
||||
/** returns whether the item is a separator or not */
|
||||
bool isSeparator () const;
|
||||
|
||||
/** returns the title of the item */
|
||||
const UTF8String& getTitle () const;
|
||||
/** returns the key modifiers of the item */
|
||||
int32_t getKeyModifiers () const;
|
||||
/** returns the keycode of the item */
|
||||
const UTF8String& getKeycode () const;
|
||||
/** returns the virtual key of the item */
|
||||
VirtualKey getVirtualKey () const;
|
||||
/** returns the submenu of the item */
|
||||
COptionMenu* getSubmenu () const;
|
||||
/** returns the icon of the item */
|
||||
CBitmap* getIcon () const;
|
||||
/** returns the tag of the item */
|
||||
int32_t getTag () const;
|
||||
//@}
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
int32_t getVirtualKeyCode () const;
|
||||
virtual void setVirtualKey (int32_t virtualKeyCode, int32_t keyModifiers = 0);
|
||||
#endif
|
||||
//------------------------------------------------------------------------
|
||||
protected:
|
||||
CMenuItem ();
|
||||
~CMenuItem () noexcept override;
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CCommandMenuItem Declaration
|
||||
/// @brief a command menu item
|
||||
/// @ingroup new_in_4_1
|
||||
//-----------------------------------------------------------------------------
|
||||
class CCommandMenuItem : public CMenuItem
|
||||
{
|
||||
public:
|
||||
struct Desc
|
||||
{
|
||||
UTF8String title;
|
||||
UTF8String commandCategory;
|
||||
UTF8String commandName;
|
||||
UTF8String keycode;
|
||||
SharedPointer<ICommandMenuItemTarget> target;
|
||||
SharedPointer<CBitmap> icon;
|
||||
int32_t keyModifiers {0};
|
||||
int32_t flags {kNoFlags};
|
||||
int32_t tag {-1};
|
||||
|
||||
Desc () = default;
|
||||
~Desc () noexcept = default;
|
||||
|
||||
Desc (const UTF8String& title, const UTF8String& keycode = nullptr,
|
||||
int32_t keyModifiers = 0, CBitmap* icon = nullptr,
|
||||
int32_t flags = kNoFlags, ICommandMenuItemTarget* target = nullptr,
|
||||
const UTF8String& commandCategory = nullptr,
|
||||
const UTF8String& commandName = nullptr)
|
||||
: title (title)
|
||||
, commandCategory (commandCategory)
|
||||
, commandName (commandName)
|
||||
, keycode (keycode)
|
||||
, target (target)
|
||||
, icon (icon)
|
||||
, keyModifiers (keyModifiers)
|
||||
, flags (flags)
|
||||
{
|
||||
}
|
||||
|
||||
Desc (const UTF8String& title, int32_t tag, ICommandMenuItemTarget* target = nullptr,
|
||||
const UTF8String& commandCategory = nullptr, const UTF8String& commandName = nullptr)
|
||||
: title (title)
|
||||
, commandCategory (commandCategory)
|
||||
, commandName (commandName)
|
||||
, target (target)
|
||||
, tag (tag)
|
||||
{
|
||||
}
|
||||
|
||||
Desc (const UTF8String& title, ICommandMenuItemTarget* target,
|
||||
const UTF8String& commandCategory = nullptr,
|
||||
const UTF8String& commandName = nullptr)
|
||||
: title (title)
|
||||
, commandCategory (commandCategory)
|
||||
, commandName (commandName)
|
||||
, target (target)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
CCommandMenuItem (Desc&& args);
|
||||
CCommandMenuItem (const Desc& args);
|
||||
CCommandMenuItem (const CCommandMenuItem& item);
|
||||
~CCommandMenuItem () noexcept override = default;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CCommandMenuItem Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
void setCommandCategory (const UTF8String& category);
|
||||
const UTF8String& getCommandCategory () const { return commandCategory; }
|
||||
bool isCommandCategory (const UTF8String& category) const;
|
||||
|
||||
void setCommandName (const UTF8String& name);
|
||||
const UTF8String& getCommandName () const { return commandName; }
|
||||
bool isCommandName (const UTF8String& name) const;
|
||||
|
||||
void setItemTarget (ICommandMenuItemTarget* target);
|
||||
ICommandMenuItemTarget* getItemTarget () const { return itemTarget; }
|
||||
|
||||
using ValidateCallbackFunction = std::function<void(CCommandMenuItem* item)>;
|
||||
using SelectedCallbackFunction = std::function<void(CCommandMenuItem* item)>;
|
||||
|
||||
void setActions (SelectedCallbackFunction&& selected, ValidateCallbackFunction&& validate = [](CCommandMenuItem*){});
|
||||
//@}
|
||||
|
||||
void execute ();
|
||||
void validate ();
|
||||
|
||||
protected:
|
||||
ValidateCallbackFunction validateFunc;
|
||||
SelectedCallbackFunction selectedFunc;
|
||||
UTF8String commandCategory;
|
||||
UTF8String commandName;
|
||||
SharedPointer<ICommandMenuItemTarget> itemTarget;
|
||||
};
|
||||
|
||||
using CMenuItemList = std::vector<SharedPointer<CMenuItem>>;
|
||||
using CMenuItemIterator = CMenuItemList::iterator;
|
||||
using CConstMenuItemIterator = CMenuItemList::const_iterator;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// COptionMenu Declaration
|
||||
//! @brief a popup menu control
|
||||
/// @ingroup controls
|
||||
//-----------------------------------------------------------------------------
|
||||
class COptionMenu : public CParamDisplay
|
||||
{
|
||||
private:
|
||||
enum StyleEnum
|
||||
{
|
||||
StylePopup = CParamDisplay::LastStyle,
|
||||
StyleCheck,
|
||||
StyleMultipleCheck,
|
||||
};
|
||||
public:
|
||||
COptionMenu ();
|
||||
COptionMenu (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background = nullptr, CBitmap* bgWhenClick = nullptr, const int32_t style = 0);
|
||||
COptionMenu (const COptionMenu& menu);
|
||||
~COptionMenu () noexcept override;
|
||||
|
||||
enum Style
|
||||
{
|
||||
kPopupStyle = 1 << StylePopup,
|
||||
kCheckStyle = 1 << StyleCheck,
|
||||
kMultipleCheckStyle = 1 << StyleMultipleCheck
|
||||
};
|
||||
|
||||
bool isPopupStyle () const { return hasBit (getStyle (), kPopupStyle); }
|
||||
bool isCheckStyle () const { return hasBit (getStyle (), kCheckStyle); }
|
||||
bool isMultipleCheckStyle () const { return hasBit (getStyle (), kMultipleCheckStyle); }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name COptionMenu Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
/** add a new entry */
|
||||
virtual CMenuItem* addEntry (CMenuItem* item, int32_t index = -1);
|
||||
/** add a new submenu entry */
|
||||
virtual CMenuItem* addEntry (COptionMenu* submenu, const UTF8String& title);
|
||||
/** add a new entry */
|
||||
virtual CMenuItem* addEntry (const UTF8String& title, int32_t index = -1, int32_t itemFlags = CMenuItem::kNoFlags);
|
||||
/** add a new separator entry */
|
||||
virtual CMenuItem* addSeparator (int32_t index = -1);
|
||||
/** get current entry */
|
||||
virtual CMenuItem* getCurrent () const;
|
||||
/** TODO: Doc */
|
||||
virtual int32_t getCurrentIndex (bool countSeparator = false) const;
|
||||
/** get entry at index position */
|
||||
virtual CMenuItem* getEntry (int32_t index) const;
|
||||
/** get number of entries */
|
||||
virtual int32_t getNbEntries () const;
|
||||
/** set current entry */
|
||||
virtual bool setCurrent (int32_t index, bool countSeparator = true);
|
||||
/** remove an entry */
|
||||
virtual bool removeEntry (int32_t index);
|
||||
/** remove all entries */
|
||||
virtual bool removeAllEntry ();
|
||||
|
||||
/** change check state of entry at index */
|
||||
virtual bool checkEntry (int32_t index, bool state);
|
||||
/** check entry at index and uncheck every other item */
|
||||
virtual bool checkEntryAlone (int32_t index);
|
||||
/** get check state of entry at index */
|
||||
virtual bool isCheckEntry (int32_t index) const;
|
||||
/** Windows only */
|
||||
virtual void setNbItemsPerColumn (int32_t val) { nbItemsPerColumn = val; }
|
||||
/** Windows only */
|
||||
virtual int32_t getNbItemsPerColumn () const { return nbItemsPerColumn; }
|
||||
|
||||
/** get last index of choosen entry */
|
||||
int32_t getLastResult () const { return lastResult; }
|
||||
/** get last menu and index of choosen entry */
|
||||
COptionMenu* getLastItemMenu (int32_t& idxInMenu) const;
|
||||
|
||||
/** set prefix numbering */
|
||||
virtual void setPrefixNumbers (int32_t preCount);
|
||||
/** get prefix numbering */
|
||||
int32_t getPrefixNumbers () const { return prefixNumbers; }
|
||||
|
||||
/** get a submenu */
|
||||
COptionMenu* getSubMenu (int32_t idx) const;
|
||||
|
||||
/** popup callback function */
|
||||
using PopupCallback = std::function<void (COptionMenu* menu)>;
|
||||
|
||||
/** pops up the menu */
|
||||
bool popup (const PopupCallback& callback = {});
|
||||
/** pops up the menu at frameLocation */
|
||||
bool popup (CFrame* frame, const CPoint& frameLocation, const PopupCallback& callback = {});
|
||||
|
||||
CMenuItemList* getItems () const { return menuItems; }
|
||||
|
||||
/** remove separators as first and last item and double separators */
|
||||
void cleanupSeparators (bool deep);
|
||||
|
||||
void registerOptionMenuListener (IOptionMenuListener* listener);
|
||||
void unregisterOptionMenuListener (IOptionMenuListener* listener);
|
||||
//@}
|
||||
|
||||
// overrides
|
||||
void setValue (float val) override;
|
||||
void setMin (float val) override {}
|
||||
float getMin () const override { return 0; }
|
||||
void setMax (float val) override {}
|
||||
float getMax () const override;
|
||||
|
||||
void draw (CDrawContext* pContext) override;
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
|
||||
void takeFocus () override;
|
||||
void looseFocus () override;
|
||||
|
||||
CLASS_METHODS(COptionMenu, CParamDisplay)
|
||||
protected:
|
||||
bool doPopup ();
|
||||
void beforePopup ();
|
||||
void afterPopup ();
|
||||
|
||||
CMenuItemList* menuItems;
|
||||
|
||||
bool inPopup {false};
|
||||
int32_t currentIndex {-1};
|
||||
CButtonState lastButton {0};
|
||||
int32_t nbItemsPerColumn {-1};
|
||||
int32_t lastResult {-1};
|
||||
int32_t prefixNumbers {0};
|
||||
SharedPointer<CBitmap> bgWhenClick;
|
||||
COptionMenu* lastMenu {nullptr};
|
||||
using MenuListenerList = DispatchList<IOptionMenuListener*>;
|
||||
std::unique_ptr<MenuListenerList> listeners;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,504 @@
|
||||
// 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 "cparamdisplay.h"
|
||||
#include "../cbitmap.h"
|
||||
#include "../cframe.h"
|
||||
#include "../cstring.h"
|
||||
#include "../cgraphicspath.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include <string>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CParamDisplay
|
||||
//------------------------------------------------------------------------
|
||||
/*! @class CParamDisplay
|
||||
Define a rectangle view where a text-value can be displayed with a given font and color.
|
||||
The user can specify its convert function (from float to char) by default the string format is "%2.2f".
|
||||
The text-value is centered in the given rect.
|
||||
*/
|
||||
CParamDisplay::CParamDisplay (const CRect& size, CBitmap* background, int32_t inStyle)
|
||||
: CControl (size, nullptr, -1, background)
|
||||
, horiTxtAlign (kCenterText)
|
||||
, style (inStyle)
|
||||
, valuePrecision (2)
|
||||
, roundRectRadius (6.)
|
||||
, frameWidth (1.)
|
||||
, textRotation (0.)
|
||||
{
|
||||
setBit (style, kAntialias, true);
|
||||
backOffset (0, 0);
|
||||
|
||||
fontID = kNormalFont; fontID->remember ();
|
||||
fontColor = kWhiteCColor;
|
||||
backColor = kBlackCColor;
|
||||
frameColor = kBlackCColor;
|
||||
shadowColor = kRedCColor;
|
||||
if (hasBit (style, kNoDrawStyle))
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CParamDisplay::CParamDisplay (const CParamDisplay& v)
|
||||
: CControl (v)
|
||||
, valueToStringFunction (v.valueToStringFunction)
|
||||
, horiTxtAlign (v.horiTxtAlign)
|
||||
, style (v.style)
|
||||
, valuePrecision (v.valuePrecision)
|
||||
, fontID (v.fontID)
|
||||
, fontColor (v.fontColor)
|
||||
, backColor (v.backColor)
|
||||
, frameColor (v.frameColor)
|
||||
, shadowColor (v.shadowColor)
|
||||
, textInset (v.textInset)
|
||||
, backOffset (v.backOffset)
|
||||
, roundRectRadius (v.roundRectRadius)
|
||||
, frameWidth (v.frameWidth)
|
||||
, textRotation (v.textRotation)
|
||||
{
|
||||
fontID->remember ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CParamDisplay::~CParamDisplay () noexcept
|
||||
{
|
||||
if (fontID)
|
||||
fontID->forget ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CParamDisplay::removed (CView* parent)
|
||||
{
|
||||
return CControl::removed (parent);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setStyle (int32_t val)
|
||||
{
|
||||
setBit (val, kAntialias, hasBit (style, kAntialias));
|
||||
if (style != val)
|
||||
{
|
||||
style = val;
|
||||
drawStyleChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
int32_t CParamDisplay::getStyle () const
|
||||
{
|
||||
auto tmp = style;
|
||||
setBit (tmp, kAntialias, false);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setPrecision (uint8_t precision)
|
||||
{
|
||||
if (valuePrecision != precision)
|
||||
{
|
||||
valuePrecision = precision;
|
||||
drawStyleChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setValueToStringFunction2 (const ValueToStringFunction2& valueToStringFunc)
|
||||
{
|
||||
valueToStringFunction = valueToStringFunc;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setValueToStringFunction2 (ValueToStringFunction2&& valueToStringFunc)
|
||||
{
|
||||
valueToStringFunction = std::move (valueToStringFunc);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setValueToStringFunction (const ValueToStringFunction& func)
|
||||
{
|
||||
if (!func)
|
||||
{
|
||||
setValueToStringFunction2 (nullptr);
|
||||
return;
|
||||
}
|
||||
setValueToStringFunction2 ([=] (float value, std::string& str, CParamDisplay* display) {
|
||||
char string[256];
|
||||
string[0] = 0;
|
||||
if (func (value, string, display))
|
||||
{
|
||||
str = string;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setValueToStringFunction (ValueToStringFunction&& func)
|
||||
{
|
||||
setValueToStringFunction (func);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CParamDisplay::getFocusPath (CGraphicsPath& outPath)
|
||||
{
|
||||
if (wantsFocus ())
|
||||
{
|
||||
auto lineWidth = getFrameWidth ();
|
||||
if (lineWidth < 0.)
|
||||
lineWidth = 1.;
|
||||
CCoord focusWidth = getFrame ()->getFocusWidth ();
|
||||
CRect r (getViewSize ());
|
||||
if (hasBit (style, kRoundRectStyle))
|
||||
{
|
||||
r.inset (lineWidth / 2., lineWidth / 2.);
|
||||
outPath.addRoundRect (r, roundRectRadius);
|
||||
outPath.closeSubpath ();
|
||||
r.extend (focusWidth, focusWidth);
|
||||
outPath.addRoundRect (r, roundRectRadius);
|
||||
}
|
||||
else
|
||||
{
|
||||
r.inset (lineWidth / 2., lineWidth / 2.);
|
||||
outPath.addRect (r);
|
||||
r.extend (focusWidth, focusWidth);
|
||||
outPath.addRect (r);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::draw (CDrawContext *pContext)
|
||||
{
|
||||
if (hasBit (style, kNoDrawStyle))
|
||||
return;
|
||||
|
||||
std::string string;
|
||||
|
||||
bool converted = false;
|
||||
if (valueToStringFunction)
|
||||
converted = valueToStringFunction (value, string, this);
|
||||
if (!converted)
|
||||
{
|
||||
char tmp[255];
|
||||
char precisionStr[10];
|
||||
snprintf (precisionStr, 10, "%%.%hhuf", valuePrecision);
|
||||
snprintf (tmp, 255, precisionStr, value);
|
||||
string = tmp;
|
||||
}
|
||||
|
||||
drawBack (pContext);
|
||||
drawPlatformText (pContext, UTF8String (string));
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::drawBack (CDrawContext* pContext, CBitmap* newBack)
|
||||
{
|
||||
pContext->setDrawMode (kAliasing);
|
||||
auto lineWidth = getFrameWidth ();
|
||||
if (lineWidth < 0.)
|
||||
lineWidth = pContext->getHairlineSize ();
|
||||
if (newBack)
|
||||
{
|
||||
newBack->draw (pContext, getViewSize (), backOffset);
|
||||
}
|
||||
else if (getDrawBackground ())
|
||||
{
|
||||
getDrawBackground ()->draw (pContext, getViewSize (), backOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!getTransparency ())
|
||||
{
|
||||
bool strokePath = !(hasBit (style, (k3DIn|k3DOut|kNoFrame)));
|
||||
pContext->setFillColor (backColor);
|
||||
if (hasBit (style, kRoundRectStyle))
|
||||
{
|
||||
CRect pathRect = getViewSize ();
|
||||
pathRect.inset (lineWidth/2., lineWidth/2.);
|
||||
SharedPointer<CGraphicsPath> path = owned (pContext->createRoundRectGraphicsPath (pathRect, roundRectRadius));
|
||||
if (path)
|
||||
{
|
||||
pContext->setDrawMode (kAntiAliasing);
|
||||
pContext->drawGraphicsPath (path, CDrawContext::kPathFilled);
|
||||
if (strokePath)
|
||||
{
|
||||
pContext->setLineStyle (kLineSolid);
|
||||
pContext->setLineWidth (lineWidth);
|
||||
pContext->setFrameColor (frameColor);
|
||||
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pContext->setDrawMode (kAntiAliasing);
|
||||
SharedPointer<CGraphicsPath> path = owned (pContext->createGraphicsPath ());
|
||||
if (path)
|
||||
{
|
||||
CRect frameRect = getViewSize ();
|
||||
if (strokePath)
|
||||
frameRect.inset (lineWidth/2., lineWidth/2.);
|
||||
path->addRect (frameRect);
|
||||
pContext->drawGraphicsPath (path, CDrawContext::kPathFilled);
|
||||
if (strokePath)
|
||||
{
|
||||
pContext->setLineStyle (kLineSolid);
|
||||
pContext->setLineWidth (lineWidth);
|
||||
pContext->setFrameColor (frameColor);
|
||||
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pContext->drawRect (getViewSize (), kDrawFilled);
|
||||
|
||||
if (strokePath)
|
||||
{
|
||||
CRect frameRect = getViewSize ();
|
||||
frameRect.inset (lineWidth/2., lineWidth/2.);
|
||||
|
||||
pContext->setLineStyle (kLineSolid);
|
||||
pContext->setLineWidth (lineWidth);
|
||||
pContext->setFrameColor (frameColor);
|
||||
pContext->drawRect (frameRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// draw the frame for the 3D effect
|
||||
if (hasBit (style, (k3DIn|k3DOut)))
|
||||
{
|
||||
CRect r (getViewSize ());
|
||||
r.inset (lineWidth/2., lineWidth/2.);
|
||||
pContext->setDrawMode (kAliasing);
|
||||
pContext->setLineWidth (lineWidth);
|
||||
pContext->setLineStyle (kLineSolid);
|
||||
if (hasBit (style, k3DIn))
|
||||
pContext->setFrameColor (backColor);
|
||||
else
|
||||
pContext->setFrameColor (frameColor);
|
||||
|
||||
CPoint p;
|
||||
SharedPointer<CGraphicsPath> path = owned (pContext->createGraphicsPath ());
|
||||
if (path)
|
||||
{
|
||||
path->beginSubpath (p (r.left, r.bottom));
|
||||
path->addLine (p (r.left, r.top));
|
||||
path->addLine (p (r.right, r.top));
|
||||
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
|
||||
}
|
||||
else
|
||||
{
|
||||
pContext->drawLine (CPoint (r.left, r.bottom), CPoint (r.left, r.top));
|
||||
pContext->drawLine (CPoint (r.left, r.top), CPoint (r.right, r.top));
|
||||
}
|
||||
|
||||
if (hasBit (style, k3DIn))
|
||||
pContext->setFrameColor (frameColor);
|
||||
else
|
||||
pContext->setFrameColor (backColor);
|
||||
|
||||
path = owned (pContext->createGraphicsPath ());
|
||||
if (path)
|
||||
{
|
||||
path->beginSubpath (p (r.right, r.top));
|
||||
path->addLine (p (r.right, r.bottom));
|
||||
path->addLine (p (r.left, r.bottom));
|
||||
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
|
||||
}
|
||||
else
|
||||
{
|
||||
pContext->drawLine (CPoint (r.right, r.top), CPoint (r.right, r.bottom));
|
||||
pContext->drawLine (CPoint (r.right, r.bottom), CPoint (r.left, r.bottom));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::drawPlatformText (CDrawContext* pContext, const UTF8String& string)
|
||||
{
|
||||
drawPlatformText (pContext, string, getViewSize ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::drawPlatformText (CDrawContext* pContext, const UTF8String& string,
|
||||
const CRect& size)
|
||||
{
|
||||
if (!hasBit (style, kNoTextStyle))
|
||||
{
|
||||
pContext->saveGlobalState ();
|
||||
CRect textRect (size);
|
||||
textRect.inset (textInset.x, textInset.y);
|
||||
|
||||
drawClipped (pContext, textRect, [&] () {
|
||||
CPoint center (textRect.getCenter ());
|
||||
CGraphicsTransform transform;
|
||||
transform.rotate (textRotation, center);
|
||||
CDrawContext::Transform ctxTransform (*pContext, transform);
|
||||
|
||||
pContext->setDrawMode (kAntiAliasing);
|
||||
pContext->setFont (fontID);
|
||||
|
||||
// draw darker text (as shadow)
|
||||
if (hasBit (style, kShadowText))
|
||||
{
|
||||
CRect newSize (textRect);
|
||||
newSize.offset (shadowTextOffset);
|
||||
pContext->setFontColor (shadowColor);
|
||||
pContext->drawString (string.getPlatformString (), newSize, horiTxtAlign,
|
||||
hasBit (style, kAntialias));
|
||||
}
|
||||
pContext->setFontColor (fontColor);
|
||||
pContext->drawString (string.getPlatformString (), textRect, horiTxtAlign,
|
||||
hasBit (style, kAntialias));
|
||||
});
|
||||
pContext->restoreGlobalState ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setFont (CFontRef inFontID)
|
||||
{
|
||||
if (fontID)
|
||||
fontID->forget ();
|
||||
fontID = inFontID;
|
||||
if (fontID)
|
||||
fontID->remember ();
|
||||
drawStyleChanged ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setFontColor (CColor color)
|
||||
{
|
||||
// to force the redraw
|
||||
if (fontColor != color)
|
||||
{
|
||||
fontColor = color;
|
||||
drawStyleChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setBackColor (CColor color)
|
||||
{
|
||||
// to force the redraw
|
||||
if (backColor != color)
|
||||
{
|
||||
backColor = color;
|
||||
drawStyleChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setFrameColor (CColor color)
|
||||
{
|
||||
// to force the redraw
|
||||
if (frameColor != color)
|
||||
{
|
||||
frameColor = color;
|
||||
drawStyleChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setShadowColor (CColor color)
|
||||
{
|
||||
// to force the redraw
|
||||
if (shadowColor != color)
|
||||
{
|
||||
shadowColor = color;
|
||||
drawStyleChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setShadowTextOffset (const CPoint& offset)
|
||||
{
|
||||
if (shadowTextOffset != offset)
|
||||
{
|
||||
shadowTextOffset = offset;
|
||||
drawStyleChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setHoriAlign (CHoriTxtAlign hAlign)
|
||||
{
|
||||
// to force the redraw
|
||||
if (horiTxtAlign != hAlign)
|
||||
{
|
||||
horiTxtAlign = hAlign;
|
||||
drawStyleChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setTextInset (const CPoint& p)
|
||||
{
|
||||
if (textInset != p)
|
||||
{
|
||||
textInset = p;
|
||||
drawStyleChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setTextRotation (double angle)
|
||||
{
|
||||
while (angle < 0.)
|
||||
angle += 360.;
|
||||
while (angle > 360.)
|
||||
angle -= 360.;
|
||||
if (textRotation != angle)
|
||||
{
|
||||
textRotation = angle;
|
||||
drawStyleChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setRoundRectRadius (const CCoord& radius)
|
||||
{
|
||||
if (roundRectRadius != radius)
|
||||
{
|
||||
roundRectRadius = radius;
|
||||
drawStyleChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setFrameWidth (const CCoord& width)
|
||||
{
|
||||
if (frameWidth != width)
|
||||
{
|
||||
frameWidth = width;
|
||||
drawStyleChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::drawStyleChanged ()
|
||||
{
|
||||
setDirty ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CParamDisplay::setBackOffset (const CPoint &offset)
|
||||
{
|
||||
backOffset = offset;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CParamDisplay::copyBackOffset ()
|
||||
{
|
||||
backOffset (getViewSize ().left, getViewSize ().top);
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,154 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ccontrol.h"
|
||||
#include "../cfont.h"
|
||||
#include "../ccolor.h"
|
||||
#include "../cdrawdefs.h"
|
||||
#include <functional>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
using CParamDisplayValueToStringProc = bool (*) (float value, char utf8String[256], void* userData);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CParamDisplay Declaration
|
||||
//! @brief a parameter display
|
||||
/// @ingroup views
|
||||
//-----------------------------------------------------------------------------
|
||||
class CParamDisplay : public CControl
|
||||
{
|
||||
protected:
|
||||
enum StyleEnum
|
||||
{
|
||||
StyleShadowText = 0,
|
||||
Style3DIn,
|
||||
Style3DOut,
|
||||
StyleNoText,
|
||||
StyleNoDraw,
|
||||
StyleRoundRect,
|
||||
StyleNoFrame,
|
||||
StyleAntialias,
|
||||
LastStyle
|
||||
};
|
||||
|
||||
public:
|
||||
CParamDisplay (const CRect& size, CBitmap* background = nullptr, int32_t style = 0);
|
||||
CParamDisplay (const CParamDisplay& paramDisplay);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CParamDisplay Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
virtual void setFont (CFontRef fontID);
|
||||
const CFontRef getFont () const { return fontID; }
|
||||
|
||||
virtual void setFontColor (CColor color);
|
||||
CColor getFontColor () const { return fontColor; }
|
||||
|
||||
virtual void setBackColor (CColor color);
|
||||
CColor getBackColor () const { return backColor; }
|
||||
|
||||
virtual void setFrameColor (CColor color);
|
||||
CColor getFrameColor () const { return frameColor; }
|
||||
|
||||
virtual void setShadowColor (CColor color);
|
||||
CColor getShadowColor () const { return shadowColor; }
|
||||
|
||||
virtual void setShadowTextOffset (const CPoint& offset);
|
||||
CPoint getShadowTextOffset () const { return shadowTextOffset; }
|
||||
|
||||
virtual void setAntialias (bool state) { setBit (style, kAntialias, state); }
|
||||
bool getAntialias () const { return hasBit (style, kAntialias); }
|
||||
|
||||
virtual void setHoriAlign (CHoriTxtAlign hAlign);
|
||||
CHoriTxtAlign getHoriAlign () const { return horiTxtAlign; }
|
||||
|
||||
virtual void setTextInset (const CPoint& p);
|
||||
CPoint getTextInset () const { return textInset; }
|
||||
|
||||
virtual void setTextRotation (double angle);
|
||||
double getTextRotation () const { return textRotation; }
|
||||
|
||||
virtual void setRoundRectRadius (const CCoord& radius);
|
||||
CCoord getRoundRectRadius () const { return roundRectRadius; }
|
||||
|
||||
virtual void setFrameWidth (const CCoord& width);
|
||||
CCoord getFrameWidth () const { return frameWidth; }
|
||||
|
||||
using ValueToStringUserData = CParamDisplay;
|
||||
using ValueToStringFunction = std::function<bool (float value, char utf8String[256], CParamDisplay* display)>;
|
||||
|
||||
void setValueToStringFunction (const ValueToStringFunction& valueToStringFunc);
|
||||
void setValueToStringFunction (ValueToStringFunction&& valueToStringFunc);
|
||||
|
||||
using ValueToStringFunction2 = std::function<bool (float value, std::string& result, CParamDisplay* display)>;
|
||||
|
||||
void setValueToStringFunction2 (const ValueToStringFunction2& valueToStringFunc);
|
||||
void setValueToStringFunction2 (ValueToStringFunction2&& valueToStringFunc);
|
||||
|
||||
enum Style
|
||||
{
|
||||
kShadowText = 1 << StyleShadowText,
|
||||
k3DIn = 1 << Style3DIn,
|
||||
k3DOut = 1 << Style3DOut,
|
||||
kNoTextStyle = 1 << StyleNoText,
|
||||
kNoDrawStyle = 1 << StyleNoDraw,
|
||||
kRoundRectStyle = 1 << StyleRoundRect,
|
||||
kNoFrame = 1 << StyleNoFrame,
|
||||
};
|
||||
virtual void setStyle (int32_t val);
|
||||
int32_t getStyle () const;
|
||||
|
||||
virtual void setPrecision (uint8_t precision);
|
||||
uint8_t getPrecision () const { return valuePrecision; }
|
||||
|
||||
virtual void setBackOffset (const CPoint& offset);
|
||||
const CPoint& getBackOffset () const { return backOffset; }
|
||||
void copyBackOffset ();
|
||||
|
||||
//@}
|
||||
|
||||
void draw (CDrawContext* pContext) override;
|
||||
bool getFocusPath (CGraphicsPath& outPath) override;
|
||||
bool removed (CView* parent) override;
|
||||
|
||||
CLASS_METHODS(CParamDisplay, CControl)
|
||||
protected:
|
||||
~CParamDisplay () noexcept override;
|
||||
virtual void drawBack (CDrawContext* pContext, CBitmap* newBack = nullptr);
|
||||
|
||||
virtual void drawPlatformText (CDrawContext* pContext, const UTF8String& string);
|
||||
virtual void drawPlatformText (CDrawContext* pContext, const UTF8String& string,
|
||||
const CRect& size);
|
||||
|
||||
virtual void drawStyleChanged ();
|
||||
|
||||
ValueToStringFunction2 valueToStringFunction;
|
||||
|
||||
enum StylePrivate {
|
||||
kAntialias = 1 << StyleAntialias,
|
||||
};
|
||||
|
||||
CHoriTxtAlign horiTxtAlign;
|
||||
int32_t style;
|
||||
uint8_t valuePrecision;
|
||||
|
||||
CFontRef fontID;
|
||||
CColor fontColor;
|
||||
CColor backColor;
|
||||
CColor frameColor;
|
||||
CColor shadowColor;
|
||||
CPoint textInset;
|
||||
CPoint shadowTextOffset {1., 1.};
|
||||
CPoint backOffset;
|
||||
CCoord roundRectRadius;
|
||||
CCoord frameWidth;
|
||||
double textRotation;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,416 @@
|
||||
// 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 "cscrollbar.h"
|
||||
#include "../cvstguitimer.h"
|
||||
#include "../animation/animations.h"
|
||||
#include "../animation/timingfunctions.h"
|
||||
#include "../cframe.h"
|
||||
#include "../cgraphicspath.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include "../events.h"
|
||||
#include "../algorithm.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CScrollbar::CScrollbar (const CRect& size, IControlListener* listener, int32_t tag, ScrollbarDirection direction, const CRect& scrollSize)
|
||||
: CControl (size, listener, tag, nullptr)
|
||||
, direction (direction)
|
||||
, scrollSize (scrollSize)
|
||||
, scrollerArea (size)
|
||||
, stepValue (0.1f)
|
||||
, scrollerLength (0)
|
||||
, overlayStyle (false)
|
||||
, mouseIsInside (false)
|
||||
, drawer (nullptr)
|
||||
{
|
||||
setTransparency (true);
|
||||
setWheelInc (0.05f);
|
||||
scrollerArea.inset (2, 2);
|
||||
calculateScrollerLength ();
|
||||
frameColor (0, 0, 0, 255);
|
||||
scrollerColor (0, 0, 255, 255);
|
||||
backgroundColor (255, 255, 255, 200);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CScrollbar::CScrollbar (const CScrollbar& v)
|
||||
: CControl (v)
|
||||
, direction (v.direction)
|
||||
, scrollSize (v.scrollSize)
|
||||
, scrollerArea (v.scrollerArea)
|
||||
, stepValue (v.stepValue)
|
||||
, scrollerLength (v.scrollerLength)
|
||||
, frameColor (v.frameColor)
|
||||
, scrollerColor (v.scrollerColor)
|
||||
, backgroundColor (v.backgroundColor)
|
||||
, overlayStyle (v.overlayStyle)
|
||||
, mouseIsInside (false)
|
||||
, drawer (v.drawer)
|
||||
{
|
||||
calculateScrollerLength ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CScrollbar::setViewSize (const CRect &newSize, bool invalid)
|
||||
{
|
||||
scrollerArea = newSize;
|
||||
scrollerArea.inset (2, 2);
|
||||
CControl::setViewSize (newSize, invalid);
|
||||
calculateScrollerLength ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CScrollbar::setScrollSize (const CRect& ssize)
|
||||
{
|
||||
if (scrollSize != ssize)
|
||||
{
|
||||
scrollSize = ssize;
|
||||
calculateScrollerLength ();
|
||||
setDirty (true);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CScrollbar::calculateScrollerLength ()
|
||||
{
|
||||
CCoord newScrollerLength;
|
||||
if (direction == kHorizontal)
|
||||
{
|
||||
double factor = scrollSize.getWidth () > 0. ? getViewSize ().getWidth () / scrollSize.getWidth () : 0.;
|
||||
if (factor >= 1.f)
|
||||
factor = 0;
|
||||
newScrollerLength = (CCoord) (getViewSize ().getWidth () * factor);
|
||||
}
|
||||
else
|
||||
{
|
||||
double factor = scrollSize.getHeight () > 0 ? getViewSize ().getHeight () / scrollSize.getHeight () : 0.;
|
||||
if (factor >= 1.f)
|
||||
factor = 0;
|
||||
newScrollerLength = (CCoord) (getViewSize ().getHeight () * factor);
|
||||
}
|
||||
if (newScrollerLength < minScrollerLenght && newScrollerLength > 0.)
|
||||
newScrollerLength = minScrollerLenght;
|
||||
if (newScrollerLength != scrollerLength)
|
||||
{
|
||||
scrollerLength = newScrollerLength;
|
||||
setDirty (true);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CRect CScrollbar::getScrollerRect ()
|
||||
{
|
||||
CRect sr (scrollerArea);
|
||||
CCoord l = (direction == kHorizontal) ? scrollerArea.getWidth () : scrollerArea.getHeight ();
|
||||
CCoord scrollerOffset = (CCoord)(getValueNormalized () * (l - scrollerLength));
|
||||
if (direction == kHorizontal)
|
||||
{
|
||||
sr.setWidth (scrollerLength);
|
||||
sr.offset (scrollerOffset, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
sr.setHeight (scrollerLength);
|
||||
sr.offset (0, scrollerOffset);
|
||||
}
|
||||
return sr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CScrollbar::doStepping ()
|
||||
{
|
||||
CRect sr = getScrollerRect ();
|
||||
if (timer)
|
||||
{
|
||||
if (!getViewSize ().pointInside (startPoint) || sr.pointInside (startPoint))
|
||||
return;
|
||||
}
|
||||
bool dir = (direction == kHorizontal && startPoint.x < sr.left) || (direction == kVertical && startPoint.y < sr.top);
|
||||
float newValue = getValueNormalized ();
|
||||
if (direction == kHorizontal)
|
||||
{
|
||||
if (dir)
|
||||
newValue -= (float)scrollerLength / (float)scrollerArea.getWidth ();
|
||||
else
|
||||
newValue += (float)scrollerLength / (float)scrollerArea.getWidth ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dir)
|
||||
newValue -= (float)scrollerLength / (float)scrollerArea.getHeight ();
|
||||
else
|
||||
newValue += (float)scrollerLength / (float)scrollerArea.getHeight ();
|
||||
}
|
||||
newValue = clampNorm (newValue);
|
||||
if (newValue != getValueNormalized ())
|
||||
{
|
||||
setValueNormalized (newValue);
|
||||
valueChanged ();
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CMessageResult CScrollbar::notify (CBaseObject* sender, IdStringPtr message)
|
||||
{
|
||||
if (message == CVSTGUITimer::kMsgTimer && timer)
|
||||
{
|
||||
doStepping ();
|
||||
timer->setFireTime (80);
|
||||
return kMessageNotified;
|
||||
}
|
||||
return kMessageUnknown;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CScrollbar::setOverlayStyle (bool state)
|
||||
{
|
||||
if (overlayStyle != state)
|
||||
{
|
||||
overlayStyle = state;
|
||||
setAlphaValue (overlayStyle ? 0.001f : 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CScrollbar::setMinScrollerLength (CCoord length)
|
||||
{
|
||||
if (minScrollerLenght != length)
|
||||
{
|
||||
minScrollerLenght = length;
|
||||
calculateScrollerLength ();
|
||||
setDirty ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CMouseEventResult CScrollbar::onMouseEntered (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (overlayStyle && scrollerLength != 0)
|
||||
{
|
||||
addAnimation ("AlphaValueAnimation", new Animation::AlphaValueAnimation (1.f), new Animation::LinearTimingFunction (100));
|
||||
}
|
||||
mouseIsInside = true;
|
||||
return kMouseEventNotHandled;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CMouseEventResult CScrollbar::onMouseExited (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (overlayStyle && scrollerLength != 0)
|
||||
{
|
||||
Animation::ITimingFunction* timingFunction = nullptr;
|
||||
if (getAlphaValue () == 1.f)
|
||||
{
|
||||
auto* interpolTimingFunction = new Animation::InterpolationTimingFunction (400);
|
||||
interpolTimingFunction->addPoint (300.f/400.f, 1.f);
|
||||
timingFunction = interpolTimingFunction;
|
||||
}
|
||||
else
|
||||
timingFunction = new Animation::LinearTimingFunction (100);
|
||||
addAnimation ("AlphaValueAnimation", new Animation::AlphaValueAnimation (0.001f), timingFunction);
|
||||
}
|
||||
mouseIsInside = false;
|
||||
return kMouseEventNotHandled;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CMouseEventResult CScrollbar::onMouseDown (CPoint &where, const CButtonState& buttons)
|
||||
{
|
||||
if (buttons != kLButton || scrollerLength == 0)
|
||||
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
|
||||
|
||||
startPoint = where;
|
||||
scrollerRect = getScrollerRect ();
|
||||
scrolling = scrollerRect.pointInside (where);
|
||||
if (scrolling)
|
||||
{
|
||||
scrollerRect = getScrollerRect ();
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
else if (scrollerArea.pointInside (where))
|
||||
{
|
||||
doStepping ();
|
||||
timer = makeOwned<CVSTGUITimer> (this, 250, true);
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CMouseEventResult CScrollbar::onMouseUp (CPoint &where, const CButtonState& buttons)
|
||||
{
|
||||
timer = nullptr;
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CMouseEventResult CScrollbar::onMouseMoved (CPoint &where, const CButtonState& buttons)
|
||||
{
|
||||
if (buttons & kLButton)
|
||||
{
|
||||
if (scrolling)
|
||||
{
|
||||
float newValue = 0.f;
|
||||
CPoint newPoint (where);
|
||||
newPoint.x -= startPoint.x - scrollerRect.left;
|
||||
newPoint.y -= startPoint.y - scrollerRect.top;
|
||||
if (direction == kHorizontal)
|
||||
{
|
||||
newValue = (float)((float)(newPoint.x - scrollerArea.left) / ((float)scrollerArea.getWidth () - scrollerRect.getWidth ()));
|
||||
}
|
||||
else
|
||||
{
|
||||
newValue = (float)((float)(newPoint.y - scrollerArea.top) / ((float)scrollerArea.getHeight () - scrollerRect.getHeight ()));
|
||||
}
|
||||
newValue = clampNorm (newValue);
|
||||
if (newValue != getValueNormalized ())
|
||||
{
|
||||
setValueNormalized (newValue);
|
||||
valueChanged ();
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CPoint old (startPoint);
|
||||
startPoint = where;
|
||||
CRect scollerRect = getScrollerRect ();
|
||||
if (getViewSize ().pointInside (where) && scollerRect.pointInside (old) && !scrollerRect.pointInside (startPoint))
|
||||
doStepping ();
|
||||
}
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
return kMouseEventNotHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CScrollbar::onMouseCancel ()
|
||||
{
|
||||
timer = nullptr;
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CScrollbar::onVisualChange ()
|
||||
{
|
||||
if (isAttached () && overlayStyle && !mouseIsInside)
|
||||
{
|
||||
if (scrollerLength != 0)
|
||||
{
|
||||
auto timingFunction = new Animation::InterpolationTimingFunction (1100);
|
||||
timingFunction->addPoint (1000.f/1100.f, 0);
|
||||
addAnimation ("AlphaValueAnimation", new Animation::AlphaValueAnimation (0.001f), timingFunction);
|
||||
setAlphaValue (1.f);
|
||||
}
|
||||
else
|
||||
{
|
||||
removeAnimation ("AlphaValueAnimation");
|
||||
setAlphaValue (0.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CScrollbar::onMouseWheelEvent (MouseWheelEvent& event)
|
||||
{
|
||||
if (scrollerLength == 0 || !getMouseEnabled ())
|
||||
return;
|
||||
|
||||
if (!event.modifiers.empty () && !(event.modifiers.has (ModifierKey::Shift) &&
|
||||
event.flags & MouseWheelEvent::DirectionInvertedFromDevice))
|
||||
return;
|
||||
|
||||
float distance = 0.f;
|
||||
if (direction == kHorizontal)
|
||||
distance = static_cast<float> (event.deltaX);
|
||||
else
|
||||
distance = static_cast<float> (event.deltaY);
|
||||
|
||||
if (distance == 0.f)
|
||||
return;
|
||||
|
||||
if (event.flags & MouseWheelEvent::DirectionInvertedFromDevice)
|
||||
distance *= -1;
|
||||
|
||||
float newValue = getValueNormalized ();
|
||||
if (event.modifiers.has (ModifierKey::Shift))
|
||||
newValue -= 0.1f * distance * getWheelInc ();
|
||||
else
|
||||
newValue -= distance * getWheelInc ();
|
||||
newValue = clampNorm (newValue);
|
||||
|
||||
if (newValue != getValueNormalized ())
|
||||
{
|
||||
setValueNormalized (newValue);
|
||||
onVisualChange ();
|
||||
valueChanged ();
|
||||
invalid ();
|
||||
}
|
||||
event.consumed = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CScrollbar::drawBackground (CDrawContext* pContext)
|
||||
{
|
||||
CRect r (getViewSize ());
|
||||
if (drawer)
|
||||
drawer->drawScrollbarBackground (pContext, r, direction, this);
|
||||
else
|
||||
{
|
||||
pContext->setDrawMode (kAliasing);
|
||||
pContext->setLineWidth (1);
|
||||
pContext->setFillColor (backgroundColor);
|
||||
pContext->setFrameColor (frameColor);
|
||||
pContext->setLineStyle (kLineSolid);
|
||||
pContext->drawRect (r, kDrawFilledAndStroked);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CScrollbar::drawScroller (CDrawContext* pContext, const CRect& size)
|
||||
{
|
||||
CRect r (size);
|
||||
if (drawer)
|
||||
drawer->drawScrollbarScroller (pContext, r, direction, this);
|
||||
else
|
||||
{
|
||||
pContext->setLineWidth (1);
|
||||
pContext->setFillColor (scrollerColor);
|
||||
pContext->setFrameColor (frameColor);
|
||||
|
||||
CCoord wideness = (direction == kVertical ? getWidth() : getHeight()) / 2 - 2;
|
||||
auto path = (wideness > 2) ? owned (pContext->createGraphicsPath ()) : nullptr;
|
||||
if (path)
|
||||
{
|
||||
if (wideness > 4)
|
||||
wideness = 4;
|
||||
pContext->setDrawMode (kAntiAliasing|kNonIntegralMode);
|
||||
path->addRoundRect (r, wideness);
|
||||
pContext->drawGraphicsPath (path, CDrawContext::kPathFilled);
|
||||
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
|
||||
}
|
||||
else
|
||||
{
|
||||
pContext->setDrawMode (kAliasing|kNonIntegralMode);
|
||||
pContext->drawRect (r, kDrawFilledAndStroked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CScrollbar::draw (CDrawContext* pContext)
|
||||
{
|
||||
drawBackground (pContext);
|
||||
if (scrollerLength > 0)
|
||||
{
|
||||
CRect sr = getScrollerRect ();
|
||||
drawScroller (pContext, sr);
|
||||
}
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ccontrol.h"
|
||||
#include "../ccolor.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CScrollbar Declaration
|
||||
//! @brief a scrollbar control
|
||||
/// @ingroup controls
|
||||
//-----------------------------------------------------------------------------
|
||||
class CScrollbar : public CControl
|
||||
{
|
||||
public:
|
||||
enum ScrollbarDirection {
|
||||
kHorizontal,
|
||||
kVertical
|
||||
};
|
||||
|
||||
CScrollbar (const CRect& size, IControlListener* listener, int32_t tag, ScrollbarDirection style, const CRect& scrollSize);
|
||||
CScrollbar (const CScrollbar& scrollbar);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CScrollbar Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
virtual void setDrawer (IScrollbarDrawer* d) { drawer = d; }
|
||||
virtual void setScrollSize (const CRect& ssize);
|
||||
virtual void setStep (float newStep) { stepValue = newStep; }
|
||||
|
||||
CRect& getScrollSize (CRect& rect) const { rect = scrollSize; return rect; }
|
||||
float getStep () const { return stepValue; }
|
||||
|
||||
virtual void setFrameColor (const CColor& color) { frameColor = color; }
|
||||
virtual void setScrollerColor (const CColor& color) { scrollerColor = color; }
|
||||
virtual void setBackgroundColor (const CColor& color) { backgroundColor = color; }
|
||||
|
||||
CColor getFrameColor () const { return frameColor; }
|
||||
CColor getScrollerColor () const { return scrollerColor; }
|
||||
CColor getBackgroundColor () const { return backgroundColor; }
|
||||
|
||||
bool getOverlayStyle () const { return overlayStyle; }
|
||||
virtual void setOverlayStyle (bool state);
|
||||
|
||||
void setMinScrollerLength (CCoord length);
|
||||
CCoord getMinScrollerLength () const { return minScrollerLenght; }
|
||||
|
||||
virtual void onVisualChange ();
|
||||
CRect getScrollerRect ();
|
||||
//@}
|
||||
|
||||
// overwrite
|
||||
void draw (CDrawContext* pContext) override;
|
||||
void onMouseWheelEvent (MouseWheelEvent& event) override;
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseCancel () override;
|
||||
CMessageResult notify (CBaseObject* sender, IdStringPtr message) override;
|
||||
void setViewSize (const CRect& newSize, bool invalid) override;
|
||||
|
||||
CMouseEventResult onMouseEntered (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseExited (CPoint& where, const CButtonState& buttons) override;
|
||||
|
||||
CLASS_METHODS(CScrollbar, CControl)
|
||||
//-----------------------------------------------------------------------------
|
||||
protected:
|
||||
~CScrollbar () noexcept override = default;
|
||||
|
||||
void drawBackground (CDrawContext* pContext);
|
||||
void drawScroller (CDrawContext* pContext, const CRect& size);
|
||||
|
||||
void calculateScrollerLength ();
|
||||
void doStepping ();
|
||||
|
||||
ScrollbarDirection direction;
|
||||
CRect scrollSize;
|
||||
CRect scrollerArea;
|
||||
|
||||
float stepValue;
|
||||
CCoord scrollerLength;
|
||||
CCoord minScrollerLenght {8.0};
|
||||
|
||||
CColor frameColor;
|
||||
CColor scrollerColor;
|
||||
CColor backgroundColor;
|
||||
|
||||
bool overlayStyle;
|
||||
bool mouseIsInside;
|
||||
|
||||
IScrollbarDrawer* drawer;
|
||||
private:
|
||||
SharedPointer<CVSTGUITimer> timer;
|
||||
CPoint startPoint;
|
||||
CRect scrollerRect;
|
||||
bool scrolling;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IScrollbarDrawer
|
||||
//-----------------------------------------------------------------------------
|
||||
{
|
||||
public:
|
||||
virtual void drawScrollbarBackground (CDrawContext* pContext, const CRect& size, CScrollbar::ScrollbarDirection direction, CScrollbar* bar) = 0;
|
||||
virtual void drawScrollbarScroller (CDrawContext* pContext, const CRect& size, CScrollbar::ScrollbarDirection direction, CScrollbar* bar) = 0;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,167 @@
|
||||
// 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 "csearchtextedit.h"
|
||||
|
||||
#include "../cframe.h"
|
||||
#include "../cgraphicspath.h"
|
||||
#include "../cdrawcontext.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
CSearchTextEdit::CSearchTextEdit (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
UTF8StringPtr txt, CBitmap* background, const int32_t style)
|
||||
: CTextEdit (size, listener, tag, nullptr, background, style)
|
||||
{
|
||||
setPlaceholderString ("Search");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CSearchTextEdit::setClearMarkInset (CPoint inset)
|
||||
{
|
||||
if (inset != clearMarkInset)
|
||||
{
|
||||
clearMarkInset = inset;
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CPoint CSearchTextEdit::getClearMarkInset () const
|
||||
{
|
||||
return clearMarkInset;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
CRect CSearchTextEdit::getClearMarkRect () const
|
||||
{
|
||||
CRect r (getViewSize ());
|
||||
if (getHoriAlign () == kRightText)
|
||||
r.right = r.left + getHeight ();
|
||||
else
|
||||
r.left = r.right - getHeight ();
|
||||
r.inset (getClearMarkInset ());
|
||||
return r;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
CMouseEventResult CSearchTextEdit::onMouseDown (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (buttons.isLeftButton ())
|
||||
{
|
||||
if (!getText ().empty ())
|
||||
{
|
||||
if (getClearMarkRect ().pointInside (where))
|
||||
{
|
||||
beginEdit ();
|
||||
setText ("");
|
||||
valueChanged ();
|
||||
endEdit ();
|
||||
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
|
||||
}
|
||||
}
|
||||
}
|
||||
return CTextEdit::onMouseDown (where, buttons);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void CSearchTextEdit::drawClearMark (CDrawContext* context) const
|
||||
{
|
||||
if (!((platformControl && !platformControl->getText ().empty ()) || !getText ().empty ()))
|
||||
return;
|
||||
|
||||
auto path = owned (context->createGraphicsPath ());
|
||||
if (path == nullptr)
|
||||
return;
|
||||
|
||||
CRect r = getClearMarkRect ();
|
||||
CColor color (fontColor);
|
||||
color.alpha /= 2;
|
||||
context->setFillColor (color);
|
||||
context->setDrawMode (kAntiAliasing);
|
||||
context->drawEllipse (r, kDrawFilled);
|
||||
double h,s,v;
|
||||
color.toHSV (h, s, v);
|
||||
v = 1. - v;
|
||||
color.fromHSV (h, s, v);
|
||||
context->setFrameColor (color);
|
||||
context->setLineWidth (2.);
|
||||
r.inset (r.getWidth () / (M_PI * 2.) + 1, r.getHeight () / (M_PI * 2.) + 1);
|
||||
path->beginSubpath (r.getTopLeft ());
|
||||
path->addLine (r.getBottomRight ());
|
||||
path->beginSubpath (r.getBottomLeft ());
|
||||
path->addLine (r.getTopRight ());
|
||||
context->setDrawMode (kAntiAliasing);
|
||||
context->drawGraphicsPath (path, CDrawContext::kPathStroked);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void CSearchTextEdit::draw (CDrawContext *pContext)
|
||||
{
|
||||
drawBack (pContext);
|
||||
drawClearMark (pContext);
|
||||
|
||||
if (platformControl)
|
||||
{
|
||||
setDirty (false);
|
||||
return;
|
||||
}
|
||||
|
||||
pContext->setDrawMode (kAntiAliasing);
|
||||
|
||||
CColor origFontColor (fontColor);
|
||||
if (getText ().empty ())
|
||||
{
|
||||
CColor color (fontColor);
|
||||
color.alpha /= 2;
|
||||
setFontColor (color);
|
||||
drawPlatformText (pContext, getPlaceholderString (), getTextRect ());
|
||||
}
|
||||
else
|
||||
drawPlatformText (pContext, getText (), getTextRect ());
|
||||
|
||||
setDirty (false);
|
||||
setFontColor (origFontColor);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CRect CSearchTextEdit::getTextRect () const
|
||||
{
|
||||
CRect rect = getViewSize ();
|
||||
CRect cmr = getClearMarkRect ();
|
||||
if (getHoriAlign () == kRightText)
|
||||
rect.left = cmr.right;
|
||||
else
|
||||
rect.right = cmr.left;
|
||||
return rect;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CRect CSearchTextEdit::platformGetSize () const
|
||||
{
|
||||
return translateToGlobal (getTextRect ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CRect CSearchTextEdit::platformGetVisibleSize () const
|
||||
{
|
||||
CRect rect = getTextRect ();
|
||||
if (getParentView ())
|
||||
rect = getParentView ()->asViewContainer ()->getVisibleSize (rect);
|
||||
else if (getFrame ())
|
||||
rect = getFrame ()->getVisibleSize (rect);
|
||||
|
||||
return translateToGlobal (rect);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CSearchTextEdit::platformTextDidChange ()
|
||||
{
|
||||
invalidRect (getClearMarkRect ());
|
||||
CTextEdit::platformTextDidChange ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ctextedit.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Search text edit field
|
||||
* @ingroup new_in_4_5
|
||||
*/
|
||||
class CSearchTextEdit : public CTextEdit
|
||||
{
|
||||
public:
|
||||
CSearchTextEdit (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
UTF8StringPtr txt = nullptr, CBitmap* background = nullptr,
|
||||
const int32_t style = 0);
|
||||
|
||||
void setClearMarkInset (CPoint inset);
|
||||
CPoint getClearMarkInset () const;
|
||||
|
||||
void draw (CDrawContext *pContext) override;
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
protected:
|
||||
void drawClearMark (CDrawContext* context) const;
|
||||
CRect getClearMarkRect () const;
|
||||
CRect getTextRect () const;
|
||||
|
||||
CRect platformGetSize () const override;
|
||||
CRect platformGetVisibleSize () const override;
|
||||
void platformTextDidChange () override;
|
||||
|
||||
CPoint clearMarkInset {2., 2.};
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,634 @@
|
||||
// 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 "csegmentbutton.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include "../cframe.h"
|
||||
#include "../cgraphicspath.h"
|
||||
#include "../events.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CSegmentButton::CSegmentButton (const CRect& size, IControlListener* listener, int32_t tag)
|
||||
: CControl (size, listener, tag), font (kNormalFont)
|
||||
{
|
||||
setWantsFocus (true);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CSegmentButton::canAddOneMoreSegment () const
|
||||
{
|
||||
return (getSelectionMode () != SelectionMode::kMultiple || segments.size () < 32);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CSegmentButton::addSegment (const Segment& segment, uint32_t index)
|
||||
{
|
||||
if (!canAddOneMoreSegment ())
|
||||
return false;
|
||||
if (index == kPushBack && segments.size () < kPushBack)
|
||||
segments.emplace_back (segment);
|
||||
else if (index < segments.size ())
|
||||
{
|
||||
auto it = segments.begin ();
|
||||
std::advance (it, index);
|
||||
segments.insert (it, segment);
|
||||
}
|
||||
updateSegmentSizes ();
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CSegmentButton::addSegment (Segment&& segment, uint32_t index)
|
||||
{
|
||||
if (!canAddOneMoreSegment ())
|
||||
return false;
|
||||
if (index == kPushBack && segments.size () < kPushBack)
|
||||
segments.emplace_back (std::move (segment));
|
||||
else if (index < segments.size ())
|
||||
{
|
||||
auto it = segments.begin ();
|
||||
std::advance (it, index);
|
||||
segments.insert (it, std::move (segment));
|
||||
}
|
||||
updateSegmentSizes ();
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::removeSegment (uint32_t index)
|
||||
{
|
||||
if (index < segments.size ())
|
||||
{
|
||||
auto it = segments.begin ();
|
||||
std::advance (it, index);
|
||||
segments.erase (it);
|
||||
}
|
||||
updateSegmentSizes ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::removeAllSegments ()
|
||||
{
|
||||
segments.clear ();
|
||||
invalid ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::valueChanged ()
|
||||
{
|
||||
switch (getSelectionMode ())
|
||||
{
|
||||
case SelectionMode::kSingle:
|
||||
case SelectionMode::kSingleToggle:
|
||||
{
|
||||
auto index = static_cast<int64_t> (getSelectedSegment ());
|
||||
for (auto& segment : segments)
|
||||
{
|
||||
bool state = index == 0;
|
||||
if (state != segment.selected)
|
||||
{
|
||||
segment.selected = state;
|
||||
invalidRect (segment.rect);
|
||||
}
|
||||
--index;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SelectionMode::kMultiple:
|
||||
{
|
||||
auto bitset = static_cast<uint32_t> (value);
|
||||
size_t index = 0;
|
||||
for (auto& segment : segments)
|
||||
{
|
||||
bool state = (hasBit (bitset, 1 << index));
|
||||
if (state != segment.selected)
|
||||
{
|
||||
segment.selected = state;
|
||||
invalidRect (segment.rect);
|
||||
}
|
||||
++index;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
CControl::valueChanged ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::setSelectedSegment (uint32_t index)
|
||||
{
|
||||
if (index >= segments.size ())
|
||||
return;
|
||||
beginEdit ();
|
||||
setValueNormalized (static_cast<float> (index) / static_cast<float> (segments.size () - 1));
|
||||
valueChanged ();
|
||||
endEdit ();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
uint32_t CSegmentButton::getSelectedSegment () const
|
||||
{
|
||||
return getSegmentIndex (getValueNormalized ());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::setStyle (Style newStyle)
|
||||
{
|
||||
if (style != newStyle)
|
||||
{
|
||||
style = newStyle;
|
||||
updateSegmentSizes ();
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::setSelectionMode (SelectionMode mode)
|
||||
{
|
||||
if (mode != selectionMode)
|
||||
{
|
||||
selectionMode = mode;
|
||||
if (isAttached ())
|
||||
{
|
||||
verifySelections ();
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::setTextTruncateMode (CDrawMethods::TextTruncateMode mode)
|
||||
{
|
||||
if (textTruncateMode != mode)
|
||||
{
|
||||
textTruncateMode = mode;
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::setGradient (CGradient* newGradient)
|
||||
{
|
||||
if (gradient != newGradient)
|
||||
{
|
||||
gradient = newGradient;
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::setGradientHighlighted (CGradient* newGradient)
|
||||
{
|
||||
if (gradientHighlighted != newGradient)
|
||||
{
|
||||
gradientHighlighted = newGradient;
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::setRoundRadius (CCoord newRoundRadius)
|
||||
{
|
||||
if (roundRadius != newRoundRadius)
|
||||
{
|
||||
roundRadius = newRoundRadius;
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::setFont (CFontRef newFont)
|
||||
{
|
||||
if (font != newFont)
|
||||
{
|
||||
font = newFont;
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::setTextAlignment (CHoriTxtAlign newAlignment)
|
||||
{
|
||||
if (textAlignment != newAlignment)
|
||||
{
|
||||
textAlignment = newAlignment;
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::setTextMargin (CCoord newMargin)
|
||||
{
|
||||
if (textMargin != newMargin)
|
||||
{
|
||||
textMargin = newMargin;
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::setTextColor (CColor newColor)
|
||||
{
|
||||
if (textColor != newColor)
|
||||
{
|
||||
textColor = newColor;
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::setTextColorHighlighted (CColor newColor)
|
||||
{
|
||||
if (textColorHighlighted != newColor)
|
||||
{
|
||||
textColorHighlighted = newColor;
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::setFrameColor (CColor newColor)
|
||||
{
|
||||
if (frameColor != newColor)
|
||||
{
|
||||
frameColor = newColor;
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::setFrameWidth (CCoord newWidth)
|
||||
{
|
||||
if (frameWidth != newWidth)
|
||||
{
|
||||
frameWidth = newWidth;
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CSegmentButton::attached (CView* parent)
|
||||
{
|
||||
if (CControl::attached (parent))
|
||||
{
|
||||
verifySelections ();
|
||||
updateSegmentSizes ();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::setViewSize (const CRect& rect, bool invalid)
|
||||
{
|
||||
CControl::setViewSize (rect, invalid);
|
||||
updateSegmentSizes ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CSegmentButton::selectSegment (uint32_t index, bool state)
|
||||
{
|
||||
beginEdit ();
|
||||
auto bitset = static_cast<uint32_t> (value);
|
||||
setBit (bitset, (1 << index), state);
|
||||
value = static_cast<float> (bitset);
|
||||
valueChanged ();
|
||||
endEdit ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CSegmentButton::isSegmentSelected (uint32_t index) const
|
||||
{
|
||||
return segments[index].selected;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CMouseEventResult CSegmentButton::onMouseDown (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (buttons.isLeftButton ())
|
||||
{
|
||||
float newValue = 0;
|
||||
float valueOffset = 1.f / (segments.size () - 1);
|
||||
for (auto& segment : segments)
|
||||
{
|
||||
if (segment.rect.pointInside (where))
|
||||
{
|
||||
uint32_t newIndex = getSegmentIndex (newValue);
|
||||
switch (selectionMode)
|
||||
{
|
||||
case SelectionMode::kSingle:
|
||||
{
|
||||
uint32_t currentIndex = getSegmentIndex (getValueNormalized ());
|
||||
if (newIndex != currentIndex)
|
||||
setSelectedSegment (newIndex);
|
||||
break;
|
||||
}
|
||||
case SelectionMode::kSingleToggle:
|
||||
{
|
||||
uint32_t currentIndex = getSegmentIndex (getValueNormalized ());
|
||||
if (newIndex != currentIndex)
|
||||
setSelectedSegment (newIndex);
|
||||
else
|
||||
{
|
||||
++currentIndex;
|
||||
if (getSegments ().size () - 1 < currentIndex)
|
||||
currentIndex = 0;
|
||||
setSelectedSegment (currentIndex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SelectionMode::kMultiple:
|
||||
{
|
||||
selectSegment (newIndex, !segment.selected);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break; // out of for loop
|
||||
}
|
||||
newValue += valueOffset;
|
||||
|
||||
// Last segment can lead to newValue > 1.0
|
||||
newValue = std::min(newValue, 1.f);
|
||||
}
|
||||
}
|
||||
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::onKeyboardEvent (KeyboardEvent& event)
|
||||
{
|
||||
if (event.type != EventType::KeyDown || event.modifiers.empty () == false ||
|
||||
event.character != 0)
|
||||
return;
|
||||
if (selectionMode != SelectionMode::kMultiple)
|
||||
{
|
||||
uint32_t newIndex = getSegmentIndex (getValueNormalized ());
|
||||
uint32_t oldIndex = newIndex;
|
||||
switch (event.virt)
|
||||
{
|
||||
case VirtualKey::Left:
|
||||
{
|
||||
if (style == Style::kHorizontal && newIndex > 0)
|
||||
newIndex--;
|
||||
else if (style == Style::kHorizontalInverse && newIndex < segments.size () - 1)
|
||||
newIndex++;
|
||||
event.consumed = true;
|
||||
break;
|
||||
}
|
||||
case VirtualKey::Right:
|
||||
{
|
||||
if (style == Style::kHorizontal && newIndex < segments.size () - 1)
|
||||
newIndex++;
|
||||
else if (style == Style::kHorizontalInverse && newIndex > 0)
|
||||
newIndex--;
|
||||
event.consumed = true;
|
||||
break;
|
||||
}
|
||||
case VirtualKey::Up:
|
||||
{
|
||||
if (style == Style::kVertical && newIndex > 0)
|
||||
newIndex--;
|
||||
else if (style == Style::kVerticalInverse && newIndex < segments.size () - 1)
|
||||
newIndex++;
|
||||
event.consumed = true;
|
||||
break;
|
||||
}
|
||||
case VirtualKey::Down:
|
||||
{
|
||||
if (style == Style::kVertical && newIndex < segments.size () - 1)
|
||||
newIndex++;
|
||||
else if (style == Style::kVerticalInverse && newIndex > 0)
|
||||
newIndex--;
|
||||
event.consumed = true;
|
||||
break;
|
||||
}
|
||||
default: return;
|
||||
}
|
||||
if (newIndex != oldIndex)
|
||||
{
|
||||
setSelectedSegment (newIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::draw (CDrawContext* pContext)
|
||||
{
|
||||
CView::draw (pContext);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::drawRect (CDrawContext* pContext, const CRect& dirtyRect)
|
||||
{
|
||||
if (getOldValue () != getValue ())
|
||||
verifySelections ();
|
||||
|
||||
bool isHorizontal = isHorizontalStyle (style);
|
||||
bool drawLines = getFrameWidth () != 0. && getFrameColor ().alpha != 0;
|
||||
auto lineWidth = getFrameWidth ();
|
||||
if (lineWidth < 0.)
|
||||
{
|
||||
lineWidth = pContext->getHairlineSize ();
|
||||
}
|
||||
SharedPointer<CGraphicsPath> path;
|
||||
if (gradient || gradientHighlighted || drawLines)
|
||||
{
|
||||
CRect r (getViewSize ());
|
||||
r.inset (lineWidth / 2., lineWidth / 2.);
|
||||
path = owned (pContext->createGraphicsPath ());
|
||||
if (!path)
|
||||
return;
|
||||
path->addRoundRect (r, getRoundRadius ());
|
||||
}
|
||||
pContext->setDrawMode (kAntiAliasing);
|
||||
if (drawLines)
|
||||
{
|
||||
pContext->setLineStyle (kLineSolid);
|
||||
pContext->setLineWidth (lineWidth);
|
||||
pContext->setFrameColor (getFrameColor ());
|
||||
}
|
||||
if (gradient)
|
||||
{
|
||||
if (isHorizontal)
|
||||
{
|
||||
pContext->fillLinearGradient (path, *gradient, getViewSize ().getTopLeft (),
|
||||
getViewSize ().getBottomLeft ());
|
||||
}
|
||||
else
|
||||
{
|
||||
pContext->fillLinearGradient (path, *gradient, getViewSize ().getTopLeft (),
|
||||
getViewSize ().getTopRight ());
|
||||
}
|
||||
}
|
||||
auto lineIndexStart = 1u;
|
||||
auto lineIndexEnd = segments.size ();
|
||||
if (isInverseStyle (style))
|
||||
{
|
||||
--lineIndexStart;
|
||||
--lineIndexEnd;
|
||||
}
|
||||
|
||||
for (uint32_t index = 0u, end = static_cast<uint32_t> (segments.size ()); index < end; ++index)
|
||||
{
|
||||
const auto& segment = segments[index];
|
||||
if (!dirtyRect.rectOverlap (segment.rect))
|
||||
continue;
|
||||
|
||||
drawClipped (pContext, segment.rect, [&] () {
|
||||
if (segment.selected && gradientHighlighted)
|
||||
{
|
||||
if (isHorizontal)
|
||||
{
|
||||
pContext->fillLinearGradient (path, *gradientHighlighted,
|
||||
segment.rect.getTopLeft (),
|
||||
segment.rect.getBottomLeft ());
|
||||
}
|
||||
else
|
||||
{
|
||||
pContext->fillLinearGradient (path, *gradientHighlighted,
|
||||
segment.rect.getTopLeft (),
|
||||
segment.rect.getTopRight ());
|
||||
}
|
||||
}
|
||||
if (segment.selected && segment.backgroundHighlighted)
|
||||
{
|
||||
segment.backgroundHighlighted->draw (pContext, segment.rect);
|
||||
}
|
||||
else if (segment.background)
|
||||
{
|
||||
segment.background->draw (pContext, segment.rect);
|
||||
}
|
||||
CDrawMethods::drawIconAndText (
|
||||
pContext, segment.selected ? segment.iconHighlighted : segment.icon,
|
||||
segment.iconPosition, textAlignment, textMargin, segment.rect, segment.name, font,
|
||||
segment.selected ? textColorHighlighted : textColor, textTruncateMode);
|
||||
});
|
||||
if (drawLines && index >= lineIndexStart && index < lineIndexEnd)
|
||||
{
|
||||
path->beginSubpath (segment.rect.getTopLeft ());
|
||||
path->addLine (isHorizontal ? segment.rect.getBottomLeft () :
|
||||
segment.rect.getTopRight ());
|
||||
}
|
||||
}
|
||||
if (drawLines)
|
||||
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
uint32_t CSegmentButton::getSegmentIndex (float value) const
|
||||
{
|
||||
if (value < 0.f || value > 1.f)
|
||||
return kPushBack;
|
||||
|
||||
return std::min<uint32_t> (static_cast<uint32_t> (segments.size () - 1),
|
||||
static_cast<uint32_t> (value * (segments.size ())));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::updateSegmentSizes ()
|
||||
{
|
||||
if (isAttached () && !segments.empty ())
|
||||
{
|
||||
switch (style)
|
||||
{
|
||||
case Style::kHorizontal:
|
||||
{
|
||||
CCoord width = getWidth () / segments.size ();
|
||||
CRect r (getViewSize ());
|
||||
r.setWidth (width);
|
||||
for (auto& segment : segments)
|
||||
{
|
||||
segment.rect = r;
|
||||
r.offset (width, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Style::kHorizontalInverse:
|
||||
{
|
||||
CCoord width = getWidth () / segments.size ();
|
||||
CRect r (getViewSize ());
|
||||
r.setWidth (width);
|
||||
for (auto it = segments.rbegin(); it != segments.rend(); ++it)
|
||||
{
|
||||
(*it).rect = r;
|
||||
r.offset (width, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Style::kVertical:
|
||||
{
|
||||
CCoord height = getHeight () / segments.size ();
|
||||
CRect r (getViewSize ());
|
||||
r.setHeight (height);
|
||||
for (auto& segment : segments)
|
||||
{
|
||||
segment.rect = r;
|
||||
r.offset (0, height);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Style::kVerticalInverse:
|
||||
{
|
||||
CCoord height = getHeight () / segments.size ();
|
||||
CRect r (getViewSize ());
|
||||
r.setHeight (height);
|
||||
for (auto it = segments.rbegin(); it != segments.rend(); ++it)
|
||||
{
|
||||
(*it).rect = r;
|
||||
r.offset (0, height);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSegmentButton::verifySelections ()
|
||||
{
|
||||
if (selectionMode == SelectionMode::kMultiple)
|
||||
{
|
||||
auto bitset = static_cast<uint32_t> (value);
|
||||
for (auto index = 0u; index < segments.size (); ++index)
|
||||
{
|
||||
segments[index].selected = (bitset & (1 << index)) != 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto selectedIndex = getSelectedSegment ();
|
||||
if (selectedIndex > segments.size ())
|
||||
selectedIndex = 0;
|
||||
for (auto& segment : segments)
|
||||
segment.selected = false;
|
||||
segments[selectedIndex].selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CSegmentButton::drawFocusOnTop ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CSegmentButton::getFocusPath (CGraphicsPath& outPath)
|
||||
{
|
||||
auto lineWidth = getFrameWidth ();
|
||||
if (lineWidth < 0.)
|
||||
lineWidth = 1.;
|
||||
CRect r (getViewSize ());
|
||||
r.inset (lineWidth / 2., lineWidth / 2.);
|
||||
outPath.addRoundRect (r, getRoundRadius ());
|
||||
CCoord focusWidth = getFrame ()->getFocusWidth ();
|
||||
r.extend (focusWidth, focusWidth);
|
||||
outPath.addRoundRect (r, getRoundRadius ());
|
||||
return true;
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,177 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ccontrol.h"
|
||||
#include "../cdrawmethods.h"
|
||||
#include "../cbitmap.h"
|
||||
#include "../cgradient.h"
|
||||
#include "../cstring.h"
|
||||
#include "../ccolor.h"
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @brief Control which draws a segmented button
|
||||
/// @ingroup new_in_4_3
|
||||
//-----------------------------------------------------------------------------
|
||||
class CSegmentButton : public CControl
|
||||
{
|
||||
public:
|
||||
enum class Style {
|
||||
/** horizontally layouted segments */
|
||||
kHorizontal,
|
||||
/** vertically layouted segments */
|
||||
kVertical,
|
||||
/** horizontally inverse layouted segments */
|
||||
kHorizontalInverse,
|
||||
/** vertically inverse layouted segments */
|
||||
kVerticalInverse,
|
||||
};
|
||||
|
||||
enum class SelectionMode
|
||||
{
|
||||
/** a single segment is selected at any time */
|
||||
kSingle,
|
||||
/** a single segment is selected at any time, when a segment is clicked which is already
|
||||
selected, the next segment is selected */
|
||||
kSingleToggle,
|
||||
/** multiple segments may be selected */
|
||||
kMultiple,
|
||||
};
|
||||
|
||||
struct Segment {
|
||||
mutable UTF8String name;
|
||||
mutable SharedPointer<CBitmap> icon;
|
||||
mutable SharedPointer<CBitmap> iconHighlighted;
|
||||
mutable SharedPointer<CBitmap> background;
|
||||
mutable SharedPointer<CBitmap> backgroundHighlighted;
|
||||
mutable CDrawMethods::IconPosition iconPosition;
|
||||
|
||||
CRect rect;
|
||||
bool selected {false};
|
||||
};
|
||||
using Segments = std::vector<Segment>;
|
||||
static constexpr uint32_t kPushBack = (std::numeric_limits<uint32_t>::max) ();
|
||||
|
||||
CSegmentButton (const CRect& size, IControlListener* listener = nullptr, int32_t tag = -1);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name Segment Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
bool addSegment (const Segment& segment, uint32_t index = kPushBack);
|
||||
bool addSegment (Segment&& segment, uint32_t index = kPushBack);
|
||||
void removeSegment (uint32_t index);
|
||||
void removeAllSegments ();
|
||||
const Segments& getSegments () const { return segments; }
|
||||
|
||||
/** set the selected segment in single selection mode */
|
||||
void setSelectedSegment (uint32_t index);
|
||||
/** get the selected segment in single selection mode */
|
||||
uint32_t getSelectedSegment () const;
|
||||
|
||||
/** set selection state for a segment in multiple selection mode */
|
||||
void selectSegment (uint32_t index, bool state);
|
||||
/** get selection state for a segment in multiple selection mode */
|
||||
bool isSegmentSelected (uint32_t index) const;
|
||||
//@}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CSegmentButton Style Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
void setStyle (Style newStyle);
|
||||
Style getStyle () const { return style; }
|
||||
|
||||
void setSelectionMode (SelectionMode mode);
|
||||
SelectionMode getSelectionMode () const { return selectionMode; }
|
||||
|
||||
void setTextTruncateMode (CDrawMethods::TextTruncateMode mode);
|
||||
CDrawMethods::TextTruncateMode getTextTruncateMode () const { return textTruncateMode; }
|
||||
|
||||
void setGradient (CGradient* newGradient);
|
||||
CGradient* getGradient () const { return gradient; }
|
||||
|
||||
void setGradientHighlighted (CGradient* newGradient);
|
||||
CGradient* getGradientHighlighted () const { return gradientHighlighted; }
|
||||
|
||||
void setRoundRadius (CCoord newRoundRadius);
|
||||
CCoord getRoundRadius () const { return roundRadius; }
|
||||
|
||||
void setFont (CFontRef font);
|
||||
CFontRef getFont () const { return font; }
|
||||
|
||||
void setTextAlignment (CHoriTxtAlign alignment);
|
||||
CHoriTxtAlign getTextAlignment () const { return textAlignment; }
|
||||
|
||||
void setTextMargin (CCoord newMargin);
|
||||
CCoord getTextMargin () const { return textMargin; }
|
||||
|
||||
void setTextColor (CColor newColor);
|
||||
CColor getTextColor () const { return textColor; }
|
||||
|
||||
void setTextColorHighlighted (CColor newColor);
|
||||
CColor getTextColorHighlighted () const { return textColorHighlighted; }
|
||||
|
||||
void setFrameColor (CColor newColor);
|
||||
CColor getFrameColor () const { return frameColor; }
|
||||
|
||||
void setFrameWidth (CCoord newWidth);
|
||||
CCoord getFrameWidth () const { return frameWidth; }
|
||||
//@}
|
||||
|
||||
// overrides
|
||||
bool attached (CView *parent) override;
|
||||
void setViewSize (const CRect& rect, bool invalid = true) override;
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
void draw (CDrawContext* pContext) override;
|
||||
void drawRect (CDrawContext* pContext, const CRect& dirtyRect) override;
|
||||
bool drawFocusOnTop () override;
|
||||
bool getFocusPath (CGraphicsPath& outPath) override;
|
||||
void valueChanged () override;
|
||||
|
||||
static bool isHorizontalStyle (Style style)
|
||||
{
|
||||
return style == Style::kHorizontal || style == Style::kHorizontalInverse;
|
||||
}
|
||||
|
||||
static bool isVerticalStyle (Style style)
|
||||
{
|
||||
return style == Style::kVertical || style == Style::kVerticalInverse;
|
||||
}
|
||||
|
||||
static bool isInverseStyle (Style style)
|
||||
{
|
||||
return style == Style::kHorizontalInverse || style == Style::kVerticalInverse;
|
||||
}
|
||||
|
||||
CLASS_METHODS (CSegmentButton, CControl)
|
||||
private:
|
||||
bool canAddOneMoreSegment () const;
|
||||
void updateSegmentSizes ();
|
||||
void verifySelections ();
|
||||
uint32_t getSegmentIndex (float value) const;
|
||||
|
||||
Segments segments;
|
||||
SharedPointer<CGradient> gradient;
|
||||
SharedPointer<CGradient> gradientHighlighted;
|
||||
SharedPointer<CFontDesc> font;
|
||||
CColor textColor {kBlackCColor};
|
||||
CColor textColorHighlighted {kWhiteCColor};
|
||||
CColor frameColor {kBlackCColor};
|
||||
CHoriTxtAlign textAlignment {kCenterText};
|
||||
CCoord textMargin {0.};
|
||||
CCoord roundRadius {5.};
|
||||
CCoord frameWidth {1.};
|
||||
Style style {Style::kHorizontal};
|
||||
SelectionMode selectionMode {SelectionMode::kSingle};
|
||||
CDrawMethods::TextTruncateMode textTruncateMode {CDrawMethods::kTextTruncateNone};
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,197 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../ccolor.h"
|
||||
#include "../enumbitset.h"
|
||||
#include "ccontrol.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class CSliderBase : public CControl, protected CMouseWheelEditingSupport
|
||||
{
|
||||
public:
|
||||
enum Style : int32_t
|
||||
{
|
||||
kHorizontal,
|
||||
kVertical,
|
||||
kLeft,
|
||||
kRight,
|
||||
kTop,
|
||||
kBottom,
|
||||
};
|
||||
using Styles = EnumBitset<Style>;
|
||||
|
||||
CSliderBase (const CRect& size, IControlListener* listener, int32_t tag);
|
||||
CSliderBase (const CSliderBase& slider);
|
||||
|
||||
void setOffsetHandle (const CPoint& val);
|
||||
CPoint getOffsetHandle () const;
|
||||
|
||||
void setStyle (Styles style);
|
||||
Styles getStyle () const;
|
||||
bool isStyleHorizontal () const;
|
||||
bool isStyleRight () const;
|
||||
bool isStyleBottom () const;
|
||||
bool isInverseStyle () const;
|
||||
|
||||
void setZoomFactor (float val);
|
||||
float getZoomFactor () const;
|
||||
|
||||
void setSliderMode (CSliderMode mode);
|
||||
CSliderMode getSliderMode () const;
|
||||
CSliderMode getEffectiveSliderMode () const;
|
||||
|
||||
static void setGlobalMode (CSliderMode mode);
|
||||
static CSliderMode getGlobalMode ();
|
||||
|
||||
// overrides
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseCancel () override;
|
||||
|
||||
void onMouseWheelEvent (MouseWheelEvent& event) override;
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
|
||||
void setViewSize (const CRect& rect, bool invalid) override;
|
||||
|
||||
static bool kAlwaysUseZoomFactor;
|
||||
|
||||
protected:
|
||||
~CSliderBase () noexcept;
|
||||
|
||||
CRect calculateHandleRect (float normValue) const;
|
||||
|
||||
// for sub-classes to access private variables:
|
||||
void setHandleSizePrivate (CCoord width, CCoord height);
|
||||
CPoint getHandleSizePrivate () const;
|
||||
CPoint getControlSizePrivate () const;
|
||||
void setHandleRangePrivate (CCoord range);
|
||||
void setHandleMinPosPrivate (CCoord pos);
|
||||
CCoord getHandleMinPosPrivate () const;
|
||||
|
||||
private:
|
||||
void updateInternalHandleValues ();
|
||||
float calculateDelta (const CPoint& where, CRect* handleRect = nullptr) const;
|
||||
void doRamping ();
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CSlider Declaration
|
||||
//! @brief a slider control
|
||||
/// @ingroup controls
|
||||
//------------------------------------------------------------------------
|
||||
class CSlider : public CSliderBase
|
||||
{
|
||||
private:
|
||||
public:
|
||||
CSlider (const CRect& size, IControlListener* listener, int32_t tag, int32_t iMinPos,
|
||||
int32_t iMaxPos, CBitmap* handle, CBitmap* background,
|
||||
const CPoint& offset = CPoint (0, 0), Styles style = {{kLeft, kHorizontal}});
|
||||
CSlider (const CRect& rect, IControlListener* listener, int32_t tag, const CPoint& offsetHandle,
|
||||
int32_t rangeHandle, CBitmap* handle, CBitmap* background,
|
||||
const CPoint& offset = CPoint (0, 0), Styles style = {{kLeft, kHorizontal}});
|
||||
CSlider (const CSlider& slider);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/// @name CSlider Methods
|
||||
//------------------------------------------------------------------------
|
||||
//@{
|
||||
VSTGUI_DEPRECATED (
|
||||
/** \deprecated use setBackgroundOffset */
|
||||
virtual void setOffset (const CPoint& val);)
|
||||
VSTGUI_DEPRECATED (
|
||||
/** \deprecated use getBackgroundOffset*/
|
||||
virtual CPoint getOffset () const;)
|
||||
|
||||
/** set background draw offset */
|
||||
void setBackgroundOffset (const CPoint& offset);
|
||||
/** get background draw offset */
|
||||
CPoint getBackgroundOffset () const;
|
||||
|
||||
virtual void setHandle (CBitmap* pHandle);
|
||||
virtual CBitmap* getHandle () const;
|
||||
//@}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/// @name Draw Style Methods
|
||||
//------------------------------------------------------------------------
|
||||
//@{
|
||||
enum DrawStyle
|
||||
{
|
||||
kDrawFrame = 1 << 0,
|
||||
kDrawBack = 1 << 1,
|
||||
kDrawValue = 1 << 2,
|
||||
kDrawValueFromCenter = 1 << 3,
|
||||
kDrawInverted = 1 << 4
|
||||
};
|
||||
|
||||
virtual void setDrawStyle (int32_t style);
|
||||
virtual void setFrameWidth (CCoord width);
|
||||
virtual void setFrameColor (CColor color);
|
||||
virtual void setBackColor (CColor color);
|
||||
virtual void setValueColor (CColor color);
|
||||
|
||||
int32_t getDrawStyle () const;
|
||||
CCoord getFrameWidth () const;
|
||||
CColor getFrameColor () const;
|
||||
CColor getBackColor () const;
|
||||
CColor getValueColor () const;
|
||||
//@}
|
||||
|
||||
// overrides
|
||||
void draw (CDrawContext*) override;
|
||||
bool sizeToFit () override;
|
||||
|
||||
CLASS_METHODS (CSlider, CControl)
|
||||
protected:
|
||||
~CSlider () noexcept override;
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CVerticalSlider Declaration
|
||||
//! @brief a vertical slider control
|
||||
/// @ingroup controls
|
||||
//------------------------------------------------------------------------
|
||||
class CVerticalSlider : public CSlider
|
||||
{
|
||||
public:
|
||||
CVerticalSlider (const CRect& size, IControlListener* listener, int32_t tag, int32_t iMinPos,
|
||||
int32_t iMaxPos, CBitmap* handle, CBitmap* background,
|
||||
const CPoint& offset = CPoint (0, 0), Styles style = kBottom);
|
||||
CVerticalSlider (const CRect& rect, IControlListener* listener, int32_t tag,
|
||||
const CPoint& offsetHandle, int32_t rangeHandle, CBitmap* handle,
|
||||
CBitmap* background, const CPoint& offset = CPoint (0, 0),
|
||||
Styles style = kBottom);
|
||||
CVerticalSlider (const CVerticalSlider& slider) = default;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CHorizontalSlider Declaration
|
||||
//! @brief a horizontal slider control
|
||||
/// @ingroup controls
|
||||
//------------------------------------------------------------------------
|
||||
class CHorizontalSlider : public CSlider
|
||||
{
|
||||
public:
|
||||
CHorizontalSlider (const CRect& size, IControlListener* listener, int32_t tag, int32_t iMinPos,
|
||||
int32_t iMaxPos, CBitmap* handle, CBitmap* background,
|
||||
const CPoint& offset = CPoint (0, 0), Styles style = kRight);
|
||||
CHorizontalSlider (const CRect& rect, IControlListener* listener, int32_t tag,
|
||||
const CPoint& offsetHandle, int32_t rangeHandle, CBitmap* handle,
|
||||
CBitmap* background, const CPoint& offset = CPoint (0, 0),
|
||||
Styles style = kRight);
|
||||
CHorizontalSlider (const CHorizontalSlider& slider) = default;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,130 @@
|
||||
// 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 "cspecialdigit.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include "../cbitmap.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CSpecialDigit
|
||||
//------------------------------------------------------------------------
|
||||
/*! @class CSpecialDigit
|
||||
Can be used to display a counter with maximum 7 digits.
|
||||
All digit have the same size and are stacked in height in the bitmap.
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CSpecialDigit constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param dwPos actual value
|
||||
* @param iNumbers amount of numbers (max 7)
|
||||
* @param xpos array of all X positions, can be NULL
|
||||
* @param ypos array of all Y positions, can be NULL
|
||||
* @param width width of one number in the bitmap
|
||||
* @param height height of one number in the bitmap
|
||||
* @param background bitmap
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CSpecialDigit::CSpecialDigit (const CRect& size, IControlListener* listener, int32_t tag, int32_t dwPos, int32_t inNumbers, int32_t* xpos, int32_t* ypos, int32_t width, int32_t height, CBitmap* background)
|
||||
: CControl (size, listener, tag, background)
|
||||
, iNumbers (inNumbers)
|
||||
, width (width)
|
||||
, height (height)
|
||||
{
|
||||
setValue ((float)dwPos); // actual value
|
||||
|
||||
if (iNumbers > 7)
|
||||
iNumbers = 7;
|
||||
|
||||
if (xpos == nullptr)
|
||||
{
|
||||
// automatically init xpos/ypos if not provided by caller
|
||||
const int32_t numw = (const int32_t)background->getWidth();
|
||||
int32_t x = (int32_t)size.left;
|
||||
for (int32_t i = 0; i < inNumbers; i++)
|
||||
{
|
||||
this->xpos[i] = x;
|
||||
this->ypos[i] = (int32_t)size.top;
|
||||
x += numw;
|
||||
}
|
||||
}
|
||||
else if (xpos && ypos)
|
||||
{
|
||||
// store coordinates of x/y pos of each digit
|
||||
for (int32_t i = 0; i < inNumbers; i++)
|
||||
{
|
||||
this->xpos[i] = xpos[i];
|
||||
this->ypos[i] = ypos[i];
|
||||
}
|
||||
}
|
||||
|
||||
setMax ((float)pow (10.f, (float)inNumbers) - 1.0f);
|
||||
setMin (0.0f);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CSpecialDigit::CSpecialDigit (const CSpecialDigit& v)
|
||||
: CControl (v)
|
||||
, iNumbers (v.iNumbers)
|
||||
, width (v.width)
|
||||
, height (v.height)
|
||||
{
|
||||
for (int32_t i = 0; i < 7; i++)
|
||||
{
|
||||
xpos[i] = v.xpos[i];
|
||||
ypos[i] = v.ypos[i];
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CSpecialDigit::draw (CDrawContext *pContext)
|
||||
{
|
||||
CPoint where;
|
||||
CRect rectDest;
|
||||
int32_t i, j;
|
||||
int32_t one_digit[16] = {};
|
||||
|
||||
int32_t dwValue = static_cast<int32_t> (getValue ());
|
||||
int32_t intMax = static_cast<int32_t> (getMax ());
|
||||
if (dwValue > intMax)
|
||||
dwValue = intMax;
|
||||
else if (dwValue < static_cast<int32_t> (getMin ()))
|
||||
dwValue = static_cast<int32_t> (getMin ());
|
||||
|
||||
for (i = 0, j = (intMax + 1) / 10; i < iNumbers; i++, j /= 10)
|
||||
{
|
||||
one_digit[i] = dwValue / j;
|
||||
dwValue -= (one_digit[i] * j);
|
||||
}
|
||||
|
||||
where.x = 0;
|
||||
for (i = 0; i < iNumbers; i++)
|
||||
{
|
||||
j = one_digit[i];
|
||||
if (j > 9)
|
||||
j = 9;
|
||||
|
||||
rectDest.left = (CCoord)xpos[i];
|
||||
rectDest.top = (CCoord)ypos[i];
|
||||
|
||||
rectDest.right = rectDest.left + width;
|
||||
rectDest.bottom = rectDest.top + height;
|
||||
|
||||
// where = src from bitmap
|
||||
where.y = (CCoord)j * height;
|
||||
if (getDrawBackground ())
|
||||
{
|
||||
getDrawBackground ()->draw (pContext, rectDest, where);
|
||||
}
|
||||
}
|
||||
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ccontrol.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CSpecialDigit Declaration
|
||||
//! @brief special display with custom numbers (0...9)
|
||||
/// @ingroup views
|
||||
//-----------------------------------------------------------------------------
|
||||
class CSpecialDigit : public CControl
|
||||
{
|
||||
public:
|
||||
CSpecialDigit (const CRect& size, IControlListener* listener, int32_t tag, int32_t dwPos, int32_t iNumbers, int32_t* xpos, int32_t* ypos, int32_t width, int32_t height, CBitmap* background);
|
||||
CSpecialDigit (const CSpecialDigit& digit);
|
||||
|
||||
void draw (CDrawContext*) override;
|
||||
|
||||
CLASS_METHODS(CSpecialDigit, CControl)
|
||||
protected:
|
||||
~CSpecialDigit () noexcept override = default;
|
||||
int32_t iNumbers;
|
||||
int32_t xpos[7];
|
||||
int32_t ypos[7];
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,334 @@
|
||||
// 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 "csplashscreen.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include "../cbitmap.h"
|
||||
#include "../cframe.h"
|
||||
#include "../animation/animations.h"
|
||||
#include "../animation/timingfunctions.h"
|
||||
#include "../events.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
/// @cond ignore
|
||||
//------------------------------------------------------------------------
|
||||
class CDefaultSplashScreenView : public CControl
|
||||
{
|
||||
public:
|
||||
CDefaultSplashScreenView (const CRect& size, IControlListener* listener, CBitmap* bitmap,
|
||||
const CPoint& offset)
|
||||
: CControl (size, listener), offset (offset)
|
||||
{
|
||||
setBackground (bitmap);
|
||||
}
|
||||
|
||||
void draw (CDrawContext *pContext) override
|
||||
{
|
||||
if (getDrawBackground ())
|
||||
getDrawBackground ()->draw (pContext, getViewSize (), offset);
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override
|
||||
{
|
||||
if (buttons.isLeftButton ())
|
||||
{
|
||||
valueChanged ();
|
||||
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
|
||||
}
|
||||
return kMouseEventNotHandled;
|
||||
}
|
||||
CLASS_METHODS(CDefaultSplashScreenView, CControl)
|
||||
protected:
|
||||
CPoint offset;
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CSplashScreen
|
||||
//------------------------------------------------------------------------
|
||||
/*! @class CSplashScreen
|
||||
One click on its activated region and its bitmap or view is displayed, in this state the other controls can not be used,
|
||||
and another click on the displayed area will leave the modal mode.
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CSplashScreen constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param background the bitmap
|
||||
* @param toDisplay the region where to display the bitmap
|
||||
* @param offset offset of background bitmap
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CSplashScreen::CSplashScreen (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
CBitmap* background, const CRect& toDisplay, const CPoint& offset)
|
||||
: CControl (size, listener, tag, background), toDisplay (toDisplay), offset (offset)
|
||||
{
|
||||
modalView = new CDefaultSplashScreenView (toDisplay, this, background, offset);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CSplashScreen constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param splashView the view to show
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CSplashScreen::CSplashScreen (const CRect& size, IControlListener* listener, int32_t tag, CView* splashView)
|
||||
: CControl (size, listener, tag)
|
||||
, modalView (splashView)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CSplashScreen::CSplashScreen (const CSplashScreen& v)
|
||||
: CControl (v)
|
||||
, toDisplay (v.toDisplay)
|
||||
, keepSize (v.keepSize)
|
||||
, offset (v.offset)
|
||||
{
|
||||
modalView = static_cast<CView*> (v.modalView->newCopy ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CSplashScreen::~CSplashScreen () noexcept
|
||||
{
|
||||
if (modalView)
|
||||
modalView->forget ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CSplashScreen::draw (CDrawContext *pContext)
|
||||
{
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CSplashScreen::hitTest (const CPoint& where, const Event& event)
|
||||
{
|
||||
bool result = CView::hitTest (where, event);
|
||||
if (result)
|
||||
{
|
||||
if (auto mouseEvent = asMouseEvent (event))
|
||||
{
|
||||
if (!mouseEvent->buttonState.isLeft ())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CSplashScreen::onMouseDown (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (buttons & kLButton)
|
||||
{
|
||||
value = (value == getMax ()) ? getMin () : getMax ();
|
||||
if (value == getMax () && !modalViewSessionID && modalView)
|
||||
{
|
||||
if (auto frame = getFrame ())
|
||||
{
|
||||
if (modalView)
|
||||
{
|
||||
if ((modalViewSessionID = frame->beginModalViewSession (modalView)))
|
||||
{
|
||||
modalView->remember ();
|
||||
CControl::valueChanged ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
|
||||
}
|
||||
return kMouseEventNotHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CSplashScreen::valueChanged (CControl *pControl)
|
||||
{
|
||||
if (pControl == modalView)
|
||||
{
|
||||
unSplash ();
|
||||
CControl::valueChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CSplashScreen::unSplash ()
|
||||
{
|
||||
value = getMin ();
|
||||
|
||||
if (auto frame = getFrame ())
|
||||
{
|
||||
if (modalViewSessionID)
|
||||
{
|
||||
if (modalView)
|
||||
modalView->invalid ();
|
||||
frame->endModalViewSession (*modalViewSessionID);
|
||||
modalViewSessionID = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
CAnimationSplashScreen::CAnimationSplashScreen (const CRect& size, int32_t tag, CBitmap* background, CBitmap* splashBitmap)
|
||||
: CSplashScreen (size, nullptr, tag, splashBitmap, CRect (0, 0, 0, 0))
|
||||
{
|
||||
CView::setBackground (background);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CAnimationSplashScreen::setSplashBitmap (CBitmap* bitmap)
|
||||
{
|
||||
if (modalView)
|
||||
{
|
||||
modalView->setBackground (bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CBitmap* CAnimationSplashScreen::getSplashBitmap () const
|
||||
{
|
||||
if (modalView)
|
||||
return modalView->getBackground ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CAnimationSplashScreen::setSplashRect (const CRect& splashRect)
|
||||
{
|
||||
if (modalView)
|
||||
{
|
||||
modalView->setViewSize (splashRect);
|
||||
modalView->setMouseableArea (splashRect);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const CRect& CAnimationSplashScreen::getSplashRect () const
|
||||
{
|
||||
if (modalView)
|
||||
return modalView->getViewSize ();
|
||||
return getViewSize ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CAnimationSplashScreen::onMouseDown (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
CMouseEventResult result = CSplashScreen::onMouseDown (where, buttons);
|
||||
if (modalView && value == getMax ())
|
||||
{
|
||||
createAnimation (animationIndex, animationTime, modalView, false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CAnimationSplashScreen::unSplash ()
|
||||
{
|
||||
value = getMin ();
|
||||
|
||||
if (auto frame = getFrame ())
|
||||
{
|
||||
if (frame->getModalView () == modalView)
|
||||
{
|
||||
if (!createAnimation (animationIndex, animationTime, modalView, true))
|
||||
{
|
||||
if (modalView)
|
||||
modalView->invalid ();
|
||||
if (modalViewSessionID)
|
||||
{
|
||||
frame->endModalViewSession (*modalViewSessionID);
|
||||
modalViewSessionID = {};
|
||||
}
|
||||
setMouseEnabled (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CAnimationSplashScreen::draw (CDrawContext *pContext)
|
||||
{
|
||||
CView::draw (pContext);
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CAnimationSplashScreen::sizeToFit ()
|
||||
{
|
||||
if (modalView && modalView->getBackground ())
|
||||
{
|
||||
CRect r = modalView->getViewSize ();
|
||||
r.setWidth (modalView->getBackground ()->getWidth ());
|
||||
r.setHeight (modalView->getBackground ()->getHeight ());
|
||||
if (getFrame ())
|
||||
{
|
||||
r.centerInside (getFrame ()->getViewSize ());
|
||||
}
|
||||
modalView->setViewSize (r);
|
||||
modalView->setMouseableArea (r);
|
||||
}
|
||||
if (getBackground ())
|
||||
{
|
||||
CRect r = getViewSize ();
|
||||
r.setWidth (getBackground ()->getWidth ());
|
||||
r.setHeight (getBackground ()->getHeight ());
|
||||
setViewSize (r);
|
||||
setMouseableArea (r);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CAnimationSplashScreen::createAnimation (uint32_t animIndex, uint32_t animTime,
|
||||
CView* splashView, bool removeViewAnimation)
|
||||
{
|
||||
if (!isAttached ())
|
||||
return false;
|
||||
switch (animIndex)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (removeViewAnimation)
|
||||
{
|
||||
splashView->setMouseEnabled (false);
|
||||
splashView->addAnimation (
|
||||
"AnimationSplashScreenAnimation", new Animation::AlphaValueAnimation (0.f),
|
||||
new Animation::PowerTimingFunction (animTime, 2),
|
||||
[this] (CView*, const IdStringPtr, Animation::IAnimationTarget*) {
|
||||
if (modalView)
|
||||
{
|
||||
modalView->invalid ();
|
||||
modalView->setMouseEnabled (true);
|
||||
}
|
||||
if (modalViewSessionID)
|
||||
{
|
||||
if (auto frame = getFrame ())
|
||||
frame->endModalViewSession (*modalViewSessionID);
|
||||
modalViewSessionID = {};
|
||||
}
|
||||
setMouseEnabled (true);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
setMouseEnabled (false);
|
||||
splashView->setAlphaValue (0.f);
|
||||
splashView->addAnimation ("AnimationSplashScreenAnimation", new Animation::AlphaValueAnimation (1.f), new Animation::PowerTimingFunction (animTime, 2));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,99 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ccontrol.h"
|
||||
#include "icontrollistener.h"
|
||||
#include "../optional.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CSplashScreen Declaration
|
||||
//!
|
||||
/// @ingroup views
|
||||
//-----------------------------------------------------------------------------
|
||||
class CSplashScreen : public CControl, public IControlListener
|
||||
{
|
||||
public:
|
||||
CSplashScreen (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background, const CRect& toDisplay, const CPoint& offset = CPoint (0, 0));
|
||||
CSplashScreen (const CRect& size, IControlListener* listener, int32_t tag, CView* splashView);
|
||||
CSplashScreen (const CSplashScreen& splashScreen);
|
||||
|
||||
void draw (CDrawContext*) override;
|
||||
bool hitTest (const CPoint& where, const Event& event) override;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CSplashScreen Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
virtual void unSplash ();
|
||||
|
||||
/** set the area in which the splash will be displayed */
|
||||
virtual void setDisplayArea (const CRect& rect) { toDisplay = rect; }
|
||||
/** get the area in which the splash will be displayed */
|
||||
virtual CRect& getDisplayArea (CRect& rect) const { rect = toDisplay; return rect; }
|
||||
//@}
|
||||
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
|
||||
CLASS_METHODS(CSplashScreen, CControl)
|
||||
protected:
|
||||
~CSplashScreen () noexcept override;
|
||||
using CControl::valueChanged;
|
||||
void valueChanged (CControl *pControl) override;
|
||||
|
||||
CRect toDisplay;
|
||||
CRect keepSize;
|
||||
CPoint offset;
|
||||
CView* modalView{nullptr};
|
||||
Optional<ModalViewSessionID> modalViewSessionID;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CAnimationSplashScreen Declaration
|
||||
/// @brief a splash screen which animates the opening and closing of the splash bitmap
|
||||
/// @ingroup views
|
||||
/// @ingroup new_in_4_0
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAnimationSplashScreen : public CSplashScreen
|
||||
{
|
||||
public:
|
||||
CAnimationSplashScreen (const CRect& size, int32_t tag, CBitmap* background, CBitmap* splashBitmap);
|
||||
CAnimationSplashScreen (const CAnimationSplashScreen& splashScreen) = default;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CAnimationSplashScreen Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
virtual void setSplashBitmap (CBitmap* bitmap);
|
||||
CBitmap* getSplashBitmap () const;
|
||||
|
||||
virtual void setSplashRect (const CRect& splashRect);
|
||||
const CRect& getSplashRect () const;
|
||||
|
||||
virtual void setAnimationIndex (uint32_t index) { animationIndex = index; }
|
||||
uint32_t getAnimationIndex () const { return animationIndex; }
|
||||
|
||||
virtual void setAnimationTime (uint32_t time) { animationTime = time; }
|
||||
uint32_t getAnimationTime () const { return animationTime; }
|
||||
|
||||
/** create the animation. subclasses can override this to add special animations */
|
||||
virtual bool createAnimation (uint32_t animationIndex, uint32_t animationTime, CView* splashView, bool removeViewAnimation);
|
||||
//@}
|
||||
|
||||
void unSplash () override;
|
||||
void draw (CDrawContext*) override;
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
bool sizeToFit () override;
|
||||
|
||||
protected:
|
||||
~CAnimationSplashScreen () noexcept override = default;
|
||||
|
||||
uint32_t animationIndex{0};
|
||||
uint32_t animationTime{500};
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,222 @@
|
||||
// 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 "cstringlist.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include "../ccolor.h"
|
||||
#include "../cfont.h"
|
||||
#include "../platform/platformfactory.h"
|
||||
#include "../platform/iplatformstring.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct StringListControlDrawer::Impl
|
||||
{
|
||||
Func func;
|
||||
|
||||
SharedPointer<CFontDesc> font {kNormalFont};
|
||||
CColor fontColor {kBlackCColor};
|
||||
CColor fontColorSelected {kWhiteCColor};
|
||||
CColor backColor {kWhiteCColor};
|
||||
CColor backColorSelected {kBlueCColor};
|
||||
CColor hoverColor {MakeCColor (0, 0, 0, 100)};
|
||||
CColor lineColor {kBlackCColor};
|
||||
CCoord lineWidth {1.};
|
||||
CCoord textInset {5.};
|
||||
CHoriTxtAlign textAlign {kLeftText};
|
||||
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
StringListControlDrawer::StringListControlDrawer ()
|
||||
{
|
||||
impl = std::unique_ptr<Impl> (new Impl);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
StringListControlDrawer::~StringListControlDrawer () noexcept = default;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void StringListControlDrawer::setStringProvider (Func&& getStringFunc)
|
||||
{
|
||||
impl->func = std::move (getStringFunc);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void StringListControlDrawer::setStringProvider (const Func& getStringFunc)
|
||||
{
|
||||
impl->func = getStringFunc;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void StringListControlDrawer::setFont (CFontRef f)
|
||||
{
|
||||
impl->font = f;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void StringListControlDrawer::setFontColor (CColor color)
|
||||
{
|
||||
impl->fontColor = color;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void StringListControlDrawer::setSelectedFontColor (CColor color)
|
||||
{
|
||||
impl->fontColorSelected = color;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void StringListControlDrawer::setBackColor (CColor color)
|
||||
{
|
||||
impl->backColor = color;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void StringListControlDrawer::setSelectedBackColor (CColor color)
|
||||
{
|
||||
impl->backColorSelected = color;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void StringListControlDrawer::setHoverColor (CColor color)
|
||||
{
|
||||
impl->hoverColor = color;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void StringListControlDrawer::setLineColor (CColor color)
|
||||
{
|
||||
impl->lineColor = color;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void StringListControlDrawer::setLineWidth (CCoord width)
|
||||
{
|
||||
impl->lineWidth = width;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void StringListControlDrawer::setTextInset (CCoord inset)
|
||||
{
|
||||
impl->textInset = inset;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void StringListControlDrawer::setTextAlign (CHoriTxtAlign align)
|
||||
{
|
||||
impl->textAlign = align;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CFontRef StringListControlDrawer::getFont () const
|
||||
{
|
||||
return impl->font;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CColor StringListControlDrawer::getFontColor () const
|
||||
{
|
||||
return impl->fontColor;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CColor StringListControlDrawer::getSelectedFontColor () const
|
||||
{
|
||||
return impl->fontColorSelected;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CColor StringListControlDrawer::getBackColor () const
|
||||
{
|
||||
return impl->backColor;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CColor StringListControlDrawer::getSelectedBackColor () const
|
||||
{
|
||||
return impl->backColorSelected;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CColor StringListControlDrawer::getHoverColor () const
|
||||
{
|
||||
return impl->hoverColor;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CColor StringListControlDrawer::getLineColor () const
|
||||
{
|
||||
return impl->lineColor;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CCoord StringListControlDrawer::getLineWidth () const
|
||||
{
|
||||
return impl->lineWidth;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CCoord StringListControlDrawer::getTextInset () const
|
||||
{
|
||||
return impl->textInset;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CHoriTxtAlign StringListControlDrawer::getTextAlign () const
|
||||
{
|
||||
return impl->textAlign;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void StringListControlDrawer::drawBackground (CDrawContext* context, CRect size)
|
||||
{
|
||||
context->setFillColor (impl->backColor);
|
||||
context->drawRect (size, kDrawFilled);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void StringListControlDrawer::drawRow (CDrawContext* context, CRect size, Row row)
|
||||
{
|
||||
context->setDrawMode (kAntiAliasing);
|
||||
if (row.isHovered ())
|
||||
{
|
||||
context->setFillColor (impl->hoverColor);
|
||||
context->drawRect (size, kDrawFilled);
|
||||
}
|
||||
if (row.isSelected ())
|
||||
{
|
||||
context->setFillColor (impl->backColorSelected);
|
||||
context->drawRect (size, kDrawFilled);
|
||||
}
|
||||
|
||||
auto lw = impl->lineWidth < 0. ? context->getHairlineSize () : impl->lineWidth;
|
||||
size.bottom -= lw * 0.5;
|
||||
|
||||
if (!(row.isLastRow ()) && lw != 0.)
|
||||
{
|
||||
context->setDrawMode (kAntiAliasing | kNonIntegralMode);
|
||||
context->setFrameColor (impl->lineColor);
|
||||
context->setLineWidth (lw);
|
||||
context->drawLine (size.getBottomLeft (), size.getBottomRight ());
|
||||
}
|
||||
if (auto string = getString (row))
|
||||
{
|
||||
size.inset (impl->textInset, 0);
|
||||
context->setFontColor (row.isSelected () ? impl->fontColorSelected : impl->fontColor);
|
||||
context->setFont (impl->font);
|
||||
context->drawString (string, size, impl->textAlign);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
PlatformStringPtr StringListControlDrawer::getString (int32_t row) const
|
||||
{
|
||||
return impl->func ? impl->func (row) : getPlatformFactory ().createString (toString (row));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,66 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "clistcontrol.h"
|
||||
#include "../cdrawdefs.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** A specialized list control drawer to draw strings
|
||||
*
|
||||
* You set an instance of this class as the drawer in a CListControl instance and it draws the
|
||||
* strings you setup via the provider function.
|
||||
*
|
||||
* @ingroup new_in_4_9
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
class StringListControlDrawer : public IListControlDrawer, public NonAtomicReferenceCounted
|
||||
{
|
||||
public:
|
||||
using Func = std::function<PlatformStringPtr (int32_t row)>;
|
||||
|
||||
StringListControlDrawer ();
|
||||
~StringListControlDrawer () noexcept override;
|
||||
|
||||
void setStringProvider (Func&& getStringFunc);
|
||||
void setStringProvider (const Func& getStringFunc);
|
||||
|
||||
void setFont (CFontRef f);
|
||||
void setFontColor (CColor color);
|
||||
void setSelectedFontColor (CColor color);
|
||||
void setBackColor (CColor color);
|
||||
void setSelectedBackColor (CColor color);
|
||||
void setHoverColor (CColor color);
|
||||
void setLineColor (CColor color);
|
||||
void setLineWidth (CCoord width);
|
||||
void setTextInset (CCoord inset);
|
||||
void setTextAlign (CHoriTxtAlign align);
|
||||
|
||||
CFontRef getFont () const;
|
||||
CColor getFontColor () const;
|
||||
CColor getSelectedFontColor () const;
|
||||
CColor getBackColor () const;
|
||||
CColor getSelectedBackColor () const;
|
||||
CColor getHoverColor () const;
|
||||
CColor getLineColor () const;
|
||||
CCoord getLineWidth () const;
|
||||
CCoord getTextInset () const;
|
||||
CHoriTxtAlign getTextAlign () const;
|
||||
|
||||
void drawBackground (CDrawContext* context, CRect size) override;
|
||||
void drawRow (CDrawContext* context, CRect size, Row row) override;
|
||||
|
||||
private:
|
||||
PlatformStringPtr getString (int32_t row) const;
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,770 @@
|
||||
// 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 "cswitch.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include "../cbitmap.h"
|
||||
#include "../cvstguitimer.h"
|
||||
#include "../events.h"
|
||||
#include "../algorithm.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
bool CSwitchBase::useLegacyIndexCalculation = false;
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CSwitchBase::CSwitchBase (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
CBitmap* background)
|
||||
: CControl (size, listener, tag, background)
|
||||
{
|
||||
setDefaultValue (0.f);
|
||||
setWantsFocus (true);
|
||||
}
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
//------------------------------------------------------------------------
|
||||
CSwitchBase::CSwitchBase (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
int32_t subPixmaps, CCoord heightOfOneImage, int32_t iMaxPositions,
|
||||
CBitmap* background, const CPoint& offset)
|
||||
: CControl (size, listener, tag, background), offset (offset)
|
||||
{
|
||||
setNumSubPixmaps (subPixmaps);
|
||||
setHeightOfOneImage (heightOfOneImage);
|
||||
setDefaultValue (0.f);
|
||||
setWantsFocus (true);
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CSwitchBase::CSwitchBase (const CSwitchBase& other) : CControl (other)
|
||||
{
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
offset = other.offset;
|
||||
setNumSubPixmaps (other.subPixmaps);
|
||||
setHeightOfOneImage (other.heightOfOneImage);
|
||||
#endif
|
||||
setWantsFocus (true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
int32_t CSwitchBase::normalizedToIndex (float norm) const
|
||||
{
|
||||
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (getDrawBackground ()))
|
||||
{
|
||||
return getMultiFrameBitmapIndex (*mfb, norm);
|
||||
}
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
#include "../private/disabledeprecatedmessage.h"
|
||||
if (useLegacyIndexCalculation)
|
||||
#include "../private/enabledeprecatedmessage.h"
|
||||
return static_cast<int32_t> (norm * (getNumSubPixmaps () - 1) + 0.5f);
|
||||
return normalizedToSteps (norm, getNumSubPixmaps () - 1);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
float CSwitchBase::indexToNormalized (int32_t index) const
|
||||
{
|
||||
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (getDrawBackground ()))
|
||||
{
|
||||
return getNormValueFromMultiFrameBitmapIndex (*mfb, static_cast<uint16_t> (index));
|
||||
}
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
return static_cast<float> (index) / static_cast<float> (getNumSubPixmaps () - 1);
|
||||
#else
|
||||
return 0.f;
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CSwitchBase::draw (CDrawContext* pContext)
|
||||
{
|
||||
if (auto bitmap = getDrawBackground ())
|
||||
{
|
||||
float norm = getValueNormalized ();
|
||||
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
|
||||
{
|
||||
auto frameIndex = getMultiFrameBitmapIndex (*mfb, norm);
|
||||
if (inverseBitmap)
|
||||
frameIndex = getInverseIndex (*mfb, frameIndex);
|
||||
mfb->drawFrame (pContext, frameIndex, getViewSize ().getTopLeft ());
|
||||
}
|
||||
else
|
||||
{
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
if (inverseBitmap)
|
||||
norm = 1.f - norm;
|
||||
// source position in bitmap
|
||||
CPoint where (0, heightOfOneImage * normalizedToIndex (norm));
|
||||
bitmap->draw (pContext, getViewSize (), where);
|
||||
#else
|
||||
bitmap->draw (pContext, getViewSize ());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CSwitchBase::sizeToFit ()
|
||||
{
|
||||
if (auto bitmap = getDrawBackground ())
|
||||
{
|
||||
CRect vs (getViewSize ());
|
||||
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
|
||||
{
|
||||
vs.setSize (mfb->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;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CSwitchBase::onMouseDown (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (!(buttons & kLButton))
|
||||
return kMouseEventNotHandled;
|
||||
|
||||
coef = calculateCoef ();
|
||||
|
||||
beginEdit ();
|
||||
|
||||
mouseStartValue = getValue ();
|
||||
return onMouseMoved (where, buttons);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CSwitchBase::onMouseUp (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (isEditing ())
|
||||
endEdit ();
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CSwitchBase::onMouseCancel ()
|
||||
{
|
||||
if (isEditing ())
|
||||
{
|
||||
value = mouseStartValue;
|
||||
if (isDirty ())
|
||||
{
|
||||
valueChanged ();
|
||||
invalid ();
|
||||
}
|
||||
endEdit ();
|
||||
}
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CSwitchBase::onMouseMoved (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (isEditing ())
|
||||
{
|
||||
float norm = calcNormFromPoint (where);
|
||||
if (inverseBitmap)
|
||||
norm = 1.f - norm;
|
||||
value = getMin () + norm * (getMax () - getMin ());
|
||||
bounceValue ();
|
||||
|
||||
if (isDirty ())
|
||||
{
|
||||
valueChanged ();
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CSwitchBase::setInverseBitmap (bool state)
|
||||
{
|
||||
if (inverseBitmap != state)
|
||||
{
|
||||
inverseBitmap = state;
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CVerticalSwitch
|
||||
//------------------------------------------------------------------------
|
||||
/*! @class CVerticalSwitch
|
||||
Define a switch with a given number of positions, the current position is defined by the position
|
||||
of the last click on this object (the object is divided in its height by the number of position).
|
||||
Each position has its subbitmap, each subbitmap is stacked in the given handle bitmap.
|
||||
By clicking Alt+Left Mouse the default value is used.
|
||||
Use a CMultiFrameBitmap for its background bitmap.
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CVerticalSwitch constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param background the switch bitmap
|
||||
* @param offset unused
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CVerticalSwitch::CVerticalSwitch (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
CBitmap* background)
|
||||
: CSwitchBase (size, listener, tag, background)
|
||||
{
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
heightOfOneImage = size.getHeight ();
|
||||
setNumSubPixmaps (
|
||||
background ? static_cast<int32_t> (background->getHeight () / heightOfOneImage) : 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CVerticalSwitch 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 height of one sub bitmap
|
||||
* @param iMaxPositions TODO
|
||||
* @param background the switch bitmap
|
||||
* @param offset unused
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CVerticalSwitch::CVerticalSwitch (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
int32_t subPixmaps, CCoord heightOfOneImage,
|
||||
int32_t iMaxPositions, CBitmap* background, const CPoint& offset)
|
||||
: CSwitchBase (size, listener, tag, subPixmaps, heightOfOneImage, iMaxPositions, background, offset)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CVerticalSwitch::CVerticalSwitch (const CVerticalSwitch& v)
|
||||
: CSwitchBase (v)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
double CVerticalSwitch::calculateCoef () const
|
||||
{
|
||||
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (getDrawBackground ()))
|
||||
{
|
||||
return mfb->getFrameSize ().y / static_cast<double> (getMultiFrameBitmapRangeLength (*mfb));
|
||||
}
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
return static_cast<double> (heightOfOneImage) / static_cast<double> (getNumSubPixmaps ());
|
||||
#else
|
||||
return 1.;
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
float CVerticalSwitch::calcNormFromPoint (const CPoint& where) const
|
||||
{
|
||||
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (getDrawBackground ()))
|
||||
{
|
||||
return static_cast<int32_t> ((where.y - getViewSize ().top) / getCoef ()) /
|
||||
static_cast<float> (getMultiFrameBitmapRangeLength (*mfb) - 1);
|
||||
}
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
return static_cast<int32_t> ((where.y - getViewSize ().top) / getCoef ()) /
|
||||
static_cast<float> (getNumSubPixmaps () - 1);
|
||||
#else
|
||||
return 0.f;
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CVerticalSwitch::onKeyboardEvent (KeyboardEvent& event)
|
||||
{
|
||||
if (event.type != EventType::KeyDown || event.modifiers.empty () == false)
|
||||
return;
|
||||
float norm = getValueNormalized ();
|
||||
int32_t currentIndex = normalizedToIndex (norm);
|
||||
if (event.virt == VirtualKey::Up && currentIndex > 0)
|
||||
{
|
||||
--currentIndex;
|
||||
norm = indexToNormalized (currentIndex);
|
||||
value = (getMax () - getMin ()) * norm + getMin ();
|
||||
bounceValue ();
|
||||
}
|
||||
if (event.virt == VirtualKey::Down && norm < 1.f)
|
||||
{
|
||||
++currentIndex;
|
||||
norm = indexToNormalized (currentIndex);
|
||||
value = (getMax () - getMin ()) * norm + getMin ();
|
||||
bounceValue ();
|
||||
}
|
||||
if (isDirty ())
|
||||
{
|
||||
invalid ();
|
||||
beginEdit ();
|
||||
valueChanged ();
|
||||
endEdit ();
|
||||
event.consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CHorizontalSwitch
|
||||
//------------------------------------------------------------------------
|
||||
/*! @class CHorizontalSwitch
|
||||
Same as the CVerticalSwitch but horizontal.
|
||||
Use a CMultiFrameBitmap for its background bitmap.
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CHorizontalSwitch constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param background the bitmap of the switch
|
||||
* @param offset unused
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CHorizontalSwitch::CHorizontalSwitch (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
CBitmap* background)
|
||||
: CSwitchBase (size, listener, tag, background)
|
||||
{
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
heightOfOneImage = size.getWidth ();
|
||||
setNumSubPixmaps (background ? (int32_t) (background->getWidth () / heightOfOneImage) : 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CHorizontalSwitch 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 height of one sub bitmap
|
||||
* @param iMaxPositions ignored
|
||||
* @param background the switch bitmap
|
||||
* @param offset unused
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CHorizontalSwitch::CHorizontalSwitch (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
int32_t subPixmaps, CCoord heightOfOneImage,
|
||||
int32_t iMaxPositions, CBitmap* background,
|
||||
const CPoint& offset)
|
||||
: CSwitchBase (size, listener, tag, subPixmaps, heightOfOneImage, iMaxPositions, background, offset)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CHorizontalSwitch::CHorizontalSwitch (const CHorizontalSwitch& v)
|
||||
: CSwitchBase (v)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
double CHorizontalSwitch::calculateCoef () const
|
||||
{
|
||||
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (getDrawBackground ()))
|
||||
{
|
||||
return mfb->getFrameSize ().x / static_cast<double> (getMultiFrameBitmapRangeLength (*mfb));
|
||||
}
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
return getDrawBackground ()->getWidth () / static_cast<double> (getNumSubPixmaps ());
|
||||
#else
|
||||
return 1.;
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
float CHorizontalSwitch::calcNormFromPoint (const CPoint& where) const
|
||||
{
|
||||
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (getDrawBackground ()))
|
||||
{
|
||||
return static_cast<int32_t> ((where.x - getViewSize ().left) / getCoef ()) /
|
||||
static_cast<float> (getMultiFrameBitmapRangeLength (*mfb) - 1);
|
||||
}
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
return static_cast<int32_t> ((where.x - getViewSize ().left) / getCoef ()) /
|
||||
static_cast<float> (getNumSubPixmaps () - 1);
|
||||
#else
|
||||
return 0.f;
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CHorizontalSwitch::onKeyboardEvent(KeyboardEvent &event)
|
||||
{
|
||||
if (event.type != EventType::KeyDown || event.modifiers.empty () == false)
|
||||
return;
|
||||
float norm = getValueNormalized ();
|
||||
int32_t currentIndex = normalizedToIndex (norm);
|
||||
if (event.virt == VirtualKey::Left && currentIndex > 0)
|
||||
{
|
||||
--currentIndex;
|
||||
norm = indexToNormalized (currentIndex);
|
||||
value = (getMax () - getMin ()) * norm + getMin ();
|
||||
bounceValue ();
|
||||
}
|
||||
if (event.virt == VirtualKey::Right && norm < 1.f)
|
||||
{
|
||||
++currentIndex;
|
||||
norm = indexToNormalized (currentIndex);
|
||||
value = (getMax () - getMin ()) * norm + getMin ();
|
||||
bounceValue ();
|
||||
}
|
||||
if (isDirty ())
|
||||
{
|
||||
invalid ();
|
||||
beginEdit ();
|
||||
valueChanged ();
|
||||
endEdit ();
|
||||
event.consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CRockerSwitch
|
||||
//------------------------------------------------------------------------
|
||||
/*! @class CRockerSwitch
|
||||
Define a rocker switch with 3 states using 3 subbitmaps.
|
||||
One click on its leftside, then the first subbitmap is displayed.
|
||||
One click on its rightside, then the third subbitmap is displayed.
|
||||
When the mouse button is relaxed, the second subbitmap is framed. */
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CRockerSwitch constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param background bitmap with 3 stacked images of the rocker switch
|
||||
* @param style
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CRockerSwitch::CRockerSwitch (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
CBitmap* background, const int32_t style)
|
||||
: CControl (size, listener, tag, background), style (style), resetValueTimer (nullptr)
|
||||
{
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
setNumSubPixmaps (3);
|
||||
setHeightOfOneImage (size.getHeight ());
|
||||
#endif
|
||||
setWantsFocus (true);
|
||||
setMin (-1.f);
|
||||
setMax (1.f);
|
||||
setValue ((getMax () - getMin ()) / 2.f + getMin ());
|
||||
}
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CRockerSwitch constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param background bitmap with 3 stacked images of the rocker switch
|
||||
* @param offset
|
||||
* @param style
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CRockerSwitch::CRockerSwitch (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background, const CPoint &offset, const int32_t style)
|
||||
: CControl (size, listener, tag, background)
|
||||
, offset (offset)
|
||||
, style (style)
|
||||
, resetValueTimer (nullptr)
|
||||
{
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
setNumSubPixmaps (3);
|
||||
setHeightOfOneImage (size.getHeight ());
|
||||
#endif
|
||||
setWantsFocus (true);
|
||||
setMin (-1.f);
|
||||
setMax (1.f);
|
||||
setValue ((getMax () - getMin ()) / 2.f + getMin ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CRockerSwitch constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param heightOfOneImage height of one image in pixel
|
||||
* @param background bitmap with 3 stacked images of the rocker switch
|
||||
* @param offset
|
||||
* @param style
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CRockerSwitch::CRockerSwitch (const CRect& size, IControlListener* listener, int32_t tag, CCoord heightOfOneImage, CBitmap* background, const CPoint &offset, const int32_t style)
|
||||
: CControl (size, listener, tag, background)
|
||||
, offset (offset)
|
||||
, style (style)
|
||||
, resetValueTimer (nullptr)
|
||||
{
|
||||
setNumSubPixmaps (3);
|
||||
setHeightOfOneImage (heightOfOneImage);
|
||||
setWantsFocus (true);
|
||||
setMin (-1.f);
|
||||
setMax (1.f);
|
||||
setValue ((getMax () - getMin ()) / 2.f + getMin ());
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CRockerSwitch::CRockerSwitch (const CRockerSwitch& v)
|
||||
: CControl (v), style (v.style), resetValueTimer (nullptr)
|
||||
{
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
offset = v.offset;
|
||||
setHeightOfOneImage (v.heightOfOneImage);
|
||||
#endif
|
||||
setWantsFocus (true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CRockerSwitch::~CRockerSwitch () noexcept
|
||||
{
|
||||
if (resetValueTimer)
|
||||
resetValueTimer->forget ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CRockerSwitch::draw (CDrawContext *pContext)
|
||||
{
|
||||
if (auto bitmap = getDrawBackground ())
|
||||
{
|
||||
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
|
||||
{
|
||||
uint16_t frameIndex = getMultiFrameBitmapIndex (*mfb, getValueNormalized ());
|
||||
mfb->drawFrame (pContext, frameIndex, getViewSize ().getTopLeft ());
|
||||
}
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CPoint where (offset.x, offset.y);
|
||||
|
||||
if (value == getMax ())
|
||||
where.y += 2 * heightOfOneImage;
|
||||
else if (value == (getMax () - getMin ()) / 2.f + getMin ())
|
||||
where.y += heightOfOneImage;
|
||||
|
||||
bitmap->draw (pContext, getViewSize (), where);
|
||||
#else
|
||||
bitmap->draw (pContext, getViewSize ());
|
||||
#endif
|
||||
}
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CRockerSwitch::onMouseDown (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (!(buttons & kLButton))
|
||||
return kMouseEventNotHandled;
|
||||
mouseStartValue = value;
|
||||
beginEdit ();
|
||||
return onMouseMoved (where, buttons);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CRockerSwitch::onMouseUp (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (isEditing ())
|
||||
{
|
||||
value = (getMax () - getMin ()) / 2.f + getMin ();
|
||||
if (isDirty ())
|
||||
invalid ();
|
||||
endEdit ();
|
||||
}
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CRockerSwitch::onMouseCancel ()
|
||||
{
|
||||
if (isEditing ())
|
||||
{
|
||||
value = mouseStartValue;
|
||||
if (isDirty ())
|
||||
{
|
||||
valueChanged ();
|
||||
invalid ();
|
||||
}
|
||||
endEdit ();
|
||||
}
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CRockerSwitch::onMouseMoved (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (isEditing ())
|
||||
{
|
||||
CCoord width_2 = getViewSize ().getWidth () / 2;
|
||||
CCoord height_2 = getViewSize ().getHeight () / 2;
|
||||
|
||||
if (style & kHorizontal)
|
||||
{
|
||||
if (where.x >= getViewSize ().left && where.y >= getViewSize ().top &&
|
||||
where.x <= (getViewSize ().left + width_2) && where.y <= getViewSize ().bottom)
|
||||
value = getMin ();
|
||||
else if (where.x >= (getViewSize ().left + width_2) && where.y >= getViewSize ().top &&
|
||||
where.x <= getViewSize ().right && where.y <= getViewSize ().bottom)
|
||||
value = getMax ();
|
||||
else
|
||||
value = mouseStartValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (where.x >= getViewSize ().left && where.y >= getViewSize ().top &&
|
||||
where.x <= getViewSize ().right && where.y <= (getViewSize ().top + height_2))
|
||||
value = getMin ();
|
||||
else if (where.x >= getViewSize ().left && where.y >= (getViewSize ().top + height_2) &&
|
||||
where.x <= getViewSize ().right && where.y <= getViewSize ().bottom)
|
||||
value = getMax ();
|
||||
else
|
||||
value = mouseStartValue;
|
||||
}
|
||||
|
||||
if (isDirty ())
|
||||
{
|
||||
valueChanged ();
|
||||
invalid ();
|
||||
}
|
||||
}
|
||||
return kMouseEventHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CRockerSwitch::onKeyboardEvent (KeyboardEvent& event)
|
||||
{
|
||||
if (event.modifiers.empty () == false)
|
||||
return;
|
||||
if (event.type == EventType::KeyDown)
|
||||
{
|
||||
if (style & kHorizontal &&
|
||||
(event.virt == VirtualKey::Left || event.virt == VirtualKey::Right))
|
||||
{
|
||||
value = event.virt == VirtualKey::Left ? getMin () : getMax ();
|
||||
invalid ();
|
||||
beginEdit ();
|
||||
valueChanged ();
|
||||
event.consumed = true;
|
||||
}
|
||||
if (style & kVertical && (event.virt == VirtualKey::Up || event.virt == VirtualKey::Down))
|
||||
{
|
||||
value = event.virt == VirtualKey::Up ? getMin () : getMax ();
|
||||
invalid ();
|
||||
beginEdit ();
|
||||
valueChanged ();
|
||||
event.consumed = true;
|
||||
}
|
||||
}
|
||||
else if (event.type == EventType::KeyUp)
|
||||
{
|
||||
if ((style & kHorizontal &&
|
||||
(event.virt == VirtualKey::Left || event.virt == VirtualKey::Right)) ||
|
||||
(style & kVertical && (event.virt == VirtualKey::Up || event.virt == VirtualKey::Down)))
|
||||
{
|
||||
value = (getMax () - getMin ()) / 2.f + getMin ();
|
||||
invalid ();
|
||||
valueChanged ();
|
||||
endEdit ();
|
||||
event.consumed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CRockerSwitch::onMouseWheelEvent (MouseWheelEvent& event)
|
||||
{
|
||||
auto distance = event.deltaY;
|
||||
if (distance == 0.)
|
||||
return;
|
||||
|
||||
if (distance > 0)
|
||||
value = getMin ();
|
||||
else
|
||||
value = getMax ();
|
||||
|
||||
if (isDirty ())
|
||||
{
|
||||
invalid ();
|
||||
if (!isEditing ())
|
||||
beginEdit ();
|
||||
valueChanged ();
|
||||
}
|
||||
|
||||
if (resetValueTimer == nullptr)
|
||||
resetValueTimer = new CVSTGUITimer (this, 200);
|
||||
resetValueTimer->stop ();
|
||||
resetValueTimer->start ();
|
||||
|
||||
event.consumed = true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMessageResult CRockerSwitch::notify (CBaseObject* sender, IdStringPtr message)
|
||||
{
|
||||
if (sender == resetValueTimer)
|
||||
{
|
||||
float newValue = (getMax () - getMin ()) / 2.f + getMin ();
|
||||
if (value != newValue)
|
||||
{
|
||||
value = newValue;
|
||||
if (!isEditing ())
|
||||
beginEdit ();
|
||||
valueChanged ();
|
||||
endEdit ();
|
||||
setDirty (true);
|
||||
}
|
||||
resetValueTimer->forget ();
|
||||
resetValueTimer = nullptr;
|
||||
return kMessageNotified;
|
||||
}
|
||||
return CControl::notify (sender, message);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool CRockerSwitch::sizeToFit ()
|
||||
{
|
||||
if (auto bitmap = getDrawBackground ())
|
||||
{
|
||||
CRect vs (getViewSize ());
|
||||
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
|
||||
{
|
||||
vs.setSize (mfb->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;
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,190 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ccontrol.h"
|
||||
#include "../cbitmap.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CSwitchBase : public CControl,
|
||||
public MultiFrameBitmapView<CSwitchBase>
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
,
|
||||
public IMultiBitmapControl
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
void setInverseBitmap (bool state);
|
||||
bool getInverseBitmap () const { return inverseBitmap; }
|
||||
|
||||
protected:
|
||||
CSwitchBase (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background);
|
||||
CSwitchBase (const CSwitchBase& other);
|
||||
~CSwitchBase () noexcept override = default;
|
||||
|
||||
void draw (CDrawContext*) override;
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseCancel () override;
|
||||
bool sizeToFit () override;
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CSwitchBase (const CRect& size, IControlListener* listener, int32_t tag, int32_t subPixmaps,
|
||||
CCoord heightOfOneImage, int32_t iMaxPositions, CBitmap* background,
|
||||
const CPoint& offset = CPoint (0, 0));
|
||||
void setNumSubPixmaps (int32_t numSubPixmaps) override
|
||||
{
|
||||
IMultiBitmapControl::setNumSubPixmaps (numSubPixmaps);
|
||||
invalid ();
|
||||
}
|
||||
const CPoint& getOffset () const { return offset; }
|
||||
#endif
|
||||
|
||||
double getCoef () const { return coef; }
|
||||
int32_t normalizedToIndex (float norm) const;
|
||||
float indexToNormalized (int32_t index) const;
|
||||
|
||||
virtual double calculateCoef () const = 0;
|
||||
virtual float calcNormFromPoint (const CPoint& where) const = 0;
|
||||
|
||||
VSTGUI_DEPRECATED_MSG (static bool useLegacyIndexCalculation;
|
||||
, "Use CMultiFrameBitmap::normalizedValueToFrameIndex() instead")
|
||||
|
||||
private:
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CPoint offset {};
|
||||
#endif
|
||||
double coef;
|
||||
float mouseStartValue;
|
||||
bool inverseBitmap{false};
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CVerticalSwitch Declaration
|
||||
//! @brief a vertical switch control
|
||||
/// @ingroup controls uses_multi_frame_bitmaps
|
||||
//-----------------------------------------------------------------------------
|
||||
class CVerticalSwitch : public CSwitchBase
|
||||
{
|
||||
public:
|
||||
CVerticalSwitch (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
CBitmap* background);
|
||||
CVerticalSwitch (const CVerticalSwitch& vswitch);
|
||||
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CVerticalSwitch (const CRect& size, IControlListener* listener, int32_t tag, int32_t subPixmaps,
|
||||
CCoord heightOfOneImage, int32_t iMaxPositions, CBitmap* background,
|
||||
const CPoint& offset = CPoint (0, 0));
|
||||
#endif
|
||||
|
||||
CLASS_METHODS(CVerticalSwitch, CControl)
|
||||
protected:
|
||||
~CVerticalSwitch () noexcept override = default;
|
||||
|
||||
double calculateCoef () const override;
|
||||
float calcNormFromPoint (const CPoint& where) const override;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CHorizontalSwitch Declaration
|
||||
//! @brief a horizontal switch control
|
||||
/// @ingroup controls uses_multi_frame_bitmaps
|
||||
//-----------------------------------------------------------------------------
|
||||
class CHorizontalSwitch : public CSwitchBase
|
||||
{
|
||||
public:
|
||||
CHorizontalSwitch (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
CBitmap* background);
|
||||
CHorizontalSwitch (const CHorizontalSwitch& hswitch);
|
||||
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CHorizontalSwitch (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
int32_t subPixmaps, CCoord heightOfOneImage, int32_t iMaxPositions,
|
||||
CBitmap* background, const CPoint& offset = CPoint (0, 0));
|
||||
#endif
|
||||
|
||||
CLASS_METHODS(CHorizontalSwitch, CControl)
|
||||
protected:
|
||||
~CHorizontalSwitch () noexcept override = default;
|
||||
|
||||
double calculateCoef () const override;
|
||||
float calcNormFromPoint (const CPoint& where) const override;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CRockerSwitch Declaration
|
||||
//! @brief a switch control with 3 sub bitmaps
|
||||
/// @ingroup controls use_multi_frame_bitmaps
|
||||
//-----------------------------------------------------------------------------
|
||||
class CRockerSwitch : public CControl,
|
||||
public MultiFrameBitmapView<CRockerSwitch>
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
,
|
||||
public IMultiBitmapControl
|
||||
#endif
|
||||
{
|
||||
private:
|
||||
enum StyleEnum
|
||||
{
|
||||
StyleHorizontal = 0,
|
||||
StyleVertical,
|
||||
};
|
||||
public:
|
||||
enum Style
|
||||
{
|
||||
kHorizontal = 1 << StyleHorizontal,
|
||||
kVertical = 1 << StyleVertical,
|
||||
};
|
||||
|
||||
CRockerSwitch (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background,
|
||||
const int32_t style = kHorizontal);
|
||||
CRockerSwitch (const CRockerSwitch& rswitch);
|
||||
|
||||
void draw (CDrawContext*) override;
|
||||
void onMouseWheelEvent (MouseWheelEvent& event) override;
|
||||
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
|
||||
CMouseEventResult onMouseCancel () override;
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
|
||||
bool sizeToFit () override;
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CRockerSwitch (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background,
|
||||
const CPoint& offset, const int32_t style = kHorizontal);
|
||||
CRockerSwitch (const CRect& size, IControlListener* listener, int32_t tag,
|
||||
CCoord heightOfOneImage, CBitmap* background,
|
||||
const CPoint& offset = CPoint (0, 0), const int32_t style = kHorizontal);
|
||||
void setNumSubPixmaps (int32_t numSubPixmaps) override { IMultiBitmapControl::setNumSubPixmaps (numSubPixmaps); invalid (); }
|
||||
#endif
|
||||
|
||||
CLASS_METHODS(CRockerSwitch, CControl)
|
||||
protected:
|
||||
~CRockerSwitch () noexcept override;
|
||||
|
||||
CMessageResult notify (CBaseObject* sender, IdStringPtr message) override;
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
CPoint offset {};
|
||||
#endif
|
||||
int32_t style;
|
||||
|
||||
CVSTGUITimer* resetValueTimer;
|
||||
private:
|
||||
float mouseStartValue;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,421 @@
|
||||
// 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 "ctextedit.h"
|
||||
#include "itexteditlistener.h"
|
||||
#include "../cframe.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include "../events.h"
|
||||
#include "../platform/iplatformframe.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CTextEdit
|
||||
//------------------------------------------------------------------------
|
||||
/*! @class CTextEdit
|
||||
Define a rectangle view where a text-value can be displayed and edited with a given font and color.
|
||||
The user can specify its convert function (from char to char). The text-value is centered in the given rect.
|
||||
A bitmap can be used as background.
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CTextEdit constructor.
|
||||
* @param size the size of this view
|
||||
* @param listener the listener
|
||||
* @param tag the control tag
|
||||
* @param txt the initial text as c string (UTF-8 encoded)
|
||||
* @param background the background bitmap
|
||||
* @param style the display style (see CParamDisplay for styles)
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CTextEdit::CTextEdit (const CRect& size, IControlListener* listener, int32_t tag, UTF8StringPtr txt,
|
||||
CBitmap* background, const int32_t style)
|
||||
: CTextLabel (size, txt, background, style)
|
||||
{
|
||||
this->listener = listener;
|
||||
this->tag = tag;
|
||||
|
||||
setWantsFocus (true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CTextEdit::CTextEdit (const CTextEdit& v)
|
||||
: CTextLabel (v)
|
||||
, bWasReturnPressed (false)
|
||||
, stringToValueFunction (v.stringToValueFunction)
|
||||
, immediateTextChange (v.immediateTextChange)
|
||||
, secureStyle (v.secureStyle)
|
||||
, platformFont (v.platformFont)
|
||||
, placeholderString (v.placeholderString)
|
||||
{
|
||||
setWantsFocus (true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CTextEdit::~CTextEdit () noexcept
|
||||
{
|
||||
listener = nullptr;
|
||||
vstgui_assert (platformControl == nullptr);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::setStringToValueFunction (const StringToValueFunction& stringToValueFunc)
|
||||
{
|
||||
stringToValueFunction = stringToValueFunc;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::setStringToValueFunction (StringToValueFunction&& stringToValueFunc)
|
||||
{
|
||||
stringToValueFunction = std::move (stringToValueFunc);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::setImmediateTextChange (bool state)
|
||||
{
|
||||
immediateTextChange = state;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::setSecureStyle (bool state)
|
||||
{
|
||||
if (secureStyle != state)
|
||||
{
|
||||
secureStyle = state;
|
||||
if (platformControl)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CTextEdit::getSecureStyle () const
|
||||
{
|
||||
return secureStyle;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::registerTextEditListener (ITextEditListener* listener)
|
||||
{
|
||||
textEditListeners.add (listener);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::unregisterTextEditListener (ITextEditListener* listener)
|
||||
{
|
||||
textEditListeners.remove (listener);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::setValue (float val)
|
||||
{
|
||||
CTextLabel::setValue (val);
|
||||
bool converted = false;
|
||||
std::string string;
|
||||
if (valueToStringFunction)
|
||||
converted = valueToStringFunction (getValue (), string, this);
|
||||
if (!converted)
|
||||
{
|
||||
char tmp[255];
|
||||
char precisionStr[10];
|
||||
snprintf (precisionStr, 10, "%%.%hhuf", valuePrecision);
|
||||
snprintf (tmp, 255, precisionStr, getValue ());
|
||||
string = tmp;
|
||||
}
|
||||
|
||||
if (converted)
|
||||
{
|
||||
CTextLabel::setText (UTF8String (std::move (string)));
|
||||
if (platformControl)
|
||||
platformControl->setText (getText ());
|
||||
}
|
||||
else
|
||||
setText (UTF8String (std::move (string)));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::setText (const UTF8String& txt)
|
||||
{
|
||||
if (stringToValueFunction)
|
||||
{
|
||||
float val = getValue ();
|
||||
if (stringToValueFunction (txt, val, this))
|
||||
{
|
||||
CTextLabel::setValue (val);
|
||||
if (valueToStringFunction)
|
||||
{
|
||||
std::string string;
|
||||
valueToStringFunction (getValue (), string, this);
|
||||
CTextLabel::setText (UTF8String (std::move (string)));
|
||||
if (platformControl)
|
||||
platformControl->setText (getText ());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
CTextLabel::setText (txt);
|
||||
if (platformControl)
|
||||
platformControl->setText (getText ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::valueChanged ()
|
||||
{
|
||||
if (stringToValueFunction)
|
||||
CTextLabel::valueChanged ();
|
||||
|
||||
CParamDisplay::valueChanged ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::setPlaceholderString (const UTF8String& str)
|
||||
{
|
||||
placeholderString = str;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::draw (CDrawContext *pContext)
|
||||
{
|
||||
if (platformControl)
|
||||
{
|
||||
drawBack (pContext);
|
||||
if (!platformControl->drawsPlaceholder () && !placeholderString.empty () &&
|
||||
platformControl->getText ().empty ())
|
||||
{
|
||||
pContext->saveGlobalState ();
|
||||
pContext->setGlobalAlpha (pContext->getGlobalAlpha () * 0.5f);
|
||||
drawPlatformText (pContext, placeholderString);
|
||||
pContext->restoreGlobalState ();
|
||||
}
|
||||
setDirty (false);
|
||||
return;
|
||||
}
|
||||
drawBack (pContext);
|
||||
if (text.empty ())
|
||||
{
|
||||
if (!placeholderString.empty ())
|
||||
{
|
||||
pContext->saveGlobalState ();
|
||||
pContext->setGlobalAlpha (pContext->getGlobalAlpha () * 0.5f);
|
||||
drawPlatformText (pContext, placeholderString);
|
||||
pContext->restoreGlobalState ();
|
||||
}
|
||||
}
|
||||
else if (getSecureStyle ())
|
||||
{
|
||||
constexpr auto bulletCharacter = "\xE2\x80\xA2";
|
||||
UTF8String str;
|
||||
for (auto i = 0u; i < text.length (); ++i)
|
||||
str += bulletCharacter;
|
||||
drawPlatformText (pContext, str);
|
||||
}
|
||||
else
|
||||
CTextLabel::draw (pContext);
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CMouseEventResult CTextEdit::onMouseDown (CPoint& where, const CButtonState& buttons)
|
||||
{
|
||||
if (buttons & kLButton)
|
||||
{
|
||||
if (getFrame ()->getFocusView () != this)
|
||||
{
|
||||
if (isDoubleClickStyle ())
|
||||
{
|
||||
if (!(buttons & kDoubleClick))
|
||||
return kMouseEventNotHandled;
|
||||
}
|
||||
|
||||
takeFocus ();
|
||||
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
|
||||
}
|
||||
}
|
||||
return kMouseEventNotHandled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::onKeyboardEvent (KeyboardEvent& event)
|
||||
{
|
||||
if (!platformControl || event.type != EventType::KeyDown)
|
||||
return;
|
||||
|
||||
if (event.virt == VirtualKey::Escape)
|
||||
{
|
||||
bWasReturnPressed = false;
|
||||
platformControl->setText (text);
|
||||
getFrame ()->setFocusView (nullptr);
|
||||
looseFocus ();
|
||||
event.consumed = true;
|
||||
}
|
||||
else if (event.virt == VirtualKey::Return)
|
||||
{
|
||||
bWasReturnPressed = true;
|
||||
getFrame ()->setFocusView (nullptr);
|
||||
looseFocus ();
|
||||
event.consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CFontRef CTextEdit::platformGetFont () const
|
||||
{
|
||||
CFontRef font = getFont ();
|
||||
CCoord fontSize = font->getSize ();
|
||||
fontSize *= getGlobalTransform ().m11;
|
||||
if (fontSize == font->getSize ())
|
||||
return font;
|
||||
platformFont = makeOwned<CFontDesc> (*font);
|
||||
platformFont->setSize (fontSize);
|
||||
return platformFont;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CRect CTextEdit::platformGetSize () const
|
||||
{
|
||||
return translateToGlobal (getViewSize ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CRect CTextEdit::platformGetVisibleSize () const
|
||||
{
|
||||
return translateToGlobal (getVisibleViewSize ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::platformLooseFocus (bool returnPressed)
|
||||
{
|
||||
remember ();
|
||||
bWasReturnPressed = returnPressed;
|
||||
if (getFrame ()->getFocusView () == this)
|
||||
getFrame ()->setFocusView (nullptr);
|
||||
forget ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::platformOnKeyboardEvent (KeyboardEvent& event)
|
||||
{
|
||||
dynamic_cast<IPlatformFrameCallback*> (getFrame ())->platformOnEvent (event);
|
||||
if (event.consumed)
|
||||
return;
|
||||
if (event.virt == VirtualKey::Return)
|
||||
{
|
||||
platformLooseFocus (true);
|
||||
event.consumed = true;
|
||||
}
|
||||
else if (event.virt == VirtualKey::Escape)
|
||||
{
|
||||
platformLooseFocus (false);
|
||||
event.consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::platformTextDidChange ()
|
||||
{
|
||||
if (platformControl && immediateTextChange)
|
||||
updateText (platformControl);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CTextEdit::platformIsSecureTextEdit ()
|
||||
{
|
||||
return getSecureStyle ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::parentSizeChanged ()
|
||||
{
|
||||
if (platformControl)
|
||||
platformControl->updateSize ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::setViewSize (const CRect& newSize, bool invalid)
|
||||
{
|
||||
CTextLabel::setViewSize (newSize, invalid);
|
||||
if (platformControl)
|
||||
platformControl->updateSize ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::createPlatformTextEdit ()
|
||||
{
|
||||
if (platformControl)
|
||||
return;
|
||||
|
||||
bWasReturnPressed = false;
|
||||
platformControl = getFrame ()->getPlatformFrame ()->createPlatformTextEdit (this);
|
||||
textEditListeners.forEach (
|
||||
[this] (ITextEditListener* l) { l->onTextEditPlatformControlTookFocus (this); });
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CTextEdit::wantsFocus () const
|
||||
{
|
||||
if (isDoubleClickStyle () && !platformControl)
|
||||
return false;
|
||||
return CTextLabel::wantsFocus ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::takeFocus ()
|
||||
{
|
||||
if (!getFrame ())
|
||||
return;
|
||||
createPlatformTextEdit ();
|
||||
if (getFrame()->getFocusView () != this)
|
||||
getFrame()->setFocusView (this);
|
||||
CTextLabel::takeFocus ();
|
||||
invalid ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::looseFocus ()
|
||||
{
|
||||
if (platformControl == nullptr)
|
||||
return;
|
||||
|
||||
CBaseObjectGuard guard (this);
|
||||
|
||||
auto _platformControl = std::move (platformControl);
|
||||
updateText (_platformControl);
|
||||
|
||||
_platformControl = nullptr;
|
||||
|
||||
textEditListeners.forEach (
|
||||
[this] (ITextEditListener* l) { l->onTextEditPlatformControlLostFocus (this); });
|
||||
|
||||
// if you want to destroy the text edit do it with the loose focus message
|
||||
CView* receiver = getParentView () ? getParentView () : getFrame ();
|
||||
while (receiver)
|
||||
{
|
||||
if (receiver->notify (this, kMsgLooseFocus) == kMessageNotified)
|
||||
break;
|
||||
receiver = receiver->getParentView ();
|
||||
}
|
||||
CTextLabel::looseFocus ();
|
||||
invalid ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextEdit::updateText (IPlatformTextEdit* pte)
|
||||
{
|
||||
auto newText = pte->getText ();
|
||||
if (newText != getText ())
|
||||
{
|
||||
beginEdit ();
|
||||
|
||||
setText (newText);
|
||||
|
||||
CParamDisplay::valueChanged ();
|
||||
|
||||
endEdit ();
|
||||
}
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,122 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ctextlabel.h"
|
||||
#include "../dispatchlist.h"
|
||||
#include "../platform/iplatformtextedit.h"
|
||||
#include <functional>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
using CTextEditStringToValueProc = bool (*) (UTF8StringPtr txt, float& result, void* userData);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CTextEdit Declaration
|
||||
//! @brief a text edit control
|
||||
/// @ingroup controls
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTextEdit : public CTextLabel, public IPlatformTextEditCallback
|
||||
{
|
||||
private:
|
||||
enum StyleEnum
|
||||
{
|
||||
StyleDoubleClick = CParamDisplay::LastStyle,
|
||||
};
|
||||
public:
|
||||
using PlatformTextEditPtr = SharedPointer<IPlatformTextEdit>;
|
||||
|
||||
CTextEdit (const CRect& size, IControlListener* listener, int32_t tag, UTF8StringPtr txt = nullptr, CBitmap* background = nullptr, const int32_t style = 0);
|
||||
CTextEdit (const CTextEdit& textEdit);
|
||||
|
||||
enum Style
|
||||
{
|
||||
kDoubleClickStyle = 1 << StyleDoubleClick,
|
||||
};
|
||||
|
||||
bool isDoubleClickStyle () const { return hasBit (getStyle (), kDoubleClickStyle); }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CTextEdit Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
using StringToValueUserData = CTextEdit;
|
||||
|
||||
using StringToValueFunction = std::function<bool(UTF8StringPtr txt, float& result, CTextEdit* textEdit)>;
|
||||
|
||||
void setStringToValueFunction (const StringToValueFunction& stringToValueFunc);
|
||||
void setStringToValueFunction (StringToValueFunction&& stringToValueFunc);
|
||||
|
||||
/** enable/disable immediate text change behaviour */
|
||||
virtual void setImmediateTextChange (bool state);
|
||||
/** get immediate text change behaviour */
|
||||
bool getImmediateTextChange () const { return immediateTextChange; }
|
||||
|
||||
/** enable/disable secure style */
|
||||
void setSecureStyle (bool state);
|
||||
/** get secure style */
|
||||
bool getSecureStyle () const;
|
||||
|
||||
virtual void setPlaceholderString (const UTF8String& str);
|
||||
const UTF8String& getPlaceholderString () const { return placeholderString; }
|
||||
|
||||
void registerTextEditListener (ITextEditListener* listener);
|
||||
void unregisterTextEditListener (ITextEditListener* listener);
|
||||
//@}
|
||||
|
||||
// overrides
|
||||
void setText (const UTF8String& txt) override;
|
||||
void valueChanged () override;
|
||||
void setValue (float val) override;
|
||||
void setTextRotation (double angle) override { } // not supported
|
||||
|
||||
void draw (CDrawContext* pContext) override;
|
||||
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
|
||||
void takeFocus () override;
|
||||
void looseFocus () override;
|
||||
bool wantsFocus () const override;
|
||||
|
||||
void setViewSize (const CRect& newSize, bool invalid = true) override;
|
||||
void parentSizeChanged () override;
|
||||
|
||||
bool bWasReturnPressed {false};
|
||||
|
||||
PlatformTextEditPtr getPlatformTextEdit () const { return platformControl; }
|
||||
|
||||
CLASS_METHODS(CTextEdit, CParamDisplay)
|
||||
protected:
|
||||
~CTextEdit () noexcept override;
|
||||
|
||||
void createPlatformTextEdit ();
|
||||
void updateText (IPlatformTextEdit* pte);
|
||||
|
||||
CColor platformGetBackColor () const override { return getBackColor (); }
|
||||
CColor platformGetFontColor () const override { return getFontColor (); }
|
||||
CFontRef platformGetFont () const override;
|
||||
CHoriTxtAlign platformGetHoriTxtAlign () const override { return getHoriAlign (); }
|
||||
const UTF8String& platformGetText () const override { return text; }
|
||||
const UTF8String& platformGetPlaceholderText () const override { return placeholderString; }
|
||||
CRect platformGetSize () const override;
|
||||
CRect platformGetVisibleSize () const override;
|
||||
CPoint platformGetTextInset () const override { return getTextInset (); }
|
||||
void platformLooseFocus (bool returnPressed) override;
|
||||
void platformOnKeyboardEvent (KeyboardEvent& event) override;
|
||||
void platformTextDidChange () override;
|
||||
bool platformIsSecureTextEdit () override;
|
||||
|
||||
PlatformTextEditPtr platformControl;
|
||||
|
||||
StringToValueFunction stringToValueFunction;
|
||||
|
||||
bool immediateTextChange {false};
|
||||
bool secureStyle {false};
|
||||
mutable SharedPointer<CFontDesc> platformFont;
|
||||
UTF8String placeholderString;
|
||||
DispatchList<ITextEditListener*> textEditListeners;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,483 @@
|
||||
// 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 "ctextlabel.h"
|
||||
#include "../platform/iplatformfont.h"
|
||||
#include "../cdrawmethods.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include <sstream>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CTextLabel
|
||||
//------------------------------------------------------------------------
|
||||
/*! @class CTextLabel
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CTextLabel constructor.
|
||||
* @param size the size of this view
|
||||
* @param txt the initial text as c string (UTF-8 encoded)
|
||||
* @param background the background bitmap
|
||||
* @param style the display style (see CParamDisplay for styles)
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CTextLabel::CTextLabel (const CRect& size, UTF8StringPtr txt, CBitmap* background, const int32_t style)
|
||||
: CParamDisplay (size, background, style)
|
||||
, textTruncateMode (kTruncateNone)
|
||||
{
|
||||
setText (txt);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CTextLabel::CTextLabel (const CTextLabel& v)
|
||||
: CParamDisplay (v)
|
||||
, textTruncateMode (v.textTruncateMode)
|
||||
{
|
||||
setText (v.getText ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextLabel::registerTextLabelListener (ITextLabelListener* listener)
|
||||
{
|
||||
if (!listeners)
|
||||
listeners = std::unique_ptr<TextLabelListenerList> (new TextLabelListenerList ());
|
||||
listeners->add (listener);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextLabel::unregisterTextLabelListener (ITextLabelListener* listener)
|
||||
{
|
||||
if (listeners)
|
||||
listeners->remove (listener);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextLabel::setText (const UTF8String& txt)
|
||||
{
|
||||
if (text == txt)
|
||||
return;
|
||||
text = txt;
|
||||
if (textTruncateMode != kTruncateNone)
|
||||
calculateTruncatedText ();
|
||||
setDirty (true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextLabel::setTextTruncateMode (TextTruncateMode mode)
|
||||
{
|
||||
if (textTruncateMode != mode)
|
||||
{
|
||||
textTruncateMode = mode;
|
||||
calculateTruncatedText ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextLabel::calculateTruncatedText ()
|
||||
{
|
||||
if (textRotation != 0.) // currently truncation is only supported when not rotated
|
||||
{
|
||||
truncatedText = "";
|
||||
return;
|
||||
}
|
||||
if (!(textTruncateMode == kTruncateNone || text.empty () || fontID == nullptr || fontID->getPlatformFont () == nullptr || fontID->getPlatformFont ()->getPainter () == nullptr))
|
||||
{
|
||||
CDrawMethods::TextTruncateMode mode = textTruncateMode == kTruncateHead ? CDrawMethods::kTextTruncateHead : CDrawMethods::kTextTruncateTail;
|
||||
truncatedText = CDrawMethods::createTruncatedText (mode, text, fontID, getWidth () - getTextInset ().x * 2.);
|
||||
if (truncatedText == text)
|
||||
truncatedText.clear ();
|
||||
if (listeners)
|
||||
{
|
||||
listeners->forEach (
|
||||
[this] (ITextLabelListener* l) { l->onTextLabelTruncatedTextChanged (this); });
|
||||
}
|
||||
}
|
||||
else if (!truncatedText.empty ())
|
||||
truncatedText.clear ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
const UTF8String& CTextLabel::getText () const
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextLabel::draw (CDrawContext *pContext)
|
||||
{
|
||||
drawBack (pContext);
|
||||
drawPlatformText (pContext, truncatedText.empty () ? text : truncatedText);
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CTextLabel::sizeToFit ()
|
||||
{
|
||||
if (fontID == nullptr || fontID->getPlatformFont () == nullptr || fontID->getPlatformFont ()->getPainter () == nullptr)
|
||||
return false;
|
||||
CCoord width = fontID->getPlatformFont ()->getPainter ()->getStringWidth (nullptr, text.getPlatformString (), true);
|
||||
if (width > 0)
|
||||
{
|
||||
width += (getTextInset ().x * 2.);
|
||||
CRect newSize = getViewSize ();
|
||||
newSize.setWidth (width);
|
||||
setViewSize (newSize);
|
||||
setMouseableArea (newSize);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextLabel::setViewSize (const CRect& rect, bool invalid)
|
||||
{
|
||||
CRect current (getViewSize ());
|
||||
CParamDisplay::setViewSize (rect, invalid);
|
||||
if (textTruncateMode != kTruncateNone && current.getWidth () != getWidth ())
|
||||
{
|
||||
calculateTruncatedText ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextLabel::drawStyleChanged ()
|
||||
{
|
||||
if (textTruncateMode != kTruncateNone)
|
||||
{
|
||||
calculateTruncatedText ();
|
||||
}
|
||||
CParamDisplay::drawStyleChanged ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CTextLabel::valueChanged ()
|
||||
{
|
||||
if (valueToStringFunction)
|
||||
{
|
||||
std::string string;
|
||||
if (valueToStringFunction (getValue (), string, this))
|
||||
setText (UTF8String (std::move (string)));
|
||||
}
|
||||
CParamDisplay::valueChanged ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
CMultiLineTextLabel::CMultiLineTextLabel (const CRect& size)
|
||||
: CTextLabel (size)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMultiLineTextLabel::setValue (float val)
|
||||
{
|
||||
CTextLabel::setValue (val);
|
||||
|
||||
if (valueToStringFunction)
|
||||
{
|
||||
std::string string;
|
||||
if (valueToStringFunction (value, string, this))
|
||||
setText (UTF8String (string));
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMultiLineTextLabel::setTextTruncateMode (TextTruncateMode)
|
||||
{
|
||||
// not supported on multi line labels
|
||||
CTextLabel::setTextTruncateMode (kTruncateNone);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMultiLineTextLabel::setLineLayout (LineLayout layout)
|
||||
{
|
||||
if (lineLayout == layout)
|
||||
return;
|
||||
lineLayout = layout;
|
||||
lines.clear ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMultiLineTextLabel::setAutoHeight (bool state)
|
||||
{
|
||||
if (autoHeight == state)
|
||||
return;
|
||||
autoHeight = state;
|
||||
if (autoHeight && isAttached ())
|
||||
{
|
||||
if (lines.empty ())
|
||||
recalculateLines (nullptr);
|
||||
recalculateHeight ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMultiLineTextLabel::setVerticalCentered (bool state)
|
||||
{
|
||||
if (verticalCentered == state)
|
||||
return;
|
||||
verticalCentered = state;
|
||||
lines.clear ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CCoord CMultiLineTextLabel::getMaxLineWidth ()
|
||||
{
|
||||
if (lines.empty () && getText ().empty () == false)
|
||||
recalculateLines (nullptr);
|
||||
CCoord maxWidth {};
|
||||
for (const auto& line : lines)
|
||||
{
|
||||
if (line.r.getWidth () > maxWidth)
|
||||
maxWidth = line.r.getWidth ();
|
||||
}
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMultiLineTextLabel::drawRect (CDrawContext* pContext, const CRect& updateRect)
|
||||
{
|
||||
if (getText ().empty () == false && lines.empty ())
|
||||
recalculateLines (pContext);
|
||||
drawBack (pContext);
|
||||
|
||||
CRect newClip (updateRect);
|
||||
newClip.inset (getTextInset ());
|
||||
ConcatClip clip (*pContext, newClip);
|
||||
newClip = clip.get ();
|
||||
|
||||
pContext->setDrawMode (kAntiAliasing);
|
||||
pContext->setFont (getFont ());
|
||||
|
||||
newClip.offsetInverse (getViewSize().getTopLeft ());
|
||||
|
||||
CDrawContext::Transform t (*pContext, CGraphicsTransform ().translate (getViewSize ().getTopLeft ()));
|
||||
|
||||
if (style & kShadowText)
|
||||
{
|
||||
CDrawContext::Transform t2 (*pContext, CGraphicsTransform ().translate (shadowTextOffset));
|
||||
pContext->setFontColor (getShadowColor ());
|
||||
for (const auto& line : lines)
|
||||
{
|
||||
if (line.r.rectOverlap (newClip))
|
||||
pContext->drawString (line.str.getPlatformString (), line.r, getHoriAlign (), getAntialias ());
|
||||
}
|
||||
}
|
||||
|
||||
pContext->setFontColor (getFontColor ());
|
||||
for (const auto& line : lines)
|
||||
{
|
||||
if (line.r.rectOverlap (newClip))
|
||||
pContext->drawString (line.str.getPlatformString (), line.r, getHoriAlign (), getAntialias ());
|
||||
else if (line.r.bottom > newClip.bottom)
|
||||
break;
|
||||
}
|
||||
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CMultiLineTextLabel::sizeToFit ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMultiLineTextLabel::setText (const UTF8String& txt)
|
||||
{
|
||||
if (getText () == txt)
|
||||
return;
|
||||
CTextLabel::setText (txt);
|
||||
lines.clear ();
|
||||
if (autoHeight && isAttached ())
|
||||
{
|
||||
recalculateLines (nullptr);
|
||||
recalculateHeight ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMultiLineTextLabel::recalculateHeight ()
|
||||
{
|
||||
auto viewSize = getViewSize ();
|
||||
if (lines.empty ())
|
||||
viewSize.setHeight (0.);
|
||||
else
|
||||
{
|
||||
auto lastLine = lines.back ().r;
|
||||
viewSize.setHeight (lastLine.bottom + getTextInset ().y);
|
||||
}
|
||||
CTextLabel::setViewSize (viewSize);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMultiLineTextLabel::setViewSize (const CRect& rect, bool invalid)
|
||||
{
|
||||
auto viewSize = getViewSize ();
|
||||
auto normRect = rect;
|
||||
viewSize.originize ();
|
||||
normRect.originize ();
|
||||
if (viewSize != normRect)
|
||||
{
|
||||
if (lineLayout != LineLayout::clip ||
|
||||
(lineLayout == LineLayout::clip &&
|
||||
viewSize.getHeight () != normRect.getHeight ()))
|
||||
{
|
||||
lines.clear ();
|
||||
}
|
||||
}
|
||||
CTextLabel::setViewSize (rect, invalid);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMultiLineTextLabel::drawStyleChanged ()
|
||||
{
|
||||
lines.clear ();
|
||||
CTextLabel::drawStyleChanged ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline bool isLineBreakSeparator (char32_t c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '-': return true;
|
||||
case '_': return true;
|
||||
case '/': return true;
|
||||
case '\\': return true;
|
||||
case '.': return true;
|
||||
case ',': return true;
|
||||
case ':': return true;
|
||||
case ';': return true;
|
||||
case '?': return true;
|
||||
case '!': return true;
|
||||
case '*': return true;
|
||||
case '+': return true;
|
||||
case '&': return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMultiLineTextLabel::calculateWrapLine (CDrawContext* context,
|
||||
std::pair<UTF8String, double>& element,
|
||||
const IFontPainter* const& fontPainter, double lineHeight,
|
||||
double lineWidth, double maxWidth, const CPoint& textInset,
|
||||
CCoord& y)
|
||||
{
|
||||
auto start = element.first.begin ();
|
||||
auto lastSeparator = start;
|
||||
auto pos = start;
|
||||
while (pos != element.first.end () && *pos != 0)
|
||||
{
|
||||
if (isspace (*pos))
|
||||
lastSeparator = pos;
|
||||
else if (isLineBreakSeparator (*pos))
|
||||
lastSeparator = ++pos;
|
||||
if (pos == element.first.end ())
|
||||
break;
|
||||
auto tmpEnd = pos;
|
||||
UTF8String tmp ({start.base (), (++tmpEnd).base ()});
|
||||
auto width = fontPainter->getStringWidth (
|
||||
context ? context->getPlatformDeviceContext () : nullptr, tmp.getPlatformString ());
|
||||
if (width > maxWidth)
|
||||
{
|
||||
if (lastSeparator == element.first.end ())
|
||||
lastSeparator = pos;
|
||||
if (start == lastSeparator)
|
||||
lastSeparator = pos;
|
||||
lines.emplace_back (
|
||||
Line {CRect (textInset.x, y, lineWidth, y + lineHeight + textInset.y),
|
||||
UTF8String ({start.base (), lastSeparator.base ()})});
|
||||
y += lineHeight;
|
||||
pos = lastSeparator;
|
||||
start = pos;
|
||||
if (isspace (*start))
|
||||
++start;
|
||||
lastSeparator = element.first.end ();
|
||||
}
|
||||
++pos;
|
||||
}
|
||||
if (start != element.first.end ())
|
||||
{
|
||||
lines.emplace_back (Line {CRect (textInset.x, y, lineWidth, y + lineHeight + textInset.y),
|
||||
UTF8String ({start.base (), element.first.end ().base ()})});
|
||||
y += lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CMultiLineTextLabel::recalculateLines (CDrawContext* context)
|
||||
{
|
||||
const auto& font = getFont ()->getPlatformFont ();
|
||||
const auto& fontPainter = getFont ()->getFontPainter ();
|
||||
auto ascent = font->getAscent ();
|
||||
auto descent = font->getDescent ();
|
||||
auto leading = font->getLeading ();
|
||||
auto lineHeight = ascent + descent + leading;
|
||||
|
||||
const auto& textInset = getTextInset ();
|
||||
auto maxWidth = getWidth () - (textInset.x * 2);
|
||||
|
||||
std::vector<std::pair<UTF8String, CCoord>> elements;
|
||||
std::stringstream stream (getText ().getString ());
|
||||
std::string line;
|
||||
while (std::getline (stream, line, '\n'))
|
||||
{
|
||||
UTF8String str (std::move (line));
|
||||
auto width = fontPainter->getStringWidth (
|
||||
context ? context->getPlatformDeviceContext () : nullptr, str.getPlatformString ());
|
||||
elements.emplace_back (std::move (str), width);
|
||||
}
|
||||
|
||||
CCoord y = textInset.y;
|
||||
|
||||
auto lineWidth = getWidth () - textInset.x;
|
||||
|
||||
for (auto& element : elements)
|
||||
{
|
||||
if (lineLayout == LineLayout::clip)
|
||||
{
|
||||
lines.emplace_back (Line {
|
||||
CRect (textInset.x, y, element.second + textInset.x, y + lineHeight + textInset.y),
|
||||
std::move (element.first)});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (element.second > maxWidth)
|
||||
{
|
||||
if (lineLayout == LineLayout::truncate)
|
||||
{
|
||||
element.first = CDrawMethods::createTruncatedText (
|
||||
CDrawMethods::kTextTruncateTail, element.first, fontID, maxWidth);
|
||||
}
|
||||
else // wrap
|
||||
{
|
||||
calculateWrapLine (context, element, fontPainter, lineHeight, lineWidth,
|
||||
maxWidth, textInset, y);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
lines.emplace_back (
|
||||
Line {CRect (textInset.x, y, lineWidth, y + lineHeight + textInset.y),
|
||||
std::move (element.first)});
|
||||
}
|
||||
y += lineHeight;
|
||||
}
|
||||
if (getVerticalCentered () && !lines.empty ())
|
||||
{
|
||||
auto maxHeight = lines.back ().r.bottom;
|
||||
auto offset = ((getHeight () - textInset.y) - maxHeight) / 2.;
|
||||
if (offset > 0)
|
||||
{
|
||||
for (auto& l : lines)
|
||||
l.r.offset (0, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,143 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cparamdisplay.h"
|
||||
#include "itextlabellistener.h"
|
||||
#include "../dispatchlist.h"
|
||||
#include "../cstring.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CLabel Declaration
|
||||
//! @brief a text label
|
||||
/// @ingroup controls
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTextLabel : public CParamDisplay
|
||||
{
|
||||
public:
|
||||
CTextLabel (const CRect& size, UTF8StringPtr txt = nullptr, CBitmap* background = nullptr, const int32_t style = 0);
|
||||
CTextLabel (const CTextLabel& textLabel);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CTextLabel Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
/** set text */
|
||||
virtual void setText (const UTF8String& txt);
|
||||
/** read only access to text */
|
||||
virtual const UTF8String& getText () const;
|
||||
|
||||
enum TextTruncateMode {
|
||||
/** no characters will be removed */
|
||||
kTruncateNone = 0,
|
||||
/** characters will be removed from the beginning of the text */
|
||||
kTruncateHead,
|
||||
/** characters will be removed from the end of the text */
|
||||
kTruncateTail
|
||||
};
|
||||
|
||||
/** set text truncate mode */
|
||||
virtual void setTextTruncateMode (TextTruncateMode mode);
|
||||
/** get text truncate mode */
|
||||
TextTruncateMode getTextTruncateMode () const { return textTruncateMode; }
|
||||
/** get the truncated text */
|
||||
const UTF8String& getTruncatedText () const { return truncatedText; }
|
||||
|
||||
/** register a text label listener */
|
||||
void registerTextLabelListener (ITextLabelListener* listener);
|
||||
/** unregister a text label listener */
|
||||
void unregisterTextLabelListener (ITextLabelListener* listener);
|
||||
//@}
|
||||
|
||||
void draw (CDrawContext* pContext) override;
|
||||
bool sizeToFit () override;
|
||||
void setViewSize (const CRect& rect, bool invalid = true) override;
|
||||
void drawStyleChanged () override;
|
||||
void valueChanged () override;
|
||||
|
||||
CLASS_METHODS(CTextLabel, CParamDisplay)
|
||||
protected:
|
||||
~CTextLabel () noexcept override = default;
|
||||
void freeText ();
|
||||
void calculateTruncatedText ();
|
||||
|
||||
#if VSTGUI_ENABLE_DEPRECATED_METHODS
|
||||
bool onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons) override { return false; }
|
||||
#endif
|
||||
|
||||
TextTruncateMode textTruncateMode;
|
||||
UTF8String text;
|
||||
UTF8String truncatedText;
|
||||
using TextLabelListenerList = DispatchList<ITextLabelListener*>;
|
||||
std::unique_ptr<TextLabelListenerList> listeners;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Multi line text label
|
||||
* @ingroup new_in_4_5
|
||||
*/
|
||||
class CMultiLineTextLabel : public CTextLabel
|
||||
{
|
||||
public:
|
||||
CMultiLineTextLabel (const CRect& size);
|
||||
CMultiLineTextLabel (const CMultiLineTextLabel&) = default;
|
||||
|
||||
enum class LineLayout {
|
||||
/** clip lines overflowing the view size width */
|
||||
clip,
|
||||
/** truncate lines overflowing the view size width */
|
||||
truncate,
|
||||
/** wrap overflowing words to next line */
|
||||
wrap
|
||||
};
|
||||
void setLineLayout (LineLayout layout);
|
||||
LineLayout getLineLayout () const { return lineLayout; }
|
||||
|
||||
/** automatically resize the view according to the contents (only the height)
|
||||
* @param state on or off
|
||||
*/
|
||||
void setAutoHeight (bool state);
|
||||
/** returns true if this view resizes itself according to the contents */
|
||||
bool getAutoHeight () const { return autoHeight; }
|
||||
|
||||
/** draw the lines vertical centered
|
||||
* @param state on or off
|
||||
*/
|
||||
void setVerticalCentered (bool state);
|
||||
/** returns true if the view draws the lines vertically centered */
|
||||
bool getVerticalCentered () const { return verticalCentered; }
|
||||
|
||||
/** return the maximum line width of all lines */
|
||||
CCoord getMaxLineWidth ();
|
||||
|
||||
void drawRect (CDrawContext* pContext, const CRect& updateRect) override;
|
||||
bool sizeToFit () override;
|
||||
void setText (const UTF8String& txt) override;
|
||||
void setViewSize (const CRect& rect, bool invalid = true) override;
|
||||
void setTextTruncateMode (TextTruncateMode mode) override;
|
||||
void setValue (float val) override;
|
||||
private:
|
||||
void drawStyleChanged () override;
|
||||
void calculateWrapLine (CDrawContext *context, std::pair<UTF8String, double> &element, const IFontPainter *const &fontPainter, double lineHeight, double lineWidth, double maxWidth, const CPoint &textInset, CCoord &y);
|
||||
|
||||
void recalculateLines (CDrawContext* context);
|
||||
void recalculateHeight ();
|
||||
|
||||
bool autoHeight {false};
|
||||
bool verticalCentered {false};
|
||||
LineLayout lineLayout {LineLayout::clip};
|
||||
|
||||
struct Line
|
||||
{
|
||||
CRect r;
|
||||
UTF8String str;
|
||||
};
|
||||
using Lines = std::vector<Line>;
|
||||
Lines lines;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,155 @@
|
||||
// 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 "cvumeter.h"
|
||||
#include "../coffscreencontext.h"
|
||||
#include "../cbitmap.h"
|
||||
#include "../cvstguitimer.h"
|
||||
#include <list>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// CVuMeter
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* CVuMeter constructor.
|
||||
* @param size the size of this view
|
||||
* @param onBitmap TODO
|
||||
* @param offBitmap TODO
|
||||
* @param nbLed TODO
|
||||
* @param style kHorizontal or kVertical
|
||||
*/
|
||||
//------------------------------------------------------------------------
|
||||
CVuMeter::CVuMeter (const CRect& size, CBitmap* onBitmap, CBitmap* offBitmap, int32_t nbLed,
|
||||
Style style)
|
||||
: CControl (size, nullptr, 0), offBitmap (nullptr), nbLed (nbLed), style (style)
|
||||
{
|
||||
setDecreaseStepValue (0.1f);
|
||||
|
||||
setOnBitmap (onBitmap);
|
||||
setOffBitmap (offBitmap);
|
||||
|
||||
rectOn (size.left, size.top, size.right, size.bottom);
|
||||
rectOff (size.left, size.top, size.right, size.bottom);
|
||||
|
||||
setWantsIdle (true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CVuMeter::CVuMeter (const CVuMeter& v)
|
||||
: CControl (v)
|
||||
, offBitmap (nullptr)
|
||||
, nbLed (v.nbLed)
|
||||
, style (v.style)
|
||||
, decreaseValue (v.decreaseValue)
|
||||
, rectOn (v.rectOn)
|
||||
, rectOff (v.rectOff)
|
||||
{
|
||||
setOffBitmap (v.offBitmap);
|
||||
setWantsIdle (true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CVuMeter::~CVuMeter () noexcept
|
||||
{
|
||||
setOnBitmap (nullptr);
|
||||
setOffBitmap (nullptr);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CVuMeter::setViewSize (const CRect& newSize, bool invalid)
|
||||
{
|
||||
CControl::setViewSize (newSize, invalid);
|
||||
rectOn = getViewSize ();
|
||||
rectOff = getViewSize ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool CVuMeter::sizeToFit ()
|
||||
{
|
||||
if (getDrawBackground ())
|
||||
{
|
||||
CRect vs (getViewSize ());
|
||||
vs.setWidth (getDrawBackground ()->getWidth ());
|
||||
vs.setHeight (getDrawBackground ()->getHeight ());
|
||||
setViewSize (vs);
|
||||
setMouseableArea (vs);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CVuMeter::setOffBitmap (CBitmap* bitmap)
|
||||
{
|
||||
if (offBitmap)
|
||||
offBitmap->forget ();
|
||||
offBitmap = bitmap;
|
||||
if (offBitmap)
|
||||
offBitmap->remember ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CVuMeter::setDirty (bool state)
|
||||
{
|
||||
CView::setDirty (state);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CVuMeter::onIdle ()
|
||||
{
|
||||
if (getOldValue () != value)
|
||||
invalid ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CVuMeter::draw (CDrawContext *_pContext)
|
||||
{
|
||||
if (!getOnBitmap ())
|
||||
return;
|
||||
|
||||
CRect _rectOn (rectOn);
|
||||
CRect _rectOff (rectOff);
|
||||
CPoint pointOn;
|
||||
CPoint pointOff;
|
||||
CDrawContext *pContext = _pContext;
|
||||
|
||||
bounceValue ();
|
||||
|
||||
float newValue = getOldValue () - decreaseValue;
|
||||
if (newValue < value)
|
||||
newValue = value;
|
||||
setOldValue (newValue);
|
||||
|
||||
newValue = (newValue - getMin ()) / getRange (); // normalize
|
||||
|
||||
if (style == Style::kHorizontal)
|
||||
{
|
||||
auto tmp = (CCoord)(((int32_t)(nbLed * newValue + 0.5f) / (float)nbLed) * getOnBitmap ()->getWidth ());
|
||||
pointOff (tmp, 0);
|
||||
|
||||
_rectOff.left += tmp;
|
||||
_rectOn.right = tmp + rectOn.left;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmp = (CCoord)(((int32_t)(nbLed * (1.f - newValue) + 0.5f) / (float)nbLed) * getOnBitmap ()->getHeight ());
|
||||
pointOn (0, tmp);
|
||||
|
||||
_rectOff.bottom = tmp + rectOff.top;
|
||||
_rectOn.top += tmp;
|
||||
}
|
||||
|
||||
if (getOffBitmap ())
|
||||
{
|
||||
getOffBitmap ()->draw (pContext, _rectOff, pointOff);
|
||||
}
|
||||
|
||||
getOnBitmap ()->draw (pContext, _rectOn, pointOn);
|
||||
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,74 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../enumbitset.h"
|
||||
#include "ccontrol.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CVuMeter Declaration
|
||||
//!
|
||||
/// @ingroup controls
|
||||
//-----------------------------------------------------------------------------
|
||||
class CVuMeter : public CControl
|
||||
{
|
||||
public:
|
||||
enum class Style : int32_t
|
||||
{
|
||||
kHorizontal,
|
||||
kVertical,
|
||||
};
|
||||
|
||||
CVuMeter (const CRect& size, CBitmap* onBitmap, CBitmap* offBitmap, int32_t nbLed,
|
||||
Style style = Style::kVertical);
|
||||
CVuMeter (const CVuMeter& vuMeter);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @name CVuMeter Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
//@{
|
||||
float getDecreaseStepValue () const { return decreaseValue; }
|
||||
virtual void setDecreaseStepValue (float value) { decreaseValue = value; }
|
||||
|
||||
virtual CBitmap* getOnBitmap () const { return getBackground (); }
|
||||
virtual CBitmap* getOffBitmap () const { return offBitmap; }
|
||||
virtual void setOnBitmap (CBitmap* bitmap) { setBackground (bitmap); }
|
||||
virtual void setOffBitmap (CBitmap* bitmap);
|
||||
|
||||
int32_t getNbLed () const { return nbLed; }
|
||||
void setNbLed (int32_t nb) { nbLed = nb; invalid (); }
|
||||
|
||||
void setStyle (Style newStyle)
|
||||
{
|
||||
style = newStyle;
|
||||
invalid ();
|
||||
}
|
||||
Style getStyle () const { return style; }
|
||||
//@}
|
||||
|
||||
// overrides
|
||||
void setDirty (bool state) override;
|
||||
void draw (CDrawContext* pContext) override;
|
||||
void setViewSize (const CRect& newSize, bool invalid = true) override;
|
||||
bool sizeToFit () override;
|
||||
void onIdle () override;
|
||||
|
||||
CLASS_METHODS(CVuMeter, CControl)
|
||||
protected:
|
||||
~CVuMeter () noexcept override;
|
||||
|
||||
CBitmap* offBitmap;
|
||||
|
||||
int32_t nbLed;
|
||||
Style style;
|
||||
float decreaseValue;
|
||||
|
||||
CRect rectOn;
|
||||
CRect rectOff;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,225 @@
|
||||
// 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 "cxypad.h"
|
||||
#include "../cdrawcontext.h"
|
||||
#include "../events.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CXYPad::CXYPad (const CRect& size)
|
||||
: CParamDisplay (size)
|
||||
, stopTrackingOnMouseExit (false)
|
||||
{
|
||||
CParamDisplay::setMax (2.f);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CXYPad::setHandleBitmap (CBitmap* bitmap)
|
||||
{
|
||||
handle = bitmap;
|
||||
invalid ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CBitmap* CXYPad::getHandleBitmap () const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CXYPad::draw (CDrawContext* context)
|
||||
{
|
||||
drawBack (context);
|
||||
|
||||
auto width = getWidth () - getRoundRectRadius ();
|
||||
auto height = getHeight () - getRoundRectRadius ();
|
||||
|
||||
float x, y;
|
||||
calculateXY (getValue (), x, y);
|
||||
|
||||
CRect r (x*width, y*height, x*width, y*height);
|
||||
if (auto bitmap = getHandleBitmap ())
|
||||
{
|
||||
auto bitmapSize = bitmap->getSize ();
|
||||
r.extend (bitmapSize.x / 2., bitmapSize.y / 2.);
|
||||
r.offset (getViewSize ().left + getRoundRectRadius () / 2.,
|
||||
getViewSize ().top + getRoundRectRadius () / 2.);
|
||||
bitmap->draw (context, r);
|
||||
}
|
||||
else
|
||||
{
|
||||
r.extend (getRoundRectRadius () / 2., getRoundRectRadius () / 2.);
|
||||
r.offset (getViewSize ().left + getRoundRectRadius () / 2.,
|
||||
getViewSize ().top + getRoundRectRadius () / 2.);
|
||||
context->setFillColor (getFontColor ());
|
||||
context->setDrawMode (kAntiAliasing);
|
||||
context->drawEllipse (r, kDrawFilled);
|
||||
}
|
||||
setDirty (false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CXYPad::drawBack (CDrawContext* context, CBitmap* newBack)
|
||||
{
|
||||
CParamDisplay::drawBack (context);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CXYPad::onMouseDownEvent (MouseDownEvent& event)
|
||||
{
|
||||
if (event.buttonState.isLeft ())
|
||||
{
|
||||
invalidMouseWheelEditTimer (this);
|
||||
mouseStartValue = getValue ();
|
||||
mouseChangeStartPoint = event.mousePosition;
|
||||
mouseChangeStartPoint.offset (-getViewSize ().left - getRoundRectRadius () / 2.,
|
||||
-getViewSize ().top - getRoundRectRadius () / 2.);
|
||||
beginEdit ();
|
||||
onMouseMove (event);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CXYPad::onMouseUpEvent (MouseUpEvent& event)
|
||||
{
|
||||
if (isEditing ())
|
||||
{
|
||||
endEdit ();
|
||||
event.consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CXYPad::onMouseCancelEvent (MouseCancelEvent &event)
|
||||
{
|
||||
if (isEditing ())
|
||||
{
|
||||
value = mouseStartValue;
|
||||
if (isDirty ())
|
||||
{
|
||||
valueChanged ();
|
||||
invalid ();
|
||||
}
|
||||
endEdit ();
|
||||
event.consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CXYPad::onMouseMoveEvent (MouseMoveEvent& event)
|
||||
{
|
||||
if (event.buttonState.isLeft () && isEditing ())
|
||||
{
|
||||
onMouseMove (event);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CXYPad::onMouseMove (MouseDownUpMoveEvent& event)
|
||||
{
|
||||
auto where = event.mousePosition;
|
||||
if (stopTrackingOnMouseExit)
|
||||
{
|
||||
if (!hitTest (where, event))
|
||||
{
|
||||
endEdit ();
|
||||
event.ignoreFollowUpMoveAndUpEvents (true);
|
||||
event.consumed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
float x, y;
|
||||
CCoord width = getWidth() - getRoundRectRadius ();
|
||||
CCoord height = getHeight() - getRoundRectRadius ();
|
||||
where.offset (-getViewSize ().left - getRoundRectRadius () / 2.,
|
||||
-getViewSize ().top - getRoundRectRadius () / 2.);
|
||||
|
||||
x = (float)(where.x / width);
|
||||
y = (float)(where.y / height);
|
||||
|
||||
boundValues (x, y);
|
||||
setValue (calculateValue (x, y));
|
||||
if (isDirty ())
|
||||
{
|
||||
valueChanged ();
|
||||
invalid ();
|
||||
}
|
||||
lastMouseChangePoint = where;
|
||||
event.consumed = true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CXYPad::onMouseWheelEvent (MouseWheelEvent& event)
|
||||
{
|
||||
float x, y;
|
||||
calculateXY (getValue (), x, y);
|
||||
|
||||
auto distanceX = static_cast<float> (event.deltaX) * getWheelInc ();
|
||||
auto distanceY = static_cast<float> (event.deltaY) * getWheelInc ();
|
||||
if (event.flags & MouseWheelEvent::DirectionInvertedFromDevice)
|
||||
{
|
||||
distanceX *= -1.f;
|
||||
distanceY *= -1.f;
|
||||
}
|
||||
if (event.modifiers.has (ModifierKey::Shift))
|
||||
{
|
||||
distanceX *= 0.1f;
|
||||
distanceY *= 0.1f;
|
||||
}
|
||||
x += distanceX;
|
||||
y += distanceY;
|
||||
boundValues (x, y);
|
||||
onMouseWheelEditing (this);
|
||||
setValue (calculateValue (x, y));
|
||||
if (isDirty ())
|
||||
{
|
||||
invalid ();
|
||||
valueChanged ();
|
||||
}
|
||||
event.consumed = true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CXYPad::onKeyboardEvent (KeyboardEvent& event)
|
||||
{
|
||||
if (event.type != EventType::KeyDown)
|
||||
return;
|
||||
if (event.virt == VirtualKey::Escape)
|
||||
{
|
||||
if (isEditing ())
|
||||
{
|
||||
onMouseCancel ();
|
||||
event.consumed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CXYPad::boundValues (float& x, float& y)
|
||||
{
|
||||
if (x < 0.f)
|
||||
x = 0.f;
|
||||
else if (x > 1.f)
|
||||
x = 1.f;
|
||||
if (y < 0.f)
|
||||
y = 0.f;
|
||||
else if (y > 1.f)
|
||||
y = 1.f;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CXYPad::setDefaultValue (float val)
|
||||
{
|
||||
CControl::setDefaultValue (calculateValue (val, val));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CXYPad::setDefaultValues (float x, float y)
|
||||
{
|
||||
CControl::setDefaultValue (calculateValue (x, y));
|
||||
}
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,68 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cparamdisplay.h"
|
||||
#include "../cbitmap.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class CXYPad : public CParamDisplay, protected CMouseWheelEditingSupport
|
||||
{
|
||||
public:
|
||||
explicit CXYPad (const CRect& size = CRect (0, 0, 0, 0));
|
||||
|
||||
void setStopTrackingOnMouseExit (bool state) { stopTrackingOnMouseExit = state; }
|
||||
bool getStopTrackingOnMouseExit () const { return stopTrackingOnMouseExit; }
|
||||
|
||||
void setHandleBitmap (CBitmap* bitmap);
|
||||
CBitmap* getHandleBitmap () const;
|
||||
|
||||
void draw (CDrawContext* context) override;
|
||||
void drawBack (CDrawContext* pContext, CBitmap* newBack = nullptr) override;
|
||||
|
||||
void onMouseDownEvent (MouseDownEvent& event) override;
|
||||
void onMouseUpEvent (MouseUpEvent& event) override;
|
||||
void onMouseMoveEvent (MouseMoveEvent& event) override;
|
||||
void onMouseCancelEvent (MouseCancelEvent& event) override;
|
||||
void onMouseWheelEvent (MouseWheelEvent& event) override;
|
||||
void onKeyboardEvent (KeyboardEvent& event) override;
|
||||
|
||||
/** set default value so that x and y default to val */
|
||||
void setDefaultValue (float val) override;
|
||||
/** set default value for x and y */
|
||||
void setDefaultValues (float x, float y);
|
||||
|
||||
static float calculateValue (float x, float y)
|
||||
{
|
||||
x = std::floor (x * 1000.f + 0.5f) * 0.001f;
|
||||
y = std::floor (y * 1000.f + 0.5f) * 0.0000001f;
|
||||
return x + y;
|
||||
}
|
||||
|
||||
static void calculateXY (float value, float& x, float& y)
|
||||
{
|
||||
x = std::floor (value * 1000.f + 0.5f) * 0.001f;
|
||||
y = std::floor ((value - x) * 10000000.f + 0.5f) * 0.001f;
|
||||
}
|
||||
|
||||
protected:
|
||||
void onMouseMove (MouseDownUpMoveEvent& event);
|
||||
|
||||
void setMin (float val) override { }
|
||||
void setMax (float val) override { }
|
||||
|
||||
void boundValues (float& x, float& y);
|
||||
|
||||
float mouseStartValue;
|
||||
CPoint mouseChangeStartPoint;
|
||||
CPoint lastMouseChangePoint;
|
||||
bool stopTrackingOnMouseExit;
|
||||
SharedPointer<CBitmap> handle;
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** Command menu item target
|
||||
* @ingroup new_in_4_7
|
||||
*/
|
||||
class ICommandMenuItemTarget : public virtual IReference
|
||||
{
|
||||
public:
|
||||
/** called before the item is shown to validate its state */
|
||||
virtual bool validateCommandMenuItem (CCommandMenuItem* item) = 0;
|
||||
/** called when the item was selected */
|
||||
virtual bool onCommandMenuItemSelected (CCommandMenuItem* item) = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class CommandMenuItemTargetAdapter : public ICommandMenuItemTarget
|
||||
{
|
||||
public:
|
||||
bool validateCommandMenuItem (CCommandMenuItem* item) override { return false; }
|
||||
bool onCommandMenuItemSelected (CCommandMenuItem* item) override { return false; }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
#include "../cbuttonstate.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class IControlListener
|
||||
{
|
||||
public:
|
||||
virtual ~IControlListener () noexcept = default;
|
||||
virtual void valueChanged (CControl* pControl) = 0;
|
||||
/** return 1 if you want the control to not handle it, otherwise 0 */
|
||||
virtual int32_t controlModifierClicked (CControl* pControl, CButtonState button) { return 0; }
|
||||
virtual void controlBeginEdit (CControl* pControl) {}
|
||||
virtual void controlEndEdit (CControl* pControl) {}
|
||||
virtual void controlTagWillChange (CControl* pControl) {}
|
||||
virtual void controlTagDidChange (CControl* pControl) {}
|
||||
};
|
||||
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Option menu listener
|
||||
* @ingroup new_in_4_7
|
||||
*/
|
||||
class IOptionMenuListener
|
||||
{
|
||||
public:
|
||||
/** called before the menu pops up */
|
||||
virtual void onOptionMenuPrePopup (COptionMenu* menu) = 0;
|
||||
/** called after the menu pops up */
|
||||
virtual void onOptionMenuPostPopup (COptionMenu* menu) = 0;
|
||||
/** called when the platform optionmenu returns the result and before the value of the option
|
||||
* menu is set.
|
||||
* @ingroup new_in_4_10
|
||||
*
|
||||
* @param menu the listened menu
|
||||
* @param selectedMenu the menu containing the selected item
|
||||
* @param selectedIndex the index of the selected item
|
||||
* @return return true to prevent further propagating the call to other listeners and to
|
||||
* prevent setting the value of the option menu
|
||||
*/
|
||||
virtual bool onOptionMenuSetPopupResult (COptionMenu* menu, COptionMenu* selectedMenu,
|
||||
int32_t selectedIndex) = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class OptionMenuListenerAdapter : public IOptionMenuListener
|
||||
{
|
||||
public:
|
||||
void onOptionMenuPrePopup (COptionMenu* menu) override {}
|
||||
void onOptionMenuPostPopup (COptionMenu* menu) override {}
|
||||
bool onOptionMenuSetPopupResult (COptionMenu* menu, COptionMenu* selectedMenu,
|
||||
int32_t selectedIndex) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** Listener for a text edit
|
||||
* @ingroup new_in_4_10
|
||||
*/
|
||||
class ITextEditListener
|
||||
{
|
||||
public:
|
||||
/** called when the native platform text edit control was created and started to listen for keyboard input. */
|
||||
virtual void onTextEditPlatformControlTookFocus (CTextEdit* textEdit) = 0;
|
||||
/** called when the natvie platform text edit control is going to be destroyed. */
|
||||
virtual void onTextEditPlatformControlLostFocus (CTextEdit* textEdit) = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class TextEditListenerAdapter : public ITextEditListener
|
||||
{
|
||||
public:
|
||||
void onTextEditPlatformControlTookFocus (CTextEdit* textEdit) override {}
|
||||
void onTextEditPlatformControlLostFocus (CTextEdit* textEdit) override {}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vstguifwd.h"
|
||||
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** Listener for a text label
|
||||
* @ingroup new_in_4_7
|
||||
*/
|
||||
class ITextLabelListener
|
||||
{
|
||||
public:
|
||||
/** the truncated text has changed */
|
||||
virtual void onTextLabelTruncatedTextChanged (CTextLabel* label) = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class TextLabelListenerAdapter : public ITextLabelListener
|
||||
{
|
||||
public:
|
||||
void onTextLabelTruncatedTextChanged (CTextLabel* label) override {}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
Reference in New Issue
Block a user