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.

1108 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 d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
  512. float value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * 10.f * ev.delta.getY());
  513. if (fUsingLog)
  514. value = _logscale(value);
  515. if (value < fMinimum)
  516. {
  517. fValueTmp = value = fMinimum;
  518. }
  519. else if (value > fMaximum)
  520. {
  521. fValueTmp = value = fMaximum;
  522. }
  523. else if (d_isNotZero(fStep))
  524. {
  525. fValueTmp = value;
  526. const float rest = std::fmod(value, fStep);
  527. value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f);
  528. }
  529. setValue(value, true);
  530. return true;
  531. }
  532. // -----------------------------------------------------------------------
  533. float ImageKnob::_logscale(float value) const
  534. {
  535. const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum);
  536. const float a = fMaximum/std::exp(fMaximum*b);
  537. return a * std::exp(b*value);
  538. }
  539. float ImageKnob::_invlogscale(float value) const
  540. {
  541. const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum);
  542. const float a = fMaximum/std::exp(fMaximum*b);
  543. return std::log(value/a)/b;
  544. }
  545. // -----------------------------------------------------------------------
  546. ImageSlider::ImageSlider(Window& parent, const Image& image) noexcept
  547. : Widget(parent),
  548. fImage(image),
  549. fMinimum(0.0f),
  550. fMaximum(1.0f),
  551. fStep(0.0f),
  552. fValue(0.5f),
  553. fValueDef(fValue),
  554. fValueTmp(fValue),
  555. fUsingDefault(false),
  556. fDragging(false),
  557. fInverted(false),
  558. fValueIsSet(false),
  559. fStartedX(0),
  560. fStartedY(0),
  561. fCallback(nullptr),
  562. fStartPos(),
  563. fEndPos(),
  564. fSliderArea()
  565. {
  566. pData->needsFullViewport = true;
  567. }
  568. ImageSlider::ImageSlider(Widget* widget, const Image& image) noexcept
  569. : Widget(widget->getParentWindow()),
  570. fImage(image),
  571. fMinimum(0.0f),
  572. fMaximum(1.0f),
  573. fStep(0.0f),
  574. fValue(0.5f),
  575. fValueDef(fValue),
  576. fValueTmp(fValue),
  577. fUsingDefault(false),
  578. fDragging(false),
  579. fInverted(false),
  580. fValueIsSet(false),
  581. fStartedX(0),
  582. fStartedY(0),
  583. fCallback(nullptr),
  584. fStartPos(),
  585. fEndPos(),
  586. fSliderArea()
  587. {
  588. pData->needsFullViewport = true;
  589. }
  590. float ImageSlider::getValue() const noexcept
  591. {
  592. return fValue;
  593. }
  594. void ImageSlider::setValue(float value, bool sendCallback) noexcept
  595. {
  596. if (! fValueIsSet)
  597. fValueIsSet = true;
  598. if (d_isEqual(fValue, value))
  599. return;
  600. fValue = value;
  601. if (d_isZero(fStep))
  602. fValueTmp = value;
  603. repaint();
  604. if (sendCallback && fCallback != nullptr)
  605. {
  606. try {
  607. fCallback->imageSliderValueChanged(this, fValue);
  608. } DISTRHO_SAFE_EXCEPTION("ImageSlider::setValue");
  609. }
  610. }
  611. void ImageSlider::setStartPos(const Point<int>& startPos) noexcept
  612. {
  613. fStartPos = startPos;
  614. _recheckArea();
  615. }
  616. void ImageSlider::setStartPos(int x, int y) noexcept
  617. {
  618. setStartPos(Point<int>(x, y));
  619. }
  620. void ImageSlider::setEndPos(const Point<int>& endPos) noexcept
  621. {
  622. fEndPos = endPos;
  623. _recheckArea();
  624. }
  625. void ImageSlider::setEndPos(int x, int y) noexcept
  626. {
  627. setEndPos(Point<int>(x, y));
  628. }
  629. void ImageSlider::setInverted(bool inverted) noexcept
  630. {
  631. if (fInverted == inverted)
  632. return;
  633. fInverted = inverted;
  634. repaint();
  635. }
  636. void ImageSlider::setDefault(float value) noexcept
  637. {
  638. fValueDef = value;
  639. fUsingDefault = true;
  640. }
  641. void ImageSlider::setRange(float min, float max) noexcept
  642. {
  643. fMinimum = min;
  644. fMaximum = max;
  645. if (fValue < min)
  646. {
  647. fValue = min;
  648. repaint();
  649. if (fCallback != nullptr && fValueIsSet)
  650. {
  651. try {
  652. fCallback->imageSliderValueChanged(this, fValue);
  653. } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange < min");
  654. }
  655. }
  656. else if (fValue > max)
  657. {
  658. fValue = max;
  659. repaint();
  660. if (fCallback != nullptr && fValueIsSet)
  661. {
  662. try {
  663. fCallback->imageSliderValueChanged(this, fValue);
  664. } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange > max");
  665. }
  666. }
  667. }
  668. void ImageSlider::setStep(float step) noexcept
  669. {
  670. fStep = step;
  671. }
  672. void ImageSlider::setCallback(Callback* callback) noexcept
  673. {
  674. fCallback = callback;
  675. }
  676. void ImageSlider::onDisplay()
  677. {
  678. #if 0 // DEBUG, paints slider area
  679. glColor3f(0.4f, 0.5f, 0.1f);
  680. glRecti(fSliderArea.getX(), fSliderArea.getY(), fSliderArea.getX()+fSliderArea.getWidth(), fSliderArea.getY()+fSliderArea.getHeight());
  681. glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
  682. #endif
  683. const float normValue = (fValue - fMinimum) / (fMaximum - fMinimum);
  684. int x, y;
  685. if (fStartPos.getY() == fEndPos.getY())
  686. {
  687. // horizontal
  688. if (fInverted)
  689. x = fEndPos.getX() - static_cast<int>(normValue*static_cast<float>(fEndPos.getX()-fStartPos.getX()));
  690. else
  691. x = fStartPos.getX() + static_cast<int>(normValue*static_cast<float>(fEndPos.getX()-fStartPos.getX()));
  692. y = fStartPos.getY();
  693. }
  694. else
  695. {
  696. // vertical
  697. x = fStartPos.getX();
  698. if (fInverted)
  699. y = fEndPos.getY() - static_cast<int>(normValue*static_cast<float>(fEndPos.getY()-fStartPos.getY()));
  700. else
  701. y = fStartPos.getY() + static_cast<int>(normValue*static_cast<float>(fEndPos.getY()-fStartPos.getY()));
  702. }
  703. fImage.drawAt(x, y);
  704. }
  705. bool ImageSlider::onMouse(const MouseEvent& ev)
  706. {
  707. if (ev.button != 1)
  708. return false;
  709. if (ev.press)
  710. {
  711. if (! fSliderArea.contains(ev.pos))
  712. return false;
  713. if ((ev.mod & kModifierShift) != 0 && fUsingDefault)
  714. {
  715. setValue(fValueDef, true);
  716. fValueTmp = fValue;
  717. return true;
  718. }
  719. float vper;
  720. const int x = ev.pos.getX();
  721. const int y = ev.pos.getY();
  722. if (fStartPos.getY() == fEndPos.getY())
  723. {
  724. // horizontal
  725. vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth());
  726. }
  727. else
  728. {
  729. // vertical
  730. vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight());
  731. }
  732. float value;
  733. if (fInverted)
  734. value = fMaximum - vper * (fMaximum - fMinimum);
  735. else
  736. value = fMinimum + vper * (fMaximum - fMinimum);
  737. if (value < fMinimum)
  738. {
  739. fValueTmp = value = fMinimum;
  740. }
  741. else if (value > fMaximum)
  742. {
  743. fValueTmp = value = fMaximum;
  744. }
  745. else if (d_isNotZero(fStep))
  746. {
  747. fValueTmp = value;
  748. const float rest = std::fmod(value, fStep);
  749. value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f);
  750. }
  751. fDragging = true;
  752. fStartedX = x;
  753. fStartedY = y;
  754. if (fCallback != nullptr)
  755. fCallback->imageSliderDragStarted(this);
  756. setValue(value, true);
  757. return true;
  758. }
  759. else if (fDragging)
  760. {
  761. if (fCallback != nullptr)
  762. fCallback->imageSliderDragFinished(this);
  763. fDragging = false;
  764. return true;
  765. }
  766. return false;
  767. }
  768. bool ImageSlider::onMotion(const MotionEvent& ev)
  769. {
  770. if (! fDragging)
  771. return false;
  772. const bool horizontal = fStartPos.getY() == fEndPos.getY();
  773. const int x = ev.pos.getX();
  774. const int y = ev.pos.getY();
  775. if ((horizontal && fSliderArea.containsX(x)) || (fSliderArea.containsY(y) && ! horizontal))
  776. {
  777. float vper;
  778. if (horizontal)
  779. {
  780. // horizontal
  781. vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth());
  782. }
  783. else
  784. {
  785. // vertical
  786. vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight());
  787. }
  788. float value;
  789. if (fInverted)
  790. value = fMaximum - vper * (fMaximum - fMinimum);
  791. else
  792. value = fMinimum + vper * (fMaximum - fMinimum);
  793. if (value < fMinimum)
  794. {
  795. fValueTmp = value = fMinimum;
  796. }
  797. else if (value > fMaximum)
  798. {
  799. fValueTmp = value = fMaximum;
  800. }
  801. else if (d_isNotZero(fStep))
  802. {
  803. fValueTmp = value;
  804. const float rest = std::fmod(value, fStep);
  805. value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f);
  806. }
  807. setValue(value, true);
  808. }
  809. else if (horizontal)
  810. {
  811. if (x < fSliderArea.getX())
  812. setValue(fInverted ? fMaximum : fMinimum, true);
  813. else
  814. setValue(fInverted ? fMinimum : fMaximum, true);
  815. }
  816. else
  817. {
  818. if (y < fSliderArea.getY())
  819. setValue(fInverted ? fMaximum : fMinimum, true);
  820. else
  821. setValue(fInverted ? fMinimum : fMaximum, true);
  822. }
  823. return true;
  824. }
  825. void ImageSlider::_recheckArea() noexcept
  826. {
  827. if (fStartPos.getY() == fEndPos.getY())
  828. {
  829. // horizontal
  830. fSliderArea = Rectangle<int>(fStartPos.getX(),
  831. fStartPos.getY(),
  832. fEndPos.getX() + static_cast<int>(fImage.getWidth()) - fStartPos.getX(),
  833. static_cast<int>(fImage.getHeight()));
  834. }
  835. else
  836. {
  837. // vertical
  838. fSliderArea = Rectangle<int>(fStartPos.getX(),
  839. fStartPos.getY(),
  840. static_cast<int>(fImage.getWidth()),
  841. fEndPos.getY() + static_cast<int>(fImage.getHeight()) - fStartPos.getY());
  842. }
  843. }
  844. // -----------------------------------------------------------------------
  845. ImageSwitch::ImageSwitch(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept
  846. : Widget(parent),
  847. fImageNormal(imageNormal),
  848. fImageDown(imageDown),
  849. fIsDown(false),
  850. fCallback(nullptr)
  851. {
  852. DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize());
  853. setSize(fImageNormal.getSize());
  854. }
  855. ImageSwitch::ImageSwitch(Widget* widget, const Image& imageNormal, const Image& imageDown) noexcept
  856. : Widget(widget->getParentWindow()),
  857. fImageNormal(imageNormal),
  858. fImageDown(imageDown),
  859. fIsDown(false),
  860. fCallback(nullptr)
  861. {
  862. DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize());
  863. setSize(fImageNormal.getSize());
  864. }
  865. ImageSwitch::ImageSwitch(const ImageSwitch& imageSwitch) noexcept
  866. : Widget(imageSwitch.getParentWindow()),
  867. fImageNormal(imageSwitch.fImageNormal),
  868. fImageDown(imageSwitch.fImageDown),
  869. fIsDown(imageSwitch.fIsDown),
  870. fCallback(imageSwitch.fCallback)
  871. {
  872. DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize());
  873. setSize(fImageNormal.getSize());
  874. }
  875. ImageSwitch& ImageSwitch::operator=(const ImageSwitch& imageSwitch) noexcept
  876. {
  877. fImageNormal = imageSwitch.fImageNormal;
  878. fImageDown = imageSwitch.fImageDown;
  879. fIsDown = imageSwitch.fIsDown;
  880. fCallback = imageSwitch.fCallback;
  881. DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize());
  882. setSize(fImageNormal.getSize());
  883. return *this;
  884. }
  885. bool ImageSwitch::isDown() const noexcept
  886. {
  887. return fIsDown;
  888. }
  889. void ImageSwitch::setDown(bool down) noexcept
  890. {
  891. if (fIsDown == down)
  892. return;
  893. fIsDown = down;
  894. repaint();
  895. }
  896. void ImageSwitch::setCallback(Callback* callback) noexcept
  897. {
  898. fCallback = callback;
  899. }
  900. void ImageSwitch::onDisplay()
  901. {
  902. if (fIsDown)
  903. fImageDown.draw();
  904. else
  905. fImageNormal.draw();
  906. }
  907. bool ImageSwitch::onMouse(const MouseEvent& ev)
  908. {
  909. if (ev.press && contains(ev.pos))
  910. {
  911. fIsDown = !fIsDown;
  912. repaint();
  913. if (fCallback != nullptr)
  914. fCallback->imageSwitchClicked(this, fIsDown);
  915. return true;
  916. }
  917. return false;
  918. }
  919. // -----------------------------------------------------------------------
  920. END_NAMESPACE_DGL