Cleanup
[carla.git] / source / modules / dgl / src / ImageBaseWidgets.cpp
blobbb16f0ceefa0084f9b8a73b3a1d3e9f9a1a995b7
1 /*
2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
5 * Permission to use, copy, modify, and/or distribute this software for any purpose with
6 * or without fee is hereby granted, provided that the above copyright notice and this
7 * permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
10 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
11 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include "../ImageBaseWidgets.hpp"
18 #include "../Color.hpp"
20 START_NAMESPACE_DGL
22 // --------------------------------------------------------------------------------------------------------------------
24 template <class ImageType>
25 ImageBaseAboutWindow<ImageType>::ImageBaseAboutWindow(Window& transientParentWindow, const ImageType& image)
26 : StandaloneWindow(transientParentWindow.getApp(), transientParentWindow),
27 img(image)
29 setResizable(false);
30 setTitle("About");
32 if (image.isValid())
34 setSize(image.getSize());
35 setGeometryConstraints(image.getWidth(), image.getHeight(), true, true);
38 done();
41 template <class ImageType>
42 ImageBaseAboutWindow<ImageType>::ImageBaseAboutWindow(TopLevelWidget* const topLevelWidget, const ImageType& image)
43 : StandaloneWindow(topLevelWidget->getApp(), topLevelWidget->getWindow()),
44 img(image)
46 setResizable(false);
47 setTitle("About");
49 if (image.isValid())
51 setSize(image.getSize());
52 setGeometryConstraints(image.getWidth(), image.getHeight(), true, true);
55 done();
58 template <class ImageType>
59 void ImageBaseAboutWindow<ImageType>::setImage(const ImageType& image)
61 if (img == image)
62 return;
64 img = image;
66 if (image.isInvalid())
67 return;
69 setSize(image.getSize());
70 setGeometryConstraints(image.getWidth(), image.getHeight(), true, true);
73 template <class ImageType>
74 void ImageBaseAboutWindow<ImageType>::onDisplay()
76 img.draw(getGraphicsContext());
79 template <class ImageType>
80 bool ImageBaseAboutWindow<ImageType>::onKeyboard(const KeyboardEvent& ev)
82 if (ev.press && ev.key == kKeyEscape)
84 close();
85 return true;
88 return false;
91 template <class ImageType>
92 bool ImageBaseAboutWindow<ImageType>::onMouse(const MouseEvent& ev)
94 if (ev.press)
96 close();
97 return true;
100 return false;
103 // --------------------------------------------------------------------------------------------------------------------
105 template <class ImageType>
106 struct ImageBaseButton<ImageType>::PrivateData : public ButtonEventHandler::Callback {
107 ImageBaseButton<ImageType>::Callback* callback;
108 ImageType imageNormal;
109 ImageType imageHover;
110 ImageType imageDown;
112 PrivateData(const ImageType& normal, const ImageType& hover, const ImageType& down)
113 : callback(nullptr),
114 imageNormal(normal),
115 imageHover(hover),
116 imageDown(down) {}
118 void buttonClicked(SubWidget* widget, int button) override
120 if (callback != nullptr)
121 if (ImageBaseButton* const imageButton = dynamic_cast<ImageBaseButton*>(widget))
122 callback->imageButtonClicked(imageButton, button);
125 DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
128 // --------------------------------------------------------------------------------------------------------------------
130 template <class ImageType>
131 ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& image)
132 : SubWidget(parentWidget),
133 ButtonEventHandler(this),
134 pData(new PrivateData(image, image, image))
136 ButtonEventHandler::setCallback(pData);
137 setSize(image.getSize());
140 template <class ImageType>
141 ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown)
142 : SubWidget(parentWidget),
143 ButtonEventHandler(this),
144 pData(new PrivateData(imageNormal, imageNormal, imageDown))
146 DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize());
148 ButtonEventHandler::setCallback(pData);
149 setSize(imageNormal.getSize());
152 template <class ImageType>
153 ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown)
154 : SubWidget(parentWidget),
155 ButtonEventHandler(this),
156 pData(new PrivateData(imageNormal, imageHover, imageDown))
158 DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize());
160 ButtonEventHandler::setCallback(pData);
161 setSize(imageNormal.getSize());
164 template <class ImageType>
165 ImageBaseButton<ImageType>::~ImageBaseButton()
167 delete pData;
170 template <class ImageType>
171 void ImageBaseButton<ImageType>::setCallback(Callback* callback) noexcept
173 pData->callback = callback;
176 template <class ImageType>
177 void ImageBaseButton<ImageType>::onDisplay()
179 const GraphicsContext& context(getGraphicsContext());
181 const State state = ButtonEventHandler::getState();
183 if (state & kButtonStateActive)
184 pData->imageDown.draw(context);
185 else if (state & kButtonStateHover)
186 pData->imageHover.draw(context);
187 else
188 pData->imageNormal.draw(context);
191 template <class ImageType>
192 bool ImageBaseButton<ImageType>::onMouse(const MouseEvent& ev)
194 if (SubWidget::onMouse(ev))
195 return true;
196 return ButtonEventHandler::mouseEvent(ev);
199 template <class ImageType>
200 bool ImageBaseButton<ImageType>::onMotion(const MotionEvent& ev)
202 if (SubWidget::onMotion(ev))
203 return true;
204 return ButtonEventHandler::motionEvent(ev);
207 // --------------------------------------------------------------------------------------------------------------------
209 template <class ImageType>
210 struct ImageBaseKnob<ImageType>::PrivateData : public KnobEventHandler::Callback {
211 ImageBaseKnob<ImageType>::Callback* callback;
212 ImageType image;
214 int rotationAngle;
216 bool alwaysRepaint;
217 bool isImgVertical;
218 uint imgLayerWidth;
219 uint imgLayerHeight;
220 uint imgLayerCount;
221 bool isReady;
223 union {
224 uint glTextureId;
225 void* cairoSurface;
228 explicit PrivateData(const ImageType& img)
229 : callback(nullptr),
230 image(img),
231 rotationAngle(0),
232 alwaysRepaint(false),
233 isImgVertical(img.getHeight() > img.getWidth()),
234 imgLayerWidth(isImgVertical ? img.getWidth() : img.getHeight()),
235 imgLayerHeight(imgLayerWidth),
236 imgLayerCount(isImgVertical ? img.getHeight()/imgLayerHeight : img.getWidth()/imgLayerWidth),
237 isReady(false)
239 init();
242 explicit PrivateData(PrivateData* const other)
243 : callback(other->callback),
244 image(other->image),
245 rotationAngle(other->rotationAngle),
246 alwaysRepaint(other->alwaysRepaint),
247 isImgVertical(other->isImgVertical),
248 imgLayerWidth(other->imgLayerWidth),
249 imgLayerHeight(other->imgLayerHeight),
250 imgLayerCount(other->imgLayerCount),
251 isReady(false)
253 init();
256 void assignFrom(PrivateData* const other)
258 cleanup();
259 image = other->image;
260 rotationAngle = other->rotationAngle;
261 callback = other->callback;
262 alwaysRepaint = other->alwaysRepaint;
263 isImgVertical = other->isImgVertical;
264 imgLayerWidth = other->imgLayerWidth;
265 imgLayerHeight = other->imgLayerHeight;
266 imgLayerCount = other->imgLayerCount;
267 isReady = false;
268 init();
271 ~PrivateData()
273 cleanup();
276 void knobDragStarted(SubWidget* const widget) override
278 if (callback != nullptr)
279 if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget))
280 callback->imageKnobDragStarted(imageKnob);
283 void knobDragFinished(SubWidget* const widget) override
285 if (callback != nullptr)
286 if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget))
287 callback->imageKnobDragFinished(imageKnob);
290 void knobValueChanged(SubWidget* const widget, const float value) override
292 if (rotationAngle == 0 || alwaysRepaint)
293 isReady = false;
295 if (callback != nullptr)
296 if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget))
297 callback->imageKnobValueChanged(imageKnob, value);
300 // implemented independently per graphics backend
301 void init();
302 void cleanup();
304 DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
307 // --------------------------------------------------------------------------------------------------------------------
309 template <class ImageType>
310 ImageBaseKnob<ImageType>::ImageBaseKnob(Widget* const parentWidget,
311 const ImageType& image,
312 const Orientation orientation) noexcept
313 : SubWidget(parentWidget),
314 KnobEventHandler(this),
315 pData(new PrivateData(image))
317 KnobEventHandler::setCallback(pData);
318 setOrientation(orientation);
319 setSize(pData->imgLayerWidth, pData->imgLayerHeight);
322 template <class ImageType>
323 ImageBaseKnob<ImageType>::ImageBaseKnob(const ImageBaseKnob<ImageType>& imageKnob)
324 : SubWidget(imageKnob.getParentWidget()),
325 KnobEventHandler(this, imageKnob),
326 pData(new PrivateData(imageKnob.pData))
328 KnobEventHandler::setCallback(pData);
329 setOrientation(imageKnob.getOrientation());
330 setSize(pData->imgLayerWidth, pData->imgLayerHeight);
333 template <class ImageType>
334 ImageBaseKnob<ImageType>& ImageBaseKnob<ImageType>::operator=(const ImageBaseKnob<ImageType>& imageKnob)
336 KnobEventHandler::operator=(imageKnob);
337 pData->assignFrom(imageKnob.pData);
338 setSize(pData->imgLayerWidth, pData->imgLayerHeight);
339 return *this;
342 template <class ImageType>
343 ImageBaseKnob<ImageType>::~ImageBaseKnob()
345 delete pData;
348 template <class ImageType>
349 void ImageBaseKnob<ImageType>::setCallback(Callback* callback) noexcept
351 pData->callback = callback;
354 template <class ImageType>
355 void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept
357 DISTRHO_SAFE_ASSERT_RETURN(count > 1,);
359 pData->imgLayerCount = count;
361 if (pData->isImgVertical)
362 pData->imgLayerHeight = pData->image.getHeight()/count;
363 else
364 pData->imgLayerWidth = pData->image.getWidth()/count;
366 setSize(pData->imgLayerWidth, pData->imgLayerHeight);
369 template <class ImageType>
370 void ImageBaseKnob<ImageType>::setRotationAngle(int angle)
372 if (pData->rotationAngle == angle)
373 return;
375 pData->rotationAngle = angle;
376 pData->isReady = false;
379 template <class ImageType>
380 bool ImageBaseKnob<ImageType>::setValue(float value, bool sendCallback) noexcept
382 if (KnobEventHandler::setValue(value, sendCallback))
384 if (pData->rotationAngle == 0 || pData->alwaysRepaint)
385 pData->isReady = false;
387 return true;
390 return false;
393 template <class ImageType>
394 bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev)
396 if (SubWidget::onMouse(ev))
397 return true;
398 return KnobEventHandler::mouseEvent(ev);
401 template <class ImageType>
402 bool ImageBaseKnob<ImageType>::onMotion(const MotionEvent& ev)
404 if (SubWidget::onMotion(ev))
405 return true;
406 return KnobEventHandler::motionEvent(ev);
409 template <class ImageType>
410 bool ImageBaseKnob<ImageType>::onScroll(const ScrollEvent& ev)
412 if (SubWidget::onScroll(ev))
413 return true;
414 return KnobEventHandler::scrollEvent(ev);
417 // --------------------------------------------------------------------------------------------------------------------
419 template <class ImageType>
420 struct ImageBaseSlider<ImageType>::PrivateData {
421 ImageType image;
422 float minimum;
423 float maximum;
424 float step;
425 float value;
426 float valueDef;
427 float valueTmp;
428 bool usingDefault;
430 bool dragging;
431 bool inverted;
432 bool valueIsSet;
433 double startedX;
434 double startedY;
436 Callback* callback;
438 Point<int> startPos;
439 Point<int> endPos;
440 Rectangle<double> sliderArea;
442 PrivateData(const ImageType& img)
443 : image(img),
444 minimum(0.0f),
445 maximum(1.0f),
446 step(0.0f),
447 value(0.5f),
448 valueDef(value),
449 valueTmp(value),
450 usingDefault(false),
451 dragging(false),
452 inverted(false),
453 valueIsSet(false),
454 startedX(0.0),
455 startedY(0.0),
456 callback(nullptr),
457 startPos(),
458 endPos(),
459 sliderArea() {}
461 void recheckArea() noexcept
463 if (startPos.getY() == endPos.getY())
465 // horizontal
466 sliderArea = Rectangle<double>(startPos.getX(),
467 startPos.getY(),
468 endPos.getX() + static_cast<int>(image.getWidth()) - startPos.getX(),
469 static_cast<int>(image.getHeight()));
471 else
473 // vertical
474 sliderArea = Rectangle<double>(startPos.getX(),
475 startPos.getY(),
476 static_cast<int>(image.getWidth()),
477 endPos.getY() + static_cast<int>(image.getHeight()) - startPos.getY());
481 DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
484 // --------------------------------------------------------------------------------------------------------------------
486 template <class ImageType>
487 ImageBaseSlider<ImageType>::ImageBaseSlider(Widget* const parentWidget, const ImageType& image) noexcept
488 : SubWidget(parentWidget),
489 pData(new PrivateData(image))
491 setNeedsFullViewportDrawing();
494 template <class ImageType>
495 ImageBaseSlider<ImageType>::~ImageBaseSlider()
497 delete pData;
500 template <class ImageType>
501 float ImageBaseSlider<ImageType>::getValue() const noexcept
503 return pData->value;
506 template <class ImageType>
507 void ImageBaseSlider<ImageType>::setValue(float value, bool sendCallback) noexcept
509 if (! pData->valueIsSet)
510 pData->valueIsSet = true;
512 if (d_isEqual(pData->value, value))
513 return;
515 pData->value = value;
517 if (d_isZero(pData->step))
518 pData->valueTmp = value;
520 repaint();
522 if (sendCallback && pData->callback != nullptr)
524 try {
525 pData->callback->imageSliderValueChanged(this, pData->value);
526 } DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setValue");
530 template <class ImageType>
531 void ImageBaseSlider<ImageType>::setStartPos(const Point<int>& startPos) noexcept
533 pData->startPos = startPos;
534 pData->recheckArea();
537 template <class ImageType>
538 void ImageBaseSlider<ImageType>::setStartPos(int x, int y) noexcept
540 setStartPos(Point<int>(x, y));
543 template <class ImageType>
544 void ImageBaseSlider<ImageType>::setEndPos(const Point<int>& endPos) noexcept
546 pData->endPos = endPos;
547 pData->recheckArea();
550 template <class ImageType>
551 void ImageBaseSlider<ImageType>::setEndPos(int x, int y) noexcept
553 setEndPos(Point<int>(x, y));
556 template <class ImageType>
557 void ImageBaseSlider<ImageType>::setInverted(bool inverted) noexcept
559 if (pData->inverted == inverted)
560 return;
562 pData->inverted = inverted;
563 repaint();
566 template <class ImageType>
567 void ImageBaseSlider<ImageType>::setDefault(float value) noexcept
569 pData->valueDef = value;
570 pData->usingDefault = true;
573 template <class ImageType>
574 void ImageBaseSlider<ImageType>::setRange(float min, float max) noexcept
576 pData->minimum = min;
577 pData->maximum = max;
579 if (pData->value < min)
581 pData->value = min;
582 repaint();
584 if (pData->callback != nullptr && pData->valueIsSet)
586 try {
587 pData->callback->imageSliderValueChanged(this, pData->value);
588 } DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setRange < min");
591 else if (pData->value > max)
593 pData->value = max;
594 repaint();
596 if (pData->callback != nullptr && pData->valueIsSet)
598 try {
599 pData->callback->imageSliderValueChanged(this, pData->value);
600 } DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setRange > max");
605 template <class ImageType>
606 void ImageBaseSlider<ImageType>::setStep(float step) noexcept
608 pData->step = step;
611 template <class ImageType>
612 void ImageBaseSlider<ImageType>::setCallback(Callback* callback) noexcept
614 pData->callback = callback;
617 template <class ImageType>
618 void ImageBaseSlider<ImageType>::onDisplay()
620 const GraphicsContext& context(getGraphicsContext());
622 #if 0 // DEBUG, paints slider area
623 Color(1.0f, 1.0f, 1.0f, 0.5f).setFor(context, true);
624 Rectangle<int>(pData->sliderArea.getX(),
625 pData->sliderArea.getY(),
626 pData->sliderArea.getX()+pData->sliderArea.getWidth(),
627 pData->sliderArea.getY()+pData->sliderArea.getHeight()).draw(context);
628 Color(1.0f, 1.0f, 1.0f, 1.0f).setFor(context, true);
629 #endif
631 const float normValue = (pData->value - pData->minimum) / (pData->maximum - pData->minimum);
633 int x, y;
635 if (pData->startPos.getY() == pData->endPos.getY())
637 // horizontal
638 if (pData->inverted)
639 x = pData->endPos.getX() - static_cast<int>(normValue*static_cast<float>(pData->endPos.getX()-pData->startPos.getX()));
640 else
641 x = pData->startPos.getX() + static_cast<int>(normValue*static_cast<float>(pData->endPos.getX()-pData->startPos.getX()));
643 y = pData->startPos.getY();
645 else
647 // vertical
648 x = pData->startPos.getX();
650 if (pData->inverted)
651 y = pData->endPos.getY() - static_cast<int>(normValue*static_cast<float>(pData->endPos.getY()-pData->startPos.getY()));
652 else
653 y = pData->startPos.getY() + static_cast<int>(normValue*static_cast<float>(pData->endPos.getY()-pData->startPos.getY()));
656 pData->image.drawAt(context, x, y);
659 template <class ImageType>
660 bool ImageBaseSlider<ImageType>::onMouse(const MouseEvent& ev)
662 if (ev.button != 1)
663 return false;
665 if (ev.press)
667 if (! pData->sliderArea.contains(ev.pos))
668 return false;
670 if ((ev.mod & kModifierShift) != 0 && pData->usingDefault)
672 setValue(pData->valueDef, true);
673 pData->valueTmp = pData->value;
674 return true;
677 float vper;
678 const double x = ev.pos.getX();
679 const double y = ev.pos.getY();
681 if (pData->startPos.getY() == pData->endPos.getY())
683 // horizontal
684 vper = float(x - pData->sliderArea.getX()) / float(pData->sliderArea.getWidth());
686 else
688 // vertical
689 vper = float(y - pData->sliderArea.getY()) / float(pData->sliderArea.getHeight());
692 float value;
694 if (pData->inverted)
695 value = pData->maximum - vper * (pData->maximum - pData->minimum);
696 else
697 value = pData->minimum + vper * (pData->maximum - pData->minimum);
699 if (value < pData->minimum)
701 pData->valueTmp = value = pData->minimum;
703 else if (value > pData->maximum)
705 pData->valueTmp = value = pData->maximum;
707 else if (d_isNotZero(pData->step))
709 pData->valueTmp = value;
710 const float rest = std::fmod(value, pData->step);
711 value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f);
714 pData->dragging = true;
715 pData->startedX = x;
716 pData->startedY = y;
718 if (pData->callback != nullptr)
719 pData->callback->imageSliderDragStarted(this);
721 setValue(value, true);
723 return true;
725 else if (pData->dragging)
727 if (pData->callback != nullptr)
728 pData->callback->imageSliderDragFinished(this);
730 pData->dragging = false;
731 return true;
734 return false;
737 template <class ImageType>
738 bool ImageBaseSlider<ImageType>::onMotion(const MotionEvent& ev)
740 if (! pData->dragging)
741 return false;
743 const bool horizontal = pData->startPos.getY() == pData->endPos.getY();
744 const double x = ev.pos.getX();
745 const double y = ev.pos.getY();
747 if ((horizontal && pData->sliderArea.containsX(x)) || (pData->sliderArea.containsY(y) && ! horizontal))
749 float vper;
751 if (horizontal)
753 // horizontal
754 vper = float(x - pData->sliderArea.getX()) / float(pData->sliderArea.getWidth());
756 else
758 // vertical
759 vper = float(y - pData->sliderArea.getY()) / float(pData->sliderArea.getHeight());
762 float value;
764 if (pData->inverted)
765 value = pData->maximum - vper * (pData->maximum - pData->minimum);
766 else
767 value = pData->minimum + vper * (pData->maximum - pData->minimum);
769 if (value < pData->minimum)
771 pData->valueTmp = value = pData->minimum;
773 else if (value > pData->maximum)
775 pData->valueTmp = value = pData->maximum;
777 else if (d_isNotZero(pData->step))
779 pData->valueTmp = value;
780 const float rest = std::fmod(value, pData->step);
781 value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f);
784 setValue(value, true);
786 else if (horizontal)
788 if (x < pData->sliderArea.getX())
789 setValue(pData->inverted ? pData->maximum : pData->minimum, true);
790 else
791 setValue(pData->inverted ? pData->minimum : pData->maximum, true);
793 else
795 if (y < pData->sliderArea.getY())
796 setValue(pData->inverted ? pData->maximum : pData->minimum, true);
797 else
798 setValue(pData->inverted ? pData->minimum : pData->maximum, true);
801 return true;
804 // --------------------------------------------------------------------------------------------------------------------
806 template <class ImageType>
807 struct ImageBaseSwitch<ImageType>::PrivateData {
808 ImageType imageNormal;
809 ImageType imageDown;
810 bool isDown;
811 Callback* callback;
813 PrivateData(const ImageType& normal, const ImageType& down)
814 : imageNormal(normal),
815 imageDown(down),
816 isDown(false),
817 callback(nullptr)
819 DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize());
822 PrivateData(PrivateData* const other)
823 : imageNormal(other->imageNormal),
824 imageDown(other->imageDown),
825 isDown(other->isDown),
826 callback(other->callback)
828 DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize());
831 void assignFrom(PrivateData* const other)
833 imageNormal = other->imageNormal;
834 imageDown = other->imageDown;
835 isDown = other->isDown;
836 callback = other->callback;
837 DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize());
840 DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
843 // --------------------------------------------------------------------------------------------------------------------
845 template <class ImageType>
846 ImageBaseSwitch<ImageType>::ImageBaseSwitch(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept
847 : SubWidget(parentWidget),
848 pData(new PrivateData(imageNormal, imageDown))
850 setSize(imageNormal.getSize());
853 template <class ImageType>
854 ImageBaseSwitch<ImageType>::ImageBaseSwitch(const ImageBaseSwitch<ImageType>& imageSwitch) noexcept
855 : SubWidget(imageSwitch.getParentWidget()),
856 pData(new PrivateData(imageSwitch.pData))
858 setSize(pData->imageNormal.getSize());
861 template <class ImageType>
862 ImageBaseSwitch<ImageType>& ImageBaseSwitch<ImageType>::operator=(const ImageBaseSwitch<ImageType>& imageSwitch) noexcept
864 pData->assignFrom(imageSwitch.pData);
865 setSize(pData->imageNormal.getSize());
866 return *this;
869 template <class ImageType>
870 ImageBaseSwitch<ImageType>::~ImageBaseSwitch()
872 delete pData;
875 template <class ImageType>
876 bool ImageBaseSwitch<ImageType>::isDown() const noexcept
878 return pData->isDown;
881 template <class ImageType>
882 void ImageBaseSwitch<ImageType>::setDown(const bool down) noexcept
884 if (pData->isDown == down)
885 return;
887 pData->isDown = down;
888 repaint();
891 template <class ImageType>
892 void ImageBaseSwitch<ImageType>::setCallback(Callback* const callback) noexcept
894 pData->callback = callback;
897 template <class ImageType>
898 void ImageBaseSwitch<ImageType>::onDisplay()
900 const GraphicsContext& context(getGraphicsContext());
902 if (pData->isDown)
903 pData->imageDown.draw(context);
904 else
905 pData->imageNormal.draw(context);
908 template <class ImageType>
909 bool ImageBaseSwitch<ImageType>::onMouse(const MouseEvent& ev)
911 if (ev.press && contains(ev.pos))
913 pData->isDown = !pData->isDown;
915 repaint();
917 if (pData->callback != nullptr)
918 pData->callback->imageSwitchClicked(this, pData->isDown);
920 return true;
923 return false;
926 // --------------------------------------------------------------------------------------------------------------------
928 END_NAMESPACE_DGL