/* * DISTRHO Plugin Framework (DPF) * Copyright (C) 2012-2015 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "../ImageWidgets.hpp" #include START_NAMESPACE_DGL // ----------------------------------------------------------------------- ImageAboutWindow::ImageAboutWindow(Window& parent, const Image& image) : Window(parent.getApp(), parent), Widget((Window&)*this), fImgBackground(image), leakDetector_ImageAboutWindow() { Window::setResizable(false); Window::setSize(image.getSize()); Window::setTitle("About"); } ImageAboutWindow::ImageAboutWindow(Widget* widget, const Image& image) : Window(widget->getParentApp(), widget->getParentWindow()), Widget((Window&)*this), fImgBackground(image), leakDetector_ImageAboutWindow() { Window::setResizable(false); Window::setSize(image.getSize()); Window::setTitle("About"); } void ImageAboutWindow::setImage(const Image& image) { if (fImgBackground == image) return; fImgBackground = image; Window::setSize(image.getSize()); } void ImageAboutWindow::onDisplay() { fImgBackground.draw(); } bool ImageAboutWindow::onKeyboard(const KeyboardEvent& ev) { if (ev.press && ev.key == CHAR_ESCAPE) { Window::close(); return true; } return false; } bool ImageAboutWindow::onMouse(const MouseEvent& ev) { if (ev.press) { Window::close(); return true; } return false; } void ImageAboutWindow::onReshape(uint width, uint height) { Widget::setSize(width, height); Window::onReshape(width, height); } // ----------------------------------------------------------------------- ImageButton::ImageButton(Window& parent, const Image& image) noexcept : Widget(parent), fImageNormal(image), fImageHover(image), fImageDown(image), fCurButton(-1), fCurState(0), fCallback(nullptr), leakDetector_ImageButton() { setSize(image.getSize()); } ImageButton::ImageButton(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept : Widget(parent), fImageNormal(imageNormal), fImageHover(imageNormal), fImageDown(imageDown), fCurButton(-1), fCurState(0), fCallback(nullptr), leakDetector_ImageButton() { DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); setSize(imageNormal.getSize()); } ImageButton::ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown) noexcept : Widget(parent), fImageNormal(imageNormal), fImageHover(imageHover), fImageDown(imageDown), fCurButton(-1), fCurState(0), fCallback(nullptr), leakDetector_ImageButton() { DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize()); setSize(imageNormal.getSize()); } ImageButton::ImageButton(Widget* widget, const Image& image) noexcept : Widget(widget->getParentWindow()), fImageNormal(image), fImageHover(image), fImageDown(image), fCurButton(-1), fCurState(0), fCallback(nullptr), leakDetector_ImageButton() { setSize(image.getSize()); } ImageButton::ImageButton(Widget* widget, const Image& imageNormal, const Image& imageDown) noexcept : Widget(widget->getParentWindow()), fImageNormal(imageNormal), fImageHover(imageNormal), fImageDown(imageDown), fCurButton(-1), fCurState(0), fCallback(nullptr), leakDetector_ImageButton() { DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); setSize(imageNormal.getSize()); } ImageButton::ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown) noexcept : Widget(widget->getParentWindow()), fImageNormal(imageNormal), fImageHover(imageHover), fImageDown(imageDown), fCurButton(-1), fCurState(0), fCallback(nullptr), leakDetector_ImageButton() { DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize()); setSize(imageNormal.getSize()); } void ImageButton::setCallback(Callback* callback) noexcept { fCallback = callback; } void ImageButton::onDisplay() { switch (fCurState) { case 2: fImageDown.draw(); break; case 1: fImageHover.draw(); break; default: fImageNormal.draw(); break; } } bool ImageButton::onMouse(const MouseEvent& ev) { // button was released, handle it now if (fCurButton != -1 && ! ev.press) { DISTRHO_SAFE_ASSERT(fCurState == 2); // release button const int button = fCurButton; fCurButton = -1; // cursor was moved outside the button bounds, ignore click if (! contains(ev.pos)) { fCurState = 0; repaint(); return true; } // still on bounds, register click fCurState = 1; repaint(); if (fCallback != nullptr) fCallback->imageButtonClicked(this, button); return true; } // button was pressed, wait for release if (ev.press && contains(ev.pos)) { fCurButton = ev.button; fCurState = 2; repaint(); return true; } return false; } bool ImageButton::onMotion(const MotionEvent& ev) { // keep pressed if (fCurButton != -1) return true; if (contains(ev.pos)) { // check if entering hover if (fCurState == 0) { fCurState = 1; repaint(); return true; } } else { // check if exiting hover if (fCurState == 1) { fCurState = 0; repaint(); return true; } } return false; } // ----------------------------------------------------------------------- ImageKnob::ImageKnob(Window& parent, const Image& image, Orientation orientation) noexcept : Widget(parent), 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), leakDetector_ImageKnob() { glGenTextures(1, &fTextureId); setSize(fImgLayerWidth, fImgLayerHeight); } ImageKnob::ImageKnob(Widget* widget, const Image& image, Orientation orientation) noexcept : Widget(widget->getParentWindow()), 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), leakDetector_ImageKnob() { glGenTextures(1, &fTextureId); setSize(fImgLayerWidth, fImgLayerHeight); } ImageKnob::ImageKnob(const ImageKnob& imageKnob) : Widget(imageKnob.getParentWindow()), 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), leakDetector_ImageKnob() { 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() == GL_BGRA || fImage.getFormat() == GL_RGBA) ? 4 : 3); /* */ imageDataOffset = layerDataSize * uint(normValue * float(fImgLayerCount-1)); } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast(getWidth()), static_cast(getHeight()), 0, fImage.getFormat(), fImage.getType(), fImage.getRawData() + imageDataOffset); fIsReady = true; } const int w = static_cast(getWidth()); const int h = static_cast(getHeight()); if (fRotationAngle != 0) { glPushMatrix(); const int w2 = w/2; const int h2 = h/2; glTranslatef(static_cast(w2), static_cast(h2), 0.0f); glRotatef(normValue*static_cast(fRotationAngle), 0.0f, 0.0f, 1.0f); Rectangle(-w2, -h2, w, h).draw(); glPopMatrix(); } else { Rectangle(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 & MODIFIER_SHIFT) != 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 & MODIFIER_CTRL) ? 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 & MODIFIER_CTRL) ? 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 & MODIFIER_CTRL) ? 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(Window& parent, const Image& image) noexcept : Widget(parent), fImage(image), fMinimum(0.0f), fMaximum(1.0f), fStep(0.0f), fValue(0.5f), fValueTmp(fValue), fDragging(false), fInverted(false), fStartedX(0), fStartedY(0), fCallback(nullptr), fStartPos(), fEndPos(), fSliderArea(), leakDetector_ImageSlider() { fNeedsFullViewport = true; } ImageSlider::ImageSlider(Widget* widget, const Image& image) noexcept : Widget(widget->getParentWindow()), fImage(image), fMinimum(0.0f), fMaximum(1.0f), fStep(0.0f), fValue(0.5f), fValueTmp(fValue), fDragging(false), fInverted(false), fStartedX(0), fStartedY(0), fCallback(nullptr), fStartPos(), fEndPos(), fSliderArea(), leakDetector_ImageSlider() { fNeedsFullViewport = true; } ImageSlider::ImageSlider(const ImageSlider& imageSlider) noexcept : Widget(imageSlider.getParentWindow()), fImage(imageSlider.fImage), fMinimum(imageSlider.fMinimum), fMaximum(imageSlider.fMaximum), fStep(imageSlider.fStep), fValue(imageSlider.fValue), fValueTmp(fValue), fDragging(false), fInverted(imageSlider.fInverted), fStartedX(0), fStartedY(0), fCallback(imageSlider.fCallback), fStartPos(imageSlider.fStartPos), fEndPos(imageSlider.fEndPos), fSliderArea(imageSlider.fSliderArea), leakDetector_ImageSlider() { fNeedsFullViewport = true; } ImageSlider& ImageSlider::operator=(const ImageSlider& imageSlider) noexcept { fImage = imageSlider.fImage; fMinimum = imageSlider.fMinimum; fMaximum = imageSlider.fMaximum; fStep = imageSlider.fStep; fValue = imageSlider.fValue; fValueTmp = fValue; fDragging = false; fInverted = imageSlider.fInverted; fStartedX = 0; fStartedY = 0; fCallback = imageSlider.fCallback; fStartPos = imageSlider.fStartPos; fEndPos = imageSlider.fEndPos; fSliderArea = imageSlider.fSliderArea; return *this; } float ImageSlider::getValue() const noexcept { return fValue; } void ImageSlider::setStartPos(const Point& startPos) noexcept { fStartPos = startPos; _recheckArea(); } void ImageSlider::setStartPos(int x, int y) noexcept { setStartPos(Point(x, y)); } void ImageSlider::setEndPos(const Point& endPos) noexcept { fEndPos = endPos; _recheckArea(); } void ImageSlider::setEndPos(int x, int y) noexcept { setEndPos(Point(x, y)); } void ImageSlider::setInverted(bool inverted) noexcept { if (fInverted == inverted) return; fInverted = inverted; repaint(); } void ImageSlider::setRange(float min, float max) noexcept { if (fValue < min) { fValue = min; repaint(); if (fCallback != nullptr) { try { fCallback->imageSliderValueChanged(this, fValue); } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange < min"); } } else if (fValue > max) { fValue = max; repaint(); if (fCallback != nullptr) { try { fCallback->imageSliderValueChanged(this, fValue); } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange > max"); } } fMinimum = min; fMaximum = max; } void ImageSlider::setStep(float step) noexcept { fStep = step; } void ImageSlider::setValue(float value, bool sendCallback) noexcept { 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::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(normValue*static_cast(fEndPos.getX()-fStartPos.getX())); else x = fStartPos.getX() + static_cast(normValue*static_cast(fEndPos.getX()-fStartPos.getX())); y = fStartPos.getY(); } else { // vertical x = fStartPos.getX(); if (fInverted) y = fEndPos.getY() - static_cast(normValue*static_cast(fEndPos.getY()-fStartPos.getY())); else y = fStartPos.getY() + static_cast(normValue*static_cast(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; 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(fStartPos.getX(), fStartPos.getY(), fEndPos.getX() + static_cast(fImage.getWidth()) - fStartPos.getX(), static_cast(fImage.getHeight())); } else { // vertical fSliderArea = Rectangle(fStartPos.getX(), fStartPos.getY(), static_cast(fImage.getWidth()), fEndPos.getY() + static_cast(fImage.getHeight()) - fStartPos.getY()); } } // ----------------------------------------------------------------------- ImageSwitch::ImageSwitch(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept : Widget(parent), fImageNormal(imageNormal), fImageDown(imageDown), fIsDown(false), fCallback(nullptr), leakDetector_ImageSwitch() { DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); setSize(fImageNormal.getSize()); } ImageSwitch::ImageSwitch(Widget* widget, const Image& imageNormal, const Image& imageDown) noexcept : Widget(widget->getParentWindow()), fImageNormal(imageNormal), fImageDown(imageDown), fIsDown(false), fCallback(nullptr), leakDetector_ImageSwitch() { DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); setSize(fImageNormal.getSize()); } ImageSwitch::ImageSwitch(const ImageSwitch& imageSwitch) noexcept : Widget(imageSwitch.getParentWindow()), fImageNormal(imageSwitch.fImageNormal), fImageDown(imageSwitch.fImageDown), fIsDown(imageSwitch.fIsDown), fCallback(imageSwitch.fCallback), leakDetector_ImageSwitch() { 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