diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index e4c804af2..b5beb6160 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -585,6 +585,10 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { Float getViewportWidth( const bool& forceVScroll = false ) const; + bool getShowIndentationGuides() const; + + void setShowIndentationGuides( bool showIndentationGuides ); + protected: struct LastXOffset { TextPosition position{ 0, 0 }; @@ -620,6 +624,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { bool mMinimapHover{ false }; bool mAutoCloseXMLTags{ false }; bool mFindReplaceEnabled{ true }; + bool mShowIndentationGuides{ false }; TextRange mLinkPosition; String mLink; Uint32 mTabWidth; @@ -782,6 +787,9 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { virtual void drawWhitespaces( const std::pair& lineRange, const Vector2f& startScroll, const Float& lineHeight ); + virtual void drawIndentationGuides( const std::pair& lineRange, + const Vector2f& startScroll, const Float& lineHeight ); + virtual void drawLineEndings( const std::pair& lineRange, const Vector2f& startScroll, const Float& lineHeight ); diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 79d9e376b..daae32f3d 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -290,6 +290,10 @@ void UICodeEditor::draw() { drawWordMatch( mHighlightWord, lineRange, startScroll, lineHeight ); } + if ( mShowIndentationGuides ) { + drawIndentationGuides( lineRange, startScroll, lineHeight ); + } + // Draw tab marker if ( mShowWhitespaces ) { drawWhitespaces( lineRange, startScroll, lineHeight ); @@ -574,6 +578,17 @@ Float UICodeEditor::getViewportWidth( const bool& forceVScroll ) const { return viewWidth; } +bool UICodeEditor::getShowIndentationGuides() const { + return mShowIndentationGuides; +} + +void UICodeEditor::setShowIndentationGuides( bool showIndentationGuides ) { + if ( showIndentationGuides != mShowIndentationGuides ) { + mShowIndentationGuides = showIndentationGuides; + invalidateDraw(); + } +} + UICodeEditor* UICodeEditor::setFontSize( const Float& dpSize ) { if ( mFontStyleConfig.CharacterSize != dpSize ) { mFontStyleConfig.CharacterSize = @@ -2811,8 +2826,8 @@ void UICodeEditor::drawWhitespaces( const std::pair& lineRange, Color color( Color( mWhitespaceColor ).blendAlpha( mAlpha ) ); unsigned int fontSize = getCharacterSize(); // We use the GlyphDrawable since can batch the draw calls instead of Text. - GlyphDrawable* adv = mFont->getGlyphDrawable( 187 /*'»'*/, fontSize ); - GlyphDrawable* cpoint = mFont->getGlyphDrawable( 183 /*'·'*/, fontSize ); + GlyphDrawable* adv = mFont->getGlyphDrawable( L'»', fontSize ); + GlyphDrawable* cpoint = mFont->getGlyphDrawable( L'·', fontSize ); Float tabCenter = ( tabWidth - adv->getPixelsSize().getWidth() ) * 0.5f; adv->setDrawMode( GlyphDrawable::DrawMode::Text ); cpoint->setDrawMode( GlyphDrawable::DrawMode::Text ); @@ -2842,6 +2857,47 @@ void UICodeEditor::drawWhitespaces( const std::pair& lineRange, } } +static Int64 getLineSpaces( TextDocument& doc, int line, int dir, int indentSize ) { + if ( line < 0 || line >= (int)doc.linesCount() ) + return -1; + const auto& text = doc.line( line ).getText(); + if ( text.size() <= 1 ) + return -1; + auto s = text.find_first_not_of( " \t\n" ); + if ( s == std::string::npos ) + return -getLineSpaces( doc, line + dir, dir, indentSize ); + int n = 0; + for ( size_t i = 0; i < s; ++i ) + n += text[i] == ' ' ? 1 : indentSize; + return n; +} + +static Int64 getLineIndentGuideSpaces( TextDocument& doc, int line, int indentSize ) { + if ( doc.line( line ).getText().find_first_not_of( " \t\n" ) == std::string::npos ) + return eemax( getLineSpaces( doc, line - 1, -1, indentSize ), + getLineSpaces( doc, line + 1, 1, indentSize ) ); + return getLineSpaces( doc, line, 0, indentSize ); +} + +void UICodeEditor::drawIndentationGuides( const std::pair& lineRange, + const Vector2f& startScroll, const Float& lineHeight ) { + Primitives p; + p.setForceDraw( false ); + Float w = eefloor( PixelDensity::dpToPx( 1 ) ); + String idt( mDoc->getIndentString() ); + int spaceW = getTextWidth( " " ); + p.setColor( Color( mWhitespaceColor ).blendAlpha( mAlpha ) ); + int indentSize = mDoc->getIndentType() == TextDocument::IndentType::IndentTabs + ? getTabWidth() + : mDoc->getIndentWidth(); + for ( int index = lineRange.first; index <= lineRange.second; index++ ) { + Vector2f position( { startScroll.x, startScroll.y + lineHeight * index } ); + int spaces = getLineIndentGuideSpaces( *mDoc.get(), index, indentSize ); + for ( int i = 0; i < spaces; i += indentSize ) + p.drawRectangle( Rectf( { position.x + spaceW * i, position.y }, { w, lineHeight } ) ); + } +} + void UICodeEditor::drawLineEndings( const std::pair& lineRange, const Vector2f& startScroll, const Float& lineHeight ) { diff --git a/src/tools/ecode/appconfig.cpp b/src/tools/ecode/appconfig.cpp index 6bbc93ce1..f1d1b1f76 100644 --- a/src/tools/ecode/appconfig.cpp +++ b/src/tools/ecode/appconfig.cpp @@ -79,6 +79,7 @@ void AppConfig::load( const std::string& confPath, std::string& keybindingsPath, editor.showLineNumbers = ini.getValueB( "editor", "show_line_numbers", true ); editor.showWhiteSpaces = ini.getValueB( "editor", "show_white_spaces", true ); editor.showLineEndings = ini.getValueB( "editor", "show_line_endings", false ); + editor.showIndentationGuides = ini.getValueB( "editor", "show_indentation_guides", false ); editor.highlightMatchingBracket = ini.getValueB( "editor", "highlight_matching_brackets", true ); editor.highlightCurrentLine = ini.getValueB( "editor", "highlight_current_line", true ); @@ -197,6 +198,7 @@ void AppConfig::save( const std::vector& recentFiles, String::join( urlEncode( recentFolders ), ';' ) ); ini.setValueB( "editor", "show_line_numbers", editor.showLineNumbers ); ini.setValueB( "editor", "show_white_spaces", editor.showWhiteSpaces ); + ini.setValueB( "editor", "show_indentation_guides", editor.showIndentationGuides ); ini.setValueB( "editor", "show_line_endings", editor.showLineEndings ); ini.setValueB( "editor", "highlight_matching_brackets", editor.highlightMatchingBracket ); ini.setValueB( "editor", "highlight_current_line", editor.highlightCurrentLine ); diff --git a/src/tools/ecode/appconfig.hpp b/src/tools/ecode/appconfig.hpp index 8ca369a61..d5b4495bd 100644 --- a/src/tools/ecode/appconfig.hpp +++ b/src/tools/ecode/appconfig.hpp @@ -51,6 +51,7 @@ struct CodeEditorConfig { bool showLineNumbers{ true }; bool showWhiteSpaces{ true }; bool showLineEndings{ false }; + bool showIndentationGuides{ false }; bool highlightMatchingBracket{ true }; bool verticalScrollbar{ true }; bool horizontalScrollbar{ true }; diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 8929dbce8..8bbf64cfb 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -1656,6 +1656,7 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { editor->setShowLineNumber( config.showLineNumbers ); editor->setShowWhitespaces( config.showWhiteSpaces ); editor->setShowLineEndings( config.showLineEndings ); + editor->setShowIndentationGuides( config.showIndentationGuides ); editor->setHighlightMatchingBracket( config.highlightMatchingBracket ); editor->setVerticalScrollBarEnabled( config.verticalScrollbar ); editor->setHorizontalScrollBarEnabled( config.horizontalScrollbar ); @@ -1950,7 +1951,7 @@ UIMessageBox* App::newInputMsgBox( const String& title, const String& msg ) { static void fsRenameFile( const std::string& fpath, const std::string& newFilePath ) { #if EE_PLATFORM == EE_PLATFORM_WIN - fs::rename( String( fpath ).toWideString(), String(newFilePath).toWideString() ); + fs::rename( String( fpath ).toWideString(), String( newFilePath ).toWideString() ); #else fs::rename( fpath, newFilePath ); #endif diff --git a/src/tools/ecode/settingsmenu.cpp b/src/tools/ecode/settingsmenu.cpp index 58eac463b..b4535b9f4 100644 --- a/src/tools/ecode/settingsmenu.cpp +++ b/src/tools/ecode/settingsmenu.cpp @@ -1033,6 +1033,9 @@ UIMenu* SettingsMenu::createViewMenu() { mViewMenu->addCheckBox( i18n( "show_line_endings", "Show Line Endings" ) ) ->setActive( mApp->getConfig().editor.showLineEndings ) ->setId( "show-line-endings" ); + mViewMenu->addCheckBox( i18n( "show_indentation_guides", "Show Indentation Guides" ) ) + ->setActive( mApp->getConfig().editor.showIndentationGuides ) + ->setId( "show-indentation-guides" ); mViewMenu->addCheckBox( i18n( "show_doc_info", "Show Document Info" ) ) ->setActive( mApp->getConfig().editor.showDocInfo ) ->setId( "show-doc-info" ); @@ -1106,6 +1109,12 @@ UIMenu* SettingsMenu::createViewMenu() { mSplitter->forEachEditor( [&]( UICodeEditor* editor ) { editor->setShowLineEndings( mApp->getConfig().editor.showLineEndings ); } ); + } else if ( item->getId() == "show-indentation-guides" ) { + mApp->getConfig().editor.showIndentationGuides = + item->asType()->isActive(); + mSplitter->forEachEditor( [&]( UICodeEditor* editor ) { + editor->setShowIndentationGuides( mApp->getConfig().editor.showIndentationGuides ); + } ); } else if ( item->getId() == "show-doc-info" ) { mApp->getConfig().editor.showDocInfo = item->asType()->isActive(); if ( mApp->getDocInfo() )