Initial release

This commit is contained in:
civ
2026-08-16 18:24:52 +07:00
commit 876886a39a
13244 changed files with 2353959 additions and 0 deletions
@@ -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