diff --git a/dgl/EventHandlers.hpp b/dgl/EventHandlers.hpp index e1844943..1e450b8d 100644 --- a/dgl/EventHandlers.hpp +++ b/dgl/EventHandlers.hpp @@ -54,8 +54,8 @@ public: void setCallback(Callback* callback) noexcept; - bool mouseEvent(const Widget::MouseEvent& ev); - bool motionEvent(const Widget::MotionEvent& ev); + bool mouseEvent(const Widget::MouseEvent& ev); + bool motionEvent(const Widget::MotionEvent& ev); protected: State getState() const noexcept; @@ -63,6 +63,9 @@ protected: virtual void stateChanged(State state, State oldState); + void setInternalCallback(Callback* callback) noexcept; + void triggerUserCallback(SubWidget* widget, int button); + private: struct PrivateData; PrivateData* const pData; @@ -72,6 +75,76 @@ private: // -------------------------------------------------------------------------------------------------------------------- +class KnobEventHandler +{ +public: + enum Orientation { + Horizontal, + Vertical + }; + + // NOTE hover not implemented yet + enum State { + kKnobStateDefault = 0x0, + kKnobStateHover = 0x1, + kKnobStateDragging = 0x2, + kKnobStateDraggingHover = kKnobStateDragging|kKnobStateHover + }; + + class Callback + { + public: + virtual ~Callback() {} + virtual void knobDragStarted(SubWidget* widget) = 0; + virtual void knobDragFinished(SubWidget* widget) = 0; + virtual void knobValueChanged(SubWidget* widget, float value) = 0; + }; + + explicit KnobEventHandler(SubWidget* self); + explicit KnobEventHandler(SubWidget* self, const KnobEventHandler& other); + KnobEventHandler& operator=(const KnobEventHandler& other); + ~KnobEventHandler(); + + // returns raw value, is assumed to be scaled if using log + float getValue() const noexcept; + + // NOTE: value is assumed to be scaled if using log + void setValue(float value, bool sendCallback = false) noexcept; + + // returns 0-1 ranged value, already with log scale as needed + float getNormalizedValue() const noexcept; + + // NOTE: value is assumed to be scaled if using log + void setDefault(float def) noexcept; + + // NOTE: value is assumed to be scaled if using log + void setRange(float min, float max) noexcept; + + void setStep(float step) noexcept; + + void setUsingLogScale(bool yesNo) noexcept; + + Orientation getOrientation() const noexcept; + void setOrientation(const Orientation orientation) noexcept; + + void setCallback(Callback* callback) noexcept; + + bool mouseEvent(const Widget::MouseEvent& ev); + bool motionEvent(const Widget::MotionEvent& ev); + bool scrollEvent(const Widget::ScrollEvent& ev); + +protected: + State getState() const noexcept; + +private: + struct PrivateData; + PrivateData* const pData; + + DISTRHO_LEAK_DETECTOR(KnobEventHandler) +}; + +// -------------------------------------------------------------------------------------------------------------------- + END_NAMESPACE_DGL #endif // DGL_EVENT_HANDLERS_HPP_INCLUDED diff --git a/dgl/ImageBaseWidgets.hpp b/dgl/ImageBaseWidgets.hpp index 0ae59412..9dfc9425 100644 --- a/dgl/ImageBaseWidgets.hpp +++ b/dgl/ImageBaseWidgets.hpp @@ -82,14 +82,10 @@ private: // -------------------------------------------------------------------------------------------------------------------- template -class ImageBaseKnob : public SubWidget +class ImageBaseKnob : public SubWidget, + public KnobEventHandler { public: - enum Orientation { - Horizontal, - Vertical - }; - class Callback { public: @@ -104,19 +100,9 @@ public: 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; + void setRotationAngle(int angle); protected: void onDisplay() override; diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index 347419e2..9922a397 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -24,7 +24,6 @@ #include "../Color.hpp" #include "../ImageBaseWidgets.hpp" -#include "Common.hpp" #include "SubWidgetPrivateData.hpp" #include "TopLevelWidgetPrivateData.hpp" #include "WidgetPrivateData.hpp" @@ -657,8 +656,7 @@ void ImageBaseKnob::onDisplay() { const GraphicsContext& context(getGraphicsContext()); cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; - const double normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) - / (pData->maximum - pData->minimum); + const double normValue = getNormalizedValue(); cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface; diff --git a/dgl/src/Common.hpp b/dgl/src/Common.hpp deleted file mode 100644 index 408e553c..00000000 --- a/dgl/src/Common.hpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho - * - * 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. - */ - -#ifndef DGL_COMMON_HPP_INCLUDED -#define DGL_COMMON_HPP_INCLUDED - -#include "../ImageBaseWidgets.hpp" - -START_NAMESPACE_DGL - -// ----------------------------------------------------------------------- - -template -struct ImageBaseKnob::PrivateData { - ImageType image; - float minimum; - float maximum; - float step; - float value; - float valueDef; - float valueTmp; - bool usingDefault; - bool usingLog; - Orientation orientation; - - int rotationAngle; - bool dragging; - double lastX; - double lastY; - - Callback* callback; - - bool alwaysRepaint; - bool isImgVertical; - uint imgLayerWidth; - uint imgLayerHeight; - uint imgLayerCount; - bool isReady; - - union { - uint glTextureId; - void* cairoSurface; - }; - - explicit PrivateData(const ImageType& img, const Orientation o); - explicit PrivateData(PrivateData* const other); - void assignFrom(PrivateData* const other); - - ~PrivateData() - { - cleanup(); - } - - void init(); - void cleanup(); - - inline float logscale(const float v) const - { - const float b = std::log(maximum/minimum)/(maximum-minimum); - const float a = maximum/std::exp(maximum*b); - return a * std::exp(b*v); - } - - inline float invlogscale(const float v) const - { - const float b = std::log(maximum/minimum)/(maximum-minimum); - const float a = maximum/std::exp(maximum*b); - return std::log(v/a)/b; - } - - DISTRHO_DECLARE_NON_COPYABLE(PrivateData) -}; - -// ----------------------------------------------------------------------- - -END_NAMESPACE_DGL - -#endif // DGL_APP_PRIVATE_DATA_HPP_INCLUDED diff --git a/dgl/src/EventHandlers.cpp b/dgl/src/EventHandlers.cpp index a9bee81c..b602164f 100644 --- a/dgl/src/EventHandlers.cpp +++ b/dgl/src/EventHandlers.cpp @@ -24,7 +24,8 @@ START_NAMESPACE_DGL struct ButtonEventHandler::PrivateData { ButtonEventHandler* const self; SubWidget* const widget; - ButtonEventHandler::Callback* callback; + ButtonEventHandler::Callback* internalCallback; + ButtonEventHandler::Callback* userCallback; int button; int state; @@ -36,7 +37,8 @@ struct ButtonEventHandler::PrivateData { PrivateData(ButtonEventHandler* const s, SubWidget* const w) : self(s), widget(w), - callback(nullptr), + internalCallback(nullptr), + userCallback(nullptr), button(-1), state(kButtonStateDefault), checkable(false), @@ -72,8 +74,10 @@ struct ButtonEventHandler::PrivateData { if (checkable) checked = !checked; - if (callback != nullptr) - callback->buttonClicked(widget, button2); + if (internalCallback != nullptr) + internalCallback->buttonClicked(widget, button2); + else if (userCallback != nullptr) + userCallback->buttonClicked(widget, button2); return true; } @@ -141,8 +145,13 @@ struct ButtonEventHandler::PrivateData { state |= kButtonStateActive; widget->repaint(); - if (sendCallback && callback != nullptr) - callback->buttonClicked(widget, -1); + if (sendCallback) + { + if (internalCallback != nullptr) + internalCallback->buttonClicked(widget, -1); + else if (userCallback != nullptr) + userCallback->buttonClicked(widget, -1); + } } void setChecked(const bool checked2, const bool sendCallback) noexcept @@ -153,8 +162,13 @@ struct ButtonEventHandler::PrivateData { checked = checked2; widget->repaint(); - if (sendCallback && callback != nullptr) - callback->buttonClicked(widget, -1); + if (sendCallback) + { + if (internalCallback != nullptr) + internalCallback->buttonClicked(widget, -1); + else if (userCallback != nullptr) + userCallback->buttonClicked(widget, -1); + } } DISTRHO_DECLARE_NON_COPYABLE(PrivateData) @@ -205,7 +219,7 @@ void ButtonEventHandler::setCheckable(const bool checkable) noexcept void ButtonEventHandler::setCallback(Callback* const callback) noexcept { - pData->callback = callback; + pData->userCallback = callback; } bool ButtonEventHandler::mouseEvent(const Widget::MouseEvent& ev) @@ -232,6 +246,375 @@ void ButtonEventHandler::stateChanged(State, State) { } +void ButtonEventHandler::setInternalCallback(Callback* const callback) noexcept +{ + pData->internalCallback = callback; +} + +void ButtonEventHandler::triggerUserCallback(SubWidget* const widget, const int button) +{ + if (pData->userCallback != nullptr) + pData->userCallback->buttonClicked(widget, button); +} + +// -------------------------------------------------------------------------------------------------------------------- + +struct KnobEventHandler::PrivateData { + KnobEventHandler* const self; + SubWidget* const widget; + KnobEventHandler::Callback* callback; + + float minimum; + float maximum; + float step; + float value; + float valueDef; + float valueTmp; + bool usingDefault; + bool usingLog; + Orientation orientation; + int state; + + double lastX; + double lastY; + + PrivateData(KnobEventHandler* const s, SubWidget* const w) + : self(s), + widget(w), + callback(nullptr), + minimum(0.0f), + maximum(1.0f), + step(0.0f), + value(0.5f), + valueDef(value), + valueTmp(value), + usingDefault(false), + usingLog(false), + orientation(Horizontal), + state(kKnobStateDefault), + lastX(0.0), + lastY(0.0) {} + + PrivateData(KnobEventHandler* const s, SubWidget* const w, PrivateData* const other) + : self(s), + widget(w), + callback(other->callback), + minimum(other->minimum), + maximum(other->maximum), + step(other->step), + value(other->value), + valueDef(other->valueDef), + valueTmp(value), + usingDefault(other->usingDefault), + usingLog(other->usingLog), + orientation(other->orientation), + state(kKnobStateDefault), + lastX(0.0), + lastY(0.0) {} + + void assignFrom(PrivateData* const other) + { + callback = other->callback; + minimum = other->minimum; + maximum = other->maximum; + step = other->step; + value = other->value; + valueDef = other->valueDef; + valueTmp = value; + usingDefault = other->usingDefault; + usingLog = other->usingLog; + orientation = other->orientation; + state = kKnobStateDefault; + lastX = 0.0; + lastY = 0.0; + } + + inline float logscale(const float v) const + { + const float b = std::log(maximum/minimum)/(maximum-minimum); + const float a = maximum/std::exp(maximum*b); + return a * std::exp(b*v); + } + + inline float invlogscale(const float v) const + { + const float b = std::log(maximum/minimum)/(maximum-minimum); + const float a = maximum/std::exp(maximum*b); + return std::log(v/a)/b; + } + + bool mouseEvent(const Widget::MouseEvent& ev) + { + if (ev.button != 1) + return false; + + if (ev.press) + { + if (! widget->contains(ev.pos)) + return false; + + if ((ev.mod & kModifierShift) != 0 && usingDefault) + { + setValue(valueDef, true); + valueTmp = value; + return true; + } + + state |= kKnobStateDragging; + lastX = ev.pos.getX(); + lastY = ev.pos.getY(); + widget->repaint(); + + if (callback != nullptr) + callback->knobDragStarted(widget); + + return true; + } + else if (state & kKnobStateDragging) + { + state &= ~kKnobStateDragging; + widget->repaint(); + + if (callback != nullptr) + callback->knobDragFinished(widget); + + return true; + } + + return false; + } + + bool motionEvent(const Widget::MotionEvent& ev) + { + if ((state & kKnobStateDragging) == 0x0) + return false; + + bool doVal = false; + float d, value2 = 0.0f; + + if (orientation == Horizontal) + { + if (const double movX = ev.pos.getX() - lastX) + { + d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; + value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + (float(maximum - minimum) / d * float(movX)); + doVal = true; + } + } + else if (orientation == Vertical) + { + if (const double movY = lastY - ev.pos.getY()) + { + d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; + value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + (float(maximum - minimum) / d * float(movY)); + doVal = true; + } + } + + if (! doVal) + return false; + + if (usingLog) + value2 = logscale(value2); + + if (value2 < minimum) + { + valueTmp = value2 = minimum; + } + else if (value2 > maximum) + { + valueTmp = value2 = maximum; + } + else + { + valueTmp = value2; + + if (d_isNotZero(step)) + { + const float rest = std::fmod(value2, step); + value2 -= rest + (rest > step/2.0f ? step : 0.0f); + } + } + + setValue(value2, true); + + lastX = ev.pos.getX(); + lastY = ev.pos.getY(); + + return true; + } + + bool scrollEvent(const Widget::ScrollEvent& ev) + { + if (! widget->contains(ev.pos)) + return false; + + const float dir = (ev.delta.getY() > 0.f) ? 1.f : -1.f; + const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; + float value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + + ((maximum - minimum) / d * 10.f * dir); + + if (usingLog) + value2 = logscale(value2); + + if (value2 < minimum) + { + valueTmp = value2 = minimum; + } + else if (value2 > maximum) + { + valueTmp = value2 = maximum; + } + else + { + valueTmp = value2; + + if (d_isNotZero(step)) + { + const float rest = std::fmod(value2, step); + value2 = value2 - rest + (rest > step/2.0f ? step : 0.0f); + } + } + + setValue(value2, true); + return true; + } + + float getNormalizedValue() const noexcept + { + const float diff = maximum - minimum; + return ((usingLog ? invlogscale(value) : value) - minimum) / diff; + } + + void setRange(const float min, const float max) noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(max > min,); + + if (value < min) + { + valueTmp = value = min; + widget->repaint(); + } + else if (value > max) + { + valueTmp = value = max; + widget->repaint(); + } + + minimum = min; + maximum = max; + } + + void setValue(const float value2, const bool sendCallback) + { + if (d_isEqual(value, value2)) + return; + + valueTmp = value = value2; + widget->repaint(); + + if (sendCallback && callback != nullptr) + { + try { + callback->knobValueChanged(widget, value); + } DISTRHO_SAFE_EXCEPTION("KnobEventHandler::setValue"); + } + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +KnobEventHandler::KnobEventHandler(SubWidget* const self) + : pData(new PrivateData(this, self)) {} + +KnobEventHandler::KnobEventHandler(SubWidget* const self, const KnobEventHandler& other) + : pData(new PrivateData(this, self, other.pData)) {} + +KnobEventHandler& KnobEventHandler::operator=(const KnobEventHandler& other) +{ + pData->assignFrom(other.pData); + return *this; +} + +KnobEventHandler::~KnobEventHandler() +{ + delete pData; +} + +float KnobEventHandler::getValue() const noexcept +{ + return pData->value; +} + +void KnobEventHandler::setValue(const float value, const bool sendCallback) noexcept +{ + pData->setValue(value, sendCallback); +} + +float KnobEventHandler::getNormalizedValue() const noexcept +{ + return pData->getNormalizedValue(); +} + +void KnobEventHandler::setDefault(const float def) noexcept +{ + pData->valueDef = def; + pData->usingDefault = true; +} + +void KnobEventHandler::setRange(const float min, const float max) noexcept +{ + pData->setRange(min, max); +} + +void KnobEventHandler::setStep(const float step) noexcept +{ + pData->step = step; +} + +void KnobEventHandler::setUsingLogScale(const bool yesNo) noexcept +{ + pData->usingLog = yesNo; +} + +KnobEventHandler::Orientation KnobEventHandler::getOrientation() const noexcept +{ + return pData->orientation; +} + +void KnobEventHandler::setOrientation(const Orientation orientation) noexcept +{ + if (pData->orientation == orientation) + return; + + pData->orientation = orientation; +} + +void KnobEventHandler::setCallback(Callback* const callback) noexcept +{ + pData->callback = callback; +} + +bool KnobEventHandler::mouseEvent(const Widget::MouseEvent& ev) +{ + return pData->mouseEvent(ev); +} + +bool KnobEventHandler::motionEvent(const Widget::MotionEvent& ev) +{ + return pData->motionEvent(ev); +} + +bool KnobEventHandler::scrollEvent(const Widget::ScrollEvent& ev) +{ + return pData->scrollEvent(ev); +} + +KnobEventHandler::State KnobEventHandler::getState() const noexcept +{ + return static_cast(pData->state); +} + // -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/ImageBaseWidgets.cpp b/dgl/src/ImageBaseWidgets.cpp index 06df7551..ae6fa975 100644 --- a/dgl/src/ImageBaseWidgets.cpp +++ b/dgl/src/ImageBaseWidgets.cpp @@ -16,7 +16,6 @@ #include "../ImageBaseWidgets.hpp" #include "../Color.hpp" -#include "Common.hpp" START_NAMESPACE_DGL @@ -193,108 +192,131 @@ bool ImageBaseButton::onMotion(const MotionEvent& ev) // -------------------------------------------------------------------------------------------------------------------- template -ImageBaseKnob::PrivateData::PrivateData(const ImageType& img, const Orientation o) - : image(img), - minimum(0.0f), - maximum(1.0f), - step(0.0f), - value(0.5f), - valueDef(value), - valueTmp(value), - usingDefault(false), - usingLog(false), - orientation(o), - rotationAngle(0), - dragging(false), - lastX(0.0), - lastY(0.0), - callback(nullptr), - 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(); -} +struct ImageBaseKnob::PrivateData : public KnobEventHandler::Callback { + ImageBaseKnob::Callback* callback; + ImageType image; -template -ImageBaseKnob::PrivateData::PrivateData(PrivateData* const other) - : image(other->image), - minimum(other->minimum), - maximum(other->maximum), - step(other->step), - value(other->value), - valueDef(other->valueDef), - valueTmp(value), - usingDefault(other->usingDefault), - usingLog(other->usingLog), - orientation(other->orientation), - rotationAngle(other->rotationAngle), - dragging(false), - lastX(0.0), - lastY(0.0), - callback(other->callback), - alwaysRepaint(other->alwaysRepaint), - isImgVertical(other->isImgVertical), - imgLayerWidth(other->imgLayerWidth), - imgLayerHeight(other->imgLayerHeight), - imgLayerCount(other->imgLayerCount), - isReady(false) -{ - init(); -} + int rotationAngle; -template -void ImageBaseKnob::PrivateData::assignFrom(PrivateData* const other) -{ - cleanup(); - image = other->image; - minimum = other->minimum; - maximum = other->maximum; - step = other->step; - value = other->value; - valueDef = other->valueDef; - valueTmp = value; - usingDefault = other->usingDefault; - usingLog = other->usingLog; - orientation = other->orientation; - rotationAngle = other->rotationAngle; - dragging = false; - lastX = 0.0; - lastY = 0.0; - callback = other->callback; - alwaysRepaint = other->alwaysRepaint; - isImgVertical = other->isImgVertical; - imgLayerWidth = other->imgLayerWidth; - imgLayerHeight = other->imgLayerHeight; - imgLayerCount = other->imgLayerCount; - isReady = false; - init(); -} + 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(widget)) + callback->imageKnobDragStarted(imageKnob); + } + + void knobDragFinished(SubWidget* const widget) override + { + if (callback != nullptr) + if (ImageBaseKnob* const imageKnob = dynamic_cast(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(widget)) + callback->imageKnobValueChanged(imageKnob, value); + } + + // implemented independently per graphics backend + void init(); + void cleanup(); + + DISTRHO_DECLARE_NON_COPYABLE(PrivateData) +}; // -------------------------------------------------------------------------------------------------------------------- template -ImageBaseKnob::ImageBaseKnob(Widget* const parentWidget, const ImageType& image, const Orientation orientation) noexcept +ImageBaseKnob::ImageBaseKnob(Widget* const parentWidget, + const ImageType& image, + const Orientation orientation) noexcept : SubWidget(parentWidget), - pData(new PrivateData(image, orientation)) + KnobEventHandler(this), + pData(new PrivateData(image)) { + setOrientation(orientation); setSize(pData->imgLayerWidth, pData->imgLayerHeight); } template ImageBaseKnob::ImageBaseKnob(const ImageBaseKnob& imageKnob) : SubWidget(imageKnob.getParentWidget()), + KnobEventHandler(this, imageKnob), pData(new PrivateData(imageKnob.pData)) { + setOrientation(imageKnob.getOrientation()); setSize(pData->imgLayerWidth, pData->imgLayerHeight); } template ImageBaseKnob& ImageBaseKnob::operator=(const ImageBaseKnob& imageKnob) { + KnobEventHandler::operator=(imageKnob); pData->assignFrom(imageKnob.pData); setSize(pData->imgLayerWidth, pData->imgLayerHeight); return *this; @@ -306,116 +328,12 @@ ImageBaseKnob::~ImageBaseKnob() delete pData; } -template -float ImageBaseKnob::getValue() const noexcept -{ - return pData->value; -} - -// NOTE: value is assumed to be scaled if using log -template -void ImageBaseKnob::setDefault(float value) noexcept -{ - pData->valueDef = value; - pData->usingDefault = true; -} - -template -void ImageBaseKnob::setRange(float min, float max) noexcept -{ - DISTRHO_SAFE_ASSERT_RETURN(max > min,); - - if (pData->value < min) - { - pData->value = min; - repaint(); - - if (pData->callback != nullptr) - { - try { - pData->callback->imageKnobValueChanged(this, pData->value); - } DISTRHO_SAFE_EXCEPTION("ImageBaseKnob::setRange < min"); - } - } - else if (pData->value > max) - { - pData->value = max; - repaint(); - - if (pData->callback != nullptr) - { - try { - pData->callback->imageKnobValueChanged(this, pData->value); - } DISTRHO_SAFE_EXCEPTION("ImageBaseKnob::setRange > max"); - } - } - - pData->minimum = min; - pData->maximum = max; -} - -template -void ImageBaseKnob::setStep(float step) noexcept -{ - pData->step = step; -} - -// NOTE: value is assumed to be scaled if using log -template -void ImageBaseKnob::setValue(float value, bool sendCallback) noexcept -{ - if (d_isEqual(pData->value, value)) - return; - - pData->value = value; - - if (d_isZero(pData->step)) - pData->valueTmp = value; - - if (pData->rotationAngle == 0 || pData->alwaysRepaint) - pData->isReady = false; - - repaint(); - - if (sendCallback && pData->callback != nullptr) - { - try { - pData->callback->imageKnobValueChanged(this, pData->value); - } DISTRHO_SAFE_EXCEPTION("ImageBaseKnob::setValue"); - } -} - -template -void ImageBaseKnob::setUsingLogScale(bool yesNo) noexcept -{ - pData->usingLog = yesNo; -} - template void ImageBaseKnob::setCallback(Callback* callback) noexcept { pData->callback = callback; } -template -void ImageBaseKnob::setOrientation(Orientation orientation) noexcept -{ - if (pData->orientation == orientation) - return; - - pData->orientation = orientation; -} - -template -void ImageBaseKnob::setRotationAngle(int angle) -{ - if (pData->rotationAngle == angle) - return; - - pData->rotationAngle = angle; - pData->isReady = false; -} - template void ImageBaseKnob::setImageLayerCount(uint count) noexcept { @@ -432,132 +350,37 @@ void ImageBaseKnob::setImageLayerCount(uint count) noexcept } template -bool ImageBaseKnob::onMouse(const MouseEvent& ev) +void ImageBaseKnob::setRotationAngle(int angle) { - if (ev.button != 1) - return false; - - if (ev.press) - { - if (! contains(ev.pos)) - return false; - - if ((ev.mod & kModifierShift) != 0 && pData->usingDefault) - { - setValue(pData->valueDef, true); - pData->valueTmp = pData->value; - return true; - } - - pData->dragging = true; - pData->lastX = ev.pos.getX(); - pData->lastY = ev.pos.getY(); - - if (pData->callback != nullptr) - pData->callback->imageKnobDragStarted(this); + if (pData->rotationAngle == angle) + return; - return true; - } - else if (pData->dragging) - { - if (pData->callback != nullptr) - pData->callback->imageKnobDragFinished(this); + pData->rotationAngle = angle; + pData->isReady = false; +} - pData->dragging = false; +template +bool ImageBaseKnob::onMouse(const MouseEvent& ev) +{ + if (SubWidget::onMouse(ev)) return true; - } - - return false; + return KnobEventHandler::mouseEvent(ev); } template bool ImageBaseKnob::onMotion(const MotionEvent& ev) { - if (! pData->dragging) - return false; - - bool doVal = false; - float d, value = 0.0f; - - if (pData->orientation == ImageBaseKnob::Horizontal) - { - if (const double movX = ev.pos.getX() - pData->lastX) - { - d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; - value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp) + (float(pData->maximum - pData->minimum) / d * float(movX)); - doVal = true; - } - } - else if (pData->orientation == ImageBaseKnob::Vertical) - { - if (const double movY = pData->lastY - ev.pos.getY()) - { - d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; - value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp) + (float(pData->maximum - pData->minimum) / d * float(movY)); - doVal = true; - } - } - - if (! doVal) - return false; - - if (pData->usingLog) - value = pData->logscale(value); - - 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); - - pData->lastX = ev.pos.getX(); - pData->lastY = ev.pos.getY(); - - return true; + if (SubWidget::onMotion(ev)) + return true; + return KnobEventHandler::motionEvent(ev); } template bool ImageBaseKnob::onScroll(const ScrollEvent& ev) { - if (! contains(ev.pos)) - return false; - - const float dir = (ev.delta.getY() > 0.f) ? 1.f : -1.f; - const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; - float value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp) - + ((pData->maximum - pData->minimum) / d * 10.f * dir); - - if (pData->usingLog) - value = pData->logscale(value); - - 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); - return true; + if (SubWidget::onScroll(ev)) + return true; + return KnobEventHandler::scrollEvent(ev); } // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 7dc82f0f..46588af9 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -23,7 +23,6 @@ #include "../Color.hpp" #include "../ImageWidgets.hpp" -#include "Common.hpp" #include "SubWidgetPrivateData.hpp" #include "TopLevelWidgetPrivateData.hpp" #include "WidgetPrivateData.hpp" @@ -486,8 +485,7 @@ template <> void ImageBaseKnob::onDisplay() { const GraphicsContext& context(getGraphicsContext()); - const float normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) - / (pData->maximum - pData->minimum); + const float normValue = getNormalizedValue(); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, pData->glTextureId);