Signed-off-by: falkTX <falktx@falktx.com>pull/292/head
| @@ -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" | |||
| @@ -0,0 +1,77 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| * permission notice appear in all copies. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #ifndef DGL_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 | |||
| @@ -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 ImageType> | |||
| class ImageBaseButton : public SubWidget | |||
| class ImageBaseButton : public SubWidget, | |||
| public ButtonEventHandler | |||
| { | |||
| public: | |||
| class Callback | |||
| @@ -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 | |||
| # --------------------------------------------------------------------------------------------------------------------- | |||
| @@ -23,103 +23,6 @@ START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| template <class ImageType> | |||
| struct ButtonImpl { | |||
| enum State { | |||
| kStateNormal = 0, | |||
| kStateHover, | |||
| kStateDown | |||
| }; | |||
| int button; | |||
| int state; | |||
| ImageBaseButton<ImageType>* const self; | |||
| typename ImageBaseButton<ImageType>::Callback* callback_img; | |||
| explicit ButtonImpl(ImageBaseButton<ImageType>* 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<int>(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 <class ImageType> | |||
| struct ImageBaseKnob<ImageType>::PrivateData { | |||
| ImageType image; | |||
| @@ -0,0 +1,232 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| * permission notice appear in all copies. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #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<double> 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<State>(state2), static_cast<State>(state)); | |||
| widget->repaint(); | |||
| return true; | |||
| } | |||
| // still on bounds, register click | |||
| self->stateChanged(static_cast<State>(state2), static_cast<State>(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<int>(ev.button); | |||
| state |= kButtonStateActive; | |||
| self->stateChanged(static_cast<State>(state2), static_cast<State>(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<State>(state2), static_cast<State>(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<State>(state2), static_cast<State>(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<State>(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 | |||
| @@ -89,18 +89,25 @@ bool ImageBaseAboutWindow<ImageType>::onMouse(const MouseEvent& ev) | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| template <class ImageType> | |||
| struct ImageBaseButton<ImageType>::PrivateData { | |||
| ButtonImpl<ImageType> impl; | |||
| struct ImageBaseButton<ImageType>::PrivateData : public ButtonEventHandler::Callback { | |||
| ImageBaseButton<ImageType>::Callback* callback; | |||
| ImageType imageNormal; | |||
| ImageType imageHover; | |||
| ImageType imageDown; | |||
| PrivateData(ImageBaseButton<ImageType>* 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<ImageBaseButton*>(widget)) | |||
| callback->imageButtonClicked(imageButton, button); | |||
| } | |||
| DISTRHO_DECLARE_NON_COPYABLE(PrivateData) | |||
| }; | |||
| @@ -109,28 +116,34 @@ struct ImageBaseButton<ImageType>::PrivateData { | |||
| template <class ImageType> | |||
| ImageBaseButton<ImageType>::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 <class ImageType> | |||
| ImageBaseButton<ImageType>::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 <class ImageType> | |||
| ImageBaseButton<ImageType>::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<ImageType>::~ImageBaseButton() | |||
| template <class ImageType> | |||
| void ImageBaseButton<ImageType>::setCallback(Callback* callback) noexcept | |||
| { | |||
| pData->impl.callback_img = callback; | |||
| pData->callback = callback; | |||
| } | |||
| template <class ImageType> | |||
| @@ -151,30 +164,30 @@ void ImageBaseButton<ImageType>::onDisplay() | |||
| { | |||
| const GraphicsContext& context(getGraphicsContext()); | |||
| switch (pData->impl.state) | |||
| { | |||
| case ButtonImpl<ImageType>::kStateDown: | |||
| const State state = ButtonEventHandler::getState(); | |||
| if (state & kButtonStateActive) | |||
| pData->imageDown.draw(context); | |||
| break; | |||
| case ButtonImpl<ImageType>::kStateHover: | |||
| else if (state & kButtonStateHover) | |||
| pData->imageHover.draw(context); | |||
| break; | |||
| default: | |||
| else | |||
| pData->imageNormal.draw(context); | |||
| break; | |||
| } | |||
| } | |||
| template <class ImageType> | |||
| bool ImageBaseButton<ImageType>::onMouse(const MouseEvent& ev) | |||
| { | |||
| return pData->impl.onMouse(ev); | |||
| if (SubWidget::onMouse(ev)) | |||
| return true; | |||
| return ButtonEventHandler::mouseEvent(ev); | |||
| } | |||
| template <class ImageType> | |||
| bool ImageBaseButton<ImageType>::onMotion(const MotionEvent& ev) | |||
| { | |||
| return pData->impl.onMotion(ev); | |||
| if (SubWidget::onMotion(ev)) | |||
| return true; | |||
| return ButtonEventHandler::motionEvent(ev); | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||