The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

586 lines
17KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. class ColourComponentSlider : public Slider
  21. {
  22. public:
  23. ColourComponentSlider (const String& name)
  24. : Slider (name)
  25. {
  26. setRange (0.0, 255.0, 1.0);
  27. }
  28. const String getTextFromValue (double value)
  29. {
  30. return String::toHexString ((int) value).toUpperCase().paddedLeft ('0', 2);
  31. }
  32. double getValueFromText (const String& text)
  33. {
  34. return (double) text.getHexValue32();
  35. }
  36. private:
  37. JUCE_DECLARE_NON_COPYABLE (ColourComponentSlider);
  38. };
  39. //==============================================================================
  40. class ColourSpaceMarker : public Component
  41. {
  42. public:
  43. ColourSpaceMarker()
  44. {
  45. setInterceptsMouseClicks (false, false);
  46. }
  47. void paint (Graphics& g)
  48. {
  49. g.setColour (Colour::greyLevel (0.1f));
  50. g.drawEllipse (1.0f, 1.0f, getWidth() - 2.0f, getHeight() - 2.0f, 1.0f);
  51. g.setColour (Colour::greyLevel (0.9f));
  52. g.drawEllipse (2.0f, 2.0f, getWidth() - 4.0f, getHeight() - 4.0f, 1.0f);
  53. }
  54. private:
  55. JUCE_DECLARE_NON_COPYABLE (ColourSpaceMarker);
  56. };
  57. //==============================================================================
  58. class ColourSelector::ColourSpaceView : public Component
  59. {
  60. public:
  61. ColourSpaceView (ColourSelector& owner_,
  62. float& h_, float& s_, float& v_,
  63. const int edgeSize)
  64. : owner (owner_),
  65. h (h_), s (s_), v (v_),
  66. lastHue (0.0f),
  67. edge (edgeSize)
  68. {
  69. addAndMakeVisible (&marker);
  70. setMouseCursor (MouseCursor::CrosshairCursor);
  71. }
  72. void paint (Graphics& g)
  73. {
  74. if (colours.isNull())
  75. {
  76. const int width = getWidth() / 2;
  77. const int height = getHeight() / 2;
  78. colours = Image (Image::RGB, width, height, false);
  79. Image::BitmapData pixels (colours, Image::BitmapData::writeOnly);
  80. for (int y = 0; y < height; ++y)
  81. {
  82. const float val = 1.0f - y / (float) height;
  83. for (int x = 0; x < width; ++x)
  84. {
  85. const float sat = x / (float) width;
  86. pixels.setPixelColour (x, y, Colour (h, sat, val, 1.0f));
  87. }
  88. }
  89. }
  90. g.setOpacity (1.0f);
  91. g.drawImage (colours, edge, edge, getWidth() - edge * 2, getHeight() - edge * 2,
  92. 0, 0, colours.getWidth(), colours.getHeight());
  93. }
  94. void mouseDown (const MouseEvent& e)
  95. {
  96. mouseDrag (e);
  97. }
  98. void mouseDrag (const MouseEvent& e)
  99. {
  100. const float sat = (e.x - edge) / (float) (getWidth() - edge * 2);
  101. const float val = 1.0f - (e.y - edge) / (float) (getHeight() - edge * 2);
  102. owner.setSV (sat, val);
  103. }
  104. void updateIfNeeded()
  105. {
  106. if (lastHue != h)
  107. {
  108. lastHue = h;
  109. colours = Image::null;
  110. repaint();
  111. }
  112. updateMarker();
  113. }
  114. void resized()
  115. {
  116. colours = Image::null;
  117. updateMarker();
  118. }
  119. private:
  120. ColourSelector& owner;
  121. float& h;
  122. float& s;
  123. float& v;
  124. float lastHue;
  125. ColourSpaceMarker marker;
  126. const int edge;
  127. Image colours;
  128. void updateMarker()
  129. {
  130. marker.setBounds (roundToInt ((getWidth() - edge * 2) * s),
  131. roundToInt ((getHeight() - edge * 2) * (1.0f - v)),
  132. edge * 2, edge * 2);
  133. }
  134. JUCE_DECLARE_NON_COPYABLE (ColourSpaceView);
  135. };
  136. //==============================================================================
  137. class HueSelectorMarker : public Component
  138. {
  139. public:
  140. HueSelectorMarker()
  141. {
  142. setInterceptsMouseClicks (false, false);
  143. }
  144. void paint (Graphics& g)
  145. {
  146. Path p;
  147. p.addTriangle (1.0f, 1.0f,
  148. getWidth() * 0.3f, getHeight() * 0.5f,
  149. 1.0f, getHeight() - 1.0f);
  150. p.addTriangle (getWidth() - 1.0f, 1.0f,
  151. getWidth() * 0.7f, getHeight() * 0.5f,
  152. getWidth() - 1.0f, getHeight() - 1.0f);
  153. g.setColour (Colours::white.withAlpha (0.75f));
  154. g.fillPath (p);
  155. g.setColour (Colours::black.withAlpha (0.75f));
  156. g.strokePath (p, PathStrokeType (1.2f));
  157. }
  158. private:
  159. JUCE_DECLARE_NON_COPYABLE (HueSelectorMarker);
  160. };
  161. //==============================================================================
  162. class ColourSelector::HueSelectorComp : public Component
  163. {
  164. public:
  165. HueSelectorComp (ColourSelector& owner_,
  166. float& h_, float& s_, float& v_,
  167. const int edgeSize)
  168. : owner (owner_),
  169. h (h_), s (s_), v (v_),
  170. lastHue (0.0f),
  171. edge (edgeSize)
  172. {
  173. addAndMakeVisible (&marker);
  174. }
  175. void paint (Graphics& g)
  176. {
  177. const float yScale = 1.0f / (getHeight() - edge * 2);
  178. const Rectangle<int> clip (g.getClipBounds());
  179. for (int y = jmin (clip.getBottom(), getHeight() - edge); --y >= jmax (edge, clip.getY());)
  180. {
  181. g.setColour (Colour ((y - edge) * yScale, 1.0f, 1.0f, 1.0f));
  182. g.fillRect (edge, y, getWidth() - edge * 2, 1);
  183. }
  184. }
  185. void resized()
  186. {
  187. marker.setBounds (0, roundToInt ((getHeight() - edge * 2) * h),
  188. getWidth(), edge * 2);
  189. }
  190. void mouseDown (const MouseEvent& e)
  191. {
  192. mouseDrag (e);
  193. }
  194. void mouseDrag (const MouseEvent& e)
  195. {
  196. owner.setHue ((e.y - edge) / (float) (getHeight() - edge * 2));
  197. }
  198. void updateIfNeeded()
  199. {
  200. resized();
  201. }
  202. private:
  203. ColourSelector& owner;
  204. float& h;
  205. float& s;
  206. float& v;
  207. float lastHue;
  208. HueSelectorMarker marker;
  209. const int edge;
  210. JUCE_DECLARE_NON_COPYABLE (HueSelectorComp);
  211. };
  212. //==============================================================================
  213. class ColourSelector::SwatchComponent : public Component
  214. {
  215. public:
  216. SwatchComponent (ColourSelector& owner_, int index_)
  217. : owner (owner_), index (index_)
  218. {
  219. }
  220. void paint (Graphics& g)
  221. {
  222. const Colour colour (owner.getSwatchColour (index));
  223. g.fillCheckerBoard (getLocalBounds(), 6, 6,
  224. Colour (0xffdddddd).overlaidWith (colour),
  225. Colour (0xffffffff).overlaidWith (colour));
  226. }
  227. void mouseDown (const MouseEvent&)
  228. {
  229. PopupMenu m;
  230. m.addItem (1, TRANS("Use this swatch as the current colour"));
  231. m.addSeparator();
  232. m.addItem (2, TRANS("Set this swatch to the current colour"));
  233. m.showMenuAsync (PopupMenu::Options().withTargetComponent (this),
  234. ModalCallbackFunction::forComponent (menuStaticCallback, this));
  235. }
  236. private:
  237. ColourSelector& owner;
  238. const int index;
  239. static void menuStaticCallback (int result, SwatchComponent* comp)
  240. {
  241. if (comp != nullptr)
  242. {
  243. if (result == 1)
  244. comp->setColourFromSwatch();
  245. else if (result == 2)
  246. comp->setSwatchFromColour();
  247. }
  248. }
  249. void setColourFromSwatch()
  250. {
  251. owner.setCurrentColour (owner.getSwatchColour (index));
  252. }
  253. void setSwatchFromColour()
  254. {
  255. if (owner.getSwatchColour (index) != owner.getCurrentColour())
  256. {
  257. owner.setSwatchColour (index, owner.getCurrentColour());
  258. repaint();
  259. }
  260. }
  261. JUCE_DECLARE_NON_COPYABLE (SwatchComponent);
  262. };
  263. //==============================================================================
  264. ColourSelector::ColourSelector (const int flags_,
  265. const int edgeGap_,
  266. const int gapAroundColourSpaceComponent)
  267. : colour (Colours::white),
  268. flags (flags_),
  269. edgeGap (edgeGap_)
  270. {
  271. // not much point having a selector with no components in it!
  272. jassert ((flags_ & (showColourAtTop | showSliders | showColourspace)) != 0);
  273. updateHSV();
  274. if ((flags & showSliders) != 0)
  275. {
  276. addAndMakeVisible (sliders[0] = new ColourComponentSlider (TRANS ("red")));
  277. addAndMakeVisible (sliders[1] = new ColourComponentSlider (TRANS ("green")));
  278. addAndMakeVisible (sliders[2] = new ColourComponentSlider (TRANS ("blue")));
  279. addChildComponent (sliders[3] = new ColourComponentSlider (TRANS ("alpha")));
  280. sliders[3]->setVisible ((flags & showAlphaChannel) != 0);
  281. for (int i = 4; --i >= 0;)
  282. sliders[i]->addListener (this);
  283. }
  284. if ((flags & showColourspace) != 0)
  285. {
  286. addAndMakeVisible (colourSpace = new ColourSpaceView (*this, h, s, v, gapAroundColourSpaceComponent));
  287. addAndMakeVisible (hueSelector = new HueSelectorComp (*this, h, s, v, gapAroundColourSpaceComponent));
  288. }
  289. update();
  290. }
  291. ColourSelector::~ColourSelector()
  292. {
  293. dispatchPendingMessages();
  294. swatchComponents.clear();
  295. }
  296. //==============================================================================
  297. Colour ColourSelector::getCurrentColour() const
  298. {
  299. return ((flags & showAlphaChannel) != 0) ? colour
  300. : colour.withAlpha ((uint8) 0xff);
  301. }
  302. void ColourSelector::setCurrentColour (const Colour& c)
  303. {
  304. if (c != colour)
  305. {
  306. colour = ((flags & showAlphaChannel) != 0) ? c : c.withAlpha ((uint8) 0xff);
  307. updateHSV();
  308. update();
  309. }
  310. }
  311. void ColourSelector::setHue (float newH)
  312. {
  313. newH = jlimit (0.0f, 1.0f, newH);
  314. if (h != newH)
  315. {
  316. h = newH;
  317. colour = Colour (h, s, v, colour.getFloatAlpha());
  318. update();
  319. }
  320. }
  321. void ColourSelector::setSV (float newS, float newV)
  322. {
  323. newS = jlimit (0.0f, 1.0f, newS);
  324. newV = jlimit (0.0f, 1.0f, newV);
  325. if (s != newS || v != newV)
  326. {
  327. s = newS;
  328. v = newV;
  329. colour = Colour (h, s, v, colour.getFloatAlpha());
  330. update();
  331. }
  332. }
  333. //==============================================================================
  334. void ColourSelector::updateHSV()
  335. {
  336. colour.getHSB (h, s, v);
  337. }
  338. void ColourSelector::update()
  339. {
  340. if (sliders[0] != nullptr)
  341. {
  342. sliders[0]->setValue ((int) colour.getRed());
  343. sliders[1]->setValue ((int) colour.getGreen());
  344. sliders[2]->setValue ((int) colour.getBlue());
  345. sliders[3]->setValue ((int) colour.getAlpha());
  346. }
  347. if (colourSpace != nullptr)
  348. {
  349. colourSpace->updateIfNeeded();
  350. hueSelector->updateIfNeeded();
  351. }
  352. if ((flags & showColourAtTop) != 0)
  353. repaint (previewArea);
  354. sendChangeMessage();
  355. }
  356. //==============================================================================
  357. void ColourSelector::paint (Graphics& g)
  358. {
  359. g.fillAll (findColour (backgroundColourId));
  360. if ((flags & showColourAtTop) != 0)
  361. {
  362. const Colour currentColour (getCurrentColour());
  363. g.fillCheckerBoard (previewArea, 10, 10,
  364. Colour (0xffdddddd).overlaidWith (currentColour),
  365. Colour (0xffffffff).overlaidWith (currentColour));
  366. g.setColour (Colours::white.overlaidWith (currentColour).contrasting());
  367. g.setFont (14.0f, true);
  368. g.drawText (currentColour.toDisplayString ((flags & showAlphaChannel) != 0),
  369. previewArea.getX(), previewArea.getY(), previewArea.getWidth(), previewArea.getHeight(),
  370. Justification::centred, false);
  371. }
  372. if ((flags & showSliders) != 0)
  373. {
  374. g.setColour (findColour (labelTextColourId));
  375. g.setFont (11.0f);
  376. for (int i = 4; --i >= 0;)
  377. {
  378. if (sliders[i]->isVisible())
  379. g.drawText (sliders[i]->getName() + ":",
  380. 0, sliders[i]->getY(),
  381. sliders[i]->getX() - 8, sliders[i]->getHeight(),
  382. Justification::centredRight, false);
  383. }
  384. }
  385. }
  386. void ColourSelector::resized()
  387. {
  388. const int swatchesPerRow = 8;
  389. const int swatchHeight = 22;
  390. const int numSliders = ((flags & showAlphaChannel) != 0) ? 4 : 3;
  391. const int numSwatches = getNumSwatches();
  392. const int swatchSpace = numSwatches > 0 ? edgeGap + swatchHeight * ((numSwatches + 7) / swatchesPerRow) : 0;
  393. const int sliderSpace = ((flags & showSliders) != 0) ? jmin (22 * numSliders + edgeGap, proportionOfHeight (0.3f)) : 0;
  394. const int topSpace = ((flags & showColourAtTop) != 0) ? jmin (30 + edgeGap * 2, proportionOfHeight (0.2f)) : edgeGap;
  395. previewArea.setBounds (edgeGap, edgeGap, getWidth() - edgeGap * 2, topSpace - edgeGap * 2);
  396. int y = topSpace;
  397. if ((flags & showColourspace) != 0)
  398. {
  399. const int hueWidth = jmin (50, proportionOfWidth (0.15f));
  400. colourSpace->setBounds (edgeGap, y,
  401. getWidth() - hueWidth - edgeGap - 4,
  402. getHeight() - topSpace - sliderSpace - swatchSpace - edgeGap);
  403. hueSelector->setBounds (colourSpace->getRight() + 4, y,
  404. getWidth() - edgeGap - (colourSpace->getRight() + 4),
  405. colourSpace->getHeight());
  406. y = getHeight() - sliderSpace - swatchSpace - edgeGap;
  407. }
  408. if ((flags & showSliders) != 0)
  409. {
  410. const int sliderHeight = jmax (4, sliderSpace / numSliders);
  411. for (int i = 0; i < numSliders; ++i)
  412. {
  413. sliders[i]->setBounds (proportionOfWidth (0.2f), y,
  414. proportionOfWidth (0.72f), sliderHeight - 2);
  415. y += sliderHeight;
  416. }
  417. }
  418. if (numSwatches > 0)
  419. {
  420. const int startX = 8;
  421. const int xGap = 4;
  422. const int yGap = 4;
  423. const int swatchWidth = (getWidth() - startX * 2) / swatchesPerRow;
  424. y += edgeGap;
  425. if (swatchComponents.size() != numSwatches)
  426. {
  427. swatchComponents.clear();
  428. for (int i = 0; i < numSwatches; ++i)
  429. {
  430. SwatchComponent* const sc = new SwatchComponent (*this, i);
  431. swatchComponents.add (sc);
  432. addAndMakeVisible (sc);
  433. }
  434. }
  435. int x = startX;
  436. for (int i = 0; i < swatchComponents.size(); ++i)
  437. {
  438. SwatchComponent* const sc = swatchComponents.getUnchecked(i);
  439. sc->setBounds (x + xGap / 2,
  440. y + yGap / 2,
  441. swatchWidth - xGap,
  442. swatchHeight - yGap);
  443. if (((i + 1) % swatchesPerRow) == 0)
  444. {
  445. x = startX;
  446. y += swatchHeight;
  447. }
  448. else
  449. {
  450. x += swatchWidth;
  451. }
  452. }
  453. }
  454. }
  455. void ColourSelector::sliderValueChanged (Slider*)
  456. {
  457. if (sliders[0] != nullptr)
  458. setCurrentColour (Colour ((uint8) sliders[0]->getValue(),
  459. (uint8) sliders[1]->getValue(),
  460. (uint8) sliders[2]->getValue(),
  461. (uint8) sliders[3]->getValue()));
  462. }
  463. //==============================================================================
  464. int ColourSelector::getNumSwatches() const
  465. {
  466. return 0;
  467. }
  468. Colour ColourSelector::getSwatchColour (const int) const
  469. {
  470. jassertfalse; // if you've overridden getNumSwatches(), you also need to implement this method
  471. return Colours::black;
  472. }
  473. void ColourSelector::setSwatchColour (const int, const Colour&) const
  474. {
  475. jassertfalse; // if you've overridden getNumSwatches(), you also need to implement this method
  476. }
  477. END_JUCE_NAMESPACE