@@ -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); | |||
} | |||