KiCad PCB EDA Suite
collapsible_pane.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 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <bitmaps.h>
22
23#include <wx/collpane.h>
24#include <wx/dc.h>
25#include <wx/dcclient.h>
26#include <wx/panel.h>
27#include <wx/renderer.h>
28#include <wx/settings.h>
29#include <wx/sizer.h>
30#include <wx/toplevel.h>
31#include <wx/window.h>
32
33#ifdef _WIN32
34#include <windows.h>
35#endif
36
37#include <algorithm>
38
39
40wxDEFINE_EVENT( WX_COLLAPSIBLE_PANE_HEADER_CHANGED, wxCommandEvent );
41wxDEFINE_EVENT( WX_COLLAPSIBLE_PANE_CHANGED, wxCommandEvent );
42
43
44bool WX_COLLAPSIBLE_PANE:: Create( wxWindow* aParent, wxWindowID aId, const wxString& aLabel,
45 const wxPoint& aPos, const wxSize& aSize, long aStyle,
46 const wxValidator& aValidator, const wxString& aName )
47{
48 if( !wxControl::Create( aParent, aId, aPos, aSize, aStyle, aValidator, aName ) )
49 return false;
50
51 m_sizer = new wxBoxSizer( wxVERTICAL );
52
53 m_header = new WX_COLLAPSIBLE_PANE_HEADER( this, wxID_ANY, aLabel, wxPoint( 0, 0 ),
54 wxDefaultSize );
55
56 m_sizer->Add( m_header, wxSizerFlags().Border( wxBOTTOM, getBorder() ) );
57
58 m_pane = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
59 wxTAB_TRAVERSAL | wxNO_BORDER, wxT( "COLLAPSIBLE_PANE_PANE" ) );
60
61 m_pane->Hide();
62
63 Bind( wxEVT_SIZE, &WX_COLLAPSIBLE_PANE::onSize, this );
64 Bind( WX_COLLAPSIBLE_PANE_HEADER_CHANGED, &WX_COLLAPSIBLE_PANE::onHeaderClicked, this );
65
66 return true;
67}
68
69
71{
72 m_pane = nullptr;
73 m_sizer = nullptr;
74 m_header = nullptr;
75}
76
77
79{
80 if( m_sizer )
81 m_sizer->SetContainingWindow( nullptr );
82
83 // Not owned by wxWindow
84 delete m_sizer;
85}
86
87
88void WX_COLLAPSIBLE_PANE::Collapse( bool aCollapse )
89{
90 if( IsCollapsed() == aCollapse )
91 return;
92
93 InvalidateBestSize();
94
95 m_pane->Show( !aCollapse );
96 m_header->SetCollapsed( aCollapse );
97
98 SetSize( GetBestSize() );
99}
100
101
103{
104 return !m_pane || !m_pane->IsShown();
105}
106
107
108void WX_COLLAPSIBLE_PANE::SetLabel( const wxString& aLabel )
109{
110 m_header->SetLabel( aLabel );
111 m_header->SetInitialSize();
112
113 Layout();
114}
115
116
117bool WX_COLLAPSIBLE_PANE::SetBackgroundColour( const wxColour& aColor )
118{
119 m_header->SetBackgroundColour( aColor );
120 return wxWindow::SetBackgroundColour( aColor );
121}
122
123
124bool WX_COLLAPSIBLE_PANE::InformFirstDirection( int aDirection, int aSize, int aAvailableOtherDir )
125{
126 wxWindow* const pane = GetPane();
127
128 if( !pane )
129 return false;
130
131 if( !pane->InformFirstDirection( aDirection, aSize, aAvailableOtherDir ) )
132 return false;
133
134 InvalidateBestSize();
135
136 return true;
137}
138
139
141{
142 wxSize size = m_sizer->GetMinSize();
143
144 if( IsExpanded() )
145 {
146 wxSize paneSize = m_pane->GetBestSize();
147
148 size.SetWidth( std::max( size.GetWidth(), paneSize.x ) );
149 size.SetHeight( size.y + getBorder() + paneSize.y );
150 }
151
152 return size;
153}
154
155
157{
158 if( !m_sizer || !m_pane || !m_header )
159 return false;
160
161 wxSize size( GetSize() );
162
163 m_sizer->SetDimension( 0, 0, size.x, m_sizer->GetMinSize().y );
164 m_sizer->Layout();
165
166 if( IsExpanded() )
167 {
168 int yoffset = m_sizer->GetSize().y + getBorder();
169 m_pane->SetSize( 0, yoffset, size.x, size.y - yoffset );
170 m_pane->Layout();
171 }
172
173 return true;
174}
175
176
178{
179#if defined( __WXMSW__ )
180 wxASSERT( m_header );
181 return m_header->ConvertDialogToPixels( wxSize( 2, 0 ) ).x;
182#else
183 return 3;
184#endif
185}
186
187
188void WX_COLLAPSIBLE_PANE::onSize( wxSizeEvent& aEvent )
189{
190 Layout();
191 aEvent.Skip();
192}
193
194
195void WX_COLLAPSIBLE_PANE::onHeaderClicked( wxCommandEvent& aEvent )
196{
197 if( aEvent.GetEventObject() != m_header )
198 {
199 aEvent.Skip();
200 return;
201 }
202
203 Collapse( !IsCollapsed() );
204
205 wxCommandEvent evt( WX_COLLAPSIBLE_PANE_CHANGED, GetId() );
206 evt.SetEventObject( this );
207 ProcessEvent( evt );
208}
209
210
211// WX_COLLAPSIBLE_PANE_HEADER implementation
212
213
215{
216 m_collapsed = true;
217 m_inWindow = false;
218}
219
220
221bool WX_COLLAPSIBLE_PANE_HEADER::Create( wxWindow* aParent, wxWindowID aId, const wxString& aLabel,
222 const wxPoint& aPos, const wxSize& aSize, long aStyle,
223 const wxValidator& aValidator, const wxString& aName )
224{
225 if ( !wxControl::Create( aParent, aId, aPos, aSize, aStyle, aValidator, aName ) )
226 return false;
227
228 SetLabel( aLabel );
229
230 Bind( wxEVT_PAINT, &WX_COLLAPSIBLE_PANE_HEADER::onPaint, this );
231 Bind( wxEVT_SET_FOCUS, &WX_COLLAPSIBLE_PANE_HEADER::onFocus, this );
232 Bind( wxEVT_KILL_FOCUS, &WX_COLLAPSIBLE_PANE_HEADER::onFocus, this );
233 Bind( wxEVT_ENTER_WINDOW, &WX_COLLAPSIBLE_PANE_HEADER::onEnterWindow, this);
234 Bind( wxEVT_LEAVE_WINDOW, &WX_COLLAPSIBLE_PANE_HEADER::onLeaveWindow, this);
235 Bind( wxEVT_LEFT_UP, &WX_COLLAPSIBLE_PANE_HEADER::onLeftUp, this );
236 Bind( wxEVT_CHAR, &WX_COLLAPSIBLE_PANE_HEADER::onChar, this );
237
238 return true;
239}
240
241
243{
244 m_collapsed = aCollapsed;
245 Refresh();
246}
247
248
250{
251 SetCollapsed( aCollapsed );
252
253 wxCommandEvent evt( WX_COLLAPSIBLE_PANE_HEADER_CHANGED, GetId() );
254 evt.SetEventObject( this );
255 ProcessEvent( evt );
256}
257
258
260{
261 WX_COLLAPSIBLE_PANE_HEADER* self = const_cast<WX_COLLAPSIBLE_PANE_HEADER*>( this );
262
263 // The code here parallels that of OnPaint() -- except without drawing.
264 wxClientDC dc( self );
265 wxString text;
266
267 wxControl::FindAccelIndex( GetLabel(), &text );
268
269 wxSize size = dc.GetTextExtent( text );
270
271 // Reserve space for arrow (which is a square the height of the text)
272 size.x += size.GetHeight();
273
274#ifdef __WXMSW__
275 size.IncBy( GetSystemMetrics( SM_CXFOCUSBORDER ),
276 GetSystemMetrics( SM_CYFOCUSBORDER ) );
277#endif // __WXMSW__
278
279 return size;
280}
281
282
283void WX_COLLAPSIBLE_PANE_HEADER::onPaint( wxPaintEvent& aEvent )
284{
285 wxPaintDC dc( this );
286 wxRect rect( wxPoint( 0, 0 ), GetClientSize() );
287
288#ifdef __WXMSW__
289 wxBrush brush = dc.GetBrush();
290 brush.SetColour( GetParent()->GetBackgroundColour() );
291 dc.SetBrush( brush );
292 dc.SetPen( *wxTRANSPARENT_PEN );
293 dc.DrawRectangle( rect );
294#endif
295
296 // Make the background look like a button when the pointer is over it
297 if( m_inWindow )
298 {
299 dc.SetBrush( wxBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNHIGHLIGHT ) ) );
300 dc.SetPen( *wxTRANSPARENT_PEN );
301 dc.DrawRectangle( rect );
302 }
303
304 wxString text;
305 int indexAccel = wxControl::FindAccelIndex( GetLabel(), &text );
306
307 wxSize textSize = dc.GetTextExtent( text );
308
309 // Compute all the sizes
310 wxRect arrowRect( 0, 0, textSize.GetHeight(), textSize.GetHeight() );
311 wxRect textRect( arrowRect.GetTopRight(), textSize );
312 textRect = textRect.CenterIn( rect, wxVERTICAL );
313
314 // Find out if the window we are in is active or not
315 bool isActive = true;
316 wxTopLevelWindow* tlw = dynamic_cast<wxTopLevelWindow*>( wxGetTopLevelParent( this ) );
317
318 if( tlw && !tlw->IsActive() )
319 isActive = false;
320
321 // Draw the arrow
322 drawArrow( dc, arrowRect, isActive );
323
324 // We are responsible for showing the text as disabled when the window isn't active
325 wxColour clr;
326
327 if( isActive )
328 clr = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
329 else
330 clr = wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT );
331
332 dc.SetTextForeground( clr );
333 dc.DrawLabel( text, textRect, wxALIGN_CENTER_VERTICAL, indexAccel );
334
335#ifdef __WXMSW__
336 int flags = 0;
337
338 if( m_inWindow )
339 flags |= wxCONTROL_CURRENT;
340
341 int focusSize = GetSystemMetrics( SM_CXFOCUSBORDER );
342
343 if( HasFocus() )
344 wxRendererNative::Get().DrawFocusRect( this, dc, textRect.Inflate( focusSize ), flags );
345#endif
346}
347
348
349void WX_COLLAPSIBLE_PANE_HEADER::onFocus( wxFocusEvent& aEvent )
350{
351 Refresh();
352 aEvent.Skip();
353}
354
355
357{
358 m_inWindow = true;
359 Refresh();
360 aEvent.Skip();
361}
362
363
365{
366 m_inWindow = false;
367 Refresh();
368 aEvent.Skip();
369}
370
371
372void WX_COLLAPSIBLE_PANE_HEADER::onLeftUp( wxMouseEvent& aEvent )
373{
375 aEvent.Skip();
376}
377
378
379void WX_COLLAPSIBLE_PANE_HEADER::onChar( wxKeyEvent& aEvent )
380{
381 switch( aEvent.GetKeyCode() )
382 {
383 case WXK_SPACE:
384 case WXK_RETURN:
386 break;
387
388 default:
389 aEvent.Skip();
390 break;
391 }
392}
393
394
395void WX_COLLAPSIBLE_PANE_HEADER::drawArrow( wxDC& aDC, wxRect aRect, bool aIsActive )
396{
397 // The bottom corner of the triangle is located halfway across the area and 3/4 down from the top
398 wxPoint btmCorner( aRect.GetWidth() / 2, 3 * aRect.GetHeight() / 4 );
399
400 // The right corner of the triangle is located halfway down from the top and 3/4 across the area
401 wxPoint rtCorner( 3 * aRect.GetWidth() / 4, aRect.GetHeight() / 2 );
402
403 // Choose the other corner depending on if the panel is expanded or collapsed
404 wxPoint otherCorner( 0, 0 );
405
406 if( m_collapsed )
407 otherCorner = wxPoint( aRect.GetWidth() / 2, aRect.GetHeight() / 4 );
408 else
409 otherCorner = wxPoint( aRect.GetWidth() / 4, aRect.GetHeight() / 2 );
410
411 // Choose the color to draw the triangle
412 wxColour clr;
413
414 // Highlight the arrow when the pointer is inside the header, otherwise use text color
415 if( m_inWindow )
416 clr = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT );
417 else
418 clr = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
419
420 // If the window isn't active, then use the disabled text color
421 if( !aIsActive )
422 clr = wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT );
423
424 // Must set both the pen (for the outline) and the brush (for the polygon fill)
425 aDC.SetPen( wxPen( clr ) );
426 aDC.SetBrush( wxBrush( clr ) );
427
428 // Draw the triangle
429 wxPointList points;
430 points.Append( &btmCorner );
431 points.Append( &rtCorner );
432 points.Append( &otherCorner );
433
434 aDC.DrawPolygon( &points );
435}
A header control for WX_COLLAPSIBLE_PANE Looks like a static text with a unicode arrow prepended to s...
void drawArrow(wxDC &aDC, wxRect aRect, bool aIsActive)
void onLeaveWindow(wxMouseEvent &aEvent)
void onFocus(wxFocusEvent &aEvent)
void onChar(wxKeyEvent &aEvent)
void SetCollapsed(bool aCollapsed=true)
void onPaint(wxPaintEvent &aEvent)
wxSize DoGetBestClientSize() const override
void doSetCollapsed(bool aCollapsed)
void onEnterWindow(wxMouseEvent &aEvent)
void onLeftUp(wxMouseEvent &aEvent)
bool Create(wxWindow *aParent, wxWindowID aId, const wxString &aLabel, const wxPoint &aPos=wxDefaultPosition, const wxSize &aSize=wxDefaultSize, long aStyle=wxBORDER_NONE, const wxValidator &aValidator=wxDefaultValidator, const wxString &aName=wxT("COLLAPSIBLE_PANE_HEADER"))
wxSize DoGetBestClientSize() const override
void Collapse(bool aCollapse=true)
void onSize(wxSizeEvent &aEvent)
void onHeaderClicked(wxCommandEvent &aEvent)
bool Layout() override
WX_COLLAPSIBLE_PANE_HEADER * m_header
void SetLabel(const wxString &aLabel) override
bool InformFirstDirection(int aDirection, int aSize, int aAvailableOtherDir) override
bool Create(wxWindow *aParent, wxWindowID aId, const wxString &aLabel, const wxPoint &aPos=wxDefaultPosition, const wxSize &aSize=wxDefaultSize, long aStyle=wxBORDER_NONE, const wxValidator &aValidator=wxDefaultValidator, const wxString &aName=wxT("COLLAPSIBLE_PANE_HEADER"))
bool SetBackgroundColour(const wxColour &aColor) override
wxDEFINE_EVENT(WX_COLLAPSIBLE_PANE_HEADER_CHANGED, wxCommandEvent)
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...