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/renderer.h>
25 
26 #include <algorithm>
27 
28 
29 wxDEFINE_EVENT( WX_COLLAPSIBLE_PANE_HEADER_CHANGED, wxCommandEvent );
30 wxDEFINE_EVENT( WX_COLLAPSIBLE_PANE_CHANGED, wxCommandEvent );
31 
32 
33 bool WX_COLLAPSIBLE_PANE:: Create( wxWindow* aParent, wxWindowID aId, const wxString& aLabel,
34  const wxPoint& aPos, const wxSize& aSize, long aStyle,
35  const wxValidator& aValidator, const wxString& aName )
36 {
37  if( !wxControl::Create( aParent, aId, aPos, aSize, aStyle, aValidator, aName ) )
38  return false;
39 
40  m_sizer = new wxBoxSizer( wxVERTICAL );
41 
42  m_header = new WX_COLLAPSIBLE_PANE_HEADER( this, wxID_ANY, aLabel, wxPoint( 0, 0 ),
43  wxDefaultSize );
44 
45  m_sizer->Add( m_header, wxSizerFlags().Border( wxBOTTOM, getBorder() ) );
46 
47  m_pane = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
48  wxTAB_TRAVERSAL | wxNO_BORDER, wxT( "COLLAPSIBLE_PANE_PANE" ) );
49 
50  m_pane->Hide();
51 
52  Bind( wxEVT_SIZE, &WX_COLLAPSIBLE_PANE::onSize, this );
53  Bind( WX_COLLAPSIBLE_PANE_HEADER_CHANGED, &WX_COLLAPSIBLE_PANE::onHeaderClicked, this );
54 
55  return true;
56 }
57 
58 
60 {
61  m_pane = nullptr;
62  m_sizer = nullptr;
63  m_header = nullptr;
64 }
65 
66 
68 {
69  if( m_sizer )
70  m_sizer->SetContainingWindow( nullptr );
71 
72  // Not owned by wxWindow
73  delete m_sizer;
74 }
75 
76 
77 void WX_COLLAPSIBLE_PANE::Collapse( bool aCollapse )
78 {
79  if( IsCollapsed() == aCollapse )
80  return;
81 
82  InvalidateBestSize();
83 
84  m_pane->Show( !aCollapse );
85  m_header->SetCollapsed( aCollapse );
86 
87  SetSize( GetBestSize() );
88 }
89 
90 
92 {
93  return !m_pane || !m_pane->IsShown();
94 }
95 
96 
97 void WX_COLLAPSIBLE_PANE::SetLabel( const wxString& aLabel )
98 {
99  m_header->SetLabel( aLabel );
100  m_header->SetInitialSize();
101 
102  Layout();
103 }
104 
105 
106 bool WX_COLLAPSIBLE_PANE::SetBackgroundColour( const wxColour& aColor )
107 {
108  m_header->SetBackgroundColour( aColor );
109  return wxWindow::SetBackgroundColour( aColor );
110 }
111 
112 
113 bool WX_COLLAPSIBLE_PANE::InformFirstDirection( int aDirection, int aSize, int aAvailableOtherDir )
114 {
115  wxWindow* const pane = GetPane();
116 
117  if( !pane )
118  return false;
119 
120  if( !pane->InformFirstDirection( aDirection, aSize, aAvailableOtherDir ) )
121  return false;
122 
123  InvalidateBestSize();
124 
125  return true;
126 }
127 
128 
130 {
131  wxSize size = m_sizer->GetMinSize();
132 
133  if( IsExpanded() )
134  {
135  wxSize paneSize = m_pane->GetBestSize();
136 
137  size.SetWidth( std::max( size.GetWidth(), paneSize.x ) );
138  size.SetHeight( size.y + getBorder() + paneSize.y );
139  }
140 
141  return size;
142 }
143 
144 
146 {
147  if( !m_sizer || !m_pane || !m_header )
148  return false;
149 
150  wxSize size( GetSize() );
151 
152  m_sizer->SetDimension( 0, 0, size.x, m_sizer->GetMinSize().y );
153  m_sizer->Layout();
154 
155  if( IsExpanded() )
156  {
157  int yoffset = m_sizer->GetSize().y + getBorder();
158  m_pane->SetSize( 0, yoffset, size.x, size.y - yoffset );
159  m_pane->Layout();
160  }
161 
162  return true;
163 }
164 
165 
167 {
168 #if defined( __WXMSW__ )
169  wxASSERT( m_header );
170  return m_header->ConvertDialogToPixels( wxSize( 2, 0 ) ).x;
171 #else
172  return 3;
173 #endif
174 }
175 
176 
177 void WX_COLLAPSIBLE_PANE::onSize( wxSizeEvent& aEvent )
178 {
179  Layout();
180  aEvent.Skip();
181 }
182 
183 
184 void WX_COLLAPSIBLE_PANE::onHeaderClicked( wxCommandEvent& aEvent )
185 {
186  if( aEvent.GetEventObject() != m_header )
187  {
188  aEvent.Skip();
189  return;
190  }
191 
192  Collapse( !IsCollapsed() );
193 
194  wxCommandEvent evt( WX_COLLAPSIBLE_PANE_CHANGED, GetId() );
195  evt.SetEventObject( this );
196  ProcessEvent( evt );
197 }
198 
199 
200 // WX_COLLAPSIBLE_PANE_HEADER implementation
201 
202 
204 {
205  m_collapsed = true;
206  m_inWindow = false;
207 }
208 
209 
210 bool WX_COLLAPSIBLE_PANE_HEADER::Create( wxWindow* aParent, wxWindowID aId, const wxString& aLabel,
211  const wxPoint& aPos, const wxSize& aSize, long aStyle,
212  const wxValidator& aValidator, const wxString& aName )
213 {
214  if ( !wxControl::Create( aParent, aId, aPos, aSize, aStyle, aValidator, aName ) )
215  return false;
216 
219 
220  SetLabel( aLabel );
221 
222  Bind( wxEVT_PAINT, &WX_COLLAPSIBLE_PANE_HEADER::onPaint, this );
223  Bind( wxEVT_SET_FOCUS, &WX_COLLAPSIBLE_PANE_HEADER::onFocus, this );
224  Bind( wxEVT_KILL_FOCUS, &WX_COLLAPSIBLE_PANE_HEADER::onFocus, this );
225  Bind( wxEVT_ENTER_WINDOW, &WX_COLLAPSIBLE_PANE_HEADER::onEnterWindow, this);
226  Bind( wxEVT_LEAVE_WINDOW, &WX_COLLAPSIBLE_PANE_HEADER::onLeaveWindow, this);
227  Bind( wxEVT_LEFT_UP, &WX_COLLAPSIBLE_PANE_HEADER::onLeftUp, this );
228  Bind( wxEVT_CHAR, &WX_COLLAPSIBLE_PANE_HEADER::onChar, this );
229 
230  return true;
231 }
232 
233 
235 {
236  m_collapsed = aCollapsed;
237  Refresh();
238 }
239 
240 
242 {
243  SetCollapsed( aCollapsed );
244 
245  wxCommandEvent evt( WX_COLLAPSIBLE_PANE_HEADER_CHANGED, GetId() );
246  evt.SetEventObject( this );
247  ProcessEvent( evt );
248 }
249 
250 
252 {
253  WX_COLLAPSIBLE_PANE_HEADER* self = const_cast<WX_COLLAPSIBLE_PANE_HEADER*>( this );
254 
255  // The code here parallels that of OnPaint() -- except without drawing.
256  wxClientDC dc( self );
257  wxString text;
258 
259  wxControl::FindAccelIndex( GetLabel(), &text );
260 
261  wxSize size = dc.GetTextExtent( text );
262 
263  // Reserve space for icon
264  size.x += m_iconRight.GetWidth();
265  size.y = std::max( size.y, m_iconRight.GetHeight() );
266 
267 #ifdef __WXMSW__
268  size.IncBy( GetSystemMetrics( SM_CXFOCUSBORDER ),
269  GetSystemMetrics( SM_CYFOCUSBORDER ) );
270 #endif // __WXMSW__
271 
272  return size;
273 }
274 
275 
276 void WX_COLLAPSIBLE_PANE_HEADER::onPaint( wxPaintEvent& aEvent )
277 {
278  wxPaintDC dc( this );
279  wxRect rect( wxPoint( 0, 0 ), GetClientSize() );
280 
281 #ifdef __WXMSW__
282  wxBrush brush = dc.GetBrush();
283  brush.SetColour( GetParent()->GetBackgroundColour() );
284  dc.SetBrush( brush );
285  dc.SetPen( *wxTRANSPARENT_PEN );
286  dc.DrawRectangle( rect );
287 #endif
288 
289  wxString text;
290  int indexAccel = wxControl::FindAccelIndex( GetLabel(), &text );
291 
292  wxSize textSize = dc.GetTextExtent( text );
293  wxRect textRect( wxPoint( m_iconRight.GetWidth(), 0 ), textSize );
294  textRect = textRect.CenterIn( rect, wxVERTICAL );
295 
296  wxBitmap* icon = m_collapsed ? &m_iconRight : &m_iconDown;
297 
298  if( m_inWindow )
299  {
300  dc.SetTextForeground( wxSystemColour::wxSYS_COLOUR_HOTLIGHT );
301  dc.DrawBitmap( icon->ConvertToDisabled( 200 ), wxPoint( 0, 0 ) );
302  }
303  else
304  {
305  dc.DrawBitmap( *icon, wxPoint( 0, 0 ) );
306  }
307 
308  dc.DrawLabel( text, textRect, wxALIGN_CENTER_VERTICAL, indexAccel );
309 
310 #ifdef __WXMSW__
311  int flags = 0;
312 
313  if( m_inWindow )
314  flags |= wxCONTROL_CURRENT;
315 
316  int focusSize = GetSystemMetrics( SM_CXFOCUSBORDER );
317 
318  if( HasFocus() )
319  wxRendererNative::Get().DrawFocusRect( this, dc, textRect.Inflate( focusSize ), flags );
320 #endif
321 }
322 
323 
324 void WX_COLLAPSIBLE_PANE_HEADER::onFocus( wxFocusEvent& aEvent )
325 {
326  Refresh();
327  aEvent.Skip();
328 }
329 
330 
331 void WX_COLLAPSIBLE_PANE_HEADER::onEnterWindow( wxMouseEvent& aEvent )
332 {
333  m_inWindow = true;
334  Refresh();
335  aEvent.Skip();
336 }
337 
338 
339 void WX_COLLAPSIBLE_PANE_HEADER::onLeaveWindow( wxMouseEvent& aEvent )
340 {
341  m_inWindow = false;
342  Refresh();
343  aEvent.Skip();
344 }
345 
346 
347 void WX_COLLAPSIBLE_PANE_HEADER::onLeftUp( wxMouseEvent& aEvent )
348 {
350  aEvent.Skip();
351 }
352 
353 
354 void WX_COLLAPSIBLE_PANE_HEADER::onChar( wxKeyEvent& aEvent )
355 {
356  switch( aEvent.GetKeyCode() )
357  {
358  case WXK_SPACE:
359  case WXK_RETURN:
361  break;
362 
363  default:
364  aEvent.Skip();
365  break;
366  }
367 }
bool SetBackgroundColour(const wxColour &aColor) 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"))
void onFocus(wxFocusEvent &aEvent)
A header control for WX_COLLAPSIBLE_PANE Looks like a static text with a unicode arrow prepended to s...
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"))
const BITMAP_OPAQUE triangle_down_xpm[1]
void doSetCollapsed(bool aCollapsed)
bool InformFirstDirection(int aDirection, int aSize, int aAvailableOtherDir) override
void onEnterWindow(wxMouseEvent &aEvent)
void onHeaderClicked(wxCommandEvent &aEvent)
void SetCollapsed(bool aCollapsed=true)
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Construct a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:82
WX_COLLAPSIBLE_PANE_HEADER * m_header
const BITMAP_OPAQUE triangle_right_xpm[1]
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
void onPaint(wxPaintEvent &aEvent)
wxSize DoGetBestClientSize() const override
wxDEFINE_EVENT(WX_COLLAPSIBLE_PANE_HEADER_CHANGED, wxCommandEvent)
bool Layout() override
void onLeaveWindow(wxMouseEvent &aEvent)
void onChar(wxKeyEvent &aEvent)
void onSize(wxSizeEvent &aEvent)
void SetLabel(const wxString &aLabel) override
void Collapse(bool aCollapse=true)
void onLeftUp(wxMouseEvent &aEvent)
wxSize DoGetBestClientSize() const override