From 14f004d2a529f036975bce307556e0f0feb956ec Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Wed, 24 May 2023 12:08:52 +0100 Subject: [PATCH] Hook up text variable auto-complete for PCBNew. Fixes https://gitlab.com/kicad/code/kicad/-/issues/14777 --- eeschema/tools/sch_drawing_tools.cpp | 4 +- eeschema/tools/sch_edit_tool.cpp | 2 +- pcbnew/board.cpp | 18 ++++++ pcbnew/board.h | 1 + pcbnew/dialogs/dialog_text_properties.cpp | 65 ++++++++++++++++++- pcbnew/dialogs/dialog_text_properties.h | 1 + pcbnew/dialogs/dialog_textbox_properties.cpp | 68 ++++++++++++++++++-- pcbnew/dialogs/dialog_textbox_properties.h | 1 + pcbnew/tools/drawing_tool.cpp | 3 +- 9 files changed, 152 insertions(+), 11 deletions(-) diff --git a/eeschema/tools/sch_drawing_tools.cpp b/eeschema/tools/sch_drawing_tools.cpp index 7852c2703c..d873f5a5a5 100644 --- a/eeschema/tools/sch_drawing_tools.cpp +++ b/eeschema/tools/sch_drawing_tools.cpp @@ -1003,7 +1003,7 @@ SCH_TEXT* SCH_DRAWING_TOOLS::createNewText( const VECTOR2I& aPosition, int aType { DIALOG_TEXT_PROPERTIES dlg( m_frame, textItem ); - // Must be quasi modal for syntax help + // QuasiModal required for syntax help and Scintilla auto-complete if( dlg.ShowQuasiModal() != wxID_OK ) { delete textItem; @@ -1648,7 +1648,7 @@ int SCH_DRAWING_TOOLS::DrawShape( const TOOL_EVENT& aEvent ) getViewControls()->SetAutoPan( false ); getViewControls()->CaptureCursor( false ); - // Must be quasi modal for syntax help + // QuasiModal required for syntax help and Scintilla auto-complete if( dlg.ShowQuasiModal() != wxID_OK ) { cleanup(); diff --git a/eeschema/tools/sch_edit_tool.cpp b/eeschema/tools/sch_edit_tool.cpp index 1a78ea26cf..9f9f1cc294 100644 --- a/eeschema/tools/sch_edit_tool.cpp +++ b/eeschema/tools/sch_edit_tool.cpp @@ -1873,7 +1873,7 @@ int SCH_EDIT_TOOL::Properties( const TOOL_EVENT& aEvent ) { DIALOG_TEXT_PROPERTIES dlg( m_frame, static_cast( curr_item ) ); - // Must be quasi modal for syntax help + // QuasiModal required for syntax help and Scintilla auto-complete if( dlg.ShowQuasiModal() == wxID_OK ) { m_toolMgr->PostEvent( EVENTS::SelectedItemsModified ); diff --git a/pcbnew/board.cpp b/pcbnew/board.cpp index a5523f23de..b911f87e40 100644 --- a/pcbnew/board.cpp +++ b/pcbnew/board.cpp @@ -352,6 +352,24 @@ std::vector BOARD::ResolveDRCExclusions( bool aCreateMarkers ) } +void BOARD::GetContextualTextVars( wxArrayString* aVars ) const +{ + auto add = + [&]( const wxString& aVar ) + { + if( !alg::contains( *aVars, aVar ) ) + aVars->push_back( aVar ); + }; + + add( wxT( "LAYER" ) ); + + GetTitleBlock().GetContextualTextVars( aVars ); + + for( std::pair entry : GetProject()->GetTextVars() ) + add( entry.first ); +} + + bool BOARD::ResolveTextVar( wxString* token, int aDepth ) const { if( token->Contains( ':' ) ) diff --git a/pcbnew/board.h b/pcbnew/board.h index 4a93104a63..05eb4a0b72 100644 --- a/pcbnew/board.h +++ b/pcbnew/board.h @@ -339,6 +339,7 @@ public: const std::map& GetProperties() const { return m_properties; } void SetProperties( const std::map& aProps ) { m_properties = aProps; } + void GetContextualTextVars( wxArrayString* aVars ) const; bool ResolveTextVar( wxString* token, int aDepth ) const; /// Visibility settings stored in board prior to 6.0, only used for loading legacy files diff --git a/pcbnew/dialogs/dialog_text_properties.cpp b/pcbnew/dialogs/dialog_text_properties.cpp index 2cd67e8856..15f911b5f9 100644 --- a/pcbnew/dialogs/dialog_text_properties.cpp +++ b/pcbnew/dialogs/dialog_text_properties.cpp @@ -26,17 +26,15 @@ #include #include #include -#include #include #include #include -#include #include #include +#include #include #include #include -#include // for KiROUND #include @@ -167,6 +165,11 @@ DIALOG_TEXT_PROPERTIES::DIALOG_TEXT_PROPERTIES( PCB_BASE_EDIT_FRAME* aParent, PC Connect( wxEVT_CHAR_HOOK, wxKeyEventHandler( DIALOG_TEXT_PROPERTIES::OnCharHook ), nullptr, this ); + m_MultiLineText->Bind( wxEVT_STC_CHARADDED, + &DIALOG_TEXT_PROPERTIES::onScintillaCharAdded, this ); + m_MultiLineText->Bind( wxEVT_STC_AUTOCOMP_CHAR_DELETED, + &DIALOG_TEXT_PROPERTIES::onScintillaCharAdded, this ); + finishDialogSettings(); } @@ -209,6 +212,62 @@ void DIALOG_TEXT_PROPERTIES::OnSetFocusText( wxFocusEvent& event ) } +void DIALOG_TEXT_PROPERTIES::onScintillaCharAdded( wxStyledTextEvent &aEvent ) +{ + wxStyledTextCtrl* te = m_MultiLineText; + wxArrayString autocompleteTokens; + int text_pos = te->GetCurrentPos(); + int start = te->WordStartPosition( text_pos, true ); + wxString partial; + + auto textVarRef = + [&]( int pos ) + { + return pos >= 2 && te->GetCharAt( pos-2 ) == '$' && te->GetCharAt( pos-1 ) == '{'; + }; + + // Check for cross-reference + if( start > 1 && te->GetCharAt( start-1 ) == ':' ) + { + int refStart = te->WordStartPosition( start-1, true ); + + if( textVarRef( refStart ) ) + { + partial = te->GetRange( start, text_pos ); + + wxString ref = te->GetRange( refStart, start-1 ); + BOARD* board = m_item->GetBoard(); + + for( FOOTPRINT* candidate : board->Footprints() ) + { + if( candidate->GetReference() == ref ) + { + candidate->GetContextualTextVars( &autocompleteTokens ); + break; + } + } + } + } + else if( textVarRef( start ) ) + { + partial = te->GetTextRange( start, text_pos ); + + BOARD* board = m_item->GetBoard(); + + board->GetContextualTextVars( &autocompleteTokens ); + + if( FOOTPRINT* footprint = m_item->GetParentFootprint() ) + footprint->GetContextualTextVars( &autocompleteTokens ); + + for( std::pair entry : board->GetProject()->GetTextVars() ) + autocompleteTokens.push_back( entry.first ); + } + + m_scintillaTricks->DoAutocomplete( partial, autocompleteTokens ); + m_MultiLineText->SetFocus(); +} + + bool DIALOG_TEXT_PROPERTIES::TransferDataToWindow() { BOARD* board = m_frame->GetBoard(); diff --git a/pcbnew/dialogs/dialog_text_properties.h b/pcbnew/dialogs/dialog_text_properties.h index 482d47a524..5b3ec29628 100644 --- a/pcbnew/dialogs/dialog_text_properties.h +++ b/pcbnew/dialogs/dialog_text_properties.h @@ -56,6 +56,7 @@ private: void onAlignButton( wxCommandEvent &aEvent ) override; void onValignButton( wxCommandEvent &aEvent ) override; void onThickness( wxCommandEvent &aEvent ) override; + void onScintillaCharAdded( wxStyledTextEvent &aEvent ); bool TransferDataToWindow() override; bool TransferDataFromWindow() override; diff --git a/pcbnew/dialogs/dialog_textbox_properties.cpp b/pcbnew/dialogs/dialog_textbox_properties.cpp index b25d22d73e..02cbc8b3cd 100644 --- a/pcbnew/dialogs/dialog_textbox_properties.cpp +++ b/pcbnew/dialogs/dialog_textbox_properties.cpp @@ -25,19 +25,16 @@ #include #include #include -#include #include #include #include #include -#include #include #include +#include #include #include -#include // for KiROUND #include -#include "macros.h" DIALOG_TEXTBOX_PROPERTIES::DIALOG_TEXTBOX_PROPERTIES( PCB_BASE_EDIT_FRAME* aParent, PCB_TEXTBOX* aTextBox ) : @@ -125,6 +122,11 @@ DIALOG_TEXTBOX_PROPERTIES::DIALOG_TEXTBOX_PROPERTIES( PCB_BASE_EDIT_FRAME* aPare Connect( wxEVT_CHAR_HOOK, wxKeyEventHandler( DIALOG_TEXTBOX_PROPERTIES::OnCharHook ), nullptr, this ); + m_MultiLineText->Bind( wxEVT_STC_CHARADDED, + &DIALOG_TEXTBOX_PROPERTIES::onScintillaCharAdded, this ); + m_MultiLineText->Bind( wxEVT_STC_AUTOCOMP_CHAR_DELETED, + &DIALOG_TEXTBOX_PROPERTIES::onScintillaCharAdded, this ); + finishDialogSettings(); } @@ -141,10 +143,68 @@ DIALOG_TEXTBOX_PROPERTIES::~DIALOG_TEXTBOX_PROPERTIES() int PCB_BASE_EDIT_FRAME::ShowTextBoxPropertiesDialog( PCB_TEXTBOX* aTextBox ) { DIALOG_TEXTBOX_PROPERTIES dlg( this, aTextBox ); + + // QuasiModal required for Scintilla auto-complete return dlg.ShowQuasiModal(); } +void DIALOG_TEXTBOX_PROPERTIES::onScintillaCharAdded( wxStyledTextEvent &aEvent ) +{ + wxStyledTextCtrl* te = m_MultiLineText; + wxArrayString autocompleteTokens; + int text_pos = te->GetCurrentPos(); + int start = te->WordStartPosition( text_pos, true ); + wxString partial; + + auto textVarRef = + [&]( int pos ) + { + return pos >= 2 && te->GetCharAt( pos-2 ) == '$' && te->GetCharAt( pos-1 ) == '{'; + }; + + // Check for cross-reference + if( start > 1 && te->GetCharAt( start-1 ) == ':' ) + { + int refStart = te->WordStartPosition( start-1, true ); + + if( textVarRef( refStart ) ) + { + partial = te->GetRange( start, text_pos ); + + wxString ref = te->GetRange( refStart, start-1 ); + BOARD* board = m_textBox->GetBoard(); + + for( FOOTPRINT* candidate : board->Footprints() ) + { + if( candidate->GetReference() == ref ) + { + candidate->GetContextualTextVars( &autocompleteTokens ); + break; + } + } + } + } + else if( textVarRef( start ) ) + { + partial = te->GetTextRange( start, text_pos ); + + BOARD* board = m_textBox->GetBoard(); + + board->GetContextualTextVars( &autocompleteTokens ); + + if( FOOTPRINT* footprint = m_textBox->GetParentFootprint() ) + footprint->GetContextualTextVars( &autocompleteTokens ); + + for( std::pair entry : board->GetProject()->GetTextVars() ) + autocompleteTokens.push_back( entry.first ); + } + + m_scintillaTricks->DoAutocomplete( partial, autocompleteTokens ); + m_MultiLineText->SetFocus(); +} + + bool DIALOG_TEXTBOX_PROPERTIES::TransferDataToWindow() { BOARD* board = m_frame->GetBoard(); diff --git a/pcbnew/dialogs/dialog_textbox_properties.h b/pcbnew/dialogs/dialog_textbox_properties.h index b60dc6c330..1815c7ec85 100644 --- a/pcbnew/dialogs/dialog_textbox_properties.h +++ b/pcbnew/dialogs/dialog_textbox_properties.h @@ -47,6 +47,7 @@ private: void onAlignButton( wxCommandEvent &aEvent ) override; void onThickness( wxCommandEvent &aEvent ) override; void onBorderChecked( wxCommandEvent& event ) override; + void onScintillaCharAdded( wxStyledTextEvent &aEvent ); bool TransferDataToWindow() override; bool TransferDataFromWindow() override; diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp index bb79c1d5b1..42d7892933 100644 --- a/pcbnew/tools/drawing_tool.cpp +++ b/pcbnew/tools/drawing_tool.cpp @@ -888,7 +888,8 @@ int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent ) RunMainStack( [&]() { - cancelled = !textDialog.ShowModal(); + // QuasiModal required for Scintilla auto-complete + cancelled = !textDialog.ShowQuasiModal(); } ); if( cancelled || NoPrintableChars( text->GetText() ) )