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.

975 lines
27KB

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