2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../../core/juce_StandardHeader.h"
30 #include "juce_ColourSelector.h"
31 #include "../menus/juce_PopupMenu.h"
32 #include "../mouse/juce_MouseEvent.h"
33 #include "../../graphics/imaging/juce_Image.h"
34 #include "../../../text/juce_LocalisedStrings.h"
37 //==============================================================================
38 class ColourComponentSlider
: public Slider
41 ColourComponentSlider (const String
& name
)
44 setRange (0.0, 255.0, 1.0);
47 const String
getTextFromValue (double value
)
49 return String::toHexString ((int) value
).toUpperCase().paddedLeft ('0', 2);
52 double getValueFromText (const String
& text
)
54 return (double) text
.getHexValue32();
58 JUCE_DECLARE_NON_COPYABLE (ColourComponentSlider
);
61 //==============================================================================
62 class ColourSpaceMarker
: public Component
67 setInterceptsMouseClicks (false, false);
70 void paint (Graphics
& g
)
72 g
.setColour (Colour::greyLevel (0.1f
));
73 g
.drawEllipse (1.0f
, 1.0f
, getWidth() - 2.0f
, getHeight() - 2.0f
, 1.0f
);
74 g
.setColour (Colour::greyLevel (0.9f
));
75 g
.drawEllipse (2.0f
, 2.0f
, getWidth() - 4.0f
, getHeight() - 4.0f
, 1.0f
);
79 JUCE_DECLARE_NON_COPYABLE (ColourSpaceMarker
);
82 //==============================================================================
83 class ColourSelector::ColourSpaceView
: public Component
86 ColourSpaceView (ColourSelector
& owner_
,
87 float& h_
, float& s_
, float& v_
,
90 h (h_
), s (s_
), v (v_
),
94 addAndMakeVisible (&marker
);
95 setMouseCursor (MouseCursor::CrosshairCursor
);
98 void paint (Graphics
& g
)
100 if (colours
.isNull())
102 const int width
= getWidth() / 2;
103 const int height
= getHeight() / 2;
104 colours
= Image (Image::RGB
, width
, height
, false);
106 Image::BitmapData
pixels (colours
, Image::BitmapData::writeOnly
);
108 for (int y
= 0; y
< height
; ++y
)
110 const float val
= 1.0f
- y
/ (float) height
;
112 for (int x
= 0; x
< width
; ++x
)
114 const float sat
= x
/ (float) width
;
116 pixels
.setPixelColour (x
, y
, Colour (h
, sat
, val
, 1.0f
));
122 g
.drawImage (colours
, edge
, edge
, getWidth() - edge
* 2, getHeight() - edge
* 2,
123 0, 0, colours
.getWidth(), colours
.getHeight());
126 void mouseDown (const MouseEvent
& e
)
131 void mouseDrag (const MouseEvent
& e
)
133 const float sat
= (e
.x
- edge
) / (float) (getWidth() - edge
* 2);
134 const float val
= 1.0f
- (e
.y
- edge
) / (float) (getHeight() - edge
* 2);
136 owner
.setSV (sat
, val
);
139 void updateIfNeeded()
144 colours
= Image::null
;
153 colours
= Image::null
;
158 ColourSelector
& owner
;
163 ColourSpaceMarker marker
;
169 marker
.setBounds (roundToInt ((getWidth() - edge
* 2) * s
),
170 roundToInt ((getHeight() - edge
* 2) * (1.0f
- v
)),
174 JUCE_DECLARE_NON_COPYABLE (ColourSpaceView
);
177 //==============================================================================
178 class HueSelectorMarker
: public Component
183 setInterceptsMouseClicks (false, false);
186 void paint (Graphics
& g
)
189 p
.addTriangle (1.0f
, 1.0f
,
190 getWidth() * 0.3f
, getHeight() * 0.5f
,
191 1.0f
, getHeight() - 1.0f
);
193 p
.addTriangle (getWidth() - 1.0f
, 1.0f
,
194 getWidth() * 0.7f
, getHeight() * 0.5f
,
195 getWidth() - 1.0f
, getHeight() - 1.0f
);
197 g
.setColour (Colours::white
.withAlpha (0.75f
));
200 g
.setColour (Colours::black
.withAlpha (0.75f
));
201 g
.strokePath (p
, PathStrokeType (1.2f
));
205 JUCE_DECLARE_NON_COPYABLE (HueSelectorMarker
);
208 //==============================================================================
209 class ColourSelector::HueSelectorComp
: public Component
212 HueSelectorComp (ColourSelector
& owner_
,
213 float& h_
, float& s_
, float& v_
,
216 h (h_
), s (s_
), v (v_
),
220 addAndMakeVisible (&marker
);
223 void paint (Graphics
& g
)
225 const float yScale
= 1.0f
/ (getHeight() - edge
* 2);
227 const Rectangle
<int> clip (g
.getClipBounds());
229 for (int y
= jmin (clip
.getBottom(), getHeight() - edge
); --y
>= jmax (edge
, clip
.getY());)
231 g
.setColour (Colour ((y
- edge
) * yScale
, 1.0f
, 1.0f
, 1.0f
));
232 g
.fillRect (edge
, y
, getWidth() - edge
* 2, 1);
238 marker
.setBounds (0, roundToInt ((getHeight() - edge
* 2) * h
),
239 getWidth(), edge
* 2);
242 void mouseDown (const MouseEvent
& e
)
247 void mouseDrag (const MouseEvent
& e
)
249 owner
.setHue ((e
.y
- edge
) / (float) (getHeight() - edge
* 2));
252 void updateIfNeeded()
258 ColourSelector
& owner
;
263 HueSelectorMarker marker
;
266 JUCE_DECLARE_NON_COPYABLE (HueSelectorComp
);
269 //==============================================================================
270 class ColourSelector::SwatchComponent
: public Component
273 SwatchComponent (ColourSelector
& owner_
, int index_
)
274 : owner (owner_
), index (index_
)
278 void paint (Graphics
& g
)
280 const Colour
colour (owner
.getSwatchColour (index
));
282 g
.fillCheckerBoard (getLocalBounds(), 6, 6,
283 Colour (0xffdddddd).overlaidWith (colour
),
284 Colour (0xffffffff).overlaidWith (colour
));
287 void mouseDown (const MouseEvent
&)
290 m
.addItem (1, TRANS("Use this swatch as the current colour"));
292 m
.addItem (2, TRANS("Set this swatch to the current colour"));
294 m
.showMenuAsync (PopupMenu::Options().withTargetComponent (this),
295 ModalCallbackFunction::forComponent (menuStaticCallback
, this));
299 ColourSelector
& owner
;
302 static void menuStaticCallback (int result
, SwatchComponent
* comp
)
307 comp
->setColourFromSwatch();
308 else if (result
== 2)
309 comp
->setSwatchFromColour();
313 void setColourFromSwatch()
315 owner
.setCurrentColour (owner
.getSwatchColour (index
));
318 void setSwatchFromColour()
320 if (owner
.getSwatchColour (index
) != owner
.getCurrentColour())
322 owner
.setSwatchColour (index
, owner
.getCurrentColour());
327 JUCE_DECLARE_NON_COPYABLE (SwatchComponent
);
330 //==============================================================================
331 ColourSelector::ColourSelector (const int flags_
,
333 const int gapAroundColourSpaceComponent
)
334 : colour (Colours::white
),
338 // not much point having a selector with no components in it!
339 jassert ((flags_
& (showColourAtTop
| showSliders
| showColourspace
)) != 0);
343 if ((flags
& showSliders
) != 0)
345 addAndMakeVisible (sliders
[0] = new ColourComponentSlider (TRANS ("red")));
346 addAndMakeVisible (sliders
[1] = new ColourComponentSlider (TRANS ("green")));
347 addAndMakeVisible (sliders
[2] = new ColourComponentSlider (TRANS ("blue")));
348 addChildComponent (sliders
[3] = new ColourComponentSlider (TRANS ("alpha")));
350 sliders
[3]->setVisible ((flags
& showAlphaChannel
) != 0);
352 for (int i
= 4; --i
>= 0;)
353 sliders
[i
]->addListener (this);
356 if ((flags
& showColourspace
) != 0)
358 addAndMakeVisible (colourSpace
= new ColourSpaceView (*this, h
, s
, v
, gapAroundColourSpaceComponent
));
359 addAndMakeVisible (hueSelector
= new HueSelectorComp (*this, h
, s
, v
, gapAroundColourSpaceComponent
));
365 ColourSelector::~ColourSelector()
367 dispatchPendingMessages();
368 swatchComponents
.clear();
371 //==============================================================================
372 const Colour
ColourSelector::getCurrentColour() const
374 return ((flags
& showAlphaChannel
) != 0) ? colour
375 : colour
.withAlpha ((uint8
) 0xff);
378 void ColourSelector::setCurrentColour (const Colour
& c
)
382 colour
= ((flags
& showAlphaChannel
) != 0) ? c
: c
.withAlpha ((uint8
) 0xff);
389 void ColourSelector::setHue (float newH
)
391 newH
= jlimit (0.0f
, 1.0f
, newH
);
396 colour
= Colour (h
, s
, v
, colour
.getFloatAlpha());
401 void ColourSelector::setSV (float newS
, float newV
)
403 newS
= jlimit (0.0f
, 1.0f
, newS
);
404 newV
= jlimit (0.0f
, 1.0f
, newV
);
406 if (s
!= newS
|| v
!= newV
)
410 colour
= Colour (h
, s
, v
, colour
.getFloatAlpha());
415 //==============================================================================
416 void ColourSelector::updateHSV()
418 colour
.getHSB (h
, s
, v
);
421 void ColourSelector::update()
423 if (sliders
[0] != nullptr)
425 sliders
[0]->setValue ((int) colour
.getRed());
426 sliders
[1]->setValue ((int) colour
.getGreen());
427 sliders
[2]->setValue ((int) colour
.getBlue());
428 sliders
[3]->setValue ((int) colour
.getAlpha());
431 if (colourSpace
!= nullptr)
433 colourSpace
->updateIfNeeded();
434 hueSelector
->updateIfNeeded();
437 if ((flags
& showColourAtTop
) != 0)
438 repaint (previewArea
);
443 //==============================================================================
444 void ColourSelector::paint (Graphics
& g
)
446 g
.fillAll (findColour (backgroundColourId
));
448 if ((flags
& showColourAtTop
) != 0)
450 const Colour
currentColour (getCurrentColour());
452 g
.fillCheckerBoard (previewArea
, 10, 10,
453 Colour (0xffdddddd).overlaidWith (currentColour
),
454 Colour (0xffffffff).overlaidWith (currentColour
));
456 g
.setColour (Colours::white
.overlaidWith (currentColour
).contrasting());
457 g
.setFont (14.0f
, true);
458 g
.drawText (currentColour
.toDisplayString ((flags
& showAlphaChannel
) != 0),
459 previewArea
.getX(), previewArea
.getY(), previewArea
.getWidth(), previewArea
.getHeight(),
460 Justification::centred
, false);
463 if ((flags
& showSliders
) != 0)
465 g
.setColour (findColour (labelTextColourId
));
468 for (int i
= 4; --i
>= 0;)
470 if (sliders
[i
]->isVisible())
471 g
.drawText (sliders
[i
]->getName() + ":",
472 0, sliders
[i
]->getY(),
473 sliders
[i
]->getX() - 8, sliders
[i
]->getHeight(),
474 Justification::centredRight
, false);
479 void ColourSelector::resized()
481 const int swatchesPerRow
= 8;
482 const int swatchHeight
= 22;
484 const int numSliders
= ((flags
& showAlphaChannel
) != 0) ? 4 : 3;
485 const int numSwatches
= getNumSwatches();
487 const int swatchSpace
= numSwatches
> 0 ? edgeGap
+ swatchHeight
* ((numSwatches
+ 7) / swatchesPerRow
) : 0;
488 const int sliderSpace
= ((flags
& showSliders
) != 0) ? jmin (22 * numSliders
+ edgeGap
, proportionOfHeight (0.3f
)) : 0;
489 const int topSpace
= ((flags
& showColourAtTop
) != 0) ? jmin (30 + edgeGap
* 2, proportionOfHeight (0.2f
)) : edgeGap
;
491 previewArea
.setBounds (edgeGap
, edgeGap
, getWidth() - edgeGap
* 2, topSpace
- edgeGap
* 2);
495 if ((flags
& showColourspace
) != 0)
497 const int hueWidth
= jmin (50, proportionOfWidth (0.15f
));
499 colourSpace
->setBounds (edgeGap
, y
,
500 getWidth() - hueWidth
- edgeGap
- 4,
501 getHeight() - topSpace
- sliderSpace
- swatchSpace
- edgeGap
);
503 hueSelector
->setBounds (colourSpace
->getRight() + 4, y
,
504 getWidth() - edgeGap
- (colourSpace
->getRight() + 4),
505 colourSpace
->getHeight());
507 y
= getHeight() - sliderSpace
- swatchSpace
- edgeGap
;
510 if ((flags
& showSliders
) != 0)
512 const int sliderHeight
= jmax (4, sliderSpace
/ numSliders
);
514 for (int i
= 0; i
< numSliders
; ++i
)
516 sliders
[i
]->setBounds (proportionOfWidth (0.2f
), y
,
517 proportionOfWidth (0.72f
), sliderHeight
- 2);
525 const int startX
= 8;
528 const int swatchWidth
= (getWidth() - startX
* 2) / swatchesPerRow
;
531 if (swatchComponents
.size() != numSwatches
)
533 swatchComponents
.clear();
535 for (int i
= 0; i
< numSwatches
; ++i
)
537 SwatchComponent
* const sc
= new SwatchComponent (*this, i
);
538 swatchComponents
.add (sc
);
539 addAndMakeVisible (sc
);
545 for (int i
= 0; i
< swatchComponents
.size(); ++i
)
547 SwatchComponent
* const sc
= swatchComponents
.getUnchecked(i
);
549 sc
->setBounds (x
+ xGap
/ 2,
552 swatchHeight
- yGap
);
554 if (((i
+ 1) % swatchesPerRow
) == 0)
567 void ColourSelector::sliderValueChanged (Slider
*)
569 if (sliders
[0] != nullptr)
570 setCurrentColour (Colour ((uint8
) sliders
[0]->getValue(),
571 (uint8
) sliders
[1]->getValue(),
572 (uint8
) sliders
[2]->getValue(),
573 (uint8
) sliders
[3]->getValue()));
576 //==============================================================================
577 int ColourSelector::getNumSwatches() const
582 const Colour
ColourSelector::getSwatchColour (const int) const
584 jassertfalse
; // if you've overridden getNumSwatches(), you also need to implement this method
585 return Colours::black
;
588 void ColourSelector::setSwatchColour (const int, const Colour
&) const
590 jassertfalse
; // if you've overridden getNumSwatches(), you also need to implement this method