DISTRHO Plugin Framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

898 lines
25KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2021 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. #include "../ImageBaseWidgets.hpp"
  17. #include "../Color.hpp"
  18. START_NAMESPACE_DGL
  19. // --------------------------------------------------------------------------------------------------------------------
  20. template <class ImageType>
  21. ImageBaseAboutWindow<ImageType>::ImageBaseAboutWindow(Window& parentWindow, const ImageType& image)
  22. : StandaloneWindow(parentWindow.getApp(), parentWindow),
  23. img(image)
  24. {
  25. setResizable(false);
  26. setTitle("About");
  27. if (image.isValid())
  28. setSize(image.getSize());
  29. }
  30. template <class ImageType>
  31. ImageBaseAboutWindow<ImageType>::ImageBaseAboutWindow(TopLevelWidget* const parentTopLevelWidget, const ImageType& image)
  32. : StandaloneWindow(parentTopLevelWidget->getApp(), parentTopLevelWidget->getWindow()),
  33. img(image)
  34. {
  35. setResizable(false);
  36. setTitle("About");
  37. if (image.isValid())
  38. setSize(image.getSize());
  39. }
  40. template <class ImageType>
  41. void ImageBaseAboutWindow<ImageType>::setImage(const ImageType& image)
  42. {
  43. if (img == image)
  44. return;
  45. img = image;
  46. setSize(image.getSize());
  47. }
  48. template <class ImageType>
  49. void ImageBaseAboutWindow<ImageType>::onDisplay()
  50. {
  51. img.draw(getGraphicsContext());
  52. }
  53. template <class ImageType>
  54. bool ImageBaseAboutWindow<ImageType>::onKeyboard(const KeyboardEvent& ev)
  55. {
  56. if (ev.press && ev.key == kKeyEscape)
  57. {
  58. close();
  59. return true;
  60. }
  61. return false;
  62. }
  63. template <class ImageType>
  64. bool ImageBaseAboutWindow<ImageType>::onMouse(const MouseEvent& ev)
  65. {
  66. if (ev.press)
  67. {
  68. close();
  69. return true;
  70. }
  71. return false;
  72. }
  73. // --------------------------------------------------------------------------------------------------------------------
  74. template <class ImageType>
  75. struct ImageBaseButton<ImageType>::PrivateData : public ButtonEventHandler::Callback {
  76. ImageBaseButton<ImageType>::Callback* callback;
  77. ImageType imageNormal;
  78. ImageType imageHover;
  79. ImageType imageDown;
  80. PrivateData(const ImageType& normal, const ImageType& hover, const ImageType& down)
  81. : callback(nullptr),
  82. imageNormal(normal),
  83. imageHover(hover),
  84. imageDown(down) {}
  85. void buttonClicked(SubWidget* widget, int button) override
  86. {
  87. if (callback != nullptr)
  88. if (ImageBaseButton* const imageButton = dynamic_cast<ImageBaseButton*>(widget))
  89. callback->imageButtonClicked(imageButton, button);
  90. }
  91. DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
  92. };
  93. // --------------------------------------------------------------------------------------------------------------------
  94. template <class ImageType>
  95. ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& image)
  96. : SubWidget(parentWidget),
  97. ButtonEventHandler(this),
  98. pData(new PrivateData(image, image, image))
  99. {
  100. ButtonEventHandler::setCallback(pData);
  101. setSize(image.getSize());
  102. }
  103. template <class ImageType>
  104. ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown)
  105. : SubWidget(parentWidget),
  106. ButtonEventHandler(this),
  107. pData(new PrivateData(imageNormal, imageNormal, imageDown))
  108. {
  109. DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize());
  110. ButtonEventHandler::setCallback(pData);
  111. setSize(imageNormal.getSize());
  112. }
  113. template <class ImageType>
  114. ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown)
  115. : SubWidget(parentWidget),
  116. ButtonEventHandler(this),
  117. pData(new PrivateData(imageNormal, imageHover, imageDown))
  118. {
  119. DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize());
  120. ButtonEventHandler::setCallback(pData);
  121. setSize(imageNormal.getSize());
  122. }
  123. template <class ImageType>
  124. ImageBaseButton<ImageType>::~ImageBaseButton()
  125. {
  126. delete pData;
  127. }
  128. template <class ImageType>
  129. void ImageBaseButton<ImageType>::setCallback(Callback* callback) noexcept
  130. {
  131. pData->callback = callback;
  132. }
  133. template <class ImageType>
  134. void ImageBaseButton<ImageType>::onDisplay()
  135. {
  136. const GraphicsContext& context(getGraphicsContext());
  137. const State state = ButtonEventHandler::getState();
  138. if (state & kButtonStateActive)
  139. pData->imageDown.draw(context);
  140. else if (state & kButtonStateHover)
  141. pData->imageHover.draw(context);
  142. else
  143. pData->imageNormal.draw(context);
  144. }
  145. template <class ImageType>
  146. bool ImageBaseButton<ImageType>::onMouse(const MouseEvent& ev)
  147. {
  148. if (SubWidget::onMouse(ev))
  149. return true;
  150. return ButtonEventHandler::mouseEvent(ev);
  151. }
  152. template <class ImageType>
  153. bool ImageBaseButton<ImageType>::onMotion(const MotionEvent& ev)
  154. {
  155. if (SubWidget::onMotion(ev))
  156. return true;
  157. return ButtonEventHandler::motionEvent(ev);
  158. }
  159. // --------------------------------------------------------------------------------------------------------------------
  160. template <class ImageType>
  161. struct ImageBaseKnob<ImageType>::PrivateData : public KnobEventHandler::Callback {
  162. ImageBaseKnob<ImageType>::Callback* callback;
  163. ImageType image;
  164. int rotationAngle;
  165. bool alwaysRepaint;
  166. bool isImgVertical;
  167. uint imgLayerWidth;
  168. uint imgLayerHeight;
  169. uint imgLayerCount;
  170. bool isReady;
  171. union {
  172. uint glTextureId;
  173. void* cairoSurface;
  174. };
  175. explicit PrivateData(const ImageType& img)
  176. : callback(nullptr),
  177. image(img),
  178. rotationAngle(0),
  179. alwaysRepaint(false),
  180. isImgVertical(img.getHeight() > img.getWidth()),
  181. imgLayerWidth(isImgVertical ? img.getWidth() : img.getHeight()),
  182. imgLayerHeight(imgLayerWidth),
  183. imgLayerCount(isImgVertical ? img.getHeight()/imgLayerHeight : img.getWidth()/imgLayerWidth),
  184. isReady(false)
  185. {
  186. init();
  187. }
  188. explicit PrivateData(PrivateData* const other)
  189. : callback(other->callback),
  190. image(other->image),
  191. rotationAngle(other->rotationAngle),
  192. alwaysRepaint(other->alwaysRepaint),
  193. isImgVertical(other->isImgVertical),
  194. imgLayerWidth(other->imgLayerWidth),
  195. imgLayerHeight(other->imgLayerHeight),
  196. imgLayerCount(other->imgLayerCount),
  197. isReady(false)
  198. {
  199. init();
  200. }
  201. void assignFrom(PrivateData* const other)
  202. {
  203. cleanup();
  204. image = other->image;
  205. rotationAngle = other->rotationAngle;
  206. callback = other->callback;
  207. alwaysRepaint = other->alwaysRepaint;
  208. isImgVertical = other->isImgVertical;
  209. imgLayerWidth = other->imgLayerWidth;
  210. imgLayerHeight = other->imgLayerHeight;
  211. imgLayerCount = other->imgLayerCount;
  212. isReady = false;
  213. init();
  214. }
  215. ~PrivateData()
  216. {
  217. cleanup();
  218. }
  219. void knobDragStarted(SubWidget* const widget) override
  220. {
  221. if (callback != nullptr)
  222. if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget))
  223. callback->imageKnobDragStarted(imageKnob);
  224. }
  225. void knobDragFinished(SubWidget* const widget) override
  226. {
  227. if (callback != nullptr)
  228. if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget))
  229. callback->imageKnobDragFinished(imageKnob);
  230. }
  231. void knobValueChanged(SubWidget* const widget, const float value) override
  232. {
  233. if (rotationAngle == 0 || alwaysRepaint)
  234. isReady = false;
  235. if (callback != nullptr)
  236. if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget))
  237. callback->imageKnobValueChanged(imageKnob, value);
  238. }
  239. // implemented independently per graphics backend
  240. void init();
  241. void cleanup();
  242. DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
  243. };
  244. // --------------------------------------------------------------------------------------------------------------------
  245. template <class ImageType>
  246. ImageBaseKnob<ImageType>::ImageBaseKnob(Widget* const parentWidget,
  247. const ImageType& image,
  248. const Orientation orientation) noexcept
  249. : SubWidget(parentWidget),
  250. KnobEventHandler(this),
  251. pData(new PrivateData(image))
  252. {
  253. setOrientation(orientation);
  254. setSize(pData->imgLayerWidth, pData->imgLayerHeight);
  255. }
  256. template <class ImageType>
  257. ImageBaseKnob<ImageType>::ImageBaseKnob(const ImageBaseKnob<ImageType>& imageKnob)
  258. : SubWidget(imageKnob.getParentWidget()),
  259. KnobEventHandler(this, imageKnob),
  260. pData(new PrivateData(imageKnob.pData))
  261. {
  262. setOrientation(imageKnob.getOrientation());
  263. setSize(pData->imgLayerWidth, pData->imgLayerHeight);
  264. }
  265. template <class ImageType>
  266. ImageBaseKnob<ImageType>& ImageBaseKnob<ImageType>::operator=(const ImageBaseKnob<ImageType>& imageKnob)
  267. {
  268. KnobEventHandler::operator=(imageKnob);
  269. pData->assignFrom(imageKnob.pData);
  270. setSize(pData->imgLayerWidth, pData->imgLayerHeight);
  271. return *this;
  272. }
  273. template <class ImageType>
  274. ImageBaseKnob<ImageType>::~ImageBaseKnob()
  275. {
  276. delete pData;
  277. }
  278. template <class ImageType>
  279. void ImageBaseKnob<ImageType>::setCallback(Callback* callback) noexcept
  280. {
  281. pData->callback = callback;
  282. }
  283. template <class ImageType>
  284. void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept
  285. {
  286. DISTRHO_SAFE_ASSERT_RETURN(count > 1,);
  287. pData->imgLayerCount = count;
  288. if (pData->isImgVertical)
  289. pData->imgLayerHeight = pData->image.getHeight()/count;
  290. else
  291. pData->imgLayerWidth = pData->image.getWidth()/count;
  292. setSize(pData->imgLayerWidth, pData->imgLayerHeight);
  293. }
  294. template <class ImageType>
  295. void ImageBaseKnob<ImageType>::setRotationAngle(int angle)
  296. {
  297. if (pData->rotationAngle == angle)
  298. return;
  299. pData->rotationAngle = angle;
  300. pData->isReady = false;
  301. }
  302. template <class ImageType>
  303. bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev)
  304. {
  305. if (SubWidget::onMouse(ev))
  306. return true;
  307. return KnobEventHandler::mouseEvent(ev);
  308. }
  309. template <class ImageType>
  310. bool ImageBaseKnob<ImageType>::onMotion(const MotionEvent& ev)
  311. {
  312. if (SubWidget::onMotion(ev))
  313. return true;
  314. return KnobEventHandler::motionEvent(ev);
  315. }
  316. template <class ImageType>
  317. bool ImageBaseKnob<ImageType>::onScroll(const ScrollEvent& ev)
  318. {
  319. if (SubWidget::onScroll(ev))
  320. return true;
  321. return KnobEventHandler::scrollEvent(ev);
  322. }
  323. // --------------------------------------------------------------------------------------------------------------------
  324. template <class ImageType>
  325. struct ImageBaseSlider<ImageType>::PrivateData {
  326. ImageType image;
  327. float minimum;
  328. float maximum;
  329. float step;
  330. float value;
  331. float valueDef;
  332. float valueTmp;
  333. bool usingDefault;
  334. bool dragging;
  335. bool inverted;
  336. bool valueIsSet;
  337. double startedX;
  338. double startedY;
  339. Callback* callback;
  340. Point<int> startPos;
  341. Point<int> endPos;
  342. Rectangle<double> sliderArea;
  343. PrivateData(const ImageType& img)
  344. : image(img),
  345. minimum(0.0f),
  346. maximum(1.0f),
  347. step(0.0f),
  348. value(0.5f),
  349. valueDef(value),
  350. valueTmp(value),
  351. usingDefault(false),
  352. dragging(false),
  353. inverted(false),
  354. valueIsSet(false),
  355. startedX(0.0),
  356. startedY(0.0),
  357. callback(nullptr),
  358. startPos(),
  359. endPos(),
  360. sliderArea() {}
  361. void recheckArea() noexcept
  362. {
  363. if (startPos.getY() == endPos.getY())
  364. {
  365. // horizontal
  366. sliderArea = Rectangle<double>(startPos.getX(),
  367. startPos.getY(),
  368. endPos.getX() + static_cast<int>(image.getWidth()) - startPos.getX(),
  369. static_cast<int>(image.getHeight()));
  370. }
  371. else
  372. {
  373. // vertical
  374. sliderArea = Rectangle<double>(startPos.getX(),
  375. startPos.getY(),
  376. static_cast<int>(image.getWidth()),
  377. endPos.getY() + static_cast<int>(image.getHeight()) - startPos.getY());
  378. }
  379. }
  380. DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
  381. };
  382. // --------------------------------------------------------------------------------------------------------------------
  383. template <class ImageType>
  384. ImageBaseSlider<ImageType>::ImageBaseSlider(Widget* const parentWidget, const ImageType& image) noexcept
  385. : SubWidget(parentWidget),
  386. pData(new PrivateData(image))
  387. {
  388. setNeedsFullViewportDrawing();
  389. }
  390. template <class ImageType>
  391. ImageBaseSlider<ImageType>::~ImageBaseSlider()
  392. {
  393. delete pData;
  394. }
  395. template <class ImageType>
  396. float ImageBaseSlider<ImageType>::getValue() const noexcept
  397. {
  398. return pData->value;
  399. }
  400. template <class ImageType>
  401. void ImageBaseSlider<ImageType>::setValue(float value, bool sendCallback) noexcept
  402. {
  403. if (! pData->valueIsSet)
  404. pData->valueIsSet = true;
  405. if (d_isEqual(pData->value, value))
  406. return;
  407. pData->value = value;
  408. if (d_isZero(pData->step))
  409. pData->valueTmp = value;
  410. repaint();
  411. if (sendCallback && pData->callback != nullptr)
  412. {
  413. try {
  414. pData->callback->imageSliderValueChanged(this, pData->value);
  415. } DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setValue");
  416. }
  417. }
  418. template <class ImageType>
  419. void ImageBaseSlider<ImageType>::setStartPos(const Point<int>& startPos) noexcept
  420. {
  421. pData->startPos = startPos;
  422. pData->recheckArea();
  423. }
  424. template <class ImageType>
  425. void ImageBaseSlider<ImageType>::setStartPos(int x, int y) noexcept
  426. {
  427. setStartPos(Point<int>(x, y));
  428. }
  429. template <class ImageType>
  430. void ImageBaseSlider<ImageType>::setEndPos(const Point<int>& endPos) noexcept
  431. {
  432. pData->endPos = endPos;
  433. pData->recheckArea();
  434. }
  435. template <class ImageType>
  436. void ImageBaseSlider<ImageType>::setEndPos(int x, int y) noexcept
  437. {
  438. setEndPos(Point<int>(x, y));
  439. }
  440. template <class ImageType>
  441. void ImageBaseSlider<ImageType>::setInverted(bool inverted) noexcept
  442. {
  443. if (pData->inverted == inverted)
  444. return;
  445. pData->inverted = inverted;
  446. repaint();
  447. }
  448. template <class ImageType>
  449. void ImageBaseSlider<ImageType>::setDefault(float value) noexcept
  450. {
  451. pData->valueDef = value;
  452. pData->usingDefault = true;
  453. }
  454. template <class ImageType>
  455. void ImageBaseSlider<ImageType>::setRange(float min, float max) noexcept
  456. {
  457. pData->minimum = min;
  458. pData->maximum = max;
  459. if (pData->value < min)
  460. {
  461. pData->value = min;
  462. repaint();
  463. if (pData->callback != nullptr && pData->valueIsSet)
  464. {
  465. try {
  466. pData->callback->imageSliderValueChanged(this, pData->value);
  467. } DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setRange < min");
  468. }
  469. }
  470. else if (pData->value > max)
  471. {
  472. pData->value = max;
  473. repaint();
  474. if (pData->callback != nullptr && pData->valueIsSet)
  475. {
  476. try {
  477. pData->callback->imageSliderValueChanged(this, pData->value);
  478. } DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setRange > max");
  479. }
  480. }
  481. }
  482. template <class ImageType>
  483. void ImageBaseSlider<ImageType>::setStep(float step) noexcept
  484. {
  485. pData->step = step;
  486. }
  487. template <class ImageType>
  488. void ImageBaseSlider<ImageType>::setCallback(Callback* callback) noexcept
  489. {
  490. pData->callback = callback;
  491. }
  492. template <class ImageType>
  493. void ImageBaseSlider<ImageType>::onDisplay()
  494. {
  495. const GraphicsContext& context(getGraphicsContext());
  496. #if 0 // DEBUG, paints slider area
  497. Color(1.0f, 1.0f, 1.0f, 0.5f).setFor(context, true);
  498. Rectangle<int>(pData->sliderArea.getX(),
  499. pData->sliderArea.getY(),
  500. pData->sliderArea.getX()+pData->sliderArea.getWidth(),
  501. pData->sliderArea.getY()+pData->sliderArea.getHeight()).draw(context);
  502. Color(1.0f, 1.0f, 1.0f, 1.0f).setFor(context, true);
  503. #endif
  504. const float normValue = (pData->value - pData->minimum) / (pData->maximum - pData->minimum);
  505. int x, y;
  506. if (pData->startPos.getY() == pData->endPos.getY())
  507. {
  508. // horizontal
  509. if (pData->inverted)
  510. x = pData->endPos.getX() - static_cast<int>(normValue*static_cast<float>(pData->endPos.getX()-pData->startPos.getX()));
  511. else
  512. x = pData->startPos.getX() + static_cast<int>(normValue*static_cast<float>(pData->endPos.getX()-pData->startPos.getX()));
  513. y = pData->startPos.getY();
  514. }
  515. else
  516. {
  517. // vertical
  518. x = pData->startPos.getX();
  519. if (pData->inverted)
  520. y = pData->endPos.getY() - static_cast<int>(normValue*static_cast<float>(pData->endPos.getY()-pData->startPos.getY()));
  521. else
  522. y = pData->startPos.getY() + static_cast<int>(normValue*static_cast<float>(pData->endPos.getY()-pData->startPos.getY()));
  523. }
  524. pData->image.drawAt(context, x, y);
  525. }
  526. template <class ImageType>
  527. bool ImageBaseSlider<ImageType>::onMouse(const MouseEvent& ev)
  528. {
  529. if (ev.button != 1)
  530. return false;
  531. if (ev.press)
  532. {
  533. if (! pData->sliderArea.contains(ev.pos))
  534. return false;
  535. if ((ev.mod & kModifierShift) != 0 && pData->usingDefault)
  536. {
  537. setValue(pData->valueDef, true);
  538. pData->valueTmp = pData->value;
  539. return true;
  540. }
  541. float vper;
  542. const double x = ev.pos.getX();
  543. const double y = ev.pos.getY();
  544. if (pData->startPos.getY() == pData->endPos.getY())
  545. {
  546. // horizontal
  547. vper = float(x - pData->sliderArea.getX()) / float(pData->sliderArea.getWidth());
  548. }
  549. else
  550. {
  551. // vertical
  552. vper = float(y - pData->sliderArea.getY()) / float(pData->sliderArea.getHeight());
  553. }
  554. float value;
  555. if (pData->inverted)
  556. value = pData->maximum - vper * (pData->maximum - pData->minimum);
  557. else
  558. value = pData->minimum + vper * (pData->maximum - pData->minimum);
  559. if (value < pData->minimum)
  560. {
  561. pData->valueTmp = value = pData->minimum;
  562. }
  563. else if (value > pData->maximum)
  564. {
  565. pData->valueTmp = value = pData->maximum;
  566. }
  567. else if (d_isNotZero(pData->step))
  568. {
  569. pData->valueTmp = value;
  570. const float rest = std::fmod(value, pData->step);
  571. value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f);
  572. }
  573. pData->dragging = true;
  574. pData->startedX = x;
  575. pData->startedY = y;
  576. if (pData->callback != nullptr)
  577. pData->callback->imageSliderDragStarted(this);
  578. setValue(value, true);
  579. return true;
  580. }
  581. else if (pData->dragging)
  582. {
  583. if (pData->callback != nullptr)
  584. pData->callback->imageSliderDragFinished(this);
  585. pData->dragging = false;
  586. return true;
  587. }
  588. return false;
  589. }
  590. template <class ImageType>
  591. bool ImageBaseSlider<ImageType>::onMotion(const MotionEvent& ev)
  592. {
  593. if (! pData->dragging)
  594. return false;
  595. const bool horizontal = pData->startPos.getY() == pData->endPos.getY();
  596. const double x = ev.pos.getX();
  597. const double y = ev.pos.getY();
  598. if ((horizontal && pData->sliderArea.containsX(x)) || (pData->sliderArea.containsY(y) && ! horizontal))
  599. {
  600. float vper;
  601. if (horizontal)
  602. {
  603. // horizontal
  604. vper = float(x - pData->sliderArea.getX()) / float(pData->sliderArea.getWidth());
  605. }
  606. else
  607. {
  608. // vertical
  609. vper = float(y - pData->sliderArea.getY()) / float(pData->sliderArea.getHeight());
  610. }
  611. float value;
  612. if (pData->inverted)
  613. value = pData->maximum - vper * (pData->maximum - pData->minimum);
  614. else
  615. value = pData->minimum + vper * (pData->maximum - pData->minimum);
  616. if (value < pData->minimum)
  617. {
  618. pData->valueTmp = value = pData->minimum;
  619. }
  620. else if (value > pData->maximum)
  621. {
  622. pData->valueTmp = value = pData->maximum;
  623. }
  624. else if (d_isNotZero(pData->step))
  625. {
  626. pData->valueTmp = value;
  627. const float rest = std::fmod(value, pData->step);
  628. value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f);
  629. }
  630. setValue(value, true);
  631. }
  632. else if (horizontal)
  633. {
  634. if (x < pData->sliderArea.getX())
  635. setValue(pData->inverted ? pData->maximum : pData->minimum, true);
  636. else
  637. setValue(pData->inverted ? pData->minimum : pData->maximum, true);
  638. }
  639. else
  640. {
  641. if (y < pData->sliderArea.getY())
  642. setValue(pData->inverted ? pData->maximum : pData->minimum, true);
  643. else
  644. setValue(pData->inverted ? pData->minimum : pData->maximum, true);
  645. }
  646. return true;
  647. }
  648. // --------------------------------------------------------------------------------------------------------------------
  649. template <class ImageType>
  650. struct ImageBaseSwitch<ImageType>::PrivateData {
  651. ImageType imageNormal;
  652. ImageType imageDown;
  653. bool isDown;
  654. Callback* callback;
  655. PrivateData(const ImageType& normal, const ImageType& down)
  656. : imageNormal(normal),
  657. imageDown(down),
  658. isDown(false),
  659. callback(nullptr)
  660. {
  661. DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize());
  662. }
  663. PrivateData(PrivateData* const other)
  664. : imageNormal(other->imageNormal),
  665. imageDown(other->imageDown),
  666. isDown(other->isDown),
  667. callback(other->callback)
  668. {
  669. DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize());
  670. }
  671. void assignFrom(PrivateData* const other)
  672. {
  673. imageNormal = other->imageNormal;
  674. imageDown = other->imageDown;
  675. isDown = other->isDown;
  676. callback = other->callback;
  677. DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize());
  678. }
  679. DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
  680. };
  681. // --------------------------------------------------------------------------------------------------------------------
  682. template <class ImageType>
  683. ImageBaseSwitch<ImageType>::ImageBaseSwitch(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept
  684. : SubWidget(parentWidget),
  685. pData(new PrivateData(imageNormal, imageDown))
  686. {
  687. setSize(imageNormal.getSize());
  688. }
  689. template <class ImageType>
  690. ImageBaseSwitch<ImageType>::ImageBaseSwitch(const ImageBaseSwitch<ImageType>& imageSwitch) noexcept
  691. : SubWidget(imageSwitch.getParentWidget()),
  692. pData(new PrivateData(imageSwitch.pData))
  693. {
  694. setSize(pData->imageNormal.getSize());
  695. }
  696. template <class ImageType>
  697. ImageBaseSwitch<ImageType>& ImageBaseSwitch<ImageType>::operator=(const ImageBaseSwitch<ImageType>& imageSwitch) noexcept
  698. {
  699. pData->assignFrom(imageSwitch.pData);
  700. setSize(pData->imageNormal.getSize());
  701. return *this;
  702. }
  703. template <class ImageType>
  704. ImageBaseSwitch<ImageType>::~ImageBaseSwitch()
  705. {
  706. delete pData;
  707. }
  708. template <class ImageType>
  709. bool ImageBaseSwitch<ImageType>::isDown() const noexcept
  710. {
  711. return pData->isDown;
  712. }
  713. template <class ImageType>
  714. void ImageBaseSwitch<ImageType>::setDown(const bool down) noexcept
  715. {
  716. if (pData->isDown == down)
  717. return;
  718. pData->isDown = down;
  719. repaint();
  720. }
  721. template <class ImageType>
  722. void ImageBaseSwitch<ImageType>::setCallback(Callback* const callback) noexcept
  723. {
  724. pData->callback = callback;
  725. }
  726. template <class ImageType>
  727. void ImageBaseSwitch<ImageType>::onDisplay()
  728. {
  729. const GraphicsContext& context(getGraphicsContext());
  730. if (pData->isDown)
  731. pData->imageDown.draw(context);
  732. else
  733. pData->imageNormal.draw(context);
  734. }
  735. template <class ImageType>
  736. bool ImageBaseSwitch<ImageType>::onMouse(const MouseEvent& ev)
  737. {
  738. if (ev.press && contains(ev.pos))
  739. {
  740. pData->isDown = !pData->isDown;
  741. repaint();
  742. if (pData->callback != nullptr)
  743. pData->callback->imageSwitchClicked(this, pData->isDown);
  744. return true;
  745. }
  746. return false;
  747. }
  748. // --------------------------------------------------------------------------------------------------------------------
  749. END_NAMESPACE_DGL