KiCad PCB EDA Suite
Loading...
Searching...
No Matches
wx_infobar.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 Ian McInerney <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <id.h>
22#include <kiplatform/ui.h>
23#include <widgets/wx_infobar.h>
24#include "wx/artprov.h"
25#include <wx/aui/framemanager.h>
26#include <wx/bmpbuttn.h>
27#include <wx/debug.h>
28#include <wx/hyperlink.h>
29#include <wx/infobar.h>
30#include <wx/sizer.h>
31#include <wx/stattext.h>
32#include <wx/timer.h>
33#include <wx/dcclient.h>
34#include <eda_base_frame.h>
35
36#ifdef __WXMSW__
37#include <dpi_scaling_common.h>
38#endif
39
40
41wxDEFINE_EVENT( KIEVT_SHOW_INFOBAR, wxCommandEvent );
42wxDEFINE_EVENT( KIEVT_DISMISS_INFOBAR, wxCommandEvent );
43
44BEGIN_EVENT_TABLE( WX_INFOBAR, wxInfoBarGeneric )
45 EVT_COMMAND( wxID_ANY, KIEVT_SHOW_INFOBAR, WX_INFOBAR::onShowInfoBar )
46 EVT_COMMAND( wxID_ANY, KIEVT_DISMISS_INFOBAR, WX_INFOBAR::onDismissInfoBar )
47
48 EVT_SYS_COLOUR_CHANGED( WX_INFOBAR::onThemeChange )
51END_EVENT_TABLE()
52
53
54WX_INFOBAR::WX_INFOBAR( wxWindow* aParent, wxAuiManager* aMgr, wxWindowID aWinid )
55 : wxInfoBarGeneric( aParent, aWinid ),
56 m_showTime( 0 ),
57 m_updateLock( false ),
58 m_showTimer( nullptr ),
59 m_auiManager( aMgr ),
61{
62 m_showTimer = new wxTimer( this, ID_CLOSE_INFOBAR );
63
64 wxColour fg, bg;
66 SetBackgroundColour( bg );
67 SetForegroundColour( fg );
68
69#ifdef __WXMAC__
70 // Infobar is broken on Mac without the effects
71 SetShowHideEffects( wxSHOW_EFFECT_ROLL_TO_BOTTOM, wxSHOW_EFFECT_ROLL_TO_TOP );
72 SetEffectDuration( 200 );
73#else
74 // Infobar freezes canvas on Windows with the effect, and GTK looks bad with it
75 SetShowHideEffects( wxSHOW_EFFECT_NONE, wxSHOW_EFFECT_NONE );
76#endif
77
78 // The infobar seems to start too small, so increase its height
79 int sx, sy;
80 GetSize( &sx, &sy );
81 sy = 1.5 * sy;
82
83 // The bitmap gets cutoff sometimes with the default size, so force it to be the same
84 // height as the infobar.
85 wxSizer* sizer = GetSizer();
86 wxSize iconSize = wxArtProvider::GetSizeHint( wxART_BUTTON );
87
88#ifdef __WXMSW__
89 DPI_SCALING_COMMON dpi( nullptr, aParent );
90 iconSize.x *= dpi.GetContentScaleFactor();
91 sx *= dpi.GetContentScaleFactor();
92 sy *= dpi.GetContentScaleFactor();
93#endif
94
95 SetSize( sx, sy );
96
97 sizer->SetItemMinSize( (size_t) 0, iconSize.x, sy );
98
99 // Forcefully remove all existing buttons added by the wx constructors.
100 // The default close button doesn't work with the AUI manager update scheme, so this
101 // ensures any close button displayed is ours.
103
104 Layout();
105
106 m_parent->Bind( wxEVT_SIZE, &WX_INFOBAR::onSize, this );
107}
108
109
111{
112 m_parent->Unbind( wxEVT_SIZE, &WX_INFOBAR::onSize, this );
113
114 delete m_showTimer;
115}
116
117
118void WX_INFOBAR::SetShowTime( int aTime )
119{
120 m_showTime = aTime;
121}
122
123
124void WX_INFOBAR::QueueShowMessage( const wxString& aMessage, int aFlags )
125{
126 wxCommandEvent* evt = new wxCommandEvent( KIEVT_SHOW_INFOBAR );
127
128 evt->SetString( aMessage.c_str() );
129 evt->SetInt( aFlags );
130
131 GetEventHandler()->QueueEvent( evt );
132}
133
134
136{
137 wxCommandEvent* evt = new wxCommandEvent( KIEVT_DISMISS_INFOBAR );
138
139 GetEventHandler()->QueueEvent( evt );
140}
141
142
143void WX_INFOBAR::ShowMessageFor( const wxString& aMessage, int aTime, int aFlags,
144 MESSAGE_TYPE aType )
145{
146 // Don't do anything if we requested the UI update
147 if( m_updateLock )
148 return;
149
150 m_showTime = aTime;
151 ShowMessage( aMessage, aFlags );
152
153 m_type = aType;
154}
155
156
157void WX_INFOBAR::ShowMessage( const wxString& aMessage, int aFlags )
158{
159 // Don't do anything if we requested the UI update
160 if( m_updateLock )
161 return;
162
163 m_updateLock = true;
164
165 m_message = aMessage;
166 m_message.Trim();
167
168 wxInfoBarGeneric::ShowMessage( m_message, aFlags );
169
170 if( m_auiManager )
171 updateAuiLayout( true );
172
173 if( m_showTime > 0 )
174 m_showTimer->StartOnce( m_showTime );
175
177 m_updateLock = false;
178}
179
180
181void WX_INFOBAR::ShowMessage( const wxString& aMessage, int aFlags, MESSAGE_TYPE aType )
182{
183 // Don't do anything if we requested the UI update
184 if( m_updateLock )
185 return;
186
187 ShowMessage( aMessage, aFlags );
188
189 m_type = aType;
190}
191
192
194{
195 if( !IsShownOnScreen() )
196 return;
197
198 // Don't do anything if we requested the UI update
199 if( m_updateLock )
200 return;
201
202 m_updateLock = true;
203
204 wxInfoBarGeneric::Dismiss();
205
206 if( m_auiManager )
207 updateAuiLayout( false );
208
209 if( m_callback )
210 (*m_callback)();
211
212 m_updateLock = false;
213}
214
215
216void WX_INFOBAR::onThemeChange( wxSysColourChangedEvent& aEvent )
217{
218 wxColour fg, bg;
220 SetBackgroundColour( bg );
221 SetForegroundColour( fg );
222
223 if( wxBitmapButton* btn = GetCloseButton() )
224 {
225 wxString tooltip = btn->GetToolTipText();
227 AddCloseButton( tooltip );
228 }
229}
230
231
232void WX_INFOBAR::onSize( wxSizeEvent& aEvent )
233{
234 int barWidth = GetSize().GetWidth();
235 wxSizer* sizer = GetSizer();
236
237 if( !sizer )
238 return;
239
240 // wx3.3 moved the sizer we previously wanted deeper into sizers...
241 // do we actually still need this for wx3.3?
242 #if wxCHECK_VERSION( 3, 3, 0 )
243 wxSizerItem* outerSizer = sizer->GetItem( (size_t) 0 );
244 wxSizerItem* textSizer = nullptr;
245
246 if (outerSizer->IsSizer())
247 {
248 wxBoxSizer* innerSizer1 = dynamic_cast<wxBoxSizer*>( outerSizer->GetSizer() );
249 wxBoxSizer* innerSizer2 =
250 dynamic_cast<wxBoxSizer*>( innerSizer1->GetItem((size_t)0)->GetSizer() );
251
252 if( innerSizer2 )
253 textSizer = innerSizer2->GetItem( 1 );
254 }
255 #else
256 wxSizerItem* textSizer = sizer->GetItem( 1 );
257 #endif
258
259 if( textSizer )
260 {
261 if( wxStaticText* textCtrl = dynamic_cast<wxStaticText*>( textSizer->GetWindow() ) )
262 textCtrl->SetLabelText( m_message );
263 }
264
265 // Calculate the horizontal size: because the infobar is shown on top of the draw canvas
266 // it is adjusted to the canvas width.
267 // On Mac, the canvas is the parent
268 // On other OS the parent is EDA_BASE_FRAME that contains the canvas
269 int parentWidth = m_parent->GetClientSize().GetWidth();
270 EDA_BASE_FRAME* frame = dynamic_cast<EDA_BASE_FRAME*>( m_parent );
271
272 if( frame && frame->GetToolCanvas() )
273 parentWidth = frame->GetToolCanvas()->GetSize().GetWidth();
274
275 if( barWidth != parentWidth )
276 SetSize( parentWidth, GetSize().GetHeight() );
277
278 if( textSizer )
279 {
280 if( wxStaticText* textCtrl = dynamic_cast<wxStaticText*>( textSizer->GetWindow() ) )
281 {
282 // Re-wrap the text (this is done automatically later but we need it now)
283 // And count how many lines we need. If we have embedded newlines, then
284 // multiply the number of lines by the text min height to find the correct
285 // min height for the control. The min height of the text control will be the size
286 // of a single line of text. This assumes that two lines of text are larger
287 // than the height of the icon for the bar.
288 textCtrl->Wrap( -1 );
289 wxString wrapped_text = textCtrl->GetLabel();
290 int line_count = wrapped_text.Freq( '\n' ) + 1;
291 int txt_h, txt_v;
292 wxWindowDC dc( textCtrl );
293 dc.GetTextExtent( wxT( "Xp" ), &txt_h, &txt_v );
294
295 int height = txt_v * line_count;
296 int margins = txt_v - 1;
297 SetMinSize( wxSize( GetSize().GetWidth(), height + margins ) );
298
299 textCtrl->Wrap( -1 );
300 }
301 }
302
303 aEvent.Skip();
304}
305
306
308{
309 wxASSERT( m_auiManager );
310
311 wxAuiPaneInfo& pane = m_auiManager->GetPane( this );
312
313 // If the infobar is in a pane, then show/hide the pane
314 if( pane.IsOk() )
315 {
316 if( aShow )
317 pane.Show();
318 else
319 pane.Hide();
320 }
321
322 // Update the AUI manager regardless
323 m_auiManager->Update();
324}
325
326
327void WX_INFOBAR::AddButton( wxWindowID aId, const wxString& aLabel )
328{
329 wxButton* button = new wxButton( this, aId, aLabel );
330
331 AddButton( button );
332}
333
334
335void WX_INFOBAR::AddButton( wxButton* aButton )
336{
337 wxSizer* sizer = GetSizer();
338
339 wxASSERT( aButton );
340
341#ifdef __WXMAC__
342 // Based on the code in the original class:
343 // smaller buttons look better in the (narrow) info bar under OS X
344 aButton->SetWindowVariant( wxWINDOW_VARIANT_SMALL );
345#endif // __WXMAC__
346
347 auto element = sizer->Add( aButton, wxSizerFlags( 0 ).Centre().Border( wxRIGHT ) );
348
349 element->SetFlag( wxSTRETCH_MASK );
350
351 if( IsShownOnScreen() )
352 sizer->Layout();
353}
354
355
356void WX_INFOBAR::AddButton( wxHyperlinkCtrl* aHypertextButton )
357{
358 wxSizer* sizer = GetSizer();
359
360 wxASSERT( aHypertextButton );
361
362 sizer->Add( aHypertextButton, wxSizerFlags().Centre().Border( wxRIGHT ).Shaped() );
363
364 if( IsShownOnScreen() )
365 sizer->Layout();
366}
367
368
369void WX_INFOBAR::AddCloseButton( const wxString& aTooltip )
370{
371 wxBitmapButton* button = wxBitmapButton::NewCloseButton( this, ID_CLOSE_INFOBAR );
372
373 button->SetToolTip( aTooltip );
374
375 AddButton( button );
376}
377
378
380{
381 wxSizer* sizer = GetSizer();
382
383 if( sizer->GetItemCount() == 0 )
384 return;
385
386 // The last item is already the spacer
387 if( sizer->GetItem( sizer->GetItemCount() - 1 )->IsSpacer() )
388 return;
389
390 for( int i = sizer->GetItemCount() - 1; i >= 0; i-- )
391 {
392 wxSizerItem* sItem = sizer->GetItem( i );
393
394 // The spacer is the end of the custom buttons
395 if( sItem->IsSpacer() )
396 break;
397
398 delete sItem->GetWindow();
399 }
400}
401
402
404{
405 return GetCloseButton();
406}
407
408
409wxBitmapButton* WX_INFOBAR::GetCloseButton() const
410{
411 wxSizer* sizer = GetSizer();
412
413 if( !sizer )
414 return nullptr;
415
416 if( sizer->GetItemCount() == 0 )
417 return nullptr;
418
419 if( sizer->GetItem( sizer->GetItemCount() - 1 )->IsSpacer() )
420 return nullptr;
421
422 wxSizerItem* item = sizer->GetItem( sizer->GetItemCount() - 1 );
423
424 if( item && item->GetWindow() && item->GetWindow()->GetId() == ID_CLOSE_INFOBAR )
425 return static_cast<wxBitmapButton*>( item->GetWindow() );
426
427 return nullptr;
428}
429
430
431void WX_INFOBAR::onShowInfoBar( wxCommandEvent& aEvent )
432{
435 ShowMessage( aEvent.GetString(), aEvent.GetInt() );
436}
437
438
439void WX_INFOBAR::onDismissInfoBar( wxCommandEvent& aEvent )
440{
441 Dismiss();
442}
443
444
445void WX_INFOBAR::onCloseButton( wxCommandEvent& aEvent )
446{
447 Dismiss();
448}
449
450
451void WX_INFOBAR::onTimer( wxTimerEvent& aEvent )
452{
453 // Reset and clear the timer
454 m_showTimer->Stop();
455 m_showTime = 0;
456
457 Dismiss();
458}
459
460
461EDA_INFOBAR_PANEL::EDA_INFOBAR_PANEL( wxWindow* aParent, wxWindowID aId, const wxPoint& aPos,
462 const wxSize& aSize, long aStyle, const wxString& aName )
463 : wxPanel( aParent, aId, aPos, aSize, aStyle, aName )
464{
465 m_mainSizer = new wxFlexGridSizer( 1, 0, 0 );
466
467 m_mainSizer->SetFlexibleDirection( wxBOTH );
468 m_mainSizer->AddGrowableCol( 0, 1 );
469
470 SetSizer( m_mainSizer );
471}
472
473
475{
476 wxASSERT( aInfoBar );
477
478 aInfoBar->Reparent( this );
479 m_mainSizer->Add( aInfoBar, 1, wxEXPAND, 0 );
480 m_mainSizer->Layout();
481}
482
483
484void EDA_INFOBAR_PANEL::AddOtherItem( wxWindow* aOtherItem )
485{
486 wxASSERT( aOtherItem );
487
488 aOtherItem->Reparent( this );
489 m_mainSizer->Add( aOtherItem, 1, wxEXPAND, 0 );
490
491 m_mainSizer->AddGrowableRow( 1, 1 );
492 m_mainSizer->Layout();
493}
494
495
496REPORTER& INFOBAR_REPORTER::Report( const wxString& aText, SEVERITY aSeverity )
497{
498 m_message.reset( new wxString( aText ) );
499 m_severity = aSeverity;
500 m_messageSet = true;
501
502 return *this;
503}
504
505
507{
508 return m_message && !m_message->IsEmpty();
509}
510
511
513{
514 // Don't do anything if no message was ever given
515 if( !m_infoBar || !m_messageSet )
516 return;
517
518 // Short circuit if the message is empty and it is already hidden
519 if( !HasMessage() && !m_infoBar->IsShownOnScreen() )
520 return;
521
522 int icon = wxICON_NONE;
523
524 switch( m_severity )
525 {
526 case RPT_SEVERITY_UNDEFINED: icon = wxICON_INFORMATION; break;
527 case RPT_SEVERITY_INFO: icon = wxICON_INFORMATION; break;
528 case RPT_SEVERITY_EXCLUSION: icon = wxICON_WARNING; break;
529 case RPT_SEVERITY_ACTION: icon = wxICON_WARNING; break;
530 case RPT_SEVERITY_WARNING: icon = wxICON_WARNING; break;
531 case RPT_SEVERITY_ERROR: icon = wxICON_ERROR; break;
532 case RPT_SEVERITY_IGNORE: icon = wxICON_INFORMATION; break;
533 case RPT_SEVERITY_DEBUG: icon = wxICON_INFORMATION; break;
534 }
535
536 if( m_message->EndsWith( wxS( "\n" ) ) )
537 *m_message = m_message->Left( m_message->Length() - 1 );
538
539 if( HasMessage() )
540 m_infoBar->QueueShowMessage( *m_message, icon );
541 else
542 m_infoBar->QueueDismiss();
543}
Class to handle configuration and automatic determination of the DPI scale to use for canvases.
double GetContentScaleFactor() const override
Get the content scale factor, which may be different from the scale factor on some platforms.
The base frame for deriving all KiCad main window classes.
EDA_INFOBAR_PANEL(wxWindow *aParent, wxWindowID aId=wxID_ANY, const wxPoint &aPos=wxDefaultPosition, const wxSize &aSize=wxSize(-1,-1), long aStyle=wxTAB_TRAVERSAL, const wxString &aName=wxEmptyString)
wxFlexGridSizer * m_mainSizer
Definition wx_infobar.h:313
void AddInfoBar(WX_INFOBAR *aInfoBar)
Add the given infobar object to the panel.
void AddOtherItem(wxWindow *aOtherItem)
Add the other item to the panel.
WX_INFOBAR * m_infoBar
Definition wx_infobar.h:350
void Finalize()
Update the infobar with the reported text.
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
SEVERITY m_severity
Definition wx_infobar.h:352
std::unique_ptr< wxString > m_message
Definition wx_infobar.h:351
bool HasMessage() const override
Returns true if any messages were reported.
REPORTER()
Definition reporter.h:75
virtual wxWindow * GetToolCanvas() const =0
Canvas access.
A modified version of the wxInfoBar class that allows us to:
Definition wx_infobar.h:76
void SetShowTime(int aTime)
Set the time period to show the infobar.
void RemoveAllButtons()
Remove all the buttons that have been added by the user.
void ShowMessageFor(const wxString &aMessage, int aTime, int aFlags=wxICON_INFORMATION, MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the infobar with the provided message and icon for a specific period of time.
bool HasCloseButton() const
MESSAGE_TYPE m_type
The type of message being displayed.
Definition wx_infobar.h:264
void updateAuiLayout(bool aShow)
Update the AUI pane to show or hide this infobar.
std::optional< std::function< void(void)> > m_callback
Optional callback made when closing infobar.
Definition wx_infobar.h:267
wxString m_message
The original message without wrapping.
Definition wx_infobar.h:265
int m_showTime
The time to show the infobar. 0 = don't auto hide.
Definition wx_infobar.h:260
bool m_updateLock
True if this infobar requested the UI update.
Definition wx_infobar.h:261
void onShowInfoBar(wxCommandEvent &aEvent)
Event handler for showing the infobar using a wxCommandEvent of the type KIEVT_SHOW_INFOBAR.
void onDismissInfoBar(wxCommandEvent &aEvent)
Event handler for dismissing the infobar using a wxCommandEvent of the type KIEVT_DISMISS_INFOBAR.
void AddButton(wxButton *aButton)
Add an already created button to the infobar.
MESSAGE_TYPE
Sets the type of message for special handling if needed.
Definition wx_infobar.h:94
@ GENERIC
GENERIC Are messages that do not have special handling.
Definition wx_infobar.h:95
void QueueShowMessage(const wxString &aMessage, int aFlags=wxICON_INFORMATION)
Send the infobar an event telling it to show a message.
WX_INFOBAR(wxWindow *aParent, wxAuiManager *aMgr=nullptr, wxWindowID aWinid=wxID_ANY)
Construct an infobar that can exist inside an AUI managed frame.
void onCloseButton(wxCommandEvent &aEvent)
Event handler for the close button.
wxBitmapButton * GetCloseButton() const
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
void onThemeChange(wxSysColourChangedEvent &aEvent)
Event handler for the color theme change event.
void AddCloseButton(const wxString &aTooltip=_("Hide this message."))
Add the default close button to the infobar on the right side.
wxTimer * m_showTimer
The timer counting the autoclose period.
Definition wx_infobar.h:262
void onTimer(wxTimerEvent &aEvent)
Event handler for the automatic closing timer.
void ShowMessage(const wxString &aMessage, int aFlags=wxICON_INFORMATION) override
Show the info bar with the provided message and icon.
wxAuiManager * m_auiManager
The AUI manager that contains this infobar.
Definition wx_infobar.h:263
void onSize(wxSizeEvent &aEvent)
void QueueDismiss()
Send the infobar an event telling it to hide itself.
Base window classes and related definitions.
void GetInfoBarColours(wxColour &aFGColour, wxColour &aBGColour)
Return the background and foreground colors for info bars in the current scheme.
Definition wxgtk/ui.cpp:67
SEVERITY
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_UNDEFINED
@ RPT_SEVERITY_EXCLUSION
@ RPT_SEVERITY_IGNORE
@ RPT_SEVERITY_DEBUG
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
wxDEFINE_EVENT(KIEVT_SHOW_INFOBAR, wxCommandEvent)
@ ID_CLOSE_INFOBAR
ID for the close button on the frame's infobar.
Definition wx_infobar.h:41