KiCad PCB EDA Suite
Loading...
Searching...
No Matches
color_swatch.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <kiplatform/ui.h>
26#include <wx/dcmemory.h>
27
28#include <dpi_scaling_common.h>
30#include <memory>
31
32wxDEFINE_EVENT( COLOR_SWATCH_CHANGED, wxCommandEvent );
33
34using KIGFX::COLOR4D;
35
36
37wxBitmap COLOR_SWATCH::MakeBitmap( const COLOR4D& aColor, const COLOR4D& aBackground, const wxSize& aSize,
38 const wxSize& aCheckerboardSize, const COLOR4D& aCheckerboardBackground,
39 const std::vector<int>& aMargins )
40{
41 wxBitmap bitmap( aSize );
42 wxMemoryDC iconDC;
43
44 iconDC.SelectObject( bitmap );
45
46 RenderToDC( &iconDC, aColor, aBackground, aSize, aCheckerboardSize, aCheckerboardBackground, aMargins );
47
48 return bitmap;
49}
50
51
53{
54 wxBitmap bitmap = COLOR_SWATCH::MakeBitmap( m_color, m_background, ToPhys( m_size ),
56
57 bitmap.SetScaleFactor( GetDPIScaleFactor() );
58 return bitmap;
59}
60
61
62void COLOR_SWATCH::RenderToDC( wxDC* aDC, const KIGFX::COLOR4D& aColor, const KIGFX::COLOR4D& aBackground,
63 const wxRect& aRect, const wxSize& aCheckerboardSize,
64 const KIGFX::COLOR4D& aCheckerboardBackground, const std::vector<int>& aMargins )
65{
66 wxColor fg = aColor.m_text.has_value() ? COLOR4D::UNSPECIFIED.ToColour() : aColor.ToColour();
67
68 wxBrush brush;
69 brush.SetStyle( wxBRUSHSTYLE_SOLID );
70
71 aDC->SetPen( *wxTRANSPARENT_PEN );
72
73 // Draw a checkerboard
74 COLOR4D white;
75 COLOR4D black;
76 bool rowCycle;
77
78 if( aColor.m_text.has_value() || aColor == COLOR4D::UNSPECIFIED )
79 {
80 if( aCheckerboardBackground.GetBrightness() > 0.4 )
81 {
82 white = COLOR4D::WHITE;
83 black = white.Darkened( 0.15 );
84 rowCycle = true;
85 }
86 else
87 {
88 black = COLOR4D::BLACK;
89 white = black.Brightened( 0.15 );
90 rowCycle = false;
91 }
92 }
93 else
94 {
95 if( aBackground.GetBrightness() > 0.4 )
96 {
97 white = aBackground;
98 black = white.Darkened( 0.15 );
99 rowCycle = true;
100 }
101 else
102 {
103 black = COLOR4D::BLACK;
104 white = black.Brightened( 0.15 );
105 rowCycle = false;
106 }
107 }
108
109 for( int x = aRect.GetLeft(); x <= aRect.GetRight(); x += aCheckerboardSize.x )
110 {
111 bool colCycle = rowCycle;
112
113 for( int y = aRect.GetTop(); y <= aRect.GetBottom(); y += aCheckerboardSize.y )
114 {
115 wxColor bg = colCycle ? black.ToColour() : white.ToColour();
116
117 // Blend fg bg with the checkerboard
118 unsigned char r = wxColor::AlphaBlend( fg.Red(), bg.Red(), fg.Alpha() );
119 unsigned char g = wxColor::AlphaBlend( fg.Green(), bg.Green(), fg.Alpha() );
120 unsigned char b = wxColor::AlphaBlend( fg.Blue(), bg.Blue(), fg.Alpha() );
121
122 brush.SetColour( r, g, b );
123
124 aDC->SetBrush( brush );
125 aDC->DrawRectangle( x, y, aCheckerboardSize.x, aCheckerboardSize.y );
126
127 colCycle = !colCycle;
128 }
129
130 rowCycle = !rowCycle;
131 }
132
133 aDC->SetBrush( *wxWHITE_BRUSH );
134
135 if( aMargins[0] )
136 aDC->DrawRectangle( 0, 0, aMargins[0], aRect.GetHeight() );
137
138 if( aMargins[1] )
139 aDC->DrawRectangle( 0, 0, aRect.GetWidth(), aMargins[1] );
140
141 if( aMargins[2] )
142 aDC->DrawRectangle( aRect.GetWidth() - aMargins[2], 0, aMargins[2], aRect.GetHeight() );
143
144 if( aMargins[3] )
145 aDC->DrawRectangle( 0, aRect.GetHeight() - aMargins[3], aRect.GetWidth(), aMargins[3] );
146}
147
148
149COLOR_SWATCH::COLOR_SWATCH( wxWindow* aParent, const COLOR4D& aColor, int aID, const COLOR4D& aBackground,
150 const COLOR4D& aDefault, SWATCH_SIZE aSwatchSize, bool aTriggerWithSingleClick ) :
151 wxPanel( aParent, aID ),
152 m_color( aColor ),
153 m_background( aBackground ),
154 m_default( aDefault ),
155 m_userColors( nullptr ),
156 m_readOnly( false ),
157 m_supportsOpacity( true )
158{
159 wxASSERT_MSG( aSwatchSize != SWATCH_EXPAND,
160 wxS( "SWATCH_EXPAND not supported in COLOR_SWATCH" ) );
161
162 switch( aSwatchSize )
163 {
164 case SWATCH_MEDIUM: m_size = ConvertDialogToPixels( SWATCH_SIZE_MEDIUM_DU ); break;
165 case SWATCH_SMALL: m_size = ConvertDialogToPixels( SWATCH_SIZE_SMALL_DU ); break;
166 case SWATCH_LARGE: m_size = ConvertDialogToPixels( SWATCH_SIZE_LARGE_DU ); break;
167 case SWATCH_EXPAND: m_size = ConvertDialogToPixels( SWATCH_SIZE_LARGE_DU ); break;
168 }
169
170 m_checkerboardSize = ConvertDialogToPixels( CHECKERBOARD_SIZE_DU );
171 m_checkerboardBg = aParent->GetBackgroundColour();
172
173 auto sizer = new wxBoxSizer( wxHORIZONTAL );
174 SetSizer( sizer );
175
176 m_swatch = new wxStaticBitmap( this, aID, makeBitmap() );
177
178 sizer->Add( m_swatch, 0, 0 );
179
180 setupEvents( aTriggerWithSingleClick );
181}
182
183
184COLOR_SWATCH::COLOR_SWATCH( wxWindow* aParent, wxWindowID aID, const wxPoint& aPos, const wxSize& aSize,
185 long aStyle ) :
186 wxPanel( aParent, aID, aPos, aSize, aStyle ),
187 m_userColors( nullptr ),
188 m_readOnly( false ),
189 m_supportsOpacity( true )
190{
191 if( aSize == wxDefaultSize )
192 m_size = ConvertDialogToPixels( SWATCH_SIZE_MEDIUM_DU );
193 else
194 m_size = aSize;
195
196 m_checkerboardSize = ConvertDialogToPixels( CHECKERBOARD_SIZE_DU );
197 m_checkerboardBg = aParent->GetBackgroundColour();
198
199#ifdef __WXMAC__
200 // Adjust for border
201 m_size.x -= 2;
202 m_size.y -= 2;
203#endif
204
205 SetSize( m_size );
206
207 auto sizer = new wxBoxSizer( wxHORIZONTAL );
208 SetSizer( sizer );
209
210 m_swatch = new wxStaticBitmap( this, aID, makeBitmap() );
211
212 sizer->Add( m_swatch, 0, 0 );
213
214 setupEvents( false );
215}
216
217
218void COLOR_SWATCH::setupEvents( bool aTriggerWithSingleClick )
219{
220 if( dynamic_cast<DIALOG_SHIM*>( wxGetTopLevelParent( this ) ) )
221 {
222 m_swatch->Bind( wxEVT_LEFT_DOWN,
223 [this] ( wxMouseEvent& aEvt )
224 {
226 } );
227 }
228 else
229 {
230 // forward click to any other listeners, since we don't want them
231 m_swatch->Bind( wxEVT_LEFT_DOWN, &COLOR_SWATCH::rePostEvent, this );
232
233 // bind the events that trigger the dialog
234 m_swatch->Bind( wxEVT_LEFT_DCLICK,
235 [this] ( wxMouseEvent& aEvt )
236 {
238 } );
239
240 if( aTriggerWithSingleClick )
241 {
242 m_swatch->Bind( wxEVT_LEFT_UP,
243 [this] ( wxMouseEvent& aEvt )
244 {
246 } );
247 }
248 }
249
250 m_swatch->Bind( wxEVT_MIDDLE_DOWN,
251 [this] ( wxMouseEvent& aEvt )
252 {
254 } );
255
256 m_swatch->Bind( wxEVT_RIGHT_DOWN, &COLOR_SWATCH::rePostEvent, this );
257}
258
259
260void COLOR_SWATCH::rePostEvent( wxEvent& aEvent )
261{
262 wxPostEvent( this, aEvent );
263}
264
265
266static void sendSwatchChangeEvent( COLOR_SWATCH& aSender )
267{
268 wxCommandEvent changeEvt( COLOR_SWATCH_CHANGED, aSender.GetId() );
269
270 // use this class as the object (alternative might be to
271 // set a custom event class but that's more work)
272 changeEvt.SetEventObject( &aSender );
273
274 wxPostEvent( &aSender, changeEvt );
275}
276
277
278void COLOR_SWATCH::SetSwatchColor( const COLOR4D& aColor, bool aSendEvent )
279{
280 m_color = aColor;
281
282 m_swatch->SetBitmap( makeBitmap() );
283
284 if( aSendEvent )
285 sendSwatchChangeEvent( *this );
286}
287
288
290{
291 m_default = aColor;
292}
293
294
296{
297 m_background = aBackground;
298
299 m_swatch->SetBitmap( makeBitmap() );
300}
301
302
304{
305 return m_color;
306}
307
308
310{
311 if( m_readOnly )
312 {
315
316 return;
317 }
318
319 DIALOG_COLOR_PICKER dialog( ::wxGetTopLevelParent( this ), m_color, m_supportsOpacity, m_userColors, m_default );
320
321 if( dialog.ShowModal() == wxID_OK )
322 {
323 COLOR4D newColor = dialog.GetColor();
324
326 {
327 m_color = newColor;
328
329 m_swatch->SetBitmap( makeBitmap() );
330
331 sendSwatchChangeEvent( *this );
332 }
333 }
334}
335
336
338{
339 wxWindow* parent = GetParent();
340 m_checkerboardBg = parent ? parent->GetBackgroundColour() : wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
341
342 if( m_swatch )
343 m_swatch->SetBitmap( makeBitmap() );
344}
static const COLOR4D WHITE
Definition color4d.h:405
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:402
static const COLOR4D BLACK
Definition color4d.h:406
A simple color swatch of the kind used to set layer colors.
void SetSwatchColor(const KIGFX::COLOR4D &aColor, bool aSendEvent)
Set the current swatch color directly.
wxSize m_checkerboardSize
KIGFX::COLOR4D m_checkerboardBg
KIGFX::COLOR4D m_default
bool m_supportsOpacity
If opacity is not supported the color chooser dialog will be displayed without it.
wxStaticBitmap * m_swatch
static void RenderToDC(wxDC *aDC, const KIGFX::COLOR4D &aColor, const KIGFX::COLOR4D &aBackground, const wxRect &aRect, const wxSize &aCheckerboardSize, const KIGFX::COLOR4D &aCheckerboardBackground, const std::vector< int > &aMargins={ 0, 0, 0, 0 })
void OnDarkModeToggle()
Respond to a change in the OS's DarkMode setting.
void GetNewSwatchColor()
Prompt for a new colour, using the colour picker dialog.
static wxBitmap MakeBitmap(const KIGFX::COLOR4D &aColor, const KIGFX::COLOR4D &aBackground, const wxSize &aSize, const wxSize &aCheckerboardSize, const KIGFX::COLOR4D &aCheckerboardBackground, const std::vector< int > &aMargins={ 0, 0, 0, 0 })
bool m_readOnly
A read-only swatch won't show the color chooser dialog but otherwise works normally.
KIGFX::COLOR4D GetSwatchColor() const
wxBitmap makeBitmap()
COLOR_SWATCH(wxWindow *aParent, const KIGFX::COLOR4D &aColor, int aID, const KIGFX::COLOR4D &aBackground, const KIGFX::COLOR4D &aDefault, SWATCH_SIZE aSwatchType, bool aTriggerWithSingleClick=false)
Construct a COLOR_SWATCH.
KIGFX::COLOR4D m_color
KIGFX::COLOR4D m_background
void setupEvents(bool aTriggerWithSingleClick)
std::function< void()> m_readOnlyCallback
void SetDefaultColor(const KIGFX::COLOR4D &aColor)
Sets the color that will be chosen with the "Reset to Default" button in the chooser.
void SetSwatchBackground(const KIGFX::COLOR4D &aBackground)
Set the swatch background color.
CUSTOM_COLORS_LIST * m_userColors
void rePostEvent(wxEvent &aEvent)
Pass unwanted events on to listeners of this object.
KIGFX::COLOR4D GetColor()
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition dialog_shim.h:68
int ShowModal() override
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
std::optional< wxString > m_text
Definition color4d.h:399
COLOR4D Darkened(double aFactor) const
Return a color that is darker by a given factor, without modifying object.
Definition color4d.h:283
double GetBrightness() const
Returns the brightness value of the color ranged from 0.0 to 1.0.
Definition color4d.h:334
COLOR4D Brightened(double aFactor) const
Return a color that is brighter by a given factor, without modifying object.
Definition color4d.h:269
wxColour ToColour() const
Definition color4d.cpp:225
wxDEFINE_EVENT(COLOR_SWATCH_CHANGED, wxCommandEvent)
static void sendSwatchChangeEvent(COLOR_SWATCH &aSender)
static const wxSize SWATCH_SIZE_LARGE_DU(24, 16)
static const wxSize SWATCH_SIZE_MEDIUM_DU(24, 10)
static const wxSize CHECKERBOARD_SIZE_DU(3, 3)
static const wxSize SWATCH_SIZE_SMALL_DU(8, 6)
SWATCH_SIZE
@ SWATCH_MEDIUM
@ SWATCH_LARGE
@ SWATCH_EXPAND
@ SWATCH_SMALL