Audio plugin host https://kx.studio/carla
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.

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