/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
This file is part of the JUCE library .
Copyright ( c ) 2013 - Raw Material Software Ltd .
Permission is granted to use this software under the terms of either :
a ) the GPL v2 ( or any later version )
b ) the Affero GPL v3
Details of these licenses can be found at : www . gnu . org / licenses
JUCE is distributed in the hope that it will be useful , but WITHOUT ANY
WARRANTY ; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE . See the GNU General Public License for more details .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
To release a closed - source product which uses JUCE , commercial licenses are
available : visit www . juce . com for more information .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
class ColourSelector : : ColourComponentSlider : public Slider
{
public :
ColourComponentSlider ( const String & name )
: Slider ( name )
{
setRange ( 0.0 , 255.0 , 1.0 ) ;
}
String getTextFromValue ( double value )
{
return String : : toHexString ( ( int ) value ) . toUpperCase ( ) . paddedLeft ( ' 0 ' , 2 ) ;
}
double getValueFromText ( const String & text )
{
return ( double ) text . getHexValue32 ( ) ;
}
private :
JUCE_DECLARE_NON_COPYABLE ( ColourComponentSlider )
} ;
//==============================================================================
class ColourSelector : : ColourSpaceMarker : public Component
{
public :
ColourSpaceMarker ( )
{
setInterceptsMouseClicks ( false , false ) ;
}
void paint ( Graphics & g ) override
{
g . setColour ( Colour : : greyLevel ( 0.1f ) ) ;
g . drawEllipse ( 1.0f , 1.0f , getWidth ( ) - 2.0f , getHeight ( ) - 2.0f , 1.0f ) ;
g . setColour ( Colour : : greyLevel ( 0.9f ) ) ;
g . drawEllipse ( 2.0f , 2.0f , getWidth ( ) - 4.0f , getHeight ( ) - 4.0f , 1.0f ) ;
}
private :
JUCE_DECLARE_NON_COPYABLE ( ColourSpaceMarker )
} ;
//==============================================================================
class ColourSelector : : ColourSpaceView : public Component
{
public :
ColourSpaceView ( ColourSelector & cs , float & hue , float & sat , float & val , const int edgeSize )
: owner ( cs ) , h ( hue ) , s ( sat ) , v ( val ) , lastHue ( 0.0f ) , edge ( edgeSize )
{
addAndMakeVisible ( marker ) ;
setMouseCursor ( MouseCursor : : CrosshairCursor ) ;
}
void paint ( Graphics & g ) override
{
if ( colours . isNull ( ) )
{
const int width = getWidth ( ) / 2 ;
const int height = getHeight ( ) / 2 ;
colours = Image ( Image : : RGB , width , height , false ) ;
Image : : BitmapData pixels ( colours , Image : : BitmapData : : writeOnly ) ;
for ( int y = 0 ; y < height ; + + y )
{
const float val = 1.0f - y / ( float ) height ;
for ( int x = 0 ; x < width ; + + x )
{
const float sat = x / ( float ) width ;
pixels . setPixelColour ( x , y , Colour ( h , sat , val , 1.0f ) ) ;
}
}
}
g . setOpacity ( 1.0f ) ;
g . drawImageTransformed ( colours ,
RectanglePlacement ( RectanglePlacement : : stretchToFit )
. getTransformToFit ( colours . getBounds ( ) . toFloat ( ) ,
getLocalBounds ( ) . reduced ( edge ) . toFloat ( ) ) ,
false ) ;
}
void mouseDown ( const MouseEvent & e ) override
{
mouseDrag ( e ) ;
}
void mouseDrag ( const MouseEvent & e ) override
{
const float sat = ( e . x - edge ) / ( float ) ( getWidth ( ) - edge * 2 ) ;
const float val = 1.0f - ( e . y - edge ) / ( float ) ( getHeight ( ) - edge * 2 ) ;
owner . setSV ( sat , val ) ;
}
void updateIfNeeded ( )
{
if ( lastHue ! = h )
{
lastHue = h ;
colours = Image : : null ;
repaint ( ) ;
}
updateMarker ( ) ;
}
void resized ( ) override
{
colours = Image : : null ;
updateMarker ( ) ;
}
private :
ColourSelector & owner ;
float & h ;
float & s ;
float & v ;
float lastHue ;
ColourSpaceMarker marker ;
const int edge ;
Image colours ;
void updateMarker ( )
{
marker . setBounds ( roundToInt ( ( getWidth ( ) - edge * 2 ) * s ) ,
roundToInt ( ( getHeight ( ) - edge * 2 ) * ( 1.0f - v ) ) ,
edge * 2 , edge * 2 ) ;
}
JUCE_DECLARE_NON_COPYABLE ( ColourSpaceView )
} ;
//==============================================================================
class ColourSelector : : HueSelectorMarker : public Component
{
public :
HueSelectorMarker ( )
{
setInterceptsMouseClicks ( false , false ) ;
}
void paint ( Graphics & g ) override
{
const float cw = ( float ) getWidth ( ) ;
const float ch = ( float ) getHeight ( ) ;
Path p ;
p . addTriangle ( 1.0f , 1.0f ,
cw * 0.3f , ch * 0.5f ,
1.0f , ch - 1.0f ) ;
p . addTriangle ( cw - 1.0f , 1.0f ,
cw * 0.7f , ch * 0.5f ,
cw - 1.0f , ch - 1.0f ) ;
g . setColour ( Colours : : white . withAlpha ( 0.75f ) ) ;
g . fillPath ( p ) ;
g . setColour ( Colours : : black . withAlpha ( 0.75f ) ) ;
g . strokePath ( p , PathStrokeType ( 1.2f ) ) ;
}
private :
JUCE_DECLARE_NON_COPYABLE ( HueSelectorMarker )
} ;
//==============================================================================
class ColourSelector : : HueSelectorComp : public Component
{
public :
HueSelectorComp ( ColourSelector & cs , float & hue , const int edgeSize )
: owner ( cs ) , h ( hue ) , edge ( edgeSize )
{
addAndMakeVisible ( marker ) ;
}
void paint ( Graphics & g ) override
{
ColourGradient cg ;
cg . isRadial = false ;
cg . point1 . setXY ( 0.0f , ( float ) edge ) ;
cg . point2 . setXY ( 0.0f , ( float ) ( getHeight ( ) - edge ) ) ;
for ( float i = 0.0f ; i < = 1.0f ; i + = 0.02f )
cg . addColour ( i , Colour ( i , 1.0f , 1.0f , 1.0f ) ) ;
g . setGradientFill ( cg ) ;
g . fillRect ( getLocalBounds ( ) . reduced ( edge ) ) ;
}
void resized ( ) override
{
marker . setBounds ( 0 , roundToInt ( ( getHeight ( ) - edge * 2 ) * h ) , getWidth ( ) , edge * 2 ) ;
}
void mouseDown ( const MouseEvent & e ) override
{
mouseDrag ( e ) ;
}
void mouseDrag ( const MouseEvent & e ) override
{
owner . setHue ( ( e . y - edge ) / ( float ) ( getHeight ( ) - edge * 2 ) ) ;
}
void updateIfNeeded ( )
{
resized ( ) ;
}
private :
ColourSelector & owner ;
float & h ;
HueSelectorMarker marker ;
const int edge ;
JUCE_DECLARE_NON_COPYABLE ( HueSelectorComp )
} ;
//==============================================================================
class ColourSelector : : SwatchComponent : public Component
{
public :
SwatchComponent ( ColourSelector & cs , int itemIndex )
: owner ( cs ) , index ( itemIndex )
{
}
void paint ( Graphics & g ) override
{
const Colour c ( owner . getSwatchColour ( index ) ) ;
g . fillCheckerBoard ( getLocalBounds ( ) , 6 , 6 ,
Colour ( 0xffdddddd ) . overlaidWith ( c ) ,
Colour ( 0xffffffff ) . overlaidWith ( c ) ) ;
}
void mouseDown ( const MouseEvent & ) override
{
PopupMenu m ;
m . addItem ( 1 , TRANS ( " Use this swatch as the current colour " ) ) ;
m . addSeparator ( ) ;
m . addItem ( 2 , TRANS ( " Set this swatch to the current colour " ) ) ;
m . showMenuAsync ( PopupMenu : : Options ( ) . withTargetComponent ( this ) ,
ModalCallbackFunction : : forComponent ( menuStaticCallback , this ) ) ;
}
private :
ColourSelector & owner ;
const int index ;
static void menuStaticCallback ( int result , SwatchComponent * comp )
{
if ( comp ! = nullptr )
{
if ( result = = 1 )
comp - > setColourFromSwatch ( ) ;
else if ( result = = 2 )
comp - > setSwatchFromColour ( ) ;
}
}
void setColourFromSwatch ( )
{
owner . setCurrentColour ( owner . getSwatchColour ( index ) ) ;
}
void setSwatchFromColour ( )
{
if ( owner . getSwatchColour ( index ) ! = owner . getCurrentColour ( ) )
{
owner . setSwatchColour ( index , owner . getCurrentColour ( ) ) ;
repaint ( ) ;
}
}
JUCE_DECLARE_NON_COPYABLE ( SwatchComponent )
} ;
//==============================================================================
ColourSelector : : ColourSelector ( const int sectionsToShow , const int edge , const int gapAroundColourSpaceComponent )
: colour ( Colours : : white ) ,
flags ( sectionsToShow ) ,
edgeGap ( edge )
{
// not much point having a selector with no components in it!
jassert ( ( flags & ( showColourAtTop | showSliders | showColourspace ) ) ! = 0 ) ;
updateHSV ( ) ;
if ( ( flags & showSliders ) ! = 0 )
{
addAndMakeVisible ( sliders [ 0 ] = new ColourComponentSlider ( TRANS ( " red " ) ) ) ;
addAndMakeVisible ( sliders [ 1 ] = new ColourComponentSlider ( TRANS ( " green " ) ) ) ;
addAndMakeVisible ( sliders [ 2 ] = new ColourComponentSlider ( TRANS ( " blue " ) ) ) ;
addChildComponent ( sliders [ 3 ] = new ColourComponentSlider ( TRANS ( " alpha " ) ) ) ;
sliders [ 3 ] - > setVisible ( ( flags & showAlphaChannel ) ! = 0 ) ;
for ( int i = 4 ; - - i > = 0 ; )
sliders [ i ] - > addListener ( this ) ;
}
if ( ( flags & showColourspace ) ! = 0 )
{
addAndMakeVisible ( colourSpace = new ColourSpaceView ( * this , h , s , v , gapAroundColourSpaceComponent ) ) ;
addAndMakeVisible ( hueSelector = new HueSelectorComp ( * this , h , gapAroundColourSpaceComponent ) ) ;
}
update ( ) ;
}
ColourSelector : : ~ ColourSelector ( )
{
dispatchPendingMessages ( ) ;
swatchComponents . clear ( ) ;
}
//==============================================================================
Colour ColourSelector : : getCurrentColour ( ) const
{
return ( ( flags & showAlphaChannel ) ! = 0 ) ? colour : colour . withAlpha ( ( uint8 ) 0xff ) ;
}
void ColourSelector : : setCurrentColour ( Colour c )
{
if ( c ! = colour )
{
colour = ( ( flags & showAlphaChannel ) ! = 0 ) ? c : c . withAlpha ( ( uint8 ) 0xff ) ;
updateHSV ( ) ;
update ( ) ;
}
}
void ColourSelector : : setHue ( float newH )
{
newH = jlimit ( 0.0f , 1.0f , newH ) ;
if ( h ! = newH )
{
h = newH ;
colour = Colour ( h , s , v , colour . getFloatAlpha ( ) ) ;
update ( ) ;
}
}
void ColourSelector : : setSV ( float newS , float newV )
{
newS = jlimit ( 0.0f , 1.0f , newS ) ;
newV = jlimit ( 0.0f , 1.0f , newV ) ;
if ( s ! = newS | | v ! = newV )
{
s = newS ;
v = newV ;
colour = Colour ( h , s , v , colour . getFloatAlpha ( ) ) ;
update ( ) ;
}
}
//==============================================================================
void ColourSelector : : updateHSV ( )
{
colour . getHSB ( h , s , v ) ;
}
void ColourSelector : : update ( )
{
if ( sliders [ 0 ] ! = nullptr )
{
sliders [ 0 ] - > setValue ( ( int ) colour . getRed ( ) ) ;
sliders [ 1 ] - > setValue ( ( int ) colour . getGreen ( ) ) ;
sliders [ 2 ] - > setValue ( ( int ) colour . getBlue ( ) ) ;
sliders [ 3 ] - > setValue ( ( int ) colour . getAlpha ( ) ) ;
}
if ( colourSpace ! = nullptr )
{
colourSpace - > updateIfNeeded ( ) ;
hueSelector - > updateIfNeeded ( ) ;
}
if ( ( flags & showColourAtTop ) ! = 0 )
repaint ( previewArea ) ;
sendChangeMessage ( ) ;
}
//==============================================================================
void ColourSelector : : paint ( Graphics & g )
{
g . fillAll ( findColour ( backgroundColourId ) ) ;
if ( ( flags & showColourAtTop ) ! = 0 )
{
const Colour currentColour ( getCurrentColour ( ) ) ;
g . fillCheckerBoard ( previewArea , 10 , 10 ,
Colour ( 0xffdddddd ) . overlaidWith ( currentColour ) ,
Colour ( 0xffffffff ) . overlaidWith ( currentColour ) ) ;
g . setColour ( Colours : : white . overlaidWith ( currentColour ) . contrasting ( ) ) ;
g . setFont ( Font ( 14.0f , Font : : bold ) ) ;
g . drawText ( currentColour . toDisplayString ( ( flags & showAlphaChannel ) ! = 0 ) ,
previewArea , Justification : : centred , false ) ;
}
if ( ( flags & showSliders ) ! = 0 )
{
g . setColour ( findColour ( labelTextColourId ) ) ;
g . setFont ( 11.0f ) ;
for ( int i = 4 ; - - i > = 0 ; )
{
if ( sliders [ i ] - > isVisible ( ) )
g . drawText ( sliders [ i ] - > getName ( ) + " : " ,
0 , sliders [ i ] - > getY ( ) ,
sliders [ i ] - > getX ( ) - 8 , sliders [ i ] - > getHeight ( ) ,
Justification : : centredRight , false ) ;
}
}
}
void ColourSelector : : resized ( )
{
const int swatchesPerRow = 8 ;
const int swatchHeight = 22 ;
const int numSliders = ( ( flags & showAlphaChannel ) ! = 0 ) ? 4 : 3 ;
const int numSwatches = getNumSwatches ( ) ;
const int swatchSpace = numSwatches > 0 ? edgeGap + swatchHeight * ( ( numSwatches + 7 ) / swatchesPerRow ) : 0 ;
const int sliderSpace = ( ( flags & showSliders ) ! = 0 ) ? jmin ( 22 * numSliders + edgeGap , proportionOfHeight ( 0.3f ) ) : 0 ;
const int topSpace = ( ( flags & showColourAtTop ) ! = 0 ) ? jmin ( 30 + edgeGap * 2 , proportionOfHeight ( 0.2f ) ) : edgeGap ;
previewArea . setBounds ( edgeGap , edgeGap , getWidth ( ) - edgeGap * 2 , topSpace - edgeGap * 2 ) ;
int y = topSpace ;
if ( ( flags & showColourspace ) ! = 0 )
{
const int hueWidth = jmin ( 50 , proportionOfWidth ( 0.15f ) ) ;
colourSpace - > setBounds ( edgeGap , y ,
getWidth ( ) - hueWidth - edgeGap - 4 ,
getHeight ( ) - topSpace - sliderSpace - swatchSpace - edgeGap ) ;
hueSelector - > setBounds ( colourSpace - > getRight ( ) + 4 , y ,
getWidth ( ) - edgeGap - ( colourSpace - > getRight ( ) + 4 ) ,
colourSpace - > getHeight ( ) ) ;
y = getHeight ( ) - sliderSpace - swatchSpace - edgeGap ;
}
if ( ( flags & showSliders ) ! = 0 )
{
const int sliderHeight = jmax ( 4 , sliderSpace / numSliders ) ;
for ( int i = 0 ; i < numSliders ; + + i )
{
sliders [ i ] - > setBounds ( proportionOfWidth ( 0.2f ) , y ,
proportionOfWidth ( 0.72f ) , sliderHeight - 2 ) ;
y + = sliderHeight ;
}
}
if ( numSwatches > 0 )
{
const int startX = 8 ;
const int xGap = 4 ;
const int yGap = 4 ;
const int swatchWidth = ( getWidth ( ) - startX * 2 ) / swatchesPerRow ;
y + = edgeGap ;
if ( swatchComponents . size ( ) ! = numSwatches )
{
swatchComponents . clear ( ) ;
for ( int i = 0 ; i < numSwatches ; + + i )
{
SwatchComponent * const sc = new SwatchComponent ( * this , i ) ;
swatchComponents . add ( sc ) ;
addAndMakeVisible ( sc ) ;
}
}
int x = startX ;
for ( int i = 0 ; i < swatchComponents . size ( ) ; + + i )
{
SwatchComponent * const sc = swatchComponents . getUnchecked ( i ) ;
sc - > setBounds ( x + xGap / 2 ,
y + yGap / 2 ,
swatchWidth - xGap ,
swatchHeight - yGap ) ;
if ( ( ( i + 1 ) % swatchesPerRow ) = = 0 )
{
x = startX ;
y + = swatchHeight ;
}
else
{
x + = swatchWidth ;
}
}
}
}
void ColourSelector : : sliderValueChanged ( Slider * )
{
if ( sliders [ 0 ] ! = nullptr )
setCurrentColour ( Colour ( ( uint8 ) sliders [ 0 ] - > getValue ( ) ,
( uint8 ) sliders [ 1 ] - > getValue ( ) ,
( uint8 ) sliders [ 2 ] - > getValue ( ) ,
( uint8 ) sliders [ 3 ] - > getValue ( ) ) ) ;
}
//==============================================================================
int ColourSelector : : getNumSwatches ( ) const
{
return 0 ;
}
Colour ColourSelector : : getSwatchColour ( const int ) const
{
jassertfalse ; // if you've overridden getNumSwatches(), you also need to implement this method
return Colours : : black ;
}
void ColourSelector : : setSwatchColour ( const int , const Colour & ) const
{
jassertfalse ; // if you've overridden getNumSwatches(), you also need to implement this method
}