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.

1176 lines
29KB

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