| @@ -58,13 +58,15 @@ public: | |||
| virtual void imageButtonClicked(ImageButton* imageButton, int button) = 0; | |||
| }; | |||
| explicit ImageButton(Window& parent, const Image& image) noexcept; | |||
| explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept; | |||
| explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown) noexcept; | |||
| explicit ImageButton(Window& parent, const Image& image); | |||
| explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageDown); | |||
| explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown); | |||
| explicit ImageButton(Widget* widget, const Image& image) noexcept; | |||
| explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageDown) noexcept; | |||
| explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown) noexcept; | |||
| explicit ImageButton(Widget* widget, const Image& image); | |||
| explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageDown); | |||
| explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown); | |||
| ~ImageButton() override; | |||
| void setCallback(Callback* callback) noexcept; | |||
| @@ -74,13 +76,8 @@ protected: | |||
| bool onMotion(const MotionEvent&) override; | |||
| private: | |||
| Image fImageNormal; | |||
| Image fImageHover; | |||
| Image fImageDown; | |||
| int fCurButton; | |||
| int fCurState; | |||
| Callback* fCallback; | |||
| struct PrivateData; | |||
| PrivateData* const pData; | |||
| DISTRHO_LEAK_DETECTOR(ImageButton) | |||
| }; | |||
| @@ -34,8 +34,9 @@ public: | |||
| virtual void blendishButtonClicked(BlendishButton* blendishButton, int button) = 0; | |||
| }; | |||
| explicit BlendishButton(Window& parent, const char* text = "", int iconId = -1) noexcept; | |||
| explicit BlendishButton(NanoWidget* widget, const char* text = "", int iconId = -1) noexcept; | |||
| explicit BlendishButton(Window& parent, const char* text = "", int iconId = -1); | |||
| explicit BlendishButton(NanoWidget* widget, const char* text = "", int iconId = -1); | |||
| ~BlendishButton() override; | |||
| int getIconId() const noexcept; | |||
| void setIconId(int iconId) noexcept; | |||
| @@ -51,12 +52,8 @@ protected: | |||
| bool onMotion(const MotionEvent&) override; | |||
| private: | |||
| int fCurButton; | |||
| int fCurState; | |||
| int fIconId; | |||
| DISTRHO_NAMESPACE::String fText; | |||
| Callback* fCallback; | |||
| struct PrivateData; | |||
| PrivateData* const pData; | |||
| void _updateBounds(); | |||
| @@ -0,0 +1,129 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2015 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_COMMON_HPP_INCLUDED | |||
| #define DGL_COMMON_HPP_INCLUDED | |||
| #include "../ImageWidgets.hpp" | |||
| #include "../NanoWidgets.hpp" | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| struct ButtonImpl { | |||
| enum State { | |||
| kStateNormal = 0, | |||
| kStateHover, | |||
| kStateDown | |||
| }; | |||
| int button; | |||
| int state; | |||
| Widget* self; | |||
| BlendishButton::Callback* callback_b; | |||
| ImageButton::Callback* callback_i; | |||
| ButtonImpl(Widget* const s) noexcept | |||
| : button(-1), | |||
| state(kStateNormal), | |||
| self(s), | |||
| callback_b(nullptr), | |||
| callback_i(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_b != nullptr) | |||
| callback_b->blendishButtonClicked((BlendishButton*)self, button2); | |||
| if (callback_i != nullptr) | |||
| callback_i->imageButtonClicked((ImageButton*)self, button2); | |||
| return true; | |||
| } | |||
| // button was pressed, wait for release | |||
| if (ev.press && self->contains(ev.pos)) | |||
| { | |||
| button = 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_COPY_STRUCT(ButtonImpl) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_APP_PRIVATE_DATA_HPP_INCLUDED | |||
| @@ -15,6 +15,7 @@ | |||
| */ | |||
| #include "../ImageWidgets.hpp" | |||
| #include "Common.hpp" | |||
| #include "WidgetPrivateData.hpp" | |||
| START_NAMESPACE_DGL | |||
| @@ -87,27 +88,34 @@ void ImageAboutWindow::onReshape(uint width, uint height) | |||
| // ----------------------------------------------------------------------- | |||
| ImageButton::ImageButton(Window& parent, const Image& image) noexcept | |||
| struct ImageButton::PrivateData { | |||
| ButtonImpl impl; | |||
| Image imageNormal; | |||
| Image imageHover; | |||
| Image imageDown; | |||
| PrivateData(Widget* const s, const Image& normal, const Image& hover, const Image& down) | |||
| : impl(s), | |||
| imageNormal(normal), | |||
| imageHover(hover), | |||
| imageDown(down) {} | |||
| DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| ImageButton::ImageButton(Window& parent, const Image& image) | |||
| : Widget(parent), | |||
| fImageNormal(image), | |||
| fImageHover(image), | |||
| fImageDown(image), | |||
| fCurButton(-1), | |||
| fCurState(0), | |||
| fCallback(nullptr), | |||
| pData(new PrivateData(this, image, image, image)), | |||
| leakDetector_ImageButton() | |||
| { | |||
| setSize(image.getSize()); | |||
| } | |||
| ImageButton::ImageButton(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept | |||
| ImageButton::ImageButton(Window& parent, const Image& imageNormal, const Image& imageDown) | |||
| : Widget(parent), | |||
| fImageNormal(imageNormal), | |||
| fImageHover(imageNormal), | |||
| fImageDown(imageDown), | |||
| fCurButton(-1), | |||
| fCurState(0), | |||
| fCallback(nullptr), | |||
| pData(new PrivateData(this, imageNormal, imageNormal, imageDown)), | |||
| leakDetector_ImageButton() | |||
| { | |||
| DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); | |||
| @@ -115,14 +123,9 @@ ImageButton::ImageButton(Window& parent, const Image& imageNormal, const Image& | |||
| setSize(imageNormal.getSize()); | |||
| } | |||
| ImageButton::ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown) noexcept | |||
| ImageButton::ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown) | |||
| : Widget(parent), | |||
| fImageNormal(imageNormal), | |||
| fImageHover(imageHover), | |||
| fImageDown(imageDown), | |||
| fCurButton(-1), | |||
| fCurState(0), | |||
| fCallback(nullptr), | |||
| pData(new PrivateData(this, imageNormal, imageHover, imageDown)), | |||
| leakDetector_ImageButton() | |||
| { | |||
| DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize()); | |||
| @@ -130,27 +133,17 @@ ImageButton::ImageButton(Window& parent, const Image& imageNormal, const Image& | |||
| setSize(imageNormal.getSize()); | |||
| } | |||
| ImageButton::ImageButton(Widget* widget, const Image& image) noexcept | |||
| ImageButton::ImageButton(Widget* widget, const Image& image) | |||
| : Widget(widget->getParentWindow()), | |||
| fImageNormal(image), | |||
| fImageHover(image), | |||
| fImageDown(image), | |||
| fCurButton(-1), | |||
| fCurState(0), | |||
| fCallback(nullptr), | |||
| pData(new PrivateData(this, image, image, image)), | |||
| leakDetector_ImageButton() | |||
| { | |||
| setSize(image.getSize()); | |||
| } | |||
| ImageButton::ImageButton(Widget* widget, const Image& imageNormal, const Image& imageDown) noexcept | |||
| ImageButton::ImageButton(Widget* widget, const Image& imageNormal, const Image& imageDown) | |||
| : Widget(widget->getParentWindow()), | |||
| fImageNormal(imageNormal), | |||
| fImageHover(imageNormal), | |||
| fImageDown(imageDown), | |||
| fCurButton(-1), | |||
| fCurState(0), | |||
| fCallback(nullptr), | |||
| pData(new PrivateData(this, imageNormal, imageNormal, imageDown)), | |||
| leakDetector_ImageButton() | |||
| { | |||
| DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); | |||
| @@ -158,14 +151,9 @@ ImageButton::ImageButton(Widget* widget, const Image& imageNormal, const Image& | |||
| setSize(imageNormal.getSize()); | |||
| } | |||
| ImageButton::ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown) noexcept | |||
| ImageButton::ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown) | |||
| : Widget(widget->getParentWindow()), | |||
| fImageNormal(imageNormal), | |||
| fImageHover(imageHover), | |||
| fImageDown(imageDown), | |||
| fCurButton(-1), | |||
| fCurState(0), | |||
| fCallback(nullptr), | |||
| pData(new PrivateData(this, imageNormal, imageHover, imageDown)), | |||
| leakDetector_ImageButton() | |||
| { | |||
| DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize()); | |||
| @@ -173,96 +161,40 @@ ImageButton::ImageButton(Widget* widget, const Image& imageNormal, const Image& | |||
| setSize(imageNormal.getSize()); | |||
| } | |||
| ImageButton::~ImageButton() | |||
| { | |||
| delete pData; | |||
| } | |||
| void ImageButton::setCallback(Callback* callback) noexcept | |||
| { | |||
| fCallback = callback; | |||
| pData->impl.callback_i = callback; | |||
| } | |||
| void ImageButton::onDisplay() | |||
| { | |||
| switch (fCurState) | |||
| switch (pData->impl.state) | |||
| { | |||
| case 2: | |||
| fImageDown.draw(); | |||
| case ButtonImpl::kStateDown: | |||
| pData->imageDown.draw(); | |||
| break; | |||
| case 1: | |||
| fImageHover.draw(); | |||
| case ButtonImpl::kStateHover: | |||
| pData->imageHover.draw(); | |||
| break; | |||
| default: | |||
| fImageNormal.draw(); | |||
| pData->imageNormal.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; | |||
| return pData->impl.onMouse(ev); | |||
| } | |||
| 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; | |||
| return pData->impl.onMotion(ev); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| @@ -15,6 +15,7 @@ | |||
| */ | |||
| #include "../NanoWidgets.hpp" | |||
| #include "Common.hpp" | |||
| #define BLENDISH_IMPLEMENTATION | |||
| #include "nanovg/nanovg.h" | |||
| @@ -38,149 +39,100 @@ static void registerBlendishResourcesIfNeeded(NVGcontext* const context) | |||
| // ----------------------------------------------------------------------- | |||
| BlendishButton::BlendishButton(Window& parent, const char* text, int iconId) noexcept | |||
| struct BlendishButton::PrivateData { | |||
| ButtonImpl impl; | |||
| int iconId; | |||
| DISTRHO_NAMESPACE::String text; | |||
| PrivateData(Widget* const s, const char* const t, const int i) noexcept | |||
| : impl(s), | |||
| iconId(i), | |||
| text(t) {} | |||
| DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| BlendishButton::BlendishButton(Window& parent, const char* text, int iconId) | |||
| : NanoWidget(parent), | |||
| fCurButton(-1), | |||
| fCurState(0), | |||
| fIconId(iconId), | |||
| fText(text), | |||
| fCallback(nullptr), | |||
| pData(new PrivateData(this, text, iconId)), | |||
| leakDetector_BlendishButton() | |||
| { | |||
| registerBlendishResourcesIfNeeded(getContext()); | |||
| _updateBounds(); | |||
| } | |||
| BlendishButton::BlendishButton(NanoWidget* widget, const char* text, int iconId) noexcept | |||
| BlendishButton::BlendishButton(NanoWidget* widget, const char* text, int iconId) | |||
| : NanoWidget(widget), | |||
| fCurButton(-1), | |||
| fCurState(0), | |||
| fIconId(iconId), | |||
| fText(text), | |||
| fCallback(nullptr), | |||
| pData(new PrivateData(this, text, iconId)), | |||
| leakDetector_BlendishButton() | |||
| { | |||
| registerBlendishResourcesIfNeeded(getContext()); | |||
| _updateBounds(); | |||
| } | |||
| BlendishButton::~BlendishButton() | |||
| { | |||
| delete pData; | |||
| } | |||
| int BlendishButton::getIconId() const noexcept | |||
| { | |||
| return fIconId; | |||
| return pData->iconId; | |||
| } | |||
| void BlendishButton::setIconId(int iconId) noexcept | |||
| { | |||
| if (fIconId == iconId) | |||
| if (pData->iconId == iconId) | |||
| return; | |||
| fIconId = iconId; | |||
| pData->iconId = iconId; | |||
| _updateBounds(); | |||
| repaint(); | |||
| } | |||
| const char* BlendishButton::getText() const noexcept | |||
| { | |||
| return fText; | |||
| return pData->text; | |||
| } | |||
| void BlendishButton::setText(const char* text) noexcept | |||
| { | |||
| if (fText == text) | |||
| if (pData->text == text) | |||
| return; | |||
| fText = text; | |||
| pData->text = text; | |||
| _updateBounds(); | |||
| repaint(); | |||
| } | |||
| void BlendishButton::setCallback(Callback* callback) noexcept | |||
| { | |||
| fCallback = callback; | |||
| pData->impl.callback_b = callback; | |||
| } | |||
| void BlendishButton::onNanoDisplay() | |||
| { | |||
| bndToolButton(getContext(), | |||
| getAbsoluteX(), getAbsoluteY(), getWidth(), getHeight(), | |||
| 0, static_cast<BNDwidgetState>(fCurState), fIconId, fText); | |||
| 0, static_cast<BNDwidgetState>(pData->impl.state), pData->iconId, pData->text); | |||
| } | |||
| bool BlendishButton::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->blendishButtonClicked(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; | |||
| return pData->impl.onMouse(ev); | |||
| } | |||
| bool BlendishButton::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; | |||
| return pData->impl.onMotion(ev); | |||
| } | |||
| void BlendishButton::_updateBounds() | |||
| { | |||
| const float width = bndLabelWidth (getContext(), fIconId, fText); | |||
| const float height = bndLabelHeight(getContext(), fIconId, fText, width); | |||
| const float width = bndLabelWidth (getContext(), pData->iconId, pData->text); | |||
| const float height = bndLabelHeight(getContext(), pData->iconId, pData->text, width); | |||
| setSize(width, height); | |||
| } | |||