KiCad PCB EDA Suite
Loading...
Searching...
No Matches
wx_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 The 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
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <kiplatform/ui.h>
21#include <widgets/ui_common.h>
23
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
42
43
44bool WX_COLLAPSIBLE_PANE::Create( wxWindow* aParent, wxWindowID aId, const wxString& aLabel )
45{
46 if( !wxControl::Create( aParent, aId, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE, wxDefaultValidator,
47 wxT( "COLLAPSIBLE_PANE_HEADER" ) ) )
48 {
49 return false;
50 }
51
52 m_sizer = new wxBoxSizer( wxVERTICAL );
53
54 m_header = new WX_COLLAPSIBLE_PANE_HEADER( this, wxID_ANY, aLabel );
55
56 m_sizer->Add( m_header );
57
58 m_pane = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER,
59 wxT( "COLLAPSIBLE_PANE_PANEL" ) );
60
61 m_pane->Hide();
62
63 Bind( wxEVT_SIZE, &WX_COLLAPSIBLE_PANE::onSize, 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 + GetMargin() + GetMargin() + 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 + GetMargin();
169 m_pane->SetSize( GetMargin(), yoffset, size.x - ( GetMargin() * 2 ), size.y - ( GetMargin() + 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 const wxString& aLabel ) :
216 m_parent( aParent )
217{
218 init();
219
220 Create( aParent, aId, aLabel );
221}
222
223
225{
226 m_collapsed = true;
227 m_inWindow = false;
228}
229
230
231bool WX_COLLAPSIBLE_PANE_HEADER::Create( wxWindow* aParent, wxWindowID aId, const wxString& aLabel )
232{
233 if( !wxControl::Create( aParent, aId, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE, wxDefaultValidator,
234 wxT( "COLLAPSIBLE_PANE_HEADER" ) ) )
235 {
236 return false;
237 }
238
239 SetLabel( aLabel );
240
241 Bind( wxEVT_PAINT, &WX_COLLAPSIBLE_PANE_HEADER::onPaint, this );
242 Bind( wxEVT_SET_FOCUS, &WX_COLLAPSIBLE_PANE_HEADER::onFocus, this );
243 Bind( wxEVT_KILL_FOCUS, &WX_COLLAPSIBLE_PANE_HEADER::onFocus, this );
244 Bind( wxEVT_ENTER_WINDOW, &WX_COLLAPSIBLE_PANE_HEADER::onEnterWindow, this);
245 Bind( wxEVT_LEAVE_WINDOW, &WX_COLLAPSIBLE_PANE_HEADER::onLeaveWindow, this);
246 Bind( wxEVT_LEFT_UP, &WX_COLLAPSIBLE_PANE_HEADER::onLeftUp, this );
247 Bind( wxEVT_CHAR, &WX_COLLAPSIBLE_PANE_HEADER::onChar, this );
248
249 return true;
250}
251
252
254{
255 m_collapsed = aCollapsed;
256 Refresh();
257}
258
259
261{
262 SetCollapsed( aCollapsed );
263
264 wxCommandEvent evt( WX_COLLAPSIBLE_PANE_HEADER_CHANGED, GetId() );
265 evt.SetEventObject( this );
266 ProcessEvent( evt );
267}
268
269
271{
272 WX_COLLAPSIBLE_PANE_HEADER* self = const_cast<WX_COLLAPSIBLE_PANE_HEADER*>( this );
273
274 // The code here parallels that of OnPaint() -- except without drawing.
275 wxClientDC dc( self );
276 wxString text;
277
278 wxControl::FindAccelIndex( GetLabel(), &text );
279
280 wxSize size = dc.GetTextExtent( text );
281
282 // Reserve space for arrow (which is a square the height of the text)
283 size.x += size.GetHeight() + m_parent->GetMargin() * 2;
284 size.y += m_parent->GetMargin() * 2;
285
286#ifdef __WXMSW__
287 size.IncBy( GetSystemMetrics( SM_CXFOCUSBORDER ), GetSystemMetrics( SM_CYFOCUSBORDER ) );
288#endif // __WXMSW__
289
290 return size;
291}
292
293
294void WX_COLLAPSIBLE_PANE_HEADER::onPaint( wxPaintEvent& aEvent )
295{
296 wxPaintDC dc( this );
297 wxRect rect( wxPoint( 0, 0 ), GetClientSize() );
298
299#ifdef __WXMSW__
300 wxBrush brush = dc.GetBrush();
301 brush.SetColour( GetParent()->GetBackgroundColour() );
302 dc.SetBrush( brush );
303 dc.SetPen( *wxTRANSPARENT_PEN );
304 dc.DrawRectangle( rect );
305#endif
306
307 // Make the background look like an AUI toolbar button when the pointer is over it
308 if( m_inWindow )
309 {
310 bool darkMode = KIPLATFORM::UI::IsDarkTheme();
311 wxColor highlightColor = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT );
312
313 dc.SetPen( wxPen( highlightColor ) );
314 dc.SetBrush( wxBrush( highlightColor.ChangeLightness( darkMode ? 40 : 170 ) ) );
315 dc.DrawRectangle( rect );
316 }
317
318 wxString text;
319 int indexAccel = wxControl::FindAccelIndex( GetLabel(), &text );
320
321 // Compute all the sizes
322 wxSize textSize = dc.GetTextExtent( text );
323 wxRect arrowRect( 0, 0, textSize.GetHeight(), textSize.GetHeight() );
324 wxRect textRect( arrowRect.GetRight() + m_parent->GetMargin(), 0, textSize.GetWidth(), textSize.GetHeight() );
325
326 arrowRect = arrowRect.CenterIn( rect, wxVERTICAL );
327 textRect = textRect.CenterIn( rect, wxVERTICAL );
328
329 // Find out if the window we are in is active or not
330 bool isActive = true;
331 wxTopLevelWindow* tlw = dynamic_cast<wxTopLevelWindow*>( wxGetTopLevelParent( this ) );
332
333 if( tlw && !tlw->IsActive() )
334 isActive = false;
335
336 // Draw the arrow
337 drawArrow( dc, arrowRect, isActive );
338
339 // We are responsible for showing the text as disabled when the window isn't active
340 wxColour clr = isActive ? wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT )
341 : wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT );
342
343 dc.SetTextForeground( clr );
344 dc.DrawLabel( text, textRect, wxALIGN_CENTER_VERTICAL, indexAccel );
345
346#ifdef __WXMSW__
347 int flags = 0;
348
349 if( m_inWindow )
350 flags |= wxCONTROL_CURRENT;
351
352 int focusSize = GetSystemMetrics( SM_CXFOCUSBORDER );
353
354 if( HasFocus() )
355 wxRendererNative::Get().DrawFocusRect( this, dc, textRect.Inflate( focusSize ), flags );
356#endif
357}
358
359
360void WX_COLLAPSIBLE_PANE_HEADER::onFocus( wxFocusEvent& aEvent )
361{
362 Refresh();
363 aEvent.Skip();
364}
365
366
368{
369 m_inWindow = true;
370 Refresh();
371 aEvent.Skip();
372}
373
374
376{
377 m_inWindow = false;
378 Refresh();
379 aEvent.Skip();
380}
381
382
383void WX_COLLAPSIBLE_PANE_HEADER::onLeftUp( wxMouseEvent& aEvent )
384{
386 aEvent.Skip();
387}
388
389
390void WX_COLLAPSIBLE_PANE_HEADER::onChar( wxKeyEvent& aEvent )
391{
392 switch( aEvent.GetKeyCode() )
393 {
394 case WXK_SPACE:
395 case WXK_RETURN:
396 case WXK_NUMPAD_ENTER:
398 break;
399
400 default:
401 aEvent.Skip();
402 break;
403 }
404}
405
406
407void WX_COLLAPSIBLE_PANE_HEADER::drawArrow( wxDC& aDC, wxRect aRect, bool aIsActive )
408{
409 // The bottom corner of the triangle is located halfway across the area and 3/4 down from
410 // the top
411 wxPoint btmCorner( aRect.GetLeft() + aRect.GetWidth() / 2, aRect.GetTop() + 3 * aRect.GetHeight() / 4 );
412
413 // The right corner of the triangle is located halfway down from the top and 3/4 across the area
414 wxPoint rtCorner( aRect.GetLeft() + 3 * aRect.GetWidth() / 4, aRect.GetTop() + aRect.GetHeight() / 2 );
415
416 // Choose the other corner depending on if the panel is expanded or collapsed
417 wxPoint otherCorner( 0, 0 );
418
419 if( m_collapsed )
420 otherCorner = wxPoint( aRect.GetLeft() + aRect.GetWidth() / 2, aRect.GetTop() + aRect.GetHeight() / 4 );
421 else
422 otherCorner = wxPoint( aRect.GetLeft() + aRect.GetWidth() / 4, aRect.GetTop() + aRect.GetHeight() / 2 );
423
424 // Because it looks better
425 if( !m_collapsed )
426 {
427 btmCorner.y -= 1;
428 rtCorner.y -= 1;
429 otherCorner.y -= 1;
430 }
431
432 // Choose the color to draw the triangle
433 wxColour clr = aIsActive ? wxSystemSettings::GetColour( wxSYS_COLOUR_HOTLIGHT )
434 : wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT );
435
436 // Must set both the pen (for the outline) and the brush (for the polygon fill)
437 aDC.SetPen( wxPen( clr ) );
438 aDC.SetBrush( wxBrush( clr ) );
439
440 // Draw the triangle
441 wxPointList points;
442 points.Append( &btmCorner );
443 points.Append( &rtCorner );
444 points.Append( &otherCorner );
445
446 aDC.DrawPolygon( &points );
447}
A header control for WX_COLLAPSIBLE_PANE.
WX_COLLAPSIBLE_PANE * m_parent
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)
WX_COLLAPSIBLE_PANE_HEADER(WX_COLLAPSIBLE_PANE *aParent, wxWindowID aId, const wxString &aLabel)
bool Create(wxWindow *aParent, wxWindowID aId, const wxString &aLabel)
A better wxCollapsiblePane that.
wxSize DoGetBestClientSize() const override
void Collapse(bool aCollapse=true)
void onSize(wxSizeEvent &aEvent)
bool Create(wxWindow *aParent, wxWindowID aId, const wxString &aLabel)
void onHeaderClicked(wxCommandEvent &aEvent)
WX_COLLAPSIBLE_PANE_HEADER * m_header
void SetLabel(const wxString &aLabel) override
bool InformFirstDirection(int aDirection, int aSize, int aAvailableOtherDir) override
bool SetBackgroundColour(const wxColour &aColor) override
bool IsDarkTheme()
Determine if the desktop interface is currently using a dark theme or a light theme.
Definition wxgtk/ui.cpp:51
Functions to provide common constants and other functions to assist in making a consistent UI.
wxDEFINE_EVENT(WX_COLLAPSIBLE_PANE_HEADER_CHANGED, wxCommandEvent)
const KICOMMON_API wxEventTypeTag< wxCommandEvent > WX_COLLAPSIBLE_PANE_CHANGED
const KICOMMON_API wxEventTypeTag< wxCommandEvent > WX_COLLAPSIBLE_PANE_HEADER_CHANGED