Mercurial > hg > pub > prymula > com
diff DPF-Prymula-audioplugins/dpf/dgl/src/ImageBaseWidgets.cpp @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DPF-Prymula-audioplugins/dpf/dgl/src/ImageBaseWidgets.cpp Mon Oct 16 21:53:34 2023 +0200 @@ -0,0 +1,967 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../ImageBaseWidgets.hpp" +#include "../Color.hpp" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +template <class ImageType> +ImageBaseAboutWindow<ImageType>::ImageBaseAboutWindow(Window& transientParentWindow, const ImageType& image) + : StandaloneWindow(transientParentWindow.getApp(), transientParentWindow), + img(image) +{ + setResizable(false); + setTitle("About"); + + if (image.isValid()) + { + setSize(image.getSize()); + setGeometryConstraints(image.getWidth(), image.getHeight(), true, true); + } + + done(); +} + +template <class ImageType> +ImageBaseAboutWindow<ImageType>::ImageBaseAboutWindow(TopLevelWidget* const topLevelWidget, const ImageType& image) + : StandaloneWindow(topLevelWidget->getApp(), topLevelWidget->getWindow()), + img(image) +{ + setResizable(false); + setTitle("About"); + + if (image.isValid()) + { + setSize(image.getSize()); + setGeometryConstraints(image.getWidth(), image.getHeight(), true, true); + } + + done(); +} + +template <class ImageType> +void ImageBaseAboutWindow<ImageType>::setImage(const ImageType& image) +{ + if (img == image) + return; + + if (image.isInvalid()) + { + img = image; + return; + } + + reinit(); + + img = image; + + setSize(image.getSize()); + setGeometryConstraints(image.getWidth(), image.getHeight(), true, true); + + done(); +} + +template <class ImageType> +void ImageBaseAboutWindow<ImageType>::onDisplay() +{ + img.draw(getGraphicsContext()); +} + +template <class ImageType> +bool ImageBaseAboutWindow<ImageType>::onKeyboard(const KeyboardEvent& ev) +{ + if (ev.press && ev.key == kKeyEscape) + { + close(); + return true; + } + + return false; +} + +template <class ImageType> +bool ImageBaseAboutWindow<ImageType>::onMouse(const MouseEvent& ev) +{ + if (ev.press) + { + close(); + return true; + } + + return false; +} + +// -------------------------------------------------------------------------------------------------------------------- + +template <class ImageType> +struct ImageBaseButton<ImageType>::PrivateData : public ButtonEventHandler::Callback { + ImageBaseButton<ImageType>::Callback* callback; + ImageType imageNormal; + ImageType imageHover; + ImageType imageDown; + + PrivateData(const ImageType& normal, const ImageType& hover, const ImageType& down) + : callback(nullptr), + imageNormal(normal), + imageHover(hover), + imageDown(down) {} + + void buttonClicked(SubWidget* widget, int button) override + { + if (callback != nullptr) + if (ImageBaseButton* const imageButton = dynamic_cast<ImageBaseButton*>(widget)) + callback->imageButtonClicked(imageButton, button); + } + + DISTRHO_DECLARE_NON_COPYABLE(PrivateData) +}; + +// -------------------------------------------------------------------------------------------------------------------- + +template <class ImageType> +ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& image) + : SubWidget(parentWidget), + ButtonEventHandler(this), + pData(new PrivateData(image, image, image)) +{ + ButtonEventHandler::setCallback(pData); + setSize(image.getSize()); +} + +template <class ImageType> +ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown) + : SubWidget(parentWidget), + ButtonEventHandler(this), + pData(new PrivateData(imageNormal, imageNormal, imageDown)) +{ + DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); + + ButtonEventHandler::setCallback(pData); + setSize(imageNormal.getSize()); +} + +template <class ImageType> +ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown) + : SubWidget(parentWidget), + ButtonEventHandler(this), + pData(new PrivateData(imageNormal, imageHover, imageDown)) +{ + DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize()); + + ButtonEventHandler::setCallback(pData); + setSize(imageNormal.getSize()); +} + +template <class ImageType> +ImageBaseButton<ImageType>::~ImageBaseButton() +{ + delete pData; +} + +template <class ImageType> +void ImageBaseButton<ImageType>::setCallback(Callback* callback) noexcept +{ + pData->callback = callback; +} + +template <class ImageType> +void ImageBaseButton<ImageType>::onDisplay() +{ + const GraphicsContext& context(getGraphicsContext()); + + const State state = ButtonEventHandler::getState(); + + if (ButtonEventHandler::isCheckable()) + { + if (ButtonEventHandler::isChecked()) + pData->imageDown.draw(context); + else if (state & kButtonStateHover) + pData->imageHover.draw(context); + else + pData->imageNormal.draw(context); + } + else + { + if (state & kButtonStateActive) + pData->imageDown.draw(context); + else if (state & kButtonStateHover) + pData->imageHover.draw(context); + else + pData->imageNormal.draw(context); + } +} + +template <class ImageType> +bool ImageBaseButton<ImageType>::onMouse(const MouseEvent& ev) +{ + if (SubWidget::onMouse(ev)) + return true; + return ButtonEventHandler::mouseEvent(ev); +} + +template <class ImageType> +bool ImageBaseButton<ImageType>::onMotion(const MotionEvent& ev) +{ + if (SubWidget::onMotion(ev)) + return true; + return ButtonEventHandler::motionEvent(ev); +} + +// -------------------------------------------------------------------------------------------------------------------- + +template <class ImageType> +struct ImageBaseKnob<ImageType>::PrivateData : public KnobEventHandler::Callback { + ImageBaseKnob<ImageType>::Callback* callback; + ImageType image; + + int rotationAngle; + + bool alwaysRepaint; + bool isImgVertical; + uint imgLayerWidth; + uint imgLayerHeight; + uint imgLayerCount; + bool isReady; + + union { + uint glTextureId; + void* cairoSurface; + }; + + explicit PrivateData(const ImageType& img) + : callback(nullptr), + image(img), + rotationAngle(0), + alwaysRepaint(false), + isImgVertical(img.getHeight() > img.getWidth()), + imgLayerWidth(isImgVertical ? img.getWidth() : img.getHeight()), + imgLayerHeight(imgLayerWidth), + imgLayerCount(isImgVertical ? img.getHeight()/imgLayerHeight : img.getWidth()/imgLayerWidth), + isReady(false) + { + init(); + } + + explicit PrivateData(PrivateData* const other) + : callback(other->callback), + image(other->image), + rotationAngle(other->rotationAngle), + alwaysRepaint(other->alwaysRepaint), + isImgVertical(other->isImgVertical), + imgLayerWidth(other->imgLayerWidth), + imgLayerHeight(other->imgLayerHeight), + imgLayerCount(other->imgLayerCount), + isReady(false) + { + init(); + } + + void assignFrom(PrivateData* const other) + { + cleanup(); + image = other->image; + rotationAngle = other->rotationAngle; + callback = other->callback; + alwaysRepaint = other->alwaysRepaint; + isImgVertical = other->isImgVertical; + imgLayerWidth = other->imgLayerWidth; + imgLayerHeight = other->imgLayerHeight; + imgLayerCount = other->imgLayerCount; + isReady = false; + init(); + } + + ~PrivateData() + { + cleanup(); + } + + void knobDragStarted(SubWidget* const widget) override + { + if (callback != nullptr) + if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget)) + callback->imageKnobDragStarted(imageKnob); + } + + void knobDragFinished(SubWidget* const widget) override + { + if (callback != nullptr) + if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget)) + callback->imageKnobDragFinished(imageKnob); + } + + void knobValueChanged(SubWidget* const widget, const float value) override + { + if (rotationAngle == 0 || alwaysRepaint) + isReady = false; + + if (callback != nullptr) + if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget)) + callback->imageKnobValueChanged(imageKnob, value); + } + + // implemented independently per graphics backend + void init(); + void cleanup(); + + DISTRHO_DECLARE_NON_COPYABLE(PrivateData) +}; + +// -------------------------------------------------------------------------------------------------------------------- + +template <class ImageType> +ImageBaseKnob<ImageType>::ImageBaseKnob(Widget* const parentWidget, + const ImageType& image, + const Orientation orientation) noexcept + : SubWidget(parentWidget), + KnobEventHandler(this), + pData(new PrivateData(image)) +{ + KnobEventHandler::setCallback(pData); + setOrientation(orientation); + setSize(pData->imgLayerWidth, pData->imgLayerHeight); +} + +template <class ImageType> +ImageBaseKnob<ImageType>::ImageBaseKnob(const ImageBaseKnob<ImageType>& imageKnob) + : SubWidget(imageKnob.getParentWidget()), + KnobEventHandler(this, imageKnob), + pData(new PrivateData(imageKnob.pData)) +{ + KnobEventHandler::setCallback(pData); + setOrientation(imageKnob.getOrientation()); + setSize(pData->imgLayerWidth, pData->imgLayerHeight); +} + +template <class ImageType> +ImageBaseKnob<ImageType>& ImageBaseKnob<ImageType>::operator=(const ImageBaseKnob<ImageType>& imageKnob) +{ + KnobEventHandler::operator=(imageKnob); + pData->assignFrom(imageKnob.pData); + setSize(pData->imgLayerWidth, pData->imgLayerHeight); + return *this; +} + +template <class ImageType> +ImageBaseKnob<ImageType>::~ImageBaseKnob() +{ + delete pData; +} + +template <class ImageType> +void ImageBaseKnob<ImageType>::setCallback(Callback* callback) noexcept +{ + pData->callback = callback; +} + +template <class ImageType> +void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept +{ + DISTRHO_SAFE_ASSERT_RETURN(count > 1,); + + pData->imgLayerCount = count; + + if (pData->isImgVertical) + pData->imgLayerHeight = pData->image.getHeight()/count; + else + pData->imgLayerWidth = pData->image.getWidth()/count; + + setSize(pData->imgLayerWidth, pData->imgLayerHeight); +} + +template <class ImageType> +void ImageBaseKnob<ImageType>::setRotationAngle(int angle) +{ + if (pData->rotationAngle == angle) + return; + + pData->rotationAngle = angle; + pData->isReady = false; +} + +template <class ImageType> +bool ImageBaseKnob<ImageType>::setValue(float value, bool sendCallback) noexcept +{ + if (KnobEventHandler::setValue(value, sendCallback)) + { + if (pData->rotationAngle == 0 || pData->alwaysRepaint) + pData->isReady = false; + + return true; + } + + return false; +} + +template <class ImageType> +bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev) +{ + if (SubWidget::onMouse(ev)) + return true; + return KnobEventHandler::mouseEvent(ev, getTopLevelWidget()->getScaleFactor()); +} + +template <class ImageType> +bool ImageBaseKnob<ImageType>::onMotion(const MotionEvent& ev) +{ + if (SubWidget::onMotion(ev)) + return true; + return KnobEventHandler::motionEvent(ev, getTopLevelWidget()->getScaleFactor()); +} + +template <class ImageType> +bool ImageBaseKnob<ImageType>::onScroll(const ScrollEvent& ev) +{ + if (SubWidget::onScroll(ev)) + return true; + return KnobEventHandler::scrollEvent(ev); +} + +// -------------------------------------------------------------------------------------------------------------------- + +template <class ImageType> +struct ImageBaseSlider<ImageType>::PrivateData { + ImageType image; + float minimum; + float maximum; + float step; + float value; + float valueDef; + float valueTmp; + bool usingDefault; + + bool dragging; + bool checkable; + bool inverted; + bool valueIsSet; + double startedX; + double startedY; + + Callback* callback; + + Point<int> startPos; + Point<int> endPos; + Rectangle<double> sliderArea; + + PrivateData(const ImageType& img) + : image(img), + minimum(0.0f), + maximum(1.0f), + step(0.0f), + value(0.5f), + valueDef(value), + valueTmp(value), + usingDefault(false), + dragging(false), + checkable(false), + inverted(false), + valueIsSet(false), + startedX(0.0), + startedY(0.0), + callback(nullptr), + startPos(), + endPos(), + sliderArea() {} + + void recheckArea() noexcept + { + if (startPos.getY() == endPos.getY()) + { + // horizontal + sliderArea = Rectangle<double>(startPos.getX(), + startPos.getY(), + endPos.getX() + static_cast<int>(image.getWidth()) - startPos.getX(), + static_cast<int>(image.getHeight())); + } + else + { + // vertical + sliderArea = Rectangle<double>(startPos.getX(), + startPos.getY(), + static_cast<int>(image.getWidth()), + endPos.getY() + static_cast<int>(image.getHeight()) - startPos.getY()); + } + } + + DISTRHO_DECLARE_NON_COPYABLE(PrivateData) +}; + +// -------------------------------------------------------------------------------------------------------------------- + +template <class ImageType> +ImageBaseSlider<ImageType>::ImageBaseSlider(Widget* const parentWidget, const ImageType& image) noexcept + : SubWidget(parentWidget), + pData(new PrivateData(image)) +{ + setNeedsFullViewportDrawing(); +} + +template <class ImageType> +ImageBaseSlider<ImageType>::~ImageBaseSlider() +{ + delete pData; +} + +template <class ImageType> +float ImageBaseSlider<ImageType>::getValue() const noexcept +{ + return pData->value; +} + +template <class ImageType> +void ImageBaseSlider<ImageType>::setValue(float value, bool sendCallback) noexcept +{ + if (! pData->valueIsSet) + pData->valueIsSet = true; + + if (d_isEqual(pData->value, value)) + return; + + pData->value = value; + + if (d_isZero(pData->step)) + pData->valueTmp = value; + + repaint(); + + if (sendCallback && pData->callback != nullptr) + { + try { + pData->callback->imageSliderValueChanged(this, pData->value); + } DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setValue"); + } +} + +template <class ImageType> +void ImageBaseSlider<ImageType>::setStartPos(const Point<int>& startPos) noexcept +{ + pData->startPos = startPos; + pData->recheckArea(); +} + +template <class ImageType> +void ImageBaseSlider<ImageType>::setStartPos(int x, int y) noexcept +{ + setStartPos(Point<int>(x, y)); +} + +template <class ImageType> +void ImageBaseSlider<ImageType>::setEndPos(const Point<int>& endPos) noexcept +{ + pData->endPos = endPos; + pData->recheckArea(); +} + +template <class ImageType> +void ImageBaseSlider<ImageType>::setEndPos(int x, int y) noexcept +{ + setEndPos(Point<int>(x, y)); +} + +template <class ImageType> +void ImageBaseSlider<ImageType>::setCheckable(bool checkable) noexcept +{ + if (pData->checkable == checkable) + return; + + pData->checkable = checkable; + repaint(); +} + +template <class ImageType> +void ImageBaseSlider<ImageType>::setInverted(bool inverted) noexcept +{ + if (pData->inverted == inverted) + return; + + pData->inverted = inverted; + repaint(); +} + +template <class ImageType> +void ImageBaseSlider<ImageType>::setDefault(float value) noexcept +{ + pData->valueDef = value; + pData->usingDefault = true; +} + +template <class ImageType> +void ImageBaseSlider<ImageType>::setRange(float min, float max) noexcept +{ + pData->minimum = min; + pData->maximum = max; + + if (pData->value < min) + { + pData->value = min; + repaint(); + + if (pData->callback != nullptr && pData->valueIsSet) + { + try { + pData->callback->imageSliderValueChanged(this, pData->value); + } DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setRange < min"); + } + } + else if (pData->value > max) + { + pData->value = max; + repaint(); + + if (pData->callback != nullptr && pData->valueIsSet) + { + try { + pData->callback->imageSliderValueChanged(this, pData->value); + } DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setRange > max"); + } + } +} + +template <class ImageType> +void ImageBaseSlider<ImageType>::setStep(float step) noexcept +{ + pData->step = step; +} + +template <class ImageType> +void ImageBaseSlider<ImageType>::setCallback(Callback* callback) noexcept +{ + pData->callback = callback; +} + +template <class ImageType> +void ImageBaseSlider<ImageType>::onDisplay() +{ + const GraphicsContext& context(getGraphicsContext()); + +#if 0 // DEBUG, paints slider area + Color(1.0f, 1.0f, 1.0f, 0.5f).setFor(context, true); + Rectangle<int>(pData->sliderArea.getX(), + pData->sliderArea.getY(), + pData->sliderArea.getX()+pData->sliderArea.getWidth(), + pData->sliderArea.getY()+pData->sliderArea.getHeight()).draw(context); + Color(1.0f, 1.0f, 1.0f, 1.0f).setFor(context, true); +#endif + + const float normValue = (pData->value - pData->minimum) / (pData->maximum - pData->minimum); + + int x, y; + + if (pData->startPos.getY() == pData->endPos.getY()) + { + // horizontal + if (pData->inverted) + x = pData->endPos.getX() - static_cast<int>(normValue*static_cast<float>(pData->endPos.getX()-pData->startPos.getX())); + else + x = pData->startPos.getX() + static_cast<int>(normValue*static_cast<float>(pData->endPos.getX()-pData->startPos.getX())); + + y = pData->startPos.getY(); + } + else + { + // vertical + x = pData->startPos.getX(); + + if (pData->inverted) + y = pData->endPos.getY() - static_cast<int>(normValue*static_cast<float>(pData->endPos.getY()-pData->startPos.getY())); + else + y = pData->startPos.getY() + static_cast<int>(normValue*static_cast<float>(pData->endPos.getY()-pData->startPos.getY())); + } + + pData->image.drawAt(context, x, y); +} + +template <class ImageType> +bool ImageBaseSlider<ImageType>::onMouse(const MouseEvent& ev) +{ + if (ev.button != 1) + return false; + + if (ev.press) + { + if (! pData->sliderArea.contains(ev.pos)) + return false; + + if ((ev.mod & kModifierShift) != 0 && pData->usingDefault) + { + setValue(pData->valueDef, true); + pData->valueTmp = pData->value; + return true; + } + + if (pData->checkable) + { + const float value = d_isEqual(pData->valueTmp, pData->minimum) ? pData->maximum : pData->minimum; + setValue(value, true); + pData->valueTmp = pData->value; + return true; + } + + float vper; + const double x = ev.pos.getX(); + const double y = ev.pos.getY(); + + if (pData->startPos.getY() == pData->endPos.getY()) + { + // horizontal + vper = float(x - pData->sliderArea.getX()) / float(pData->sliderArea.getWidth()); + } + else + { + // vertical + vper = float(y - pData->sliderArea.getY()) / float(pData->sliderArea.getHeight()); + } + + float value; + + if (pData->inverted) + value = pData->maximum - vper * (pData->maximum - pData->minimum); + else + value = pData->minimum + vper * (pData->maximum - pData->minimum); + + if (value < pData->minimum) + { + pData->valueTmp = value = pData->minimum; + } + else if (value > pData->maximum) + { + pData->valueTmp = value = pData->maximum; + } + else if (d_isNotZero(pData->step)) + { + pData->valueTmp = value; + const float rest = std::fmod(value, pData->step); + value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f); + } + + pData->dragging = true; + pData->startedX = x; + pData->startedY = y; + + if (pData->callback != nullptr) + pData->callback->imageSliderDragStarted(this); + + setValue(value, true); + + return true; + } + else if (pData->dragging) + { + if (pData->callback != nullptr) + pData->callback->imageSliderDragFinished(this); + + pData->dragging = false; + return true; + } + + return false; +} + +template <class ImageType> +bool ImageBaseSlider<ImageType>::onMotion(const MotionEvent& ev) +{ + if (! pData->dragging) + return false; + + const bool horizontal = pData->startPos.getY() == pData->endPos.getY(); + const double x = ev.pos.getX(); + const double y = ev.pos.getY(); + + if ((horizontal && pData->sliderArea.containsX(x)) || (pData->sliderArea.containsY(y) && ! horizontal)) + { + float vper; + + if (horizontal) + { + // horizontal + vper = float(x - pData->sliderArea.getX()) / float(pData->sliderArea.getWidth()); + } + else + { + // vertical + vper = float(y - pData->sliderArea.getY()) / float(pData->sliderArea.getHeight()); + } + + float value; + + if (pData->inverted) + value = pData->maximum - vper * (pData->maximum - pData->minimum); + else + value = pData->minimum + vper * (pData->maximum - pData->minimum); + + if (value < pData->minimum) + { + pData->valueTmp = value = pData->minimum; + } + else if (value > pData->maximum) + { + pData->valueTmp = value = pData->maximum; + } + else if (d_isNotZero(pData->step)) + { + pData->valueTmp = value; + const float rest = std::fmod(value, pData->step); + value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f); + } + + setValue(value, true); + } + else if (horizontal) + { + if (x < pData->sliderArea.getX()) + setValue(pData->inverted ? pData->maximum : pData->minimum, true); + else + setValue(pData->inverted ? pData->minimum : pData->maximum, true); + } + else + { + if (y < pData->sliderArea.getY()) + setValue(pData->inverted ? pData->maximum : pData->minimum, true); + else + setValue(pData->inverted ? pData->minimum : pData->maximum, true); + } + + return true; +} + +// -------------------------------------------------------------------------------------------------------------------- + +template <class ImageType> +struct ImageBaseSwitch<ImageType>::PrivateData { + ImageType imageNormal; + ImageType imageDown; + bool isDown; + Callback* callback; + + PrivateData(const ImageType& normal, const ImageType& down) + : imageNormal(normal), + imageDown(down), + isDown(false), + callback(nullptr) + { + DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); + } + + PrivateData(PrivateData* const other) + : imageNormal(other->imageNormal), + imageDown(other->imageDown), + isDown(other->isDown), + callback(other->callback) + { + DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); + } + + void assignFrom(PrivateData* const other) + { + imageNormal = other->imageNormal; + imageDown = other->imageDown; + isDown = other->isDown; + callback = other->callback; + DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); + } + + DISTRHO_DECLARE_NON_COPYABLE(PrivateData) +}; + +// -------------------------------------------------------------------------------------------------------------------- + +template <class ImageType> +ImageBaseSwitch<ImageType>::ImageBaseSwitch(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept + : SubWidget(parentWidget), + pData(new PrivateData(imageNormal, imageDown)) +{ + setSize(imageNormal.getSize()); +} + +template <class ImageType> +ImageBaseSwitch<ImageType>::ImageBaseSwitch(const ImageBaseSwitch<ImageType>& imageSwitch) noexcept + : SubWidget(imageSwitch.getParentWidget()), + pData(new PrivateData(imageSwitch.pData)) +{ + setSize(pData->imageNormal.getSize()); +} + +template <class ImageType> +ImageBaseSwitch<ImageType>& ImageBaseSwitch<ImageType>::operator=(const ImageBaseSwitch<ImageType>& imageSwitch) noexcept +{ + pData->assignFrom(imageSwitch.pData); + setSize(pData->imageNormal.getSize()); + return *this; +} + +template <class ImageType> +ImageBaseSwitch<ImageType>::~ImageBaseSwitch() +{ + delete pData; +} + +template <class ImageType> +bool ImageBaseSwitch<ImageType>::isDown() const noexcept +{ + return pData->isDown; +} + +template <class ImageType> +void ImageBaseSwitch<ImageType>::setDown(const bool down) noexcept +{ + if (pData->isDown == down) + return; + + pData->isDown = down; + repaint(); +} + +template <class ImageType> +void ImageBaseSwitch<ImageType>::setCallback(Callback* const callback) noexcept +{ + pData->callback = callback; +} + +template <class ImageType> +void ImageBaseSwitch<ImageType>::onDisplay() +{ + const GraphicsContext& context(getGraphicsContext()); + + if (pData->isDown) + pData->imageDown.draw(context); + else + pData->imageNormal.draw(context); +} + +template <class ImageType> +bool ImageBaseSwitch<ImageType>::onMouse(const MouseEvent& ev) +{ + if (ev.press && contains(ev.pos)) + { + pData->isDown = !pData->isDown; + + repaint(); + + if (pData->callback != nullptr) + pData->callback->imageSwitchClicked(this, pData->isDown); + + return true; + } + + return false; +} + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL