Mercurial > hg > pub > prymula > com
comparison DPF-Prymula-audioplugins/dpf/dgl/src/ImageBaseWidgets.cpp @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2:cf2cb71d31dd | 3:84e66ea83026 |
---|---|
1 /* | |
2 * DISTRHO Plugin Framework (DPF) | |
3 * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |
4 * | |
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. | |
8 * | |
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. | |
15 */ | |
16 | |
17 #include "../ImageBaseWidgets.hpp" | |
18 #include "../Color.hpp" | |
19 | |
20 START_NAMESPACE_DGL | |
21 | |
22 // -------------------------------------------------------------------------------------------------------------------- | |
23 | |
24 template <class ImageType> | |
25 ImageBaseAboutWindow<ImageType>::ImageBaseAboutWindow(Window& transientParentWindow, const ImageType& image) | |
26 : StandaloneWindow(transientParentWindow.getApp(), transientParentWindow), | |
27 img(image) | |
28 { | |
29 setResizable(false); | |
30 setTitle("About"); | |
31 | |
32 if (image.isValid()) | |
33 { | |
34 setSize(image.getSize()); | |
35 setGeometryConstraints(image.getWidth(), image.getHeight(), true, true); | |
36 } | |
37 | |
38 done(); | |
39 } | |
40 | |
41 template <class ImageType> | |
42 ImageBaseAboutWindow<ImageType>::ImageBaseAboutWindow(TopLevelWidget* const topLevelWidget, const ImageType& image) | |
43 : StandaloneWindow(topLevelWidget->getApp(), topLevelWidget->getWindow()), | |
44 img(image) | |
45 { | |
46 setResizable(false); | |
47 setTitle("About"); | |
48 | |
49 if (image.isValid()) | |
50 { | |
51 setSize(image.getSize()); | |
52 setGeometryConstraints(image.getWidth(), image.getHeight(), true, true); | |
53 } | |
54 | |
55 done(); | |
56 } | |
57 | |
58 template <class ImageType> | |
59 void ImageBaseAboutWindow<ImageType>::setImage(const ImageType& image) | |
60 { | |
61 if (img == image) | |
62 return; | |
63 | |
64 if (image.isInvalid()) | |
65 { | |
66 img = image; | |
67 return; | |
68 } | |
69 | |
70 reinit(); | |
71 | |
72 img = image; | |
73 | |
74 setSize(image.getSize()); | |
75 setGeometryConstraints(image.getWidth(), image.getHeight(), true, true); | |
76 | |
77 done(); | |
78 } | |
79 | |
80 template <class ImageType> | |
81 void ImageBaseAboutWindow<ImageType>::onDisplay() | |
82 { | |
83 img.draw(getGraphicsContext()); | |
84 } | |
85 | |
86 template <class ImageType> | |
87 bool ImageBaseAboutWindow<ImageType>::onKeyboard(const KeyboardEvent& ev) | |
88 { | |
89 if (ev.press && ev.key == kKeyEscape) | |
90 { | |
91 close(); | |
92 return true; | |
93 } | |
94 | |
95 return false; | |
96 } | |
97 | |
98 template <class ImageType> | |
99 bool ImageBaseAboutWindow<ImageType>::onMouse(const MouseEvent& ev) | |
100 { | |
101 if (ev.press) | |
102 { | |
103 close(); | |
104 return true; | |
105 } | |
106 | |
107 return false; | |
108 } | |
109 | |
110 // -------------------------------------------------------------------------------------------------------------------- | |
111 | |
112 template <class ImageType> | |
113 struct ImageBaseButton<ImageType>::PrivateData : public ButtonEventHandler::Callback { | |
114 ImageBaseButton<ImageType>::Callback* callback; | |
115 ImageType imageNormal; | |
116 ImageType imageHover; | |
117 ImageType imageDown; | |
118 | |
119 PrivateData(const ImageType& normal, const ImageType& hover, const ImageType& down) | |
120 : callback(nullptr), | |
121 imageNormal(normal), | |
122 imageHover(hover), | |
123 imageDown(down) {} | |
124 | |
125 void buttonClicked(SubWidget* widget, int button) override | |
126 { | |
127 if (callback != nullptr) | |
128 if (ImageBaseButton* const imageButton = dynamic_cast<ImageBaseButton*>(widget)) | |
129 callback->imageButtonClicked(imageButton, button); | |
130 } | |
131 | |
132 DISTRHO_DECLARE_NON_COPYABLE(PrivateData) | |
133 }; | |
134 | |
135 // -------------------------------------------------------------------------------------------------------------------- | |
136 | |
137 template <class ImageType> | |
138 ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& image) | |
139 : SubWidget(parentWidget), | |
140 ButtonEventHandler(this), | |
141 pData(new PrivateData(image, image, image)) | |
142 { | |
143 ButtonEventHandler::setCallback(pData); | |
144 setSize(image.getSize()); | |
145 } | |
146 | |
147 template <class ImageType> | |
148 ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown) | |
149 : SubWidget(parentWidget), | |
150 ButtonEventHandler(this), | |
151 pData(new PrivateData(imageNormal, imageNormal, imageDown)) | |
152 { | |
153 DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); | |
154 | |
155 ButtonEventHandler::setCallback(pData); | |
156 setSize(imageNormal.getSize()); | |
157 } | |
158 | |
159 template <class ImageType> | |
160 ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown) | |
161 : SubWidget(parentWidget), | |
162 ButtonEventHandler(this), | |
163 pData(new PrivateData(imageNormal, imageHover, imageDown)) | |
164 { | |
165 DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize()); | |
166 | |
167 ButtonEventHandler::setCallback(pData); | |
168 setSize(imageNormal.getSize()); | |
169 } | |
170 | |
171 template <class ImageType> | |
172 ImageBaseButton<ImageType>::~ImageBaseButton() | |
173 { | |
174 delete pData; | |
175 } | |
176 | |
177 template <class ImageType> | |
178 void ImageBaseButton<ImageType>::setCallback(Callback* callback) noexcept | |
179 { | |
180 pData->callback = callback; | |
181 } | |
182 | |
183 template <class ImageType> | |
184 void ImageBaseButton<ImageType>::onDisplay() | |
185 { | |
186 const GraphicsContext& context(getGraphicsContext()); | |
187 | |
188 const State state = ButtonEventHandler::getState(); | |
189 | |
190 if (ButtonEventHandler::isCheckable()) | |
191 { | |
192 if (ButtonEventHandler::isChecked()) | |
193 pData->imageDown.draw(context); | |
194 else if (state & kButtonStateHover) | |
195 pData->imageHover.draw(context); | |
196 else | |
197 pData->imageNormal.draw(context); | |
198 } | |
199 else | |
200 { | |
201 if (state & kButtonStateActive) | |
202 pData->imageDown.draw(context); | |
203 else if (state & kButtonStateHover) | |
204 pData->imageHover.draw(context); | |
205 else | |
206 pData->imageNormal.draw(context); | |
207 } | |
208 } | |
209 | |
210 template <class ImageType> | |
211 bool ImageBaseButton<ImageType>::onMouse(const MouseEvent& ev) | |
212 { | |
213 if (SubWidget::onMouse(ev)) | |
214 return true; | |
215 return ButtonEventHandler::mouseEvent(ev); | |
216 } | |
217 | |
218 template <class ImageType> | |
219 bool ImageBaseButton<ImageType>::onMotion(const MotionEvent& ev) | |
220 { | |
221 if (SubWidget::onMotion(ev)) | |
222 return true; | |
223 return ButtonEventHandler::motionEvent(ev); | |
224 } | |
225 | |
226 // -------------------------------------------------------------------------------------------------------------------- | |
227 | |
228 template <class ImageType> | |
229 struct ImageBaseKnob<ImageType>::PrivateData : public KnobEventHandler::Callback { | |
230 ImageBaseKnob<ImageType>::Callback* callback; | |
231 ImageType image; | |
232 | |
233 int rotationAngle; | |
234 | |
235 bool alwaysRepaint; | |
236 bool isImgVertical; | |
237 uint imgLayerWidth; | |
238 uint imgLayerHeight; | |
239 uint imgLayerCount; | |
240 bool isReady; | |
241 | |
242 union { | |
243 uint glTextureId; | |
244 void* cairoSurface; | |
245 }; | |
246 | |
247 explicit PrivateData(const ImageType& img) | |
248 : callback(nullptr), | |
249 image(img), | |
250 rotationAngle(0), | |
251 alwaysRepaint(false), | |
252 isImgVertical(img.getHeight() > img.getWidth()), | |
253 imgLayerWidth(isImgVertical ? img.getWidth() : img.getHeight()), | |
254 imgLayerHeight(imgLayerWidth), | |
255 imgLayerCount(isImgVertical ? img.getHeight()/imgLayerHeight : img.getWidth()/imgLayerWidth), | |
256 isReady(false) | |
257 { | |
258 init(); | |
259 } | |
260 | |
261 explicit PrivateData(PrivateData* const other) | |
262 : callback(other->callback), | |
263 image(other->image), | |
264 rotationAngle(other->rotationAngle), | |
265 alwaysRepaint(other->alwaysRepaint), | |
266 isImgVertical(other->isImgVertical), | |
267 imgLayerWidth(other->imgLayerWidth), | |
268 imgLayerHeight(other->imgLayerHeight), | |
269 imgLayerCount(other->imgLayerCount), | |
270 isReady(false) | |
271 { | |
272 init(); | |
273 } | |
274 | |
275 void assignFrom(PrivateData* const other) | |
276 { | |
277 cleanup(); | |
278 image = other->image; | |
279 rotationAngle = other->rotationAngle; | |
280 callback = other->callback; | |
281 alwaysRepaint = other->alwaysRepaint; | |
282 isImgVertical = other->isImgVertical; | |
283 imgLayerWidth = other->imgLayerWidth; | |
284 imgLayerHeight = other->imgLayerHeight; | |
285 imgLayerCount = other->imgLayerCount; | |
286 isReady = false; | |
287 init(); | |
288 } | |
289 | |
290 ~PrivateData() | |
291 { | |
292 cleanup(); | |
293 } | |
294 | |
295 void knobDragStarted(SubWidget* const widget) override | |
296 { | |
297 if (callback != nullptr) | |
298 if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget)) | |
299 callback->imageKnobDragStarted(imageKnob); | |
300 } | |
301 | |
302 void knobDragFinished(SubWidget* const widget) override | |
303 { | |
304 if (callback != nullptr) | |
305 if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget)) | |
306 callback->imageKnobDragFinished(imageKnob); | |
307 } | |
308 | |
309 void knobValueChanged(SubWidget* const widget, const float value) override | |
310 { | |
311 if (rotationAngle == 0 || alwaysRepaint) | |
312 isReady = false; | |
313 | |
314 if (callback != nullptr) | |
315 if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget)) | |
316 callback->imageKnobValueChanged(imageKnob, value); | |
317 } | |
318 | |
319 // implemented independently per graphics backend | |
320 void init(); | |
321 void cleanup(); | |
322 | |
323 DISTRHO_DECLARE_NON_COPYABLE(PrivateData) | |
324 }; | |
325 | |
326 // -------------------------------------------------------------------------------------------------------------------- | |
327 | |
328 template <class ImageType> | |
329 ImageBaseKnob<ImageType>::ImageBaseKnob(Widget* const parentWidget, | |
330 const ImageType& image, | |
331 const Orientation orientation) noexcept | |
332 : SubWidget(parentWidget), | |
333 KnobEventHandler(this), | |
334 pData(new PrivateData(image)) | |
335 { | |
336 KnobEventHandler::setCallback(pData); | |
337 setOrientation(orientation); | |
338 setSize(pData->imgLayerWidth, pData->imgLayerHeight); | |
339 } | |
340 | |
341 template <class ImageType> | |
342 ImageBaseKnob<ImageType>::ImageBaseKnob(const ImageBaseKnob<ImageType>& imageKnob) | |
343 : SubWidget(imageKnob.getParentWidget()), | |
344 KnobEventHandler(this, imageKnob), | |
345 pData(new PrivateData(imageKnob.pData)) | |
346 { | |
347 KnobEventHandler::setCallback(pData); | |
348 setOrientation(imageKnob.getOrientation()); | |
349 setSize(pData->imgLayerWidth, pData->imgLayerHeight); | |
350 } | |
351 | |
352 template <class ImageType> | |
353 ImageBaseKnob<ImageType>& ImageBaseKnob<ImageType>::operator=(const ImageBaseKnob<ImageType>& imageKnob) | |
354 { | |
355 KnobEventHandler::operator=(imageKnob); | |
356 pData->assignFrom(imageKnob.pData); | |
357 setSize(pData->imgLayerWidth, pData->imgLayerHeight); | |
358 return *this; | |
359 } | |
360 | |
361 template <class ImageType> | |
362 ImageBaseKnob<ImageType>::~ImageBaseKnob() | |
363 { | |
364 delete pData; | |
365 } | |
366 | |
367 template <class ImageType> | |
368 void ImageBaseKnob<ImageType>::setCallback(Callback* callback) noexcept | |
369 { | |
370 pData->callback = callback; | |
371 } | |
372 | |
373 template <class ImageType> | |
374 void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept | |
375 { | |
376 DISTRHO_SAFE_ASSERT_RETURN(count > 1,); | |
377 | |
378 pData->imgLayerCount = count; | |
379 | |
380 if (pData->isImgVertical) | |
381 pData->imgLayerHeight = pData->image.getHeight()/count; | |
382 else | |
383 pData->imgLayerWidth = pData->image.getWidth()/count; | |
384 | |
385 setSize(pData->imgLayerWidth, pData->imgLayerHeight); | |
386 } | |
387 | |
388 template <class ImageType> | |
389 void ImageBaseKnob<ImageType>::setRotationAngle(int angle) | |
390 { | |
391 if (pData->rotationAngle == angle) | |
392 return; | |
393 | |
394 pData->rotationAngle = angle; | |
395 pData->isReady = false; | |
396 } | |
397 | |
398 template <class ImageType> | |
399 bool ImageBaseKnob<ImageType>::setValue(float value, bool sendCallback) noexcept | |
400 { | |
401 if (KnobEventHandler::setValue(value, sendCallback)) | |
402 { | |
403 if (pData->rotationAngle == 0 || pData->alwaysRepaint) | |
404 pData->isReady = false; | |
405 | |
406 return true; | |
407 } | |
408 | |
409 return false; | |
410 } | |
411 | |
412 template <class ImageType> | |
413 bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev) | |
414 { | |
415 if (SubWidget::onMouse(ev)) | |
416 return true; | |
417 return KnobEventHandler::mouseEvent(ev, getTopLevelWidget()->getScaleFactor()); | |
418 } | |
419 | |
420 template <class ImageType> | |
421 bool ImageBaseKnob<ImageType>::onMotion(const MotionEvent& ev) | |
422 { | |
423 if (SubWidget::onMotion(ev)) | |
424 return true; | |
425 return KnobEventHandler::motionEvent(ev, getTopLevelWidget()->getScaleFactor()); | |
426 } | |
427 | |
428 template <class ImageType> | |
429 bool ImageBaseKnob<ImageType>::onScroll(const ScrollEvent& ev) | |
430 { | |
431 if (SubWidget::onScroll(ev)) | |
432 return true; | |
433 return KnobEventHandler::scrollEvent(ev); | |
434 } | |
435 | |
436 // -------------------------------------------------------------------------------------------------------------------- | |
437 | |
438 template <class ImageType> | |
439 struct ImageBaseSlider<ImageType>::PrivateData { | |
440 ImageType image; | |
441 float minimum; | |
442 float maximum; | |
443 float step; | |
444 float value; | |
445 float valueDef; | |
446 float valueTmp; | |
447 bool usingDefault; | |
448 | |
449 bool dragging; | |
450 bool checkable; | |
451 bool inverted; | |
452 bool valueIsSet; | |
453 double startedX; | |
454 double startedY; | |
455 | |
456 Callback* callback; | |
457 | |
458 Point<int> startPos; | |
459 Point<int> endPos; | |
460 Rectangle<double> sliderArea; | |
461 | |
462 PrivateData(const ImageType& img) | |
463 : image(img), | |
464 minimum(0.0f), | |
465 maximum(1.0f), | |
466 step(0.0f), | |
467 value(0.5f), | |
468 valueDef(value), | |
469 valueTmp(value), | |
470 usingDefault(false), | |
471 dragging(false), | |
472 checkable(false), | |
473 inverted(false), | |
474 valueIsSet(false), | |
475 startedX(0.0), | |
476 startedY(0.0), | |
477 callback(nullptr), | |
478 startPos(), | |
479 endPos(), | |
480 sliderArea() {} | |
481 | |
482 void recheckArea() noexcept | |
483 { | |
484 if (startPos.getY() == endPos.getY()) | |
485 { | |
486 // horizontal | |
487 sliderArea = Rectangle<double>(startPos.getX(), | |
488 startPos.getY(), | |
489 endPos.getX() + static_cast<int>(image.getWidth()) - startPos.getX(), | |
490 static_cast<int>(image.getHeight())); | |
491 } | |
492 else | |
493 { | |
494 // vertical | |
495 sliderArea = Rectangle<double>(startPos.getX(), | |
496 startPos.getY(), | |
497 static_cast<int>(image.getWidth()), | |
498 endPos.getY() + static_cast<int>(image.getHeight()) - startPos.getY()); | |
499 } | |
500 } | |
501 | |
502 DISTRHO_DECLARE_NON_COPYABLE(PrivateData) | |
503 }; | |
504 | |
505 // -------------------------------------------------------------------------------------------------------------------- | |
506 | |
507 template <class ImageType> | |
508 ImageBaseSlider<ImageType>::ImageBaseSlider(Widget* const parentWidget, const ImageType& image) noexcept | |
509 : SubWidget(parentWidget), | |
510 pData(new PrivateData(image)) | |
511 { | |
512 setNeedsFullViewportDrawing(); | |
513 } | |
514 | |
515 template <class ImageType> | |
516 ImageBaseSlider<ImageType>::~ImageBaseSlider() | |
517 { | |
518 delete pData; | |
519 } | |
520 | |
521 template <class ImageType> | |
522 float ImageBaseSlider<ImageType>::getValue() const noexcept | |
523 { | |
524 return pData->value; | |
525 } | |
526 | |
527 template <class ImageType> | |
528 void ImageBaseSlider<ImageType>::setValue(float value, bool sendCallback) noexcept | |
529 { | |
530 if (! pData->valueIsSet) | |
531 pData->valueIsSet = true; | |
532 | |
533 if (d_isEqual(pData->value, value)) | |
534 return; | |
535 | |
536 pData->value = value; | |
537 | |
538 if (d_isZero(pData->step)) | |
539 pData->valueTmp = value; | |
540 | |
541 repaint(); | |
542 | |
543 if (sendCallback && pData->callback != nullptr) | |
544 { | |
545 try { | |
546 pData->callback->imageSliderValueChanged(this, pData->value); | |
547 } DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setValue"); | |
548 } | |
549 } | |
550 | |
551 template <class ImageType> | |
552 void ImageBaseSlider<ImageType>::setStartPos(const Point<int>& startPos) noexcept | |
553 { | |
554 pData->startPos = startPos; | |
555 pData->recheckArea(); | |
556 } | |
557 | |
558 template <class ImageType> | |
559 void ImageBaseSlider<ImageType>::setStartPos(int x, int y) noexcept | |
560 { | |
561 setStartPos(Point<int>(x, y)); | |
562 } | |
563 | |
564 template <class ImageType> | |
565 void ImageBaseSlider<ImageType>::setEndPos(const Point<int>& endPos) noexcept | |
566 { | |
567 pData->endPos = endPos; | |
568 pData->recheckArea(); | |
569 } | |
570 | |
571 template <class ImageType> | |
572 void ImageBaseSlider<ImageType>::setEndPos(int x, int y) noexcept | |
573 { | |
574 setEndPos(Point<int>(x, y)); | |
575 } | |
576 | |
577 template <class ImageType> | |
578 void ImageBaseSlider<ImageType>::setCheckable(bool checkable) noexcept | |
579 { | |
580 if (pData->checkable == checkable) | |
581 return; | |
582 | |
583 pData->checkable = checkable; | |
584 repaint(); | |
585 } | |
586 | |
587 template <class ImageType> | |
588 void ImageBaseSlider<ImageType>::setInverted(bool inverted) noexcept | |
589 { | |
590 if (pData->inverted == inverted) | |
591 return; | |
592 | |
593 pData->inverted = inverted; | |
594 repaint(); | |
595 } | |
596 | |
597 template <class ImageType> | |
598 void ImageBaseSlider<ImageType>::setDefault(float value) noexcept | |
599 { | |
600 pData->valueDef = value; | |
601 pData->usingDefault = true; | |
602 } | |
603 | |
604 template <class ImageType> | |
605 void ImageBaseSlider<ImageType>::setRange(float min, float max) noexcept | |
606 { | |
607 pData->minimum = min; | |
608 pData->maximum = max; | |
609 | |
610 if (pData->value < min) | |
611 { | |
612 pData->value = min; | |
613 repaint(); | |
614 | |
615 if (pData->callback != nullptr && pData->valueIsSet) | |
616 { | |
617 try { | |
618 pData->callback->imageSliderValueChanged(this, pData->value); | |
619 } DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setRange < min"); | |
620 } | |
621 } | |
622 else if (pData->value > max) | |
623 { | |
624 pData->value = max; | |
625 repaint(); | |
626 | |
627 if (pData->callback != nullptr && pData->valueIsSet) | |
628 { | |
629 try { | |
630 pData->callback->imageSliderValueChanged(this, pData->value); | |
631 } DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setRange > max"); | |
632 } | |
633 } | |
634 } | |
635 | |
636 template <class ImageType> | |
637 void ImageBaseSlider<ImageType>::setStep(float step) noexcept | |
638 { | |
639 pData->step = step; | |
640 } | |
641 | |
642 template <class ImageType> | |
643 void ImageBaseSlider<ImageType>::setCallback(Callback* callback) noexcept | |
644 { | |
645 pData->callback = callback; | |
646 } | |
647 | |
648 template <class ImageType> | |
649 void ImageBaseSlider<ImageType>::onDisplay() | |
650 { | |
651 const GraphicsContext& context(getGraphicsContext()); | |
652 | |
653 #if 0 // DEBUG, paints slider area | |
654 Color(1.0f, 1.0f, 1.0f, 0.5f).setFor(context, true); | |
655 Rectangle<int>(pData->sliderArea.getX(), | |
656 pData->sliderArea.getY(), | |
657 pData->sliderArea.getX()+pData->sliderArea.getWidth(), | |
658 pData->sliderArea.getY()+pData->sliderArea.getHeight()).draw(context); | |
659 Color(1.0f, 1.0f, 1.0f, 1.0f).setFor(context, true); | |
660 #endif | |
661 | |
662 const float normValue = (pData->value - pData->minimum) / (pData->maximum - pData->minimum); | |
663 | |
664 int x, y; | |
665 | |
666 if (pData->startPos.getY() == pData->endPos.getY()) | |
667 { | |
668 // horizontal | |
669 if (pData->inverted) | |
670 x = pData->endPos.getX() - static_cast<int>(normValue*static_cast<float>(pData->endPos.getX()-pData->startPos.getX())); | |
671 else | |
672 x = pData->startPos.getX() + static_cast<int>(normValue*static_cast<float>(pData->endPos.getX()-pData->startPos.getX())); | |
673 | |
674 y = pData->startPos.getY(); | |
675 } | |
676 else | |
677 { | |
678 // vertical | |
679 x = pData->startPos.getX(); | |
680 | |
681 if (pData->inverted) | |
682 y = pData->endPos.getY() - static_cast<int>(normValue*static_cast<float>(pData->endPos.getY()-pData->startPos.getY())); | |
683 else | |
684 y = pData->startPos.getY() + static_cast<int>(normValue*static_cast<float>(pData->endPos.getY()-pData->startPos.getY())); | |
685 } | |
686 | |
687 pData->image.drawAt(context, x, y); | |
688 } | |
689 | |
690 template <class ImageType> | |
691 bool ImageBaseSlider<ImageType>::onMouse(const MouseEvent& ev) | |
692 { | |
693 if (ev.button != 1) | |
694 return false; | |
695 | |
696 if (ev.press) | |
697 { | |
698 if (! pData->sliderArea.contains(ev.pos)) | |
699 return false; | |
700 | |
701 if ((ev.mod & kModifierShift) != 0 && pData->usingDefault) | |
702 { | |
703 setValue(pData->valueDef, true); | |
704 pData->valueTmp = pData->value; | |
705 return true; | |
706 } | |
707 | |
708 if (pData->checkable) | |
709 { | |
710 const float value = d_isEqual(pData->valueTmp, pData->minimum) ? pData->maximum : pData->minimum; | |
711 setValue(value, true); | |
712 pData->valueTmp = pData->value; | |
713 return true; | |
714 } | |
715 | |
716 float vper; | |
717 const double x = ev.pos.getX(); | |
718 const double y = ev.pos.getY(); | |
719 | |
720 if (pData->startPos.getY() == pData->endPos.getY()) | |
721 { | |
722 // horizontal | |
723 vper = float(x - pData->sliderArea.getX()) / float(pData->sliderArea.getWidth()); | |
724 } | |
725 else | |
726 { | |
727 // vertical | |
728 vper = float(y - pData->sliderArea.getY()) / float(pData->sliderArea.getHeight()); | |
729 } | |
730 | |
731 float value; | |
732 | |
733 if (pData->inverted) | |
734 value = pData->maximum - vper * (pData->maximum - pData->minimum); | |
735 else | |
736 value = pData->minimum + vper * (pData->maximum - pData->minimum); | |
737 | |
738 if (value < pData->minimum) | |
739 { | |
740 pData->valueTmp = value = pData->minimum; | |
741 } | |
742 else if (value > pData->maximum) | |
743 { | |
744 pData->valueTmp = value = pData->maximum; | |
745 } | |
746 else if (d_isNotZero(pData->step)) | |
747 { | |
748 pData->valueTmp = value; | |
749 const float rest = std::fmod(value, pData->step); | |
750 value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f); | |
751 } | |
752 | |
753 pData->dragging = true; | |
754 pData->startedX = x; | |
755 pData->startedY = y; | |
756 | |
757 if (pData->callback != nullptr) | |
758 pData->callback->imageSliderDragStarted(this); | |
759 | |
760 setValue(value, true); | |
761 | |
762 return true; | |
763 } | |
764 else if (pData->dragging) | |
765 { | |
766 if (pData->callback != nullptr) | |
767 pData->callback->imageSliderDragFinished(this); | |
768 | |
769 pData->dragging = false; | |
770 return true; | |
771 } | |
772 | |
773 return false; | |
774 } | |
775 | |
776 template <class ImageType> | |
777 bool ImageBaseSlider<ImageType>::onMotion(const MotionEvent& ev) | |
778 { | |
779 if (! pData->dragging) | |
780 return false; | |
781 | |
782 const bool horizontal = pData->startPos.getY() == pData->endPos.getY(); | |
783 const double x = ev.pos.getX(); | |
784 const double y = ev.pos.getY(); | |
785 | |
786 if ((horizontal && pData->sliderArea.containsX(x)) || (pData->sliderArea.containsY(y) && ! horizontal)) | |
787 { | |
788 float vper; | |
789 | |
790 if (horizontal) | |
791 { | |
792 // horizontal | |
793 vper = float(x - pData->sliderArea.getX()) / float(pData->sliderArea.getWidth()); | |
794 } | |
795 else | |
796 { | |
797 // vertical | |
798 vper = float(y - pData->sliderArea.getY()) / float(pData->sliderArea.getHeight()); | |
799 } | |
800 | |
801 float value; | |
802 | |
803 if (pData->inverted) | |
804 value = pData->maximum - vper * (pData->maximum - pData->minimum); | |
805 else | |
806 value = pData->minimum + vper * (pData->maximum - pData->minimum); | |
807 | |
808 if (value < pData->minimum) | |
809 { | |
810 pData->valueTmp = value = pData->minimum; | |
811 } | |
812 else if (value > pData->maximum) | |
813 { | |
814 pData->valueTmp = value = pData->maximum; | |
815 } | |
816 else if (d_isNotZero(pData->step)) | |
817 { | |
818 pData->valueTmp = value; | |
819 const float rest = std::fmod(value, pData->step); | |
820 value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f); | |
821 } | |
822 | |
823 setValue(value, true); | |
824 } | |
825 else if (horizontal) | |
826 { | |
827 if (x < pData->sliderArea.getX()) | |
828 setValue(pData->inverted ? pData->maximum : pData->minimum, true); | |
829 else | |
830 setValue(pData->inverted ? pData->minimum : pData->maximum, true); | |
831 } | |
832 else | |
833 { | |
834 if (y < pData->sliderArea.getY()) | |
835 setValue(pData->inverted ? pData->maximum : pData->minimum, true); | |
836 else | |
837 setValue(pData->inverted ? pData->minimum : pData->maximum, true); | |
838 } | |
839 | |
840 return true; | |
841 } | |
842 | |
843 // -------------------------------------------------------------------------------------------------------------------- | |
844 | |
845 template <class ImageType> | |
846 struct ImageBaseSwitch<ImageType>::PrivateData { | |
847 ImageType imageNormal; | |
848 ImageType imageDown; | |
849 bool isDown; | |
850 Callback* callback; | |
851 | |
852 PrivateData(const ImageType& normal, const ImageType& down) | |
853 : imageNormal(normal), | |
854 imageDown(down), | |
855 isDown(false), | |
856 callback(nullptr) | |
857 { | |
858 DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); | |
859 } | |
860 | |
861 PrivateData(PrivateData* const other) | |
862 : imageNormal(other->imageNormal), | |
863 imageDown(other->imageDown), | |
864 isDown(other->isDown), | |
865 callback(other->callback) | |
866 { | |
867 DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); | |
868 } | |
869 | |
870 void assignFrom(PrivateData* const other) | |
871 { | |
872 imageNormal = other->imageNormal; | |
873 imageDown = other->imageDown; | |
874 isDown = other->isDown; | |
875 callback = other->callback; | |
876 DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); | |
877 } | |
878 | |
879 DISTRHO_DECLARE_NON_COPYABLE(PrivateData) | |
880 }; | |
881 | |
882 // -------------------------------------------------------------------------------------------------------------------- | |
883 | |
884 template <class ImageType> | |
885 ImageBaseSwitch<ImageType>::ImageBaseSwitch(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept | |
886 : SubWidget(parentWidget), | |
887 pData(new PrivateData(imageNormal, imageDown)) | |
888 { | |
889 setSize(imageNormal.getSize()); | |
890 } | |
891 | |
892 template <class ImageType> | |
893 ImageBaseSwitch<ImageType>::ImageBaseSwitch(const ImageBaseSwitch<ImageType>& imageSwitch) noexcept | |
894 : SubWidget(imageSwitch.getParentWidget()), | |
895 pData(new PrivateData(imageSwitch.pData)) | |
896 { | |
897 setSize(pData->imageNormal.getSize()); | |
898 } | |
899 | |
900 template <class ImageType> | |
901 ImageBaseSwitch<ImageType>& ImageBaseSwitch<ImageType>::operator=(const ImageBaseSwitch<ImageType>& imageSwitch) noexcept | |
902 { | |
903 pData->assignFrom(imageSwitch.pData); | |
904 setSize(pData->imageNormal.getSize()); | |
905 return *this; | |
906 } | |
907 | |
908 template <class ImageType> | |
909 ImageBaseSwitch<ImageType>::~ImageBaseSwitch() | |
910 { | |
911 delete pData; | |
912 } | |
913 | |
914 template <class ImageType> | |
915 bool ImageBaseSwitch<ImageType>::isDown() const noexcept | |
916 { | |
917 return pData->isDown; | |
918 } | |
919 | |
920 template <class ImageType> | |
921 void ImageBaseSwitch<ImageType>::setDown(const bool down) noexcept | |
922 { | |
923 if (pData->isDown == down) | |
924 return; | |
925 | |
926 pData->isDown = down; | |
927 repaint(); | |
928 } | |
929 | |
930 template <class ImageType> | |
931 void ImageBaseSwitch<ImageType>::setCallback(Callback* const callback) noexcept | |
932 { | |
933 pData->callback = callback; | |
934 } | |
935 | |
936 template <class ImageType> | |
937 void ImageBaseSwitch<ImageType>::onDisplay() | |
938 { | |
939 const GraphicsContext& context(getGraphicsContext()); | |
940 | |
941 if (pData->isDown) | |
942 pData->imageDown.draw(context); | |
943 else | |
944 pData->imageNormal.draw(context); | |
945 } | |
946 | |
947 template <class ImageType> | |
948 bool ImageBaseSwitch<ImageType>::onMouse(const MouseEvent& ev) | |
949 { | |
950 if (ev.press && contains(ev.pos)) | |
951 { | |
952 pData->isDown = !pData->isDown; | |
953 | |
954 repaint(); | |
955 | |
956 if (pData->callback != nullptr) | |
957 pData->callback->imageSwitchClicked(this, pData->isDown); | |
958 | |
959 return true; | |
960 } | |
961 | |
962 return false; | |
963 } | |
964 | |
965 // -------------------------------------------------------------------------------------------------------------------- | |
966 | |
967 END_NAMESPACE_DGL |