| @@ -19,7 +19,6 @@ | |||
| #include "ImageBase.hpp" | |||
| #include "ImageBaseWidgets.hpp" | |||
| #include "SubWidget.hpp" | |||
| #include <cairo/cairo.h> | |||
| @@ -115,25 +114,21 @@ class CairoBaseWidget : public BaseWidget | |||
| public: | |||
| /** | |||
| Constructor for a CairoSubWidget. | |||
| @see CreateFlags | |||
| */ | |||
| explicit CairoBaseWidget(Widget* const parentGroupWidget); | |||
| /** | |||
| Constructor for a CairoTopLevelWidget. | |||
| @see CreateFlags | |||
| */ | |||
| explicit CairoBaseWidget(Window& windowToMapTo); | |||
| /** | |||
| Constructor for a CairoStandaloneWindow without parent window. | |||
| @see CreateFlags | |||
| */ | |||
| explicit CairoBaseWidget(Application& app); | |||
| /** | |||
| Constructor for a CairoStandaloneWindow with parent window. | |||
| @see CreateFlags | |||
| */ | |||
| explicit CairoBaseWidget(Application& app, Window& parentWindow); | |||
| @@ -171,6 +166,9 @@ typedef CairoBaseWidget<StandaloneWindow> CairoStandaloneWindow; | |||
| typedef ImageBaseAboutWindow<CairoImage> CairoImageAboutWindow; | |||
| typedef ImageBaseButton<CairoImage> CairoImageButton; | |||
| typedef ImageBaseKnob<CairoImage> CairoImageKnob; | |||
| typedef ImageBaseSlider<CairoImage> CairoImageSlider; | |||
| typedef ImageBaseSwitch<CairoImage> CairoImageSwitch; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -82,6 +82,205 @@ private: | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| template <class ImageType> | |||
| class ImageBaseKnob : public SubWidget | |||
| { | |||
| public: | |||
| enum Orientation { | |||
| Horizontal, | |||
| Vertical | |||
| }; | |||
| class Callback | |||
| { | |||
| public: | |||
| virtual ~Callback() {} | |||
| virtual void imageKnobDragStarted(ImageBaseKnob* imageKnob) = 0; | |||
| virtual void imageKnobDragFinished(ImageBaseKnob* imageKnob) = 0; | |||
| virtual void imageKnobValueChanged(ImageBaseKnob* imageKnob, float value) = 0; | |||
| }; | |||
| explicit ImageBaseKnob(Widget* parentWidget, const ImageType& image, Orientation orientation = Vertical) noexcept; | |||
| explicit ImageBaseKnob(const ImageBaseKnob& imageKnob); | |||
| ImageBaseKnob& operator=(const ImageBaseKnob& imageKnob); | |||
| ~ImageBaseKnob() override; | |||
| float getValue() const noexcept; | |||
| void setDefault(float def) noexcept; | |||
| void setRange(float min, float max) noexcept; | |||
| void setStep(float step) noexcept; | |||
| void setValue(float value, bool sendCallback = false) noexcept; | |||
| void setUsingLogScale(bool yesNo) noexcept; | |||
| void setCallback(Callback* callback) noexcept; | |||
| void setOrientation(Orientation orientation) noexcept; | |||
| void setRotationAngle(int angle); | |||
| void setImageLayerCount(uint count) noexcept; | |||
| protected: | |||
| void onDisplay() override; | |||
| bool onMouse(const MouseEvent&) override; | |||
| bool onMotion(const MotionEvent&) override; | |||
| bool onScroll(const ScrollEvent&) override; | |||
| private: | |||
| struct PrivateData; | |||
| PrivateData* const pData; | |||
| /* | |||
| Image fImage; | |||
| float fMinimum; | |||
| float fMaximum; | |||
| float fStep; | |||
| float fValue; | |||
| float fValueDef; | |||
| float fValueTmp; | |||
| bool fUsingDefault; | |||
| bool fUsingLog; | |||
| Orientation fOrientation; | |||
| int fRotationAngle; | |||
| bool fDragging; | |||
| int fLastX; | |||
| int fLastY; | |||
| Callback* fCallback; | |||
| bool fIsImgVertical; | |||
| uint fImgLayerWidth; | |||
| uint fImgLayerHeight; | |||
| uint fImgLayerCount; | |||
| bool fIsReady; | |||
| GLuint fTextureId; | |||
| float _logscale(float value) const; | |||
| float _invlogscale(float value) const; | |||
| */ | |||
| DISTRHO_LEAK_DETECTOR(ImageBaseKnob) | |||
| }; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // note set range and step before setting the value | |||
| template <class ImageType> | |||
| class ImageBaseSlider : public SubWidget | |||
| { | |||
| public: | |||
| class Callback | |||
| { | |||
| public: | |||
| virtual ~Callback() {} | |||
| virtual void imageSliderDragStarted(ImageBaseSlider* imageSlider) = 0; | |||
| virtual void imageSliderDragFinished(ImageBaseSlider* imageSlider) = 0; | |||
| virtual void imageSliderValueChanged(ImageBaseSlider* imageSlider, float value) = 0; | |||
| }; | |||
| explicit ImageBaseSlider(Widget* parentWidget, const ImageType& image) noexcept; | |||
| float getValue() const noexcept; | |||
| void setValue(float value, bool sendCallback = false) noexcept; | |||
| void setDefault(float def) noexcept; | |||
| void setStartPos(const Point<int>& startPos) noexcept; | |||
| void setStartPos(int x, int y) noexcept; | |||
| void setEndPos(const Point<int>& endPos) noexcept; | |||
| void setEndPos(int x, int y) noexcept; | |||
| void setInverted(bool inverted) noexcept; | |||
| void setRange(float min, float max) noexcept; | |||
| void setStep(float step) noexcept; | |||
| void setCallback(Callback* callback) noexcept; | |||
| protected: | |||
| void onDisplay() override; | |||
| bool onMouse(const MouseEvent&) override; | |||
| bool onMotion(const MotionEvent&) override; | |||
| private: | |||
| struct PrivateData; | |||
| PrivateData* const pData; | |||
| /* | |||
| Image fImage; | |||
| float fMinimum; | |||
| float fMaximum; | |||
| float fStep; | |||
| float fValue; | |||
| float fValueDef; | |||
| float fValueTmp; | |||
| bool fUsingDefault; | |||
| bool fDragging; | |||
| bool fInverted; | |||
| bool fValueIsSet; | |||
| int fStartedX; | |||
| int fStartedY; | |||
| Callback* fCallback; | |||
| Point<int> fStartPos; | |||
| Point<int> fEndPos; | |||
| Rectangle<double> fSliderArea; | |||
| void _recheckArea() noexcept; | |||
| */ | |||
| // these should not be used | |||
| void setAbsoluteX(int) const noexcept {} | |||
| void setAbsoluteY(int) const noexcept {} | |||
| void setAbsolutePos(int, int) const noexcept {} | |||
| void setAbsolutePos(const Point<int>&) const noexcept {} | |||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseSlider) | |||
| }; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| template <class ImageType> | |||
| class ImageBaseSwitch : public SubWidget | |||
| { | |||
| public: | |||
| class Callback | |||
| { | |||
| public: | |||
| virtual ~Callback() {} | |||
| virtual void imageSwitchClicked(ImageBaseSwitch* imageSwitch, bool down) = 0; | |||
| }; | |||
| explicit ImageBaseSwitch(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept; | |||
| explicit ImageBaseSwitch(const ImageBaseSwitch& imageSwitch) noexcept; | |||
| ImageBaseSwitch& operator=(const ImageBaseSwitch& imageSwitch) noexcept; | |||
| bool isDown() const noexcept; | |||
| void setDown(bool down) noexcept; | |||
| void setCallback(Callback* callback) noexcept; | |||
| protected: | |||
| void onDisplay() override; | |||
| bool onMouse(const MouseEvent&) override; | |||
| private: | |||
| struct PrivateData; | |||
| PrivateData* const pData; | |||
| /* | |||
| Image fImageNormal; | |||
| Image fImageDown; | |||
| bool fIsDown; | |||
| Callback* fCallback; | |||
| */ | |||
| DISTRHO_LEAK_DETECTOR(ImageBaseSwitch) | |||
| }; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED | |||
| @@ -17,206 +17,25 @@ | |||
| #ifndef DGL_IMAGE_WIDGETS_HPP_INCLUDED | |||
| #define DGL_IMAGE_WIDGETS_HPP_INCLUDED | |||
| #include "Image.hpp" | |||
| #include "ImageBaseWidgets.hpp" | |||
| #include "SubWidget.hpp" | |||
| // TODO switch to use templated image type after merging widget-related PRs | |||
| #if defined(__GNUC__) && (__GNUC__ >= 6) | |||
| # pragma GCC diagnostic push | |||
| # pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |||
| #endif | |||
| #include "OpenGL.hpp" | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| class ImageKnob : public SubWidget | |||
| { | |||
| public: | |||
| enum Orientation { | |||
| Horizontal, | |||
| Vertical | |||
| }; | |||
| class Callback | |||
| { | |||
| public: | |||
| virtual ~Callback() {} | |||
| virtual void imageKnobDragStarted(ImageKnob* imageKnob) = 0; | |||
| virtual void imageKnobDragFinished(ImageKnob* imageKnob) = 0; | |||
| virtual void imageKnobValueChanged(ImageKnob* imageKnob, float value) = 0; | |||
| }; | |||
| explicit ImageKnob(Widget* parentWidget, const Image& image, Orientation orientation = Vertical) noexcept; | |||
| explicit ImageKnob(const ImageKnob& imageKnob); | |||
| ImageKnob& operator=(const ImageKnob& imageKnob); | |||
| ~ImageKnob() override; | |||
| float getValue() const noexcept; | |||
| void setDefault(float def) noexcept; | |||
| void setRange(float min, float max) noexcept; | |||
| void setStep(float step) noexcept; | |||
| void setValue(float value, bool sendCallback = false) noexcept; | |||
| void setUsingLogScale(bool yesNo) noexcept; | |||
| void setCallback(Callback* callback) noexcept; | |||
| void setOrientation(Orientation orientation) noexcept; | |||
| void setRotationAngle(int angle); | |||
| void setImageLayerCount(uint count) noexcept; | |||
| protected: | |||
| void onDisplay() override; | |||
| bool onMouse(const MouseEvent&) override; | |||
| bool onMotion(const MotionEvent&) override; | |||
| bool onScroll(const ScrollEvent&) override; | |||
| private: | |||
| Image fImage; | |||
| float fMinimum; | |||
| float fMaximum; | |||
| float fStep; | |||
| float fValue; | |||
| float fValueDef; | |||
| float fValueTmp; | |||
| bool fUsingDefault; | |||
| bool fUsingLog; | |||
| Orientation fOrientation; | |||
| int fRotationAngle; | |||
| bool fDragging; | |||
| int fLastX; | |||
| int fLastY; | |||
| Callback* fCallback; | |||
| bool fIsImgVertical; | |||
| uint fImgLayerWidth; | |||
| uint fImgLayerHeight; | |||
| uint fImgLayerCount; | |||
| bool fIsReady; | |||
| GLuint fTextureId; | |||
| float _logscale(float value) const; | |||
| float _invlogscale(float value) const; | |||
| DISTRHO_LEAK_DETECTOR(ImageKnob) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // note set range and step before setting the value | |||
| class ImageSlider : public SubWidget | |||
| { | |||
| public: | |||
| class Callback | |||
| { | |||
| public: | |||
| virtual ~Callback() {} | |||
| virtual void imageSliderDragStarted(ImageSlider* imageSlider) = 0; | |||
| virtual void imageSliderDragFinished(ImageSlider* imageSlider) = 0; | |||
| virtual void imageSliderValueChanged(ImageSlider* imageSlider, float value) = 0; | |||
| }; | |||
| explicit ImageSlider(Widget* parentWidget, const Image& image) noexcept; | |||
| DISTRHO_DEPRECATED_BY("OpenGLImageAboutWindow") | |||
| typedef OpenGLImageAboutWindow ImageAboutWindow; | |||
| float getValue() const noexcept; | |||
| void setValue(float value, bool sendCallback = false) noexcept; | |||
| void setDefault(float def) noexcept; | |||
| DISTRHO_DEPRECATED_BY("OpenGLImageButton") | |||
| typedef OpenGLImageButton ImageButton; | |||
| void setStartPos(const Point<int>& startPos) noexcept; | |||
| void setStartPos(int x, int y) noexcept; | |||
| void setEndPos(const Point<int>& endPos) noexcept; | |||
| void setEndPos(int x, int y) noexcept; | |||
| DISTRHO_DEPRECATED_BY("OpenGLImageKnob") | |||
| typedef OpenGLImageKnob ImageKnob; | |||
| void setInverted(bool inverted) noexcept; | |||
| void setRange(float min, float max) noexcept; | |||
| void setStep(float step) noexcept; | |||
| DISTRHO_DEPRECATED_BY("OpenGLImageSlider") | |||
| typedef OpenGLImageSlider ImageSlider; | |||
| void setCallback(Callback* callback) noexcept; | |||
| protected: | |||
| void onDisplay() override; | |||
| bool onMouse(const MouseEvent&) override; | |||
| bool onMotion(const MotionEvent&) override; | |||
| private: | |||
| Image fImage; | |||
| float fMinimum; | |||
| float fMaximum; | |||
| float fStep; | |||
| float fValue; | |||
| float fValueDef; | |||
| float fValueTmp; | |||
| bool fUsingDefault; | |||
| bool fDragging; | |||
| bool fInverted; | |||
| bool fValueIsSet; | |||
| int fStartedX; | |||
| int fStartedY; | |||
| Callback* fCallback; | |||
| Point<int> fStartPos; | |||
| Point<int> fEndPos; | |||
| Rectangle<double> fSliderArea; | |||
| void _recheckArea() noexcept; | |||
| // these should not be used | |||
| void setAbsoluteX(int) const noexcept {} | |||
| void setAbsoluteY(int) const noexcept {} | |||
| void setAbsolutePos(int, int) const noexcept {} | |||
| void setAbsolutePos(const Point<int>&) const noexcept {} | |||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageSlider) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| class ImageSwitch : public SubWidget | |||
| { | |||
| public: | |||
| class Callback | |||
| { | |||
| public: | |||
| virtual ~Callback() {} | |||
| virtual void imageSwitchClicked(ImageSwitch* imageSwitch, bool down) = 0; | |||
| }; | |||
| explicit ImageSwitch(Widget* parentWidget, const Image& imageNormal, const Image& imageDown) noexcept; | |||
| explicit ImageSwitch(const ImageSwitch& imageSwitch) noexcept; | |||
| ImageSwitch& operator=(const ImageSwitch& imageSwitch) noexcept; | |||
| bool isDown() const noexcept; | |||
| void setDown(bool down) noexcept; | |||
| void setCallback(Callback* callback) noexcept; | |||
| protected: | |||
| void onDisplay() override; | |||
| bool onMouse(const MouseEvent&) override; | |||
| private: | |||
| Image fImageNormal; | |||
| Image fImageDown; | |||
| bool fIsDown; | |||
| Callback* fCallback; | |||
| DISTRHO_LEAK_DETECTOR(ImageSwitch) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| DISTRHO_DEPRECATED_BY("OpenGLImageSwitch") | |||
| typedef OpenGLImageSwitch ImageSwitch; | |||
| END_NAMESPACE_DGL | |||
| #if defined(__GNUC__) && (__GNUC__ >= 6) | |||
| # pragma GCC diagnostic pop | |||
| #endif | |||
| #endif // DGL_IMAGE_WIDGETS_HPP_INCLUDED | |||
| @@ -56,7 +56,6 @@ endif | |||
| OBJS_opengl = $(OBJS_common) \ | |||
| ../build/dgl/OpenGL.cpp.opengl.o \ | |||
| ../build/dgl/ImageWidgets.cpp.o \ | |||
| ../build/dgl/NanoVG.cpp.opengl.o | |||
| ifeq ($(MACOS),true) | |||
| @@ -282,12 +282,9 @@ private: | |||
| typedef ImageBaseAboutWindow<OpenGLImage> OpenGLImageAboutWindow; | |||
| typedef ImageBaseButton<OpenGLImage> OpenGLImageButton; | |||
| DISTRHO_DEPRECATED_BY("OpenGLImageAboutWindow") | |||
| typedef OpenGLImageAboutWindow ImageAboutWindow; | |||
| DISTRHO_DEPRECATED_BY("OpenGLImageButton") | |||
| typedef OpenGLImageButton ImageButton; | |||
| typedef ImageBaseKnob<OpenGLImage> OpenGLImageKnob; | |||
| typedef ImageBaseSlider<OpenGLImage> OpenGLImageSlider; | |||
| typedef ImageBaseSwitch<OpenGLImage> OpenGLImageSwitch; | |||
| // ----------------------------------------------------------------------- | |||
| @@ -180,4 +180,857 @@ bool ImageBaseButton<ImageType>::onMotion(const MotionEvent& ev) | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #if 0 | |||
| ImageKnob::ImageKnob(Widget* const parentWidget, const Image& image, Orientation orientation) noexcept | |||
| : SubWidget(parentWidget), | |||
| fImage(image), | |||
| fMinimum(0.0f), | |||
| fMaximum(1.0f), | |||
| fStep(0.0f), | |||
| fValue(0.5f), | |||
| fValueDef(fValue), | |||
| fValueTmp(fValue), | |||
| fUsingDefault(false), | |||
| fUsingLog(false), | |||
| fOrientation(orientation), | |||
| fRotationAngle(0), | |||
| fDragging(false), | |||
| fLastX(0), | |||
| fLastY(0), | |||
| fCallback(nullptr), | |||
| fIsImgVertical(image.getHeight() > image.getWidth()), | |||
| fImgLayerWidth(fIsImgVertical ? image.getWidth() : image.getHeight()), | |||
| fImgLayerHeight(fImgLayerWidth), | |||
| fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerHeight : image.getWidth()/fImgLayerWidth), | |||
| fIsReady(false), | |||
| fTextureId(0) | |||
| { | |||
| glGenTextures(1, &fTextureId); | |||
| setSize(fImgLayerWidth, fImgLayerHeight); | |||
| } | |||
| ImageKnob::ImageKnob(const ImageKnob& imageKnob) | |||
| : SubWidget(imageKnob.getParentWidget()), | |||
| fImage(imageKnob.fImage), | |||
| fMinimum(imageKnob.fMinimum), | |||
| fMaximum(imageKnob.fMaximum), | |||
| fStep(imageKnob.fStep), | |||
| fValue(imageKnob.fValue), | |||
| fValueDef(imageKnob.fValueDef), | |||
| fValueTmp(fValue), | |||
| fUsingDefault(imageKnob.fUsingDefault), | |||
| fUsingLog(imageKnob.fUsingLog), | |||
| fOrientation(imageKnob.fOrientation), | |||
| fRotationAngle(imageKnob.fRotationAngle), | |||
| fDragging(false), | |||
| fLastX(0), | |||
| fLastY(0), | |||
| fCallback(imageKnob.fCallback), | |||
| fIsImgVertical(imageKnob.fIsImgVertical), | |||
| fImgLayerWidth(imageKnob.fImgLayerWidth), | |||
| fImgLayerHeight(imageKnob.fImgLayerHeight), | |||
| fImgLayerCount(imageKnob.fImgLayerCount), | |||
| fIsReady(false), | |||
| fTextureId(0) | |||
| { | |||
| glGenTextures(1, &fTextureId); | |||
| setSize(fImgLayerWidth, fImgLayerHeight); | |||
| } | |||
| ImageKnob& ImageKnob::operator=(const ImageKnob& imageKnob) | |||
| { | |||
| fImage = imageKnob.fImage; | |||
| fMinimum = imageKnob.fMinimum; | |||
| fMaximum = imageKnob.fMaximum; | |||
| fStep = imageKnob.fStep; | |||
| fValue = imageKnob.fValue; | |||
| fValueDef = imageKnob.fValueDef; | |||
| fValueTmp = fValue; | |||
| fUsingDefault = imageKnob.fUsingDefault; | |||
| fUsingLog = imageKnob.fUsingLog; | |||
| fOrientation = imageKnob.fOrientation; | |||
| fRotationAngle = imageKnob.fRotationAngle; | |||
| fDragging = false; | |||
| fLastX = 0; | |||
| fLastY = 0; | |||
| fCallback = imageKnob.fCallback; | |||
| fIsImgVertical = imageKnob.fIsImgVertical; | |||
| fImgLayerWidth = imageKnob.fImgLayerWidth; | |||
| fImgLayerHeight = imageKnob.fImgLayerHeight; | |||
| fImgLayerCount = imageKnob.fImgLayerCount; | |||
| fIsReady = false; | |||
| if (fTextureId != 0) | |||
| { | |||
| glDeleteTextures(1, &fTextureId); | |||
| fTextureId = 0; | |||
| } | |||
| glGenTextures(1, &fTextureId); | |||
| setSize(fImgLayerWidth, fImgLayerHeight); | |||
| return *this; | |||
| } | |||
| ImageKnob::~ImageKnob() | |||
| { | |||
| if (fTextureId != 0) | |||
| { | |||
| glDeleteTextures(1, &fTextureId); | |||
| fTextureId = 0; | |||
| } | |||
| } | |||
| float ImageKnob::getValue() const noexcept | |||
| { | |||
| return fValue; | |||
| } | |||
| // NOTE: value is assumed to be scaled if using log | |||
| void ImageKnob::setDefault(float value) noexcept | |||
| { | |||
| fValueDef = value; | |||
| fUsingDefault = true; | |||
| } | |||
| void ImageKnob::setRange(float min, float max) noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(max > min,); | |||
| if (fValue < min) | |||
| { | |||
| fValue = min; | |||
| repaint(); | |||
| if (fCallback != nullptr) | |||
| { | |||
| try { | |||
| fCallback->imageKnobValueChanged(this, fValue); | |||
| } DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange < min"); | |||
| } | |||
| } | |||
| else if (fValue > max) | |||
| { | |||
| fValue = max; | |||
| repaint(); | |||
| if (fCallback != nullptr) | |||
| { | |||
| try { | |||
| fCallback->imageKnobValueChanged(this, fValue); | |||
| } DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange > max"); | |||
| } | |||
| } | |||
| fMinimum = min; | |||
| fMaximum = max; | |||
| } | |||
| void ImageKnob::setStep(float step) noexcept | |||
| { | |||
| fStep = step; | |||
| } | |||
| // NOTE: value is assumed to be scaled if using log | |||
| void ImageKnob::setValue(float value, bool sendCallback) noexcept | |||
| { | |||
| if (d_isEqual(fValue, value)) | |||
| return; | |||
| fValue = value; | |||
| if (d_isZero(fStep)) | |||
| fValueTmp = value; | |||
| if (fRotationAngle == 0) | |||
| fIsReady = false; | |||
| repaint(); | |||
| if (sendCallback && fCallback != nullptr) | |||
| { | |||
| try { | |||
| fCallback->imageKnobValueChanged(this, fValue); | |||
| } DISTRHO_SAFE_EXCEPTION("ImageKnob::setValue"); | |||
| } | |||
| } | |||
| void ImageKnob::setUsingLogScale(bool yesNo) noexcept | |||
| { | |||
| fUsingLog = yesNo; | |||
| } | |||
| void ImageKnob::setCallback(Callback* callback) noexcept | |||
| { | |||
| fCallback = callback; | |||
| } | |||
| void ImageKnob::setOrientation(Orientation orientation) noexcept | |||
| { | |||
| if (fOrientation == orientation) | |||
| return; | |||
| fOrientation = orientation; | |||
| } | |||
| void ImageKnob::setRotationAngle(int angle) | |||
| { | |||
| if (fRotationAngle == angle) | |||
| return; | |||
| fRotationAngle = angle; | |||
| fIsReady = false; | |||
| } | |||
| void ImageKnob::setImageLayerCount(uint count) noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(count > 1,); | |||
| fImgLayerCount = count; | |||
| if (fIsImgVertical) | |||
| fImgLayerHeight = fImage.getHeight()/count; | |||
| else | |||
| fImgLayerWidth = fImage.getWidth()/count; | |||
| setSize(fImgLayerWidth, fImgLayerHeight); | |||
| } | |||
| void ImageKnob::onDisplay() | |||
| { | |||
| const float normValue = ((fUsingLog ? _invlogscale(fValue) : fValue) - fMinimum) / (fMaximum - fMinimum); | |||
| glEnable(GL_TEXTURE_2D); | |||
| glBindTexture(GL_TEXTURE_2D, fTextureId); | |||
| if (! fIsReady) | |||
| { | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); | |||
| static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; | |||
| glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); | |||
| glPixelStorei(GL_PACK_ALIGNMENT, 1); | |||
| glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |||
| uint imageDataOffset = 0; | |||
| if (fRotationAngle == 0) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(fImgLayerCount > 0,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,); | |||
| const uint& v1(fIsImgVertical ? fImgLayerWidth : fImgLayerHeight); | |||
| const uint& v2(fIsImgVertical ? fImgLayerHeight : fImgLayerWidth); | |||
| const uint layerDataSize = v1 * v2 * ((fImage.getFormat() == kImageFormatBGRA || | |||
| fImage.getFormat() == kImageFormatRGBA) ? 4 : 3); | |||
| /* */ imageDataOffset = layerDataSize * uint(normValue * float(fImgLayerCount-1)); | |||
| } | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, | |||
| static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0, | |||
| asOpenGLImageFormat(fImage.getFormat()), GL_UNSIGNED_BYTE, fImage.getRawData() + imageDataOffset); | |||
| fIsReady = true; | |||
| } | |||
| const int w = static_cast<int>(getWidth()); | |||
| const int h = static_cast<int>(getHeight()); | |||
| if (fRotationAngle != 0) | |||
| { | |||
| glPushMatrix(); | |||
| const int w2 = w/2; | |||
| const int h2 = h/2; | |||
| glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f); | |||
| glRotatef(normValue*static_cast<float>(fRotationAngle), 0.0f, 0.0f, 1.0f); | |||
| Rectangle<int>(-w2, -h2, w, h).draw(); | |||
| glPopMatrix(); | |||
| } | |||
| else | |||
| { | |||
| Rectangle<int>(0, 0, w, h).draw(); | |||
| } | |||
| glBindTexture(GL_TEXTURE_2D, 0); | |||
| glDisable(GL_TEXTURE_2D); | |||
| } | |||
| bool ImageKnob::onMouse(const MouseEvent& ev) | |||
| { | |||
| if (ev.button != 1) | |||
| return false; | |||
| if (ev.press) | |||
| { | |||
| if (! contains(ev.pos)) | |||
| return false; | |||
| if ((ev.mod & kModifierShift) != 0 && fUsingDefault) | |||
| { | |||
| setValue(fValueDef, true); | |||
| fValueTmp = fValue; | |||
| return true; | |||
| } | |||
| fDragging = true; | |||
| fLastX = ev.pos.getX(); | |||
| fLastY = ev.pos.getY(); | |||
| if (fCallback != nullptr) | |||
| fCallback->imageKnobDragStarted(this); | |||
| return true; | |||
| } | |||
| else if (fDragging) | |||
| { | |||
| if (fCallback != nullptr) | |||
| fCallback->imageKnobDragFinished(this); | |||
| fDragging = false; | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| bool ImageKnob::onMotion(const MotionEvent& ev) | |||
| { | |||
| if (! fDragging) | |||
| return false; | |||
| bool doVal = false; | |||
| float d, value = 0.0f; | |||
| if (fOrientation == ImageKnob::Horizontal) | |||
| { | |||
| if (const int movX = ev.pos.getX() - fLastX) | |||
| { | |||
| d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; | |||
| value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movX)); | |||
| doVal = true; | |||
| } | |||
| } | |||
| else if (fOrientation == ImageKnob::Vertical) | |||
| { | |||
| if (const int movY = fLastY - ev.pos.getY()) | |||
| { | |||
| d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; | |||
| value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movY)); | |||
| doVal = true; | |||
| } | |||
| } | |||
| if (! doVal) | |||
| return false; | |||
| if (fUsingLog) | |||
| value = _logscale(value); | |||
| if (value < fMinimum) | |||
| { | |||
| fValueTmp = value = fMinimum; | |||
| } | |||
| else if (value > fMaximum) | |||
| { | |||
| fValueTmp = value = fMaximum; | |||
| } | |||
| else if (d_isNotZero(fStep)) | |||
| { | |||
| fValueTmp = value; | |||
| const float rest = std::fmod(value, fStep); | |||
| value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); | |||
| } | |||
| setValue(value, true); | |||
| fLastX = ev.pos.getX(); | |||
| fLastY = ev.pos.getY(); | |||
| return true; | |||
| } | |||
| bool ImageKnob::onScroll(const ScrollEvent& ev) | |||
| { | |||
| if (! contains(ev.pos)) | |||
| return false; | |||
| const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; | |||
| float value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * 10.f * ev.delta.getY()); | |||
| if (fUsingLog) | |||
| value = _logscale(value); | |||
| if (value < fMinimum) | |||
| { | |||
| fValueTmp = value = fMinimum; | |||
| } | |||
| else if (value > fMaximum) | |||
| { | |||
| fValueTmp = value = fMaximum; | |||
| } | |||
| else if (d_isNotZero(fStep)) | |||
| { | |||
| fValueTmp = value; | |||
| const float rest = std::fmod(value, fStep); | |||
| value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); | |||
| } | |||
| setValue(value, true); | |||
| return true; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| float ImageKnob::_logscale(float value) const | |||
| { | |||
| const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum); | |||
| const float a = fMaximum/std::exp(fMaximum*b); | |||
| return a * std::exp(b*value); | |||
| } | |||
| float ImageKnob::_invlogscale(float value) const | |||
| { | |||
| const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum); | |||
| const float a = fMaximum/std::exp(fMaximum*b); | |||
| return std::log(value/a)/b; | |||
| } | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #if 0 | |||
| ImageSlider::ImageSlider(Widget* const parentWidget, const Image& image) noexcept | |||
| : SubWidget(parentWidget), | |||
| fImage(image), | |||
| fMinimum(0.0f), | |||
| fMaximum(1.0f), | |||
| fStep(0.0f), | |||
| fValue(0.5f), | |||
| fValueDef(fValue), | |||
| fValueTmp(fValue), | |||
| fUsingDefault(false), | |||
| fDragging(false), | |||
| fInverted(false), | |||
| fValueIsSet(false), | |||
| fStartedX(0), | |||
| fStartedY(0), | |||
| fCallback(nullptr), | |||
| fStartPos(), | |||
| fEndPos(), | |||
| fSliderArea() | |||
| { | |||
| setNeedsFullViewportDrawing(); | |||
| } | |||
| float ImageSlider::getValue() const noexcept | |||
| { | |||
| return fValue; | |||
| } | |||
| void ImageSlider::setValue(float value, bool sendCallback) noexcept | |||
| { | |||
| if (! fValueIsSet) | |||
| fValueIsSet = true; | |||
| if (d_isEqual(fValue, value)) | |||
| return; | |||
| fValue = value; | |||
| if (d_isZero(fStep)) | |||
| fValueTmp = value; | |||
| repaint(); | |||
| if (sendCallback && fCallback != nullptr) | |||
| { | |||
| try { | |||
| fCallback->imageSliderValueChanged(this, fValue); | |||
| } DISTRHO_SAFE_EXCEPTION("ImageSlider::setValue"); | |||
| } | |||
| } | |||
| void ImageSlider::setStartPos(const Point<int>& startPos) noexcept | |||
| { | |||
| fStartPos = startPos; | |||
| _recheckArea(); | |||
| } | |||
| void ImageSlider::setStartPos(int x, int y) noexcept | |||
| { | |||
| setStartPos(Point<int>(x, y)); | |||
| } | |||
| void ImageSlider::setEndPos(const Point<int>& endPos) noexcept | |||
| { | |||
| fEndPos = endPos; | |||
| _recheckArea(); | |||
| } | |||
| void ImageSlider::setEndPos(int x, int y) noexcept | |||
| { | |||
| setEndPos(Point<int>(x, y)); | |||
| } | |||
| void ImageSlider::setInverted(bool inverted) noexcept | |||
| { | |||
| if (fInverted == inverted) | |||
| return; | |||
| fInverted = inverted; | |||
| repaint(); | |||
| } | |||
| void ImageSlider::setDefault(float value) noexcept | |||
| { | |||
| fValueDef = value; | |||
| fUsingDefault = true; | |||
| } | |||
| void ImageSlider::setRange(float min, float max) noexcept | |||
| { | |||
| fMinimum = min; | |||
| fMaximum = max; | |||
| if (fValue < min) | |||
| { | |||
| fValue = min; | |||
| repaint(); | |||
| if (fCallback != nullptr && fValueIsSet) | |||
| { | |||
| try { | |||
| fCallback->imageSliderValueChanged(this, fValue); | |||
| } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange < min"); | |||
| } | |||
| } | |||
| else if (fValue > max) | |||
| { | |||
| fValue = max; | |||
| repaint(); | |||
| if (fCallback != nullptr && fValueIsSet) | |||
| { | |||
| try { | |||
| fCallback->imageSliderValueChanged(this, fValue); | |||
| } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange > max"); | |||
| } | |||
| } | |||
| } | |||
| void ImageSlider::setStep(float step) noexcept | |||
| { | |||
| fStep = step; | |||
| } | |||
| void ImageSlider::setCallback(Callback* callback) noexcept | |||
| { | |||
| fCallback = callback; | |||
| } | |||
| void ImageSlider::onDisplay() | |||
| { | |||
| #if 0 // DEBUG, paints slider area | |||
| glColor3f(0.4f, 0.5f, 0.1f); | |||
| glRecti(fSliderArea.getX(), fSliderArea.getY(), fSliderArea.getX()+fSliderArea.getWidth(), fSliderArea.getY()+fSliderArea.getHeight()); | |||
| glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | |||
| #endif | |||
| const float normValue = (fValue - fMinimum) / (fMaximum - fMinimum); | |||
| int x, y; | |||
| if (fStartPos.getY() == fEndPos.getY()) | |||
| { | |||
| // horizontal | |||
| if (fInverted) | |||
| x = fEndPos.getX() - static_cast<int>(normValue*static_cast<float>(fEndPos.getX()-fStartPos.getX())); | |||
| else | |||
| x = fStartPos.getX() + static_cast<int>(normValue*static_cast<float>(fEndPos.getX()-fStartPos.getX())); | |||
| y = fStartPos.getY(); | |||
| } | |||
| else | |||
| { | |||
| // vertical | |||
| x = fStartPos.getX(); | |||
| if (fInverted) | |||
| y = fEndPos.getY() - static_cast<int>(normValue*static_cast<float>(fEndPos.getY()-fStartPos.getY())); | |||
| else | |||
| y = fStartPos.getY() + static_cast<int>(normValue*static_cast<float>(fEndPos.getY()-fStartPos.getY())); | |||
| } | |||
| fImage.drawAt(x, y); | |||
| } | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #if 0 | |||
| bool ImageSlider::onMouse(const MouseEvent& ev) | |||
| { | |||
| if (ev.button != 1) | |||
| return false; | |||
| if (ev.press) | |||
| { | |||
| if (! fSliderArea.contains(ev.pos)) | |||
| return false; | |||
| if ((ev.mod & kModifierShift) != 0 && fUsingDefault) | |||
| { | |||
| setValue(fValueDef, true); | |||
| fValueTmp = fValue; | |||
| return true; | |||
| } | |||
| float vper; | |||
| const int x = ev.pos.getX(); | |||
| const int y = ev.pos.getY(); | |||
| if (fStartPos.getY() == fEndPos.getY()) | |||
| { | |||
| // horizontal | |||
| vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth()); | |||
| } | |||
| else | |||
| { | |||
| // vertical | |||
| vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight()); | |||
| } | |||
| float value; | |||
| if (fInverted) | |||
| value = fMaximum - vper * (fMaximum - fMinimum); | |||
| else | |||
| value = fMinimum + vper * (fMaximum - fMinimum); | |||
| if (value < fMinimum) | |||
| { | |||
| fValueTmp = value = fMinimum; | |||
| } | |||
| else if (value > fMaximum) | |||
| { | |||
| fValueTmp = value = fMaximum; | |||
| } | |||
| else if (d_isNotZero(fStep)) | |||
| { | |||
| fValueTmp = value; | |||
| const float rest = std::fmod(value, fStep); | |||
| value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); | |||
| } | |||
| fDragging = true; | |||
| fStartedX = x; | |||
| fStartedY = y; | |||
| if (fCallback != nullptr) | |||
| fCallback->imageSliderDragStarted(this); | |||
| setValue(value, true); | |||
| return true; | |||
| } | |||
| else if (fDragging) | |||
| { | |||
| if (fCallback != nullptr) | |||
| fCallback->imageSliderDragFinished(this); | |||
| fDragging = false; | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #if 0 | |||
| bool ImageSlider::onMotion(const MotionEvent& ev) | |||
| { | |||
| if (! fDragging) | |||
| return false; | |||
| const bool horizontal = fStartPos.getY() == fEndPos.getY(); | |||
| const int x = ev.pos.getX(); | |||
| const int y = ev.pos.getY(); | |||
| if ((horizontal && fSliderArea.containsX(x)) || (fSliderArea.containsY(y) && ! horizontal)) | |||
| { | |||
| float vper; | |||
| if (horizontal) | |||
| { | |||
| // horizontal | |||
| vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth()); | |||
| } | |||
| else | |||
| { | |||
| // vertical | |||
| vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight()); | |||
| } | |||
| float value; | |||
| if (fInverted) | |||
| value = fMaximum - vper * (fMaximum - fMinimum); | |||
| else | |||
| value = fMinimum + vper * (fMaximum - fMinimum); | |||
| if (value < fMinimum) | |||
| { | |||
| fValueTmp = value = fMinimum; | |||
| } | |||
| else if (value > fMaximum) | |||
| { | |||
| fValueTmp = value = fMaximum; | |||
| } | |||
| else if (d_isNotZero(fStep)) | |||
| { | |||
| fValueTmp = value; | |||
| const float rest = std::fmod(value, fStep); | |||
| value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); | |||
| } | |||
| setValue(value, true); | |||
| } | |||
| else if (horizontal) | |||
| { | |||
| if (x < fSliderArea.getX()) | |||
| setValue(fInverted ? fMaximum : fMinimum, true); | |||
| else | |||
| setValue(fInverted ? fMinimum : fMaximum, true); | |||
| } | |||
| else | |||
| { | |||
| if (y < fSliderArea.getY()) | |||
| setValue(fInverted ? fMaximum : fMinimum, true); | |||
| else | |||
| setValue(fInverted ? fMinimum : fMaximum, true); | |||
| } | |||
| return true; | |||
| } | |||
| void ImageSlider::_recheckArea() noexcept | |||
| { | |||
| if (fStartPos.getY() == fEndPos.getY()) | |||
| { | |||
| // horizontal | |||
| fSliderArea = Rectangle<double>(fStartPos.getX(), | |||
| fStartPos.getY(), | |||
| fEndPos.getX() + static_cast<int>(fImage.getWidth()) - fStartPos.getX(), | |||
| static_cast<int>(fImage.getHeight())); | |||
| } | |||
| else | |||
| { | |||
| // vertical | |||
| fSliderArea = Rectangle<double>(fStartPos.getX(), | |||
| fStartPos.getY(), | |||
| static_cast<int>(fImage.getWidth()), | |||
| fEndPos.getY() + static_cast<int>(fImage.getHeight()) - fStartPos.getY()); | |||
| } | |||
| } | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #if 0 | |||
| ImageSwitch::ImageSwitch(Widget* parentWidget, const Image& imageNormal, const Image& imageDown) noexcept | |||
| : SubWidget(parentWidget), | |||
| fImageNormal(imageNormal), | |||
| fImageDown(imageDown), | |||
| fIsDown(false), | |||
| fCallback(nullptr) | |||
| { | |||
| DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); | |||
| setSize(fImageNormal.getSize()); | |||
| } | |||
| ImageSwitch::ImageSwitch(const ImageSwitch& imageSwitch) noexcept | |||
| : SubWidget(imageSwitch.getParentWidget()), | |||
| fImageNormal(imageSwitch.fImageNormal), | |||
| fImageDown(imageSwitch.fImageDown), | |||
| fIsDown(imageSwitch.fIsDown), | |||
| fCallback(imageSwitch.fCallback) | |||
| { | |||
| DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); | |||
| setSize(fImageNormal.getSize()); | |||
| } | |||
| ImageSwitch& ImageSwitch::operator=(const ImageSwitch& imageSwitch) noexcept | |||
| { | |||
| fImageNormal = imageSwitch.fImageNormal; | |||
| fImageDown = imageSwitch.fImageDown; | |||
| fIsDown = imageSwitch.fIsDown; | |||
| fCallback = imageSwitch.fCallback; | |||
| DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); | |||
| setSize(fImageNormal.getSize()); | |||
| return *this; | |||
| } | |||
| bool ImageSwitch::isDown() const noexcept | |||
| { | |||
| return fIsDown; | |||
| } | |||
| void ImageSwitch::setDown(bool down) noexcept | |||
| { | |||
| if (fIsDown == down) | |||
| return; | |||
| fIsDown = down; | |||
| repaint(); | |||
| } | |||
| void ImageSwitch::setCallback(Callback* callback) noexcept | |||
| { | |||
| fCallback = callback; | |||
| } | |||
| void ImageSwitch::onDisplay() | |||
| { | |||
| if (fIsDown) | |||
| fImageDown.draw(); | |||
| else | |||
| fImageNormal.draw(); | |||
| } | |||
| bool ImageSwitch::onMouse(const MouseEvent& ev) | |||
| { | |||
| if (ev.press && contains(ev.pos)) | |||
| { | |||
| fIsDown = !fIsDown; | |||
| repaint(); | |||
| if (fCallback != nullptr) | |||
| fCallback->imageSwitchClicked(this, fIsDown); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -1,876 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2019 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 "../ImageWidgets.hpp" | |||
| #include "WidgetPrivateData.hpp" | |||
| // TODO make this code more generic and move GL specific bits to OpenGL.cpp | |||
| #include "../OpenGL.hpp" | |||
| // TODO switch to use templated image type after merging widget-related PRs | |||
| #if defined(__GNUC__) && (__GNUC__ >= 6) | |||
| # pragma GCC diagnostic push | |||
| # pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |||
| #endif | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| ImageKnob::ImageKnob(Widget* const parentWidget, const Image& image, Orientation orientation) noexcept | |||
| : SubWidget(parentWidget), | |||
| fImage(image), | |||
| fMinimum(0.0f), | |||
| fMaximum(1.0f), | |||
| fStep(0.0f), | |||
| fValue(0.5f), | |||
| fValueDef(fValue), | |||
| fValueTmp(fValue), | |||
| fUsingDefault(false), | |||
| fUsingLog(false), | |||
| fOrientation(orientation), | |||
| fRotationAngle(0), | |||
| fDragging(false), | |||
| fLastX(0), | |||
| fLastY(0), | |||
| fCallback(nullptr), | |||
| fIsImgVertical(image.getHeight() > image.getWidth()), | |||
| fImgLayerWidth(fIsImgVertical ? image.getWidth() : image.getHeight()), | |||
| fImgLayerHeight(fImgLayerWidth), | |||
| fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerHeight : image.getWidth()/fImgLayerWidth), | |||
| fIsReady(false), | |||
| fTextureId(0) | |||
| { | |||
| glGenTextures(1, &fTextureId); | |||
| setSize(fImgLayerWidth, fImgLayerHeight); | |||
| } | |||
| ImageKnob::ImageKnob(const ImageKnob& imageKnob) | |||
| : SubWidget(imageKnob.getParentWidget()), | |||
| fImage(imageKnob.fImage), | |||
| fMinimum(imageKnob.fMinimum), | |||
| fMaximum(imageKnob.fMaximum), | |||
| fStep(imageKnob.fStep), | |||
| fValue(imageKnob.fValue), | |||
| fValueDef(imageKnob.fValueDef), | |||
| fValueTmp(fValue), | |||
| fUsingDefault(imageKnob.fUsingDefault), | |||
| fUsingLog(imageKnob.fUsingLog), | |||
| fOrientation(imageKnob.fOrientation), | |||
| fRotationAngle(imageKnob.fRotationAngle), | |||
| fDragging(false), | |||
| fLastX(0), | |||
| fLastY(0), | |||
| fCallback(imageKnob.fCallback), | |||
| fIsImgVertical(imageKnob.fIsImgVertical), | |||
| fImgLayerWidth(imageKnob.fImgLayerWidth), | |||
| fImgLayerHeight(imageKnob.fImgLayerHeight), | |||
| fImgLayerCount(imageKnob.fImgLayerCount), | |||
| fIsReady(false), | |||
| fTextureId(0) | |||
| { | |||
| glGenTextures(1, &fTextureId); | |||
| setSize(fImgLayerWidth, fImgLayerHeight); | |||
| } | |||
| ImageKnob& ImageKnob::operator=(const ImageKnob& imageKnob) | |||
| { | |||
| fImage = imageKnob.fImage; | |||
| fMinimum = imageKnob.fMinimum; | |||
| fMaximum = imageKnob.fMaximum; | |||
| fStep = imageKnob.fStep; | |||
| fValue = imageKnob.fValue; | |||
| fValueDef = imageKnob.fValueDef; | |||
| fValueTmp = fValue; | |||
| fUsingDefault = imageKnob.fUsingDefault; | |||
| fUsingLog = imageKnob.fUsingLog; | |||
| fOrientation = imageKnob.fOrientation; | |||
| fRotationAngle = imageKnob.fRotationAngle; | |||
| fDragging = false; | |||
| fLastX = 0; | |||
| fLastY = 0; | |||
| fCallback = imageKnob.fCallback; | |||
| fIsImgVertical = imageKnob.fIsImgVertical; | |||
| fImgLayerWidth = imageKnob.fImgLayerWidth; | |||
| fImgLayerHeight = imageKnob.fImgLayerHeight; | |||
| fImgLayerCount = imageKnob.fImgLayerCount; | |||
| fIsReady = false; | |||
| if (fTextureId != 0) | |||
| { | |||
| glDeleteTextures(1, &fTextureId); | |||
| fTextureId = 0; | |||
| } | |||
| glGenTextures(1, &fTextureId); | |||
| setSize(fImgLayerWidth, fImgLayerHeight); | |||
| return *this; | |||
| } | |||
| ImageKnob::~ImageKnob() | |||
| { | |||
| if (fTextureId != 0) | |||
| { | |||
| glDeleteTextures(1, &fTextureId); | |||
| fTextureId = 0; | |||
| } | |||
| } | |||
| float ImageKnob::getValue() const noexcept | |||
| { | |||
| return fValue; | |||
| } | |||
| // NOTE: value is assumed to be scaled if using log | |||
| void ImageKnob::setDefault(float value) noexcept | |||
| { | |||
| fValueDef = value; | |||
| fUsingDefault = true; | |||
| } | |||
| void ImageKnob::setRange(float min, float max) noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(max > min,); | |||
| if (fValue < min) | |||
| { | |||
| fValue = min; | |||
| repaint(); | |||
| if (fCallback != nullptr) | |||
| { | |||
| try { | |||
| fCallback->imageKnobValueChanged(this, fValue); | |||
| } DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange < min"); | |||
| } | |||
| } | |||
| else if (fValue > max) | |||
| { | |||
| fValue = max; | |||
| repaint(); | |||
| if (fCallback != nullptr) | |||
| { | |||
| try { | |||
| fCallback->imageKnobValueChanged(this, fValue); | |||
| } DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange > max"); | |||
| } | |||
| } | |||
| fMinimum = min; | |||
| fMaximum = max; | |||
| } | |||
| void ImageKnob::setStep(float step) noexcept | |||
| { | |||
| fStep = step; | |||
| } | |||
| // NOTE: value is assumed to be scaled if using log | |||
| void ImageKnob::setValue(float value, bool sendCallback) noexcept | |||
| { | |||
| if (d_isEqual(fValue, value)) | |||
| return; | |||
| fValue = value; | |||
| if (d_isZero(fStep)) | |||
| fValueTmp = value; | |||
| if (fRotationAngle == 0) | |||
| fIsReady = false; | |||
| repaint(); | |||
| if (sendCallback && fCallback != nullptr) | |||
| { | |||
| try { | |||
| fCallback->imageKnobValueChanged(this, fValue); | |||
| } DISTRHO_SAFE_EXCEPTION("ImageKnob::setValue"); | |||
| } | |||
| } | |||
| void ImageKnob::setUsingLogScale(bool yesNo) noexcept | |||
| { | |||
| fUsingLog = yesNo; | |||
| } | |||
| void ImageKnob::setCallback(Callback* callback) noexcept | |||
| { | |||
| fCallback = callback; | |||
| } | |||
| void ImageKnob::setOrientation(Orientation orientation) noexcept | |||
| { | |||
| if (fOrientation == orientation) | |||
| return; | |||
| fOrientation = orientation; | |||
| } | |||
| void ImageKnob::setRotationAngle(int angle) | |||
| { | |||
| if (fRotationAngle == angle) | |||
| return; | |||
| fRotationAngle = angle; | |||
| fIsReady = false; | |||
| } | |||
| void ImageKnob::setImageLayerCount(uint count) noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(count > 1,); | |||
| fImgLayerCount = count; | |||
| if (fIsImgVertical) | |||
| fImgLayerHeight = fImage.getHeight()/count; | |||
| else | |||
| fImgLayerWidth = fImage.getWidth()/count; | |||
| setSize(fImgLayerWidth, fImgLayerHeight); | |||
| } | |||
| void ImageKnob::onDisplay() | |||
| { | |||
| const float normValue = ((fUsingLog ? _invlogscale(fValue) : fValue) - fMinimum) / (fMaximum - fMinimum); | |||
| glEnable(GL_TEXTURE_2D); | |||
| glBindTexture(GL_TEXTURE_2D, fTextureId); | |||
| if (! fIsReady) | |||
| { | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); | |||
| static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; | |||
| glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); | |||
| glPixelStorei(GL_PACK_ALIGNMENT, 1); | |||
| glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |||
| uint imageDataOffset = 0; | |||
| if (fRotationAngle == 0) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(fImgLayerCount > 0,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,); | |||
| const uint& v1(fIsImgVertical ? fImgLayerWidth : fImgLayerHeight); | |||
| const uint& v2(fIsImgVertical ? fImgLayerHeight : fImgLayerWidth); | |||
| const uint layerDataSize = v1 * v2 * ((fImage.getFormat() == kImageFormatBGRA || | |||
| fImage.getFormat() == kImageFormatRGBA) ? 4 : 3); | |||
| /* */ imageDataOffset = layerDataSize * uint(normValue * float(fImgLayerCount-1)); | |||
| } | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, | |||
| static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0, | |||
| asOpenGLImageFormat(fImage.getFormat()), GL_UNSIGNED_BYTE, fImage.getRawData() + imageDataOffset); | |||
| fIsReady = true; | |||
| } | |||
| const int w = static_cast<int>(getWidth()); | |||
| const int h = static_cast<int>(getHeight()); | |||
| if (fRotationAngle != 0) | |||
| { | |||
| glPushMatrix(); | |||
| const int w2 = w/2; | |||
| const int h2 = h/2; | |||
| glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f); | |||
| glRotatef(normValue*static_cast<float>(fRotationAngle), 0.0f, 0.0f, 1.0f); | |||
| Rectangle<int>(-w2, -h2, w, h).draw(); | |||
| glPopMatrix(); | |||
| } | |||
| else | |||
| { | |||
| Rectangle<int>(0, 0, w, h).draw(); | |||
| } | |||
| glBindTexture(GL_TEXTURE_2D, 0); | |||
| glDisable(GL_TEXTURE_2D); | |||
| } | |||
| bool ImageKnob::onMouse(const MouseEvent& ev) | |||
| { | |||
| if (ev.button != 1) | |||
| return false; | |||
| if (ev.press) | |||
| { | |||
| if (! contains(ev.pos)) | |||
| return false; | |||
| if ((ev.mod & kModifierShift) != 0 && fUsingDefault) | |||
| { | |||
| setValue(fValueDef, true); | |||
| fValueTmp = fValue; | |||
| return true; | |||
| } | |||
| fDragging = true; | |||
| fLastX = ev.pos.getX(); | |||
| fLastY = ev.pos.getY(); | |||
| if (fCallback != nullptr) | |||
| fCallback->imageKnobDragStarted(this); | |||
| return true; | |||
| } | |||
| else if (fDragging) | |||
| { | |||
| if (fCallback != nullptr) | |||
| fCallback->imageKnobDragFinished(this); | |||
| fDragging = false; | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| bool ImageKnob::onMotion(const MotionEvent& ev) | |||
| { | |||
| if (! fDragging) | |||
| return false; | |||
| bool doVal = false; | |||
| float d, value = 0.0f; | |||
| if (fOrientation == ImageKnob::Horizontal) | |||
| { | |||
| if (const int movX = ev.pos.getX() - fLastX) | |||
| { | |||
| d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; | |||
| value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movX)); | |||
| doVal = true; | |||
| } | |||
| } | |||
| else if (fOrientation == ImageKnob::Vertical) | |||
| { | |||
| if (const int movY = fLastY - ev.pos.getY()) | |||
| { | |||
| d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; | |||
| value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movY)); | |||
| doVal = true; | |||
| } | |||
| } | |||
| if (! doVal) | |||
| return false; | |||
| if (fUsingLog) | |||
| value = _logscale(value); | |||
| if (value < fMinimum) | |||
| { | |||
| fValueTmp = value = fMinimum; | |||
| } | |||
| else if (value > fMaximum) | |||
| { | |||
| fValueTmp = value = fMaximum; | |||
| } | |||
| else if (d_isNotZero(fStep)) | |||
| { | |||
| fValueTmp = value; | |||
| const float rest = std::fmod(value, fStep); | |||
| value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); | |||
| } | |||
| setValue(value, true); | |||
| fLastX = ev.pos.getX(); | |||
| fLastY = ev.pos.getY(); | |||
| return true; | |||
| } | |||
| bool ImageKnob::onScroll(const ScrollEvent& ev) | |||
| { | |||
| if (! contains(ev.pos)) | |||
| return false; | |||
| const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; | |||
| float value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * 10.f * ev.delta.getY()); | |||
| if (fUsingLog) | |||
| value = _logscale(value); | |||
| if (value < fMinimum) | |||
| { | |||
| fValueTmp = value = fMinimum; | |||
| } | |||
| else if (value > fMaximum) | |||
| { | |||
| fValueTmp = value = fMaximum; | |||
| } | |||
| else if (d_isNotZero(fStep)) | |||
| { | |||
| fValueTmp = value; | |||
| const float rest = std::fmod(value, fStep); | |||
| value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); | |||
| } | |||
| setValue(value, true); | |||
| return true; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| float ImageKnob::_logscale(float value) const | |||
| { | |||
| const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum); | |||
| const float a = fMaximum/std::exp(fMaximum*b); | |||
| return a * std::exp(b*value); | |||
| } | |||
| float ImageKnob::_invlogscale(float value) const | |||
| { | |||
| const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum); | |||
| const float a = fMaximum/std::exp(fMaximum*b); | |||
| return std::log(value/a)/b; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| ImageSlider::ImageSlider(Widget* const parentWidget, const Image& image) noexcept | |||
| : SubWidget(parentWidget), | |||
| fImage(image), | |||
| fMinimum(0.0f), | |||
| fMaximum(1.0f), | |||
| fStep(0.0f), | |||
| fValue(0.5f), | |||
| fValueDef(fValue), | |||
| fValueTmp(fValue), | |||
| fUsingDefault(false), | |||
| fDragging(false), | |||
| fInverted(false), | |||
| fValueIsSet(false), | |||
| fStartedX(0), | |||
| fStartedY(0), | |||
| fCallback(nullptr), | |||
| fStartPos(), | |||
| fEndPos(), | |||
| fSliderArea() | |||
| { | |||
| setNeedsFullViewportDrawing(); | |||
| } | |||
| float ImageSlider::getValue() const noexcept | |||
| { | |||
| return fValue; | |||
| } | |||
| void ImageSlider::setValue(float value, bool sendCallback) noexcept | |||
| { | |||
| if (! fValueIsSet) | |||
| fValueIsSet = true; | |||
| if (d_isEqual(fValue, value)) | |||
| return; | |||
| fValue = value; | |||
| if (d_isZero(fStep)) | |||
| fValueTmp = value; | |||
| repaint(); | |||
| if (sendCallback && fCallback != nullptr) | |||
| { | |||
| try { | |||
| fCallback->imageSliderValueChanged(this, fValue); | |||
| } DISTRHO_SAFE_EXCEPTION("ImageSlider::setValue"); | |||
| } | |||
| } | |||
| void ImageSlider::setStartPos(const Point<int>& startPos) noexcept | |||
| { | |||
| fStartPos = startPos; | |||
| _recheckArea(); | |||
| } | |||
| void ImageSlider::setStartPos(int x, int y) noexcept | |||
| { | |||
| setStartPos(Point<int>(x, y)); | |||
| } | |||
| void ImageSlider::setEndPos(const Point<int>& endPos) noexcept | |||
| { | |||
| fEndPos = endPos; | |||
| _recheckArea(); | |||
| } | |||
| void ImageSlider::setEndPos(int x, int y) noexcept | |||
| { | |||
| setEndPos(Point<int>(x, y)); | |||
| } | |||
| void ImageSlider::setInverted(bool inverted) noexcept | |||
| { | |||
| if (fInverted == inverted) | |||
| return; | |||
| fInverted = inverted; | |||
| repaint(); | |||
| } | |||
| void ImageSlider::setDefault(float value) noexcept | |||
| { | |||
| fValueDef = value; | |||
| fUsingDefault = true; | |||
| } | |||
| void ImageSlider::setRange(float min, float max) noexcept | |||
| { | |||
| fMinimum = min; | |||
| fMaximum = max; | |||
| if (fValue < min) | |||
| { | |||
| fValue = min; | |||
| repaint(); | |||
| if (fCallback != nullptr && fValueIsSet) | |||
| { | |||
| try { | |||
| fCallback->imageSliderValueChanged(this, fValue); | |||
| } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange < min"); | |||
| } | |||
| } | |||
| else if (fValue > max) | |||
| { | |||
| fValue = max; | |||
| repaint(); | |||
| if (fCallback != nullptr && fValueIsSet) | |||
| { | |||
| try { | |||
| fCallback->imageSliderValueChanged(this, fValue); | |||
| } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange > max"); | |||
| } | |||
| } | |||
| } | |||
| void ImageSlider::setStep(float step) noexcept | |||
| { | |||
| fStep = step; | |||
| } | |||
| void ImageSlider::setCallback(Callback* callback) noexcept | |||
| { | |||
| fCallback = callback; | |||
| } | |||
| void ImageSlider::onDisplay() | |||
| { | |||
| #if 0 // DEBUG, paints slider area | |||
| glColor3f(0.4f, 0.5f, 0.1f); | |||
| glRecti(fSliderArea.getX(), fSliderArea.getY(), fSliderArea.getX()+fSliderArea.getWidth(), fSliderArea.getY()+fSliderArea.getHeight()); | |||
| glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | |||
| #endif | |||
| const float normValue = (fValue - fMinimum) / (fMaximum - fMinimum); | |||
| int x, y; | |||
| if (fStartPos.getY() == fEndPos.getY()) | |||
| { | |||
| // horizontal | |||
| if (fInverted) | |||
| x = fEndPos.getX() - static_cast<int>(normValue*static_cast<float>(fEndPos.getX()-fStartPos.getX())); | |||
| else | |||
| x = fStartPos.getX() + static_cast<int>(normValue*static_cast<float>(fEndPos.getX()-fStartPos.getX())); | |||
| y = fStartPos.getY(); | |||
| } | |||
| else | |||
| { | |||
| // vertical | |||
| x = fStartPos.getX(); | |||
| if (fInverted) | |||
| y = fEndPos.getY() - static_cast<int>(normValue*static_cast<float>(fEndPos.getY()-fStartPos.getY())); | |||
| else | |||
| y = fStartPos.getY() + static_cast<int>(normValue*static_cast<float>(fEndPos.getY()-fStartPos.getY())); | |||
| } | |||
| fImage.drawAt(x, y); | |||
| } | |||
| bool ImageSlider::onMouse(const MouseEvent& ev) | |||
| { | |||
| if (ev.button != 1) | |||
| return false; | |||
| if (ev.press) | |||
| { | |||
| if (! fSliderArea.contains(ev.pos)) | |||
| return false; | |||
| if ((ev.mod & kModifierShift) != 0 && fUsingDefault) | |||
| { | |||
| setValue(fValueDef, true); | |||
| fValueTmp = fValue; | |||
| return true; | |||
| } | |||
| float vper; | |||
| const int x = ev.pos.getX(); | |||
| const int y = ev.pos.getY(); | |||
| if (fStartPos.getY() == fEndPos.getY()) | |||
| { | |||
| // horizontal | |||
| vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth()); | |||
| } | |||
| else | |||
| { | |||
| // vertical | |||
| vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight()); | |||
| } | |||
| float value; | |||
| if (fInverted) | |||
| value = fMaximum - vper * (fMaximum - fMinimum); | |||
| else | |||
| value = fMinimum + vper * (fMaximum - fMinimum); | |||
| if (value < fMinimum) | |||
| { | |||
| fValueTmp = value = fMinimum; | |||
| } | |||
| else if (value > fMaximum) | |||
| { | |||
| fValueTmp = value = fMaximum; | |||
| } | |||
| else if (d_isNotZero(fStep)) | |||
| { | |||
| fValueTmp = value; | |||
| const float rest = std::fmod(value, fStep); | |||
| value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); | |||
| } | |||
| fDragging = true; | |||
| fStartedX = x; | |||
| fStartedY = y; | |||
| if (fCallback != nullptr) | |||
| fCallback->imageSliderDragStarted(this); | |||
| setValue(value, true); | |||
| return true; | |||
| } | |||
| else if (fDragging) | |||
| { | |||
| if (fCallback != nullptr) | |||
| fCallback->imageSliderDragFinished(this); | |||
| fDragging = false; | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| bool ImageSlider::onMotion(const MotionEvent& ev) | |||
| { | |||
| if (! fDragging) | |||
| return false; | |||
| const bool horizontal = fStartPos.getY() == fEndPos.getY(); | |||
| const int x = ev.pos.getX(); | |||
| const int y = ev.pos.getY(); | |||
| if ((horizontal && fSliderArea.containsX(x)) || (fSliderArea.containsY(y) && ! horizontal)) | |||
| { | |||
| float vper; | |||
| if (horizontal) | |||
| { | |||
| // horizontal | |||
| vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth()); | |||
| } | |||
| else | |||
| { | |||
| // vertical | |||
| vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight()); | |||
| } | |||
| float value; | |||
| if (fInverted) | |||
| value = fMaximum - vper * (fMaximum - fMinimum); | |||
| else | |||
| value = fMinimum + vper * (fMaximum - fMinimum); | |||
| if (value < fMinimum) | |||
| { | |||
| fValueTmp = value = fMinimum; | |||
| } | |||
| else if (value > fMaximum) | |||
| { | |||
| fValueTmp = value = fMaximum; | |||
| } | |||
| else if (d_isNotZero(fStep)) | |||
| { | |||
| fValueTmp = value; | |||
| const float rest = std::fmod(value, fStep); | |||
| value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); | |||
| } | |||
| setValue(value, true); | |||
| } | |||
| else if (horizontal) | |||
| { | |||
| if (x < fSliderArea.getX()) | |||
| setValue(fInverted ? fMaximum : fMinimum, true); | |||
| else | |||
| setValue(fInverted ? fMinimum : fMaximum, true); | |||
| } | |||
| else | |||
| { | |||
| if (y < fSliderArea.getY()) | |||
| setValue(fInverted ? fMaximum : fMinimum, true); | |||
| else | |||
| setValue(fInverted ? fMinimum : fMaximum, true); | |||
| } | |||
| return true; | |||
| } | |||
| void ImageSlider::_recheckArea() noexcept | |||
| { | |||
| if (fStartPos.getY() == fEndPos.getY()) | |||
| { | |||
| // horizontal | |||
| fSliderArea = Rectangle<double>(fStartPos.getX(), | |||
| fStartPos.getY(), | |||
| fEndPos.getX() + static_cast<int>(fImage.getWidth()) - fStartPos.getX(), | |||
| static_cast<int>(fImage.getHeight())); | |||
| } | |||
| else | |||
| { | |||
| // vertical | |||
| fSliderArea = Rectangle<double>(fStartPos.getX(), | |||
| fStartPos.getY(), | |||
| static_cast<int>(fImage.getWidth()), | |||
| fEndPos.getY() + static_cast<int>(fImage.getHeight()) - fStartPos.getY()); | |||
| } | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| ImageSwitch::ImageSwitch(Widget* parentWidget, const Image& imageNormal, const Image& imageDown) noexcept | |||
| : SubWidget(parentWidget), | |||
| fImageNormal(imageNormal), | |||
| fImageDown(imageDown), | |||
| fIsDown(false), | |||
| fCallback(nullptr) | |||
| { | |||
| DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); | |||
| setSize(fImageNormal.getSize()); | |||
| } | |||
| ImageSwitch::ImageSwitch(const ImageSwitch& imageSwitch) noexcept | |||
| : SubWidget(imageSwitch.getParentWidget()), | |||
| fImageNormal(imageSwitch.fImageNormal), | |||
| fImageDown(imageSwitch.fImageDown), | |||
| fIsDown(imageSwitch.fIsDown), | |||
| fCallback(imageSwitch.fCallback) | |||
| { | |||
| DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); | |||
| setSize(fImageNormal.getSize()); | |||
| } | |||
| ImageSwitch& ImageSwitch::operator=(const ImageSwitch& imageSwitch) noexcept | |||
| { | |||
| fImageNormal = imageSwitch.fImageNormal; | |||
| fImageDown = imageSwitch.fImageDown; | |||
| fIsDown = imageSwitch.fIsDown; | |||
| fCallback = imageSwitch.fCallback; | |||
| DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); | |||
| setSize(fImageNormal.getSize()); | |||
| return *this; | |||
| } | |||
| bool ImageSwitch::isDown() const noexcept | |||
| { | |||
| return fIsDown; | |||
| } | |||
| void ImageSwitch::setDown(bool down) noexcept | |||
| { | |||
| if (fIsDown == down) | |||
| return; | |||
| fIsDown = down; | |||
| repaint(); | |||
| } | |||
| void ImageSwitch::setCallback(Callback* callback) noexcept | |||
| { | |||
| fCallback = callback; | |||
| } | |||
| void ImageSwitch::onDisplay() | |||
| { | |||
| if (fIsDown) | |||
| fImageDown.draw(); | |||
| else | |||
| fImageNormal.draw(); | |||
| } | |||
| bool ImageSwitch::onMouse(const MouseEvent& ev) | |||
| { | |||
| if (ev.press && contains(ev.pos)) | |||
| { | |||
| fIsDown = !fIsDown; | |||
| repaint(); | |||
| if (fCallback != nullptr) | |||
| fCallback->imageSwitchClicked(this, fIsDown); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #if defined(__GNUC__) && (__GNUC__ >= 6) | |||
| # pragma GCC diagnostic pop | |||
| #endif | |||