KiCad PCB EDA Suite
Loading...
Searching...
No Matches
font_choice.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 along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <widgets/font_choice.h>
21#include <kiplatform/ui.h>
22#include <font/fontconfig.h>
23#include <pgm_base.h>
24
25#include <wx/dc.h>
26#include <wx/event.h>
27#include <wx/fontenum.h>
28#include <wx/settings.h>
29#include <wx/textctrl.h>
30
31#include <thread>
32#include <mutex>
33#include <condition_variable>
34#include <atomic>
35#include <chrono>
36#include <algorithm>
37#include <cwctype>
38
39// The "official" name of the building Kicad stroke font (always existing)
41
42class FONT_LIST_MANAGER : public wxEvtHandler
43{
44public:
45 static FONT_LIST_MANAGER& Get();
46 wxArrayString GetFonts() const;
47 void Register( FONT_CHOICE* aCtrl );
48 void Unregister( FONT_CHOICE* aCtrl );
49
50private:
53 void Poll();
54 void UpdateFonts();
55
56 std::thread m_thread;
57 mutable std::mutex m_mutex;
58 std::condition_variable m_cv;
59 wxArrayString m_fonts;
60 std::vector<FONT_CHOICE*> m_controls;
61 std::atomic<bool> m_quit;
62};
63
65{
66 static FONT_LIST_MANAGER mgr;
67 return mgr;
68}
69
70wxArrayString FONT_LIST_MANAGER::GetFonts() const
71{
72 std::lock_guard<std::mutex> lock( m_mutex );
73 return m_fonts;
74}
75
77{
78 std::lock_guard<std::mutex> lock( m_mutex );
79 m_controls.push_back( aCtrl );
80}
81
83{
84 std::lock_guard<std::mutex> lock( m_mutex );
85 auto it = std::find( m_controls.begin(), m_controls.end(), aCtrl );
86
87 if( it != m_controls.end() )
88 m_controls.erase( it );
89}
90
92{
93 m_quit = false;
95
96// It appears that the polling mechanism does not work correctly
97// for mingw (hangs on exit)
98#ifndef __MINGW32__
99 m_thread = std::thread( &FONT_LIST_MANAGER::Poll, this );
100#endif
101}
102
104{
105 {
106 std::lock_guard<std::mutex> lock( m_mutex );
107 m_quit = true;
108 }
109
110#ifndef __MINGW32__
111 m_cv.notify_one();
112
113 if( m_thread.joinable() )
114 m_thread.join();
115#endif
116}
117
119{
120 std::unique_lock<std::mutex> lock( m_mutex );
121
122 while( !m_quit )
123 {
124 // N.B. wait_for will unlock the mutex while waiting but lock it before continuing
125 // so we need to relock before continuing in the loop
126 m_cv.wait_for( lock, std::chrono::seconds( 30 ), [&] { return m_quit.load(); } );
127
128 if( !m_quit )
129 {
130 lock.unlock();
131 UpdateFonts();
132 lock.lock();
133 }
134 }
135}
136
138{
139 std::vector<std::string> fontNames;
140 Fontconfig()->ListFonts( fontNames, std::string( Pgm().GetLanguageTag().utf8_str() ) );
141
142 wxArrayString menuList;
143
144 for( const std::string& name : fontNames )
145 menuList.Add( wxString( name ) );
146
147 menuList.Sort();
148
149 // Check if fonts changed and update controls
150 {
151 std::lock_guard<std::mutex> lock( m_mutex );
152
153 if( menuList == m_fonts )
154 return;
155
156 m_fonts = menuList;
157 }
158
159 CallAfter( [this]() {
160 std::vector<FONT_CHOICE*> controlsCopy;
161
162 // Copy controls list under lock protection
163 {
164 std::lock_guard<std::mutex> lock( m_mutex );
165 controlsCopy = m_controls;
166 }
167
168 // Update controls without holding lock
169 for( FONT_CHOICE* ctrl : controlsCopy )
170 {
171 if( ctrl && !ctrl->IsShownOnScreen() )
172 ctrl->RefreshFonts();
173 }
174 } );
175}
176
177
178FONT_CHOICE::FONT_CHOICE( wxWindow* aParent, int aId, wxPoint aPosition, wxSize aSize,
179 int nChoices, wxString* aChoices, int aStyle ) :
180 wxOwnerDrawnComboBox( aParent, aId, wxEmptyString, aPosition, aSize, 0, nullptr, aStyle )
181{
182 m_systemFontCount = nChoices;
183 m_notFound = wxS( " " ) + _( "<not found>" );
184 m_isFiltered = false;
185 m_lastText = wxEmptyString;
186 m_originalSelection = wxEmptyString;
187
189 RefreshFonts();
190
191 // Bind only essential events to restore functionality
192 Bind( wxEVT_KEY_DOWN, &FONT_CHOICE::OnKeyDown, this );
193 Bind( wxEVT_CHAR_HOOK, &FONT_CHOICE::OnCharHook, this );
194 Bind( wxEVT_COMMAND_TEXT_UPDATED, &FONT_CHOICE::OnTextCtrl, this );
195 Bind( wxEVT_COMBOBOX_DROPDOWN, &FONT_CHOICE::OnDropDown, this );
196 Bind( wxEVT_COMBOBOX_CLOSEUP, &FONT_CHOICE::OnCloseUp, this );
197 Bind( wxEVT_SET_FOCUS, &FONT_CHOICE::OnSetFocus, this );
198 Bind( wxEVT_KILL_FOCUS, &FONT_CHOICE::OnKillFocus, this );
199}
200
201
206
207
209{
210 wxArrayString menuList = FONT_LIST_MANAGER::Get().GetFonts();
211
212 wxString selection = GetValue();
213
214 // Store the full font list for filtering
215 m_fullFontList.Clear();
216
217 if( m_systemFontCount > 1 )
218 m_fullFontList.Add( _( "Default Font" ) );
219
221
222 for( const wxString& font : menuList )
223 m_fullFontList.Add( font );
224
225 Freeze();
226 Clear();
227
228 if( m_systemFontCount > 1 )
229 Append( _( "Default Font" ) );
230
231 Append( KICAD_FONT_NAME );
232 m_systemFontCount = GetCount();
233
234 Append( menuList );
235
236 if( !selection.IsEmpty() )
237 SetStringSelection( selection );
238
239 m_isFiltered = false;
240 Thaw();
241}
242
243
244void FONT_CHOICE::SetFontSelection( KIFONT::FONT* aFont, bool aSilentMode )
245{
246 if( !aFont )
247 {
248 SetSelection( 0 );
249 }
250 else
251 {
252 bool result = SetStringSelection( aFont->GetName() );
253
254 if( !result )
255 {
256 Append( aFont->GetName() + m_notFound );
257 SetSelection( GetCount() - 1 );
258 }
259 }
260}
261
262
264{
265 int sel = GetSelection();
266
267 if( sel < 0 )
268 return false;
269
270 if( GetString( sel ).EndsWith( m_notFound ) )
271 return false;
272
273 return true;
274}
275
276
277KIFONT::FONT* FONT_CHOICE::GetFontSelection( bool aBold, bool aItalic, bool aForDrawingSheet ) const
278{
279 if( GetSelection() <= 0 )
280 {
281 return nullptr;
282 }
283 else if( GetSelection() == 1 && m_systemFontCount == 2 )
284 {
285 return KIFONT::FONT::GetFont( KICAD_FONT_NAME, aBold, aItalic );
286 }
287 else
288 {
289 return KIFONT::FONT::GetFont( GetValue(), aBold, aItalic, nullptr,
290 aForDrawingSheet );
291 }
292}
293
294
295void FONT_CHOICE::OnDrawItem( wxDC& dc, const wxRect& rect, int item, int flags ) const
296{
297 if( item == wxNOT_FOUND )
298 return;
299
300 wxString name = GetString( item );
301
302 dc.SetFont( wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ) );
303 dc.DrawText( name, rect.x + 2, rect.y + 2 );
304
305 if( item >= m_systemFontCount )
306 {
307 wxCoord w, h;
308 dc.GetTextExtent( name, &w, &h );
309 wxFont sampleFont( wxFontInfo( dc.GetFont().GetPointSize() ).FaceName( name ) );
310 dc.SetFont( sampleFont );
311 dc.SetTextForeground( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
312 dc.DrawText( wxS( "AaBbCcDd123456" ), rect.x + w + 15, rect.y + 2 );
313 }
314}
315
316
318{
319 return GetValue();
320}
321
322
323void FONT_CHOICE::OnKeyDown( wxKeyEvent& aEvent )
324{
325 int keyCode = aEvent.GetKeyCode();
326
327 if( keyCode == WXK_RETURN || keyCode == WXK_NUMPAD_ENTER || keyCode == WXK_ESCAPE )
328 {
329 if( IsPopupShown() )
330 {
331 // Accept current text and close popup
332 Dismiss();
333 return;
334 }
335 }
336 else if( keyCode == WXK_BACK && !IsPopupShown() )
337 {
338 // Handle backspace when popup is not shown
339 // This allows normal character-by-character deletion instead of selecting all text
340
341 wxString currentText = GetValue();
342 long selStart, selEnd;
343 GetSelection( &selStart, &selEnd );
344
345 if( selStart != selEnd )
346 {
347 // There's a selection - delete the selected text
348 wxString newText = currentText.Left( selStart ) + currentText.Mid( selEnd );
349 m_lastText = newText; // Prevent recursive calls
350 ChangeValue( newText );
351 SetInsertionPoint( selStart );
352 return; // Don't skip this event
353 }
354 else if( selStart > 0 )
355 {
356 // No selection - delete character before cursor
357 wxString newText = currentText.Left( selStart - 1 ) + currentText.Mid( selStart );
358 m_lastText = newText; // Prevent recursive calls
359 ChangeValue( newText );
360 SetInsertionPoint( selStart - 1 );
361 return; // Don't skip this event
362 }
363 // If at beginning of text, let default behavior handle it
364 }
365
366 aEvent.Skip();
367}
368
369
370void FONT_CHOICE::OnCharHook( wxKeyEvent& aEvent )
371{
372 int keyCode = aEvent.GetUnicodeKey();
373 wchar_t wc = static_cast<wchar_t>( keyCode );
374
375 // When popup is not shown, let normal text processing handle printable characters
376 // The OnTextCtrl method will handle filtering and autocomplete
377 if( !IsPopupShown() )
378 {
379 aEvent.Skip();
380 return;
381 }
382
383 if( std::iswprint( wc ) && !std::iswcntrl( wc ) )
384 {
385 // Get current text and check if there's a selection
386 wxString currentText = GetValue();
387 long selStart, selEnd;
388 GetSelection( &selStart, &selEnd );
389
390 wxChar newChar = (wxChar)keyCode;
391 wxString newText;
392
393 if( selStart != selEnd )
394 {
395 // There's a selection - replace it with the new character
396 newText = currentText.Left( selStart ) + newChar + currentText.Mid( selEnd );
397 }
398 else
399 {
400 // No selection - append to current insertion point
401 long insertionPoint = GetInsertionPoint();
402 newText = currentText.Left( insertionPoint ) + newChar + currentText.Mid( insertionPoint );
403 }
404
405 // Update the text control
406 m_lastText = newText; // Prevent recursive calls
407 ChangeValue( newText );
408 SetInsertionPoint( selStart + 1 ); // Position cursor after new character
409
410 // Filter the font list based on new text (will handle trimming internally)
411 FilterFontList( newText );
412
413 // Try autocomplete
414 DoAutoComplete( newText );
415
416 return; // Don't skip this event
417 }
418
419 switch (keyCode)
420 {
421 case WXK_BACK:
422 {
423 wxString currentText = GetValue();
424 long selStart, selEnd;
425 GetSelection( &selStart, &selEnd );
426
427 wxString newText;
428 long newInsertionPoint;
429
430 if( selStart != selEnd )
431 {
432 // There's a selection - delete the selected text
433 newText = currentText.Left( selStart ) + currentText.Mid( selEnd );
434 newInsertionPoint = selStart;
435 }
436 else if( selStart > 0 )
437 {
438 // No selection - delete character before cursor
439 newText = currentText.Left( selStart - 1 ) + currentText.Mid( selStart );
440 newInsertionPoint = selStart - 1;
441 }
442 else
443 {
444 return; // At beginning, can't delete
445 }
446
447 m_lastText = newText; // Prevent recursive calls
448 ChangeValue( newText );
449 SetInsertionPoint( newInsertionPoint );
450
451 // Check if trimmed text is empty
452 wxString trimmedNewText = newText;
453 trimmedNewText.Trim().Trim( false );
454
455 if( trimmedNewText.IsEmpty() )
456 {
458 }
459 else
460 {
461 FilterFontList( newText );
462 // Don't call DoAutoComplete for backspace to avoid the loop
463 }
464
465 return; // Don't skip this event
466 }
467 case WXK_RETURN:
468 case WXK_NUMPAD_ENTER:
469 {
470 Dismiss();
471 return;
472 }
473 break;
474
475 case WXK_ESCAPE:
476 {
477 // Restore to original selection or default font if original doesn't exist
478 if( !m_originalSelection.IsEmpty() && FindBestMatch( m_originalSelection ) != wxNOT_FOUND )
479 {
480 SetStringSelection( m_originalSelection );
482 }
483 else
484 {
485 // Original font doesn't exist anymore, select default font
486 wxString defaultFont = GetDefaultFontName();
487 SetStringSelection( defaultFont );
488 m_lastText = defaultFont;
489 }
490
491 // Restore full font list if filtered
492 if( m_isFiltered )
493 {
495 }
496
497 // Only dismiss if popup is actually shown
498 if( IsPopupShown() )
499 {
500 Dismiss();
501 }
502 return;
503 }
504
505 default:
506 break;
507 }
508
509 aEvent.Skip();
510}
511
512
513void FONT_CHOICE::OnTextCtrl( wxCommandEvent& aEvent )
514{
515 wxString currentText = GetValue();
516
517 // Avoid recursive calls
518 if( currentText == m_lastText )
519 {
520 aEvent.Skip();
521 return;
522 }
523
524 m_lastText = currentText;
525
526 // If popup is shown, OnCharHook handles the text input, so just skip
527 if( IsPopupShown() )
528 {
529 aEvent.Skip();
530 return;
531 }
532
533 // Trim whitespace for processing
534 wxString trimmedText = currentText;
535 trimmedText.Trim().Trim(false);
536
537 // If text is empty or all whitespace, restore full list
538 if( trimmedText.IsEmpty() )
539 {
541 aEvent.Skip();
542 return;
543 }
544
545 // Filter the font list based on the text input
546 FilterFontList( currentText );
547
548 // Try to find a match for autocomplete (only when popup is not shown)
549 int bestMatch = FindBestMatch( trimmedText );
550
551 if( bestMatch != wxNOT_FOUND )
552 {
553 DoAutoComplete( trimmedText );
554 }
555
556 aEvent.Skip();
557}
558
559
560void FONT_CHOICE::OnDropDown( wxCommandEvent& aEvent )
561{
562 // Store the original selection when dropdown opens
563 m_originalSelection = GetValue();
564 aEvent.Skip();
565}
566
567
568void FONT_CHOICE::OnCloseUp( wxCommandEvent& aEvent )
569{
570 // When dropdown closes, we should only restore the full font list
571 // but NOT change the current text value unless explicitly selected
572 // The OnKillFocus handler will handle text validation when focus is lost
573
574 // Reset to full font list if filtered
575 if( m_isFiltered )
576 {
578 }
579
580 aEvent.Skip();
581}
582
583
584void FONT_CHOICE::OnSetFocus( wxFocusEvent& aEvent )
585{
586 // When the control gains focus, select all text so user can quickly replace it
587 // Only do this if we're not already showing the popup (which would indicate
588 // the user is actively interacting with the dropdown)
589 if( !GetValue().IsEmpty() && !IsPopupShown() )
590 {
591 // Use CallAfter to ensure the focus event is fully processed first
592 CallAfter( [this]() {
593 if( HasFocus() && !IsPopupShown() )
594 SelectAll();
595 } );
596 }
597
598 aEvent.Skip();
599}
600
601
602void FONT_CHOICE::OnKillFocus( wxFocusEvent& aEvent )
603{
604 // When losing focus, deselect text and validate/correct the font name
605
606 // First, deselect any selected text
607 if( GetInsertionPoint() != GetLastPosition() )
608 {
609 SetInsertionPointEnd();
610 }
611
612 // Get current text and trim whitespace
613 wxString currentText = GetValue();
614 currentText.Trim().Trim(false);
615
616 // If text is empty, set to default font
617 if( currentText.IsEmpty() )
618 {
619 wxString defaultFont = GetDefaultFontName();
620 SetStringSelection( defaultFont );
621 m_lastText = defaultFont;
622 aEvent.Skip();
623 return;
624 }
625
626 // Try to find exact match first
627 if( FindBestMatch( currentText ) != wxNOT_FOUND )
628 {
629 // Exact match found, keep the current text but ensure it's properly set
630 SetStringSelection( currentText );
631 m_lastText = currentText;
632 aEvent.Skip();
633 return;
634 }
635
636 // No exact match, try to find best partial match
637 wxString partialMatch = FindBestPartialMatch( currentText );
638
639 if( !partialMatch.IsEmpty() )
640 {
641 SetStringSelection( partialMatch );
642 m_lastText = partialMatch;
643 }
644 else
645 {
646 // No decent partial match, fall back to default font
647 wxString defaultFont = GetDefaultFontName();
648 SetStringSelection( defaultFont );
649 m_lastText = defaultFont;
650 }
651
652 // Ensure we restore full font list if it was filtered
653 if( m_isFiltered )
654 {
656 }
657
658 aEvent.Skip();
659}
660
661
662void FONT_CHOICE::DoAutoComplete( const wxString& aText )
663{
664 if( aText.IsEmpty() )
665 return;
666
667 // Find the best matching font
668 int bestMatch = FindBestMatch( aText );
669
670 if( bestMatch == wxNOT_FOUND )
671 return;
672
673
674 wxString matchText = GetString( bestMatch );
675
676 // Only do autocomplete if the match is longer than what we typed
677 if( matchText.Length() > aText.Length() && matchText.Lower().StartsWith( aText.Lower() ) )
678 {
679 // Set the text with the autocompleted portion selected
680 m_lastText = matchText; // Update to prevent recursive calls
681 ChangeValue( matchText );
682 SetInsertionPoint( aText.Length() );
683 SetSelection( aText.Length(), matchText.Length() );
684
685 if( IsPopupShown() )
686 {
687 SetSelection( bestMatch );
688 }
689 }
690}
691
692
693void FONT_CHOICE::FilterFontList( const wxString& aFilter )
694{
695 // Trim whitespace from filter
696 wxString trimmedFilter = aFilter;
697 trimmedFilter.Trim().Trim(false);
698
699 if( trimmedFilter.IsEmpty() )
700 {
702 return;
703 }
704
705 wxArrayString filteredList;
706
707 // Add system fonts first
708 for( int i = 0; i < m_systemFontCount; i++ )
709 {
710 wxString fontName = m_fullFontList[i];
711
712 if( fontName.Lower().StartsWith( trimmedFilter.Lower() ) )
713 filteredList.Add( fontName );
714 }
715
716 // Add matching fonts from the full list
717 for( size_t i = m_systemFontCount; i < m_fullFontList.GetCount(); i++ )
718 {
719 wxString fontName = m_fullFontList[i];
720
721 if( fontName.Lower().StartsWith( trimmedFilter.Lower() ) )
722 filteredList.Add( fontName );
723 }
724
725 // Preserve the current text value
726 wxString currentText = GetValue();
727
728 // Check if we had items before and now have none - this indicates we need to force refresh
729 bool hadItemsBefore = GetCount() > 0;
730 bool haveItemsNow = filteredList.GetCount() > 0;
731 bool needsPopupRefresh = hadItemsBefore && !haveItemsNow && IsPopupShown();
732
733 // Update the combo box with filtered list (even if empty)
734 Freeze();
735 Clear();
736
737 if( haveItemsNow )
738 {
739 Append( filteredList );
740 }
741 // If no matches, leave the dropdown empty
742
743 m_isFiltered = true;
744
745 // Restore the text value after filtering
746 if( !currentText.IsEmpty() )
747 {
748 ChangeValue( currentText );
749 SetInsertionPointEnd();
750 }
751
752 Thaw();
753
754 // Handle popup display
755 if( needsPopupRefresh )
756 {
757 // We had items before but now have none - dismiss the popup
758 Dismiss();
759 }
760 else if( !IsPopupShown() && haveItemsNow )
761 {
762 // Only show popup if we have items to display and control has focus
763 // This prevents popup from showing during programmatic text changes
764 if( HasFocus() )
765 {
766 Popup();
767 }
768 }
769 else if( IsPopupShown() && !haveItemsNow )
770 {
771 // If popup is shown but we have no items, dismiss it
772 Dismiss();
773 }
774
775 // Force a refresh to ensure the popup displays correctly only if it's shown and has items
776 if( IsPopupShown() && haveItemsNow )
777 {
778 Update();
779 Refresh();
780 }
781}
782
783
785{
786 if( !m_isFiltered )
787 return;
788
789 wxString selection = GetValue();
790
791 Freeze();
792 Clear();
793 Append( m_fullFontList );
794 m_isFiltered = false;
795
796 if( !selection.IsEmpty() )
797 {
798 ChangeValue( selection );
799 SetInsertionPointEnd();
800 }
801
802 Thaw();
803}
804
805
806int FONT_CHOICE::FindBestMatch( const wxString& aText )
807{
808 if( aText.IsEmpty() )
809 return wxNOT_FOUND;
810
811 // Trim whitespace from search text
812 wxString trimmedText = aText;
813 trimmedText.Trim().Trim(false);
814
815 if( trimmedText.IsEmpty() )
816 return wxNOT_FOUND;
817
818 wxString lowerText = trimmedText.Lower();
819
820 // Search in the full font list to find matches, then map to current list
821 for( size_t i = 0; i < m_fullFontList.GetCount(); i++ )
822 {
823 wxString itemText = m_fullFontList[i].Lower();
824
825 if( itemText.StartsWith( lowerText ) )
826 {
827 // Find this font in the current displayed list
828 wxString fullFontName = m_fullFontList[i];
829
830 for( unsigned int j = 0; j < GetCount(); j++ )
831 {
832 if( GetString( j ) == fullFontName )
833 return j;
834 }
835 }
836 }
837
838 return wxNOT_FOUND;
839}
840
841
842wxString FONT_CHOICE::FindBestPartialMatch( const wxString& aText )
843{
844 if( aText.IsEmpty() )
845 return wxEmptyString;
846
847 // Trim whitespace from search text
848 wxString trimmedText = aText;
849 trimmedText.Trim().Trim(false);
850
851 if( trimmedText.IsEmpty() )
852 return wxEmptyString;
853
854 wxString testText = trimmedText;
855
856 // Try progressively shorter versions of the text by removing characters from the end
857 // Don't go below a minimum length of 2 characters for meaningful partial matches
858 while( testText.Length() >= 2 )
859 {
860 wxString lowerTestText = testText.Lower();
861
862 // Search in the full font list for a match
863 for( size_t i = 0; i < m_fullFontList.GetCount(); i++ )
864 {
865 wxString itemText = m_fullFontList[i].Lower();
866
867 if( itemText.StartsWith( lowerTestText ) )
868 {
869 // Found a match, return the full font name
870 return m_fullFontList[i];
871 }
872 }
873
874 // Remove the last character and try again
875 testText = testText.Left( testText.Length() - 1 );
876 }
877
878 // No decent partial match found (need at least 2 characters for a meaningful match)
879 return wxEmptyString;
880}
881
882
884{
885 // Return KiCad font name as the default
886 return KICAD_FONT_NAME;
887}
const char * name
void SetFontSelection(KIFONT::FONT *aFont, bool aSilentMode=false)
Set the selection in wxChoice widget.
void FilterFontList(const wxString &aFilter)
void OnKeyDown(wxKeyEvent &aEvent)
void RestoreFullFontList()
void OnDropDown(wxCommandEvent &aEvent)
KIFONT::FONT * GetFontSelection(bool aBold, bool aItalic, bool aForDrawingSheet=false) const
bool HaveFontSelection() const
wxString m_originalSelection
Definition font_choice.h:76
FONT_CHOICE(wxWindow *aParent, int aId, wxPoint aPosition, wxSize aSize, int nChoices, wxString *aChoices, int aStyle)
void OnKillFocus(wxFocusEvent &aEvent)
void RefreshFonts()
wxArrayString m_fullFontList
Definition font_choice.h:73
void OnDrawItem(wxDC &dc, const wxRect &rect, int item, int flags) const override
int FindBestMatch(const wxString &aText)
wxString GetDefaultFontName() const
void DoAutoComplete(const wxString &aText)
void OnCloseUp(wxCommandEvent &aEvent)
void OnSetFocus(wxFocusEvent &aEvent)
wxString FindBestPartialMatch(const wxString &aText)
int m_systemFontCount
Definition font_choice.h:71
void OnTextCtrl(wxCommandEvent &aEvent)
virtual ~FONT_CHOICE()
wxString m_notFound
Definition font_choice.h:72
void OnCharHook(wxKeyEvent &aEvent)
wxString GetStringSelection() const override
bool m_isFiltered
Definition font_choice.h:74
wxString m_lastText
Definition font_choice.h:75
std::thread m_thread
std::vector< FONT_CHOICE * > m_controls
void Register(FONT_CHOICE *aCtrl)
wxArrayString GetFonts() const
wxArrayString m_fonts
static FONT_LIST_MANAGER & Get()
void Unregister(FONT_CHOICE *aCtrl)
std::condition_variable m_cv
std::atomic< bool > m_quit
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:131
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
Definition font.cpp:147
const wxString & GetName() const
Definition font.h:149
#define _(s)
FONTCONFIG * Fontconfig()
#define KICAD_FONT_NAME
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
PGM_BASE & Pgm()
The global program "get" accessor.
Definition pgm_base.cpp:913
see class PGM_BASE
wxString result
Test unit parsing edge cases and error handling.