Browse Source

Move ImageKnob logic into reusable KnobEventHandler

Signed-off-by: falkTX <falktx@falktx.com>
pull/292/head
falkTX 3 years ago
parent
commit
1bf94ee615
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
7 changed files with 592 additions and 422 deletions
  1. +75
    -2
      dgl/EventHandlers.hpp
  2. +3
    -17
      dgl/ImageBaseWidgets.hpp
  3. +1
    -3
      dgl/src/Cairo.cpp
  4. +0
    -91
      dgl/src/Common.hpp
  5. +392
    -9
      dgl/src/EventHandlers.cpp
  6. +120
    -297
      dgl/src/ImageBaseWidgets.cpp
  7. +1
    -3
      dgl/src/OpenGL.cpp

+ 75
- 2
dgl/EventHandlers.hpp View File

@@ -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


+ 3
- 17
dgl/ImageBaseWidgets.hpp View File

@@ -82,14 +82,10 @@ private:
// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
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;


+ 1
- 3
dgl/src/Cairo.cpp View File

@@ -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<CairoImage>::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;



+ 0
- 91
dgl/src/Common.hpp View File

@@ -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

+ 392
- 9
dgl/src/EventHandlers.cpp View File

@@ -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<State>(pData->state);
}

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 120
- 297
dgl/src/ImageBaseWidgets.cpp View File

@@ -16,7 +16,6 @@

#include "../ImageBaseWidgets.hpp"
#include "../Color.hpp"
#include "Common.hpp"

START_NAMESPACE_DGL

@@ -193,108 +192,131 @@ bool ImageBaseButton<ImageType>::onMotion(const MotionEvent& ev)
// --------------------------------------------------------------------------------------------------------------------

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>
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),
pData(new PrivateData(image, orientation))
KnobEventHandler(this),
pData(new PrivateData(image))
{
setOrientation(orientation);
setSize(pData->imgLayerWidth, pData->imgLayerHeight);
}

template <class ImageType>
ImageBaseKnob<ImageType>::ImageBaseKnob(const ImageBaseKnob<ImageType>& imageKnob)
: SubWidget(imageKnob.getParentWidget()),
KnobEventHandler(this, imageKnob),
pData(new PrivateData(imageKnob.pData))
{
setOrientation(imageKnob.getOrientation());
setSize(pData->imgLayerWidth, pData->imgLayerHeight);
}

template <class ImageType>
ImageBaseKnob<ImageType>& ImageBaseKnob<ImageType>::operator=(const ImageBaseKnob<ImageType>& imageKnob)
{
KnobEventHandler::operator=(imageKnob);
pData->assignFrom(imageKnob.pData);
setSize(pData->imgLayerWidth, pData->imgLayerHeight);
return *this;
@@ -306,116 +328,12 @@ ImageBaseKnob<ImageType>::~ImageBaseKnob()
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>
void ImageBaseKnob<ImageType>::setCallback(Callback* callback) noexcept
{
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>
void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept
{
@@ -432,132 +350,37 @@ void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept
}

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 false;
return KnobEventHandler::mouseEvent(ev);
}

template <class ImageType>
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>
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);
}

// --------------------------------------------------------------------------------------------------------------------


+ 1
- 3
dgl/src/OpenGL.cpp View File

@@ -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<OpenGLImage>::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);


Loading…
Cancel
Save