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.

1109 lines
28KB

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