KiCad PCB EDA Suite
SCINTILLA_TRICKS Class Reference

Add cut/copy/paste, autocomplete and brace highlighting to a wxStyleTextCtrl instance. More...

#include <scintilla_tricks.h>

Inheritance diagram for SCINTILLA_TRICKS:

Public Member Functions

 SCINTILLA_TRICKS (wxStyledTextCtrl *aScintilla, const wxString &aBraces, bool aSingleLine, std::function< void()> m_enterCallback=[](){ })
 
void DoAutocomplete (const wxString &aPartial, const wxArrayString &aTokens)
 

Protected Member Functions

void setupStyles ()
 
int firstNonWhitespace (int aLine, int *aWhitespaceCount=nullptr)
 
void onCharHook (wxKeyEvent &aEvent)
 
void onScintillaUpdateUI (wxStyledTextEvent &aEvent)
 
void onThemeChanged (wxSysColourChangedEvent &aEvent)
 

Protected Attributes

wxStyledTextCtrl * m_te
 
wxString m_braces
 
int m_lastCaretPos
 
bool m_suppressAutocomplete
 
bool m_singleLine
 
std::function< void()> m_returnCallback
 

Detailed Description

Add cut/copy/paste, autocomplete and brace highlighting to a wxStyleTextCtrl instance.

Definition at line 34 of file scintilla_tricks.h.

Constructor & Destructor Documentation

◆ SCINTILLA_TRICKS()

SCINTILLA_TRICKS::SCINTILLA_TRICKS ( wxStyledTextCtrl *  aScintilla,
const wxString &  aBraces,
bool  aSingleLine,
std::function< void()>  m_enterCallback = [](){ } 
)

Definition at line 35 of file scintilla_tricks.cpp.

36  :
37  m_te( aScintilla ),
38  m_braces( aBraces ),
39  m_lastCaretPos( -1 ),
40  m_suppressAutocomplete( false ),
41  m_singleLine( aSingleLine ),
42  m_returnCallback( aReturnCallback )
43 {
44  // A hack which causes Scintilla to auto-size the text editor canvas
45  // See: https://github.com/jacobslusser/ScintillaNET/issues/216
46  m_te->SetScrollWidth( 1 );
47  m_te->SetScrollWidthTracking( true );
48 
49  setupStyles();
50 
51  // Set up autocomplete
52  m_te->AutoCompSetIgnoreCase( true );
53  m_te->AutoCompSetFillUps( m_braces[1] );
54  m_te->AutoCompSetMaxHeight( 20 );
55 
56  // Hook up events
57  m_te->Bind( wxEVT_STC_UPDATEUI, &SCINTILLA_TRICKS::onScintillaUpdateUI, this );
58 
59  // Dispatch command-keys in Scintilla control.
60  m_te->Bind( wxEVT_CHAR_HOOK, &SCINTILLA_TRICKS::onCharHook, this );
61 
62  m_te->Bind( wxEVT_SYS_COLOUR_CHANGED,
63  wxSysColourChangedEventHandler( SCINTILLA_TRICKS::onThemeChanged ), this );
64 }
void onCharHook(wxKeyEvent &aEvent)
std::function< void()> m_returnCallback
void onThemeChanged(wxSysColourChangedEvent &aEvent)
wxStyledTextCtrl * m_te
void onScintillaUpdateUI(wxStyledTextEvent &aEvent)

References m_braces, m_te, onCharHook(), onScintillaUpdateUI(), onThemeChanged(), and setupStyles().

Member Function Documentation

◆ DoAutocomplete()

void SCINTILLA_TRICKS::DoAutocomplete ( const wxString &  aPartial,
const wxArrayString &  aTokens 
)

Definition at line 375 of file scintilla_tricks.cpp.

376 {
378  return;
379 
380  wxArrayString matchedTokens;
381 
382  wxString filter = wxT( "*" ) + aPartial.Lower() + wxT( "*" );
383 
384  for( const wxString& token : aTokens )
385  {
386  if( token.Lower().Matches( filter ) )
387  matchedTokens.push_back( token );
388  }
389 
390  if( matchedTokens.size() > 0 )
391  {
392  // NB: tokens MUST be in alphabetical order because the Scintilla engine is going
393  // to do a binary search on them
394  matchedTokens.Sort( []( const wxString& first, const wxString& second ) -> int
395  {
396  return first.CmpNoCase( second );
397  });
398 
399  m_te->AutoCompShow( aPartial.size(), wxJoin( matchedTokens, ' ' ) );
400  }
401 }
wxStyledTextCtrl * m_te

References filter, m_suppressAutocomplete, and m_te.

Referenced by DIALOG_TEXT_AND_LABEL_PROPERTIES::onScintillaCharAdded(), PROPERTIES_FRAME::onScintillaCharAdded(), and DIALOG_SCH_FIELD_PROPERTIES::onScintillaCharAdded().

◆ firstNonWhitespace()

int SCINTILLA_TRICKS::firstNonWhitespace ( int  aLine,
int *  aWhitespaceCount = nullptr 
)
protected

Definition at line 299 of file scintilla_tricks.cpp.

300 {
301  int lineStart = m_te->PositionFromLine( aLine );
302 
303  if( aWhitespaceCharCount )
304  *aWhitespaceCharCount = 0;
305 
306  for( int ii = 0; ii < m_te->GetLineLength( aLine ); ++ii )
307  {
308  int c = m_te->GetCharAt( lineStart + ii );
309 
310  if( c == ' ' || c == '\t' )
311  {
312  if( aWhitespaceCharCount )
313  *aWhitespaceCharCount += 1;
314 
315  continue;
316  }
317  else
318  {
319  return c;
320  }
321  }
322 
323  return '\r';
324 }
wxStyledTextCtrl * m_te

References m_te.

Referenced by onCharHook().

◆ onCharHook()

void SCINTILLA_TRICKS::onCharHook ( wxKeyEvent &  aEvent)
protected

Definition at line 149 of file scintilla_tricks.cpp.

150 {
151  wxString c = aEvent.GetUnicodeKey();
152 
153  if( !isalpha( aEvent.GetKeyCode() ) )
154  m_suppressAutocomplete = false;
155 
156  if( aEvent.GetKeyCode() == WXK_RETURN && ( m_singleLine || aEvent.ShiftDown() ) )
157  {
159  }
160  else if( ConvertSmartQuotesAndDashes( &c ) )
161  {
162  m_te->AddText( c );
163  }
164  else if( aEvent.GetKeyCode() == WXK_TAB )
165  {
166  if( aEvent.ControlDown() )
167  {
168  int flags = 0;
169 
170  if( !aEvent.ShiftDown() )
171  flags |= wxNavigationKeyEvent::IsForward;
172 
173  wxWindow* parent = m_te->GetParent();
174 
175  while( parent && dynamic_cast<DIALOG_SHIM*>( parent ) == nullptr )
176  parent = parent->GetParent();
177 
178  if( parent )
179  parent->NavigateIn( flags );
180  }
181  else
182  {
183  m_te->Tab();
184  }
185  }
186  else if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKeyCode() == 'Z' )
187  {
188  m_te->Undo();
189  }
190  else if( ( aEvent.GetModifiers() == wxMOD_SHIFT+wxMOD_CONTROL && aEvent.GetKeyCode() == 'Z' )
191  || ( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKeyCode() == 'Y' ) )
192  {
193  m_te->Redo();
194  }
195  else if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKeyCode() == 'A' )
196  {
197  m_te->SelectAll();
198  }
199  else if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKeyCode() == 'X' )
200  {
201  m_te->Cut();
202  }
203  else if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKeyCode() == 'C' )
204  {
205  m_te->Copy();
206  }
207  else if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKeyCode() == 'V' )
208  {
209  if( m_te->GetSelectionEnd() > m_te->GetSelectionStart() )
210  m_te->DeleteBack();
211 
212  wxLogNull doNotLog; // disable logging of failed clipboard actions
213 
214  if( wxTheClipboard->Open() )
215  {
216  if( wxTheClipboard->IsSupported( wxDF_TEXT ) )
217  {
218  wxTextDataObject data;
219  wxString str;
220 
221  wxTheClipboard->GetData( data );
222  str = data.GetText();
223 
225  m_te->AddText( str );
226  }
227 
228  wxTheClipboard->Close();
229  }
230  }
231  else if( aEvent.GetKeyCode() == WXK_BACK )
232  {
233  m_te->DeleteBack();
234  }
235  else if( aEvent.GetKeyCode() == WXK_DELETE )
236  {
237  if( m_te->GetSelectionEnd() == m_te->GetSelectionStart() )
238  m_te->CharRightExtend();
239 
240  if( m_te->GetSelectionEnd() > m_te->GetSelectionStart() )
241  m_te->DeleteBack();
242  }
243  else if( aEvent.GetKeyCode() == WXK_ESCAPE )
244  {
245  if( m_te->AutoCompActive() )
246  {
247  m_te->AutoCompCancel();
248  m_suppressAutocomplete = true; // Don't run autocomplete again on the next char...
249  }
250  else
251  {
252  aEvent.Skip();
253  }
254  }
255  else if( isCtrlSlash( aEvent ) )
256  {
257  int startLine = m_te->LineFromPosition( m_te->GetSelectionStart() );
258  int endLine = m_te->LineFromPosition( m_te->GetSelectionEnd() );
259  bool comment = firstNonWhitespace( startLine ) != '#';
260  int whitespaceCount;
261 
262  m_te->BeginUndoAction();
263 
264  for( int ii = startLine; ii <= endLine; ++ii )
265  {
266  if( comment )
267  m_te->InsertText( m_te->PositionFromLine( ii ), "#" );
268  else if( firstNonWhitespace( ii, &whitespaceCount ) == '#' )
269  m_te->DeleteRange( m_te->PositionFromLine( ii ) + whitespaceCount, 1 );
270  }
271 
272  m_te->SetSelection( m_te->PositionFromLine( startLine ),
273  m_te->PositionFromLine( endLine ) + m_te->GetLineLength( endLine ) );
274 
275  m_te->EndUndoAction();
276  }
277 #ifdef __WXMAC__
278  else if( aEvent.GetModifiers() == wxMOD_RAW_CONTROL && aEvent.GetKeyCode() == 'A' )
279  {
280  m_te->LineEndWrap();
281  }
282  else if( aEvent.GetModifiers() == wxMOD_RAW_CONTROL && aEvent.GetKeyCode() == 'E' )
283  {
284  m_te->HomeWrap();
285  }
286 #endif
287  else if( aEvent.GetKeyCode() == WXK_SPECIAL20 )
288  {
289  // Proxy for a wxSysColourChangedEvent
290  setupStyles();
291  }
292  else
293  {
294  aEvent.Skip();
295  }
296 }
bool isCtrlSlash(wxKeyEvent &aEvent)
int firstNonWhitespace(int aLine, int *aWhitespaceCount=nullptr)
std::function< void()> m_returnCallback
bool ConvertSmartQuotesAndDashes(wxString *aString)
Convert curly quotes and em/en dashes to straight quotes and dashes.
wxStyledTextCtrl * m_te

References ConvertSmartQuotesAndDashes(), firstNonWhitespace(), isCtrlSlash(), m_returnCallback, m_singleLine, m_suppressAutocomplete, m_te, and setupStyles().

Referenced by SCINTILLA_TRICKS().

◆ onScintillaUpdateUI()

void SCINTILLA_TRICKS::onScintillaUpdateUI ( wxStyledTextEvent &  aEvent)
protected

Definition at line 327 of file scintilla_tricks.cpp.

328 {
329  auto isBrace = [this]( int c ) -> bool
330  {
331  return m_braces.Find( (wxChar) c ) >= 0;
332  };
333 
334  // Has the caret changed position?
335  int caretPos = m_te->GetCurrentPos();
336 
337  if( m_lastCaretPos != caretPos )
338  {
339  m_lastCaretPos = caretPos;
340  int bracePos1 = -1;
341  int bracePos2 = -1;
342 
343  // Is there a brace to the left or right?
344  if( caretPos > 0 && isBrace( m_te->GetCharAt( caretPos-1 ) ) )
345  bracePos1 = ( caretPos - 1 );
346  else if( isBrace( m_te->GetCharAt( caretPos ) ) )
347  bracePos1 = caretPos;
348 
349  if( bracePos1 >= 0 )
350  {
351  // Find the matching brace
352  bracePos2 = m_te->BraceMatch( bracePos1 );
353 
354  if( bracePos2 == -1 )
355  {
356  m_te->BraceBadLight( bracePos1 );
357  m_te->SetHighlightGuide( 0 );
358  }
359  else
360  {
361  m_te->BraceHighlight( bracePos1, bracePos2 );
362  m_te->SetHighlightGuide( m_te->GetColumn( bracePos1 ) );
363  }
364  }
365  else
366  {
367  // Turn off brace matching
368  m_te->BraceHighlight( -1, -1 );
369  m_te->SetHighlightGuide( 0 );
370  }
371  }
372 }
wxStyledTextCtrl * m_te

References m_braces, m_lastCaretPos, and m_te.

Referenced by SCINTILLA_TRICKS().

◆ onThemeChanged()

void SCINTILLA_TRICKS::onThemeChanged ( wxSysColourChangedEvent &  aEvent)
protected

Definition at line 67 of file scintilla_tricks.cpp.

68 {
69  setupStyles();
70 
71  aEvent.Skip();
72 }

References setupStyles().

Referenced by SCINTILLA_TRICKS().

◆ setupStyles()

void SCINTILLA_TRICKS::setupStyles ( )
protected

Definition at line 75 of file scintilla_tricks.cpp.

76 {
77  wxTextCtrl dummy( m_te->GetParent(), wxID_ANY );
78  wxColour foreground = dummy.GetForegroundColour();
79  wxColour background = dummy.GetBackgroundColour();
80  wxColour highlight = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT );
81  wxColour highlightText = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
82 
83  m_te->StyleSetForeground( wxSTC_STYLE_DEFAULT, foreground );
84  m_te->StyleSetBackground( wxSTC_STYLE_DEFAULT, background );
85  m_te->StyleClearAll();
86 
87  m_te->SetSelForeground( true, highlightText );
88  m_te->SetSelBackground( true, highlight );
89 
90  if( !m_singleLine )
91  {
92  // Set a monospace font with a tab width of 4. This is the closest we can get to having
93  // Scintilla mimic the stroke font's tab positioning.
94  wxFont fixedFont = KIUI::GetMonospacedUIFont();
95 
96  for( size_t i = 0; i < wxSTC_STYLE_MAX; ++i )
97  m_te->StyleSetFont( i, fixedFont );
98 
99  m_te->SetTabWidth( 4 );
100  }
101 
102  // Set up the brace highlighting
103  unsigned char r = highlight.Red();
104  unsigned char g = highlight.Green();
105  unsigned char b = highlight.Blue();
106  wxColour::MakeGrey( &r, &g, &b );
107  highlight.Set( r, g, b );
108 
109  m_te->StyleSetForeground( wxSTC_STYLE_BRACELIGHT, highlightText );
110  m_te->StyleSetBackground( wxSTC_STYLE_BRACELIGHT, highlight );
111  m_te->StyleSetForeground( wxSTC_STYLE_BRACEBAD, *wxRED );
112 }
wxFont GetMonospacedUIFont()
Definition: ui_common.cpp:82
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:72
wxStyledTextCtrl * m_te

References dummy(), KIUI::GetMonospacedUIFont(), m_singleLine, and m_te.

Referenced by onCharHook(), onThemeChanged(), and SCINTILLA_TRICKS().

Member Data Documentation

◆ m_braces

wxString SCINTILLA_TRICKS::m_braces
protected

Definition at line 54 of file scintilla_tricks.h.

Referenced by onScintillaUpdateUI(), and SCINTILLA_TRICKS().

◆ m_lastCaretPos

int SCINTILLA_TRICKS::m_lastCaretPos
protected

Definition at line 55 of file scintilla_tricks.h.

Referenced by onScintillaUpdateUI().

◆ m_returnCallback

std::function<void()> SCINTILLA_TRICKS::m_returnCallback
protected

Definition at line 59 of file scintilla_tricks.h.

Referenced by onCharHook().

◆ m_singleLine

bool SCINTILLA_TRICKS::m_singleLine
protected

Definition at line 57 of file scintilla_tricks.h.

Referenced by onCharHook(), and setupStyles().

◆ m_suppressAutocomplete

bool SCINTILLA_TRICKS::m_suppressAutocomplete
protected

Definition at line 56 of file scintilla_tricks.h.

Referenced by DoAutocomplete(), and onCharHook().

◆ m_te

wxStyledTextCtrl* SCINTILLA_TRICKS::m_te
protected

The documentation for this class was generated from the following files: