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.

1200 lines
30KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2015 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 "../ImageWidgets.hpp"
  17. #include <cmath>
  18. START_NAMESPACE_DGL
  19. // -----------------------------------------------------------------------
  20. ImageAboutWindow::ImageAboutWindow(Window& parent, const Image& image)
  21. : Window(parent.getApp(), parent),
  22. Widget((Window&)*this),
  23. fImgBackground(image),
  24. leakDetector_ImageAboutWindow()
  25. {
  26. Window::setResizable(false);
  27. Window::setSize(image.getSize());
  28. Window::setTitle("About");
  29. }
  30. ImageAboutWindow::ImageAboutWindow(Widget* widget, const Image& image)
  31. : Window(widget->getParentApp(), widget->getParentWindow()),
  32. Widget((Window&)*this),
  33. fImgBackground(image),
  34. leakDetector_ImageAboutWindow()
  35. {
  36. Window::setResizable(false);
  37. Window::setSize(image.getSize());
  38. Window::setTitle("About");
  39. }
  40. void ImageAboutWindow::setImage(const Image& image)
  41. {
  42. if (fImgBackground == image)
  43. return;
  44. fImgBackground = image;
  45. Window::setSize(image.getSize());
  46. }
  47. void ImageAboutWindow::onDisplay()
  48. {
  49. fImgBackground.draw();
  50. }
  51. bool ImageAboutWindow::onKeyboard(const KeyboardEvent& ev)
  52. {
  53. if (ev.press && ev.key == CHAR_ESCAPE)
  54. {
  55. Window::close();
  56. return true;
  57. }
  58. return false;
  59. }
  60. bool ImageAboutWindow::onMouse(const MouseEvent& ev)
  61. {
  62. if (ev.press)
  63. {
  64. Window::close();
  65. return true;
  66. }
  67. return false;
  68. }
  69. void ImageAboutWindow::onReshape(uint width, uint height)
  70. {
  71. Widget::setSize(width, height);
  72. Window::onReshape(width, height);
  73. }
  74. // -----------------------------------------------------------------------
  75. ImageButton::ImageButton(Window& parent, const Image& image) noexcept
  76. : Widget(parent),
  77. fImageNormal(image),
  78. fImageHover(image),
  79. fImageDown(image),
  80. fCurButton(-1),
  81. fCurState(0),
  82. fCallback(nullptr),
  83. leakDetector_ImageButton()
  84. {
  85. setSize(image.getSize());
  86. }
  87. ImageButton::ImageButton(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept
  88. : Widget(parent),
  89. fImageNormal(imageNormal),
  90. fImageHover(imageNormal),
  91. fImageDown(imageDown),
  92. fCurButton(-1),
  93. fCurState(0),
  94. fCallback(nullptr),
  95. leakDetector_ImageButton()
  96. {
  97. DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize());
  98. setSize(imageNormal.getSize());
  99. }
  100. ImageButton::ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown) noexcept
  101. : Widget(parent),
  102. fImageNormal(imageNormal),
  103. fImageHover(imageHover),
  104. fImageDown(imageDown),
  105. fCurButton(-1),
  106. fCurState(0),
  107. fCallback(nullptr),
  108. leakDetector_ImageButton()
  109. {
  110. DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize());
  111. setSize(imageNormal.getSize());
  112. }
  113. ImageButton::ImageButton(Widget* widget, const Image& image) noexcept
  114. : Widget(widget->getParentWindow()),
  115. fImageNormal(image),
  116. fImageHover(image),
  117. fImageDown(image),
  118. fCurButton(-1),
  119. fCurState(0),
  120. fCallback(nullptr),
  121. leakDetector_ImageButton()
  122. {
  123. setSize(image.getSize());
  124. }
  125. ImageButton::ImageButton(Widget* widget, const Image& imageNormal, const Image& imageDown) noexcept
  126. : Widget(widget->getParentWindow()),
  127. fImageNormal(imageNormal),
  128. fImageHover(imageNormal),
  129. fImageDown(imageDown),
  130. fCurButton(-1),
  131. fCurState(0),
  132. fCallback(nullptr),
  133. leakDetector_ImageButton()
  134. {
  135. DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize());
  136. setSize(imageNormal.getSize());
  137. }
  138. ImageButton::ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown) noexcept
  139. : Widget(widget->getParentWindow()),
  140. fImageNormal(imageNormal),
  141. fImageHover(imageHover),
  142. fImageDown(imageDown),
  143. fCurButton(-1),
  144. fCurState(0),
  145. fCallback(nullptr),
  146. leakDetector_ImageButton()
  147. {
  148. DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize());
  149. setSize(imageNormal.getSize());
  150. }
  151. void ImageButton::setCallback(Callback* callback) noexcept
  152. {
  153. fCallback = callback;
  154. }
  155. void ImageButton::onDisplay()
  156. {
  157. switch (fCurState)
  158. {
  159. case 2:
  160. fImageDown.draw();
  161. break;
  162. case 1:
  163. fImageHover.draw();
  164. break;
  165. default:
  166. fImageNormal.draw();
  167. break;
  168. }
  169. }
  170. bool ImageButton::onMouse(const MouseEvent& ev)
  171. {
  172. // button was released, handle it now
  173. if (fCurButton != -1 && ! ev.press)
  174. {
  175. DISTRHO_SAFE_ASSERT(fCurState == 2);
  176. // release button
  177. const int button = fCurButton;
  178. fCurButton = -1;
  179. // cursor was moved outside the button bounds, ignore click
  180. if (! contains(ev.pos))
  181. {
  182. fCurState = 0;
  183. repaint();
  184. return true;
  185. }
  186. // still on bounds, register click
  187. fCurState = 1;
  188. repaint();
  189. if (fCallback != nullptr)
  190. fCallback->imageButtonClicked(this, button);
  191. return true;
  192. }
  193. // button was pressed, wait for release
  194. if (ev.press && contains(ev.pos))
  195. {
  196. fCurButton = ev.button;
  197. fCurState = 2;
  198. repaint();
  199. return true;
  200. }
  201. return false;
  202. }
  203. bool ImageButton::onMotion(const MotionEvent& ev)
  204. {
  205. // keep pressed
  206. if (fCurButton != -1)
  207. return true;
  208. if (contains(ev.pos))
  209. {
  210. // check if entering hover
  211. if (fCurState == 0)
  212. {
  213. fCurState = 1;
  214. repaint();
  215. return true;
  216. }
  217. }
  218. else
  219. {
  220. // check if exiting hover
  221. if (fCurState == 1)
  222. {
  223. fCurState = 0;
  224. repaint();
  225. return true;
  226. }
  227. }
  228. return false;
  229. }
  230. // -----------------------------------------------------------------------
  231. ImageKnob::ImageKnob(Window& parent, const Image& image, Orientation orientation) noexcept
  232. : Widget(parent),
  233. fImage(image),
  234. fMinimum(0.0f),
  235. fMaximum(1.0f),
  236. fStep(0.0f),
  237. fValue(0.5f),
  238. fValueDef(fValue),
  239. fValueTmp(fValue),
  240. fUsingDefault(false),
  241. fUsingLog(false),
  242. fOrientation(orientation),
  243. fRotationAngle(0),
  244. fDragging(false),
  245. fLastX(0),
  246. fLastY(0),
  247. fCallback(nullptr),
  248. fIsImgVertical(image.getHeight() > image.getWidth()),
  249. fImgLayerWidth(fIsImgVertical ? image.getWidth() : image.getHeight()),
  250. fImgLayerHeight(fImgLayerWidth),
  251. fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerHeight : image.getWidth()/fImgLayerWidth),
  252. fIsReady(false),
  253. fTextureId(0),
  254. leakDetector_ImageKnob()
  255. {
  256. glGenTextures(1, &fTextureId);
  257. setSize(fImgLayerWidth, fImgLayerHeight);
  258. }
  259. ImageKnob::ImageKnob(Widget* widget, const Image& image, Orientation orientation) noexcept
  260. : Widget(widget->getParentWindow()),
  261. fImage(image),
  262. fMinimum(0.0f),
  263. fMaximum(1.0f),
  264. fStep(0.0f),
  265. fValue(0.5f),
  266. fValueDef(fValue),
  267. fValueTmp(fValue),
  268. fUsingDefault(false),
  269. fUsingLog(false),
  270. fOrientation(orientation),
  271. fRotationAngle(0),
  272. fDragging(false),
  273. fLastX(0),
  274. fLastY(0),
  275. fCallback(nullptr),
  276. fIsImgVertical(image.getHeight() > image.getWidth()),
  277. fImgLayerWidth(fIsImgVertical ? image.getWidth() : image.getHeight()),
  278. fImgLayerHeight(fImgLayerWidth),
  279. fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerHeight : image.getWidth()/fImgLayerWidth),
  280. fIsReady(false),
  281. fTextureId(0),
  282. leakDetector_ImageKnob()
  283. {
  284. glGenTextures(1, &fTextureId);
  285. setSize(fImgLayerWidth, fImgLayerHeight);
  286. }
  287. ImageKnob::ImageKnob(const ImageKnob& imageKnob)
  288. : Widget(imageKnob.getParentWindow()),
  289. fImage(imageKnob.fImage),
  290. fMinimum(imageKnob.fMinimum),
  291. fMaximum(imageKnob.fMaximum),
  292. fStep(imageKnob.fStep),
  293. fValue(imageKnob.fValue),
  294. fValueDef(imageKnob.fValueDef),
  295. fValueTmp(fValue),
  296. fUsingDefault(imageKnob.fUsingDefault),
  297. fUsingLog(imageKnob.fUsingLog),
  298. fOrientation(imageKnob.fOrientation),
  299. fRotationAngle(imageKnob.fRotationAngle),
  300. fDragging(false),
  301. fLastX(0),
  302. fLastY(0),
  303. fCallback(imageKnob.fCallback),
  304. fIsImgVertical(imageKnob.fIsImgVertical),
  305. fImgLayerWidth(imageKnob.fImgLayerWidth),
  306. fImgLayerHeight(imageKnob.fImgLayerHeight),
  307. fImgLayerCount(imageKnob.fImgLayerCount),
  308. fIsReady(false),
  309. fTextureId(0),
  310. leakDetector_ImageKnob()
  311. {
  312. glGenTextures(1, &fTextureId);
  313. setSize(fImgLayerWidth, fImgLayerHeight);
  314. }
  315. ImageKnob& ImageKnob::operator=(const ImageKnob& imageKnob)
  316. {
  317. fImage = imageKnob.fImage;
  318. fMinimum = imageKnob.fMinimum;
  319. fMaximum = imageKnob.fMaximum;
  320. fStep = imageKnob.fStep;
  321. fValue = imageKnob.fValue;
  322. fValueDef = imageKnob.fValueDef;
  323. fValueTmp = fValue;
  324. fUsingDefault = imageKnob.fUsingDefault;
  325. fUsingLog = imageKnob.fUsingLog;
  326. fOrientation = imageKnob.fOrientation;
  327. fRotationAngle = imageKnob.fRotationAngle;
  328. fDragging = false;
  329. fLastX = 0;
  330. fLastY = 0;
  331. fCallback = imageKnob.fCallback;
  332. fIsImgVertical = imageKnob.fIsImgVertical;
  333. fImgLayerWidth = imageKnob.fImgLayerWidth;
  334. fImgLayerHeight = imageKnob.fImgLayerHeight;
  335. fImgLayerCount = imageKnob.fImgLayerCount;
  336. fIsReady = false;
  337. if (fTextureId != 0)
  338. {
  339. glDeleteTextures(1, &fTextureId);
  340. fTextureId = 0;
  341. }
  342. glGenTextures(1, &fTextureId);
  343. setSize(fImgLayerWidth, fImgLayerHeight);
  344. return *this;
  345. }
  346. ImageKnob::~ImageKnob()
  347. {
  348. if (fTextureId != 0)
  349. {
  350. glDeleteTextures(1, &fTextureId);
  351. fTextureId = 0;
  352. }
  353. }
  354. float ImageKnob::getValue() const noexcept
  355. {
  356. return fValue;
  357. }
  358. // NOTE: value is assumed to be scaled if using log
  359. void ImageKnob::setDefault(float value) noexcept
  360. {
  361. fValueDef = value;
  362. fUsingDefault = true;
  363. }
  364. void ImageKnob::setRange(float min, float max) noexcept
  365. {
  366. DISTRHO_SAFE_ASSERT_RETURN(max > min,);
  367. if (fValue < min)
  368. {
  369. fValue = min;
  370. repaint();
  371. if (fCallback != nullptr)
  372. {
  373. try {
  374. fCallback->imageKnobValueChanged(this, fValue);
  375. } DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange < min");
  376. }
  377. }
  378. else if (fValue > max)
  379. {
  380. fValue = max;
  381. repaint();
  382. if (fCallback != nullptr)
  383. {
  384. try {
  385. fCallback->imageKnobValueChanged(this, fValue);
  386. } DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange > max");
  387. }
  388. }
  389. fMinimum = min;
  390. fMaximum = max;
  391. }
  392. void ImageKnob::setStep(float step) noexcept
  393. {
  394. fStep = step;
  395. }
  396. // NOTE: value is assumed to be scaled if using log
  397. void ImageKnob::setValue(float value, bool sendCallback) noexcept
  398. {
  399. if (d_isEqual(fValue, value))
  400. return;
  401. fValue = value;
  402. if (d_isZero(fStep))
  403. fValueTmp = value;
  404. if (fRotationAngle == 0)
  405. fIsReady = false;
  406. repaint();
  407. if (sendCallback && fCallback != nullptr)
  408. {
  409. try {
  410. fCallback->imageKnobValueChanged(this, fValue);
  411. } DISTRHO_SAFE_EXCEPTION("ImageKnob::setValue");
  412. }
  413. }
  414. void ImageKnob::setUsingLogScale(bool yesNo) noexcept
  415. {
  416. fUsingLog = yesNo;
  417. }
  418. void ImageKnob::setCallback(Callback* callback) noexcept
  419. {
  420. fCallback = callback;
  421. }
  422. void ImageKnob::setOrientation(Orientation orientation) noexcept
  423. {
  424. if (fOrientation == orientation)
  425. return;
  426. fOrientation = orientation;
  427. }
  428. void ImageKnob::setRotationAngle(int angle)
  429. {
  430. if (fRotationAngle == angle)
  431. return;
  432. fRotationAngle = angle;
  433. fIsReady = false;
  434. }
  435. void ImageKnob::setImageLayerCount(uint count) noexcept
  436. {
  437. DISTRHO_SAFE_ASSERT_RETURN(count > 1,);
  438. fImgLayerCount = count;
  439. if (fIsImgVertical)
  440. fImgLayerHeight = fImage.getHeight()/count;
  441. else
  442. fImgLayerWidth = fImage.getWidth()/count;
  443. setSize(fImgLayerWidth, fImgLayerHeight);
  444. }
  445. void ImageKnob::onDisplay()
  446. {
  447. const float normValue = ((fUsingLog ? _invlogscale(fValue) : fValue) - fMinimum) / (fMaximum - fMinimum);
  448. glEnable(GL_TEXTURE_2D);
  449. glBindTexture(GL_TEXTURE_2D, fTextureId);
  450. if (! fIsReady)
  451. {
  452. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  453. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  454. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
  455. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
  456. static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
  457. glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);
  458. glPixelStorei(GL_PACK_ALIGNMENT, 1);
  459. glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  460. uint imageDataOffset = 0;
  461. if (fRotationAngle == 0)
  462. {
  463. DISTRHO_SAFE_ASSERT_RETURN(fImgLayerCount > 0,);
  464. DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,);
  465. const uint& v1(fIsImgVertical ? fImgLayerWidth : fImgLayerHeight);
  466. const uint& v2(fIsImgVertical ? fImgLayerHeight : fImgLayerWidth);
  467. const uint layerDataSize = v1 * v2 * ((fImage.getFormat() == GL_BGRA || fImage.getFormat() == GL_RGBA) ? 4 : 3);
  468. /* */ imageDataOffset = layerDataSize * uint(normValue * float(fImgLayerCount-1));
  469. }
  470. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
  471. static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0,
  472. fImage.getFormat(), fImage.getType(), fImage.getRawData() + imageDataOffset);
  473. fIsReady = true;
  474. }
  475. const int w = static_cast<int>(getWidth());
  476. const int h = static_cast<int>(getHeight());
  477. if (fRotationAngle != 0)
  478. {
  479. glPushMatrix();
  480. const int w2 = w/2;
  481. const int h2 = h/2;
  482. glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f);
  483. glRotatef(normValue*static_cast<float>(fRotationAngle), 0.0f, 0.0f, 1.0f);
  484. Rectangle<int>(-w2, -h2, w, h).draw();
  485. glPopMatrix();
  486. }
  487. else
  488. {
  489. Rectangle<int>(0, 0, w, h).draw();
  490. }
  491. glBindTexture(GL_TEXTURE_2D, 0);
  492. glDisable(GL_TEXTURE_2D);
  493. }
  494. bool ImageKnob::onMouse(const MouseEvent& ev)
  495. {
  496. if (ev.button != 1)
  497. return false;
  498. if (ev.press)
  499. {
  500. if (! contains(ev.pos))
  501. return false;
  502. if ((ev.mod & MODIFIER_SHIFT) != 0 && fUsingDefault)
  503. {
  504. setValue(fValueDef, true);
  505. fValueTmp = fValue;
  506. return true;
  507. }
  508. fDragging = true;
  509. fLastX = ev.pos.getX();
  510. fLastY = ev.pos.getY();
  511. if (fCallback != nullptr)
  512. fCallback->imageKnobDragStarted(this);
  513. return true;
  514. }
  515. else if (fDragging)
  516. {
  517. if (fCallback != nullptr)
  518. fCallback->imageKnobDragFinished(this);
  519. fDragging = false;
  520. return true;
  521. }
  522. return false;
  523. }
  524. bool ImageKnob::onMotion(const MotionEvent& ev)
  525. {
  526. if (! fDragging)
  527. return false;
  528. bool doVal = false;
  529. float d, value = 0.0f;
  530. if (fOrientation == ImageKnob::Horizontal)
  531. {
  532. if (const int movX = ev.pos.getX() - fLastX)
  533. {
  534. d = (ev.mod & MODIFIER_CTRL) ? 2000.0f : 200.0f;
  535. value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movX));
  536. doVal = true;
  537. }
  538. }
  539. else if (fOrientation == ImageKnob::Vertical)
  540. {
  541. if (const int movY = fLastY - ev.pos.getY())
  542. {
  543. d = (ev.mod & MODIFIER_CTRL) ? 2000.0f : 200.0f;
  544. value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movY));
  545. doVal = true;
  546. }
  547. }
  548. if (! doVal)
  549. return false;
  550. if (fUsingLog)
  551. value = _logscale(value);
  552. if (value < fMinimum)
  553. {
  554. fValueTmp = value = fMinimum;
  555. }
  556. else if (value > fMaximum)
  557. {
  558. fValueTmp = value = fMaximum;
  559. }
  560. else if (d_isNotZero(fStep))
  561. {
  562. fValueTmp = value;
  563. const float rest = std::fmod(value, fStep);
  564. value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f);
  565. }
  566. setValue(value, true);
  567. fLastX = ev.pos.getX();
  568. fLastY = ev.pos.getY();
  569. return true;
  570. }
  571. bool ImageKnob::onScroll(const ScrollEvent& ev)
  572. {
  573. if (! contains(ev.pos))
  574. return false;
  575. const float d = (ev.mod & MODIFIER_CTRL) ? 2000.0f : 200.0f;
  576. float value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * 10.f * ev.delta.getY());
  577. if (fUsingLog)
  578. value = _logscale(value);
  579. if (value < fMinimum)
  580. {
  581. fValueTmp = value = fMinimum;
  582. }
  583. else if (value > fMaximum)
  584. {
  585. fValueTmp = value = fMaximum;
  586. }
  587. else if (d_isNotZero(fStep))
  588. {
  589. fValueTmp = value;
  590. const float rest = std::fmod(value, fStep);
  591. value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f);
  592. }
  593. setValue(value, true);
  594. return true;
  595. }
  596. // -----------------------------------------------------------------------
  597. float ImageKnob::_logscale(float value) const
  598. {
  599. const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum);
  600. const float a = fMaximum/std::exp(fMaximum*b);
  601. return a * std::exp(b*value);
  602. }
  603. float ImageKnob::_invlogscale(float value) const
  604. {
  605. const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum);
  606. const float a = fMaximum/std::exp(fMaximum*b);
  607. return std::log(value/a)/b;
  608. }
  609. // -----------------------------------------------------------------------
  610. ImageSlider::ImageSlider(Window& parent, const Image& image) noexcept
  611. : Widget(parent),
  612. fImage(image),
  613. fMinimum(0.0f),
  614. fMaximum(1.0f),
  615. fStep(0.0f),
  616. fValue(0.5f),
  617. fValueTmp(fValue),
  618. fDragging(false),
  619. fInverted(false),
  620. fStartedX(0),
  621. fStartedY(0),
  622. fCallback(nullptr),
  623. fStartPos(),
  624. fEndPos(),
  625. fSliderArea(),
  626. leakDetector_ImageSlider()
  627. {
  628. fNeedsFullViewport = true;
  629. }
  630. ImageSlider::ImageSlider(Widget* widget, const Image& image) noexcept
  631. : Widget(widget->getParentWindow()),
  632. fImage(image),
  633. fMinimum(0.0f),
  634. fMaximum(1.0f),
  635. fStep(0.0f),
  636. fValue(0.5f),
  637. fValueTmp(fValue),
  638. fDragging(false),
  639. fInverted(false),
  640. fStartedX(0),
  641. fStartedY(0),
  642. fCallback(nullptr),
  643. fStartPos(),
  644. fEndPos(),
  645. fSliderArea(),
  646. leakDetector_ImageSlider()
  647. {
  648. fNeedsFullViewport = true;
  649. }
  650. ImageSlider::ImageSlider(const ImageSlider& imageSlider) noexcept
  651. : Widget(imageSlider.getParentWindow()),
  652. fImage(imageSlider.fImage),
  653. fMinimum(imageSlider.fMinimum),
  654. fMaximum(imageSlider.fMaximum),
  655. fStep(imageSlider.fStep),
  656. fValue(imageSlider.fValue),
  657. fValueTmp(fValue),
  658. fDragging(false),
  659. fInverted(imageSlider.fInverted),
  660. fStartedX(0),
  661. fStartedY(0),
  662. fCallback(imageSlider.fCallback),
  663. fStartPos(imageSlider.fStartPos),
  664. fEndPos(imageSlider.fEndPos),
  665. fSliderArea(imageSlider.fSliderArea),
  666. leakDetector_ImageSlider()
  667. {
  668. fNeedsFullViewport = true;
  669. }
  670. ImageSlider& ImageSlider::operator=(const ImageSlider& imageSlider) noexcept
  671. {
  672. fImage = imageSlider.fImage;
  673. fMinimum = imageSlider.fMinimum;
  674. fMaximum = imageSlider.fMaximum;
  675. fStep = imageSlider.fStep;
  676. fValue = imageSlider.fValue;
  677. fValueTmp = fValue;
  678. fDragging = false;
  679. fInverted = imageSlider.fInverted;
  680. fStartedX = 0;
  681. fStartedY = 0;
  682. fCallback = imageSlider.fCallback;
  683. fStartPos = imageSlider.fStartPos;
  684. fEndPos = imageSlider.fEndPos;
  685. fSliderArea = imageSlider.fSliderArea;
  686. return *this;
  687. }
  688. float ImageSlider::getValue() const noexcept
  689. {
  690. return fValue;
  691. }
  692. void ImageSlider::setStartPos(const Point<int>& startPos) noexcept
  693. {
  694. fStartPos = startPos;
  695. _recheckArea();
  696. }
  697. void ImageSlider::setStartPos(int x, int y) noexcept
  698. {
  699. setStartPos(Point<int>(x, y));
  700. }
  701. void ImageSlider::setEndPos(const Point<int>& endPos) noexcept
  702. {
  703. fEndPos = endPos;
  704. _recheckArea();
  705. }
  706. void ImageSlider::setEndPos(int x, int y) noexcept
  707. {
  708. setEndPos(Point<int>(x, y));
  709. }
  710. void ImageSlider::setInverted(bool inverted) noexcept
  711. {
  712. if (fInverted == inverted)
  713. return;
  714. fInverted = inverted;
  715. repaint();
  716. }
  717. void ImageSlider::setRange(float min, float max) noexcept
  718. {
  719. if (fValue < min)
  720. {
  721. fValue = min;
  722. repaint();
  723. if (fCallback != nullptr)
  724. {
  725. try {
  726. fCallback->imageSliderValueChanged(this, fValue);
  727. } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange < min");
  728. }
  729. }
  730. else if (fValue > max)
  731. {
  732. fValue = max;
  733. repaint();
  734. if (fCallback != nullptr)
  735. {
  736. try {
  737. fCallback->imageSliderValueChanged(this, fValue);
  738. } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange > max");
  739. }
  740. }
  741. fMinimum = min;
  742. fMaximum = max;
  743. }
  744. void ImageSlider::setStep(float step) noexcept
  745. {
  746. fStep = step;
  747. }
  748. void ImageSlider::setValue(float value, bool sendCallback) noexcept
  749. {
  750. if (d_isEqual(fValue, value))
  751. return;
  752. fValue = value;
  753. if (d_isZero(fStep))
  754. fValueTmp = value;
  755. repaint();
  756. if (sendCallback && fCallback != nullptr)
  757. {
  758. try {
  759. fCallback->imageSliderValueChanged(this, fValue);
  760. } DISTRHO_SAFE_EXCEPTION("ImageSlider::setValue");
  761. }
  762. }
  763. void ImageSlider::setCallback(Callback* callback) noexcept
  764. {
  765. fCallback = callback;
  766. }
  767. void ImageSlider::onDisplay()
  768. {
  769. #if 0 // DEBUG, paints slider area
  770. glColor3f(0.4f, 0.5f, 0.1f);
  771. glRecti(fSliderArea.getX(), fSliderArea.getY(), fSliderArea.getX()+fSliderArea.getWidth(), fSliderArea.getY()+fSliderArea.getHeight());
  772. glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
  773. #endif
  774. const float normValue = (fValue - fMinimum) / (fMaximum - fMinimum);
  775. int x, y;
  776. if (fStartPos.getY() == fEndPos.getY())
  777. {
  778. // horizontal
  779. if (fInverted)
  780. x = fEndPos.getX() - static_cast<int>(normValue*static_cast<float>(fEndPos.getX()-fStartPos.getX()));
  781. else
  782. x = fStartPos.getX() + static_cast<int>(normValue*static_cast<float>(fEndPos.getX()-fStartPos.getX()));
  783. y = fStartPos.getY();
  784. }
  785. else
  786. {
  787. // vertical
  788. x = fStartPos.getX();
  789. if (fInverted)
  790. y = fEndPos.getY() - static_cast<int>(normValue*static_cast<float>(fEndPos.getY()-fStartPos.getY()));
  791. else
  792. y = fStartPos.getY() + static_cast<int>(normValue*static_cast<float>(fEndPos.getY()-fStartPos.getY()));
  793. }
  794. fImage.drawAt(x, y);
  795. }
  796. bool ImageSlider::onMouse(const MouseEvent& ev)
  797. {
  798. if (ev.button != 1)
  799. return false;
  800. if (ev.press)
  801. {
  802. if (! fSliderArea.contains(ev.pos))
  803. return false;
  804. float vper;
  805. const int x = ev.pos.getX();
  806. const int y = ev.pos.getY();
  807. if (fStartPos.getY() == fEndPos.getY())
  808. {
  809. // horizontal
  810. vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth());
  811. }
  812. else
  813. {
  814. // vertical
  815. vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight());
  816. }
  817. float value;
  818. if (fInverted)
  819. value = fMaximum - vper * (fMaximum - fMinimum);
  820. else
  821. value = fMinimum + vper * (fMaximum - fMinimum);
  822. if (value < fMinimum)
  823. {
  824. fValueTmp = value = fMinimum;
  825. }
  826. else if (value > fMaximum)
  827. {
  828. fValueTmp = value = fMaximum;
  829. }
  830. else if (d_isNotZero(fStep))
  831. {
  832. fValueTmp = value;
  833. const float rest = std::fmod(value, fStep);
  834. value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f);
  835. }
  836. fDragging = true;
  837. fStartedX = x;
  838. fStartedY = y;
  839. if (fCallback != nullptr)
  840. fCallback->imageSliderDragStarted(this);
  841. setValue(value, true);
  842. return true;
  843. }
  844. else if (fDragging)
  845. {
  846. if (fCallback != nullptr)
  847. fCallback->imageSliderDragFinished(this);
  848. fDragging = false;
  849. return true;
  850. }
  851. return false;
  852. }
  853. bool ImageSlider::onMotion(const MotionEvent& ev)
  854. {
  855. if (! fDragging)
  856. return false;
  857. const bool horizontal = fStartPos.getY() == fEndPos.getY();
  858. const int x = ev.pos.getX();
  859. const int y = ev.pos.getY();
  860. if ((horizontal && fSliderArea.containsX(x)) || (fSliderArea.containsY(y) && ! horizontal))
  861. {
  862. float vper;
  863. if (horizontal)
  864. {
  865. // horizontal
  866. vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth());
  867. }
  868. else
  869. {
  870. // vertical
  871. vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight());
  872. }
  873. float value;
  874. if (fInverted)
  875. value = fMaximum - vper * (fMaximum - fMinimum);
  876. else
  877. value = fMinimum + vper * (fMaximum - fMinimum);
  878. if (value < fMinimum)
  879. {
  880. fValueTmp = value = fMinimum;
  881. }
  882. else if (value > fMaximum)
  883. {
  884. fValueTmp = value = fMaximum;
  885. }
  886. else if (d_isNotZero(fStep))
  887. {
  888. fValueTmp = value;
  889. const float rest = std::fmod(value, fStep);
  890. value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f);
  891. }
  892. setValue(value, true);
  893. }
  894. else if (horizontal)
  895. {
  896. if (x < fSliderArea.getX())
  897. setValue(fInverted ? fMaximum : fMinimum, true);
  898. else
  899. setValue(fInverted ? fMinimum : fMaximum, true);
  900. }
  901. else
  902. {
  903. if (y < fSliderArea.getY())
  904. setValue(fInverted ? fMaximum : fMinimum, true);
  905. else
  906. setValue(fInverted ? fMinimum : fMaximum, true);
  907. }
  908. return true;
  909. }
  910. void ImageSlider::_recheckArea() noexcept
  911. {
  912. if (fStartPos.getY() == fEndPos.getY())
  913. {
  914. // horizontal
  915. fSliderArea = Rectangle<int>(fStartPos.getX(),
  916. fStartPos.getY(),
  917. fEndPos.getX() + static_cast<int>(fImage.getWidth()) - fStartPos.getX(),
  918. static_cast<int>(fImage.getHeight()));
  919. }
  920. else
  921. {
  922. // vertical
  923. fSliderArea = Rectangle<int>(fStartPos.getX(),
  924. fStartPos.getY(),
  925. static_cast<int>(fImage.getWidth()),
  926. fEndPos.getY() + static_cast<int>(fImage.getHeight()) - fStartPos.getY());
  927. }
  928. }
  929. // -----------------------------------------------------------------------
  930. ImageSwitch::ImageSwitch(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept
  931. : Widget(parent),
  932. fImageNormal(imageNormal),
  933. fImageDown(imageDown),
  934. fIsDown(false),
  935. fCallback(nullptr),
  936. leakDetector_ImageSwitch()
  937. {
  938. DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize());
  939. setSize(fImageNormal.getSize());
  940. }
  941. ImageSwitch::ImageSwitch(Widget* widget, const Image& imageNormal, const Image& imageDown) noexcept
  942. : Widget(widget->getParentWindow()),
  943. fImageNormal(imageNormal),
  944. fImageDown(imageDown),
  945. fIsDown(false),
  946. fCallback(nullptr),
  947. leakDetector_ImageSwitch()
  948. {
  949. DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize());
  950. setSize(fImageNormal.getSize());
  951. }
  952. ImageSwitch::ImageSwitch(const ImageSwitch& imageSwitch) noexcept
  953. : Widget(imageSwitch.getParentWindow()),
  954. fImageNormal(imageSwitch.fImageNormal),
  955. fImageDown(imageSwitch.fImageDown),
  956. fIsDown(imageSwitch.fIsDown),
  957. fCallback(imageSwitch.fCallback),
  958. leakDetector_ImageSwitch()
  959. {
  960. DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize());
  961. setSize(fImageNormal.getSize());
  962. }
  963. ImageSwitch& ImageSwitch::operator=(const ImageSwitch& imageSwitch) noexcept
  964. {
  965. fImageNormal = imageSwitch.fImageNormal;
  966. fImageDown = imageSwitch.fImageDown;
  967. fIsDown = imageSwitch.fIsDown;
  968. fCallback = imageSwitch.fCallback;
  969. DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize());
  970. setSize(fImageNormal.getSize());
  971. return *this;
  972. }
  973. bool ImageSwitch::isDown() const noexcept
  974. {
  975. return fIsDown;
  976. }
  977. void ImageSwitch::setDown(bool down) noexcept
  978. {
  979. if (fIsDown == down)
  980. return;
  981. fIsDown = down;
  982. repaint();
  983. }
  984. void ImageSwitch::setCallback(Callback* callback) noexcept
  985. {
  986. fCallback = callback;
  987. }
  988. void ImageSwitch::onDisplay()
  989. {
  990. if (fIsDown)
  991. fImageDown.draw();
  992. else
  993. fImageNormal.draw();
  994. }
  995. bool ImageSwitch::onMouse(const MouseEvent& ev)
  996. {
  997. if (ev.press && contains(ev.pos))
  998. {
  999. fIsDown = !fIsDown;
  1000. repaint();
  1001. if (fCallback != nullptr)
  1002. fCallback->imageSwitchClicked(this, fIsDown);
  1003. return true;
  1004. }
  1005. return false;
  1006. }
  1007. // -----------------------------------------------------------------------
  1008. END_NAMESPACE_DGL