Signed-off-by: falkTX <falktx@falktx.com>pull/292/head
@@ -54,8 +54,8 @@ public: | |||||
void setCallback(Callback* callback) noexcept; | 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: | protected: | ||||
State getState() const noexcept; | State getState() const noexcept; | ||||
@@ -63,6 +63,9 @@ protected: | |||||
virtual void stateChanged(State state, State oldState); | virtual void stateChanged(State state, State oldState); | ||||
void setInternalCallback(Callback* callback) noexcept; | |||||
void triggerUserCallback(SubWidget* widget, int button); | |||||
private: | private: | ||||
struct PrivateData; | struct PrivateData; | ||||
PrivateData* const pData; | 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 | END_NAMESPACE_DGL | ||||
#endif // DGL_EVENT_HANDLERS_HPP_INCLUDED | #endif // DGL_EVENT_HANDLERS_HPP_INCLUDED | ||||
@@ -82,14 +82,10 @@ private: | |||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
template <class ImageType> | template <class ImageType> | ||||
class ImageBaseKnob : public SubWidget | |||||
class ImageBaseKnob : public SubWidget, | |||||
public KnobEventHandler | |||||
{ | { | ||||
public: | public: | ||||
enum Orientation { | |||||
Horizontal, | |||||
Vertical | |||||
}; | |||||
class Callback | class Callback | ||||
{ | { | ||||
public: | public: | ||||
@@ -104,19 +100,9 @@ public: | |||||
ImageBaseKnob& operator=(const ImageBaseKnob& imageKnob); | ImageBaseKnob& operator=(const ImageBaseKnob& imageKnob); | ||||
~ImageBaseKnob() override; | ~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 setCallback(Callback* callback) noexcept; | ||||
void setOrientation(Orientation orientation) noexcept; | |||||
void setRotationAngle(int angle); | |||||
void setImageLayerCount(uint count) noexcept; | void setImageLayerCount(uint count) noexcept; | ||||
void setRotationAngle(int angle); | |||||
protected: | protected: | ||||
void onDisplay() override; | void onDisplay() override; | ||||
@@ -24,7 +24,6 @@ | |||||
#include "../Color.hpp" | #include "../Color.hpp" | ||||
#include "../ImageBaseWidgets.hpp" | #include "../ImageBaseWidgets.hpp" | ||||
#include "Common.hpp" | |||||
#include "SubWidgetPrivateData.hpp" | #include "SubWidgetPrivateData.hpp" | ||||
#include "TopLevelWidgetPrivateData.hpp" | #include "TopLevelWidgetPrivateData.hpp" | ||||
#include "WidgetPrivateData.hpp" | #include "WidgetPrivateData.hpp" | ||||
@@ -657,8 +656,7 @@ void ImageBaseKnob<CairoImage>::onDisplay() | |||||
{ | { | ||||
const GraphicsContext& context(getGraphicsContext()); | const GraphicsContext& context(getGraphicsContext()); | ||||
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | 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; | cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface; | ||||
@@ -1,91 +0,0 @@ | |||||
/* | |||||
* DISTRHO Plugin Framework (DPF) | |||||
* Copyright (C) 2012-2021 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. | |||||
*/ | |||||
#ifndef DGL_COMMON_HPP_INCLUDED | |||||
#define DGL_COMMON_HPP_INCLUDED | |||||
#include "../ImageBaseWidgets.hpp" | |||||
START_NAMESPACE_DGL | |||||
// ----------------------------------------------------------------------- | |||||
template <class ImageType> | |||||
struct ImageBaseKnob<ImageType>::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 |
@@ -24,7 +24,8 @@ START_NAMESPACE_DGL | |||||
struct ButtonEventHandler::PrivateData { | struct ButtonEventHandler::PrivateData { | ||||
ButtonEventHandler* const self; | ButtonEventHandler* const self; | ||||
SubWidget* const widget; | SubWidget* const widget; | ||||
ButtonEventHandler::Callback* callback; | |||||
ButtonEventHandler::Callback* internalCallback; | |||||
ButtonEventHandler::Callback* userCallback; | |||||
int button; | int button; | ||||
int state; | int state; | ||||
@@ -36,7 +37,8 @@ struct ButtonEventHandler::PrivateData { | |||||
PrivateData(ButtonEventHandler* const s, SubWidget* const w) | PrivateData(ButtonEventHandler* const s, SubWidget* const w) | ||||
: self(s), | : self(s), | ||||
widget(w), | widget(w), | ||||
callback(nullptr), | |||||
internalCallback(nullptr), | |||||
userCallback(nullptr), | |||||
button(-1), | button(-1), | ||||
state(kButtonStateDefault), | state(kButtonStateDefault), | ||||
checkable(false), | checkable(false), | ||||
@@ -72,8 +74,10 @@ struct ButtonEventHandler::PrivateData { | |||||
if (checkable) | if (checkable) | ||||
checked = !checked; | 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; | return true; | ||||
} | } | ||||
@@ -141,8 +145,13 @@ struct ButtonEventHandler::PrivateData { | |||||
state |= kButtonStateActive; | state |= kButtonStateActive; | ||||
widget->repaint(); | 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 | void setChecked(const bool checked2, const bool sendCallback) noexcept | ||||
@@ -153,8 +162,13 @@ struct ButtonEventHandler::PrivateData { | |||||
checked = checked2; | checked = checked2; | ||||
widget->repaint(); | 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) | DISTRHO_DECLARE_NON_COPYABLE(PrivateData) | ||||
@@ -205,7 +219,7 @@ void ButtonEventHandler::setCheckable(const bool checkable) noexcept | |||||
void ButtonEventHandler::setCallback(Callback* const callback) noexcept | void ButtonEventHandler::setCallback(Callback* const callback) noexcept | ||||
{ | { | ||||
pData->callback = callback; | |||||
pData->userCallback = callback; | |||||
} | } | ||||
bool ButtonEventHandler::mouseEvent(const Widget::MouseEvent& ev) | 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<State>(pData->state); | |||||
} | |||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL |
@@ -16,7 +16,6 @@ | |||||
#include "../ImageBaseWidgets.hpp" | #include "../ImageBaseWidgets.hpp" | ||||
#include "../Color.hpp" | #include "../Color.hpp" | ||||
#include "Common.hpp" | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
@@ -193,108 +192,131 @@ bool ImageBaseButton<ImageType>::onMotion(const MotionEvent& ev) | |||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
template <class ImageType> | template <class ImageType> | ||||
ImageBaseKnob<ImageType>::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<ImageType>::PrivateData : public KnobEventHandler::Callback { | |||||
ImageBaseKnob<ImageType>::Callback* callback; | |||||
ImageType image; | |||||
template <class ImageType> | |||||
ImageBaseKnob<ImageType>::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 <class ImageType> | |||||
void ImageBaseKnob<ImageType>::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<ImageBaseKnob*>(widget)) | |||||
callback->imageKnobDragStarted(imageKnob); | |||||
} | |||||
void knobDragFinished(SubWidget* const widget) override | |||||
{ | |||||
if (callback != nullptr) | |||||
if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget)) | |||||
callback->imageKnobDragFinished(imageKnob); | |||||
} | |||||
void knobValueChanged(SubWidget* const widget, const float value) override | |||||
{ | |||||
if (rotationAngle == 0 || alwaysRepaint) | |||||
isReady = false; | |||||
if (callback != nullptr) | |||||
if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget)) | |||||
callback->imageKnobValueChanged(imageKnob, value); | |||||
} | |||||
// implemented independently per graphics backend | |||||
void init(); | |||||
void cleanup(); | |||||
DISTRHO_DECLARE_NON_COPYABLE(PrivateData) | |||||
}; | |||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
template <class ImageType> | template <class ImageType> | ||||
ImageBaseKnob<ImageType>::ImageBaseKnob(Widget* const parentWidget, const ImageType& image, const Orientation orientation) noexcept | |||||
ImageBaseKnob<ImageType>::ImageBaseKnob(Widget* const parentWidget, | |||||
const ImageType& image, | |||||
const Orientation orientation) noexcept | |||||
: SubWidget(parentWidget), | : SubWidget(parentWidget), | ||||
pData(new PrivateData(image, orientation)) | |||||
KnobEventHandler(this), | |||||
pData(new PrivateData(image)) | |||||
{ | { | ||||
setOrientation(orientation); | |||||
setSize(pData->imgLayerWidth, pData->imgLayerHeight); | setSize(pData->imgLayerWidth, pData->imgLayerHeight); | ||||
} | } | ||||
template <class ImageType> | template <class ImageType> | ||||
ImageBaseKnob<ImageType>::ImageBaseKnob(const ImageBaseKnob<ImageType>& imageKnob) | ImageBaseKnob<ImageType>::ImageBaseKnob(const ImageBaseKnob<ImageType>& imageKnob) | ||||
: SubWidget(imageKnob.getParentWidget()), | : SubWidget(imageKnob.getParentWidget()), | ||||
KnobEventHandler(this, imageKnob), | |||||
pData(new PrivateData(imageKnob.pData)) | pData(new PrivateData(imageKnob.pData)) | ||||
{ | { | ||||
setOrientation(imageKnob.getOrientation()); | |||||
setSize(pData->imgLayerWidth, pData->imgLayerHeight); | setSize(pData->imgLayerWidth, pData->imgLayerHeight); | ||||
} | } | ||||
template <class ImageType> | template <class ImageType> | ||||
ImageBaseKnob<ImageType>& ImageBaseKnob<ImageType>::operator=(const ImageBaseKnob<ImageType>& imageKnob) | ImageBaseKnob<ImageType>& ImageBaseKnob<ImageType>::operator=(const ImageBaseKnob<ImageType>& imageKnob) | ||||
{ | { | ||||
KnobEventHandler::operator=(imageKnob); | |||||
pData->assignFrom(imageKnob.pData); | pData->assignFrom(imageKnob.pData); | ||||
setSize(pData->imgLayerWidth, pData->imgLayerHeight); | setSize(pData->imgLayerWidth, pData->imgLayerHeight); | ||||
return *this; | return *this; | ||||
@@ -306,116 +328,12 @@ ImageBaseKnob<ImageType>::~ImageBaseKnob() | |||||
delete pData; | delete pData; | ||||
} | } | ||||
template <class ImageType> | |||||
float ImageBaseKnob<ImageType>::getValue() const noexcept | |||||
{ | |||||
return pData->value; | |||||
} | |||||
// NOTE: value is assumed to be scaled if using log | |||||
template <class ImageType> | |||||
void ImageBaseKnob<ImageType>::setDefault(float value) noexcept | |||||
{ | |||||
pData->valueDef = value; | |||||
pData->usingDefault = true; | |||||
} | |||||
template <class ImageType> | |||||
void ImageBaseKnob<ImageType>::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<ImageType>::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<ImageType>::setRange > max"); | |||||
} | |||||
} | |||||
pData->minimum = min; | |||||
pData->maximum = max; | |||||
} | |||||
template <class ImageType> | |||||
void ImageBaseKnob<ImageType>::setStep(float step) noexcept | |||||
{ | |||||
pData->step = step; | |||||
} | |||||
// NOTE: value is assumed to be scaled if using log | |||||
template <class ImageType> | |||||
void ImageBaseKnob<ImageType>::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<ImageType>::setValue"); | |||||
} | |||||
} | |||||
template <class ImageType> | |||||
void ImageBaseKnob<ImageType>::setUsingLogScale(bool yesNo) noexcept | |||||
{ | |||||
pData->usingLog = yesNo; | |||||
} | |||||
template <class ImageType> | template <class ImageType> | ||||
void ImageBaseKnob<ImageType>::setCallback(Callback* callback) noexcept | void ImageBaseKnob<ImageType>::setCallback(Callback* callback) noexcept | ||||
{ | { | ||||
pData->callback = callback; | pData->callback = callback; | ||||
} | } | ||||
template <class ImageType> | |||||
void ImageBaseKnob<ImageType>::setOrientation(Orientation orientation) noexcept | |||||
{ | |||||
if (pData->orientation == orientation) | |||||
return; | |||||
pData->orientation = orientation; | |||||
} | |||||
template <class ImageType> | |||||
void ImageBaseKnob<ImageType>::setRotationAngle(int angle) | |||||
{ | |||||
if (pData->rotationAngle == angle) | |||||
return; | |||||
pData->rotationAngle = angle; | |||||
pData->isReady = false; | |||||
} | |||||
template <class ImageType> | template <class ImageType> | ||||
void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept | void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept | ||||
{ | { | ||||
@@ -432,132 +350,37 @@ void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept | |||||
} | } | ||||
template <class ImageType> | template <class ImageType> | ||||
bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev) | |||||
void ImageBaseKnob<ImageType>::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 <class ImageType> | |||||
bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev) | |||||
{ | |||||
if (SubWidget::onMouse(ev)) | |||||
return true; | return true; | ||||
} | |||||
return false; | |||||
return KnobEventHandler::mouseEvent(ev); | |||||
} | } | ||||
template <class ImageType> | template <class ImageType> | ||||
bool ImageBaseKnob<ImageType>::onMotion(const MotionEvent& ev) | bool ImageBaseKnob<ImageType>::onMotion(const MotionEvent& ev) | ||||
{ | { | ||||
if (! pData->dragging) | |||||
return false; | |||||
bool doVal = false; | |||||
float d, value = 0.0f; | |||||
if (pData->orientation == ImageBaseKnob<ImageType>::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<ImageType>::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 <class ImageType> | template <class ImageType> | ||||
bool ImageBaseKnob<ImageType>::onScroll(const ScrollEvent& ev) | bool ImageBaseKnob<ImageType>::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); | |||||
} | } | ||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
@@ -23,7 +23,6 @@ | |||||
#include "../Color.hpp" | #include "../Color.hpp" | ||||
#include "../ImageWidgets.hpp" | #include "../ImageWidgets.hpp" | ||||
#include "Common.hpp" | |||||
#include "SubWidgetPrivateData.hpp" | #include "SubWidgetPrivateData.hpp" | ||||
#include "TopLevelWidgetPrivateData.hpp" | #include "TopLevelWidgetPrivateData.hpp" | ||||
#include "WidgetPrivateData.hpp" | #include "WidgetPrivateData.hpp" | ||||
@@ -486,8 +485,7 @@ template <> | |||||
void ImageBaseKnob<OpenGLImage>::onDisplay() | void ImageBaseKnob<OpenGLImage>::onDisplay() | ||||
{ | { | ||||
const GraphicsContext& context(getGraphicsContext()); | 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); | glEnable(GL_TEXTURE_2D); | ||||
glBindTexture(GL_TEXTURE_2D, pData->glTextureId); | glBindTexture(GL_TEXTURE_2D, pData->glTextureId); | ||||