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.

925 lines
26KB

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