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 (C) 2017-2023 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
25#include <wx/dcmemory.h>
26
27#include <dpi_scaling_common.h>
29#include <memory>
30
31wxDEFINE_EVENT( COLOR_SWATCH_CHANGED, wxCommandEvent );
32
33using KIGFX::COLOR4D;
34
35
36wxBitmap COLOR_SWATCH::MakeBitmap( const COLOR4D& aColor, const COLOR4D& aBackground,
37 const wxSize& aSize, const wxSize& aCheckerboardSize,
38 const COLOR4D& aCheckerboardBackground )
39{
40 wxBitmap bitmap( aSize );
41 wxMemoryDC iconDC;
42
43 iconDC.SelectObject( bitmap );
44
45 RenderToDC( &iconDC, aColor, aBackground, aSize, aCheckerboardSize, aCheckerboardBackground );
46
47 return bitmap;
48}
49
50
51void COLOR_SWATCH::RenderToDC( wxDC* aDC, const KIGFX::COLOR4D& aColor,
52 const KIGFX::COLOR4D& aBackground, const wxRect& aRect,
53 const wxSize& aCheckerboardSize,
54 const KIGFX::COLOR4D& aCheckerboardBackground )
55{
56 wxColor fg = aColor.ToColour();
57
58 wxBrush brush;
59 brush.SetStyle( wxBRUSHSTYLE_SOLID );
60
61 aDC->SetPen( *wxTRANSPARENT_PEN );
62
63 // Draw a checkerboard
64 COLOR4D white;
65 COLOR4D black;
66 bool rowCycle;
67
68 if( aColor == COLOR4D::UNSPECIFIED )
69 {
70 if( aCheckerboardBackground.GetBrightness() > 0.4 )
71 {
72 white = COLOR4D::WHITE;
73 black = white.Darkened( 0.15 );
74 rowCycle = true;
75 }
76 else
77 {
78 black = COLOR4D::BLACK;
79 white = black.Brightened( 0.15 );
80 rowCycle = false;
81 }
82 }
83 else
84 {
85 if( aBackground.GetBrightness() > 0.4 )
86 {
87 white = aBackground;
88 black = white.Darkened( 0.15 );
89 rowCycle = true;
90 }
91 else
92 {
93 black = COLOR4D::BLACK;
94 white = black.Brightened( 0.15 );
95 rowCycle = false;
96 }
97 }
98
99 for( int x = aRect.GetLeft(); x <= aRect.GetRight(); x += aCheckerboardSize.x )
100 {
101 bool colCycle = rowCycle;
102
103 for( int y = aRect.GetTop(); y <= aRect.GetBottom(); y += aCheckerboardSize.y )
104 {
105 wxColor bg = colCycle ? black.ToColour() : white.ToColour();
106
107 // Blend fg bg with the checkerboard
108 unsigned char r = wxColor::AlphaBlend( fg.Red(), bg.Red(), aColor.a );
109 unsigned char g = wxColor::AlphaBlend( fg.Green(), bg.Green(), aColor.a );
110 unsigned char b = wxColor::AlphaBlend( fg.Blue(), bg.Blue(), aColor.a );
111
112 brush.SetColour( r, g, b );
113
114 aDC->SetBrush( brush );
115 aDC->DrawRectangle( x, y, aCheckerboardSize.x, aCheckerboardSize.y );
116
117 colCycle = !colCycle;
118 }
119
120 rowCycle = !rowCycle;
121 }
122}
123
124
125COLOR_SWATCH::COLOR_SWATCH( wxWindow* aParent, const COLOR4D& aColor, int aID,
126 const COLOR4D& aBackground, const COLOR4D& aDefault,
127 SWATCH_SIZE aSwatchSize, bool aTriggerWithSingleClick ) :
128 wxPanel( aParent, aID ),
129 m_color( aColor ),
130 m_background( aBackground ),
131 m_default( aDefault ),
132 m_userColors( nullptr ),
133 m_readOnly( false ),
134 m_supportsOpacity( true )
135{
136 wxASSERT_MSG( aSwatchSize != SWATCH_EXPAND, wxS( "SWATCH_EXPAND not supported in COLOR_SWATCH" ) );
137
138 switch( aSwatchSize )
139 {
140 case SWATCH_MEDIUM: m_size = ConvertDialogToPixels( SWATCH_SIZE_MEDIUM_DU ); break;
141 case SWATCH_SMALL: m_size = ConvertDialogToPixels( SWATCH_SIZE_SMALL_DU ); break;
142 case SWATCH_LARGE: m_size = ConvertDialogToPixels( SWATCH_SIZE_LARGE_DU ); break;
143 case SWATCH_EXPAND: m_size = ConvertDialogToPixels( SWATCH_SIZE_LARGE_DU ); break;
144 }
145
146 m_checkerboardSize = ConvertDialogToPixels( CHECKERBOARD_SIZE_DU );
147 m_checkerboardBg = aParent->GetBackgroundColour();
148
149#ifdef __WXMSW__
150 // These need additional scaling on Windows because of some discrepancy between pixel and
151 // content scaling that only affects certain widgets on Windows HiDPI. On other platforms, the
152 // value returned by ConvertDialogToPixels appears to be correct.
153 DPI_SCALING_COMMON dpi( nullptr, aParent );
154
157#endif
158
159 auto sizer = new wxBoxSizer( wxHORIZONTAL );
160 SetSizer( sizer );
161
162 wxBitmap bitmap = COLOR_SWATCH::MakeBitmap( aColor, aBackground, m_size,
164 m_swatch = new wxStaticBitmap( this, aID, bitmap );
165
166 sizer->Add( m_swatch, 0, 0 );
167
168 setupEvents( aTriggerWithSingleClick );
169}
170
171
172COLOR_SWATCH::COLOR_SWATCH( wxWindow* aParent, wxWindowID aID, const wxPoint& aPos,
173 const wxSize& aSize, long aStyle ) :
174 wxPanel( aParent, aID, aPos, aSize, aStyle ),
175 m_userColors( nullptr ),
176 m_readOnly( false ),
177 m_supportsOpacity( true )
178{
179 if( aSize == wxDefaultSize )
180 m_size = ConvertDialogToPixels( SWATCH_SIZE_MEDIUM_DU );
181 else
182 m_size = aSize;
183
184 m_checkerboardSize = ConvertDialogToPixels( CHECKERBOARD_SIZE_DU );
185 m_checkerboardBg = aParent->GetBackgroundColour();
186
187 SetSize( m_size );
188
189 auto sizer = new wxBoxSizer( wxHORIZONTAL );
190 SetSizer( sizer );
191
192 wxBitmap bitmap = COLOR_SWATCH::MakeBitmap( COLOR4D::UNSPECIFIED, COLOR4D::UNSPECIFIED,
194 m_swatch = new wxStaticBitmap( this, aID, bitmap );
195
196 sizer->Add( m_swatch, 0, 0 );
197
198 setupEvents( false );
199}
200
201
202void COLOR_SWATCH::setupEvents( bool aTriggerWithSingleClick )
203{
204 wxWindow* topLevelParent = wxGetTopLevelParent( GetParent() );
205
206 if( topLevelParent && dynamic_cast<DIALOG_SHIM*>( topLevelParent ) )
207 {
208 m_swatch->Bind( wxEVT_LEFT_DOWN,
209 [this] ( wxMouseEvent& aEvt )
210 {
212 } );
213 }
214 else
215 {
216 // forward click to any other listeners, since we don't want them
217 m_swatch->Bind( wxEVT_LEFT_DOWN, &COLOR_SWATCH::rePostEvent, this );
218
219 // bind the events that trigger the dialog
220 m_swatch->Bind( wxEVT_LEFT_DCLICK,
221 [this] ( wxMouseEvent& aEvt )
222 {
224 } );
225
226 if( aTriggerWithSingleClick )
227 {
228 m_swatch->Bind( wxEVT_LEFT_UP,
229 [this] ( wxMouseEvent& aEvt )
230 {
232 } );
233 }
234 }
235
236 m_swatch->Bind( wxEVT_MIDDLE_DOWN,
237 [this] ( wxMouseEvent& aEvt )
238 {
240 } );
241
242 m_swatch->Bind( wxEVT_RIGHT_DOWN, &COLOR_SWATCH::rePostEvent, this );
243}
244
245
246void COLOR_SWATCH::rePostEvent( wxEvent& aEvent )
247{
248 wxPostEvent( this, aEvent );
249}
250
251
252static void sendSwatchChangeEvent( COLOR_SWATCH& aSender )
253{
254 wxCommandEvent changeEvt( COLOR_SWATCH_CHANGED, aSender.GetId() );
255
256 // use this class as the object (alternative might be to
257 // set a custom event class but that's more work)
258 changeEvt.SetEventObject( &aSender );
259
260 wxPostEvent( &aSender, changeEvt );
261}
262
263
264void COLOR_SWATCH::SetSwatchColor( const COLOR4D& aColor, bool aSendEvent )
265{
266 m_color = aColor;
267
269 m_swatch->SetBitmap( bm );
270
271 if( aSendEvent )
272 sendSwatchChangeEvent( *this );
273}
274
275
277{
278 m_default = aColor;
279}
280
281
283{
284 m_background = aBackground;
286 m_swatch->SetBitmap( bm );
287}
288
289
291{
292 return m_color;
293}
294
295
297{
298 if( m_readOnly )
299 {
302
303 return;
304 }
305
306 DIALOG_COLOR_PICKER dialog( ::wxGetTopLevelParent( this ), m_color, m_supportsOpacity,
308
309 if( dialog.ShowModal() == wxID_OK )
310 {
311 COLOR4D newColor = dialog.GetColor();
312
313 if( newColor != COLOR4D::UNSPECIFIED || m_default == COLOR4D::UNSPECIFIED )
314 {
315 m_color = newColor;
316
317 wxBitmap bm = MakeBitmap( newColor, m_background, m_size, m_checkerboardSize,
319 m_swatch->SetBitmap( bm );
320
321 sendSwatchChangeEvent( *this );
322 }
323 }
324}
325
326
328{
329 m_checkerboardBg = m_parent->GetBackgroundColour();
331 m_swatch->SetBitmap( bm );
332}
A simple color swatch of the kind used to set layer colors.
Definition: color_swatch.h:57
void SetSwatchColor(const KIGFX::COLOR4D &aColor, bool aSendEvent)
Set the current swatch color directly.
wxSize m_checkerboardSize
Definition: color_swatch.h:155
KIGFX::COLOR4D m_checkerboardBg
Definition: color_swatch.h:156
KIGFX::COLOR4D m_default
Definition: color_swatch.h:149
bool m_supportsOpacity
If opacity is not supported the color chooser dialog will be displayed without it.
Definition: color_swatch.h:163
wxStaticBitmap * m_swatch
Definition: color_swatch.h:152
void OnDarkModeToggle()
Respond to a change in the OS's DarkMode setting.
void GetNewSwatchColor()
Prompt for a new colour, using the colour picker dialog.
bool m_readOnly
A read-only swatch won't show the color chooser dialog but otherwise works normally.
Definition: color_swatch.h:159
KIGFX::COLOR4D GetSwatchColor() const
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
Definition: color_swatch.h:147
KIGFX::COLOR4D m_background
Definition: color_swatch.h:148
static void RenderToDC(wxDC *aDC, const KIGFX::COLOR4D &aColor, const KIGFX::COLOR4D &aBackground, const wxRect &aRect, const wxSize &aCheckerboardSize, const KIGFX::COLOR4D &aCheckerboardBackground)
void setupEvents(bool aTriggerWithSingleClick)
std::function< void()> m_readOnlyCallback
Definition: color_swatch.h:160
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
Definition: color_swatch.h:150
void rePostEvent(wxEvent &aEvent)
Pass unwanted events on to listeners of this object.
static wxBitmap MakeBitmap(const KIGFX::COLOR4D &aColor, const KIGFX::COLOR4D &aBackground, const wxSize &aSize, const wxSize &aCheckerboardSize, const KIGFX::COLOR4D &aCheckerboardBackground)
KIGFX::COLOR4D GetColor()
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:83
Class to handle configuration and automatic determination of the DPI scale to use for canvases.
double GetContentScaleFactor() const override
Get the content scale factor, which may be different from the scale factor on some platforms.
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
COLOR4D Darkened(double aFactor) const
Return a color that is darker by a given factor, without modifying object.
Definition: color4d.h:282
double GetBrightness() const
Returns the brightness value of the color ranged from 0.0 to 1.0.
Definition: color4d.h:333
double a
Alpha component.
Definition: color4d.h:379
COLOR4D Brightened(double aFactor) const
Return a color that is brighter by a given factor, without modifying object.
Definition: color4d.h:268
wxColour ToColour() const
Definition: color4d.cpp:220
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
Definition: color_swatch.h:39
@ SWATCH_MEDIUM
Definition: color_swatch.h:41
@ SWATCH_LARGE
Definition: color_swatch.h:42
@ SWATCH_EXPAND
Definition: color_swatch.h:43
@ SWATCH_SMALL
Definition: color_swatch.h:40