KiCad PCB EDA Suite
Loading...
Searching...
No Matches
split_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) 2016 Anil8735(https://stackoverflow.com/users/3659387/anil8753)
5 * from https://stackoverflow.com/a/37274011
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
27#include <wx/button.h>
28#include <wx/dcclient.h>
29#include <wx/dcmemory.h>
30#include <wx/menu.h>
31#include <wx/renderer.h>
32#include <wx/settings.h>
33#include <wx/version.h>
34#include <kiplatform/ui.h>
35
36SPLIT_BUTTON::SPLIT_BUTTON( wxWindow* aParent, wxWindowID aId, const wxString& aLabel,
37 const wxPoint& aPos, const wxSize& aSize ) :
38 wxPanel( aParent, aId, aPos, aSize, wxBORDER_NONE | wxTAB_TRAVERSAL,
39 wxS( "DropDownButton" ) ),
40 m_label( aLabel )
41{
42 m_arrowButtonWidth = FromDIP( 20 ); // just a fixed eyeballed constant width
43 m_widthPadding = FromDIP( 10 );
44
45 if( aSize == wxDefaultSize )
46 {
47 wxSize defaultSize = wxButton::GetDefaultSize( aParent );
48 wxSize textSize = GetTextExtent( m_label );
49 SetMinSize( wxSize( std::max( textSize.GetWidth(), defaultSize.GetWidth() + 1 ),
50 defaultSize.GetHeight() + 1 ) );
51 }
52
53 Bind( wxEVT_PAINT, &SPLIT_BUTTON::OnPaint, this );
54 Bind( wxEVT_LEFT_UP, &SPLIT_BUTTON::OnLeftButtonUp, this );
55 Bind( wxEVT_LEFT_DOWN, &SPLIT_BUTTON::OnLeftButtonDown, this );
56 Bind( wxEVT_KILL_FOCUS, &SPLIT_BUTTON::OnKillFocus, this );
57 Bind( wxEVT_LEAVE_WINDOW, &SPLIT_BUTTON::OnMouseLeave, this );
58 Bind( wxEVT_ENTER_WINDOW, &SPLIT_BUTTON::OnMouseEnter, this );
59
60 Bind( wxEVT_SYS_COLOUR_CHANGED, wxSysColourChangedEventHandler( SPLIT_BUTTON::onThemeChanged ),
61 this );
62
63 m_pMenu = new wxMenu();
64}
65
66
68{
69 delete m_pMenu;
70 m_pMenu = nullptr;
71}
72
73
74void SPLIT_BUTTON::onThemeChanged( wxSysColourChangedEvent &aEvent )
75{
76 Refresh();
77}
78
79
80void SPLIT_BUTTON::SetMinSize( const wxSize& aSize )
81{
82 m_unadjustedMinSize = aSize;
83 wxPanel::SetMinSize( wxSize( aSize.GetWidth() + m_arrowButtonWidth + m_widthPadding,
84 aSize.GetHeight() ) );
85}
86
87
89{
90 m_widthPadding = aPadding;
92}
93
94
95void SPLIT_BUTTON::SetBitmap( const wxBitmapBundle& aBmp )
96{
97 m_bitmap = aBmp;
98
99#ifndef __WXMSW__
100 SetMinSize( m_bitmap.GetDefaultSize() );
101#else
102 SetMinSize( m_bitmap.GetPreferredBitmapSizeFor( this ) );
103#endif
104}
105
106
107void SPLIT_BUTTON::SetLabel( const wxString& aLabel )
108{
109 if( m_label != aLabel )
110 {
111 m_label = aLabel;
112 Refresh();
113 }
114}
115
116
118{
119 return m_pMenu;
120}
121
122
123void SPLIT_BUTTON::OnKillFocus( wxFocusEvent& aEvent )
124{
125 if( m_stateButton != 0 || m_stateMenu != 0 )
126 {
127 m_stateButton = 0;
128 m_stateMenu = 0;
129 Refresh();
130 }
131
132 aEvent.Skip();
133}
134
135
136void SPLIT_BUTTON::OnMouseLeave( wxMouseEvent& aEvent )
137{
138 if( m_stateButton != 0 || m_stateMenu != 0 )
139 {
140 m_stateButton = 0;
141 m_stateMenu = 0;
142 Refresh();
143 }
144
145 aEvent.Skip();
146}
147
148
149void SPLIT_BUTTON::OnMouseEnter( wxMouseEvent& aEvent )
150{
151 if( m_stateButton != wxCONTROL_CURRENT || m_stateMenu != wxCONTROL_CURRENT )
152 {
153 m_stateButton = wxCONTROL_CURRENT;
154 m_stateMenu = wxCONTROL_CURRENT;
155 Refresh();
156 }
157
158 aEvent.Skip();
159}
160
161
162void SPLIT_BUTTON::OnLeftButtonUp( wxMouseEvent& aEvent )
163{
164 m_stateButton = 0;
165 m_stateMenu = 0;
166
167 Refresh();
168
169 int x = -1;
170 int y = -1;
171 aEvent.GetPosition( &x, &y );
172
173 if( x < ( GetSize().GetWidth() - m_arrowButtonWidth ) )
174 {
175 wxEvtHandler* pEventHandler = GetEventHandler();
176 wxASSERT( pEventHandler );
177
178 pEventHandler->CallAfter(
179 [this]()
180 {
181 wxCommandEvent evt( wxEVT_BUTTON, GetId() );
182 evt.SetEventObject( this );
183 GetEventHandler()->ProcessEvent( evt );
184 } );
185 }
186
187 m_bLButtonDown = false;
188
189 aEvent.Skip();
190}
191
192
193void SPLIT_BUTTON::OnLeftButtonDown( wxMouseEvent& aEvent )
194{
195 m_bLButtonDown = true;
196
197 int x = -1;
198 int y = -1;
199 aEvent.GetPosition( &x, &y );
200
201 if( x >= ( GetSize().GetWidth() - m_arrowButtonWidth ) )
202 {
203 m_stateButton = 0;
204 m_stateMenu = wxCONTROL_PRESSED;
205 Refresh();
206
207 wxSize size = GetSize();
208 wxPoint position;
209 position.x = 0;
210 position.y = size.GetHeight();
211 PopupMenu( m_pMenu, position );
212
213 m_stateMenu = 0;
214 Refresh();
215 }
216 else
217 {
218 m_stateButton = wxCONTROL_PRESSED;
219 m_stateMenu = wxCONTROL_PRESSED;
220 Refresh();
221 }
222
223 aEvent.Skip();
224}
225
226
227void SPLIT_BUTTON::OnPaint( wxPaintEvent& WXUNUSED( aEvent ) )
228{
229 wxPaintDC dc( this );
230 wxSize size = GetSize();
231 const int width = size.GetWidth() - m_arrowButtonWidth;
232
233#ifdef __WXMAC__
234 auto drawBackground =
235 [&]( wxRect aRect )
236 {
237 // wxWidgets doesn't have much support for dark mode on OSX; none of the
238 // system colours return the right values, nor does wxRendererNative draw
239 // the borders correctly. So we add some empirically chosen hacks here.
240
241 // NOTE: KEEP THESE HACKS IN SYNC WITH STD_BITMAP_BUTTON
242
243 wxColor fg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT );
244 wxColor bg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
245
246 aRect.width += 1;
247 aRect.height += 1;
248
250 {
251 bg = bg.ChangeLightness( m_bIsEnable ? 130 : 120 );
252 dc.SetBrush( bg );
253 dc.SetPen( bg );
254 }
255 else
256 {
257 bg = bg.ChangeLightness( m_bIsEnable ? 200 : 160 );
258 dc.SetBrush( bg );
259 fg = fg.ChangeLightness( 180 );
260 dc.SetPen( fg );
261 }
262
263 dc.DrawRoundedRectangle( aRect, aRect.height / 4.0 );
264 };
265#endif
266
267 // Draw first part of button
268 wxRect r1;
269 r1.x = 0;
270 r1.y = 0;
271 r1.width = width;
272 r1.height = size.GetHeight();
273
274#ifdef __WXMAC__
275 // wxRendereNative doesn't handle dark mode on OSX.
276 drawBackground( r1 );
277#else
278
279#ifdef _WXMSW_
280 r1.width += 2;
281#endif
282
283 wxRendererNative::Get().DrawPushButton( this, dc, r1, m_stateButton );
284#endif
285
286 SetForegroundColour( m_bIsEnable ? wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT )
287 : wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
288
289 if( m_bitmap.IsOk() )
290 {
291#ifndef __WXMSW__
292 wxSize bmpSize = m_bitmap.GetDefaultSize();
293#else
294 wxSize bmpSize = m_bitmap.GetPreferredBitmapSizeFor( this );
295#endif
296 wxBitmap bmp = m_bitmap.GetBitmapFor( this );
297 wxMemoryDC mdc( bmp );
298
299 r1.x = ( width - bmpSize.GetWidth() ) / 2;
300
301 if( r1.x < 0 )
302 r1.x = 0;
303
304 r1.y += ( size.GetHeight() - bmpSize.GetHeight() ) / 2;
305
306 dc.Blit( wxPoint( r1.x, r1.y ), bmpSize, &mdc, wxPoint( 0, 0 ), wxCOPY, true );
307 }
308 else
309 {
310 r1.y += ( ( size.GetHeight() - GetCharHeight() ) / 2 ) - 1;
311 dc.DrawLabel( m_label, r1, wxALIGN_CENTER_HORIZONTAL );
312 }
313
314 // Draw second part of button
315 wxRect r2;
316 r2.x = width;
317 r2.y = 0;
318 r2.width = m_arrowButtonWidth;
319 r2.height = size.GetHeight();
320
321#ifdef __WXMAC__
322 // wxRendereNative doesn't handle dark mode on OSX.
323 drawBackground( r2 );
324#else
325 r2.x -= 2;
326 wxRendererNative::Get().DrawPushButton( this, dc, r2, m_stateMenu );
327#endif
328
329 wxRendererNative::Get().DrawDropArrow( this, dc, r2, m_stateMenu );
330}
331
332
333bool SPLIT_BUTTON::Enable( bool aEnable )
334{
335 m_bIsEnable = aEnable;
336 wxPanel::Enable( m_bIsEnable );
337
338 if( m_bIsEnable
339 && ( m_stateButton == wxCONTROL_DISABLED || m_stateMenu == wxCONTROL_DISABLED ) )
340 {
341 m_stateButton = 0;
342 m_stateMenu = 0;
343 Refresh();
344 }
345
346 if( !m_bIsEnable
347 && ( m_stateButton != wxCONTROL_DISABLED || m_stateMenu != wxCONTROL_DISABLED ) )
348 {
349 m_stateButton = wxCONTROL_DISABLED;
350 m_stateMenu = wxCONTROL_DISABLED;
351 Refresh();
352 }
353
354 return aEnable;
355}
int m_arrowButtonWidth
Definition: split_button.h:63
void SetLabel(const wxString &aLabel) override
wxString m_label
Definition: split_button.h:66
void onThemeChanged(wxSysColourChangedEvent &aEvent)
bool m_bLButtonDown
Definition: split_button.h:65
void OnLeftButtonUp(wxMouseEvent &aEvent)
void OnKillFocus(wxFocusEvent &aEvent)
void OnMouseLeave(wxMouseEvent &aEvent)
wxBitmapBundle m_bitmap
Definition: split_button.h:68
void SetBitmap(const wxBitmapBundle &aBmp)
void SetWidthPadding(int aPadding)
wxMenu * GetSplitButtonMenu()
wxMenu * m_pMenu
Definition: split_button.h:67
void OnLeftButtonDown(wxMouseEvent &aEvent)
void SetMinSize(const wxSize &aSize) override
bool Enable(bool aEnable=true) override
wxSize m_unadjustedMinSize
Definition: split_button.h:69
void OnMouseEnter(wxMouseEvent &aEvent)
void OnPaint(wxPaintEvent &WXUNUSED(aEvent))
bool m_bIsEnable
Definition: split_button.h:62
SPLIT_BUTTON(wxWindow *aParent, wxWindowID aId, const wxString &aLabel, const wxPoint &aPos=wxDefaultPosition, const wxSize &aSize=wxDefaultSize)
int m_widthPadding
Definition: split_button.h:64
static const wxSize defaultSize(FRAME_T aFrameType, wxWindow *aWindow)
bool IsDarkTheme()
Determine if the desktop interface is currently using a dark theme or a light theme.
Definition: wxgtk/ui.cpp:48
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...