Mercurial > hg > pub > prymula > com
diff DPF-Prymula-audioplugins/dpf/dgl/src/EventHandlers.cpp @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DPF-Prymula-audioplugins/dpf/dgl/src/EventHandlers.cpp Mon Oct 16 21:53:34 2023 +0200 @@ -0,0 +1,684 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2022 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 "../EventHandlers.hpp" +#include "../SubWidget.hpp" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +struct ButtonEventHandler::PrivateData { + ButtonEventHandler* const self; + SubWidget* const widget; + ButtonEventHandler::Callback* internalCallback; + ButtonEventHandler::Callback* userCallback; + + int button; + int state; + bool checkable; + bool checked; + + Point<double> lastClickPos; + Point<double> lastMotionPos; + + PrivateData(ButtonEventHandler* const s, SubWidget* const w) + : self(s), + widget(w), + internalCallback(nullptr), + userCallback(nullptr), + button(-1), + state(kButtonStateDefault), + checkable(false), + checked(false), + lastClickPos(0, 0), + lastMotionPos(0, 0) {} + + bool mouseEvent(const Widget::MouseEvent& ev) + { + lastClickPos = ev.pos; + + // button was released, handle it now + if (button != -1 && ! ev.press) + { + DISTRHO_SAFE_ASSERT(state & kButtonStateActive); + + // release button + const int button2 = button; + button = -1; + + const int state2 = state; + state &= ~kButtonStateActive; + + self->stateChanged(static_cast<State>(state), static_cast<State>(state2)); + widget->repaint(); + + // cursor was moved outside the button bounds, ignore click + if (! widget->contains(ev.pos)) + return true; + + // still on bounds, register click + if (checkable) + checked = !checked; + + if (internalCallback != nullptr) + internalCallback->buttonClicked(widget, button2); + else if (userCallback != nullptr) + userCallback->buttonClicked(widget, button2); + + return true; + } + + // button was pressed, wait for release + if (ev.press && widget->contains(ev.pos)) + { + const int state2 = state; + button = static_cast<int>(ev.button); + state |= kButtonStateActive; + self->stateChanged(static_cast<State>(state), static_cast<State>(state2)); + widget->repaint(); + return true; + } + + return false; + } + + bool motionEvent(const Widget::MotionEvent& ev) + { + // keep pressed + if (button != -1) + { + lastMotionPos = ev.pos; + return true; + } + + bool ret = false; + + if (widget->contains(ev.pos)) + { + // check if entering hover + if ((state & kButtonStateHover) == 0x0) + { + const int state2 = state; + state |= kButtonStateHover; + ret = widget->contains(lastMotionPos); + self->stateChanged(static_cast<State>(state), static_cast<State>(state2)); + widget->repaint(); + } + } + else + { + // check if exiting hover + if (state & kButtonStateHover) + { + const int state2 = state; + state &= ~kButtonStateHover; + ret = widget->contains(lastMotionPos); + self->stateChanged(static_cast<State>(state), static_cast<State>(state2)); + widget->repaint(); + } + } + + lastMotionPos = ev.pos; + return ret; + } + + void setActive(const bool active2, const bool sendCallback) noexcept + { + const bool active = state & kButtonStateActive; + if (active == active2) + return; + + state |= kButtonStateActive; + widget->repaint(); + + 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 + { + if (checked == checked2) + return; + + checked = checked2; + widget->repaint(); + + if (sendCallback) + { + if (internalCallback != nullptr) + internalCallback->buttonClicked(widget, -1); + else if (userCallback != nullptr) + userCallback->buttonClicked(widget, -1); + } + } + + DISTRHO_DECLARE_NON_COPYABLE(PrivateData) +}; + +// -------------------------------------------------------------------------------------------------------------------- + +ButtonEventHandler::ButtonEventHandler(SubWidget* const self) + : pData(new PrivateData(this, self)) {} + +ButtonEventHandler::~ButtonEventHandler() +{ + delete pData; +} + +bool ButtonEventHandler::isActive() noexcept +{ + return pData->state & kButtonStateActive; +} + +void ButtonEventHandler::setActive(const bool active, const bool sendCallback) noexcept +{ + pData->setActive(active, sendCallback); +} + +bool ButtonEventHandler::isChecked() const noexcept +{ + return pData->checked; +} + +void ButtonEventHandler::setChecked(const bool checked, const bool sendCallback) noexcept +{ + pData->setChecked(checked, sendCallback); +} + +bool ButtonEventHandler::isCheckable() const noexcept +{ + return pData->checkable; +} + +void ButtonEventHandler::setCheckable(const bool checkable) noexcept +{ + if (pData->checkable == checkable) + return; + + pData->checkable = checkable; +} + +Point<double> ButtonEventHandler::getLastClickPosition() const noexcept +{ + return pData->lastClickPos; +} + +Point<double> ButtonEventHandler::getLastMotionPosition() const noexcept +{ + return pData->lastMotionPos; +} + +void ButtonEventHandler::setCallback(Callback* const callback) noexcept +{ + pData->userCallback = callback; +} + +bool ButtonEventHandler::mouseEvent(const Widget::MouseEvent& ev) +{ + return pData->mouseEvent(ev); +} + +bool ButtonEventHandler::motionEvent(const Widget::MotionEvent& ev) +{ + return pData->motionEvent(ev); +} + +ButtonEventHandler::State ButtonEventHandler::getState() const noexcept +{ + return static_cast<State>(pData->state); +} + +void ButtonEventHandler::clearState() noexcept +{ + pData->state = kButtonStateDefault; +} + +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 accel; + float minimum; + float maximum; + float step; + float value; + float valueDef; + float valueTmp; + bool usingDefault; + bool usingLog; + Orientation orientation; + int state; + + double lastX; + double lastY; + uint lastClickTime; + + PrivateData(KnobEventHandler* const s, SubWidget* const w) + : self(s), + widget(w), + callback(nullptr), + accel(200.f), + minimum(0.f), + maximum(1.f), + step(0.0f), + value(0.5f), + valueDef(value), + valueTmp(value), + usingDefault(false), + usingLog(false), + orientation(Vertical), + state(kKnobStateDefault), + lastX(0.0), + lastY(0.0), + lastClickTime(0) {} + + PrivateData(KnobEventHandler* const s, SubWidget* const w, PrivateData* const other) + : self(s), + widget(w), + callback(other->callback), + accel(other->accel), + 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), + lastClickTime(0) {} + + void assignFrom(PrivateData* const other) + { + callback = other->callback; + accel = other->accel; + 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; + lastClickTime = 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, const double scaleFactor) + { + 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; + } + + lastX = ev.pos.getX() / scaleFactor; + lastY = ev.pos.getY() / scaleFactor; + + if (lastClickTime > 0 && ev.time > lastClickTime && ev.time - lastClickTime <= 300) + { + lastClickTime = 0; + + if (callback != nullptr) + callback->knobDoubleClicked(widget); + + return true; + } + + lastClickTime = ev.time; + state |= kKnobStateDragging; + 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, const double scaleFactor) + { + if ((state & kKnobStateDragging) == 0x0) + return false; + + float movDiff; + + switch (orientation) + { + case Horizontal: + movDiff = ev.pos.getX() / scaleFactor - lastX; + break; + case Vertical: + movDiff = lastY - ev.pos.getY() / scaleFactor; + break; + case Both: + { + const float movDiffX = ev.pos.getX() / scaleFactor - lastX; + const float movDiffY = lastY - ev.pos.getY() / scaleFactor; + movDiff = std::abs(movDiffX) > std::abs(movDiffY) ? movDiffX : movDiffY; + } + break; + default: + return false; + } + + if (d_isZero(movDiff)) + return true; + + const float divisor = (ev.mod & kModifierControl) ? accel * 10.f : accel; + valueTmp += (maximum - minimum) / divisor * movDiff; + + if (usingLog) + valueTmp = logscale(valueTmp); + + float value2; + bool valueChanged = false; + + if (valueTmp < minimum) + { + valueTmp = value2 = minimum; + valueChanged = true; + } + else if (valueTmp > maximum) + { + valueTmp = value2 = maximum; + valueChanged = true; + } + else + { + if (d_isNotZero(step)) + { + if (std::abs(valueTmp - value) >= step) + { + const float rest = std::fmod(valueTmp, step); + valueChanged = true; + value2 = valueTmp - rest; + + if (rest < 0 && rest < step * -0.5f) + value2 -= step; + else if (rest > 0 && rest > step * 0.5f) + value2 += step; + + if (value2 < minimum) + value2 = minimum; + else if (value2 > maximum) + value2 = maximum; + } + } + else + { + value2 = valueTmp; + valueChanged = true; + } + } + + if (valueChanged) + setValue(value2, true); + + lastX = ev.pos.getX() / scaleFactor; + lastY = ev.pos.getY() / scaleFactor; + + 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) ? accel * 10.f : accel; + 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; + } + + bool setValue(const float value2, const bool sendCallback) + { + if (d_isEqual(value, value2)) + return false; + + valueTmp = value = value2; + widget->repaint(); + + if (sendCallback && callback != nullptr) + { + try { + callback->knobValueChanged(widget, value); + } DISTRHO_SAFE_EXCEPTION("KnobEventHandler::setValue"); + } + + return true; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +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; +} + +bool KnobEventHandler::isInteger() const noexcept +{ + return d_isEqual(pData->step, 1.f); +} + +float KnobEventHandler::getValue() const noexcept +{ + return pData->value; +} + +bool KnobEventHandler::setValue(const float value, const bool sendCallback) noexcept +{ + return 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 +{ + pData->orientation = orientation; +} + +void KnobEventHandler::setCallback(Callback* const callback) noexcept +{ + pData->callback = callback; +} + +void KnobEventHandler::setMouseDeceleration(float accel) noexcept +{ + pData->accel = accel; +} + +bool KnobEventHandler::mouseEvent(const Widget::MouseEvent& ev, const double scaleFactor) +{ + return pData->mouseEvent(ev, scaleFactor); +} + +bool KnobEventHandler::motionEvent(const Widget::MotionEvent& ev, const double scaleFactor) +{ + return pData->motionEvent(ev, scaleFactor); +} + +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