KiCad PCB EDA Suite
dialog_color_picker.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2018-2021 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 
22 #include <cmath>
23 #include <algorithm>
24 #include <kiface_base.h>
25 #include <settings/app_settings.h>
26 #include <widgets/color_swatch.h>
27 #include <wx/bitmap.h>
28 #include <wx/dcmemory.h>
29 
30 #define ALPHA_MAX 100 // the max value returned by the alpha (opacity) slider
31 
32 using KIGFX::COLOR4D;
33 
34 // Configure the spin controls contained inside the dialog
35 void configureSpinCtrl( wxSpinCtrl* aCtrl )
36 {
37  wxSize textLength = aCtrl->GetTextExtent( "999" );
38  wxSize ctrlSize = aCtrl->GetSizeFromTextSize( textLength );
39 
40  aCtrl->SetMinSize( ctrlSize );
41  aCtrl->SetSize( ctrlSize );
42 }
43 
44 
45 DIALOG_COLOR_PICKER::DIALOG_COLOR_PICKER( wxWindow* aParent, const COLOR4D& aCurrentColor,
46  bool aAllowOpacityControl,
47  CUSTOM_COLORS_LIST* aUserColors,
48  const COLOR4D& aDefaultColor ) :
49  DIALOG_COLOR_PICKER_BASE( aParent )
50 {
51  m_allowMouseEvents = false;
52  m_allowOpacityCtrl = aAllowOpacityControl;
53  m_previousColor4D = aCurrentColor;
54  m_newColor4D = aCurrentColor;
55  m_cursorsSize = 8; // Size of square cursors drawn on color bitmaps
56  m_newColor4D.ToHSV( m_hue, m_sat, m_val, true );
57  m_bitmapRGB = nullptr;
58  m_bitmapHSV = nullptr;
59  m_selectedCursor = nullptr;
60  m_defaultColor = aDefaultColor;
61 
62  if( !m_allowOpacityCtrl )
63  {
64  m_SizerTransparency->Show( false );
65 
66  if( aCurrentColor != COLOR4D::UNSPECIFIED )
67  {
68  m_previousColor4D.a = 1.0;
69  m_newColor4D.a = 1.0;
70  }
71  }
72 
73  // UNSPECIFIED is ( 0, 0, 0, 0 ) but that is unfriendly for editing because you have to notice
74  // first that the value slider is all the way down before you get any color
75  if( aCurrentColor == COLOR4D::UNSPECIFIED )
76  m_val = 1.0;
77 
79  wxASSERT( cfg );
80 
81  m_notebook->SetSelection( cfg->m_ColorPicker.default_tab );
82 
83  // Build the defined colors panel:
84  initDefinedColors( aUserColors );
85 
93  if( aDefaultColor == COLOR4D::UNSPECIFIED )
94  m_resetToDefault->SetLabel( _( "Clear Color" ) );
95 
96  m_sdbSizerOK->SetDefault();
97 }
98 
99 
101 {
103  wxASSERT( cfg );
104 
105  cfg->m_ColorPicker.default_tab = m_notebook->GetSelection();
106 
107  delete m_bitmapRGB;
108  delete m_bitmapHSV;
109 
110  for( wxStaticBitmap* swatch : m_colorSwatches )
111  {
112  swatch->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED,
113  wxMouseEventHandler( DIALOG_COLOR_PICKER::buttColorClick ),
114  nullptr, this );
115  }
116 }
117 
118 
119 void DIALOG_COLOR_PICKER::updatePreview( wxStaticBitmap* aStaticBitmap, COLOR4D& aColor4D )
120 {
121  wxBitmap newBm = COLOR_SWATCH::MakeBitmap( aColor4D, COLOR4D::WHITE, aStaticBitmap->GetSize(),
122  ConvertDialogToPixels( CHECKERBOARD_SIZE_DU ),
123  aStaticBitmap->GetParent()->GetBackgroundColour() );
124  aStaticBitmap->SetBitmap( newBm );
125 }
126 
127 
129 {
130  // Draw all bitmaps, with colors according to the color 4D
132  SetEditVals( ALL_CHANGED, false );
133  drawAll();
134 
135  // Configure the spin control sizes
141 
142  m_notebook->GetPage( 0 )->Layout();
143  m_notebook->GetPage( 1 )->Layout();
144 
146 
147  return true;
148 }
149 
150 
152 {
153  #define ID_COLOR_BLACK 2000 // colors_id = ID_COLOR_BLACK a ID_COLOR_BLACK + NBCOLORS-1
154 
155  // Colors are built from the colorRefs() table (size NBCOLORS).
156  // The look is better when colorRefs() order is displayed in a grid matrix
157  // of 7 row and 5 columns, first filling a row, and after the next column.
158  // But the wxFlexGrid used here must be filled by columns, then next row
159  // the best interval colorRefs() from a matrix row to the next row is 6
160  // So when have to reorder the index used to explore colorRefs()
161  int grid_col = 0;
162  int grid_row = 0;
163  int table_row_count = 7;
164 
165  wxSize swatchSize = ConvertDialogToPixels( SWATCH_SIZE_LARGE_DU );
166  wxSize checkerboardSize = ConvertDialogToPixels( CHECKERBOARD_SIZE_DU );
167  COLOR4D checkboardBackground = m_OldColorRect->GetParent()->GetBackgroundColour();
168 
169  auto addSwatch =
170  [&]( int aId, COLOR4D aColor, const wxString& aColorName )
171  {
172  wxBitmap bm = COLOR_SWATCH::MakeBitmap( aColor, COLOR4D::WHITE, swatchSize,
173  checkerboardSize, checkboardBackground );
174  wxStaticBitmap* swatch = new wxStaticBitmap( m_panelDefinedColors, aId, bm );
175 
176  m_fgridColor->Add( swatch, 0, wxALIGN_CENTER_VERTICAL, 5 );
177 
178  wxStaticText* label = new wxStaticText( m_panelDefinedColors, wxID_ANY, aColorName,
179  wxDefaultPosition, wxDefaultSize, 0 );
180  m_fgridColor->Add( label, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, 15 );
181 
182  m_colorSwatches.push_back( swatch );
183 
184  swatch->Connect( wxEVT_LEFT_DOWN,
185  wxMouseEventHandler( DIALOG_COLOR_PICKER::buttColorClick ),
186  nullptr, this );
187  swatch->Connect( wxEVT_LEFT_DCLICK,
188  wxMouseEventHandler( DIALOG_COLOR_PICKER::colorDClick ),
189  nullptr, this );
190  };
191 
192  // If no predefined list is given, build the default predefined colors:
193  if( aPredefinedColors )
194  {
195  for( unsigned jj = 0; jj < aPredefinedColors->size() && jj < NBCOLORS; ++jj )
196  {
197  CUSTOM_COLOR_ITEM* item = & *aPredefinedColors->begin() + jj;
198  int butt_ID = ID_COLOR_BLACK + jj;
199 
200  addSwatch( butt_ID, item->m_Color, item->m_ColorName );
201  m_Color4DList.push_back( item->m_Color );
202  }
203  }
204  else
205  {
206  m_Color4DList.assign( NBCOLORS, COLOR4D( 0.0, 0.0, 0.0, 1.0 ) );
207 
208  for( int jj = 0; jj < NBCOLORS; ++jj, grid_col++ )
209  {
210  if( grid_col * table_row_count >= NBCOLORS )
211  { // the current grid row is filled, and we must fill the next grid row
212  grid_col = 0;
213  grid_row++;
214  }
215 
216  int ii = grid_row + ( grid_col * table_row_count ); // The index in colorRefs()
217  int butt_ID = ID_COLOR_BLACK + ii;
218  COLOR4D buttcolor = COLOR4D( colorRefs()[ii].m_Numcolor );
219 
220  addSwatch( butt_ID, buttcolor, wxGetTranslation( colorRefs()[ii].m_ColorName ) );
221  m_Color4DList[ butt_ID - ID_COLOR_BLACK ] = buttcolor;
222  }
223  }
224 }
225 
226 
228 {
229  wxSize bmsize = m_RgbBitmap->GetSize();
230  int half_size = std::min( bmsize.x, bmsize.y )/2;
231 
232  // We use here a Y axis from bottom to top and origin to center, So we need to map
233  // coordinated to write pixel in a wxImage. MAPX and MAPY are defined above so they
234  // must be undefined here to prevent compiler warnings.
235 #undef MAPX
236 #undef MAPY
237 #define MAPX( xx ) bmsize.x / 2 + ( xx )
238 #define MAPY( yy ) bmsize.y / 2 - ( yy )
239 
240  // Reserve room to draw cursors inside the bitmap
241  half_size -= m_cursorsSize/2;
242 
243  COLOR4D color;
244 
245  // Red blue area in X Z 3d axis
246  double inc = 255.0 / half_size;
247  #define SLOPE_AXIS 50.0
248  double slope = SLOPE_AXIS/half_size;
249  color.g = 0.0;
250 
251  wxImage img( bmsize ); // a temporary buffer to build the color map
252 
253  // clear background (set the window bg color)
254  wxColor bg = GetBackgroundColour();
255 
256  // Don't do standard-color lookups on OSX each time through the loop
257  wxColourBase::ChannelType bgR = bg.Red();
258  wxColourBase::ChannelType bgG = bg.Green();
259  wxColourBase::ChannelType bgB = bg.Blue();
260 
261  for( int xx = 0; xx < bmsize.x; xx++ ) // blue axis
262  {
263  for( int yy = 0; yy < bmsize.y; yy++ ) // Red axis
264  img.SetRGB( xx, yy, bgR, bgG, bgB );
265  }
266 
267  // Build the palette
268  for( int xx = 0; xx < half_size; xx++ ) // blue axis
269  {
270  color.b = inc * xx;
271 
272  for( int yy = 0; yy < half_size; yy++ ) // Red axis
273  {
274  color.r = inc * yy;
275  img.SetRGB( MAPX( xx ), MAPY( yy - (slope*xx) ), color.r, color.g, color.b );
276  }
277  }
278 
279  // Red green area in y Z 3d axis
280  color.b = 0.0;
281 
282  for( int xx = 0; xx < half_size; xx++ ) // green axis
283  {
284  color.g = inc * xx;
285 
286  for( int yy = 0; yy < half_size; yy++ ) // Red axis
287  {
288  color.r = inc * yy;
289  img.SetRGB( MAPX( -xx ), MAPY( yy - (slope*xx) ), color.r, color.g, color.b );
290  }
291  }
292 
293  // Blue green area in x y 3d axis
294  color.r = 0.0;
295 
296  for( int xx = 0; xx < half_size; xx++ ) // green axis
297  {
298  color.g = inc * xx;
299 
300  for( int yy = 0; yy < half_size; yy++ ) // blue axis
301  {
302  color.b = inc * yy;
303 
304  // Mapping the xx, yy color axis to draw coordinates is more tricky than previously
305  // in DC coordinates:
306  // the blue axis is the (0, 0) to half_size, (-yy - SLOPE_AXIS)
307  // the green axis is the (0, 0) to - half_size, (-yy - SLOPE_AXIS)
308  int drawX = -xx + yy;
309  int drawY = - std::min( xx,yy ) * 0.9;
310  img.SetRGB( MAPX( drawX ), MAPY( drawY - std::abs( slope*drawX ) ),
311  color.r, color.g, color.b );
312  }
313  }
314 
315  delete m_bitmapRGB;
316  m_bitmapRGB = new wxBitmap( img, 24 );
317  m_RgbBitmap->SetBitmap( *m_bitmapRGB );
318 }
319 
320 
322 {
323  wxSize bmsize = m_HsvBitmap->GetSize();
324  int half_size = std::min( bmsize.x, bmsize.y )/2;
325 
326  // We use here a Y axis from bottom to top and origin to center, So we need to map
327  // coordinated to write pixel in a wxImage
328  #define MAPX( xx ) bmsize.x / 2 + ( xx )
329  #define MAPY( yy ) bmsize.y / 2 - ( yy )
330 
331  wxImage img( bmsize ); // a temporary buffer to build the color map
332 
333  // clear background (set the window bg color)
334  wxColor bg = GetBackgroundColour();
335 
336  // Don't do standard-color lookups on OSX each time through the loop
337  wxColourBase::ChannelType bgR = bg.Red();
338  wxColourBase::ChannelType bgG = bg.Green();
339  wxColourBase::ChannelType bgB = bg.Blue();
340 
341  for( int xx = 0; xx < bmsize.x; xx++ ) // blue axis
342  {
343  for( int yy = 0; yy < bmsize.y; yy++ ) // Red axis
344  img.SetRGB( xx, yy, bgR, bgG, bgB );
345  }
346 
347  // Reserve room to draw cursors inside the bitmap
348  half_size -= m_cursorsSize/2;
349 
350  double hue, sat;
351  COLOR4D color;
352  int sq_radius = half_size*half_size;
353 
354  // Build the palette
355  for( int xx = -half_size; xx < half_size; xx++ )
356  {
357  for( int yy = -half_size; yy < half_size; yy++ )
358  {
359  sat = double(xx*xx + yy*yy) / sq_radius;
360 
361  // sat is <= 1.0
362  // any value > 1.0 is not a valid HSB color:
363  if( sat > 1.0 )
364  continue;
365 
366  // sat is the distance from center
367  sat = sqrt( sat );
368  hue = atan2( (double)yy, (double)xx ) * 180 / M_PI;
369 
370  if( hue < 0.0 )
371  hue += 360.0;
372 
373  color.FromHSV( hue, sat, 1.0 );
374 
375  img.SetRGB( MAPX( xx ), MAPY( yy ), color.r*255, color.g*255, color.b*255 );
376  }
377  }
378 
379  delete m_bitmapHSV;
380  m_bitmapHSV = new wxBitmap( img, 24 );
381  m_HsvBitmap->SetBitmap( *m_bitmapHSV );
382 }
383 
384 
386 {
387  if( !m_bitmapRGB || m_bitmapRGB->GetSize() != m_RgbBitmap->GetSize() )
388  createRGBBitmap();
389 
390  wxMemoryDC bitmapDC;
391  wxSize bmsize = m_bitmapRGB->GetSize();
392  int half_size = std::min( bmsize.x, bmsize.y )/2;
393  wxBitmap newBm( *m_bitmapRGB );
394  bitmapDC.SelectObject( newBm );
395 
396  // Use Y axis from bottom to top and origin to center
397  bitmapDC.SetAxisOrientation( true, true );
398  bitmapDC.SetDeviceOrigin( half_size, half_size );
399 
400  // Reserve room to draw cursors inside the bitmap
401  half_size -= m_cursorsSize/2;
402 
403  // Draw the 3 RGB cursors, using white color to make them always visible:
404  wxPen pen( wxColor( 255, 255, 255 ) );
405  wxBrush brush( wxColor( 0, 0, 0 ), wxBRUSHSTYLE_TRANSPARENT );
406  bitmapDC.SetPen( pen );
407  bitmapDC.SetBrush( brush );
408  int half_csize = m_cursorsSize / 2;
409 
410  #define SLOPE_AXIS 50.0
411  double slope = SLOPE_AXIS / half_size;
412 
413  // Red axis cursor (Z 3Daxis):
414  m_cursorBitmapRed.x = 0;
415  m_cursorBitmapRed.y = m_newColor4D.r * half_size;
416  bitmapDC.DrawRectangle( m_cursorBitmapRed.x - half_csize,
417  m_cursorBitmapRed.y - half_csize,
419 
420  // Blue axis cursor (X 3Daxis):
421  m_cursorBitmapBlue.x = m_newColor4D.b * half_size;
423  bitmapDC.DrawRectangle( m_cursorBitmapBlue.x - half_csize,
424  m_cursorBitmapBlue.y - half_csize,
426 
427  // Green axis cursor (Y 3Daxis):
428  m_cursorBitmapGreen.x = m_newColor4D.g * half_size;
431 
432  bitmapDC.DrawRectangle( m_cursorBitmapGreen.x - half_csize,
433  m_cursorBitmapGreen.y - half_csize,
435 
436  // Draw the 3 RGB axis:
437  half_size += half_size/5;
438  bitmapDC.DrawLine( 0, 0, 0, half_size ); // Red axis (Z 3D axis)
439  bitmapDC.DrawLine( 0, 0, half_size, - half_size*slope ); // Blue axis (X 3D axis)
440  bitmapDC.DrawLine( 0, 0, -half_size, - half_size*slope ); // green axis (Y 3D axis)
441 
442  m_RgbBitmap->SetBitmap( newBm );
443 
444  /* Deselect the Tool Bitmap from DC,
445  * in order to delete the MemoryDC safely without deleting the bitmap */
446  bitmapDC.SelectObject( wxNullBitmap );
447 }
448 
449 
451 {
452  if( !m_bitmapHSV || m_bitmapHSV->GetSize() != m_HsvBitmap->GetSize() )
453  createHSVBitmap();
454 
455  wxMemoryDC bitmapDC;
456  wxSize bmsize = m_bitmapHSV->GetSize();
457  int half_size = std::min( bmsize.x, bmsize.y ) / 2;
458  wxBitmap newBm( *m_bitmapHSV );
459  bitmapDC.SelectObject( newBm );
460 
461  // Use Y axis from bottom to top and origin to center
462  bitmapDC.SetAxisOrientation( true, true );
463  bitmapDC.SetDeviceOrigin( half_size, half_size );
464 
465  // Reserve room to draw cursors inside the bitmap
466  half_size -= m_cursorsSize / 2;
467 
468  // Draw the HSB cursor:
469  m_cursorBitmapHSV.x = cos( m_hue * M_PI / 180.0 ) * half_size * m_sat;
470  m_cursorBitmapHSV.y = sin( m_hue * M_PI / 180.0 ) * half_size * m_sat;
471 
472  wxPen pen( wxColor( 0, 0, 0 ) );
473  wxBrush brush( wxColor( 0, 0, 0 ), wxBRUSHSTYLE_TRANSPARENT );
474  bitmapDC.SetPen( pen );
475  bitmapDC.SetBrush( brush );
476 
477  int half_csize = m_cursorsSize/2;
478  bitmapDC.DrawRectangle( m_cursorBitmapHSV.x- half_csize,
479  m_cursorBitmapHSV.y-half_csize,
481 
482  m_HsvBitmap->SetBitmap( newBm );
483 
484  /* Deselect the Tool Bitmap from DC,
485  * in order to delete the MemoryDC safely without deleting the bitmap
486  */
487  bitmapDC.SelectObject( wxNullBitmap );
488 }
489 
490 
491 void DIALOG_COLOR_PICKER::SetEditVals( CHANGED_COLOR aChanged, bool aCheckTransparency )
492 {
493  if( aCheckTransparency )
494  {
495  // If they've changed the color, they probably don't want it to remain 100% transparent,
496  // and it looks like a bug when changing the color has no effect.
497  if( m_newColor4D.a == 0.0 )
498  m_newColor4D.a = 1.0;
499  }
500 
502 
503  if( aChanged == RED_CHANGED || aChanged == GREEN_CHANGED || aChanged == BLUE_CHANGED )
504  m_newColor4D.ToHSV( m_hue, m_sat, m_val, true );
505 
506  if( aChanged != RED_CHANGED )
507  m_spinCtrlRed->SetValue( normalizeToInt( m_newColor4D.r ) );
508 
509  if( aChanged != GREEN_CHANGED )
511 
512  if( aChanged != BLUE_CHANGED )
514 
515  if( aChanged != HUE_CHANGED )
516  m_spinCtrlHue->SetValue( (int)m_hue );
517 
518  if( aChanged != SAT_CHANGED )
519  m_spinCtrlSaturation->SetValue( m_sat * 255 );
520 
521  if( aChanged != VAL_CHANGED )
522  m_sliderBrightness->SetValue(normalizeToInt( m_val ) );
523 
524  if( aChanged == HEX_CHANGED )
526  else
527  m_colorValue->ChangeValue( m_newColor4D.ToHexString() );
528 }
529 
530 
532 {
533  m_NewColorRect->Freeze(); // Avoid flicker
534  m_HsvBitmap->Freeze();
535  m_RgbBitmap->Freeze();
537  drawHSVPalette();
538  drawRGBPalette();
539  m_NewColorRect->Thaw();
540  m_HsvBitmap->Thaw();
541  m_RgbBitmap->Thaw();
542  m_NewColorRect->Refresh();
543  m_HsvBitmap->Refresh();
544  m_RgbBitmap->Refresh();
545 }
546 
547 
548 void DIALOG_COLOR_PICKER::colorDClick( wxMouseEvent& event )
549 {
550  wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
551 }
552 
553 
554 void DIALOG_COLOR_PICKER::buttColorClick( wxMouseEvent& event )
555 {
556  int id = event.GetId();
558  m_newColor4D.r = color.r;
559  m_newColor4D.g = color.g;
560  m_newColor4D.b = color.b;
561  m_newColor4D.a = color.a;
562 
563  m_newColor4D.ToHSV( m_hue, m_sat, m_val, true );
564  SetEditVals( ALL_CHANGED, false );
565 
566  drawAll();
567 
568  event.Skip();
569 }
570 
571 
572 void DIALOG_COLOR_PICKER::onRGBMouseClick( wxMouseEvent& event )
573 {
574  m_allowMouseEvents = true;
575  wxPoint mousePos = event.GetPosition();
576 
577  // The cursor position is relative to the m_bitmapHSV wxBitmap center
578  wxSize bmsize = m_bitmapRGB->GetSize();
579  int half_size = std::min( bmsize.x, bmsize.y ) / 2;
580  mousePos.x -= half_size;
581  mousePos.y -= half_size;
582  mousePos.y = -mousePos.y; // Use the bottom to top vertical axis
583 
584  wxPoint dist = m_cursorBitmapRed - mousePos;
585 
586  if( std::abs( dist.x ) <= m_cursorsSize/2 && std::abs( dist.y ) <= m_cursorsSize/2 )
587  {
589  return;
590  }
591 
592  dist = m_cursorBitmapGreen - mousePos;
593 
594  if( std::abs( dist.x ) <= m_cursorsSize/2 && std::abs( dist.y ) <= m_cursorsSize/2 )
595  {
597  return;
598  }
599 
600  dist = m_cursorBitmapBlue - mousePos;
601 
602  if( std::abs( dist.x ) <= m_cursorsSize/2 && std::abs( dist.y ) <= m_cursorsSize/2 )
603  {
605  return;
606  }
607 
608  m_selectedCursor = nullptr;
609 }
610 
611 
612 void DIALOG_COLOR_PICKER::onRGBMouseDrag( wxMouseEvent& event )
613 {
614  if( !event.Dragging() || !m_allowMouseEvents )
615  {
616  m_selectedCursor = nullptr;
617  return;
618  }
619 
623  return;
624 
625  // Adjust the HSV cursor position to follow the mouse cursor
626  // The cursor position is relative to the m_bitmapHSV wxBitmap center
627  wxPoint mousePos = event.GetPosition();
628  wxSize bmsize = m_bitmapRGB->GetSize();
629  int half_size = std::min( bmsize.x, bmsize.y ) / 2;
630  mousePos.x -= half_size;
631  mousePos.y -= half_size;
632  mousePos.y = -mousePos.y; // Use the bottom to top vertical axis
633 
634  half_size -= m_cursorsSize / 2; // the actual half_size of the palette area
635 
636  // Change colors according to the selected cursor:
638  {
639  if( mousePos.y >= 0 && mousePos.y <= half_size )
640  m_newColor4D.r = (double)mousePos.y / half_size;
641  else
642  return;
643  }
644 
646  {
647  mousePos.x = -mousePos.x;
648 
649  if( mousePos.x >= 0 && mousePos.x <= half_size )
650  m_newColor4D.g = (double)mousePos.x / half_size;
651  else
652  return;
653  }
654 
656  {
657  if( mousePos.x >= 0 && mousePos.x <= half_size )
658  m_newColor4D.b = (double)mousePos.x / half_size;
659  else
660  return;
661  }
662 
663  m_newColor4D.ToHSV( m_hue, m_sat, m_val, true );
664  SetEditVals( ALL_CHANGED, true );
665 
666  drawAll();
667 }
668 
669 
670 void DIALOG_COLOR_PICKER::onHSVMouseClick( wxMouseEvent& event )
671 {
672  m_allowMouseEvents = true;
673 
674  if( setHSvaluesFromCursor( event.GetPosition() ) )
675  drawAll();
676 }
677 
678 
679 void DIALOG_COLOR_PICKER::onHSVMouseDrag( wxMouseEvent& event )
680 {
681  if( !event.Dragging() || !m_allowMouseEvents )
682  return;
683 
684  if( setHSvaluesFromCursor( event.GetPosition() ) )
685  drawAll();
686 }
687 
688 
689 void DIALOG_COLOR_PICKER::OnColorValueText( wxCommandEvent& event )
690 {
691  m_newColor4D.SetFromHexString( m_colorValue->GetValue() );
692  m_newColor4D.ToHSV( m_hue, m_sat, m_val, true );
693 
694  SetEditVals( HEX_CHANGED, true );
695  drawAll();
696 }
697 
698 
699 bool DIALOG_COLOR_PICKER::setHSvaluesFromCursor( const wxPoint& aMouseCursor )
700 {
701  wxPoint mousePos = aMouseCursor;
702  wxSize bmsize = m_bitmapHSV->GetSize();
703  int half_size = std::min( bmsize.x, bmsize.y )/2;
704 
705  // Make the cursor position relative to the m_bitmapHSV wxBitmap center
706  mousePos.x -= half_size;
707  mousePos.y -= half_size;
708  mousePos.y = -mousePos.y; // Use the bottom to top vertical axis
709 
710  // The HS cursor position is restricted to a circle of radius half_size
711  double dist_from_centre = hypot( (double)mousePos.x, (double)mousePos.y );
712 
713  if( dist_from_centre > half_size )
714  // Saturation cannot be calculated:
715  return false;
716 
717  m_cursorBitmapHSV = mousePos;
718 
719  // Set saturation and hue from new cursor position:
720  half_size -= m_cursorsSize / 2; // the actual half_size of the palette area
721  m_sat = dist_from_centre / half_size;
722 
723  if( m_sat > 1.0 )
724  m_sat = 1.0;
725 
726  m_hue = atan2( mousePos.y, mousePos.x ) / M_PI * 180.0;
727 
728  if( m_hue < 0 )
729  m_hue += 360.0;
730 
732  SetEditVals( ALL_CHANGED, true );
733 
734  return true;
735 }
736 
737 
738 void DIALOG_COLOR_PICKER::OnChangeAlpha( wxScrollEvent& event )
739 {
740  double alpha = (double)event.GetPosition() / ALPHA_MAX;
741  m_newColor4D.a = alpha;
742  m_NewColorRect->Freeze(); // Avoid flicker
744  m_NewColorRect->Thaw();
745  m_NewColorRect->Refresh();
746  SetEditVals( ALPHA_CHANGED, false );
747 }
748 
749 
750 void DIALOG_COLOR_PICKER::OnChangeEditRed( wxSpinEvent& event )
751 {
752  double val = (double)event.GetPosition() / 255.0;
753  m_newColor4D.r = val;
754  SetEditVals( RED_CHANGED, true );
755 
756  drawAll();
757 }
758 
759 
760 void DIALOG_COLOR_PICKER::OnChangeEditGreen( wxSpinEvent& event )
761 {
762  double val = (double)event.GetPosition() / 255.0;
763  m_newColor4D.g = val;
764  SetEditVals( GREEN_CHANGED, true );
765 
766  drawAll();
767 }
768 
769 
770 void DIALOG_COLOR_PICKER::OnChangeEditBlue( wxSpinEvent& event )
771 {
772  double val = (double)event.GetPosition() / 255.0;
773  m_newColor4D.b = val;
774  SetEditVals( BLUE_CHANGED, true );
775 
776  drawAll();
777 }
778 
779 
780 void DIALOG_COLOR_PICKER::OnChangeEditHue( wxSpinEvent& event )
781 {
782  m_hue = (double)event.GetPosition();
783 
785 
786  SetEditVals( HUE_CHANGED, true );
787 
788  drawAll();
789 }
790 
791 
792 void DIALOG_COLOR_PICKER::OnChangeEditSat( wxSpinEvent& event )
793 {
794  m_sat = (double)event.GetPosition() / 255.0;
795 
797 
798  SetEditVals( SAT_CHANGED, true );
799 
800  drawAll();
801 }
802 
803 
804 void DIALOG_COLOR_PICKER::OnChangeBrightness( wxScrollEvent& event )
805 {
806  m_val = (double)event.GetPosition() / 255.0;
807 
809 
810  SetEditVals( VAL_CHANGED, true );
811 
812  drawAll();
813 }
814 
815 
816 void DIALOG_COLOR_PICKER::OnResetButton( wxCommandEvent& aEvent )
817 {
822 
823  m_newColor4D.ToHSV( m_hue, m_sat, m_val, true );
824  SetEditVals( ALL_CHANGED, false );
825 
826  drawAll();
827 }
double m_hue
the current hue, in degrees (0 ... 360)
bool m_allowOpacityCtrl
true to show the widget, false to keep alpha channel = 1.0
double m_sat
the current saturation (0 ... 1.0)
void ToHSV(double &aOutHue, double &aOutSaturation, double &aOutValue, bool aAlwaysDefineHue=false) const
Convert current color (stored in RGB) to HSV format.
Definition: color4d.cpp:357
void drawRGBPalette()
draws the RVB color space
static wxBitmap MakeBitmap(const KIGFX::COLOR4D &aColor, const KIGFX::COLOR4D &aBackground, const wxSize &aSize, const wxSize &aCheckerboardSize, const KIGFX::COLOR4D &aCheckerboardBackground)
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
void configureSpinCtrl(wxSpinCtrl *aCtrl)
void OnChangeEditGreen(wxSpinEvent &event) override
std::vector< KIGFX::COLOR4D > m_Color4DList
the list of color4d ordered by button ID, for predefined colors
CHANGED_COLOR
int color
Definition: DXF_plotter.cpp:57
void OnChangeAlpha(wxScrollEvent &event) override
Event handlers from wxSpinControl.
int normalizeToInt(double aValue, int aValMax=255)
static const wxSize SWATCH_SIZE_LARGE_DU(24, 16)
void onHSVMouseDrag(wxMouseEvent &event) override
void OnColorValueText(wxCommandEvent &event) override
Event handler for the reset button press.
double g
Green component.
Definition: color4d.h:372
void drawHSVPalette()
draws the HSV color circle
A class to handle a custom color (predefined color) for the color picker dialog.
std::vector< CUSTOM_COLOR_ITEM > CUSTOM_COLORS_LIST
void createHSVBitmap()
generate the bitmap that shows the HSV color circle
void OnChangeBrightness(wxScrollEvent &event) override
Event handler from wxSlider: alpha (transparency) control.
KIGFX::COLOR4D m_newColor4D
the current color4d
bool setHSvaluesFromCursor(const wxPoint &aMouseCursor)
Manage the Hue and Saturation settings when the mouse cursor is at aMouseCursor.
void onHSVMouseClick(wxMouseEvent &event) override
wxPoint m_cursorBitmapHSV
the cursor on the HSV bitmap palette.
double b
Blue component.
Definition: color4d.h:373
Number of colors.
Definition: color4d.h:79
void SetEditVals(CHANGED_COLOR aChanged, bool aCheckTransparency)
double a
Alpha component.
Definition: color4d.h:374
void onRGBMouseClick(wxMouseEvent &event) override
APP_SETTINGS_BASE is a settings class that should be derived for each standalone KiCad application.
Definition: app_settings.h:99
KIGFX::COLOR4D m_previousColor4D
the initial color4d
void createRGBBitmap()
generate the bitmap that shows the RVB color space
#define ALPHA_MAX
#define MAPY(yy)
wxBitmap * m_bitmapHSV
the basic HUV palette
void OnChangeEditHue(wxSpinEvent &event) override
Class DIALOG_COLOR_PICKER_BASE.
std::vector< wxStaticBitmap * > m_colorSwatches
list of defined colors buttons
#define _(s)
void colorDClick(wxMouseEvent &event)
called when creating the dialog
const StructColors * colorRefs()
Global list of legacy color names, still used all over the place for constructing COLOR4D's.
Definition: color4d.cpp:39
void buttColorClick(wxMouseEvent &event)
Event handler for double click on color buttons.
void onRGBMouseDrag(wxMouseEvent &event) override
void OnResetButton(wxCommandEvent &aEvent) override
void FromHSV(double aInH, double aInS, double aInV)
Changes currently used color to the one given by hue, saturation and value parameters.
Definition: color4d.cpp:411
double m_val
the current value (0 ... 1.0)
KIGFX::COLOR4D m_defaultColor
The default color4d.
Definition: color4d.h:48
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
wxPoint m_cursorBitmapRed
the red cursor on the RGB bitmap palette.
wxPoint m_cursorBitmapBlue
the blue cursor on the RGB bitmap palette.
void OnChangeEditBlue(wxSpinEvent &event) override
bool TransferDataToWindow() override
void initDefinedColors(CUSTOM_COLORS_LIST *aPredefinedColors)
Create the bitmap buttons for each defined colors.
static const wxSize CHECKERBOARD_SIZE_DU(3, 3)
void updatePreview(wxStaticBitmap *aStaticBitmap, KIGFX::COLOR4D &aColor4D)
Event handler from wxSlider: brightness (value) control.
KIGFX::COLOR4D m_Color
void OnChangeEditRed(wxSpinEvent &event) override
APP_SETTINGS_BASE * KifaceSettings() const
Definition: kiface_base.h:92
wxBitmap * m_bitmapRGB
the basic RGB palette
DIALOG_COLOR_PICKER(wxWindow *aParent, const KIGFX::COLOR4D &aCurrentColor, bool aAllowOpacityControl, CUSTOM_COLORS_LIST *aUserColors=nullptr, const KIGFX::COLOR4D &aDefaultColor=KIGFX::COLOR4D::UNSPECIFIED)
Dialog constructor.
double r
Red component.
Definition: color4d.h:371
COLOR_PICKER m_ColorPicker
Definition: app_settings.h:173
#define MAPX(xx)
#define SLOPE_AXIS
#define ID_COLOR_BLACK
void OnChangeEditSat(wxSpinEvent &event) override
mouse handlers, when clicking on a palette bitmap
wxPoint m_cursorBitmapGreen
the green cursor on the RGB bitmap palette.
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
wxPoint * m_selectedCursor
the ref cursor to the selected cursor, if any, or null.