UICodeEditor: completed minimap support.

ecode: Added minimap support.
This commit is contained in:
Martín Lucas Golini
2022-03-30 22:01:11 -03:00
parent a5f99bfea2
commit d5ae30f9e6
11 changed files with 148 additions and 49 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -79,6 +79,8 @@ class EE_API UISlider : public UIWidget {
Sizef getMinimumSize();
bool isDragging() const;
protected:
UIOrientation mOrientation;
bool mAllowHalfSliderOut;

View File

@@ -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

View File

@@ -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<Float>( 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<Float>( 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<int, int>
}
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<int, int>
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<int, int>
}
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, int>
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<int, int>
lineY = lineY + lineSpacing;
}
}
primitives.setForceDraw( true );
}
Vector2f UICodeEditor::getScreenStart() const {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<std::string>& 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 );

View File

@@ -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 };

View File

@@ -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<UIMenuCheckBox>()->isActive();
mEditorSplitter->forEachEditor(
[&]( UICodeEditor* editor ) { editor->showMinimap( mConfig.editor.minimap ); } );
} else if ( item->getText() == "Enable Auto Complete" ) {
setAutoComplete( item->asType<UIMenuCheckBox>()->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 );