diff --git a/include/eepp/system/fileinfo.hpp b/include/eepp/system/fileinfo.hpp index 3e1b3e1cf..049cc718a 100644 --- a/include/eepp/system/fileinfo.hpp +++ b/include/eepp/system/fileinfo.hpp @@ -22,11 +22,13 @@ class EE_API FileInfo { FileInfo( const std::string& filepath, bool linkInfo ); - bool operator==( const FileInfo& Other ) const; + FileInfo( const FileInfo& other ); - bool operator!=( const FileInfo& Other ) const; + bool operator==( const FileInfo& other ) const; - FileInfo& operator=( const FileInfo& Other ); + bool operator!=( const FileInfo& other ) const; + + FileInfo& operator=( const FileInfo& other ); bool isExecutable() const; @@ -36,7 +38,7 @@ class EE_API FileInfo { bool isReadable() const; - bool sameInode( const FileInfo& Other ) const; + bool sameInode( const FileInfo& other ) const; bool isLink() const; diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index 35909f3a8..a251d3472 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -91,6 +91,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { public: struct MinimapConfig { Float width{ 100 }; // dp width + Float maxPercentWidth{ 0.1f }; // 0..1 max width that a minimap can ocupy on the editor view. bool syntaxHighlight{ true }; Float scale{ 1 }; int tabWidth{ 4 }; @@ -435,12 +436,17 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void scrollToCursor( bool centered = true ); - void scrollToMakeVisible( const TextPosition& position, bool centered = false ); + void scrollTo( const TextPosition& position, bool centered = false, + bool forceExactPosition = false ); const MinimapConfig& getMinimapConfig() const; void setMinimapConfig( const MinimapConfig& newMinimapConfig ); + bool isMinimapShown() const; + + void showMinimap( bool showMinimap ); + protected: struct LastXOffset { TextPosition position; @@ -669,6 +675,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { bool isMinimapFileTooLarge() const; Rectf getMinimapRect( const Vector2f& start ) const; + + Float getMinimapWidth() const; }; }} // namespace EE::UI diff --git a/include/eepp/ui/uiscrollbar.hpp b/include/eepp/ui/uiscrollbar.hpp index 74896cd80..07ede013e 100644 --- a/include/eepp/ui/uiscrollbar.hpp +++ b/include/eepp/ui/uiscrollbar.hpp @@ -78,6 +78,8 @@ class EE_API UIScrollBar : public UIWidget { virtual std::string getPropertyString( const PropertyDefinition* propertyDef, const Uint32& propertyIndex = 0 ); + bool isDragging() const; + protected: ScrollBarType mScrollBarStyle; UISlider* mSlider; diff --git a/include/eepp/ui/uislider.hpp b/include/eepp/ui/uislider.hpp index b887a9334..d0433c2b8 100644 --- a/include/eepp/ui/uislider.hpp +++ b/include/eepp/ui/uislider.hpp @@ -79,6 +79,8 @@ class EE_API UISlider : public UIWidget { Sizef getMinimumSize(); + bool isDragging() const; + protected: UIOrientation mOrientation; bool mAllowHalfSliderOut; diff --git a/src/eepp/system/fileinfo.cpp b/src/eepp/system/fileinfo.cpp index 3a966279b..5c1b493c6 100644 --- a/src/eepp/system/fileinfo.cpp +++ b/src/eepp/system/fileinfo.cpp @@ -87,6 +87,15 @@ FileInfo::FileInfo( const std::string& filepath, bool linkInfo ) : } } +FileInfo::FileInfo( const FileInfo& other ) : + mFilepath( other.mFilepath ), + mModificationTime( other.mModificationTime ), + mSize( other.mSize ), + mOwnerId( other.mOwnerId ), + mGroupId( other.mGroupId ), + mPermissions( other.mPermissions ), + mInode( other.mInode ) {} + void FileInfo::getInfo() { #if EE_PLATFORM == EE_PLATFORM_WIN if ( mFilepath.size() == 3 && mFilepath[1] == ':' && @@ -265,14 +274,14 @@ bool FileInfo::exists() const { return 0 == res; } -FileInfo& FileInfo::operator=( const FileInfo& Other ) { - this->mFilepath = Other.mFilepath; - this->mSize = Other.mSize; - this->mModificationTime = Other.mModificationTime; - this->mGroupId = Other.mGroupId; - this->mOwnerId = Other.mOwnerId; - this->mPermissions = Other.mPermissions; - this->mInode = Other.mInode; +FileInfo& FileInfo::operator=( const FileInfo& other ) { + this->mFilepath = other.mFilepath; + this->mSize = other.mSize; + this->mModificationTime = other.mModificationTime; + this->mGroupId = other.mGroupId; + this->mOwnerId = other.mOwnerId; + this->mPermissions = other.mPermissions; + this->mInode = other.mInode; return *this; } @@ -285,12 +294,12 @@ bool FileInfo::isExecutable() const { #endif } -bool FileInfo::sameInode( const FileInfo& Other ) const { - return inodeSupported() && mInode == Other.mInode; +bool FileInfo::sameInode( const FileInfo& other ) const { + return inodeSupported() && mInode == other.mInode; } -bool FileInfo::operator!=( const FileInfo& Other ) const { - return !( *this == Other ); +bool FileInfo::operator!=( const FileInfo& other ) const { + return !( *this == other ); } }} // namespace EE::System diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index ba4cd3d0e..251006d70 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -305,6 +305,7 @@ void UICodeEditor::scheduledUpdate( const Time& ) { if ( !( getUISceneNode()->getWindow()->getInput()->getPressTrigger() & EE_BUTTON_LMASK ) ) { if ( mDraggingMinimap ) { mDraggingMinimap = false; + mVScrollBar->setEnabled( true ); getUISceneNode()->setCursor( Cursor::Arrow ); } mMouseDown = false; @@ -496,7 +497,7 @@ Float UICodeEditor::getViewportWidth( const bool& forceVScroll ) const { Float vScrollWidth = mVScrollBar->isVisible() || forceVScroll ? mVScrollBar->getPixelsSize().getWidth() : 0.f; if ( mMinimapEnabled ) - vScrollWidth += PixelDensity::dpToPx( mMinimapConfig.width ); + vScrollWidth += getMinimapWidth(); Float viewWidth = eefloor( mSize.getWidth() - mPaddingPx.Left - mPaddingPx.Right - getLineNumberWidth() - vScrollWidth ); return viewWidth; @@ -907,7 +908,8 @@ Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags if ( mMinimapEnabled ) { Rectf rect( getMinimapRect( getScreenStart() ) ); - if ( rect.contains( position.asFloat() ) ) { + if ( !mVScrollBar->isDragging() && !mDraggingMinimap && + rect.contains( position.asFloat() ) ) { Int64 lineCount = mDoc->linesCount(); Float lineSpacing = getMinimapLineSpacing(); Float minimapHeight = lineCount * lineSpacing; @@ -916,11 +918,14 @@ Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags minimapHeight = rect.getHeight(); Float dy = position.y - rect.Top; Int64 jumpToLine = eefloor( ( dy / minimapHeight ) * lineCount ) + 1; - scrollToMakeVisible( { jumpToLine, 0 } ); + scrollTo( { jumpToLine, 0 }, true, true ); mDraggingMinimap = true; mMouseDown = true; getUISceneNode()->getWindow()->getInput()->captureMouse( true ); getUISceneNode()->setCursor( Cursor::Arrow ); + mVScrollBar->setEnabled( false ); + return 1; + } else if ( mDraggingMinimap ) { return 1; } } @@ -951,12 +956,11 @@ Uint32 UICodeEditor::onMouseMove( const Vector2i& position, const Uint32& flags Int64 lineCount = mDoc->linesCount(); Float lineSpacing = getMinimapLineSpacing(); Float minimapHeight = lineCount * lineSpacing; - bool isTooLarge = isMinimapFileTooLarge(); - if ( isTooLarge ) + if ( isMinimapFileTooLarge() ) minimapHeight = rect.getHeight(); Float dy = position.y - rect.Top; Int64 jumpToLine = eefloor( ( dy / minimapHeight ) * lineCount ) + 1; - scrollToMakeVisible( { jumpToLine, 0 } ); + scrollTo( { jumpToLine, 0 }, true, true ); getUISceneNode()->setCursor( Cursor::Arrow ); return 1; } @@ -990,6 +994,7 @@ Uint32 UICodeEditor::onMouseUp( const Vector2i& position, const Uint32& flags ) if ( flags & EE_BUTTON_LMASK ) { if ( mDraggingMinimap ) { mDraggingMinimap = false; + mVScrollBar->setEnabled( true ); getUISceneNode()->setCursor( Cursor::Arrow ); } @@ -1194,7 +1199,7 @@ void UICodeEditor::updateScrollBar() { void UICodeEditor::goToLine( const TextPosition& position, bool centered ) { mDoc->setSelection( position ); - scrollToMakeVisible( mDoc->getSelection().start(), centered ); + scrollTo( mDoc->getSelection().start(), centered ); } bool UICodeEditor::getAutoCloseBrackets() const { @@ -1257,13 +1262,13 @@ void UICodeEditor::copyFilePath() { } void UICodeEditor::scrollToCursor( bool centered ) { - scrollToMakeVisible( mDoc->getSelection().start(), centered ); + scrollTo( mDoc->getSelection().start(), centered ); } void UICodeEditor::updateEditor() { mDoc->setPageSize( getVisibleLinesCount() ); if ( mDoc->getActiveClient() == this ) - scrollToMakeVisible( mDoc->getSelection().start() ); + scrollTo( mDoc->getSelection().start() ); updateScrollBar(); mDirtyEditor = false; } @@ -1334,18 +1339,23 @@ int UICodeEditor::getVisibleLinesCount() { return lines.second - lines.first; } -void UICodeEditor::scrollToMakeVisible( const TextPosition& position, bool centered ) { +void UICodeEditor::scrollTo( const TextPosition& position, bool centered, + bool forceExactPosition ) { auto lineRange = getVisibleLineRange(); Int64 minDistance = mHScrollBar->isVisible() ? 3 : 2; - if ( position.line() <= lineRange.first || position.line() >= lineRange.second - minDistance ) { + if ( forceExactPosition || position.line() <= lineRange.first || + position.line() >= lineRange.second - minDistance ) { // Vertical Scroll Float lineHeight = getLineHeight(); Float min = eefloor( lineHeight * ( eemax( 0, position.line() - 1 ) ) ); Float max = eefloor( lineHeight * ( position.line() + minDistance ) - mSize.getHeight() ); Float halfScreenLines = eefloor( mSize.getHeight() / lineHeight * 0.5f ); - if ( min < mScroll.y ) { + + if ( forceExactPosition ) { + setScrollY( lineHeight * ( eemax( 0, position.line() - 1 - halfScreenLines ) ) ); + } else if ( min < mScroll.y ) { if ( centered ) { if ( position.line() - 1 - halfScreenLines >= 0 ) min = eefloor( lineHeight * @@ -1382,6 +1392,14 @@ void UICodeEditor::setMinimapConfig( const UICodeEditor::MinimapConfig& minimapC mMinimapConfig = minimapConfig; } +bool UICodeEditor::isMinimapShown() const { + return mMinimapEnabled; +} + +void UICodeEditor::showMinimap( bool showMinimap ) { + mMinimapEnabled = showMinimap; +} + void UICodeEditor::setScrollX( const Float& val, bool emmitEvent ) { Float oldVal = mScroll.x; mScroll.x = eefloor( eemax( val, 0.f ) ); @@ -2479,8 +2497,16 @@ void UICodeEditor::resetPreviewColor() { invalidateDraw(); } -Rectf UICodeEditor::getMinimapRect( const Vector2f& start ) const { +Float UICodeEditor::getMinimapWidth() const { Float w = PixelDensity::dpToPx( mMinimapConfig.width ); + // Max [mMinimapConfig.maxPercentWidth]% of the editor view width + if ( w / getPixelsSize().getWidth() > mMinimapConfig.maxPercentWidth ) + w = getPixelsSize().getWidth() * mMinimapConfig.maxPercentWidth; + return w; +} + +Rectf UICodeEditor::getMinimapRect( const Vector2f& start ) const { + Float w = getMinimapWidth(); Float h = getPixelsSize().getHeight() - ( mHScrollBar->isVisible() ? mHScrollBar->getPixelsSize().getHeight() : 0.f ); return Rectf( @@ -2516,13 +2542,14 @@ void UICodeEditor::drawMinimap( const Vector2f& start, const std::pair } Primitives primitives; + primitives.setForceDraw( false ); if ( mMinimapConfig.drawBackground ) { - primitives.setColor( getBackgroundColor() ); + primitives.setColor( getBackgroundColor().blendAlpha( mAlpha ) ); primitives.drawRectangle( rect ); } - primitives.setColor( mCurrentLineBackgroundColor ); + primitives.setColor( Color( mCurrentLineBackgroundColor ).blendAlpha( mAlpha ) ); primitives.drawRectangle( { { rect.Left, visibleY }, Sizef( rect.getWidth(), scrollerHeight ) } ); @@ -2532,9 +2559,9 @@ void UICodeEditor::drawMinimap( const Vector2f& start, const std::pair rect.Top + ( mDoc->getSelection().end().line() - minimapStartLine ) * lineSpacing; Float selectionMinY = eemin( selectionY, selectionY2 ); Float selectionH = eeabs( selectionY2 - selectionY ) + 1; - primitives.setColor( mSelectionMatchColor ); + primitives.setColor( Color( mSelectionMatchColor ).blendAlpha( mAlpha ) ); primitives.drawRectangle( { { rect.Left, selectionMinY }, { rect.getWidth(), selectionH } } ); - primitives.setColor( mCaretColor ); + primitives.setColor( Color( mCaretColor, 128 ).blendAlpha( mAlpha ) ); primitives.drawRectangle( { { rect.Left, selectionY }, { rect.getWidth(), lineSpacing } } ); Float gutterWidth = PixelDensity::dpToPx( mMinimapConfig.gutterWidth ); @@ -2555,7 +2582,7 @@ void UICodeEditor::drawMinimap( const Vector2f& start, const std::pair } if ( batchWidth > 0 ) { - primitives.setColor( color ); + primitives.setColor( color.blendAlpha( mAlpha ) ); primitives.drawRectangle( { { batchStart, lineY }, { batchWidth, charHeight } } ); } @@ -2567,12 +2594,41 @@ void UICodeEditor::drawMinimap( const Vector2f& start, const std::pair int endidx = minimapStartLine + maxMinmapLines; endidx = eemin( endidx, lineCount - 1 ); + auto drawWordMatch = [&]( const String& text, const Int64& ln ) { + size_t pos = 0; + const String& line( mDoc->line( ln ).getText() ); + if ( line.size() > 300 ) + return; + primitives.setColor( Color( mSelectionMatchColor ).blendAlpha( mAlpha ) ); + + Float widthScale = charSpacing / getGlyphWidth(); + do { + pos = line.find( text, pos ); + if ( pos != String::InvalidPos ) { + Rectf selRect; + Int64 startCol = pos; + Int64 endCol = pos + text.size(); + selRect.Top = lineY; + selRect.Bottom = lineY + charHeight; + selRect.Left = batchStart + getXOffsetCol( { ln, startCol } ) * widthScale; + selRect.Right = batchStart + getXOffsetCol( { ln, endCol } ) * widthScale; + primitives.drawRectangle( selRect ); + pos = endCol; + } else { + break; + } + } while ( true ); + }; + if ( mMinimapConfig.syntaxHighlight ) { for ( int index = minimapStartLine; index <= endidx; index++ ) { batchSyntaxType = "normal"; batchStart = rect.Left + gutterWidth; batchWidth = 0; + if ( !mHighlightWord.empty() ) + drawWordMatch( mHighlightWord, index ); + auto& tokens = mHighlighter.getLine( index ); for ( auto& token : tokens ) { String text( token.text ); @@ -2625,6 +2681,8 @@ void UICodeEditor::drawMinimap( const Vector2f& start, const std::pair lineY = lineY + lineSpacing; } } + + primitives.setForceDraw( true ); } Vector2f UICodeEditor::getScreenStart() const { diff --git a/src/eepp/ui/uiscrollbar.cpp b/src/eepp/ui/uiscrollbar.cpp index 7b9bc0cd0..8e9f92372 100644 --- a/src/eepp/ui/uiscrollbar.cpp +++ b/src/eepp/ui/uiscrollbar.cpp @@ -380,6 +380,10 @@ std::string UIScrollBar::getPropertyString( const PropertyDefinition* propertyDe } } +bool UIScrollBar::isDragging() const { + return mSlider->isDragging(); +} + bool UIScrollBar::applyProperty( const StyleSheetProperty& attribute ) { if ( !checkPropertyDefinition( attribute ) ) return false; diff --git a/src/eepp/ui/uislider.cpp b/src/eepp/ui/uislider.cpp index 79d7cf503..5ffd3b296 100644 --- a/src/eepp/ui/uislider.cpp +++ b/src/eepp/ui/uislider.cpp @@ -158,13 +158,13 @@ void UISlider::adjustChilds() { if ( UIOrientation::Horizontal == mOrientation ) { Float size = eeceil( eemax( - ( ( Float )( getSize().getWidth() - mPadding.Left - mPadding.Right ) * percent ), + ( (Float)( getSize().getWidth() - mPadding.Left - mPadding.Right ) * percent ), mSlider->getMinSize().getWidth() ) ); mSlider->setSize( size, mSlider->getSize().getHeight() ); } else { Float size = eeceil( eemax( - ( ( Float )( getSize().getHeight() - mPadding.Top - mPadding.Bottom ) * percent ), + ( (Float)( getSize().getHeight() - mPadding.Top - mPadding.Bottom ) * percent ), mSlider->getMinSize().getHeight() ) ); mSlider->setSize( mSlider->getSize().getWidth(), size ); @@ -311,26 +311,26 @@ void UISlider::adjustSliderPos() { if ( UIOrientation::Horizontal == mOrientation ) { if ( mAllowHalfSliderOut ) - mSlider->setPosition( - mPadding.Left + ( Int32 )( (Float)mBackSlider->getSize().getWidth() * Percent ), - mSlider->getPosition().y ); + mSlider->setPosition( mPadding.Left + + (Int32)( (Float)mBackSlider->getSize().getWidth() * Percent ), + mSlider->getPosition().y ); else - mSlider->setPosition( - mPadding.Left + ( Int32 )( ( (Float)getSize().getWidth() - mPadding.Left - - mPadding.Right - mSlider->getSize().getWidth() ) * - Percent ), - mSlider->getPosition().y ); + mSlider->setPosition( mPadding.Left + + (Int32)( ( (Float)getSize().getWidth() - mPadding.Left - + mPadding.Right - mSlider->getSize().getWidth() ) * + Percent ), + mSlider->getPosition().y ); } else { if ( mAllowHalfSliderOut ) mSlider->setPosition( mSlider->getPosition().x, - mPadding.Top + ( Int32 )( (Float)mBackSlider->getSize().getHeight() * Percent ) ); + mPadding.Top + (Int32)( (Float)mBackSlider->getSize().getHeight() * Percent ) ); else mSlider->setPosition( mSlider->getPosition().x, - mPadding.Top + ( Int32 )( ( (Float)getSize().getHeight() - mPadding.Top - - mPadding.Bottom - mSlider->getSize().getHeight() ) * - Percent ) ); + mPadding.Top + (Int32)( ( (Float)getSize().getHeight() - mPadding.Top - + mPadding.Bottom - mSlider->getSize().getHeight() ) * + Percent ) ); } mOnPosChange = false; @@ -633,6 +633,10 @@ Sizef UISlider::getMinimumSize() { h + mPadding.Top + mPadding.Bottom ); } +bool UISlider::isDragging() const { + return mSlider && mSlider->isDragging(); +} + void UISlider::onAutoSize() { if ( mWidthPolicy == SizePolicy::WrapContent || mHeightPolicy == SizePolicy::WrapContent ) { bool modified = false; diff --git a/src/tools/codeeditor/appconfig.cpp b/src/tools/codeeditor/appconfig.cpp index a59d910e7..9d3f1e18f 100644 --- a/src/tools/codeeditor/appconfig.cpp +++ b/src/tools/codeeditor/appconfig.cpp @@ -87,6 +87,7 @@ void AppConfig::load( std::string& confPath, std::string& keybindingsPath, editor.highlightSelectionMatch = ini.getValueB( "editor", "highlight_selection_match", true ); editor.colorPickerSelection = ini.getValueB( "editor", "color_picker_selection", true ); editor.colorPreview = ini.getValueB( "editor", "color_preview", true ); + editor.minimap = ini.getValueB( "editor", "minimap", true ); editor.autoComplete = ini.getValueB( "editor", "auto_complete", true ); editor.linter = ini.getValueB( "editor", "linter", true ); editor.formatter = ini.getValueB( "editor", "formatter", true ); @@ -153,6 +154,7 @@ void AppConfig::save( const std::vector& recentFiles, ini.setValueB( "editor", "highlight_selection_match", editor.highlightSelectionMatch ); ini.setValueB( "editor", "color_picker_selection", editor.colorPickerSelection ); ini.setValueB( "editor", "color_preview", editor.colorPreview ); + ini.setValueB( "editor", "minimap", editor.minimap ); ini.setValueB( "editor", "auto_complete", editor.autoComplete ); ini.setValueB( "editor", "linter", editor.linter ); ini.setValueB( "editor", "formatter", editor.formatter ); diff --git a/src/tools/codeeditor/appconfig.hpp b/src/tools/codeeditor/appconfig.hpp index f098ced72..f92e0c9aa 100644 --- a/src/tools/codeeditor/appconfig.hpp +++ b/src/tools/codeeditor/appconfig.hpp @@ -54,6 +54,7 @@ struct CodeEditorConfig { bool highlightSelectionMatch{ true }; bool colorPickerSelection{ false }; bool colorPreview{ false }; + bool minimap{ true }; bool autoComplete{ true }; bool showDocInfo{ true }; bool linter{ true }; diff --git a/src/tools/codeeditor/codeeditor.cpp b/src/tools/codeeditor/codeeditor.cpp index 25b701f96..4851046b6 100644 --- a/src/tools/codeeditor/codeeditor.cpp +++ b/src/tools/codeeditor/codeeditor.cpp @@ -643,6 +643,7 @@ UIMenu* App::createViewMenu() { mViewMenu->addCheckBox( "Show Line Numbers" )->setActive( mConfig.editor.showLineNumbers ); mViewMenu->addCheckBox( "Show White Space" )->setActive( mConfig.editor.showWhiteSpaces ); mViewMenu->addCheckBox( "Show Document Info" )->setActive( mConfig.editor.showDocInfo ); + mViewMenu->addCheckBox( "Show Minimap" )->setActive( mConfig.editor.minimap ); mViewMenu->addCheckBox( "Highlight Matching Bracket" ) ->setActive( mConfig.editor.highlightMatchingBracket ); mViewMenu->addCheckBox( "Highlight Current Line" ) @@ -722,6 +723,10 @@ UIMenu* App::createViewMenu() { mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { editor->setEnableColorPickerOnSelection( mConfig.editor.colorPickerSelection ); } ); + } else if ( item->getText() == "Show Minimap" ) { + mConfig.editor.minimap = item->asType()->isActive(); + mEditorSplitter->forEachEditor( + [&]( UICodeEditor* editor ) { editor->showMinimap( mConfig.editor.minimap ); } ); } else if ( item->getText() == "Enable Auto Complete" ) { setAutoComplete( item->asType()->isActive() ); } else if ( item->getText() == "Enable Linter" ) { @@ -1524,6 +1529,8 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { updateEditorTabTitle( editor ); } ); + editor->showMinimap( config.minimap ); + if ( config.autoComplete && !mAutoCompleteModule ) setAutoComplete( config.autoComplete );