KiCad PCB EDA Suite
Loading...
Searching...
No Matches
bitmap_button.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) 2020 Ian McInerney <ian.s.mcinerney at ieee dot org>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <kiplatform/ui.h>
22#include <pgm_base.h>
25#include <wx/button.h>
26#include <wx/dcclient.h>
27#include <wx/renderer.h>
28#include <wx/settings.h>
29
30#define wxCONTROL_SEPARATOR wxCONTROL_SPECIAL
31
32
33BITMAP_BUTTON::BITMAP_BUTTON( wxWindow* aParent, wxWindowID aId, const wxPoint& aPos,
34 const wxSize& aSize, int aStyles ) :
35 wxPanel( aParent, aId, aPos, aSize, aStyles ),
36 m_isRadioButton( false ),
37 m_showBadge( false ),
38 m_badgeColor( wxColor( 210, 0, 0 ) ), // dark red
39 m_badgeTextColor( wxColor( wxT( "white" ) ) ),
40 m_buttonState( 0 ),
41 m_padding( 0 ),
42 m_isToolbarButton( false ),
44 m_centerBitmap( true )
45{
46 m_badgeFont = GetFont().Smaller().MakeBold();
47
49}
50
51
52BITMAP_BUTTON::BITMAP_BUTTON( wxWindow* aParent, wxWindowID aId, const wxBitmap& aDummyBitmap,
53 const wxPoint& aPos, const wxSize& aSize, int aStyles ) :
54 wxPanel( aParent, aId, aPos, aSize, aStyles ),
55 m_isRadioButton( false ),
56 m_showBadge( false ),
57 m_badgeColor( wxColor( 210, 0, 0 ) ), // dark red
58 m_badgeTextColor( wxColor( wxT( "white" ) ) ),
59 m_buttonState( 0 ),
60 m_padding( 5 ),
61 m_isToolbarButton( false ),
63 m_centerBitmap( true )
64{
65 m_badgeFont = GetFont().Smaller().MakeBold();
66
68}
69
70
72{
73 Bind( wxEVT_DPI_CHANGED, &BITMAP_BUTTON::OnDPIChanged, this );
74 Bind( wxEVT_PAINT, &BITMAP_BUTTON::OnPaint, this );
75 Bind( wxEVT_LEFT_UP, &BITMAP_BUTTON::OnLeftButtonUp, this );
76 Bind( wxEVT_LEFT_DOWN, &BITMAP_BUTTON::OnLeftButtonDown, this );
77 Bind( wxEVT_LEAVE_WINDOW, &BITMAP_BUTTON::OnMouseLeave, this );
78 Bind( wxEVT_ENTER_WINDOW, &BITMAP_BUTTON::OnMouseEnter, this );
79 Bind( wxEVT_KILL_FOCUS, &BITMAP_BUTTON::OnKillFocus, this );
80 Bind( wxEVT_SET_FOCUS, &BITMAP_BUTTON::OnSetFocus, this );
81}
82
83
85{
86 Unbind( wxEVT_DPI_CHANGED, &BITMAP_BUTTON::OnDPIChanged, this );
87 Unbind( wxEVT_PAINT, &BITMAP_BUTTON::OnPaint, this );
88 Unbind( wxEVT_LEFT_UP, &BITMAP_BUTTON::OnLeftButtonUp, this );
89 Unbind( wxEVT_LEFT_DOWN, &BITMAP_BUTTON::OnLeftButtonDown, this );
90 Unbind( wxEVT_LEAVE_WINDOW, &BITMAP_BUTTON::OnMouseLeave, this );
91 Unbind( wxEVT_ENTER_WINDOW, &BITMAP_BUTTON::OnMouseEnter, this );
92 Unbind( wxEVT_KILL_FOCUS, &BITMAP_BUTTON::OnKillFocus, this );
93 Unbind( wxEVT_SET_FOCUS, &BITMAP_BUTTON::OnSetFocus, this );
94}
95
96
98{
100 return wxSize( FromDIP( m_dipSize.x + m_padding * 2 ), wxButton::GetDefaultSize().y );
101
102 return FromDIP( m_dipSize + wxSize( m_padding * 2, m_padding * 2 ) );
103}
104
105
107{
108 // Uncomment to override wxFB sizes
109 // SetMinSize( DoGetBestSize() );
110
111 InvalidateBestSize();
112}
113
114
115void BITMAP_BUTTON::SetPadding( int aPaddingDIP )
116{
117 m_padding = aPaddingDIP;
118
120}
121
122
123void BITMAP_BUTTON::SetBitmap( const wxBitmapBundle& aBmp )
124{
125 m_normalBitmap = aBmp;
126 m_dipSize = m_normalBitmap.GetDefaultSize();
127
129}
130
131
132void BITMAP_BUTTON::SetDisabledBitmap( const wxBitmapBundle& aBmp )
133{
134 m_disabledBitmap = aBmp;
135}
136
137
138void BITMAP_BUTTON::AcceptDragInAsClick( bool aAcceptDragIn )
139{
140 m_acceptDraggedInClicks = aAcceptDragIn;
141}
142
143
144void BITMAP_BUTTON::OnMouseLeave( wxEvent& aEvent )
145{
146 if( hasFlag( wxCONTROL_CURRENT | wxCONTROL_PRESSED ) )
147 {
148 clearFlag( wxCONTROL_CURRENT | wxCONTROL_PRESSED );
149 Refresh();
150 }
151
152 aEvent.Skip();
153}
154
155
156void BITMAP_BUTTON::OnMouseEnter( wxEvent& aEvent )
157{
158 if( !hasFlag( wxCONTROL_CURRENT ) )
159 {
160 setFlag( wxCONTROL_CURRENT );
161 Refresh();
162 }
163
164 aEvent.Skip();
165}
166
167
168void BITMAP_BUTTON::OnKillFocus( wxEvent& aEvent )
169{
170 if( hasFlag( wxCONTROL_FOCUSED | wxCONTROL_CURRENT | wxCONTROL_PRESSED | wxCONTROL_SELECTED ) )
171 {
172 clearFlag( wxCONTROL_FOCUSED | wxCONTROL_CURRENT | wxCONTROL_PRESSED | wxCONTROL_SELECTED );
173 Refresh();
174 }
175
176 aEvent.Skip();
177}
178
179
180void BITMAP_BUTTON::OnSetFocus( wxEvent& aEvent )
181{
182 if( !hasFlag( wxCONTROL_CHECKABLE ) )
183 {
184 if( !hasFlag( wxCONTROL_FOCUSED ) )
185 {
186 setFlag( wxCONTROL_FOCUSED );
187 Refresh();
188 }
189 }
190
191 aEvent.Skip();
192}
193
194
195void BITMAP_BUTTON::OnLeftButtonUp( wxMouseEvent& aEvent )
196{
197 // Only create a button event when the control is enabled
198 // and only accept clicks that came without prior mouse-down if configured
199 if( !hasFlag( wxCONTROL_DISABLED )
200 && ( m_acceptDraggedInClicks || hasFlag( wxCONTROL_PRESSED | wxCONTROL_FOCUSED ) ) )
201 {
202 GetEventHandler()->CallAfter( [this]()
203 {
204 wxCommandEvent evt( wxEVT_BUTTON, GetId() );
205 evt.SetEventObject( this );
206 GetEventHandler()->ProcessEvent( evt );
207 } );
208 }
209
210 clearFlag( wxCONTROL_PRESSED );
211 Refresh();
212
213 aEvent.Skip();
214}
215
216
217void BITMAP_BUTTON::OnLeftButtonDown( wxMouseEvent& aEvent )
218{
219 if( hasFlag( wxCONTROL_CHECKABLE ) )
220 {
221 if( hasFlag( wxCONTROL_CHECKED ) && !m_isRadioButton )
222 {
223 clearFlag( wxCONTROL_CHECKED );
224
225 GetEventHandler()->CallAfter(
226 [this]()
227 {
228 wxCommandEvent evt( wxEVT_BUTTON, GetId() );
229 evt.SetEventObject( this );
230 evt.SetInt( 0 );
231 GetEventHandler()->ProcessEvent( evt );
232 } );
233 }
234 else
235 {
236 setFlag( wxCONTROL_CHECKED );
237
238 GetEventHandler()->CallAfter(
239 [this]()
240 {
241 wxCommandEvent evt( wxEVT_BUTTON, GetId() );
242 evt.SetEventObject( this );
243 evt.SetInt( 1 );
244 GetEventHandler()->ProcessEvent( evt );
245 } );
246 }
247 }
248 else
249 {
250 setFlag( wxCONTROL_PRESSED );
251 }
252
253 Refresh();
254
255 aEvent.Skip();
256}
257
258
259void BITMAP_BUTTON::OnDPIChanged( wxDPIChangedEvent& aEvent )
260{
262 aEvent.Skip();
263}
264
265
266void BITMAP_BUTTON::OnPaint( wxPaintEvent& aEvent )
267{
268 bool darkMode = KIPLATFORM::UI::IsDarkTheme();
269 wxColor highlightColor = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT );
270
271 // The drawing rectangle
272 wxRect rect( wxPoint( 0, 0 ), GetSize() );
273 wxPaintDC dc( this );
274
276 {
277 dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ) );
278 dc.DrawLine( wxPoint( GetSize().x / 2, 0 ), wxPoint( GetSize().x / 2, GetSize().y ) );
279 return;
280 }
281
282 // This drawing is done so the button looks the same as an AUI toolbar button
283 if( !hasFlag( wxCONTROL_DISABLED ) )
284 {
285 if( hasFlag( wxCONTROL_PRESSED ) )
286 {
287 dc.SetPen( wxPen( highlightColor ) );
288 dc.SetBrush( wxBrush( highlightColor.ChangeLightness( darkMode ? 20 : 150 ) ) );
289 dc.DrawRectangle( rect );
290 }
291 else if( hasFlag( wxCONTROL_CURRENT | wxCONTROL_FOCUSED ) )
292 {
293 dc.SetPen( wxPen( highlightColor ) );
294 dc.SetBrush( wxBrush( highlightColor.ChangeLightness( darkMode ? 40 : 170 ) ) );
295
296 // Checked items need a lighter hover rectangle
297 if( hasFlag( wxCONTROL_CHECKED ) )
298 dc.SetBrush( wxBrush( highlightColor.ChangeLightness( darkMode ? 50 : 180 ) ) );
299
300 dc.DrawRectangle( rect );
301 }
302 else if( hasFlag( wxCONTROL_CHECKED ) )
303 {
304 dc.SetPen( wxPen( highlightColor ) );
305 dc.SetBrush( wxBrush( highlightColor.ChangeLightness( darkMode ? 40 : 170 ) ) );
306 dc.DrawRectangle( rect );
307 }
308 }
309
310 const wxBitmapBundle& bmp = hasFlag( wxCONTROL_DISABLED ) ? m_disabledBitmap : m_normalBitmap;
311
312 wxPoint drawBmpPos( m_padding, m_padding );
313 wxBitmap bmpImg;
314 wxSize bmSize;
315
316 if( bmp.IsOk() )
317 {
318 bmpImg = bmp.GetBitmap( ToPhys( FromDIP( m_dipSize ) ) );
319 bmSize = bmpImg.GetLogicalSize();
320 }
321
322 if( m_centerBitmap )
323 {
324 drawBmpPos.x = ( rect.width - bmSize.x ) / 2;
325 drawBmpPos.y = ( rect.height - bmSize.y ) / 2;
326 }
327
328 // Draw the bitmap with the upper-left corner offset by the padding
329 if( bmp.IsOk() )
330 dc.DrawBitmap( bmpImg, drawBmpPos, true );
331
332 // Draw the badge
333 if( m_showBadge && !m_badgeText.IsEmpty() )
334 {
335 dc.SetFont( m_badgeFont );
336
337 wxSize text_padding( 2, 1 );
338
339 if( m_padding )
340 text_padding *= 2;
341
342 wxSize box_size = dc.GetTextExtent( m_badgeText ) + text_padding;
343 wxSize box_offset = box_size;
344
345 if( m_padding != 0 )
346 box_offset += wxSize( m_padding / 3, m_padding / 3 );
347
348 dc.SetPen( wxPen( m_badgeColor ) );
349 dc.SetBrush( wxBrush( m_badgeColor ) );
350 dc.DrawRoundedRectangle( rect.GetRightBottom() - box_offset, box_size, -0.25 );
351
352 dc.SetTextForeground( m_badgeTextColor );
353 dc.DrawText( m_badgeText, rect.GetRightBottom() - box_offset + ( text_padding / 2 ) );
354 }
355}
356
357
358bool BITMAP_BUTTON::Enable( bool aEnable )
359{
360 // If the requested state is already the current state, don't do anything
361 if( aEnable != hasFlag( wxCONTROL_DISABLED ) )
362 return false;
363
364 wxPanel::Enable( aEnable );
365
366 if( aEnable && hasFlag( wxCONTROL_DISABLED ) )
367 {
368 clearFlag( wxCONTROL_DISABLED );
369 Refresh();
370 }
371
372 if( !aEnable && !hasFlag( wxCONTROL_DISABLED ) )
373 {
374 setFlag( wxCONTROL_DISABLED );
375 Refresh();
376 }
377
378 return true;
379}
380
381
383{
384 setFlag( wxCONTROL_CHECKABLE );
385}
386
387
389{
390 setFlag( wxCONTROL_CHECKABLE );
391 m_isRadioButton = true;
392}
393
394
396{
397 setFlag( wxCONTROL_SEPARATOR | wxCONTROL_DISABLED );
398
400}
401
402
403void BITMAP_BUTTON::Check( bool aCheck )
404{
405 wxASSERT_MSG( hasFlag( wxCONTROL_CHECKABLE ), wxS( "Button is not a checkButton." ) );
406
407 if( aCheck && !hasFlag( wxCONTROL_CHECKED ) )
408 {
409 setFlag( wxCONTROL_CHECKED );
410 Refresh();
411 }
412
413 if( !aCheck && hasFlag( wxCONTROL_CHECKED ) )
414 {
415 clearFlag( wxCONTROL_CHECKED );
416 Refresh();
417 }
418}
419
420
422{
423 wxASSERT_MSG( hasFlag( wxCONTROL_CHECKABLE ), wxS( "Button is not a checkButton." ) );
424
425 return hasFlag( wxCONTROL_CHECKED );
426}
#define wxCONTROL_SEPARATOR
void OnKillFocus(wxEvent &aEvent)
bool m_centerBitmap
Draw bitmap centered in the control.
bool IsChecked() const
void OnSetFocus(wxEvent &aEvent)
void AcceptDragInAsClick(bool aAcceptDragIn=true)
Accept mouse-up as click even if mouse-down happened outside of the control.
void OnMouseLeave(wxEvent &aEvent)
bool Enable(bool aEnable=true) override
Enable the button.
void setFlag(int aFlag)
wxBitmapBundle m_disabledBitmap
void Check(bool aCheck=true)
Check the control.
bool hasFlag(int aFlag) const
void SetDisabledBitmap(const wxBitmapBundle &aBmp)
Set the bitmap shown when the button is disabled.
BITMAP_BUTTON(wxWindow *aParent, wxWindowID aId, const wxPoint &aPos=wxDefaultPosition, const wxSize &aSize=wxDefaultSize, int aStyles=wxBORDER_NONE|wxTAB_TRAVERSAL)
wxColor m_badgeTextColor
void OnLeftButtonDown(wxMouseEvent &aEvent)
wxString m_badgeText
void SetIsSeparator()
Render button as a toolbar separator.
void invalidateBestSize()
void clearFlag(int aFlag)
bool m_acceptDraggedInClicks
Accept mouse-up as click even if mouse-down happened outside of the control.
wxBitmapBundle m_normalBitmap
void SetPadding(int aPaddingDIP)
Set the amount of padding present on each side of the bitmap.
virtual wxSize DoGetBestSize() const override
void OnPaint(wxPaintEvent &aEvent)
void OnMouseEnter(wxEvent &aEvent)
void OnDPIChanged(wxDPIChangedEvent &aEvent)
void OnLeftButtonUp(wxMouseEvent &aEvent)
void SetIsCheckButton()
Setup the control as a two-state button (checked or unchecked).
void SetBitmap(const wxBitmapBundle &aBmp)
Set the bitmap shown when the button is enabled.
wxColor m_badgeColor
bool IsDarkTheme()
Determine if the desktop interface is currently using a dark theme or a light theme.
Definition wxgtk/ui.cpp:50
see class PGM_BASE