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-2024 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,
38 const wxSize& aSize, const wxSize& aCheckerboardSize,
39 const COLOR4D& aCheckerboardBackground )
40{
41 wxBitmap bitmap( aSize );
42 wxMemoryDC iconDC;
43
44 iconDC.SelectObject( bitmap );
45
46 RenderToDC( &iconDC, aColor, aBackground, aSize, aCheckerboardSize, aCheckerboardBackground );
47
48 return bitmap;
49}
50
51
53{
55 ToPhys( m_size ), ToPhys( m_checkerboardSize ),
57
58 bitmap.SetScaleFactor( GetDPIScaleFactor() );
59 return bitmap;
60}
61
62
63void COLOR_SWATCH::RenderToDC( wxDC* aDC, const KIGFX::COLOR4D& aColor,
64 const KIGFX::COLOR4D& aBackground, const wxRect& aRect,
65 const wxSize& aCheckerboardSize,
66 const KIGFX::COLOR4D& aCheckerboardBackground )
67{
68 wxColor fg = aColor.ToColour();
69
70 wxBrush brush;
71 brush.SetStyle( wxBRUSHSTYLE_SOLID );
72
73 aDC->SetPen( *wxTRANSPARENT_PEN );
74
75 // Draw a checkerboard
76 COLOR4D white;
77 COLOR4D black;
78 bool rowCycle;
79
80 if( aColor == COLOR4D::UNSPECIFIED )
81 {
82 if( aCheckerboardBackground.GetBrightness() > 0.4 )
83 {
84 white = COLOR4D::WHITE;
85 black = white.Darkened( 0.15 );
86 rowCycle = true;
87 }
88 else
89 {
90 black = COLOR4D::BLACK;
91 white = black.Brightened( 0.15 );
92 rowCycle = false;
93 }
94 }
95 else
96 {
97 if( aBackground.GetBrightness() > 0.4 )
98 {
99 white = aBackground;
100 black = white.Darkened( 0.15 );
101 rowCycle = true;
102 }
103 else
104 {
105 black = COLOR4D::BLACK;
106 white = black.Brightened( 0.15 );
107 rowCycle = false;
108 }
109 }
110
111 for( int x = aRect.GetLeft(); x <= aRect.GetRight(); x += aCheckerboardSize.x )
112 {
113 bool colCycle = rowCycle;
114
115 for( int y = aRect.GetTop(); y <= aRect.GetBottom(); y += aCheckerboardSize.y )
116 {
117 wxColor bg = colCycle ? black.ToColour() : white.ToColour();
118
119 // Blend fg bg with the checkerboard
120 unsigned char r = wxColor::AlphaBlend( fg.Red(), bg.Red(), aColor.a );
121 unsigned char g = wxColor::AlphaBlend( fg.Green(), bg.Green(), aColor.a );
122 unsigned char b = wxColor::AlphaBlend( fg.Blue(), bg.Blue(), aColor.a );
123
124 brush.SetColour( r, g, b );
125
126 aDC->SetBrush( brush );
127 aDC->DrawRectangle( x, y, aCheckerboardSize.x, aCheckerboardSize.y );
128
129 colCycle = !colCycle;
130 }
131
132 rowCycle = !rowCycle;
133 }
134}
135
136
137COLOR_SWATCH::COLOR_SWATCH( wxWindow* aParent, const COLOR4D& aColor, int aID,
138 const COLOR4D& aBackground, const COLOR4D& aDefault,
139 SWATCH_SIZE aSwatchSize, bool aTriggerWithSingleClick ) :
140 wxPanel( aParent, aID ),
141 m_color( aColor ),
142 m_background( aBackground ),
143 m_default( aDefault ),
144 m_userColors( nullptr ),
145 m_readOnly( false ),
146 m_supportsOpacity( true )
147{
148 wxASSERT_MSG( aSwatchSize != SWATCH_EXPAND, wxS( "SWATCH_EXPAND not supported in COLOR_SWATCH" ) );
149
150 switch( aSwatchSize )
151 {
152 case SWATCH_MEDIUM: m_size = ConvertDialogToPixels( SWATCH_SIZE_MEDIUM_DU ); break;
153 case SWATCH_SMALL: m_size = ConvertDialogToPixels( SWATCH_SIZE_SMALL_DU ); break;
154 case SWATCH_LARGE: m_size = ConvertDialogToPixels( SWATCH_SIZE_LARGE_DU ); break;
155 case SWATCH_EXPAND: m_size = ConvertDialogToPixels( SWATCH_SIZE_LARGE_DU ); break;
156 }
157
158 m_checkerboardSize = ConvertDialogToPixels( CHECKERBOARD_SIZE_DU );
159 m_checkerboardBg = aParent->GetBackgroundColour();
160
161 auto sizer = new wxBoxSizer( wxHORIZONTAL );
162 SetSizer( sizer );
163
164 m_swatch = new wxStaticBitmap( this, aID, makeBitmap() );
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#ifdef __WXMAC__
188 // Adjust for border
189 m_size.x -= 2;
190 m_size.y -= 2;
191#endif
192
193 SetSize( m_size );
194
195 auto sizer = new wxBoxSizer( wxHORIZONTAL );
196 SetSizer( sizer );
197
198 m_swatch = new wxStaticBitmap( this, aID, makeBitmap() );
199
200 sizer->Add( m_swatch, 0, 0 );
201
202 setupEvents( false );
203}
204
205
206void COLOR_SWATCH::setupEvents( bool aTriggerWithSingleClick )
207{
208 if( dynamic_cast<DIALOG_SHIM*>( wxGetTopLevelParent( this ) ) )
209 {
210 m_swatch->Bind( wxEVT_LEFT_DOWN,
211 [this] ( wxMouseEvent& aEvt )
212 {
214 } );
215 }
216 else
217 {
218 // forward click to any other listeners, since we don't want them
219 m_swatch->Bind( wxEVT_LEFT_DOWN, &COLOR_SWATCH::rePostEvent, this );
220
221 // bind the events that trigger the dialog
222 m_swatch->Bind( wxEVT_LEFT_DCLICK,
223 [this] ( wxMouseEvent& aEvt )
224 {
226 } );
227
228 if( aTriggerWithSingleClick )
229 {
230 m_swatch->Bind( wxEVT_LEFT_UP,
231 [this] ( wxMouseEvent& aEvt )
232 {
234 } );
235 }
236 }
237
238 m_swatch->Bind( wxEVT_MIDDLE_DOWN,
239 [this] ( wxMouseEvent& aEvt )
240 {
242 } );
243
244 m_swatch->Bind( wxEVT_RIGHT_DOWN, &COLOR_SWATCH::rePostEvent, this );
245}
246
247
248void COLOR_SWATCH::rePostEvent( wxEvent& aEvent )
249{
250 wxPostEvent( this, aEvent );
251}
252
253
254static void sendSwatchChangeEvent( COLOR_SWATCH& aSender )
255{
256 wxCommandEvent changeEvt( COLOR_SWATCH_CHANGED, aSender.GetId() );
257
258 // use this class as the object (alternative might be to
259 // set a custom event class but that's more work)
260 changeEvt.SetEventObject( &aSender );
261
262 wxPostEvent( &aSender, changeEvt );
263}
264
265
266void COLOR_SWATCH::SetSwatchColor( const COLOR4D& aColor, bool aSendEvent )
267{
268 m_color = aColor;
269
270 m_swatch->SetBitmap( makeBitmap() );
271
272 if( aSendEvent )
273 sendSwatchChangeEvent( *this );
274}
275
276
278{
279 m_default = aColor;
280}
281
282
284{
285 m_background = aBackground;
286
287 m_swatch->SetBitmap( makeBitmap() );
288}
289
290
292{
293 return m_color;
294}
295
296
298{
299 if( m_readOnly )
300 {
303
304 return;
305 }
306
307 DIALOG_COLOR_PICKER dialog( ::wxGetTopLevelParent( this ), m_color, m_supportsOpacity,
309
310 if( dialog.ShowModal() == wxID_OK )
311 {
312 COLOR4D newColor = dialog.GetColor();
313
314 if( newColor != COLOR4D::UNSPECIFIED || m_default == COLOR4D::UNSPECIFIED )
315 {
316 m_color = newColor;
317
318 m_swatch->SetBitmap( makeBitmap() );
319
320 sendSwatchChangeEvent( *this );
321 }
322 }
323}
324
325
327{
328 m_checkerboardBg = m_parent->GetBackgroundColour();
329
330 m_swatch->SetBitmap( makeBitmap() );
331}
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:157
KIGFX::COLOR4D m_checkerboardBg
Definition: color_swatch.h:158
KIGFX::COLOR4D m_default
Definition: color_swatch.h:151
bool m_supportsOpacity
If opacity is not supported the color chooser dialog will be displayed without it.
Definition: color_swatch.h:165
wxStaticBitmap * m_swatch
Definition: color_swatch.h:154
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:161
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
Definition: color_swatch.h:149
KIGFX::COLOR4D m_background
Definition: color_swatch.h:150
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:162
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:152
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:88
int ShowModal() override
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:395
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