KiCad PCB EDA Suite
SCINTILLA_TRICKS Class Reference

Add cut/copy/paste, dark theme, 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)
 
void CancelAutocomplete ()
 

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
 
int m_lastSelStart
 
int m_lastSelEnd
 
bool m_suppressAutocomplete
 
bool m_singleLine
 
std::function< void()> m_returnCallback
 

Detailed Description

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

Definition at line 35 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_lastSelStart( -1 ),
41 m_lastSelEnd( -1 ),
43 m_singleLine( aSingleLine ),
44 m_returnCallback( aReturnCallback )
45{
46 // Always use LF as eol char, regardless the platform
47 m_te->SetEOLMode( wxSTC_EOL_LF );
48
49 // A hack which causes Scintilla to auto-size the text editor canvas
50 // See: https://github.com/jacobslusser/ScintillaNET/issues/216
51 m_te->SetScrollWidth( 1 );
52 m_te->SetScrollWidthTracking( true );
53
55
56 // Set up autocomplete
57 m_te->AutoCompSetIgnoreCase( true );
58 m_te->AutoCompSetFillUps( m_braces[1] );
59 m_te->AutoCompSetMaxHeight( 20 );
60
61 // Hook up events
62 m_te->Bind( wxEVT_STC_UPDATEUI, &SCINTILLA_TRICKS::onScintillaUpdateUI, this );
63
64 // Dispatch command-keys in Scintilla control.
65 m_te->Bind( wxEVT_CHAR_HOOK, &SCINTILLA_TRICKS::onCharHook, this );
66
67 m_te->Bind( wxEVT_SYS_COLOUR_CHANGED,
68 wxSysColourChangedEventHandler( SCINTILLA_TRICKS::onThemeChanged ), this );
69}
std::function< void()> m_returnCallback
void onCharHook(wxKeyEvent &aEvent)
void onThemeChanged(wxSysColourChangedEvent &aEvent)
void onScintillaUpdateUI(wxStyledTextEvent &aEvent)
wxStyledTextCtrl * m_te

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

Member Function Documentation

◆ CancelAutocomplete()

◆ DoAutocomplete()

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

Definition at line 391 of file scintilla_tricks.cpp.

392{
394 return;
395
396 wxArrayString matchedTokens;
397
398 wxString filter = wxT( "*" ) + aPartial.Lower() + wxT( "*" );
399
400 for( const wxString& token : aTokens )
401 {
402 if( token.Lower().Matches( filter ) )
403 matchedTokens.push_back( token );
404 }
405
406 if( matchedTokens.size() > 0 )
407 {
408 // NB: tokens MUST be in alphabetical order because the Scintilla engine is going
409 // to do a binary search on them
410 matchedTokens.Sort( []( const wxString& first, const wxString& second ) -> int
411 {
412 return first.CmpNoCase( second );
413 });
414
415 m_te->AutoCompShow( aPartial.size(), wxJoin( matchedTokens, m_te->AutoCompGetSeparator() ) );
416 }
417}

References filter, m_suppressAutocomplete, and m_te.

Referenced by DIALOG_SCH_FIELD_PROPERTIES::onScintillaCharAdded(), DIALOG_TEXT_PROPERTIES::onScintillaCharAdded(), PROPERTIES_FRAME::onScintillaCharAdded(), and PANEL_SETUP_RULES::onScintillaCharAdded().

◆ firstNonWhitespace()

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

Definition at line 311 of file scintilla_tricks.cpp.

312{
313 int lineStart = m_te->PositionFromLine( aLine );
314
315 if( aWhitespaceCharCount )
316 *aWhitespaceCharCount = 0;
317
318 for( int ii = 0; ii < m_te->GetLineLength( aLine ); ++ii )
319 {
320 int c = m_te->GetCharAt( lineStart + ii );
321
322 if( c == ' ' || c == '\t' )
323 {
324 if( aWhitespaceCharCount )
325 *aWhitespaceCharCount += 1;
326
327 continue;
328 }
329 else
330 {
331 return c;
332 }
333 }
334
335 return '\r';
336}

References m_te.

Referenced by onCharHook().

◆ onCharHook()

void SCINTILLA_TRICKS::onCharHook ( wxKeyEvent &  aEvent)
protected

Definition at line 158 of file scintilla_tricks.cpp.

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

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 339 of file scintilla_tricks.cpp.

340{
341 auto isBrace = [this]( int c ) -> bool
342 {
343 return m_braces.Find( (wxChar) c ) >= 0;
344 };
345
346 // Has the caret changed position?
347 int caretPos = m_te->GetCurrentPos();
348 int selStart = m_te->GetSelectionStart();
349 int selEnd = m_te->GetSelectionEnd();
350
351 if( m_lastCaretPos != caretPos || m_lastSelStart != selStart || m_lastSelEnd != selEnd )
352 {
353 m_lastCaretPos = caretPos;
354 m_lastSelStart = selStart;
355 m_lastSelEnd = selEnd;
356 int bracePos1 = -1;
357 int bracePos2 = -1;
358
359 // Is there a brace to the left or right?
360 if( caretPos > 0 && isBrace( m_te->GetCharAt( caretPos-1 ) ) )
361 bracePos1 = ( caretPos - 1 );
362 else if( isBrace( m_te->GetCharAt( caretPos ) ) )
363 bracePos1 = caretPos;
364
365 if( bracePos1 >= 0 )
366 {
367 // Find the matching brace
368 bracePos2 = m_te->BraceMatch( bracePos1 );
369
370 if( bracePos2 == -1 )
371 {
372 m_te->BraceBadLight( bracePos1 );
373 m_te->SetHighlightGuide( 0 );
374 }
375 else
376 {
377 m_te->BraceHighlight( bracePos1, bracePos2 );
378 m_te->SetHighlightGuide( m_te->GetColumn( bracePos1 ) );
379 }
380 }
381 else
382 {
383 // Turn off brace matching
384 m_te->BraceHighlight( -1, -1 );
385 m_te->SetHighlightGuide( 0 );
386 }
387 }
388}

References m_braces, m_lastCaretPos, m_lastSelEnd, m_lastSelStart, and m_te.

Referenced by SCINTILLA_TRICKS().

◆ onThemeChanged()

void SCINTILLA_TRICKS::onThemeChanged ( wxSysColourChangedEvent &  aEvent)
protected

Definition at line 72 of file scintilla_tricks.cpp.

73{
75
76 aEvent.Skip();
77}

References setupStyles().

Referenced by SCINTILLA_TRICKS().

◆ setupStyles()

void SCINTILLA_TRICKS::setupStyles ( )
protected

Definition at line 80 of file scintilla_tricks.cpp.

81{
82 wxTextCtrl dummy( m_te->GetParent(), wxID_ANY );
83 KIGFX::COLOR4D foreground = dummy.GetForegroundColour();
84 KIGFX::COLOR4D background = dummy.GetBackgroundColour();
85 KIGFX::COLOR4D highlight = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT );
86 KIGFX::COLOR4D highlightText = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT );
87
88 m_te->StyleSetForeground( wxSTC_STYLE_DEFAULT, foreground.ToColour() );
89 m_te->StyleSetBackground( wxSTC_STYLE_DEFAULT, background.ToColour() );
90 m_te->StyleClearAll();
91
92 // Scintilla doesn't handle alpha channel, which at least OSX uses in some highlight colours,
93 // such as "graphite".
94 highlight = highlight.Mix( background, highlight.a ).WithAlpha( 1.0 );
95 highlightText = highlightText.Mix( background, highlightText.a ).WithAlpha( 1.0 );
96
97 m_te->SetSelForeground( true, highlightText.ToColour() );
98 m_te->SetSelBackground( true, highlight.ToColour() );
99 m_te->SetCaretForeground( foreground.ToColour() );
100
101 if( !m_singleLine )
102 {
103 // Set a monospace font with a tab width of 4. This is the closest we can get to having
104 // Scintilla mimic the stroke font's tab positioning.
105 wxFont fixedFont = KIUI::GetMonospacedUIFont();
106
107 for( size_t i = 0; i < wxSTC_STYLE_MAX; ++i )
108 m_te->StyleSetFont( i, fixedFont );
109
110 m_te->SetTabWidth( 4 );
111 }
112
113 // Set up the brace highlighting. Scintilla doesn't handle alpha, so we construct our own
114 // 20% wash by blending with the background.
115 KIGFX::COLOR4D braceText = foreground;
116 KIGFX::COLOR4D braceHighlight = braceText.Mix( background, 0.2 );
117
118 m_te->StyleSetForeground( wxSTC_STYLE_BRACELIGHT, highlightText.ToColour() );
119 m_te->StyleSetBackground( wxSTC_STYLE_BRACELIGHT, braceHighlight.ToColour() );
120 m_te->StyleSetForeground( wxSTC_STYLE_BRACEBAD, *wxRED );
121}
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
COLOR4D WithAlpha(double aAlpha) const
Return a color with the same color, but the given alpha.
Definition: color4d.h:321
double a
Alpha component.
Definition: color4d.h:387
COLOR4D Mix(const COLOR4D &aColor, double aFactor) const
Return a color that is mixed with the input by a factor.
Definition: color4d.h:305
wxFont GetMonospacedUIFont()
Definition: ui_common.cpp:85
std::vector< FAB_LAYER_COLOR > dummy

References KIGFX::COLOR4D::a, dummy, KIUI::GetMonospacedUIFont(), m_singleLine, m_te, KIGFX::COLOR4D::Mix(), and KIGFX::COLOR4D::WithAlpha().

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

Member Data Documentation

◆ m_braces

wxString SCINTILLA_TRICKS::m_braces
protected

Definition at line 57 of file scintilla_tricks.h.

Referenced by onScintillaUpdateUI(), and SCINTILLA_TRICKS().

◆ m_lastCaretPos

int SCINTILLA_TRICKS::m_lastCaretPos
protected

Definition at line 58 of file scintilla_tricks.h.

Referenced by onScintillaUpdateUI().

◆ m_lastSelEnd

int SCINTILLA_TRICKS::m_lastSelEnd
protected

Definition at line 60 of file scintilla_tricks.h.

Referenced by onScintillaUpdateUI().

◆ m_lastSelStart

int SCINTILLA_TRICKS::m_lastSelStart
protected

Definition at line 59 of file scintilla_tricks.h.

Referenced by onScintillaUpdateUI().

◆ m_returnCallback

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

Definition at line 64 of file scintilla_tricks.h.

Referenced by onCharHook().

◆ m_singleLine

bool SCINTILLA_TRICKS::m_singleLine
protected

Definition at line 62 of file scintilla_tricks.h.

Referenced by onCharHook(), and setupStyles().

◆ m_suppressAutocomplete

bool SCINTILLA_TRICKS::m_suppressAutocomplete
protected

Definition at line 61 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: