2016-05-16 18:28:30 -05:00
// 16 may 2016
# include "uipriv_windows.hpp"
struct colorDialog {
HWND hwnd ;
HWND svChooser ;
HWND hSlider ;
HWND opacitySlider ;
HWND editH ;
HWND editS ;
HWND editV ;
HWND editRDouble , editRInt ;
HWND editGDouble , editGInt ;
HWND editBDouble , editBInt ;
HWND editADouble , editAInt ;
HWND editHex ;
double r ;
double g ;
double b ;
double a ;
struct colorDialogRGBA * out ;
} ;
2016-05-16 22:54:28 -05:00
// both of these are from the wiki
static void rgb2HSV ( double r , double g , double b , double * h , double * s , double * v )
2016-05-16 18:28:30 -05:00
{
2016-05-16 22:54:28 -05:00
double M , m ;
int whichmax ;
double c ;
M = r ;
whichmax = 0 ;
if ( M < g ) {
M = g ;
whichmax = 1 ;
}
if ( M < b ) {
M = b ;
whichmax = 2 ;
}
m = r ;
if ( m > g )
m = g ;
if ( m > b )
m = b ;
c = M - m ;
if ( c = = 0 )
* h = 0 ;
else {
switch ( whichmax ) {
case 0 :
* h = ( ( g - b ) / c ) ;
if ( * h > 6 )
* h - = 6 ;
break ;
case 1 :
* h = ( ( b - r ) / c ) + 2 ;
break ;
case 2 :
* h = ( ( r - g ) / c ) + 4 ;
break ;
}
* h / = 6 ; // put in range [0,1)
}
* v = M ;
if ( c = = 0 )
* s = 0 ;
else
* s = c / * v ;
2016-05-16 18:28:30 -05:00
}
2016-05-16 22:54:28 -05:00
static void hsv2RGB ( double h , double s , double v , double * r , double * g , double * b )
2016-05-16 18:28:30 -05:00
{
2016-05-16 22:54:28 -05:00
double c ;
int hPrime ;
double x ;
double m ;
double c1 , c2 ;
c = v * s ;
hPrime = ( int ) ( h * 6 ) ; // equivalent to splitting into 60° chunks
x = c * ( 1 - abs ( hPrime % 2 - 1 ) ) ;
m = v - c ;
switch ( hPrime ) {
case 0 :
* r = c + m ;
* g = x + m ;
* b = m ;
return ;
case 1 :
* r = x + m ;
* g = c + m ;
* b = m ;
return ;
case 2 :
* r = m ;
* g = c + m ;
* b = x + m ;
return ;
case 3 :
* r = m ;
* g = x + m ;
* b = c + m ;
return ;
case 4 :
* r = x + m ;
* g = m ;
* b = c + m ;
return ;
case 5 :
* r = c + m ;
* g = m ;
* b = x + m ;
return ;
2016-05-16 18:28:30 -05:00
}
2016-05-16 22:54:28 -05:00
// TODO
}
2016-05-16 18:28:30 -05:00
2016-05-16 22:54:28 -05:00
// this interesting approach comes from xxxx
static void drawSVChooser ( struct colorDialog * c , ID2D1RenderTarget * rt )
{
D2D1_SIZE_F size ;
D2D1_RECT_F rect ;
double h , s , v ;
double rTop , gTop , bTop ;
D2D1_GRADIENT_STOP stops [ 2 ] ;
ID2D1GradientStopCollection * collection ;
D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES lprop ;
D2D1_BRUSH_PROPERTIES bprop ;
ID2D1LinearGradientBrush * brush ;
HRESULT hr ;
size = rt - > GetSize ( ) ;
rect . left = 0 ;
rect . top = 0 ;
rect . right = size . width ;
rect . bottom = size . height ;
// TODO draw checkerboard
// first, draw a vertical gradient from the current hue at max S/V to black
// the source example draws it upside down; let's do so too just to be safe
rgb2HSV ( c - > r , c - > g , c - > b , & h , & s , & v ) ;
hsv2RGB ( h , 1.0 , 1.0 , & rTop , & gTop , & bTop ) ;
stops [ 0 ] . position = 0 ;
stops [ 0 ] . color . r = 0.0 ;
stops [ 0 ] . color . g = 0.0 ;
stops [ 0 ] . color . b = 0.0 ;
stops [ 0 ] . color . a = 1.0 ;
stops [ 1 ] . position = 1 ;
stops [ 1 ] . color . r = rTop ;
stops [ 1 ] . color . g = gTop ;
stops [ 1 ] . color . b = bTop ;
stops [ 1 ] . color . a = 1.0 ;
hr = rt - > CreateGradientStopCollection ( stops , 2 ,
D2D1_GAMMA_2_2 , D2D1_EXTEND_MODE_CLAMP ,
& collection ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error making gradient stop collection for first gradient in SV chooser " , hr ) ;
ZeroMemory ( & lprop , sizeof ( D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES ) ) ;
lprop . startPoint . x = size . width / 2 ;
lprop . startPoint . y = size . height ;
lprop . endPoint . x = size . width / 2 ;
lprop . endPoint . y = 0 ;
ZeroMemory ( & bprop , sizeof ( D2D1_BRUSH_PROPERTIES ) ) ;
bprop . opacity = 1.0 ;
bprop . transform . _11 = 1 ;
bprop . transform . _22 = 1 ;
hr = rt - > CreateLinearGradientBrush ( & lprop , & bprop ,
collection , & brush ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error making gradient brush for first gradient in SV chooser " , hr ) ;
rt - > FillRectangle ( & rect , brush ) ;
brush - > Release ( ) ;
collection - > Release ( ) ;
}
static LRESULT CALLBACK svChooserSubProc ( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam , UINT_PTR uIdSubclass , DWORD_PTR dwRefData )
{
ID2D1RenderTarget * rt ;
struct colorDialog * c ;
switch ( uMsg ) {
case msgD2DScratchPaint :
rt = ( ID2D1RenderTarget * ) lParam ;
c = ( struct colorDialog * ) dwRefData ;
drawSVChooser ( c , rt ) ;
return 0 ;
case WM_NCDESTROY :
if ( RemoveWindowSubclass ( hwnd , svChooserSubProc , uIdSubclass ) = = FALSE )
logLastError ( L " error removing color dialog SV chooser subclass " ) ;
break ;
}
return DefSubclassProc ( hwnd , uMsg , wParam , lParam ) ;
}
// TODO extract into d2dscratch.cpp, use in font dialog
HWND replaceWithD2DScratch ( HWND parent , int id , SUBCLASSPROC subproc , void * data )
{
HWND replace ;
RECT r ;
replace = getDlgItem ( parent , id ) ;
uiWindowsEnsureGetWindowRect ( replace , & r ) ;
mapWindowRect ( NULL , parent , & r ) ;
uiWindowsEnsureDestroyWindow ( replace ) ;
return newD2DScratch ( parent , & r , ( HMENU ) id , subproc , ( DWORD_PTR ) data ) ;
// TODO preserve Z-order
2016-05-16 18:28:30 -05:00
}
// a few issues:
// - some controls are positioned wrong; see http://stackoverflow.com/questions/37263267/why-are-some-of-my-controls-positioned-slightly-off-in-a-dialog-template-in-a-re
// - labels are too low; need to adjust them by the font's internal leading
// fixupControlPositions() and the following helper routines fix that for us
static LONG offsetTo ( HWND a , HWND b )
{
RECT ra , rb ;
uiWindowsEnsureGetWindowRect ( a , & ra ) ;
uiWindowsEnsureGetWindowRect ( b , & rb ) ;
return rb . top - ra . bottom ;
}
static void moveWindowsUp ( struct colorDialog * c , LONG by , . . . )
{
va_list ap ;
HWND cur ;
RECT r ;
va_start ( ap , by ) ;
for ( ; ; ) {
cur = va_arg ( ap , HWND ) ;
if ( cur = = NULL )
break ;
uiWindowsEnsureGetWindowRect ( cur , & r ) ;
mapWindowRect ( NULL , c - > hwnd , & r ) ;
r . top - = by ;
r . bottom - = by ;
// TODO this isn't technically during a resize
uiWindowsEnsureMoveWindowDuringResize ( cur ,
r . left , r . top ,
r . right - r . left , r . bottom - r . top ) ;
}
va_end ( ap ) ;
}
static void fixupControlPositions ( struct colorDialog * c )
{
HWND labelH ;
HWND labelS ;
HWND labelV ;
HWND labelR ;
HWND labelG ;
HWND labelB ;
HWND labelA ;
HWND labelHex ;
LONG offset ;
uiWindowsSizing sizing ;
labelH = getDlgItem ( c - > hwnd , rcHLabel ) ;
labelS = getDlgItem ( c - > hwnd , rcSLabel ) ;
labelV = getDlgItem ( c - > hwnd , rcVLabel ) ;
labelR = getDlgItem ( c - > hwnd , rcRLabel ) ;
labelG = getDlgItem ( c - > hwnd , rcGLabel ) ;
labelB = getDlgItem ( c - > hwnd , rcBLabel ) ;
labelA = getDlgItem ( c - > hwnd , rcALabel ) ;
labelHex = getDlgItem ( c - > hwnd , rcHexLabel ) ;
offset = offsetTo ( c - > editH , c - > editS ) ;
moveWindowsUp ( c , offset ,
labelS , c - > editS ,
labelG , c - > editGDouble , c - > editGInt ,
NULL ) ;
offset = offsetTo ( c - > editS , c - > editV ) ;
moveWindowsUp ( c , offset ,
labelV , c - > editV ,
labelB , c - > editBDouble , c - > editBInt ,
NULL ) ;
offset = offsetTo ( c - > editBDouble , c - > editADouble ) ;
moveWindowsUp ( c , offset ,
labelA , c - > editADouble , c - > editAInt ,
NULL ) ;
// TODO this uses the message font, not the dialog font
uiWindowsGetSizing ( c - > hwnd , & sizing ) ;
offset = sizing . InternalLeading ;
moveWindowsUp ( c , offset ,
labelH , labelS , labelV ,
labelR , labelG , labelB , labelA ,
labelHex ,
NULL ) ;
}
static struct colorDialog * beginColorDialog ( HWND hwnd , LPARAM lParam )
{
struct colorDialog * c ;
c = uiNew ( struct colorDialog ) ;
c - > hwnd = hwnd ;
c - > out = ( struct colorDialogRGBA * ) lParam ;
c - > r = c - > out - > r ; // load initial values now
c - > g = c - > out - > g ;
c - > b = c - > out - > b ;
c - > a = c - > out - > a ;
// TODO set up d2dscratches
2016-05-16 22:54:28 -05:00
// TODO prefix all these with rcColor instead of just rc
2016-05-16 18:28:30 -05:00
c - > editH = getDlgItem ( c - > hwnd , rcH ) ;
c - > editS = getDlgItem ( c - > hwnd , rcS ) ;
c - > editV = getDlgItem ( c - > hwnd , rcV ) ;
c - > editRDouble = getDlgItem ( c - > hwnd , rcRDouble ) ;
c - > editRInt = getDlgItem ( c - > hwnd , rcRInt ) ;
c - > editGDouble = getDlgItem ( c - > hwnd , rcGDouble ) ;
c - > editGInt = getDlgItem ( c - > hwnd , rcGInt ) ;
c - > editBDouble = getDlgItem ( c - > hwnd , rcBDouble ) ;
c - > editBInt = getDlgItem ( c - > hwnd , rcBInt ) ;
c - > editADouble = getDlgItem ( c - > hwnd , rcADouble ) ;
c - > editAInt = getDlgItem ( c - > hwnd , rcAInt ) ;
c - > editHex = getDlgItem ( c - > hwnd , rcHex ) ;
2016-05-16 22:54:28 -05:00
c - > svChooser = replaceWithD2DScratch ( c - > hwnd , rcColorSVChooser , svChooserSubProc , c ) ;
2016-05-16 18:28:30 -05:00
fixupControlPositions ( c ) ;
return c ;
}
2016-05-16 22:54:28 -05:00
static void endColorDialog ( struct colorDialog * c , INT_PTR code )
{
if ( EndDialog ( c - > hwnd , code ) = = 0 )
logLastError ( L " error ending color dialog " ) ;
uiFree ( c ) ;
}
static BOOL tryFinishDialog ( struct colorDialog * c , WPARAM wParam )
{
// cancelling
if ( LOWORD ( wParam ) ! = IDOK ) {
endColorDialog ( c , 1 ) ;
return TRUE ;
}
// OK
c - > out - > r = c - > r ;
c - > out - > g = c - > g ;
c - > out - > b = c - > b ;
c - > out - > a = c - > a ;
endColorDialog ( c , 2 ) ;
return TRUE ;
}
2016-05-16 18:28:30 -05:00
static INT_PTR CALLBACK colorDialogDlgProc ( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
struct colorDialog * c ;
c = ( struct colorDialog * ) GetWindowLongPtrW ( hwnd , DWLP_USER ) ;
if ( c = = NULL ) {
if ( uMsg = = WM_INITDIALOG ) {
c = beginColorDialog ( hwnd , lParam ) ;
SetWindowLongPtrW ( hwnd , DWLP_USER , ( LONG_PTR ) c ) ;
return TRUE ;
}
return FALSE ;
}
switch ( uMsg ) {
case WM_COMMAND :
SetWindowLongPtrW ( c - > hwnd , DWLP_MSGRESULT , 0 ) ; // just in case
switch ( LOWORD ( wParam ) ) {
case IDOK :
case IDCANCEL :
if ( HIWORD ( wParam ) ! = BN_CLICKED )
return FALSE ;
return tryFinishDialog ( c , wParam ) ;
}
return FALSE ;
}
return FALSE ;
}
BOOL showColorDialog ( HWND parent , struct colorDialogRGBA * c )
{
switch ( DialogBoxParamW ( hInstance , MAKEINTRESOURCE ( rcColorDialog ) , parent , colorDialogDlgProc , ( LPARAM ) c ) ) {
case 1 : // cancel
return FALSE ;
case 2 : // ok
// make the compiler happy by putting the return after the switch
break ;
default :
logLastError ( L " error running color dialog " ) ;
}
return TRUE ;
}