From 2fe6b274d15f1c73e7a6505a968eb91d077bc6ac Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 14 Jun 2021 18:38:36 +0100 Subject: [PATCH] Start of common event handlers, split off button as a start Signed-off-by: falkTX --- cmake/DPF-plugin.cmake | 1 + dgl/EventHandlers.hpp | 77 ++++++++++++ dgl/ImageBaseWidgets.hpp | 4 +- dgl/Makefile | 2 +- dgl/src/Common.hpp | 97 --------------- dgl/src/EventHandlers.cpp | 232 +++++++++++++++++++++++++++++++++++ dgl/src/ImageBaseWidgets.cpp | 51 +++++--- 7 files changed, 346 insertions(+), 118 deletions(-) create mode 100644 dgl/EventHandlers.hpp create mode 100644 dgl/src/EventHandlers.cpp diff --git a/cmake/DPF-plugin.cmake b/cmake/DPF-plugin.cmake index 678b94c6..133ede97 100644 --- a/cmake/DPF-plugin.cmake +++ b/cmake/DPF-plugin.cmake @@ -326,6 +326,7 @@ function(dpf__add_dgl_cairo) "${DPF_ROOT_DIR}/dgl/src/Application.cpp" "${DPF_ROOT_DIR}/dgl/src/ApplicationPrivateData.cpp" "${DPF_ROOT_DIR}/dgl/src/Color.cpp" + "${DPF_ROOT_DIR}/dgl/src/EventHandlers.cpp" "${DPF_ROOT_DIR}/dgl/src/Geometry.cpp" "${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp" "${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.cpp" diff --git a/dgl/EventHandlers.hpp b/dgl/EventHandlers.hpp new file mode 100644 index 00000000..fc1761a7 --- /dev/null +++ b/dgl/EventHandlers.hpp @@ -0,0 +1,77 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 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. + */ + +#ifndef DGL_EVENT_HANDLERS_HPP_INCLUDED +#define DGL_EVENT_HANDLERS_HPP_INCLUDED + +#include "Widget.hpp" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +class ButtonEventHandler +{ +public: + enum State { + kButtonStateDefault = 0x0, + kButtonStateHover = 0x1, + kButtonStateActive = 0x2, + kButtonStateActiveHover = kButtonStateActive|kButtonStateHover + }; + + class Callback + { + public: + virtual ~Callback() {} + virtual void buttonClicked(SubWidget* widget, int button) = 0; + }; + + explicit ButtonEventHandler(SubWidget* self); + ~ButtonEventHandler(); + + bool isActive() noexcept; + void setActive(bool active, bool sendCallback) noexcept; + + bool isChecked() const noexcept; + void setChecked(bool checked, bool sendCallback) noexcept; + + bool isCheckable() const noexcept; + void setCheckable(bool checkable) noexcept; + + void setCallback(Callback* callback) noexcept; + +protected: + State getState() const noexcept; + + virtual void stateChanged(State state, State oldState); + + bool mouseEvent(const Widget::MouseEvent& ev); + bool motionEvent(const Widget::MotionEvent& ev); + +private: + struct PrivateData; + PrivateData* const pData; + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ButtonEventHandler) +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_EVENT_HANDLERS_HPP_INCLUDED + diff --git a/dgl/ImageBaseWidgets.hpp b/dgl/ImageBaseWidgets.hpp index bbc34b6d..0ae59412 100644 --- a/dgl/ImageBaseWidgets.hpp +++ b/dgl/ImageBaseWidgets.hpp @@ -17,6 +17,7 @@ #ifndef DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED #define DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED +#include "EventHandlers.hpp" #include "StandaloneWindow.hpp" #include "SubWidget.hpp" @@ -47,7 +48,8 @@ private: // -------------------------------------------------------------------------------------------------------------------- template -class ImageBaseButton : public SubWidget +class ImageBaseButton : public SubWidget, + public ButtonEventHandler { public: class Callback diff --git a/dgl/Makefile b/dgl/Makefile index d064a1cc..db7049c3 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -27,6 +27,7 @@ OBJS_common = \ ../build/dgl/Application.cpp.o \ ../build/dgl/ApplicationPrivateData.cpp.o \ ../build/dgl/Color.cpp.o \ + ../build/dgl/EventHandlers.cpp.o \ ../build/dgl/Geometry.cpp.o \ ../build/dgl/ImageBase.cpp.o \ ../build/dgl/ImageBaseWidgets.cpp.o \ @@ -39,7 +40,6 @@ OBJS_common = \ ../build/dgl/WidgetPrivateData.cpp.o \ ../build/dgl/Window.cpp.o \ ../build/dgl/WindowPrivateData.cpp.o -# ../build/dgl/WindowFileBrowser.cpp.o # --------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/src/Common.hpp b/dgl/src/Common.hpp index b0f86629..408e553c 100644 --- a/dgl/src/Common.hpp +++ b/dgl/src/Common.hpp @@ -23,103 +23,6 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- -template -struct ButtonImpl { - enum State { - kStateNormal = 0, - kStateHover, - kStateDown - }; - - int button; - int state; - ImageBaseButton* const self; - - typename ImageBaseButton::Callback* callback_img; - - explicit ButtonImpl(ImageBaseButton* const s) noexcept - : button(-1), - state(kStateNormal), - self(s), - callback_img(nullptr) {} - - bool onMouse(const Widget::MouseEvent& ev) - { - // button was released, handle it now - if (button != -1 && ! ev.press) - { - DISTRHO_SAFE_ASSERT(state == kStateDown); - - // release button - const int button2 = button; - button = -1; - - // cursor was moved outside the button bounds, ignore click - if (! self->contains(ev.pos)) - { - state = kStateNormal; - self->repaint(); - return true; - } - - // still on bounds, register click - state = kStateHover; - self->repaint(); - - if (callback_img != nullptr) - callback_img->imageButtonClicked(self, button2); - - return true; - } - - // button was pressed, wait for release - if (ev.press && self->contains(ev.pos)) - { - button = static_cast(ev.button); - state = kStateDown; - self->repaint(); - return true; - } - - return false; - } - - bool onMotion(const Widget::MotionEvent& ev) - { - // keep pressed - if (button != -1) - return true; - - if (self->contains(ev.pos)) - { - // check if entering hover - if (state == kStateNormal) - { - state = kStateHover; - self->repaint(); - return true; - } - } - else - { - // check if exiting hover - if (state == kStateHover) - { - state = kStateNormal; - self->repaint(); - return true; - } - } - - return false; - } - - DISTRHO_PREVENT_HEAP_ALLOCATION - DISTRHO_DECLARE_NON_COPYABLE(ButtonImpl) -}; - -// ----------------------------------------------------------------------- - template struct ImageBaseKnob::PrivateData { ImageType image; diff --git a/dgl/src/EventHandlers.cpp b/dgl/src/EventHandlers.cpp new file mode 100644 index 00000000..87187d11 --- /dev/null +++ b/dgl/src/EventHandlers.cpp @@ -0,0 +1,232 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 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 "../EventHandlers.hpp" +#include "../SubWidget.hpp" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +struct ButtonEventHandler::PrivateData { + ButtonEventHandler* const self; + SubWidget* const widget; + ButtonEventHandler::Callback* callback; + + int button; + int state; + bool checkable; + bool checked; + + Point oldMotionPos; + + PrivateData(ButtonEventHandler* const s, SubWidget* const w) + : self(s), + widget(w), + callback(nullptr), + button(-1), + state(kButtonStateDefault), + checkable(false), + checked(false), + oldMotionPos(0, 0) {} + + bool mouseEvent(const Widget::MouseEvent& ev) + { + // 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; + + // cursor was moved outside the button bounds, ignore click + if (! widget->contains(ev.pos)) + { + self->stateChanged(static_cast(state2), static_cast(state)); + widget->repaint(); + return true; + } + + // still on bounds, register click + self->stateChanged(static_cast(state2), static_cast(state)); + widget->repaint(); + + if (checkable) + checked = !checked; + + if (callback != nullptr) + callback->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(ev.button); + state |= kButtonStateActive; + self->stateChanged(static_cast(state2), static_cast(state)); + widget->repaint(); + return true; + } + + return false; + } + + bool motionEvent(const Widget::MotionEvent& ev) + { + // keep pressed + if (button != -1) + { + oldMotionPos = 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(oldMotionPos); + self->stateChanged(static_cast(state2), static_cast(state)); + widget->repaint(); + } + } + else + { + // check if exiting hover + if (state & kButtonStateHover) + { + const int state2 = state; + state &= ~kButtonStateHover; + ret = widget->contains(oldMotionPos); + self->stateChanged(static_cast(state2), static_cast(state)); + widget->repaint(); + } + } + + oldMotionPos = 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 && callback != nullptr) + callback->buttonClicked(widget, -1); + } + + void setChecked(const bool checked2, const bool sendCallback) noexcept + { + if (checked == checked2) + return; + + checked = checked2; + widget->repaint(); + + if (sendCallback && callback != nullptr) + callback->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; +} + +void ButtonEventHandler::setCallback(Callback* const callback) noexcept +{ + pData->callback = callback; +} + +ButtonEventHandler::State ButtonEventHandler::getState() const noexcept +{ + return static_cast(pData->state); +} + +void ButtonEventHandler::stateChanged(State, State) +{ +} + +bool ButtonEventHandler::mouseEvent(const Widget::MouseEvent& ev) +{ + return pData->mouseEvent(ev); +} + +bool ButtonEventHandler::motionEvent(const Widget::MotionEvent& ev) +{ + return pData->motionEvent(ev); +} + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/ImageBaseWidgets.cpp b/dgl/src/ImageBaseWidgets.cpp index f5fc7503..06df7551 100644 --- a/dgl/src/ImageBaseWidgets.cpp +++ b/dgl/src/ImageBaseWidgets.cpp @@ -89,18 +89,25 @@ bool ImageBaseAboutWindow::onMouse(const MouseEvent& ev) // -------------------------------------------------------------------------------------------------------------------- template -struct ImageBaseButton::PrivateData { - ButtonImpl impl; +struct ImageBaseButton::PrivateData : public ButtonEventHandler::Callback { + ImageBaseButton::Callback* callback; ImageType imageNormal; ImageType imageHover; ImageType imageDown; - PrivateData(ImageBaseButton* const s, const ImageType& normal, const ImageType& hover, const ImageType& down) - : impl(s), + PrivateData(const ImageType& normal, const ImageType& hover, const ImageType& down) + : callback(nullptr), imageNormal(normal), imageHover(hover), imageDown(down) {} + void buttonClicked(SubWidget* widget, int button) override + { + if (callback != nullptr) + if (ImageBaseButton* const imageButton = dynamic_cast(widget)) + callback->imageButtonClicked(imageButton, button); + } + DISTRHO_DECLARE_NON_COPYABLE(PrivateData) }; @@ -109,28 +116,34 @@ struct ImageBaseButton::PrivateData { template ImageBaseButton::ImageBaseButton(Widget* const parentWidget, const ImageType& image) : SubWidget(parentWidget), - pData(new PrivateData(this, image, image, image)) + ButtonEventHandler(this), + pData(new PrivateData(image, image, image)) { + ButtonEventHandler::setCallback(pData); setSize(image.getSize()); } template ImageBaseButton::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown) : SubWidget(parentWidget), - pData(new PrivateData(this, imageNormal, imageNormal, imageDown)) + ButtonEventHandler(this), + pData(new PrivateData(imageNormal, imageNormal, imageDown)) { DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); + ButtonEventHandler::setCallback(pData); setSize(imageNormal.getSize()); } template ImageBaseButton::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown) : SubWidget(parentWidget), - pData(new PrivateData(this, imageNormal, imageHover, imageDown)) + ButtonEventHandler(this), + pData(new PrivateData(imageNormal, imageHover, imageDown)) { DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize()); + ButtonEventHandler::setCallback(pData); setSize(imageNormal.getSize()); } @@ -143,7 +156,7 @@ ImageBaseButton::~ImageBaseButton() template void ImageBaseButton::setCallback(Callback* callback) noexcept { - pData->impl.callback_img = callback; + pData->callback = callback; } template @@ -151,30 +164,30 @@ void ImageBaseButton::onDisplay() { const GraphicsContext& context(getGraphicsContext()); - switch (pData->impl.state) - { - case ButtonImpl::kStateDown: + const State state = ButtonEventHandler::getState(); + + if (state & kButtonStateActive) pData->imageDown.draw(context); - break; - case ButtonImpl::kStateHover: + else if (state & kButtonStateHover) pData->imageHover.draw(context); - break; - default: + else pData->imageNormal.draw(context); - break; - } } template bool ImageBaseButton::onMouse(const MouseEvent& ev) { - return pData->impl.onMouse(ev); + if (SubWidget::onMouse(ev)) + return true; + return ButtonEventHandler::mouseEvent(ev); } template bool ImageBaseButton::onMotion(const MotionEvent& ev) { - return pData->impl.onMotion(ev); + if (SubWidget::onMotion(ev)) + return true; + return ButtonEventHandler::motionEvent(ev); } // --------------------------------------------------------------------------------------------------------------------