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 (C) 2020-2023 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, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <kiplatform/ui.h>
27#include <wx/button.h>
28#include <wx/dcclient.h>
29#include <wx/renderer.h>
30#include <wx/settings.h>
31
32#define wxCONTROL_SEPARATOR wxCONTROL_SPECIAL
33
34
35BITMAP_BUTTON::BITMAP_BUTTON( wxWindow* aParent, wxWindowID aId, const wxPoint& aPos,
36 const wxSize& aSize, int aStyles ) :
37 wxPanel( aParent, aId, aPos, aSize, aStyles ),
38 m_isRadioButton( false ),
39 m_showBadge( false ),
40 m_badgeColor( wxColor( 210, 0, 0 ) ), // dark red
41 m_badgeTextColor( wxColor( wxT( "white" ) ) ),
42 m_buttonState( 0 ),
43 m_padding( 0 ),
44 m_acceptDraggedInClicks( false )
45{
46 if( aSize == wxDefaultSize )
47 SetMinSize( wxButton::GetDefaultSize() + wxSize( m_padding * 2, m_padding * 2) );
48
49 m_badgeFont = GetFont().Smaller().MakeBold();
50
52}
53
54
55BITMAP_BUTTON::BITMAP_BUTTON( wxWindow* aParent, wxWindowID aId, const wxBitmap& aDummyBitmap,
56 const wxPoint& aPos, const wxSize& aSize, int aStyles ) :
57 wxPanel( aParent, aId, aPos, aSize, aStyles ),
58 m_isRadioButton( false ),
59 m_showBadge( false ),
60 m_buttonState( 0 ),
61 m_padding( 5 ),
62 m_acceptDraggedInClicks( false )
63{
64 if( aSize == wxDefaultSize )
65 SetMinSize( wxButton::GetDefaultSize() + wxSize( m_padding * 2, m_padding * 2) );
66
68}
69
70
72{
73 Bind( wxEVT_PAINT, &BITMAP_BUTTON::OnPaint, this );
74 Bind( wxEVT_LEFT_UP, &BITMAP_BUTTON::OnLeftButtonUp, this );
75 Bind( wxEVT_LEFT_DOWN, &BITMAP_BUTTON::OnLeftButtonDown, this );
76 Bind( wxEVT_LEAVE_WINDOW, &BITMAP_BUTTON::OnMouseLeave, this );
77 Bind( wxEVT_ENTER_WINDOW, &BITMAP_BUTTON::OnMouseEnter, this );
78 Bind( wxEVT_KILL_FOCUS, &BITMAP_BUTTON::OnKillFocus, this );
79 Bind( wxEVT_SET_FOCUS, &BITMAP_BUTTON::OnSetFocus, this );
80}
81
82
84{
85}
86
87
88void BITMAP_BUTTON::SetPadding( int aPadding )
89{
90 m_padding = aPadding;
91 SetMinSize( m_unadjustedMinSize + wxSize( aPadding * 2, aPadding * 2 ) );
92}
93
94
95void BITMAP_BUTTON::SetBitmap( const wxBitmapBundle& aBmp )
96{
97 m_normalBitmap = aBmp;
98#ifndef __WXMSW__
99 m_unadjustedMinSize = m_normalBitmap.GetDefaultSize();
100#else
101 m_unadjustedMinSize = m_normalBitmap.GetPreferredBitmapSizeFor( this );
102#endif
103
104 SetMinSize( wxSize( m_unadjustedMinSize.GetWidth() + ( m_padding * 2 ),
105 m_unadjustedMinSize.GetHeight() + ( m_padding * 2 ) ) );
106}
107
108
109void BITMAP_BUTTON::SetDisabledBitmap( const wxBitmapBundle& aBmp )
110{
111 m_disabledBitmap = aBmp;
112}
113
114
115void BITMAP_BUTTON::AcceptDragInAsClick( bool aAcceptDragIn )
116{
117 m_acceptDraggedInClicks = aAcceptDragIn;
118}
119
120
121void BITMAP_BUTTON::OnMouseLeave( wxEvent& aEvent )
122{
123 if( hasFlag( wxCONTROL_CURRENT | wxCONTROL_PRESSED ) )
124 {
125 clearFlag( wxCONTROL_CURRENT | wxCONTROL_PRESSED );
126 Refresh();
127 }
128
129 aEvent.Skip();
130}
131
132
133void BITMAP_BUTTON::OnMouseEnter( wxEvent& aEvent )
134{
135 if( !hasFlag( wxCONTROL_CURRENT ) )
136 {
137 setFlag( wxCONTROL_CURRENT );
138 Refresh();
139 }
140
141 aEvent.Skip();
142}
143
144
145void BITMAP_BUTTON::OnKillFocus( wxEvent& aEvent )
146{
147 if( hasFlag( wxCONTROL_FOCUSED | wxCONTROL_CURRENT | wxCONTROL_PRESSED ) )
148 {
149 clearFlag( wxCONTROL_FOCUSED | wxCONTROL_CURRENT | wxCONTROL_PRESSED );
150 Refresh();
151 }
152
153 aEvent.Skip();
154}
155
156
157void BITMAP_BUTTON::OnSetFocus( wxEvent& aEvent )
158{
159 if( !hasFlag( wxCONTROL_CHECKABLE ) )
160 {
161 if( !hasFlag( wxCONTROL_FOCUSED ) )
162 {
163 setFlag( wxCONTROL_FOCUSED );
164 Refresh();
165 }
166 }
167
168 aEvent.Skip();
169}
170
171
172void BITMAP_BUTTON::OnLeftButtonUp( wxMouseEvent& aEvent )
173{
174 // Only create a button event when the control is enabled
175 // and only accept clicks that came without prior mouse-down if configured
176 if( !hasFlag( wxCONTROL_DISABLED )
177 && ( m_acceptDraggedInClicks || hasFlag( wxCONTROL_PRESSED | wxCONTROL_FOCUSED ) ) )
178 {
179 GetEventHandler()->CallAfter( [=]()
180 {
181 wxCommandEvent evt( wxEVT_BUTTON, GetId() );
182 evt.SetEventObject( this );
183 GetEventHandler()->ProcessEvent( evt );
184 } );
185 }
186
187 clearFlag( wxCONTROL_PRESSED );
188 Refresh();
189
190 aEvent.Skip();
191}
192
193
194void BITMAP_BUTTON::OnLeftButtonDown( wxMouseEvent& aEvent )
195{
196 if( hasFlag( wxCONTROL_CHECKABLE ) )
197 {
198 if( hasFlag( wxCONTROL_CHECKED ) && !m_isRadioButton )
199 {
200 clearFlag( wxCONTROL_CHECKED );
201
202 GetEventHandler()->CallAfter(
203 [=]()
204 {
205 wxCommandEvent evt( wxEVT_BUTTON, GetId() );
206 evt.SetEventObject( this );
207 evt.SetInt( 0 );
208 GetEventHandler()->ProcessEvent( evt );
209 } );
210 }
211 else
212 {
213 setFlag( wxCONTROL_CHECKED );
214
215 GetEventHandler()->CallAfter(
216 [=]()
217 {
218 wxCommandEvent evt( wxEVT_BUTTON, GetId() );
219 evt.SetEventObject( this );
220 evt.SetInt( 1 );
221 GetEventHandler()->ProcessEvent( evt );
222 } );
223 }
224 }
225 else
226 {
227 setFlag( wxCONTROL_PRESSED );
228 }
229
230 Refresh();
231
232 aEvent.Skip();
233}
234
235
236void BITMAP_BUTTON::OnPaint( wxPaintEvent& aEvent )
237{
238 bool darkMode = KIPLATFORM::UI::IsDarkTheme();
239 wxColor highlightColor = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT );
240
241 // The drawing rectangle
242 wxRect rect( wxPoint( 0, 0 ), GetSize() );
243 wxPaintDC dc( this );
244
246 {
247 dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ) );
248 dc.DrawLine( wxPoint( GetSize().x / 2, 0 ), wxPoint( GetSize().x / 2, GetSize().y ) );
249 return;
250 }
251
252 // This drawing is done so the button looks the same as an AUI toolbar button
253 if( !hasFlag( wxCONTROL_DISABLED ) )
254 {
255 if( hasFlag( wxCONTROL_PRESSED ) )
256 {
257 dc.SetPen( wxPen( highlightColor ) );
258 dc.SetBrush( wxBrush( highlightColor.ChangeLightness( darkMode ? 20 : 150 ) ) );
259 dc.DrawRectangle( rect );
260 }
261 else if( hasFlag( wxCONTROL_CURRENT | wxCONTROL_FOCUSED ) )
262 {
263 dc.SetPen( wxPen( highlightColor ) );
264 dc.SetBrush( wxBrush( highlightColor.ChangeLightness( darkMode ? 40 : 170 ) ) );
265
266 // Checked items need a lighter hover rectangle
267 if( hasFlag( wxCONTROL_CHECKED ) )
268 dc.SetBrush( wxBrush( highlightColor.ChangeLightness( darkMode ? 50 : 180 ) ) );
269
270 dc.DrawRectangle( rect );
271 }
272 else if( hasFlag( wxCONTROL_CHECKED ) )
273 {
274 dc.SetPen( wxPen( highlightColor ) );
275 dc.SetBrush( wxBrush( highlightColor.ChangeLightness( darkMode ? 40 : 170 ) ) );
276 dc.DrawRectangle( rect );
277 }
278 }
279
280 const wxBitmapBundle& bmp = hasFlag( wxCONTROL_DISABLED ) ? m_disabledBitmap : m_normalBitmap;
281
282 // Draw the bitmap with the upper-left corner offset by the padding
283 if( bmp.IsOk() )
284 dc.DrawBitmap( bmp.GetBitmapFor( this ), m_padding, m_padding, true );
285
286 // Draw the badge
287 if( m_showBadge )
288 {
289 dc.SetFont( m_badgeFont );
290
291 wxSize box_size = dc.GetTextExtent( m_badgeText ) + wxSize( 6, 2 );
292 wxSize box_offset = box_size + wxSize( m_padding - 2, m_padding );
293 wxSize text_offset = box_offset - wxSize( 3, 1 );
294
295 dc.SetPen( wxPen( m_badgeColor ) );
296 dc.SetBrush( wxBrush( m_badgeColor ) );
297 dc.DrawRoundedRectangle( rect.GetRightBottom() - box_offset, box_size, -0.25 );
298
299 dc.SetTextForeground( m_badgeTextColor );
300 dc.DrawText( m_badgeText, rect.GetRightBottom() - text_offset );
301 }
302}
303
304
305bool BITMAP_BUTTON::Enable( bool aEnable )
306{
307 // If the requested state is already the current state, don't do anything
308 if( aEnable != hasFlag( wxCONTROL_DISABLED ) )
309 return false;
310
311 wxPanel::Enable( aEnable );
312
313 if( aEnable && hasFlag( wxCONTROL_DISABLED ) )
314 {
315 clearFlag( wxCONTROL_DISABLED );
316 Refresh();
317 }
318
319 if( !aEnable && !hasFlag( wxCONTROL_DISABLED ) )
320 {
321 setFlag( wxCONTROL_DISABLED );
322 Refresh();
323 }
324
325 return true;
326}
327
328
330{
331 setFlag( wxCONTROL_CHECKABLE );
332}
333
334
336{
337 setFlag( wxCONTROL_CHECKABLE );
338 m_isRadioButton = true;
339}
340
341
343{
344 setFlag( wxCONTROL_SEPARATOR | wxCONTROL_DISABLED );
345 SetMinSize( wxSize( m_padding * 2, wxButton::GetDefaultSize().y ) );
346}
347
348
349void BITMAP_BUTTON::Check( bool aCheck )
350{
351 wxASSERT_MSG( hasFlag( wxCONTROL_CHECKABLE ), wxS( "Button is not a checkButton." ) );
352
353 if( aCheck && !hasFlag( wxCONTROL_CHECKED ) )
354 {
355 setFlag( wxCONTROL_CHECKED );
356 Refresh();
357 }
358
359 if( !aCheck && hasFlag( wxCONTROL_CHECKED ) )
360 {
361 clearFlag( wxCONTROL_CHECKED );
362 Refresh();
363 }
364}
365
366
368{
369 wxASSERT_MSG( hasFlag( wxCONTROL_CHECKABLE ), wxS( "Button is not a checkButton." ) );
370
371 return hasFlag( wxCONTROL_CHECKED );
372}
#define wxCONTROL_SEPARATOR
void OnKillFocus(wxEvent &aEvent)
void SetIsRadioButton()
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.
wxFont m_badgeFont
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.
wxSize m_unadjustedMinSize
Accept mouse-up as click even if mouse-down happened outside of the control.
void clearFlag(int aFlag)
bool m_acceptDraggedInClicks
wxBitmapBundle m_normalBitmap
void OnPaint(wxPaintEvent &aEvent)
void OnMouseEnter(wxEvent &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
void SetPadding(int aPadding)
Set the amount of padding present on each side of the bitmap.
bool IsDarkTheme()
Determine if the desktop interface is currently using a dark theme or a light theme.
Definition: gtk/ui.cpp:31
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...