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.

1062 lines
29KB

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