Browse Source

Move ImageWidgets code to generic file, WIP

pull/272/head
falkTX 4 years ago
parent
commit
7baa2c0458
7 changed files with 1069 additions and 1080 deletions
  1. +3
    -5
      dgl/Cairo.hpp
  2. +199
    -0
      dgl/ImageBaseWidgets.hpp
  3. +11
    -192
      dgl/ImageWidgets.hpp
  4. +0
    -1
      dgl/Makefile
  5. +3
    -6
      dgl/OpenGL.hpp
  6. +853
    -0
      dgl/src/ImageBaseWidgets.cpp
  7. +0
    -876
      dgl/src/ImageWidgets.cpp

+ 3
- 5
dgl/Cairo.hpp View File

@@ -19,7 +19,6 @@

#include "ImageBase.hpp"
#include "ImageBaseWidgets.hpp"
#include "SubWidget.hpp"

#include <cairo/cairo.h>

@@ -115,25 +114,21 @@ class CairoBaseWidget : public BaseWidget
public:
/**
Constructor for a CairoSubWidget.
@see CreateFlags
*/
explicit CairoBaseWidget(Widget* const parentGroupWidget);

/**
Constructor for a CairoTopLevelWidget.
@see CreateFlags
*/
explicit CairoBaseWidget(Window& windowToMapTo);

/**
Constructor for a CairoStandaloneWindow without parent window.
@see CreateFlags
*/
explicit CairoBaseWidget(Application& app);

/**
Constructor for a CairoStandaloneWindow with parent window.
@see CreateFlags
*/
explicit CairoBaseWidget(Application& app, Window& parentWindow);

@@ -171,6 +166,9 @@ typedef CairoBaseWidget<StandaloneWindow> CairoStandaloneWindow;

typedef ImageBaseAboutWindow<CairoImage> CairoImageAboutWindow;
typedef ImageBaseButton<CairoImage> CairoImageButton;
typedef ImageBaseKnob<CairoImage> CairoImageKnob;
typedef ImageBaseSlider<CairoImage> CairoImageSlider;
typedef ImageBaseSwitch<CairoImage> CairoImageSwitch;

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



+ 199
- 0
dgl/ImageBaseWidgets.hpp View File

@@ -82,6 +82,205 @@ private:

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

template <class ImageType>
class ImageBaseKnob : public SubWidget
{
public:
enum Orientation {
Horizontal,
Vertical
};

class Callback
{
public:
virtual ~Callback() {}
virtual void imageKnobDragStarted(ImageBaseKnob* imageKnob) = 0;
virtual void imageKnobDragFinished(ImageBaseKnob* imageKnob) = 0;
virtual void imageKnobValueChanged(ImageBaseKnob* imageKnob, float value) = 0;
};

explicit ImageBaseKnob(Widget* parentWidget, const ImageType& image, Orientation orientation = Vertical) noexcept;
explicit ImageBaseKnob(const ImageBaseKnob& imageKnob);
ImageBaseKnob& operator=(const ImageBaseKnob& imageKnob);
~ImageBaseKnob() override;

float getValue() const noexcept;

void setDefault(float def) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;
void setValue(float value, bool sendCallback = false) noexcept;
void setUsingLogScale(bool yesNo) noexcept;

void setCallback(Callback* callback) noexcept;
void setOrientation(Orientation orientation) noexcept;
void setRotationAngle(int angle);

void setImageLayerCount(uint count) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;
bool onScroll(const ScrollEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

/*
Image fImage;
float fMinimum;
float fMaximum;
float fStep;
float fValue;
float fValueDef;
float fValueTmp;
bool fUsingDefault;
bool fUsingLog;
Orientation fOrientation;

int fRotationAngle;
bool fDragging;
int fLastX;
int fLastY;

Callback* fCallback;

bool fIsImgVertical;
uint fImgLayerWidth;
uint fImgLayerHeight;
uint fImgLayerCount;
bool fIsReady;
GLuint fTextureId;

float _logscale(float value) const;
float _invlogscale(float value) const;
*/

DISTRHO_LEAK_DETECTOR(ImageBaseKnob)
};

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

// note set range and step before setting the value

template <class ImageType>
class ImageBaseSlider : public SubWidget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSliderDragStarted(ImageBaseSlider* imageSlider) = 0;
virtual void imageSliderDragFinished(ImageBaseSlider* imageSlider) = 0;
virtual void imageSliderValueChanged(ImageBaseSlider* imageSlider, float value) = 0;
};

explicit ImageBaseSlider(Widget* parentWidget, const ImageType& image) noexcept;

float getValue() const noexcept;
void setValue(float value, bool sendCallback = false) noexcept;
void setDefault(float def) noexcept;

void setStartPos(const Point<int>& startPos) noexcept;
void setStartPos(int x, int y) noexcept;
void setEndPos(const Point<int>& endPos) noexcept;
void setEndPos(int x, int y) noexcept;

void setInverted(bool inverted) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

/*
Image fImage;
float fMinimum;
float fMaximum;
float fStep;
float fValue;
float fValueDef;
float fValueTmp;
bool fUsingDefault;

bool fDragging;
bool fInverted;
bool fValueIsSet;
int fStartedX;
int fStartedY;

Callback* fCallback;

Point<int> fStartPos;
Point<int> fEndPos;
Rectangle<double> fSliderArea;

void _recheckArea() noexcept;
*/

// these should not be used
void setAbsoluteX(int) const noexcept {}
void setAbsoluteY(int) const noexcept {}
void setAbsolutePos(int, int) const noexcept {}
void setAbsolutePos(const Point<int>&) const noexcept {}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseSlider)
};

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

template <class ImageType>
class ImageBaseSwitch : public SubWidget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSwitchClicked(ImageBaseSwitch* imageSwitch, bool down) = 0;
};

explicit ImageBaseSwitch(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept;
explicit ImageBaseSwitch(const ImageBaseSwitch& imageSwitch) noexcept;
ImageBaseSwitch& operator=(const ImageBaseSwitch& imageSwitch) noexcept;

bool isDown() const noexcept;
void setDown(bool down) noexcept;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

/*
Image fImageNormal;
Image fImageDown;
bool fIsDown;
Callback* fCallback;
*/

DISTRHO_LEAK_DETECTOR(ImageBaseSwitch)
};

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

END_NAMESPACE_DGL

#endif // DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED

+ 11
- 192
dgl/ImageWidgets.hpp View File

@@ -17,206 +17,25 @@
#ifndef DGL_IMAGE_WIDGETS_HPP_INCLUDED
#define DGL_IMAGE_WIDGETS_HPP_INCLUDED

#include "Image.hpp"
#include "ImageBaseWidgets.hpp"
#include "SubWidget.hpp"

// TODO switch to use templated image type after merging widget-related PRs
#if defined(__GNUC__) && (__GNUC__ >= 6)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#include "OpenGL.hpp"

START_NAMESPACE_DGL

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

class ImageKnob : public SubWidget
{
public:
enum Orientation {
Horizontal,
Vertical
};

class Callback
{
public:
virtual ~Callback() {}
virtual void imageKnobDragStarted(ImageKnob* imageKnob) = 0;
virtual void imageKnobDragFinished(ImageKnob* imageKnob) = 0;
virtual void imageKnobValueChanged(ImageKnob* imageKnob, float value) = 0;
};

explicit ImageKnob(Widget* parentWidget, const Image& image, Orientation orientation = Vertical) noexcept;
explicit ImageKnob(const ImageKnob& imageKnob);
ImageKnob& operator=(const ImageKnob& imageKnob);
~ImageKnob() override;

float getValue() const noexcept;

void setDefault(float def) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;
void setValue(float value, bool sendCallback = false) noexcept;
void setUsingLogScale(bool yesNo) noexcept;

void setCallback(Callback* callback) noexcept;
void setOrientation(Orientation orientation) noexcept;
void setRotationAngle(int angle);

void setImageLayerCount(uint count) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;
bool onScroll(const ScrollEvent&) override;

private:
Image fImage;
float fMinimum;
float fMaximum;
float fStep;
float fValue;
float fValueDef;
float fValueTmp;
bool fUsingDefault;
bool fUsingLog;
Orientation fOrientation;

int fRotationAngle;
bool fDragging;
int fLastX;
int fLastY;

Callback* fCallback;

bool fIsImgVertical;
uint fImgLayerWidth;
uint fImgLayerHeight;
uint fImgLayerCount;
bool fIsReady;
GLuint fTextureId;

float _logscale(float value) const;
float _invlogscale(float value) const;

DISTRHO_LEAK_DETECTOR(ImageKnob)
};

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

// note set range and step before setting the value

class ImageSlider : public SubWidget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSliderDragStarted(ImageSlider* imageSlider) = 0;
virtual void imageSliderDragFinished(ImageSlider* imageSlider) = 0;
virtual void imageSliderValueChanged(ImageSlider* imageSlider, float value) = 0;
};

explicit ImageSlider(Widget* parentWidget, const Image& image) noexcept;
DISTRHO_DEPRECATED_BY("OpenGLImageAboutWindow")
typedef OpenGLImageAboutWindow ImageAboutWindow;

float getValue() const noexcept;
void setValue(float value, bool sendCallback = false) noexcept;
void setDefault(float def) noexcept;
DISTRHO_DEPRECATED_BY("OpenGLImageButton")
typedef OpenGLImageButton ImageButton;

void setStartPos(const Point<int>& startPos) noexcept;
void setStartPos(int x, int y) noexcept;
void setEndPos(const Point<int>& endPos) noexcept;
void setEndPos(int x, int y) noexcept;
DISTRHO_DEPRECATED_BY("OpenGLImageKnob")
typedef OpenGLImageKnob ImageKnob;

void setInverted(bool inverted) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;
DISTRHO_DEPRECATED_BY("OpenGLImageSlider")
typedef OpenGLImageSlider ImageSlider;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;

private:
Image fImage;
float fMinimum;
float fMaximum;
float fStep;
float fValue;
float fValueDef;
float fValueTmp;
bool fUsingDefault;

bool fDragging;
bool fInverted;
bool fValueIsSet;
int fStartedX;
int fStartedY;

Callback* fCallback;

Point<int> fStartPos;
Point<int> fEndPos;
Rectangle<double> fSliderArea;

void _recheckArea() noexcept;

// these should not be used
void setAbsoluteX(int) const noexcept {}
void setAbsoluteY(int) const noexcept {}
void setAbsolutePos(int, int) const noexcept {}
void setAbsolutePos(const Point<int>&) const noexcept {}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageSlider)
};

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

class ImageSwitch : public SubWidget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSwitchClicked(ImageSwitch* imageSwitch, bool down) = 0;
};

explicit ImageSwitch(Widget* parentWidget, const Image& imageNormal, const Image& imageDown) noexcept;
explicit ImageSwitch(const ImageSwitch& imageSwitch) noexcept;
ImageSwitch& operator=(const ImageSwitch& imageSwitch) noexcept;

bool isDown() const noexcept;
void setDown(bool down) noexcept;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;

private:
Image fImageNormal;
Image fImageDown;
bool fIsDown;

Callback* fCallback;

DISTRHO_LEAK_DETECTOR(ImageSwitch)
};

// -----------------------------------------------------------------------
DISTRHO_DEPRECATED_BY("OpenGLImageSwitch")
typedef OpenGLImageSwitch ImageSwitch;

END_NAMESPACE_DGL

#if defined(__GNUC__) && (__GNUC__ >= 6)
# pragma GCC diagnostic pop
#endif

#endif // DGL_IMAGE_WIDGETS_HPP_INCLUDED

+ 0
- 1
dgl/Makefile View File

@@ -56,7 +56,6 @@ endif

OBJS_opengl = $(OBJS_common) \
../build/dgl/OpenGL.cpp.opengl.o \
../build/dgl/ImageWidgets.cpp.o \
../build/dgl/NanoVG.cpp.opengl.o

ifeq ($(MACOS),true)


+ 3
- 6
dgl/OpenGL.hpp View File

@@ -282,12 +282,9 @@ private:

typedef ImageBaseAboutWindow<OpenGLImage> OpenGLImageAboutWindow;
typedef ImageBaseButton<OpenGLImage> OpenGLImageButton;

DISTRHO_DEPRECATED_BY("OpenGLImageAboutWindow")
typedef OpenGLImageAboutWindow ImageAboutWindow;

DISTRHO_DEPRECATED_BY("OpenGLImageButton")
typedef OpenGLImageButton ImageButton;
typedef ImageBaseKnob<OpenGLImage> OpenGLImageKnob;
typedef ImageBaseSlider<OpenGLImage> OpenGLImageSlider;
typedef ImageBaseSwitch<OpenGLImage> OpenGLImageSwitch;

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



+ 853
- 0
dgl/src/ImageBaseWidgets.cpp View File

@@ -180,4 +180,857 @@ bool ImageBaseButton<ImageType>::onMotion(const MotionEvent& ev)

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

#if 0
ImageKnob::ImageKnob(Widget* const parentWidget, const Image& image, Orientation orientation) noexcept
: SubWidget(parentWidget),
fImage(image),
fMinimum(0.0f),
fMaximum(1.0f),
fStep(0.0f),
fValue(0.5f),
fValueDef(fValue),
fValueTmp(fValue),
fUsingDefault(false),
fUsingLog(false),
fOrientation(orientation),
fRotationAngle(0),
fDragging(false),
fLastX(0),
fLastY(0),
fCallback(nullptr),
fIsImgVertical(image.getHeight() > image.getWidth()),
fImgLayerWidth(fIsImgVertical ? image.getWidth() : image.getHeight()),
fImgLayerHeight(fImgLayerWidth),
fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerHeight : image.getWidth()/fImgLayerWidth),
fIsReady(false),
fTextureId(0)
{
glGenTextures(1, &fTextureId);
setSize(fImgLayerWidth, fImgLayerHeight);
}

ImageKnob::ImageKnob(const ImageKnob& imageKnob)
: SubWidget(imageKnob.getParentWidget()),
fImage(imageKnob.fImage),
fMinimum(imageKnob.fMinimum),
fMaximum(imageKnob.fMaximum),
fStep(imageKnob.fStep),
fValue(imageKnob.fValue),
fValueDef(imageKnob.fValueDef),
fValueTmp(fValue),
fUsingDefault(imageKnob.fUsingDefault),
fUsingLog(imageKnob.fUsingLog),
fOrientation(imageKnob.fOrientation),
fRotationAngle(imageKnob.fRotationAngle),
fDragging(false),
fLastX(0),
fLastY(0),
fCallback(imageKnob.fCallback),
fIsImgVertical(imageKnob.fIsImgVertical),
fImgLayerWidth(imageKnob.fImgLayerWidth),
fImgLayerHeight(imageKnob.fImgLayerHeight),
fImgLayerCount(imageKnob.fImgLayerCount),
fIsReady(false),
fTextureId(0)
{
glGenTextures(1, &fTextureId);
setSize(fImgLayerWidth, fImgLayerHeight);
}

ImageKnob& ImageKnob::operator=(const ImageKnob& imageKnob)
{
fImage = imageKnob.fImage;
fMinimum = imageKnob.fMinimum;
fMaximum = imageKnob.fMaximum;
fStep = imageKnob.fStep;
fValue = imageKnob.fValue;
fValueDef = imageKnob.fValueDef;
fValueTmp = fValue;
fUsingDefault = imageKnob.fUsingDefault;
fUsingLog = imageKnob.fUsingLog;
fOrientation = imageKnob.fOrientation;
fRotationAngle = imageKnob.fRotationAngle;
fDragging = false;
fLastX = 0;
fLastY = 0;
fCallback = imageKnob.fCallback;
fIsImgVertical = imageKnob.fIsImgVertical;
fImgLayerWidth = imageKnob.fImgLayerWidth;
fImgLayerHeight = imageKnob.fImgLayerHeight;
fImgLayerCount = imageKnob.fImgLayerCount;
fIsReady = false;

if (fTextureId != 0)
{
glDeleteTextures(1, &fTextureId);
fTextureId = 0;
}

glGenTextures(1, &fTextureId);
setSize(fImgLayerWidth, fImgLayerHeight);

return *this;
}

ImageKnob::~ImageKnob()
{
if (fTextureId != 0)
{
glDeleteTextures(1, &fTextureId);
fTextureId = 0;
}
}

float ImageKnob::getValue() const noexcept
{
return fValue;
}

// NOTE: value is assumed to be scaled if using log
void ImageKnob::setDefault(float value) noexcept
{
fValueDef = value;
fUsingDefault = true;
}

void ImageKnob::setRange(float min, float max) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(max > min,);

if (fValue < min)
{
fValue = min;
repaint();

if (fCallback != nullptr)
{
try {
fCallback->imageKnobValueChanged(this, fValue);
} DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange < min");
}
}
else if (fValue > max)
{
fValue = max;
repaint();

if (fCallback != nullptr)
{
try {
fCallback->imageKnobValueChanged(this, fValue);
} DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange > max");
}
}

fMinimum = min;
fMaximum = max;
}

void ImageKnob::setStep(float step) noexcept
{
fStep = step;
}

// NOTE: value is assumed to be scaled if using log
void ImageKnob::setValue(float value, bool sendCallback) noexcept
{
if (d_isEqual(fValue, value))
return;

fValue = value;

if (d_isZero(fStep))
fValueTmp = value;

if (fRotationAngle == 0)
fIsReady = false;

repaint();

if (sendCallback && fCallback != nullptr)
{
try {
fCallback->imageKnobValueChanged(this, fValue);
} DISTRHO_SAFE_EXCEPTION("ImageKnob::setValue");
}
}

void ImageKnob::setUsingLogScale(bool yesNo) noexcept
{
fUsingLog = yesNo;
}

void ImageKnob::setCallback(Callback* callback) noexcept
{
fCallback = callback;
}

void ImageKnob::setOrientation(Orientation orientation) noexcept
{
if (fOrientation == orientation)
return;

fOrientation = orientation;
}

void ImageKnob::setRotationAngle(int angle)
{
if (fRotationAngle == angle)
return;

fRotationAngle = angle;
fIsReady = false;
}

void ImageKnob::setImageLayerCount(uint count) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(count > 1,);

fImgLayerCount = count;

if (fIsImgVertical)
fImgLayerHeight = fImage.getHeight()/count;
else
fImgLayerWidth = fImage.getWidth()/count;

setSize(fImgLayerWidth, fImgLayerHeight);
}

void ImageKnob::onDisplay()
{
const float normValue = ((fUsingLog ? _invlogscale(fValue) : fValue) - fMinimum) / (fMaximum - fMinimum);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, fTextureId);

if (! fIsReady)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

uint imageDataOffset = 0;

if (fRotationAngle == 0)
{
DISTRHO_SAFE_ASSERT_RETURN(fImgLayerCount > 0,);
DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,);

const uint& v1(fIsImgVertical ? fImgLayerWidth : fImgLayerHeight);
const uint& v2(fIsImgVertical ? fImgLayerHeight : fImgLayerWidth);

const uint layerDataSize = v1 * v2 * ((fImage.getFormat() == kImageFormatBGRA ||
fImage.getFormat() == kImageFormatRGBA) ? 4 : 3);
/* */ imageDataOffset = layerDataSize * uint(normValue * float(fImgLayerCount-1));
}

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0,
asOpenGLImageFormat(fImage.getFormat()), GL_UNSIGNED_BYTE, fImage.getRawData() + imageDataOffset);

fIsReady = true;
}

const int w = static_cast<int>(getWidth());
const int h = static_cast<int>(getHeight());

if (fRotationAngle != 0)
{
glPushMatrix();

const int w2 = w/2;
const int h2 = h/2;

glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f);
glRotatef(normValue*static_cast<float>(fRotationAngle), 0.0f, 0.0f, 1.0f);

Rectangle<int>(-w2, -h2, w, h).draw();

glPopMatrix();
}
else
{
Rectangle<int>(0, 0, w, h).draw();
}

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

bool ImageKnob::onMouse(const MouseEvent& ev)
{
if (ev.button != 1)
return false;

if (ev.press)
{
if (! contains(ev.pos))
return false;

if ((ev.mod & kModifierShift) != 0 && fUsingDefault)
{
setValue(fValueDef, true);
fValueTmp = fValue;
return true;
}

fDragging = true;
fLastX = ev.pos.getX();
fLastY = ev.pos.getY();

if (fCallback != nullptr)
fCallback->imageKnobDragStarted(this);

return true;
}
else if (fDragging)
{
if (fCallback != nullptr)
fCallback->imageKnobDragFinished(this);

fDragging = false;
return true;
}

return false;
}

bool ImageKnob::onMotion(const MotionEvent& ev)
{
if (! fDragging)
return false;

bool doVal = false;
float d, value = 0.0f;

if (fOrientation == ImageKnob::Horizontal)
{
if (const int movX = ev.pos.getX() - fLastX)
{
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movX));
doVal = true;
}
}
else if (fOrientation == ImageKnob::Vertical)
{
if (const int movY = fLastY - ev.pos.getY())
{
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movY));
doVal = true;
}
}

if (! doVal)
return false;

if (fUsingLog)
value = _logscale(value);

if (value < fMinimum)
{
fValueTmp = value = fMinimum;
}
else if (value > fMaximum)
{
fValueTmp = value = fMaximum;
}
else if (d_isNotZero(fStep))
{
fValueTmp = value;
const float rest = std::fmod(value, fStep);
value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f);
}

setValue(value, true);

fLastX = ev.pos.getX();
fLastY = ev.pos.getY();

return true;
}

bool ImageKnob::onScroll(const ScrollEvent& ev)
{
if (! contains(ev.pos))
return false;

const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
float value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * 10.f * ev.delta.getY());

if (fUsingLog)
value = _logscale(value);

if (value < fMinimum)
{
fValueTmp = value = fMinimum;
}
else if (value > fMaximum)
{
fValueTmp = value = fMaximum;
}
else if (d_isNotZero(fStep))
{
fValueTmp = value;
const float rest = std::fmod(value, fStep);
value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f);
}

setValue(value, true);
return true;
}

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

float ImageKnob::_logscale(float value) const
{
const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum);
const float a = fMaximum/std::exp(fMaximum*b);
return a * std::exp(b*value);
}

float ImageKnob::_invlogscale(float value) const
{
const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum);
const float a = fMaximum/std::exp(fMaximum*b);
return std::log(value/a)/b;
}
#endif

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

#if 0
ImageSlider::ImageSlider(Widget* const parentWidget, const Image& image) noexcept
: SubWidget(parentWidget),
fImage(image),
fMinimum(0.0f),
fMaximum(1.0f),
fStep(0.0f),
fValue(0.5f),
fValueDef(fValue),
fValueTmp(fValue),
fUsingDefault(false),
fDragging(false),
fInverted(false),
fValueIsSet(false),
fStartedX(0),
fStartedY(0),
fCallback(nullptr),
fStartPos(),
fEndPos(),
fSliderArea()
{
setNeedsFullViewportDrawing();
}

float ImageSlider::getValue() const noexcept
{
return fValue;
}

void ImageSlider::setValue(float value, bool sendCallback) noexcept
{
if (! fValueIsSet)
fValueIsSet = true;

if (d_isEqual(fValue, value))
return;

fValue = value;

if (d_isZero(fStep))
fValueTmp = value;

repaint();

if (sendCallback && fCallback != nullptr)
{
try {
fCallback->imageSliderValueChanged(this, fValue);
} DISTRHO_SAFE_EXCEPTION("ImageSlider::setValue");
}
}

void ImageSlider::setStartPos(const Point<int>& startPos) noexcept
{
fStartPos = startPos;
_recheckArea();
}

void ImageSlider::setStartPos(int x, int y) noexcept
{
setStartPos(Point<int>(x, y));
}

void ImageSlider::setEndPos(const Point<int>& endPos) noexcept
{
fEndPos = endPos;
_recheckArea();
}

void ImageSlider::setEndPos(int x, int y) noexcept
{
setEndPos(Point<int>(x, y));
}

void ImageSlider::setInverted(bool inverted) noexcept
{
if (fInverted == inverted)
return;

fInverted = inverted;
repaint();
}

void ImageSlider::setDefault(float value) noexcept
{
fValueDef = value;
fUsingDefault = true;
}

void ImageSlider::setRange(float min, float max) noexcept
{
fMinimum = min;
fMaximum = max;

if (fValue < min)
{
fValue = min;
repaint();

if (fCallback != nullptr && fValueIsSet)
{
try {
fCallback->imageSliderValueChanged(this, fValue);
} DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange < min");
}
}
else if (fValue > max)
{
fValue = max;
repaint();

if (fCallback != nullptr && fValueIsSet)
{
try {
fCallback->imageSliderValueChanged(this, fValue);
} DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange > max");
}
}
}

void ImageSlider::setStep(float step) noexcept
{
fStep = step;
}

void ImageSlider::setCallback(Callback* callback) noexcept
{
fCallback = callback;
}

void ImageSlider::onDisplay()
{
#if 0 // DEBUG, paints slider area
glColor3f(0.4f, 0.5f, 0.1f);
glRecti(fSliderArea.getX(), fSliderArea.getY(), fSliderArea.getX()+fSliderArea.getWidth(), fSliderArea.getY()+fSliderArea.getHeight());
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
#endif

const float normValue = (fValue - fMinimum) / (fMaximum - fMinimum);

int x, y;

if (fStartPos.getY() == fEndPos.getY())
{
// horizontal
if (fInverted)
x = fEndPos.getX() - static_cast<int>(normValue*static_cast<float>(fEndPos.getX()-fStartPos.getX()));
else
x = fStartPos.getX() + static_cast<int>(normValue*static_cast<float>(fEndPos.getX()-fStartPos.getX()));

y = fStartPos.getY();
}
else
{
// vertical
x = fStartPos.getX();

if (fInverted)
y = fEndPos.getY() - static_cast<int>(normValue*static_cast<float>(fEndPos.getY()-fStartPos.getY()));
else
y = fStartPos.getY() + static_cast<int>(normValue*static_cast<float>(fEndPos.getY()-fStartPos.getY()));
}

fImage.drawAt(x, y);
}
#endif

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

#if 0
bool ImageSlider::onMouse(const MouseEvent& ev)
{
if (ev.button != 1)
return false;

if (ev.press)
{
if (! fSliderArea.contains(ev.pos))
return false;

if ((ev.mod & kModifierShift) != 0 && fUsingDefault)
{
setValue(fValueDef, true);
fValueTmp = fValue;
return true;
}

float vper;
const int x = ev.pos.getX();
const int y = ev.pos.getY();

if (fStartPos.getY() == fEndPos.getY())
{
// horizontal
vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth());
}
else
{
// vertical
vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight());
}

float value;

if (fInverted)
value = fMaximum - vper * (fMaximum - fMinimum);
else
value = fMinimum + vper * (fMaximum - fMinimum);

if (value < fMinimum)
{
fValueTmp = value = fMinimum;
}
else if (value > fMaximum)
{
fValueTmp = value = fMaximum;
}
else if (d_isNotZero(fStep))
{
fValueTmp = value;
const float rest = std::fmod(value, fStep);
value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f);
}

fDragging = true;
fStartedX = x;
fStartedY = y;

if (fCallback != nullptr)
fCallback->imageSliderDragStarted(this);

setValue(value, true);

return true;
}
else if (fDragging)
{
if (fCallback != nullptr)
fCallback->imageSliderDragFinished(this);

fDragging = false;
return true;
}

return false;
}
#endif

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

#if 0
bool ImageSlider::onMotion(const MotionEvent& ev)
{
if (! fDragging)
return false;

const bool horizontal = fStartPos.getY() == fEndPos.getY();
const int x = ev.pos.getX();
const int y = ev.pos.getY();

if ((horizontal && fSliderArea.containsX(x)) || (fSliderArea.containsY(y) && ! horizontal))
{
float vper;

if (horizontal)
{
// horizontal
vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth());
}
else
{
// vertical
vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight());
}

float value;

if (fInverted)
value = fMaximum - vper * (fMaximum - fMinimum);
else
value = fMinimum + vper * (fMaximum - fMinimum);

if (value < fMinimum)
{
fValueTmp = value = fMinimum;
}
else if (value > fMaximum)
{
fValueTmp = value = fMaximum;
}
else if (d_isNotZero(fStep))
{
fValueTmp = value;
const float rest = std::fmod(value, fStep);
value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f);
}

setValue(value, true);
}
else if (horizontal)
{
if (x < fSliderArea.getX())
setValue(fInverted ? fMaximum : fMinimum, true);
else
setValue(fInverted ? fMinimum : fMaximum, true);
}
else
{
if (y < fSliderArea.getY())
setValue(fInverted ? fMaximum : fMinimum, true);
else
setValue(fInverted ? fMinimum : fMaximum, true);
}

return true;
}

void ImageSlider::_recheckArea() noexcept
{
if (fStartPos.getY() == fEndPos.getY())
{
// horizontal
fSliderArea = Rectangle<double>(fStartPos.getX(),
fStartPos.getY(),
fEndPos.getX() + static_cast<int>(fImage.getWidth()) - fStartPos.getX(),
static_cast<int>(fImage.getHeight()));
}
else
{
// vertical
fSliderArea = Rectangle<double>(fStartPos.getX(),
fStartPos.getY(),
static_cast<int>(fImage.getWidth()),
fEndPos.getY() + static_cast<int>(fImage.getHeight()) - fStartPos.getY());
}
}
#endif

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

#if 0
ImageSwitch::ImageSwitch(Widget* parentWidget, const Image& imageNormal, const Image& imageDown) noexcept
: SubWidget(parentWidget),
fImageNormal(imageNormal),
fImageDown(imageDown),
fIsDown(false),
fCallback(nullptr)
{
DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize());

setSize(fImageNormal.getSize());
}

ImageSwitch::ImageSwitch(const ImageSwitch& imageSwitch) noexcept
: SubWidget(imageSwitch.getParentWidget()),
fImageNormal(imageSwitch.fImageNormal),
fImageDown(imageSwitch.fImageDown),
fIsDown(imageSwitch.fIsDown),
fCallback(imageSwitch.fCallback)
{
DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize());

setSize(fImageNormal.getSize());
}

ImageSwitch& ImageSwitch::operator=(const ImageSwitch& imageSwitch) noexcept
{
fImageNormal = imageSwitch.fImageNormal;
fImageDown = imageSwitch.fImageDown;
fIsDown = imageSwitch.fIsDown;
fCallback = imageSwitch.fCallback;

DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize());

setSize(fImageNormal.getSize());

return *this;
}

bool ImageSwitch::isDown() const noexcept
{
return fIsDown;
}

void ImageSwitch::setDown(bool down) noexcept
{
if (fIsDown == down)
return;

fIsDown = down;
repaint();
}

void ImageSwitch::setCallback(Callback* callback) noexcept
{
fCallback = callback;
}

void ImageSwitch::onDisplay()
{
if (fIsDown)
fImageDown.draw();
else
fImageNormal.draw();
}

bool ImageSwitch::onMouse(const MouseEvent& ev)
{
if (ev.press && contains(ev.pos))
{
fIsDown = !fIsDown;

repaint();

if (fCallback != nullptr)
fCallback->imageSwitchClicked(this, fIsDown);

return true;
}

return false;
}
#endif

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

END_NAMESPACE_DGL

+ 0
- 876
dgl/src/ImageWidgets.cpp View File

@@ -1,876 +0,0 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "../ImageWidgets.hpp"
#include "WidgetPrivateData.hpp"

// TODO make this code more generic and move GL specific bits to OpenGL.cpp
#include "../OpenGL.hpp"

// TODO switch to use templated image type after merging widget-related PRs
#if defined(__GNUC__) && (__GNUC__ >= 6)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif

START_NAMESPACE_DGL

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

ImageKnob::ImageKnob(Widget* const parentWidget, const Image& image, Orientation orientation) noexcept
: SubWidget(parentWidget),
fImage(image),
fMinimum(0.0f),
fMaximum(1.0f),
fStep(0.0f),
fValue(0.5f),
fValueDef(fValue),
fValueTmp(fValue),
fUsingDefault(false),
fUsingLog(false),
fOrientation(orientation),
fRotationAngle(0),
fDragging(false),
fLastX(0),
fLastY(0),
fCallback(nullptr),
fIsImgVertical(image.getHeight() > image.getWidth()),
fImgLayerWidth(fIsImgVertical ? image.getWidth() : image.getHeight()),
fImgLayerHeight(fImgLayerWidth),
fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerHeight : image.getWidth()/fImgLayerWidth),
fIsReady(false),
fTextureId(0)
{
glGenTextures(1, &fTextureId);
setSize(fImgLayerWidth, fImgLayerHeight);
}

ImageKnob::ImageKnob(const ImageKnob& imageKnob)
: SubWidget(imageKnob.getParentWidget()),
fImage(imageKnob.fImage),
fMinimum(imageKnob.fMinimum),
fMaximum(imageKnob.fMaximum),
fStep(imageKnob.fStep),
fValue(imageKnob.fValue),
fValueDef(imageKnob.fValueDef),
fValueTmp(fValue),
fUsingDefault(imageKnob.fUsingDefault),
fUsingLog(imageKnob.fUsingLog),
fOrientation(imageKnob.fOrientation),
fRotationAngle(imageKnob.fRotationAngle),
fDragging(false),
fLastX(0),
fLastY(0),
fCallback(imageKnob.fCallback),
fIsImgVertical(imageKnob.fIsImgVertical),
fImgLayerWidth(imageKnob.fImgLayerWidth),
fImgLayerHeight(imageKnob.fImgLayerHeight),
fImgLayerCount(imageKnob.fImgLayerCount),
fIsReady(false),
fTextureId(0)
{
glGenTextures(1, &fTextureId);
setSize(fImgLayerWidth, fImgLayerHeight);
}

ImageKnob& ImageKnob::operator=(const ImageKnob& imageKnob)
{
fImage = imageKnob.fImage;
fMinimum = imageKnob.fMinimum;
fMaximum = imageKnob.fMaximum;
fStep = imageKnob.fStep;
fValue = imageKnob.fValue;
fValueDef = imageKnob.fValueDef;
fValueTmp = fValue;
fUsingDefault = imageKnob.fUsingDefault;
fUsingLog = imageKnob.fUsingLog;
fOrientation = imageKnob.fOrientation;
fRotationAngle = imageKnob.fRotationAngle;
fDragging = false;
fLastX = 0;
fLastY = 0;
fCallback = imageKnob.fCallback;
fIsImgVertical = imageKnob.fIsImgVertical;
fImgLayerWidth = imageKnob.fImgLayerWidth;
fImgLayerHeight = imageKnob.fImgLayerHeight;
fImgLayerCount = imageKnob.fImgLayerCount;
fIsReady = false;

if (fTextureId != 0)
{
glDeleteTextures(1, &fTextureId);
fTextureId = 0;
}

glGenTextures(1, &fTextureId);
setSize(fImgLayerWidth, fImgLayerHeight);

return *this;
}

ImageKnob::~ImageKnob()
{
if (fTextureId != 0)
{
glDeleteTextures(1, &fTextureId);
fTextureId = 0;
}
}

float ImageKnob::getValue() const noexcept
{
return fValue;
}

// NOTE: value is assumed to be scaled if using log
void ImageKnob::setDefault(float value) noexcept
{
fValueDef = value;
fUsingDefault = true;
}

void ImageKnob::setRange(float min, float max) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(max > min,);

if (fValue < min)
{
fValue = min;
repaint();

if (fCallback != nullptr)
{
try {
fCallback->imageKnobValueChanged(this, fValue);
} DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange < min");
}
}
else if (fValue > max)
{
fValue = max;
repaint();

if (fCallback != nullptr)
{
try {
fCallback->imageKnobValueChanged(this, fValue);
} DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange > max");
}
}

fMinimum = min;
fMaximum = max;
}

void ImageKnob::setStep(float step) noexcept
{
fStep = step;
}

// NOTE: value is assumed to be scaled if using log
void ImageKnob::setValue(float value, bool sendCallback) noexcept
{
if (d_isEqual(fValue, value))
return;

fValue = value;

if (d_isZero(fStep))
fValueTmp = value;

if (fRotationAngle == 0)
fIsReady = false;

repaint();

if (sendCallback && fCallback != nullptr)
{
try {
fCallback->imageKnobValueChanged(this, fValue);
} DISTRHO_SAFE_EXCEPTION("ImageKnob::setValue");
}
}

void ImageKnob::setUsingLogScale(bool yesNo) noexcept
{
fUsingLog = yesNo;
}

void ImageKnob::setCallback(Callback* callback) noexcept
{
fCallback = callback;
}

void ImageKnob::setOrientation(Orientation orientation) noexcept
{
if (fOrientation == orientation)
return;

fOrientation = orientation;
}

void ImageKnob::setRotationAngle(int angle)
{
if (fRotationAngle == angle)
return;

fRotationAngle = angle;
fIsReady = false;
}

void ImageKnob::setImageLayerCount(uint count) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(count > 1,);

fImgLayerCount = count;

if (fIsImgVertical)
fImgLayerHeight = fImage.getHeight()/count;
else
fImgLayerWidth = fImage.getWidth()/count;

setSize(fImgLayerWidth, fImgLayerHeight);
}

void ImageKnob::onDisplay()
{
const float normValue = ((fUsingLog ? _invlogscale(fValue) : fValue) - fMinimum) / (fMaximum - fMinimum);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, fTextureId);

if (! fIsReady)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

uint imageDataOffset = 0;

if (fRotationAngle == 0)
{
DISTRHO_SAFE_ASSERT_RETURN(fImgLayerCount > 0,);
DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,);

const uint& v1(fIsImgVertical ? fImgLayerWidth : fImgLayerHeight);
const uint& v2(fIsImgVertical ? fImgLayerHeight : fImgLayerWidth);

const uint layerDataSize = v1 * v2 * ((fImage.getFormat() == kImageFormatBGRA ||
fImage.getFormat() == kImageFormatRGBA) ? 4 : 3);
/* */ imageDataOffset = layerDataSize * uint(normValue * float(fImgLayerCount-1));
}

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0,
asOpenGLImageFormat(fImage.getFormat()), GL_UNSIGNED_BYTE, fImage.getRawData() + imageDataOffset);

fIsReady = true;
}

const int w = static_cast<int>(getWidth());
const int h = static_cast<int>(getHeight());

if (fRotationAngle != 0)
{
glPushMatrix();

const int w2 = w/2;
const int h2 = h/2;

glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f);
glRotatef(normValue*static_cast<float>(fRotationAngle), 0.0f, 0.0f, 1.0f);

Rectangle<int>(-w2, -h2, w, h).draw();

glPopMatrix();
}
else
{
Rectangle<int>(0, 0, w, h).draw();
}

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

bool ImageKnob::onMouse(const MouseEvent& ev)
{
if (ev.button != 1)
return false;

if (ev.press)
{
if (! contains(ev.pos))
return false;

if ((ev.mod & kModifierShift) != 0 && fUsingDefault)
{
setValue(fValueDef, true);
fValueTmp = fValue;
return true;
}

fDragging = true;
fLastX = ev.pos.getX();
fLastY = ev.pos.getY();

if (fCallback != nullptr)
fCallback->imageKnobDragStarted(this);

return true;
}
else if (fDragging)
{
if (fCallback != nullptr)
fCallback->imageKnobDragFinished(this);

fDragging = false;
return true;
}

return false;
}

bool ImageKnob::onMotion(const MotionEvent& ev)
{
if (! fDragging)
return false;

bool doVal = false;
float d, value = 0.0f;

if (fOrientation == ImageKnob::Horizontal)
{
if (const int movX = ev.pos.getX() - fLastX)
{
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movX));
doVal = true;
}
}
else if (fOrientation == ImageKnob::Vertical)
{
if (const int movY = fLastY - ev.pos.getY())
{
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movY));
doVal = true;
}
}

if (! doVal)
return false;

if (fUsingLog)
value = _logscale(value);

if (value < fMinimum)
{
fValueTmp = value = fMinimum;
}
else if (value > fMaximum)
{
fValueTmp = value = fMaximum;
}
else if (d_isNotZero(fStep))
{
fValueTmp = value;
const float rest = std::fmod(value, fStep);
value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f);
}

setValue(value, true);

fLastX = ev.pos.getX();
fLastY = ev.pos.getY();

return true;
}

bool ImageKnob::onScroll(const ScrollEvent& ev)
{
if (! contains(ev.pos))
return false;

const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
float value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * 10.f * ev.delta.getY());

if (fUsingLog)
value = _logscale(value);

if (value < fMinimum)
{
fValueTmp = value = fMinimum;
}
else if (value > fMaximum)
{
fValueTmp = value = fMaximum;
}
else if (d_isNotZero(fStep))
{
fValueTmp = value;
const float rest = std::fmod(value, fStep);
value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f);
}

setValue(value, true);
return true;
}

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

float ImageKnob::_logscale(float value) const
{
const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum);
const float a = fMaximum/std::exp(fMaximum*b);
return a * std::exp(b*value);
}

float ImageKnob::_invlogscale(float value) const
{
const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum);
const float a = fMaximum/std::exp(fMaximum*b);
return std::log(value/a)/b;
}

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

ImageSlider::ImageSlider(Widget* const parentWidget, const Image& image) noexcept
: SubWidget(parentWidget),
fImage(image),
fMinimum(0.0f),
fMaximum(1.0f),
fStep(0.0f),
fValue(0.5f),
fValueDef(fValue),
fValueTmp(fValue),
fUsingDefault(false),
fDragging(false),
fInverted(false),
fValueIsSet(false),
fStartedX(0),
fStartedY(0),
fCallback(nullptr),
fStartPos(),
fEndPos(),
fSliderArea()
{
setNeedsFullViewportDrawing();
}

float ImageSlider::getValue() const noexcept
{
return fValue;
}

void ImageSlider::setValue(float value, bool sendCallback) noexcept
{
if (! fValueIsSet)
fValueIsSet = true;

if (d_isEqual(fValue, value))
return;

fValue = value;

if (d_isZero(fStep))
fValueTmp = value;

repaint();

if (sendCallback && fCallback != nullptr)
{
try {
fCallback->imageSliderValueChanged(this, fValue);
} DISTRHO_SAFE_EXCEPTION("ImageSlider::setValue");
}
}

void ImageSlider::setStartPos(const Point<int>& startPos) noexcept
{
fStartPos = startPos;
_recheckArea();
}

void ImageSlider::setStartPos(int x, int y) noexcept
{
setStartPos(Point<int>(x, y));
}

void ImageSlider::setEndPos(const Point<int>& endPos) noexcept
{
fEndPos = endPos;
_recheckArea();
}

void ImageSlider::setEndPos(int x, int y) noexcept
{
setEndPos(Point<int>(x, y));
}

void ImageSlider::setInverted(bool inverted) noexcept
{
if (fInverted == inverted)
return;

fInverted = inverted;
repaint();
}

void ImageSlider::setDefault(float value) noexcept
{
fValueDef = value;
fUsingDefault = true;
}

void ImageSlider::setRange(float min, float max) noexcept
{
fMinimum = min;
fMaximum = max;

if (fValue < min)
{
fValue = min;
repaint();

if (fCallback != nullptr && fValueIsSet)
{
try {
fCallback->imageSliderValueChanged(this, fValue);
} DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange < min");
}
}
else if (fValue > max)
{
fValue = max;
repaint();

if (fCallback != nullptr && fValueIsSet)
{
try {
fCallback->imageSliderValueChanged(this, fValue);
} DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange > max");
}
}
}

void ImageSlider::setStep(float step) noexcept
{
fStep = step;
}

void ImageSlider::setCallback(Callback* callback) noexcept
{
fCallback = callback;
}

void ImageSlider::onDisplay()
{
#if 0 // DEBUG, paints slider area
glColor3f(0.4f, 0.5f, 0.1f);
glRecti(fSliderArea.getX(), fSliderArea.getY(), fSliderArea.getX()+fSliderArea.getWidth(), fSliderArea.getY()+fSliderArea.getHeight());
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
#endif

const float normValue = (fValue - fMinimum) / (fMaximum - fMinimum);

int x, y;

if (fStartPos.getY() == fEndPos.getY())
{
// horizontal
if (fInverted)
x = fEndPos.getX() - static_cast<int>(normValue*static_cast<float>(fEndPos.getX()-fStartPos.getX()));
else
x = fStartPos.getX() + static_cast<int>(normValue*static_cast<float>(fEndPos.getX()-fStartPos.getX()));

y = fStartPos.getY();
}
else
{
// vertical
x = fStartPos.getX();

if (fInverted)
y = fEndPos.getY() - static_cast<int>(normValue*static_cast<float>(fEndPos.getY()-fStartPos.getY()));
else
y = fStartPos.getY() + static_cast<int>(normValue*static_cast<float>(fEndPos.getY()-fStartPos.getY()));
}

fImage.drawAt(x, y);
}

bool ImageSlider::onMouse(const MouseEvent& ev)
{
if (ev.button != 1)
return false;

if (ev.press)
{
if (! fSliderArea.contains(ev.pos))
return false;

if ((ev.mod & kModifierShift) != 0 && fUsingDefault)
{
setValue(fValueDef, true);
fValueTmp = fValue;
return true;
}

float vper;
const int x = ev.pos.getX();
const int y = ev.pos.getY();

if (fStartPos.getY() == fEndPos.getY())
{
// horizontal
vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth());
}
else
{
// vertical
vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight());
}

float value;

if (fInverted)
value = fMaximum - vper * (fMaximum - fMinimum);
else
value = fMinimum + vper * (fMaximum - fMinimum);

if (value < fMinimum)
{
fValueTmp = value = fMinimum;
}
else if (value > fMaximum)
{
fValueTmp = value = fMaximum;
}
else if (d_isNotZero(fStep))
{
fValueTmp = value;
const float rest = std::fmod(value, fStep);
value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f);
}

fDragging = true;
fStartedX = x;
fStartedY = y;

if (fCallback != nullptr)
fCallback->imageSliderDragStarted(this);

setValue(value, true);

return true;
}
else if (fDragging)
{
if (fCallback != nullptr)
fCallback->imageSliderDragFinished(this);

fDragging = false;
return true;
}

return false;
}

bool ImageSlider::onMotion(const MotionEvent& ev)
{
if (! fDragging)
return false;

const bool horizontal = fStartPos.getY() == fEndPos.getY();
const int x = ev.pos.getX();
const int y = ev.pos.getY();

if ((horizontal && fSliderArea.containsX(x)) || (fSliderArea.containsY(y) && ! horizontal))
{
float vper;

if (horizontal)
{
// horizontal
vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth());
}
else
{
// vertical
vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight());
}

float value;

if (fInverted)
value = fMaximum - vper * (fMaximum - fMinimum);
else
value = fMinimum + vper * (fMaximum - fMinimum);

if (value < fMinimum)
{
fValueTmp = value = fMinimum;
}
else if (value > fMaximum)
{
fValueTmp = value = fMaximum;
}
else if (d_isNotZero(fStep))
{
fValueTmp = value;
const float rest = std::fmod(value, fStep);
value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f);
}

setValue(value, true);
}
else if (horizontal)
{
if (x < fSliderArea.getX())
setValue(fInverted ? fMaximum : fMinimum, true);
else
setValue(fInverted ? fMinimum : fMaximum, true);
}
else
{
if (y < fSliderArea.getY())
setValue(fInverted ? fMaximum : fMinimum, true);
else
setValue(fInverted ? fMinimum : fMaximum, true);
}

return true;
}

void ImageSlider::_recheckArea() noexcept
{
if (fStartPos.getY() == fEndPos.getY())
{
// horizontal
fSliderArea = Rectangle<double>(fStartPos.getX(),
fStartPos.getY(),
fEndPos.getX() + static_cast<int>(fImage.getWidth()) - fStartPos.getX(),
static_cast<int>(fImage.getHeight()));
}
else
{
// vertical
fSliderArea = Rectangle<double>(fStartPos.getX(),
fStartPos.getY(),
static_cast<int>(fImage.getWidth()),
fEndPos.getY() + static_cast<int>(fImage.getHeight()) - fStartPos.getY());
}
}

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

ImageSwitch::ImageSwitch(Widget* parentWidget, const Image& imageNormal, const Image& imageDown) noexcept
: SubWidget(parentWidget),
fImageNormal(imageNormal),
fImageDown(imageDown),
fIsDown(false),
fCallback(nullptr)
{
DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize());

setSize(fImageNormal.getSize());
}

ImageSwitch::ImageSwitch(const ImageSwitch& imageSwitch) noexcept
: SubWidget(imageSwitch.getParentWidget()),
fImageNormal(imageSwitch.fImageNormal),
fImageDown(imageSwitch.fImageDown),
fIsDown(imageSwitch.fIsDown),
fCallback(imageSwitch.fCallback)
{
DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize());

setSize(fImageNormal.getSize());
}

ImageSwitch& ImageSwitch::operator=(const ImageSwitch& imageSwitch) noexcept
{
fImageNormal = imageSwitch.fImageNormal;
fImageDown = imageSwitch.fImageDown;
fIsDown = imageSwitch.fIsDown;
fCallback = imageSwitch.fCallback;

DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize());

setSize(fImageNormal.getSize());

return *this;
}

bool ImageSwitch::isDown() const noexcept
{
return fIsDown;
}

void ImageSwitch::setDown(bool down) noexcept
{
if (fIsDown == down)
return;

fIsDown = down;
repaint();
}

void ImageSwitch::setCallback(Callback* callback) noexcept
{
fCallback = callback;
}

void ImageSwitch::onDisplay()
{
if (fIsDown)
fImageDown.draw();
else
fImageNormal.draw();
}

bool ImageSwitch::onMouse(const MouseEvent& ev)
{
if (ev.press && contains(ev.pos))
{
fIsDown = !fIsDown;

repaint();

if (fCallback != nullptr)
fCallback->imageSwitchClicked(this, fIsDown);

return true;
}

return false;
}

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

END_NAMESPACE_DGL

#if defined(__GNUC__) && (__GNUC__ >= 6)
# pragma GCC diagnostic pop
#endif

Loading…
Cancel
Save