KiCad PCB EDA Suite
Loading...
Searching...
No Matches
ui_common.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 <wx/arrstr.h>
21#include <wx/dcclient.h>
22#include <wx/checkbox.h>
23#include <wx/choice.h>
24#include <wx/menu.h>
25#include <wx/menuitem.h>
26#include <wx/listbox.h>
27#include <wx/dataview.h>
28#include <wx/radiobut.h>
29#include <wx/slider.h>
30#include <wx/spinctrl.h>
31#include <wx/srchctrl.h>
32#include <wx/stc/stc.h>
33#include <wx/scrolbar.h>
34#include <wx/grid.h>
35#include <wx/hyperlink.h>
36#include <wx/notebook.h>
37#include <widgets/ui_common.h>
38
39#include <algorithm>
40#include <dialog_shim.h>
41#include <pgm_base.h>
42#include <wx/settings.h>
45#include <string_utils.h>
46
47
48const wxString KIUI::s_FocusStealableInputName = wxS( "KI_NOFOCUS" );
49
50
52{
53 // This is the value used in (most) wxFB dialogs
54 return 5;
55}
56
57
58SEVERITY SeverityFromString( const wxString& aSeverity )
59{
60 if( aSeverity == wxT( "warning" ) )
62 else if( aSeverity == wxT( "ignore" ) )
64 else
65 return RPT_SEVERITY_ERROR;
66}
67
68
69wxString SeverityToString( const SEVERITY& aSeverity )
70{
71 if( aSeverity == RPT_SEVERITY_IGNORE )
72 return wxT( "ignore" );
73 else if( aSeverity == RPT_SEVERITY_WARNING )
74 return wxT( "warning" );
75 else
76 return wxT( "error" );
77}
78
79
80wxSize KIUI::GetTextSize( const wxString& aSingleLine, wxWindow* aWindow )
81{
82 wxCoord width;
83 wxCoord height;
84
85 {
86 wxClientDC dc( aWindow );
87 dc.SetFont( aWindow->GetFont() );
88 dc.GetTextExtent( aSingleLine, &width, &height );
89 }
90
91 return wxSize( width, height );
92}
93
94
95wxFont KIUI::GetMonospacedUIFont( int aRelativeSize )
96{
97 static const int guiFontSize = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ).GetPointSize();
98
99 wxFont font( guiFontSize + aRelativeSize, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL );
100
101#ifdef __WXMAC__
102 // https://trac.wxwidgets.org/ticket/19210
103 if( font.GetFaceName().IsEmpty() )
104 font.SetFaceName( wxS( "Menlo" ) );
105#endif
106
107 return font;
108}
109
110
111wxFont getGUIFont( wxWindow* aWindow, int aRelativeSize )
112{
113 wxFont font = aWindow->GetFont();
114
115 font.SetPointSize( font.GetPointSize() + aRelativeSize );
116
117 if( Pgm().GetCommonSettings() && Pgm().GetCommonSettings()->m_Appearance.apply_icon_scale_to_fonts )
118 font.SetPointSize( KiROUND( KiIconScale( aWindow ) * font.GetPointSize() / 4.0 ) );
119
120#ifdef __WXMAC__
121 // https://trac.wxwidgets.org/ticket/19210
122 if( font.GetFaceName().IsEmpty() )
123 font.SetFaceName( wxS( "San Francisco" ) );
124
125 // OSX 10.1 .. 10.9: Lucida Grande
126 // OSX 10.10: Helvetica Neue
127 // OSX 10.11 .. : San Francisco
128#endif
129
130 return font;
131}
132
133
134wxFont KIUI::GetStatusFont( wxWindow* aWindow )
135{
136#ifdef __WXMAC__
137 int scale = -2;
138#else
139 int scale = 0;
140#endif
141
142 return getGUIFont( aWindow, scale );
143}
144
145
146wxFont KIUI::GetDockedPaneFont( wxWindow* aWindow )
147{
148#ifdef __WXMAC__
149 int scale = -1;
150#else
151 int scale = 0;
152#endif
153
154 return getGUIFont( aWindow, scale );
155}
156
157
158wxFont KIUI::GetInfoFont( wxWindow* aWindow )
159{
160 return getGUIFont( aWindow, -1 );
161}
162
163
164wxFont KIUI::GetSmallInfoFont( wxWindow* aWindow )
165{
166 return getGUIFont( aWindow, - 2 );
167}
168
169
170wxFont KIUI::GetControlFont( wxWindow* aWindow )
171{
172 return getGUIFont( aWindow, 0 );
173}
174
175
176bool KIUI::EnsureTextCtrlWidth( wxTextCtrl* aCtrl, const wxString* aString )
177{
178 wxWindow* window = aCtrl->GetParent();
179
180 if( !window )
181 window = aCtrl;
182
183 wxString ctrlText;
184
185 if( !aString )
186 {
187 ctrlText = aCtrl->GetValue();
188 aString = &ctrlText;
189 }
190
191 wxSize textz = GetTextSize( *aString, window );
192 wxSize ctrlz = aCtrl->GetSize();
193
194 if( ctrlz.GetWidth() < textz.GetWidth() + 10 )
195 {
196 ctrlz.SetWidth( textz.GetWidth() + 10 );
197 aCtrl->SetSizeHints( ctrlz );
198 return true;
199 }
200
201 return false;
202}
203
204
205wxString KIUI::EllipsizeStatusText( wxWindow* aWindow, const wxString& aString )
206{
207 wxString msg = UnescapeString( aString );
208
209 msg.Replace( wxT( "\n" ), wxT( " " ) );
210 msg.Replace( wxT( "\r" ), wxT( " " ) );
211 msg.Replace( wxT( "\t" ), wxT( " " ) );
212
213 wxClientDC dc( aWindow );
214 int statusWidth = aWindow->GetSize().GetWidth();
215
216 // 30% of the first 800 pixels plus 60% of the remaining width
217 int textWidth = std::min( statusWidth, 800 ) * 0.3 + std::max( statusWidth - 800, 0 ) * 0.6;
218
219 return wxControl::Ellipsize( msg, dc, wxELLIPSIZE_END, textWidth );
220}
221
222
223wxString KIUI::EllipsizeMenuText( const wxString& aString )
224{
225 wxString msg = UnescapeString( aString );
226
227 msg.Replace( wxT( "\n" ), wxT( " " ) );
228 msg.Replace( wxT( "\r" ), wxT( " " ) );
229 msg.Replace( wxT( "\t" ), wxT( " " ) );
230
231 if( msg.Length() > 36 )
232 msg = msg.Left( 34 ) + wxT( "..." );
233
234 return msg;
235}
236
237
238wxString KIUI::WrapText( wxWindow* aWindow, const wxString& aText, int aWidth )
239{
240 wxClientDC dc( aWindow );
241 dc.SetFont( aWindow->GetFont() );
242
243 auto measure =
244 [&]( const wxString& aLine )
245 {
246 return dc.GetTextExtent( aLine ).x;
247 };
248
249 wxString wrapped;
250
251 for( const wxString& line : wxSplit( aText, '\n', '\0' ) )
252 {
253 wxString current;
254
255 for( wxString word : wxSplit( line, ' ', '\0' ) )
256 {
257 const bool isPath = word.Contains( wxS( "/" ) ) || word.Contains( wxS( "\\" ) );
258
259 if( isPath && measure( word ) > aWidth )
260 word = wxControl::Ellipsize( word, dc, wxELLIPSIZE_MIDDLE, aWidth, wxELLIPSIZE_FLAGS_NONE );
261
262 const wxString joined = current.IsEmpty() ? word : current + wxS( " " ) + word;
263
264 if( measure( joined ) <= aWidth )
265 {
266 current = joined;
267 continue;
268 }
269
270 if( !current.IsEmpty() )
271 {
272 wrapped += current + wxS( "\n" );
273 current.clear();
274 }
275
276 for( const wxString& cluster : SplitGraphemeClusters( word ) )
277 {
278 if( !current.IsEmpty() && measure( current + cluster ) > aWidth )
279 {
280 wrapped += current + wxS( "\n" );
281 current.clear();
282 }
283
284 current += cluster;
285 }
286 }
287
288 wrapped += current + wxS( "\n" );
289 }
290
291 wrapped.RemoveLast();
292 return wrapped;
293}
294
295
296void KIUI::SelectReferenceNumber( wxTextEntry* aTextEntry )
297{
298 wxString ref = aTextEntry->GetValue();
299
300 if( ref.find_first_of( '?' ) != ref.npos )
301 {
302 aTextEntry->SetSelection( ref.find_first_of( '?' ), ref.find_last_of( '?' ) + 1 );
303 }
304 else if( ref.find_first_of( '*' ) != ref.npos )
305 {
306 aTextEntry->SetSelection( ref.find_first_of( '*' ), ref.find_last_of( '*' ) + 1 );
307 }
308 else
309 {
310 wxString num = ref;
311
312 while( !num.IsEmpty() && ( !isdigit( num.Last() ) || !isdigit( num.GetChar( 0 ) ) ) )
313 {
314 // Trim non-digit from end
315 if( !isdigit( num.Last() ) )
316 num.RemoveLast();
317
318 // Trim non-digit from the start
319 if( !num.IsEmpty() && !isdigit( num.GetChar( 0 ) ) )
320 num = num.Right( num.Length() - 1 );
321 }
322
323 aTextEntry->SetSelection( ref.Find( num ), ref.Find( num ) + num.Length() );
324
325 if( num.IsEmpty() )
326 aTextEntry->SetSelection( -1, -1 );
327 }
328}
329
330
331bool KIUI::IsInputControlFocused( wxWindow* aFocus )
332{
333 if( aFocus == nullptr )
334 aFocus = wxWindow::FindFocus();
335
336 if( !aFocus )
337 return false;
338
339 // These widgets are never considered focused
340 if( aFocus->GetName() == s_FocusStealableInputName )
341 return false;
342
343 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( aFocus );
344 wxStyledTextCtrl* styledText = dynamic_cast<wxStyledTextCtrl*>( aFocus );
345 wxListBox* listBox = dynamic_cast<wxListBox*>( aFocus );
346 wxSearchCtrl* searchCtrl = dynamic_cast<wxSearchCtrl*>( aFocus );
347 wxCheckBox* checkboxCtrl = dynamic_cast<wxCheckBox*>( aFocus );
348 wxChoice* choiceCtrl = dynamic_cast<wxChoice*>( aFocus );
349 wxRadioButton* radioBtn = dynamic_cast<wxRadioButton*>( aFocus );
350 wxSpinCtrl* spinCtrl = dynamic_cast<wxSpinCtrl*>( aFocus );
351 wxSpinCtrlDouble* spinDblCtrl = dynamic_cast<wxSpinCtrlDouble*>( aFocus );
352 wxSlider* sliderCtl = dynamic_cast<wxSlider*>( aFocus );
353
354 // Data view control is annoying, the focus is on a "wxDataViewCtrlMainWindow" class that
355 // is not formally exported via the header.
356 wxDataViewCtrl* dataViewCtrl = nullptr;
357
358 wxWindow* parent = aFocus->GetParent();
359
360 if( parent )
361 dataViewCtrl = dynamic_cast<wxDataViewCtrl*>( parent );
362
363 return ( textEntry || styledText || listBox || searchCtrl || checkboxCtrl || choiceCtrl
364 || radioBtn || spinCtrl || spinDblCtrl || sliderCtl || dataViewCtrl );
365}
366
367
368bool KIUI::IsInputControlEditable( wxWindow* aFocus )
369{
370 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( aFocus );
371 wxStyledTextCtrl* styledText = dynamic_cast<wxStyledTextCtrl*>( aFocus );
372 wxSearchCtrl* searchCtrl = dynamic_cast<wxSearchCtrl*>( aFocus );
373
374 if( textEntry )
375 return textEntry->IsEditable();
376 else if( styledText )
377 return styledText->IsEditable();
378 else if( searchCtrl )
379 return searchCtrl->IsEditable();
380
381 // Must return true if we can't determine the state, intentionally true for non inputs as well.
382 return true;
383}
384
385
387{
388 return !Pgm().m_ModalDialogs.empty();
389}
390
391
392void KIUI::Disable( wxWindow* aWindow )
393{
394 wxScrollBar* scrollBar = dynamic_cast<wxScrollBar*>( aWindow );
395 wxHyperlinkCtrl* hyperlink = dynamic_cast<wxHyperlinkCtrl*>( aWindow );
396 wxGrid* grid = dynamic_cast<wxGrid*>( aWindow );
397 wxStyledTextCtrl* scintilla = dynamic_cast<wxStyledTextCtrl*>( aWindow );
398 wxControl* control = dynamic_cast<wxControl*>( aWindow );
399
400 if( scrollBar || hyperlink )
401 {
402 // Leave navigation controls active
403 }
404 else if( grid )
405 {
406 for( int row = 0; row < grid->GetNumberRows(); ++row )
407 {
408 for( int col = 0; col < grid->GetNumberCols(); ++col )
409 grid->SetReadOnly( row, col );
410 }
411 }
412 else if( scintilla )
413 {
414 scintilla->SetReadOnly( true );
415 }
416 else if( control )
417 {
418 control->Disable();
419 }
420 else
421 {
422 for( wxWindow* child : aWindow->GetChildren() )
423 Disable( child );
424 }
425}
426
427
428void KIUI::AddBitmapToMenuItem( wxMenuItem* aMenu, const wxBitmapBundle& aImage )
429{
430 // Retrieve the global application show icon option:
431 bool useImagesInMenus = Pgm().GetCommonSettings() && Pgm().GetCommonSettings()->m_Appearance.use_icons_in_menus;
432
433 wxItemKind menu_type = aMenu->GetKind();
434
435 if( useImagesInMenus && menu_type != wxITEM_CHECK && menu_type != wxITEM_RADIO )
436 aMenu->SetBitmap( aImage );
437}
438
439
440wxMenuItem* KIUI::AddMenuItem( wxMenu* aMenu, int aId, const wxString& aText,
441 const wxBitmapBundle& aImage, wxItemKind aType )
442{
443 wxMenuItem* item = new wxMenuItem( aMenu, aId, aText, wxEmptyString, aType );
444 AddBitmapToMenuItem( item, aImage );
445
446 aMenu->Append( item );
447
448 return item;
449}
450
451
452wxMenuItem* KIUI::AddMenuItem( wxMenu* aMenu, int aId, const wxString& aText,
453 const wxString& aHelpText, const wxBitmapBundle& aImage,
454 wxItemKind aType )
455{
456 wxMenuItem* item = new wxMenuItem( aMenu, aId, aText, aHelpText, aType );
457 AddBitmapToMenuItem( item, aImage );
458
459 aMenu->Append( item );
460
461 return item;
462}
463
464
465wxMenuItem* KIUI::AddMenuItem( wxMenu* aMenu, wxMenu* aSubMenu, int aId, const wxString& aText,
466 const wxBitmapBundle& aImage )
467{
468 wxMenuItem* item = new wxMenuItem( aMenu, aId, aText );
469 item->SetSubMenu( aSubMenu );
470 AddBitmapToMenuItem( item, aImage );
471
472 aMenu->Append( item );
473
474 return item;
475}
476
477
478wxMenuItem* KIUI::AddMenuItem( wxMenu* aMenu, wxMenu* aSubMenu, int aId, const wxString& aText,
479 const wxString& aHelpText, const wxBitmapBundle& aImage )
480{
481 wxMenuItem* item = new wxMenuItem( aMenu, aId, aText, aHelpText );
482 item->SetSubMenu( aSubMenu );
483 AddBitmapToMenuItem( item, aImage );
484
485 aMenu->Append( item );
486
487 return item;
488}
489
490
491int FindNotebookPage( wxNotebook* aNotebook, wxWindow* aChildWindow )
492{
493 wxWindow* candidate = aChildWindow;
494
495 while( candidate && candidate->GetParent() != aNotebook )
496 candidate = candidate->GetParent();
497
498 if ( candidate )
499 return aNotebook->FindPage( candidate );
500
501 return 0;
502}
int KiIconScale(wxWindow *aWindow)
Return the automatic scale factor that would be used for a given window by KiScaledBitmap and KiScale...
Definition bitmap.cpp:130
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
APPEARANCE m_Appearance
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:562
std::vector< void * > m_ModalDialogs
Definition pgm_base.h:388
KICOMMON_API int GetStdMargin()
Get the standard margin around a widget in the KiCad UI.
Definition ui_common.cpp:51
KICOMMON_API wxFont GetDockedPaneFont(wxWindow *aWindow)
KICOMMON_API wxFont GetStatusFont(wxWindow *aWindow)
KICOMMON_API bool IsInputControlFocused(wxWindow *aFocus=nullptr)
Check if a input control has focus.
KICOMMON_API bool IsInputControlEditable(wxWindow *aControl)
Check if a input control has focus.
KICOMMON_API wxString EllipsizeMenuText(const wxString &aString)
Ellipsize text (at the end) to be no more than 36 characters.
KICOMMON_API wxFont GetInfoFont(wxWindow *aWindow)
KICOMMON_API wxString WrapText(wxWindow *aWindow, const wxString &aText, int aWidth)
Break text into lines no wider than aWidth pixels in aWindow's font.
KICOMMON_API wxFont GetSmallInfoFont(wxWindow *aWindow)
KICOMMON_API bool IsModalDialogFocused()
KICOMMON_API wxFont GetControlFont(wxWindow *aWindow)
KICOMMON_API wxMenuItem * AddMenuItem(wxMenu *aMenu, int aId, const wxString &aText, const wxBitmapBundle &aImage, wxItemKind aType=wxITEM_NORMAL)
Create and insert a menu item with an icon into aMenu.
KICOMMON_API wxString EllipsizeStatusText(wxWindow *aWindow, const wxString &aString)
Ellipsize text (at the end) to be no more than 1/3 of the window width.
KICOMMON_API const wxString s_FocusStealableInputName
Definition ui_common.cpp:48
KICOMMON_API void SelectReferenceNumber(wxTextEntry *aTextEntry)
Select the number (or "?") in a reference for ease of editing.
KICOMMON_API wxSize GetTextSize(const wxString &aSingleLine, wxWindow *aWindow)
Return the size of aSingleLine of text when it is rendered in aWindow using whatever font is currentl...
Definition ui_common.cpp:80
KICOMMON_API void AddBitmapToMenuItem(wxMenuItem *aMenu, const wxBitmapBundle &aImage)
Add a bitmap to a menuitem.
KICOMMON_API wxFont GetMonospacedUIFont(int aRelativeSize=0)
Definition ui_common.cpp:95
KICOMMON_API bool EnsureTextCtrlWidth(wxTextCtrl *aCtrl, const wxString *aString=nullptr)
Set the minimum pixel width on a text control in order to make a text string be fully visible within ...
KICOMMON_API void Disable(wxWindow *aWindow)
Makes a window read-only.
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
SEVERITY
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_IGNORE
const int scale
wxString UnescapeString(const wxString &aSource)
std::vector< wxString > SplitGraphemeClusters(const wxString &aText)
Split text into the units a reader sees as one character.
int FindNotebookPage(wxNotebook *aNotebook, wxWindow *aChildWindow)
SEVERITY SeverityFromString(const wxString &aSeverity)
Definition ui_common.cpp:58
wxString SeverityToString(const SEVERITY &aSeverity)
Definition ui_common.cpp:69
wxFont getGUIFont(wxWindow *aWindow, int aRelativeSize)
Functions to provide common constants and other functions to assist in making a consistent UI.