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, see <https://www.gnu.org/licenses/>.
20 */
21
23#include <wx/button.h>
24#include <wx/dcclient.h>
25#include <wx/dcmemory.h>
26#include <wx/menu.h>
27#include <wx/renderer.h>
28#include <wx/settings.h>
29#include <wx/version.h>
30#include <kiplatform/ui.h>
31
32SPLIT_BUTTON::SPLIT_BUTTON( wxWindow* aParent, wxWindowID aId, const wxString& aLabel,
33 const wxPoint& aPos, const wxSize& aSize ) :
34 wxPanel( aParent, aId, aPos, aSize, wxBORDER_NONE | wxTAB_TRAVERSAL,
35 wxS( "DropDownButton" ) ),
36 m_label( aLabel )
37{
38 m_arrowButtonWidth = FromDIP( 20 ); // just a fixed eyeballed constant width
39 m_widthPadding = FromDIP( 10 );
40
41 if( aSize == wxDefaultSize )
42 {
43 wxSize defaultSize = wxButton::GetDefaultSize( aParent );
44 wxSize textSize = GetTextExtent( m_label );
45 SetMinSize( wxSize( std::max( textSize.GetWidth(), defaultSize.GetWidth() + 1 ),
46 defaultSize.GetHeight() + 1 ) );
47 }
48
49 Bind( wxEVT_PAINT, &SPLIT_BUTTON::OnPaint, this );
50 Bind( wxEVT_LEFT_UP, &SPLIT_BUTTON::OnLeftButtonUp, this );
51 Bind( wxEVT_LEFT_DOWN, &SPLIT_BUTTON::OnLeftButtonDown, this );
52 Bind( wxEVT_KILL_FOCUS, &SPLIT_BUTTON::OnKillFocus, this );
53 Bind( wxEVT_LEAVE_WINDOW, &SPLIT_BUTTON::OnMouseLeave, this );
54 Bind( wxEVT_ENTER_WINDOW, &SPLIT_BUTTON::OnMouseEnter, this );
55
56 Bind( wxEVT_SYS_COLOUR_CHANGED, wxSysColourChangedEventHandler( SPLIT_BUTTON::onThemeChanged ),
57 this );
58
59 m_pMenu = new wxMenu();
60}
61
62
64{
65 delete m_pMenu;
66 m_pMenu = nullptr;
67}
68
69
70void SPLIT_BUTTON::onThemeChanged( wxSysColourChangedEvent &aEvent )
71{
72 Refresh();
73}
74
75
76void SPLIT_BUTTON::SetMinSize( const wxSize& aSize )
77{
78 m_unadjustedMinSize = aSize;
79 wxPanel::SetMinSize( wxSize( aSize.GetWidth() + m_arrowButtonWidth + m_widthPadding,
80 aSize.GetHeight() ) );
81}
82
83
85{
86 m_widthPadding = aPadding;
88}
89
90
91void SPLIT_BUTTON::SetBitmap( const wxBitmapBundle& aBmp )
92{
93 m_bitmap = aBmp;
94
95#ifndef __WXMSW__
96 SetMinSize( m_bitmap.GetDefaultSize() );
97#else
98 SetMinSize( m_bitmap.GetPreferredBitmapSizeFor( this ) );
99#endif
100}
101
102
103void SPLIT_BUTTON::SetLabel( const wxString& aLabel )
104{
105 if( m_label != aLabel )
106 {
107 m_label = aLabel;
108 Refresh();
109 }
110}
111
112
114{
115 return m_pMenu;
116}
117
118
119void SPLIT_BUTTON::OnKillFocus( wxFocusEvent& aEvent )
120{
121 if( m_stateButton != 0 || m_stateMenu != 0 )
122 {
123 m_stateButton = 0;
124 m_stateMenu = 0;
125 Refresh();
126 }
127
128 aEvent.Skip();
129}
130
131
132void SPLIT_BUTTON::OnMouseLeave( wxMouseEvent& aEvent )
133{
134 if( m_stateButton != 0 || m_stateMenu != 0 )
135 {
136 m_stateButton = 0;
137 m_stateMenu = 0;
138 Refresh();
139 }
140
141 aEvent.Skip();
142}
143
144
145void SPLIT_BUTTON::OnMouseEnter( wxMouseEvent& aEvent )
146{
147 if( m_stateButton != wxCONTROL_CURRENT || m_stateMenu != wxCONTROL_CURRENT )
148 {
149 m_stateButton = wxCONTROL_CURRENT;
150 m_stateMenu = wxCONTROL_CURRENT;
151 Refresh();
152 }
153
154 aEvent.Skip();
155}
156
157
158void SPLIT_BUTTON::OnLeftButtonUp( wxMouseEvent& aEvent )
159{
160 m_stateButton = 0;
161 m_stateMenu = 0;
162
163 Refresh();
164
165 int x = -1;
166 int y = -1;
167 aEvent.GetPosition( &x, &y );
168
169 if( x < ( GetSize().GetWidth() - m_arrowButtonWidth ) )
170 {
171 wxEvtHandler* pEventHandler = GetEventHandler();
172 wxASSERT( pEventHandler );
173
174 pEventHandler->CallAfter(
175 [this]()
176 {
177 wxCommandEvent evt( wxEVT_BUTTON, GetId() );
178 evt.SetEventObject( this );
179 GetEventHandler()->ProcessEvent( evt );
180 } );
181 }
182
183 m_bLButtonDown = false;
184
185 aEvent.Skip();
186}
187
188
189void SPLIT_BUTTON::OnLeftButtonDown( wxMouseEvent& aEvent )
190{
191 m_bLButtonDown = true;
192
193 int x = -1;
194 int y = -1;
195 aEvent.GetPosition( &x, &y );
196
197 if( x >= ( GetSize().GetWidth() - m_arrowButtonWidth ) )
198 {
199 m_stateButton = 0;
200 m_stateMenu = wxCONTROL_PRESSED;
201 Refresh();
202
203 wxSize size = GetSize();
204 wxPoint position;
205 position.x = 0;
206 position.y = size.GetHeight();
207 PopupMenu( m_pMenu, position );
208
209 m_stateMenu = 0;
210 Refresh();
211 }
212 else
213 {
214 m_stateButton = wxCONTROL_PRESSED;
215 m_stateMenu = wxCONTROL_PRESSED;
216 Refresh();
217 }
218
219 aEvent.Skip();
220}
221
222
223void SPLIT_BUTTON::OnPaint( wxPaintEvent& WXUNUSED( aEvent ) )
224{
225 wxPaintDC dc( this );
226 wxSize size = GetSize();
227 const int width = size.GetWidth() - m_arrowButtonWidth;
228
229#ifdef __WXMAC__
230 auto drawBackground =
231 [&]( wxRect aRect )
232 {
233 // wxWidgets doesn't have much support for dark mode on OSX; none of the
234 // system colours return the right values, nor does wxRendererNative draw
235 // the borders correctly. So we add some empirically chosen hacks here.
236
237 // NOTE: KEEP THESE HACKS IN SYNC WITH STD_BITMAP_BUTTON
238
239 wxColor fg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT );
240 wxColor bg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
241
243 {
244 bg = bg.ChangeLightness( m_bIsEnable ? 130 : 120 );
245 dc.SetBrush( bg );
246 dc.SetPen( bg );
247 }
248 else
249 {
250 bg = bg.ChangeLightness( m_bIsEnable ? 200 : 160 );
251 dc.SetBrush( bg );
252 fg = fg.ChangeLightness( 180 );
253 dc.SetPen( fg );
254 }
255
256 dc.DrawRoundedRectangle( aRect, aRect.height / 4.0 );
257 };
258#endif
259
260 // Draw first part of button
261 wxRect r1;
262 r1.x = 0;
263 r1.y = 0;
264 r1.width = width;
265 r1.height = size.GetHeight();
266
267#ifdef __WXMAC__
268 // wxRendereNative doesn't handle dark mode on OSX.
269 drawBackground( r1 );
270#else
271
272#ifdef _WXMSW_
273 r1.width += 2;
274#endif
275
276 wxRendererNative::Get().DrawPushButton( this, dc, r1, m_stateButton );
277#endif
278
279 SetForegroundColour( m_bIsEnable ? wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT )
280 : wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
281
282 if( m_bitmap.IsOk() )
283 {
284#ifndef __WXMSW__
285 wxSize bmpSize = m_bitmap.GetDefaultSize();
286#else
287 wxSize bmpSize = m_bitmap.GetPreferredBitmapSizeFor( this );
288#endif
289 wxBitmap bmp = m_bitmap.GetBitmapFor( this );
290 wxMemoryDC mdc( bmp );
291
292 r1.x = ( width - bmpSize.GetWidth() ) / 2;
293
294 if( r1.x < 0 )
295 r1.x = 0;
296
297 r1.y += ( size.GetHeight() - bmpSize.GetHeight() ) / 2;
298
299 dc.Blit( wxPoint( r1.x, r1.y ), bmpSize, &mdc, wxPoint( 0, 0 ), wxCOPY, true );
300 }
301 else
302 {
303 r1.y += ( ( size.GetHeight() - GetCharHeight() ) / 2 ) - 1;
304 dc.DrawLabel( m_label, r1, wxALIGN_CENTER_HORIZONTAL );
305 }
306
307 // Draw second part of button
308 wxRect r2;
309 r2.x = width;
310 r2.y = 0;
311 r2.width = m_arrowButtonWidth;
312 r2.height = size.GetHeight();
313
314#ifdef __WXMAC__
315 // wxRendereNative doesn't handle dark mode on OSX.
316 drawBackground( r2 );
317#else
318 r2.x -= 2;
319 wxRendererNative::Get().DrawPushButton( this, dc, r2, m_stateMenu );
320#endif
321
322 wxRendererNative::Get().DrawDropArrow( this, dc, r2, m_stateMenu );
323}
324
325
326bool SPLIT_BUTTON::Enable( bool aEnable )
327{
328 m_bIsEnable = aEnable;
329 wxPanel::Enable( m_bIsEnable );
330
331 if( m_bIsEnable
332 && ( m_stateButton == wxCONTROL_DISABLED || m_stateMenu == wxCONTROL_DISABLED ) )
333 {
334 m_stateButton = 0;
335 m_stateMenu = 0;
336 Refresh();
337 }
338
339 if( !m_bIsEnable
340 && ( m_stateButton != wxCONTROL_DISABLED || m_stateMenu != wxCONTROL_DISABLED ) )
341 {
342 m_stateButton = wxCONTROL_DISABLED;
343 m_stateMenu = wxCONTROL_DISABLED;
344 Refresh();
345 }
346
347 return aEnable;
348}
int m_arrowButtonWidth
void SetLabel(const wxString &aLabel) override
wxString m_label
void onThemeChanged(wxSysColourChangedEvent &aEvent)
bool m_bLButtonDown
void OnLeftButtonUp(wxMouseEvent &aEvent)
void OnKillFocus(wxFocusEvent &aEvent)
void OnMouseLeave(wxMouseEvent &aEvent)
wxBitmapBundle m_bitmap
void SetBitmap(const wxBitmapBundle &aBmp)
void SetWidthPadding(int aPadding)
wxMenu * GetSplitButtonMenu()
wxMenu * m_pMenu
void OnLeftButtonDown(wxMouseEvent &aEvent)
void SetMinSize(const wxSize &aSize) override
bool Enable(bool aEnable=true) override
wxSize m_unadjustedMinSize
void OnMouseEnter(wxMouseEvent &aEvent)
void OnPaint(wxPaintEvent &WXUNUSED(aEvent))
SPLIT_BUTTON(wxWindow *aParent, wxWindowID aId, const wxString &aLabel, const wxPoint &aPos=wxDefaultPosition, const wxSize &aSize=wxDefaultSize)
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:50