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 // Do the same as wxOwnerDrawnComboBox::Clear().
211 // But, on MSW, Clear() has 2 issues:
212 // - it generate wxWidgets alerts
213 // - it generate a OnTextCtrl event, creating also recursions, not so easy to fix.
214 // We use wxOwnerDrawnComboBox::Delete that do not have these issues and can do the same job
215
216#if defined( __WXMSW__ )
217 while( GetCount() )
218 Delete( GetCount() - 1 );
219#else
220 Clear();
221#endif
222}
223
224
226{
227 wxArrayString menuList = FONT_LIST_MANAGER::Get().GetFonts();
228
229 wxString selection = GetValue();
230
231 // Store the full font list for filtering
232 m_fullFontList.Clear();
233
234 if( m_systemFontCount > 1 )
235 m_fullFontList.Add( _( "Default Font" ) );
236
238
239 for( const wxString& font : menuList )
240 m_fullFontList.Add( font );
241
242 Freeze();
243 clearList();
244
245 if( m_systemFontCount > 1 )
246 Append( _( "Default Font" ) );
247
248 Append( KICAD_FONT_NAME );
249 m_systemFontCount = GetCount();
250
251 Append( menuList );
252
253 if( !selection.IsEmpty() )
254 SetStringSelection( selection );
255
256 m_isFiltered = false;
257 Thaw();
258}
259
260
261void FONT_CHOICE::SetFontSelection( KIFONT::FONT* aFont, bool aSilentMode )
262{
263 if( !aFont )
264 {
265 SetSelection( 0 );
266 }
267 else
268 {
269 bool result = SetStringSelection( aFont->GetName() );
270
271 if( !result )
272 {
273 Append( aFont->GetName() + m_notFound );
274 SetSelection( GetCount() - 1 );
275 }
276 }
277}
278
279
281{
282 int sel = GetSelection();
283
284 if( sel < 0 )
285 return false;
286
287 if( GetString( sel ).EndsWith( m_notFound ) )
288 return false;
289
290 return true;
291}
292
293
294KIFONT::FONT* FONT_CHOICE::GetFontSelection( bool aBold, bool aItalic, bool aForDrawingSheet ) const
295{
296 if( GetSelection() <= 0 )
297 {
298 return nullptr;
299 }
300 else if( GetSelection() == 1 && m_systemFontCount == 2 )
301 {
302 return KIFONT::FONT::GetFont( KICAD_FONT_NAME, aBold, aItalic );
303 }
304 else
305 {
306 return KIFONT::FONT::GetFont( GetValue(), aBold, aItalic, nullptr,
307 aForDrawingSheet );
308 }
309}
310
311
312void FONT_CHOICE::OnDrawItem( wxDC& dc, const wxRect& rect, int item, int flags ) const
313{
314 if( item == wxNOT_FOUND )
315 return;
316
317 wxString name = GetString( item );
318
319 dc.SetFont( wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ) );
320 dc.DrawText( name, rect.x + 2, rect.y + 2 );
321
322 if( item >= m_systemFontCount )
323 {
324 wxCoord w, h;
325 dc.GetTextExtent( name, &w, &h );
326 wxFont sampleFont( wxFontInfo( dc.GetFont().GetPointSize() ).FaceName( name ) );
327 dc.SetFont( sampleFont );
328 dc.SetTextForeground( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
329 dc.DrawText( wxS( "AaBbCcDd123456" ), rect.x + w + 15, rect.y + 2 );
330 }
331}
332
333
335{
336 return GetValue();
337}
338
339
340void FONT_CHOICE::OnKeyDown( wxKeyEvent& aEvent )
341{
342 int keyCode = aEvent.GetKeyCode();
343
344 if( keyCode == WXK_RETURN || keyCode == WXK_NUMPAD_ENTER || keyCode == WXK_ESCAPE )
345 {
346 if( IsPopupShown() )
347 {
348 // Accept current text and close popup
349 Dismiss();
350 return;
351 }
352 }
353 else if( keyCode == WXK_BACK && !IsPopupShown() )
354 {
355 // Handle backspace when popup is not shown
356 // This allows normal character-by-character deletion instead of selecting all text
357
358 wxString currentText = GetValue();
359 long selStart, selEnd;
360 GetSelection( &selStart, &selEnd );
361
362 if( selStart != selEnd )
363 {
364 // There's a selection - delete the selected text
365 wxString newText = currentText.Left( selStart ) + currentText.Mid( selEnd );
366 m_lastText = newText; // Prevent recursive calls
367 ChangeValue( newText );
368 SetInsertionPoint( selStart );
369 return; // Don't skip this event
370 }
371 else if( selStart > 0 )
372 {
373 // No selection - delete character before cursor
374 wxString newText = currentText.Left( selStart - 1 ) + currentText.Mid( selStart );
375 m_lastText = newText; // Prevent recursive calls
376 ChangeValue( newText );
377 SetInsertionPoint( selStart - 1 );
378 return; // Don't skip this event
379 }
380 // If at beginning of text, let default behavior handle it
381 }
382
383 aEvent.Skip();
384}
385
386
387void FONT_CHOICE::OnCharHook( wxKeyEvent& aEvent )
388{
389 int keyCode = aEvent.GetUnicodeKey();
390 wchar_t wc = static_cast<wchar_t>( keyCode );
391
392 // When popup is not shown, let normal text processing handle printable characters
393 // The OnTextCtrl method will handle filtering and autocomplete
394 if( !IsPopupShown() )
395 {
396 aEvent.Skip();
397 return;
398 }
399
400 if( std::iswprint( wc ) && !std::iswcntrl( wc ) )
401 {
402 // Get current text and check if there's a selection
403 wxString currentText = GetValue();
404 long selStart, selEnd;
405 GetSelection( &selStart, &selEnd );
406
407 wxChar newChar = (wxChar)keyCode;
408 wxString newText;
409
410 if( selStart != selEnd )
411 {
412 // There's a selection - replace it with the new character
413 newText = currentText.Left( selStart ) + newChar + currentText.Mid( selEnd );
414 }
415 else
416 {
417 // No selection - append to current insertion point
418 long insertionPoint = GetInsertionPoint();
419 newText = currentText.Left( insertionPoint ) + newChar + currentText.Mid( insertionPoint );
420 }
421
422 // Update the text control
423 m_lastText = newText; // Prevent recursive calls
424 ChangeValue( newText );
425 SetInsertionPoint( selStart + 1 ); // Position cursor after new character
426
427 // Filter the font list based on new text (will handle trimming internally)
428 FilterFontList( newText );
429
430 // Try autocomplete
431 DoAutoComplete( newText );
432
433 return; // Don't skip this event
434 }
435
436 switch (keyCode)
437 {
438 case WXK_BACK:
439 {
440 wxString currentText = GetValue();
441 long selStart, selEnd;
442 GetSelection( &selStart, &selEnd );
443
444 wxString newText;
445 long newInsertionPoint;
446
447 if( selStart != selEnd )
448 {
449 // There's a selection - delete the selected text
450 newText = currentText.Left( selStart ) + currentText.Mid( selEnd );
451 newInsertionPoint = selStart;
452 }
453 else if( selStart > 0 )
454 {
455 // No selection - delete character before cursor
456 newText = currentText.Left( selStart - 1 ) + currentText.Mid( selStart );
457 newInsertionPoint = selStart - 1;
458 }
459 else
460 {
461 return; // At beginning, can't delete
462 }
463
464 m_lastText = newText; // Prevent recursive calls
465 ChangeValue( newText );
466 SetInsertionPoint( newInsertionPoint );
467
468 // Check if trimmed text is empty
469 wxString trimmedNewText = newText;
470 trimmedNewText.Trim().Trim( false );
471
472 if( trimmedNewText.IsEmpty() )
473 {
475 }
476 else
477 {
478 FilterFontList( newText );
479 // Don't call DoAutoComplete for backspace to avoid the loop
480 }
481
482 return; // Don't skip this event
483 }
484 case WXK_RETURN:
485 case WXK_NUMPAD_ENTER:
486 {
487 Dismiss();
488 return;
489 }
490 break;
491
492 case WXK_ESCAPE:
493 {
494 // Restore to original selection or default font if original doesn't exist
495 if( !m_originalSelection.IsEmpty() && FindBestMatch( m_originalSelection ) != wxNOT_FOUND )
496 {
497 SetStringSelection( m_originalSelection );
499 }
500 else
501 {
502 // Original font doesn't exist anymore, select default font
503 wxString defaultFont = GetDefaultFontName();
504 SetStringSelection( defaultFont );
505 m_lastText = defaultFont;
506 }
507
508 // Restore full font list if filtered
509 if( m_isFiltered )
510 {
512 }
513
514 // Only dismiss if popup is actually shown
515 if( IsPopupShown() )
516 {
517 Dismiss();
518 }
519 return;
520 }
521
522 default:
523 break;
524 }
525
526 aEvent.Skip();
527}
528
529
530void FONT_CHOICE::OnTextCtrl( wxCommandEvent& aEvent )
531{
532 wxString currentText = GetValue();
533
534 // Avoid recursive calls
535 if( currentText == m_lastText )
536 {
537 aEvent.Skip();
538 return;
539 }
540
541 m_lastText = currentText;
542
543 // If popup is shown, OnCharHook handles the text input, so just skip
544 if( IsPopupShown() )
545 {
546 aEvent.Skip();
547 return;
548 }
549
550 // Trim whitespace for processing
551 wxString trimmedText = currentText;
552 trimmedText.Trim().Trim(false);
553
554 // If text is empty or all whitespace, restore full list
555 if( trimmedText.IsEmpty() )
556 {
558 aEvent.Skip();
559 return;
560 }
561
562 // Filter the font list based on the text input
563 FilterFontList( currentText );
564
565 // Try to find a match for autocomplete (only when popup is not shown)
566 int bestMatch = FindBestMatch( trimmedText );
567
568 if( bestMatch != wxNOT_FOUND )
569 {
570 DoAutoComplete( trimmedText );
571 }
572
573 aEvent.Skip();
574}
575
576
577void FONT_CHOICE::OnDropDown( wxCommandEvent& aEvent )
578{
579 // Store the original selection when dropdown opens
580 m_originalSelection = GetValue();
581 aEvent.Skip();
582}
583
584
585void FONT_CHOICE::OnCloseUp( wxCommandEvent& aEvent )
586{
587 // When dropdown closes, we should only restore the full font list
588 // but NOT change the current text value unless explicitly selected
589 // The OnKillFocus handler will handle text validation when focus is lost
590
591 // Reset to full font list if filtered
592 if( m_isFiltered )
593 {
595 }
596
597 aEvent.Skip();
598}
599
600
601void FONT_CHOICE::OnSetFocus( wxFocusEvent& aEvent )
602{
603 // When the control gains focus, select all text so user can quickly replace it
604 // Only do this if we're not already showing the popup (which would indicate
605 // the user is actively interacting with the dropdown)
606 if( !GetValue().IsEmpty() && !IsPopupShown() )
607 {
608 // Use CallAfter to ensure the focus event is fully processed first
609 CallAfter( [this]() {
610 if( HasFocus() && !IsPopupShown() )
611 SelectAll();
612 } );
613 }
614
615 aEvent.Skip();
616}
617
618
619void FONT_CHOICE::OnKillFocus( wxFocusEvent& aEvent )
620{
621 // When losing focus, deselect text and validate/correct the font name
622
623 // First, deselect any selected text
624 if( GetInsertionPoint() != GetLastPosition() )
625 {
626 SetInsertionPointEnd();
627 }
628
629 // Get current text and trim whitespace
630 wxString currentText = GetValue();
631 currentText.Trim().Trim(false);
632
633 // If text is empty, set to default font
634 if( currentText.IsEmpty() )
635 {
636 wxString defaultFont = GetDefaultFontName();
637 SetStringSelection( defaultFont );
638 m_lastText = defaultFont;
639 aEvent.Skip();
640 return;
641 }
642
643 // Try to find exact match first
644 if( FindBestMatch( currentText ) != wxNOT_FOUND )
645 {
646 // Exact match found, keep the current text but ensure it's properly set
647 SetStringSelection( currentText );
648 m_lastText = currentText;
649 aEvent.Skip();
650 return;
651 }
652
653 // No exact match, try to find best partial match
654 wxString partialMatch = FindBestPartialMatch( currentText );
655
656 if( !partialMatch.IsEmpty() )
657 {
658 SetStringSelection( partialMatch );
659 m_lastText = partialMatch;
660 }
661 else
662 {
663 // No decent partial match, fall back to default font
664 wxString defaultFont = GetDefaultFontName();
665 SetStringSelection( defaultFont );
666 m_lastText = defaultFont;
667 }
668
669 // Ensure we restore full font list if it was filtered
670 if( m_isFiltered )
671 {
673 }
674
675 aEvent.Skip();
676}
677
678
679void FONT_CHOICE::DoAutoComplete( const wxString& aText )
680{
681 if( aText.IsEmpty() )
682 return;
683
684 // Find the best matching font
685 int bestMatch = FindBestMatch( aText );
686
687 if( bestMatch == wxNOT_FOUND )
688 return;
689
690
691 wxString matchText = GetString( bestMatch );
692
693 // Only do autocomplete if the match is longer than what we typed
694 if( matchText.Length() > aText.Length() && matchText.Lower().StartsWith( aText.Lower() ) )
695 {
696 // Set the text with the autocompleted portion selected
697 m_lastText = matchText; // Update to prevent recursive calls
698 ChangeValue( matchText );
699 SetInsertionPoint( aText.Length() );
700 SetSelection( aText.Length(), matchText.Length() );
701
702 if( IsPopupShown() )
703 {
704 SetSelection( bestMatch );
705 }
706 }
707}
708
709
710void FONT_CHOICE::FilterFontList( const wxString& aFilter )
711{
712 // Trim whitespace from filter
713 wxString trimmedFilter = aFilter;
714 trimmedFilter.Trim().Trim(false);
715
716 if( trimmedFilter.IsEmpty() )
717 {
719 return;
720 }
721
722 wxArrayString filteredList;
723
724 // Add system fonts first
725 for( int i = 0; i < m_systemFontCount; i++ )
726 {
727 wxString fontName = m_fullFontList[i];
728
729 if( fontName.Lower().StartsWith( trimmedFilter.Lower() ) )
730 filteredList.Add( fontName );
731 }
732
733 // Add matching fonts from the full list
734 for( size_t i = m_systemFontCount; i < m_fullFontList.GetCount(); i++ )
735 {
736 wxString fontName = m_fullFontList[i];
737
738 if( fontName.Lower().StartsWith( trimmedFilter.Lower() ) )
739 filteredList.Add( fontName );
740 }
741
742 // Preserve the current text value
743 wxString currentText = GetValue();
744
745 // Check if we had items before and now have none - this indicates we need to force refresh
746 bool hadItemsBefore = GetCount() > 0;
747 bool haveItemsNow = filteredList.GetCount() > 0;
748 bool needsPopupRefresh = hadItemsBefore && !haveItemsNow && IsPopupShown();
749
750 // Update the combo box with filtered list (even if empty)
751 Freeze();
752 clearList();
753
754 if( haveItemsNow )
755 {
756 Append( filteredList );
757 }
758 // If no matches, leave the dropdown empty
759
760 m_isFiltered = true;
761
762 // Restore the text value after filtering
763 if( !currentText.IsEmpty() )
764 {
765 ChangeValue( currentText );
766 SetInsertionPointEnd();
767 }
768
769 Thaw();
770
771 // Handle popup display
772 if( needsPopupRefresh )
773 {
774 // We had items before but now have none - dismiss the popup
775 Dismiss();
776 }
777 else if( !IsPopupShown() && haveItemsNow )
778 {
779 // Only show popup if we have items to display and control has focus
780 // This prevents popup from showing during programmatic text changes
781 if( HasFocus() )
782 {
783 Popup();
784 }
785 }
786 else if( IsPopupShown() && !haveItemsNow )
787 {
788 // If popup is shown but we have no items, dismiss it
789 Dismiss();
790 }
791
792 // Force a refresh to ensure the popup displays correctly only if it's shown and has items
793 if( IsPopupShown() && haveItemsNow )
794 {
795 Update();
796 Refresh();
797 }
798}
799
800
802{
803 if( !m_isFiltered )
804 return;
805
806 wxString selection = GetValue();
807
808 Freeze();
809 clearList();
810 Append( m_fullFontList );
811 m_isFiltered = false;
812
813 if( !selection.IsEmpty() )
814 {
815 ChangeValue( selection );
816 SetInsertionPointEnd();
817 }
818
819 Thaw();
820}
821
822
823int FONT_CHOICE::FindBestMatch( const wxString& aText )
824{
825 if( aText.IsEmpty() )
826 return wxNOT_FOUND;
827
828 // Trim whitespace from search text
829 wxString trimmedText = aText;
830 trimmedText.Trim().Trim(false);
831
832 if( trimmedText.IsEmpty() )
833 return wxNOT_FOUND;
834
835 wxString lowerText = trimmedText.Lower();
836
837 // Search in the full font list to find matches, then map to current list
838 for( size_t i = 0; i < m_fullFontList.GetCount(); i++ )
839 {
840 wxString itemText = m_fullFontList[i].Lower();
841
842 if( itemText.StartsWith( lowerText ) )
843 {
844 // Find this font in the current displayed list
845 wxString fullFontName = m_fullFontList[i];
846
847 for( unsigned int j = 0; j < GetCount(); j++ )
848 {
849 if( GetString( j ) == fullFontName )
850 return j;
851 }
852 }
853 }
854
855 return wxNOT_FOUND;
856}
857
858
859wxString FONT_CHOICE::FindBestPartialMatch( const wxString& aText )
860{
861 if( aText.IsEmpty() )
862 return wxEmptyString;
863
864 // Trim whitespace from search text
865 wxString trimmedText = aText;
866 trimmedText.Trim().Trim(false);
867
868 if( trimmedText.IsEmpty() )
869 return wxEmptyString;
870
871 wxString testText = trimmedText;
872
873 // Try progressively shorter versions of the text by removing characters from the end
874 // Don't go below a minimum length of 2 characters for meaningful partial matches
875 while( testText.Length() >= 2 )
876 {
877 wxString lowerTestText = testText.Lower();
878
879 // Search in the full font list for a match
880 for( size_t i = 0; i < m_fullFontList.GetCount(); i++ )
881 {
882 wxString itemText = m_fullFontList[i].Lower();
883
884 if( itemText.StartsWith( lowerTestText ) )
885 {
886 // Found a match, return the full font name
887 return m_fullFontList[i];
888 }
889 }
890
891 // Remove the last character and try again
892 testText = testText.Left( testText.Length() - 1 );
893 }
894
895 // No decent partial match found (need at least 2 characters for a meaningful match)
896 return wxEmptyString;
897}
898
899
901{
902 // Return KiCad font name as the default
903 return KICAD_FONT_NAME;
904}
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:79
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:76
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:74
void OnTextCtrl(wxCommandEvent &aEvent)
virtual ~FONT_CHOICE()
wxString m_notFound
Definition font_choice.h:75
void OnCharHook(wxKeyEvent &aEvent)
wxString GetStringSelection() const override
bool m_isFiltered
Definition font_choice.h:77
wxString m_lastText
Definition font_choice.h:78
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.