887 lines
24 KiB
C++
887 lines
24 KiB
C++
// SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
|
// license. You should have receive a LICENSE.md file along with the code.
|
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
|
|
|
#include "GUIComponents.h"
|
|
#include "ColorHelpers.h"
|
|
#include <complex>
|
|
#include <cmath>
|
|
|
|
#include "utility/vstgui_before.h"
|
|
#include "vstgui/lib/cdrawcontext.h"
|
|
#include "vstgui/lib/cgraphicspath.h"
|
|
#include "vstgui/lib/cvstguitimer.h"
|
|
#include "vstgui/lib/cframe.h"
|
|
#include "utility/vstgui_after.h"
|
|
|
|
///
|
|
SBoxContainer::SBoxContainer(const CRect& size)
|
|
: CViewContainer(size)
|
|
{
|
|
CViewContainer::setBackgroundColor(CColor(0, 0, 0, 0));
|
|
}
|
|
|
|
void SBoxContainer::setCornerRadius(CCoord radius)
|
|
{
|
|
cornerRadius_ = radius;
|
|
invalid();
|
|
}
|
|
|
|
void SBoxContainer::setBackgroundColor(const CColor& color)
|
|
{
|
|
backgroundColor_ = color;
|
|
invalid();
|
|
}
|
|
|
|
CColor SBoxContainer::getBackgroundColor() const
|
|
{
|
|
return backgroundColor_;
|
|
}
|
|
|
|
void SBoxContainer::drawRect(CDrawContext* dc, const CRect& updateRect)
|
|
{
|
|
CRect bounds = getViewSize();
|
|
|
|
dc->setDrawMode(kAntiAliasing);
|
|
|
|
SharedPointer<CGraphicsPath> path = owned(dc->createGraphicsPath());
|
|
path->addRoundRect(bounds, cornerRadius_);
|
|
|
|
dc->setFillColor(backgroundColor_);
|
|
dc->drawGraphicsPath(path.get(), CDrawContext::kPathFilled);
|
|
|
|
CViewContainer::drawRect(dc, updateRect);
|
|
}
|
|
|
|
///
|
|
STitleContainer::STitleContainer(const CRect& size, UTF8StringPtr text)
|
|
: SBoxContainer(size), text_(text ? text : ""), titleFont_(kNormalFont)
|
|
{
|
|
}
|
|
|
|
void STitleContainer::setTitleFont(CFontRef font)
|
|
{
|
|
titleFont_ = font;
|
|
invalid();
|
|
}
|
|
|
|
void STitleContainer::setTitleFontColor(CColor color)
|
|
{
|
|
titleFontColor_ = color;
|
|
invalid();
|
|
}
|
|
|
|
void STitleContainer::setTitleBackgroundColor(CColor color)
|
|
{
|
|
titleBackgroundColor_ = color;
|
|
invalid();
|
|
}
|
|
|
|
void STitleContainer::drawRect(CDrawContext* dc, const CRect& updateRect)
|
|
{
|
|
SBoxContainer::drawRect(dc, updateRect);
|
|
|
|
CRect bounds = getViewSize();
|
|
CCoord cornerRadius = cornerRadius_;
|
|
|
|
dc->setDrawMode(kAntiAliasing);
|
|
|
|
CCoord fontHeight = titleFont_->getSize();
|
|
CCoord titleHeight = fontHeight + 8.0;
|
|
|
|
CRect titleBounds = bounds;
|
|
titleBounds.bottom = titleBounds.top + titleHeight;
|
|
|
|
SharedPointer<CGraphicsPath> path = owned(dc->createGraphicsPath());
|
|
path->beginSubpath(titleBounds.getBottomRight());
|
|
path->addLine(titleBounds.getBottomLeft());
|
|
path->addArc(CRect(titleBounds.left, titleBounds.top, titleBounds.left + 2.0 * cornerRadius, titleBounds.top + 2.0 * cornerRadius), 180., 270., true);
|
|
path->addArc(CRect(titleBounds.right - 2.0 * cornerRadius, titleBounds.top, titleBounds.right, titleBounds.top + 2.0 * cornerRadius), 270., 360., true);
|
|
path->closeSubpath();
|
|
|
|
dc->setFillColor(titleBackgroundColor_);
|
|
dc->drawGraphicsPath(path, CDrawContext::kPathFilled);
|
|
|
|
dc->setFont(titleFont_);
|
|
dc->setFontColor(titleFontColor_);
|
|
dc->drawString(text_.c_str(), titleBounds, kCenterText);
|
|
}
|
|
|
|
///
|
|
void SFileDropTarget::setFileDropFunction(FileDropFunction f)
|
|
{
|
|
dropFunction_ = std::move(f);
|
|
}
|
|
|
|
DragOperation SFileDropTarget::onDragEnter(DragEventData data)
|
|
{
|
|
op_ = isFileDrop(data.drag) ?
|
|
DragOperation::Copy : DragOperation::None;
|
|
return op_;
|
|
}
|
|
|
|
DragOperation SFileDropTarget::onDragMove(DragEventData data)
|
|
{
|
|
(void)data;
|
|
return op_;
|
|
}
|
|
|
|
void SFileDropTarget::onDragLeave(DragEventData data)
|
|
{
|
|
(void)data;
|
|
op_ = DragOperation::None;
|
|
}
|
|
|
|
bool SFileDropTarget::onDrop(DragEventData data)
|
|
{
|
|
if (op_ != DragOperation::Copy || !isFileDrop(data.drag))
|
|
return false;
|
|
|
|
IDataPackage::Type type;
|
|
const void* bytes;
|
|
uint32_t size = data.drag->getData(0, bytes, type);
|
|
std::string path(reinterpret_cast<const char*>(bytes), size);
|
|
|
|
if (dropFunction_)
|
|
dropFunction_(path);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SFileDropTarget::isFileDrop(IDataPackage* package)
|
|
{
|
|
return package->getCount() == 1 &&
|
|
package->getDataType(0) == IDataPackage::kFilePath;
|
|
}
|
|
|
|
///
|
|
SValueMenu::SValueMenu(const CRect& bounds, IControlListener* listener, int32_t tag)
|
|
: CParamDisplay(bounds), menuListener_(owned(new MenuListener(*this)))
|
|
{
|
|
setListener(listener);
|
|
setTag(tag);
|
|
setWheelInc(0.0f);
|
|
}
|
|
|
|
void SValueMenu::setHoverColor(const CColor& color)
|
|
{
|
|
hoverColor_ = color;
|
|
invalid();
|
|
}
|
|
|
|
CMenuItem* SValueMenu::addEntry(CMenuItem* item, float value, int32_t index)
|
|
{
|
|
if (index < 0 || index > getNbEntries()) {
|
|
menuItems_.emplace_back(owned(item));
|
|
menuItemValues_.emplace_back(value);
|
|
}
|
|
else
|
|
{
|
|
menuItems_.insert(menuItems_.begin() + index, owned(item));
|
|
menuItemValues_.insert(menuItemValues_.begin() + index, value);
|
|
}
|
|
return item;
|
|
}
|
|
|
|
CMenuItem* SValueMenu::addEntry(const UTF8String& title, float value, int32_t index, int32_t itemFlags)
|
|
{
|
|
if (title == "-")
|
|
return addSeparator(index);
|
|
CMenuItem* item = new CMenuItem(title, nullptr, 0, nullptr, itemFlags);
|
|
return addEntry(item, value, index);
|
|
}
|
|
|
|
CMenuItem* SValueMenu::addSeparator(int32_t index)
|
|
{
|
|
CMenuItem* item = new CMenuItem("", nullptr, 0, nullptr, CMenuItem::kSeparator);
|
|
return addEntry(item, 0.0f, index);
|
|
}
|
|
|
|
int32_t SValueMenu::getNbEntries() const
|
|
{
|
|
return static_cast<int32_t>(menuItems_.size());
|
|
}
|
|
|
|
void SValueMenu::draw(CDrawContext* dc)
|
|
{
|
|
CColor backupColor = fontColor;
|
|
if (hovered_)
|
|
fontColor = hoverColor_;
|
|
CParamDisplay::draw(dc);
|
|
if (hovered_)
|
|
fontColor = backupColor;
|
|
}
|
|
|
|
CMouseEventResult SValueMenu::onMouseEntered(CPoint& where, const CButtonState& buttons)
|
|
{
|
|
hovered_ = true;
|
|
invalid();
|
|
return CParamDisplay::onMouseEntered(where, buttons);
|
|
}
|
|
|
|
CMouseEventResult SValueMenu::onMouseExited(CPoint& where, const CButtonState& buttons)
|
|
{
|
|
hovered_ = false;
|
|
invalid();
|
|
return CParamDisplay::onMouseExited(where, buttons);
|
|
}
|
|
|
|
CMouseEventResult SValueMenu::onMouseDown(CPoint& where, const CButtonState& buttons)
|
|
{
|
|
(void)where;
|
|
|
|
if (buttons & (kLButton|kRButton|kApple)) {
|
|
CFrame* frame = getFrame();
|
|
CRect bounds = getViewSize();
|
|
|
|
CPoint frameWhere = bounds.getBottomLeft();
|
|
this->localToFrame(frameWhere);
|
|
|
|
auto self = shared(this);
|
|
frame->doAfterEventProcessing([self, frameWhere]() {
|
|
if (CFrame* frame = self->getFrame()) {
|
|
SharedPointer<COptionMenu> menu = owned(new COptionMenu(CRect(), self->menuListener_, -1, nullptr, nullptr, COptionMenu::kPopupStyle));
|
|
for (const SharedPointer<CMenuItem>& item : self->menuItems_) {
|
|
menu->addEntry(item);
|
|
item->remember(); // above call does not increment refcount
|
|
}
|
|
menu->setFont(self->getFont());
|
|
menu->setFontColor(self->getFontColor());
|
|
menu->setBackColor(self->getBackColor());
|
|
menu->popup(frame, frameWhere + CPoint(0.0, 1.0));
|
|
}
|
|
});
|
|
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
|
|
}
|
|
|
|
return kMouseEventNotHandled;
|
|
}
|
|
|
|
bool SValueMenu::onWheel(const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons)
|
|
{
|
|
(void)where;
|
|
(void)buttons;
|
|
|
|
if (axis != kMouseWheelAxisY)
|
|
return false;
|
|
|
|
float wheelInc = getWheelInc();
|
|
if (wheelInc != 0) {
|
|
float oldValue = getValue();
|
|
setValueNormalized(getValueNormalized() + distance * wheelInc);
|
|
if (getValue() != oldValue) {
|
|
valueChanged();
|
|
invalid();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void SValueMenu::onItemClicked(int32_t index)
|
|
{
|
|
float oldValue = getValue();
|
|
setValue(menuItemValues_[index]);
|
|
if (getValue() != oldValue) {
|
|
valueChanged();
|
|
invalid();
|
|
}
|
|
}
|
|
|
|
///
|
|
SActionMenu::SActionMenu(const CRect& bounds, IControlListener* listener)
|
|
: CParamDisplay(bounds), menuListener_(owned(new MenuListener(*this)))
|
|
{
|
|
setListener(listener);
|
|
|
|
auto toString = [](float, std::string& result, CParamDisplay* display) {
|
|
result = static_cast<SActionMenu*>(display)->getTitle();
|
|
return true;
|
|
};
|
|
|
|
setValueToStringFunction2(toString);
|
|
}
|
|
|
|
void SActionMenu::setTitle(std::string title)
|
|
{
|
|
title_ = std::move(title);
|
|
invalid();
|
|
}
|
|
|
|
void SActionMenu::setHoverColor(const CColor& color)
|
|
{
|
|
hoverColor_ = color;
|
|
invalid();
|
|
}
|
|
|
|
CMenuItem* SActionMenu::addEntry(CMenuItem* item, int32_t tag, int32_t index)
|
|
{
|
|
if (index < 0 || index > getNbEntries()) {
|
|
menuItems_.emplace_back(owned(item));
|
|
menuItemTags_.emplace_back(tag);
|
|
}
|
|
else
|
|
{
|
|
menuItems_.insert(menuItems_.begin() + index, owned(item));
|
|
menuItemTags_.insert(menuItemTags_.begin() + index, tag);
|
|
}
|
|
return item;
|
|
}
|
|
|
|
CMenuItem* SActionMenu::addEntry(const UTF8String& title, int32_t tag, int32_t index, int32_t itemFlags)
|
|
{
|
|
if (title == "-")
|
|
return addSeparator(index);
|
|
CMenuItem* item = new CMenuItem(title, nullptr, 0, nullptr, itemFlags);
|
|
return addEntry(item, tag, index);
|
|
}
|
|
|
|
CMenuItem* SActionMenu::addSeparator(int32_t index)
|
|
{
|
|
CMenuItem* item = new CMenuItem("", nullptr, 0, nullptr, CMenuItem::kSeparator);
|
|
return addEntry(item, 0.0f, index);
|
|
}
|
|
|
|
int32_t SActionMenu::getNbEntries() const
|
|
{
|
|
return static_cast<int32_t>(menuItems_.size());
|
|
}
|
|
|
|
void SActionMenu::draw(CDrawContext* dc)
|
|
{
|
|
CColor backupColor = fontColor;
|
|
if (hovered_)
|
|
fontColor = hoverColor_;
|
|
CParamDisplay::draw(dc);
|
|
if (hovered_)
|
|
fontColor = backupColor;
|
|
}
|
|
|
|
CMouseEventResult SActionMenu::onMouseEntered(CPoint& where, const CButtonState& buttons)
|
|
{
|
|
hovered_ = true;
|
|
invalid();
|
|
return CParamDisplay::onMouseEntered(where, buttons);
|
|
}
|
|
|
|
CMouseEventResult SActionMenu::onMouseExited(CPoint& where, const CButtonState& buttons)
|
|
{
|
|
hovered_ = false;
|
|
invalid();
|
|
return CParamDisplay::onMouseExited(where, buttons);
|
|
}
|
|
|
|
CMouseEventResult SActionMenu::onMouseDown(CPoint& where, const CButtonState& buttons)
|
|
{
|
|
(void)where;
|
|
|
|
if (buttons & (kLButton|kRButton|kApple)) {
|
|
CFrame* frame = getFrame();
|
|
CRect bounds = getViewSize();
|
|
|
|
CPoint frameWhere = bounds.getBottomLeft();
|
|
this->localToFrame(frameWhere);
|
|
|
|
auto self = shared(this);
|
|
frame->doAfterEventProcessing([self, frameWhere]() {
|
|
if (CFrame* frame = self->getFrame()) {
|
|
SharedPointer<COptionMenu> menu = owned(new COptionMenu(CRect(), self->menuListener_, -1, nullptr, nullptr, COptionMenu::kPopupStyle));
|
|
for (const SharedPointer<CMenuItem>& item : self->menuItems_) {
|
|
menu->addEntry(item);
|
|
item->remember(); // above call does not increment refcount
|
|
}
|
|
menu->setFont(self->getFont());
|
|
menu->setFontColor(self->getFontColor());
|
|
menu->setBackColor(self->getBackColor());
|
|
menu->popup(frame, frameWhere + CPoint(0.0, 1.0));
|
|
}
|
|
});
|
|
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
|
|
}
|
|
|
|
return kMouseEventNotHandled;
|
|
}
|
|
|
|
void SActionMenu::onItemClicked(int32_t index)
|
|
{
|
|
setTag(menuItemTags_[index]);
|
|
setValue(1.0f);
|
|
if (listener)
|
|
listener->valueChanged(this);
|
|
setValue(0.0f);
|
|
if (listener)
|
|
listener->valueChanged(this);
|
|
}
|
|
|
|
///
|
|
void STextButton::setHoverColor(const CColor& color)
|
|
{
|
|
hoverColor_ = color;
|
|
invalid();
|
|
}
|
|
|
|
void STextButton::setInactiveColor(const CColor& color)
|
|
{
|
|
inactiveColor_ = color;
|
|
invalid();
|
|
}
|
|
|
|
void STextButton::setInactive(bool b)
|
|
{
|
|
inactive_ = b;
|
|
invalid();
|
|
}
|
|
|
|
void STextButton::draw(CDrawContext* context)
|
|
{
|
|
CColor backupColor = textColor;
|
|
if (hovered_)
|
|
textColor = hoverColor_; // textColor is protected
|
|
else if (inactive_)
|
|
textColor = inactiveColor_;
|
|
CTextButton::draw(context);
|
|
textColor = backupColor;
|
|
}
|
|
|
|
|
|
CMouseEventResult STextButton::onMouseEntered (CPoint& where, const CButtonState& buttons)
|
|
{
|
|
hovered_ = true;
|
|
if (OnHoverEnter)
|
|
OnHoverEnter();
|
|
invalid();
|
|
return CTextButton::onMouseEntered(where, buttons);
|
|
}
|
|
|
|
CMouseEventResult STextButton::onMouseExited (CPoint& where, const CButtonState& buttons)
|
|
{
|
|
hovered_ = false;
|
|
if (OnHoverLeave)
|
|
OnHoverLeave();
|
|
invalid();
|
|
return CTextButton::onMouseExited(where, buttons);
|
|
}
|
|
|
|
///
|
|
SStyledKnob::SStyledKnob(const CRect& size, IControlListener* listener, int32_t tag)
|
|
: CKnobBase(size, listener, tag, nullptr)
|
|
{
|
|
}
|
|
|
|
void SStyledKnob::setActiveTrackColor(const CColor& color)
|
|
{
|
|
if (activeTrackColor_ == color)
|
|
return;
|
|
activeTrackColor_ = color;
|
|
invalid();
|
|
}
|
|
|
|
void SStyledKnob::setInactiveTrackColor(const CColor& color)
|
|
{
|
|
if (inactiveTrackColor_ == color)
|
|
return;
|
|
inactiveTrackColor_ = color;
|
|
invalid();
|
|
}
|
|
|
|
void SStyledKnob::setLineIndicatorColor(const CColor& color)
|
|
{
|
|
if (lineIndicatorColor_ == color)
|
|
return;
|
|
lineIndicatorColor_ = color;
|
|
invalid();
|
|
}
|
|
|
|
void SStyledKnob::setFont(CFontRef font)
|
|
{
|
|
if (font_ == font)
|
|
return;
|
|
font_ = font;
|
|
invalid();
|
|
}
|
|
|
|
void SStyledKnob::setFontColor(CColor fontColor)
|
|
{
|
|
if (fontColor_ == fontColor)
|
|
return;
|
|
fontColor_ = fontColor;
|
|
invalid();
|
|
}
|
|
|
|
void SStyledKnob::setValueToStringFunction(ValueToStringFunction func)
|
|
{
|
|
valueToStringFunction_ = std::move(func);
|
|
invalid();
|
|
}
|
|
|
|
void SStyledKnob::draw(CDrawContext* dc)
|
|
{
|
|
const CCoord lineWidth = 4.0;
|
|
const CCoord indicatorLineLength = 8.0;
|
|
const CCoord angleSpread = 250.0;
|
|
const CCoord angle1 = 270.0 - 0.5 * angleSpread;
|
|
const CCoord angle2 = 270.0 + 0.5 * angleSpread;
|
|
|
|
dc->setDrawMode(kAntiAliasing);
|
|
|
|
const CRect bounds = getViewSize();
|
|
|
|
// compute inner bounds
|
|
CRect rect(bounds);
|
|
rect.setWidth(std::min(rect.getWidth(), rect.getHeight()));
|
|
rect.setHeight(rect.getWidth());
|
|
rect.centerInside(bounds);
|
|
rect.extend(-lineWidth, -lineWidth);
|
|
|
|
SharedPointer<CGraphicsPath> path;
|
|
|
|
// inactive track
|
|
path = owned(dc->createGraphicsPath());
|
|
path->addArc(rect, angle1, angle2, true);
|
|
|
|
dc->setFrameColor(inactiveTrackColor_);
|
|
dc->setLineWidth(lineWidth);
|
|
dc->setLineStyle(kLineSolid);
|
|
dc->drawGraphicsPath(path, CDrawContext::kPathStroked);
|
|
|
|
// active track
|
|
const CCoord v = getValueNormalized();
|
|
const CCoord vAngle = angle1 + v * angleSpread;
|
|
path = owned(dc->createGraphicsPath());
|
|
path->addArc(rect, angle1, vAngle, true);
|
|
|
|
dc->setFrameColor(activeTrackColor_);
|
|
dc->setLineWidth(lineWidth + 0.5);
|
|
dc->setLineStyle(kLineSolid);
|
|
dc->drawGraphicsPath(path, CDrawContext::kPathStroked);
|
|
|
|
// indicator line
|
|
{
|
|
CCoord module1 = 0.5 * rect.getWidth() - indicatorLineLength;
|
|
CCoord module2 = 0.5 * rect.getWidth();
|
|
std::complex<CCoord> c1 = std::polar(module1, vAngle * (M_PI / 180.0));
|
|
std::complex<CCoord> c2 = std::polar(module2, vAngle * (M_PI / 180.0));
|
|
|
|
CPoint p1(c1.real(), c1.imag());
|
|
CPoint p2(c2.real(), c2.imag());
|
|
p1.offset(rect.getCenter());
|
|
p2.offset(rect.getCenter());
|
|
|
|
dc->setFrameColor(lineIndicatorColor_);
|
|
dc->setLineWidth(1.0);
|
|
dc->setLineStyle(kLineSolid);
|
|
dc->drawLine(p1, p2);
|
|
}
|
|
|
|
if (valueToStringFunction_ && fontColor_.alpha > 0) {
|
|
std::string text;
|
|
if (valueToStringFunction_(getValue(), text)) {
|
|
dc->setFont(font_);
|
|
dc->setFontColor(fontColor_);
|
|
dc->drawString(text.c_str(), bounds);
|
|
}
|
|
}
|
|
}
|
|
|
|
///
|
|
SKnobCCBox::SKnobCCBox(const CRect& size, IControlListener* listener, int32_t tag)
|
|
: CViewContainer(size),
|
|
label_(makeOwned<CTextLabel>(CRect())),
|
|
knob_(makeOwned<SStyledKnob>(CRect(), listener, tag)),
|
|
ccLabel_(makeOwned<CTextLabel>(CRect()))
|
|
{
|
|
setBackgroundColor(CColor(0x00, 0x00, 0x00, 0x00));
|
|
|
|
label_->setText("Parameter");
|
|
label_->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
|
|
label_->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
|
|
label_->setFontColor(CColor(0x00, 0x00, 0x00, 0xff));
|
|
|
|
knob_->setLineIndicatorColor(CColor(0x00, 0x00, 0x00, 0xff));
|
|
|
|
ccLabel_->setText("CC 1");
|
|
ccLabel_->setStyle(CParamDisplay::kRoundRectStyle);
|
|
ccLabel_->setRoundRectRadius(5.0);
|
|
ccLabel_->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
|
|
ccLabel_->setFontColor(CColor(0xff, 0xff, 0xff));
|
|
|
|
addView(label_);
|
|
label_->remember();
|
|
addView(knob_);
|
|
knob_->remember();
|
|
addView(ccLabel_);
|
|
ccLabel_->remember();
|
|
|
|
updateViewColors();
|
|
updateViewSizes();
|
|
}
|
|
|
|
void SKnobCCBox::setHue(float hue)
|
|
{
|
|
hue_ = hue;
|
|
updateViewColors();
|
|
}
|
|
|
|
void SKnobCCBox::setNameLabelFont(CFontRef font)
|
|
{
|
|
label_->setFont(font);
|
|
updateViewSizes();
|
|
}
|
|
|
|
void SKnobCCBox::setCCLabelFont(CFontRef font)
|
|
{
|
|
ccLabel_->setFont(font);
|
|
updateViewSizes();
|
|
}
|
|
|
|
void SKnobCCBox::updateViewSizes()
|
|
{
|
|
const CRect size = getViewSize();
|
|
const CCoord ypad = 4.0;
|
|
|
|
const CFontRef nameFont = label_->getFont();
|
|
const CFontRef ccFont = ccLabel_->getFont();
|
|
|
|
nameLabelSize_ = CRect(0.0, 0.0, size.getWidth(), nameFont->getSize() + 2 * ypad);
|
|
ccLabelSize_ = CRect(0.0, size.getHeight() - ccFont->getSize() - 2 * ypad, size.getWidth(), size.getHeight());
|
|
knobSize_ = CRect(0.0, nameLabelSize_.bottom, size.getWidth(), ccLabelSize_.top);
|
|
|
|
// remove knob side areas
|
|
CCoord side = std::max(0.0, knobSize_.getWidth() - knobSize_.getHeight());
|
|
knobSize_.extend(-0.5 * side, 0.0);
|
|
|
|
//
|
|
label_->setViewSize(nameLabelSize_);
|
|
knob_->setViewSize(knobSize_);
|
|
ccLabel_->setViewSize(ccLabelSize_);
|
|
|
|
invalid();
|
|
}
|
|
|
|
void SKnobCCBox::updateViewColors()
|
|
{
|
|
const float knobLuma = 0.4;
|
|
const float ccLuma = 0.25;
|
|
|
|
SColorHCY knobActiveTrackColor { hue_, 1.0, knobLuma };
|
|
SColorHCY knobInactiveTrackColor { 0.0, 0.0, knobLuma };
|
|
knob_->setActiveTrackColor(knobActiveTrackColor.toColor());
|
|
knob_->setInactiveTrackColor(knobInactiveTrackColor.toColor());
|
|
|
|
SColorHCY ccLabelColor { hue_, 1.0, ccLuma };
|
|
ccLabel_->setBackColor(ccLabelColor.toColor());
|
|
|
|
invalid();
|
|
}
|
|
|
|
///
|
|
SControlsPanel::SControlsPanel(const CRect& size)
|
|
: CScrollView(
|
|
size, CRect(),
|
|
CScrollView::kVerticalScrollbar|CScrollView::kDontDrawFrame|CScrollView::kAutoHideScrollbars),
|
|
listener_(new ControlSlotListener(this))
|
|
{
|
|
setBackgroundColor(CColor(0x00, 0x00, 0x00, 0x00));
|
|
|
|
setScrollbarWidth(10.0);
|
|
|
|
relayoutTrigger_ = makeOwned<CVSTGUITimer>(
|
|
[this](CVSTGUITimer* timer) { timer->stop(); updateLayout(); },
|
|
1, false);
|
|
}
|
|
|
|
void SControlsPanel::setControlUsed(uint32_t index, bool used)
|
|
{
|
|
ControlSlot* slot = getSlot(index);
|
|
if (!slot && !used)
|
|
return;
|
|
if (!slot)
|
|
slot = getOrCreateSlot(index);
|
|
if (used != slot->used) {
|
|
slot->used = used;
|
|
relayoutTrigger_->start();
|
|
}
|
|
}
|
|
|
|
std::string SControlsPanel::getDefaultLabelText(uint32_t index)
|
|
{
|
|
(void)index;
|
|
return {};
|
|
}
|
|
|
|
SControlsPanel::ControlSlot* SControlsPanel::getSlot(uint32_t index)
|
|
{
|
|
ControlSlot* slot = nullptr;
|
|
if (index < slots_.size())
|
|
slot = slots_[index].get();
|
|
return slot;
|
|
}
|
|
|
|
SControlsPanel::ControlSlot* SControlsPanel::getOrCreateSlot(uint32_t index)
|
|
{
|
|
ControlSlot* slot = getSlot(index);
|
|
if (slot)
|
|
return slot;
|
|
|
|
if (index + 1 > slots_.size())
|
|
slots_.resize(index + 1);
|
|
|
|
slot = new ControlSlot;
|
|
slots_[index].reset(slot);
|
|
|
|
CRect boxSize { 0.0, 0.0, 120.0, 90.0 };
|
|
SharedPointer<SKnobCCBox> box = makeOwned<SKnobCCBox>(boxSize, listener_.get(), index);
|
|
slot->box = box;
|
|
slot->box->setCCLabelText(("CC " + std::to_string(index)).c_str());
|
|
|
|
slot->box->setValueToStringFunction([](float value, std::string& text) -> bool {
|
|
text = std::to_string(std::lround(value * 127));
|
|
return true;
|
|
});
|
|
|
|
return slot;
|
|
}
|
|
|
|
void SControlsPanel::setControlValue(uint32_t index, float value)
|
|
{
|
|
ControlSlot* slot = getOrCreateSlot(index);
|
|
SKnobCCBox* box = slot->box;
|
|
auto* control = box->getControl();
|
|
float oldValue = control->getValue();
|
|
control->setValue(value);
|
|
if (control->getValue() != oldValue)
|
|
box->invalid();
|
|
}
|
|
|
|
void SControlsPanel::setControlDefaultValue(uint32_t index, float value)
|
|
{
|
|
ControlSlot* slot = getOrCreateSlot(index);
|
|
SKnobCCBox* box = slot->box;
|
|
box->getControl()->setDefaultValue(value);
|
|
}
|
|
|
|
void SControlsPanel::setControlLabelText(uint32_t index, UTF8StringPtr text)
|
|
{
|
|
ControlSlot* slot = getOrCreateSlot(index);
|
|
SKnobCCBox* box = slot->box;
|
|
if (text && text[0] != '\0')
|
|
box->setNameLabelText(text);
|
|
else
|
|
box->setNameLabelText(getDefaultLabelText(index).c_str());
|
|
box->invalid();
|
|
}
|
|
|
|
void SControlsPanel::recalculateSubViews()
|
|
{
|
|
CScrollView::recalculateSubViews();
|
|
|
|
// maybe the operation just created the scroll bar
|
|
if (CScrollbar* vsb = getVerticalScrollbar()) {
|
|
// update scrollbar style
|
|
vsb->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
|
|
vsb->setBackgroundColor(CColor(0x00, 0x00, 0x00, 0x00));
|
|
vsb->setScrollerColor(CColor(0x00, 0x00, 0x00, 0x80));
|
|
}
|
|
}
|
|
|
|
void SControlsPanel::updateLayout()
|
|
{
|
|
removeAll();
|
|
|
|
const CRect viewBounds = getViewSize();
|
|
|
|
bool isFirstSlot = true;
|
|
CCoord itemWidth {};
|
|
CCoord itemHeight {};
|
|
CCoord itemOffsetX {};
|
|
|
|
int numColumns {};
|
|
const CCoord horizontalPadding = 4.0;
|
|
CCoord verticalPadding = 4.0;
|
|
CCoord interRowPadding = {};
|
|
CCoord interColumnPadding = 8.0;
|
|
|
|
int currentRow = 0;
|
|
int currentColumn = 0;
|
|
int containerBottom = 0;
|
|
|
|
uint32_t numSlots = static_cast<uint32_t>(slots_.size());
|
|
for (uint32_t i = 0; i < numSlots; ++i) {
|
|
ControlSlot* slot = slots_[i].get();
|
|
if (!slot || !slot->used)
|
|
continue;
|
|
|
|
CViewContainer* box = slot->box;
|
|
|
|
if (isFirstSlot) {
|
|
itemWidth = box->getWidth();
|
|
itemHeight = box->getHeight();
|
|
isFirstSlot = false;
|
|
numColumns = int((viewBounds.getWidth() - horizontalPadding) /
|
|
(itemWidth + interColumnPadding));
|
|
numColumns = std::max(1, numColumns);
|
|
itemOffsetX = (viewBounds.getWidth() - horizontalPadding -
|
|
numColumns * (itemWidth + interColumnPadding)) / 2.0;
|
|
|
|
int maxRowsShown = int((viewBounds.getHeight() - 2 * verticalPadding) / itemHeight);
|
|
if (maxRowsShown > 1)
|
|
interRowPadding = (viewBounds.getHeight() - 2 * verticalPadding - itemHeight * maxRowsShown) / (maxRowsShown - 1);
|
|
}
|
|
|
|
CRect itemBounds = box->getViewSize();
|
|
itemBounds.moveTo(
|
|
itemOffsetX + horizontalPadding + currentColumn * (interColumnPadding + itemWidth),
|
|
verticalPadding + currentRow * (interRowPadding + itemHeight));
|
|
|
|
box->setViewSize(itemBounds);
|
|
|
|
containerBottom = itemBounds.bottom;
|
|
|
|
addView(box);
|
|
box->remember();
|
|
|
|
if (++currentColumn == numColumns) {
|
|
currentColumn = 0;
|
|
++currentRow;
|
|
}
|
|
}
|
|
|
|
setContainerSize(CRect(0.0, 0.0, viewBounds.getWidth(), containerBottom + verticalPadding));
|
|
|
|
invalid();
|
|
}
|
|
|
|
void SControlsPanel::ControlSlotListener::valueChanged(CControl* pControl)
|
|
{
|
|
if (panel_->ValueChangeFunction)
|
|
panel_->ValueChangeFunction(pControl->getTag(), pControl->getValue());
|
|
}
|
|
|
|
void SControlsPanel::ControlSlotListener::controlBeginEdit(CControl* pControl)
|
|
{
|
|
if (panel_->BeginEditFunction)
|
|
panel_->BeginEditFunction(pControl->getTag());
|
|
}
|
|
|
|
void SControlsPanel::ControlSlotListener::controlEndEdit(CControl* pControl)
|
|
{
|
|
if (panel_->EndEditFunction)
|
|
panel_->EndEditFunction(pControl->getTag());
|
|
}
|
|
|
|
///
|
|
SPlaceHolder::SPlaceHolder(const CRect& size, const CColor& color)
|
|
: CView(size), color_(color)
|
|
{
|
|
}
|
|
|
|
void SPlaceHolder::draw(CDrawContext* dc)
|
|
{
|
|
const CRect bounds = getViewSize();
|
|
dc->setDrawMode(kAliasing);
|
|
dc->setFrameColor(color_);
|
|
dc->drawRect(bounds);
|
|
dc->drawLine(bounds.getTopLeft(), bounds.getBottomRight());
|
|
dc->drawLine(bounds.getTopRight(), bounds.getBottomLeft());
|
|
}
|