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