From 472edd7e411945d453ed2608dfd12c82f60be2a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 24 May 2020 06:14:54 -0300 Subject: [PATCH] Added vertical scrollbar to the UICodeEditor. Some minor fixes. --- include/eepp/ui/doc/textdocument.hpp | 5 ++ include/eepp/ui/uicodeeditor.hpp | 9 +++ include/eepp/ui/uiscrollbar.hpp | 4 +- include/eepp/ui/uislider.hpp | 2 +- projects/linux/ee.creator.user | 4 +- src/eepp/ui/doc/syntaxdefinitionmanager.cpp | 2 +- src/eepp/ui/doc/textdocument.cpp | 15 +++++ src/eepp/ui/uicodeeditor.cpp | 72 +++++++++++++++------ src/eepp/ui/uinode.cpp | 28 ++++---- src/eepp/ui/uiscrollbar.cpp | 8 ++- src/eepp/ui/uislider.cpp | 19 +++--- src/tools/codeeditor/codeeditor.cpp | 28 ++++++-- 12 files changed, 142 insertions(+), 54 deletions(-) diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index 371d6d1c0..1f0ef9a67 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -24,6 +24,8 @@ class EE_API TextDocument { virtual void onDocumentTextChanged() = 0; virtual void onDocumentCursorChange( const TextPosition& ) = 0; virtual void onDocumentSelectionChange( const TextRange& ) = 0; + virtual void onDocumentLineCountChange( const size_t& lastCount, + const size_t& newCount ) = 0; }; enum IndentType { IndentSpaces, IndentTabs }; @@ -208,6 +210,7 @@ class EE_API TextDocument { const std::string& getFilePath() const; bool isDirty() const; + protected: friend class UndoStack; UndoStack mUndoStack; @@ -232,6 +235,8 @@ class EE_API TextDocument { void notifySelectionChanged(); + void notifyLineCountChanged( const size_t& lastCount, const size_t& newCount ); + void insertAtStartOfSelectedLines( String text, bool skipEmpty ); void removeFromStartOfSelectedLines( String text, bool skipEmpty ); diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index a4819e1c9..8a612d279 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -16,6 +16,8 @@ class Font; namespace EE { namespace UI { +class UIScrollBar; + class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { public: static UICodeEditor* New(); @@ -127,6 +129,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { Color mCaretColor; SyntaxColorScheme mColorScheme; SyntaxHighlighter mHighlighter; + UIScrollBar* mVScrollBar; void invalidateEditor(); @@ -160,12 +163,16 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void onDocumentSelectionChange( const TextRange& ); + void onDocumentLineCountChange( const size_t& lastCount, const size_t& newCount ); + std::pair getVisibleLineRange(); int getVisibleLinesCount(); void scrollToMakeVisible( const TextPosition& position ); + void setScrollY( const Float& val, bool emmitEvent = true ); + Float getXOffsetCol( const TextPosition& position ) const; Float getTextWidth( const String& text ) const; @@ -187,6 +194,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { Vector2f getViewPortLineCount() const; Sizef getMaxScroll() const; + + void updateScrollBar(); }; }} // namespace EE::UI diff --git a/include/eepp/ui/uiscrollbar.hpp b/include/eepp/ui/uiscrollbar.hpp index dd227e628..987ac5b02 100644 --- a/include/eepp/ui/uiscrollbar.hpp +++ b/include/eepp/ui/uiscrollbar.hpp @@ -24,7 +24,7 @@ class EE_API UIScrollBar : public UIWidget { virtual bool isType( const Uint32& type ) const; - virtual void setValue( Float Val ); + virtual void setValue( Float val, const bool& emmitEvent = true ); const Float& getValue() const; @@ -81,6 +81,8 @@ class EE_API UIScrollBar : public UIWidget { virtual void onAutoSize(); + virtual Uint32 onMouseOver( const Vector2i& position, const Uint32& flags ); + void adjustChilds(); void onValueChangeCb( const Event* Event ); diff --git a/include/eepp/ui/uislider.hpp b/include/eepp/ui/uislider.hpp index 13e7b24e4..fea2245bc 100644 --- a/include/eepp/ui/uislider.hpp +++ b/include/eepp/ui/uislider.hpp @@ -30,7 +30,7 @@ class EE_API UISlider : public UIWidget { virtual void setTheme( UITheme* Theme ); - virtual void setValue( Float Val ); + virtual void setValue( Float val, bool emmitEvent = true ); const Float& getValue() const; diff --git a/projects/linux/ee.creator.user b/projects/linux/ee.creator.user index e18b3c96c..fef1021c9 100644 --- a/projects/linux/ee.creator.user +++ b/projects/linux/ee.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -1847,7 +1847,7 @@ eepp-codeeditor-debug ProjectExplorer.CustomExecutableRunConfiguration - /home/downloads/codeeditor.cpp + ../src/tools/codeeditor/codeeditor.cpp false true diff --git a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp index 1ae147ff8..eeeb8c974 100644 --- a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp +++ b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp @@ -224,7 +224,7 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { { {{"//.-\n"}, "comment"}, {{"/%*", "%*/"}, "comment"}, - {{"#", "[^\\]\n"}, "comment"}, + {{"#", "[^\\]\n"}, "keyword2"}, {{"\"", "\"", "\\"}, "string"}, {{"'", "'", "\\"}, "string"}, {{"-?0x%x+"}, "number"}, diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index e9d26ce8a..b0e18895b 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -208,6 +208,7 @@ TextPosition TextDocument::insert( const TextPosition& position, const String& t TextPosition TextDocument::insert( const TextPosition& position, const String& text, UndoStackContainer& undoStack, const Time& time ) { TextPosition cursor = position; + size_t lineCount = mLines.size(); for ( size_t i = 0; i < text.length(); ++i ) { cursor = insert( cursor, text[i] ); @@ -218,6 +219,10 @@ TextPosition TextDocument::insert( const TextPosition& position, const String& t notifyTextChanged(); + if ( lineCount != mLines.size() ) { + notifyLineCountChanged( lineCount, mLines.size() ); + } + return cursor; } @@ -261,11 +266,15 @@ void TextDocument::remove( TextPosition position ) { } void TextDocument::remove( TextRange range ) { + size_t lineCount = mLines.size(); mUndoStack.clearRedoStack(); range = range.normalized(); range.setStart( sanitizePosition( range.start() ) ); range.setEnd( sanitizePosition( range.end() ) ); remove( range, mUndoStack.getUndoStackContainer(), mTimer.getElapsedTime() ); + if ( lineCount != mLines.size() ) { + notifyLineCountChanged( lineCount, mLines.size() ); + } } void TextDocument::remove( TextRange range, UndoStackContainer& undoStack, const Time& time ) { @@ -819,6 +828,12 @@ void TextDocument::notifySelectionChanged() { } } +void TextDocument::notifyLineCountChanged( const size_t& lastCount, const size_t& newCount ) { + for ( auto& client : mClients ) { + client->onDocumentLineCountChange( lastCount, newCount ); + } +} + TextDocument::Client::~Client() {} }}} // namespace EE::UI::Doc diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index ef75bc2f3..89db52123 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,14 @@ UICodeEditor::UICodeEditor() : setFontColor( Color::fromString( "#e1e1e6" ) ); mFontStyleConfig.setFontSelectionBackColor( Color::fromString( "#48484f" ) ); + mVScrollBar = UIScrollBar::NewVertical(); + mVScrollBar->setParent( this ); + mVScrollBar->addEventListener( Event::OnSizeChange, + [&]( const Event* ) { updateScrollBar(); } ); + mVScrollBar->addEventListener( Event::OnValueChange, [&]( const Event* ) { + setScrollY( mVScrollBar->getValue() * getMaxScroll().y, false ); + } ); + if ( NULL == mFont ) eePRINTL( "A monospace font must be loaded to be able to use the code editor.\nTry loading " "a font with the name \"monospace\"" ); @@ -418,8 +427,7 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) { } case KEY_UP: { if ( event.getMod() & KEYMOD_CTRL ) { - mScroll.y = eefloor( eemax( 0, mScroll.y - getLineHeight() ) ); - invalidateDraw(); + setScrollY( mScroll.y - getLineHeight() ); } else if ( event.getMod() & KEYMOD_SHIFT ) { mDoc.selectToPreviousLine( mLastColOffset ); } else { @@ -429,9 +437,7 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) { } case KEY_DOWN: { if ( event.getMod() & KEYMOD_CTRL ) { - mScroll.y = - eefloor( eemin( getMaxScroll().y, mScroll.y + getLineHeight() ) ); - invalidateDraw(); + setScrollY( mScroll.y + getLineHeight() ); } else if ( event.getMod() & KEYMOD_SHIFT ) { mDoc.selectToNextLine( mLastColOffset ); } else { @@ -470,9 +476,8 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) { mDoc.selectToStartOfLine(); updateLastColumnOffset(); } else if ( event.getMod() & KEYMOD_CTRL ) { - mScroll.y = 0; + setScrollY( 0 ); mDoc.setSelection( {0, 0} ); - invalidateDraw(); } else { mDoc.setSelection( mDoc.startOfLine( mDoc.getSelection().start() ) ); updateLastColumnOffset(); @@ -484,7 +489,7 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) { mDoc.selectToEndOfLine(); updateLastColumnOffset(); } else if ( event.getMod() & KEYMOD_CTRL ) { - mScroll.y = getMaxScroll().y; + setScrollY( getMaxScroll().y ); mDoc.setSelection( {static_cast( mDoc.linesCount() - 1 ), static_cast( mDoc.line( mDoc.linesCount() - 1 ).length() )} ); @@ -612,8 +617,8 @@ TextPosition UICodeEditor::resolveScreenPosition( const Vector2f& position ) con } Vector2f UICodeEditor::getViewPortLineCount() const { - return Vector2f( eefloor( mSize.getWidth() / getGlyphWidth() ), - eefloor( mSize.getHeight() / getLineHeight() ) ); + return Vector2f( eefloor( ( mSize.getWidth() - mRealPadding.Left ) / getGlyphWidth() ), + eefloor( ( mSize.getHeight() - mRealPadding.Top ) / getLineHeight() ) ); } Sizef UICodeEditor::getMaxScroll() const { @@ -626,7 +631,8 @@ Sizef UICodeEditor::getMaxScroll() const { } Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags ) { - if ( NULL != mFont && !mMouseDown && ( flags & EE_BUTTON_LMASK ) ) { + if ( !getUISceneNode()->getEventDispatcher()->isNodeDragging() && NULL != mFont && + !mMouseDown && ( flags & EE_BUTTON_LMASK ) ) { mMouseDown = true; Input* input = getUISceneNode()->getWindow()->getInput(); if ( input->isShiftPressed() ) { @@ -639,7 +645,8 @@ Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags } Uint32 UICodeEditor::onMouseMove( const Vector2i& position, const Uint32& flags ) { - if ( NULL != mFont && mMouseDown && ( flags & EE_BUTTON_LMASK ) ) { + if ( !getUISceneNode()->getEventDispatcher()->isNodeDragging() && hasFocus() && NULL != mFont && + mMouseDown && ( flags & EE_BUTTON_LMASK ) ) { TextRange selection = mDoc.getSelection(); selection.setStart( resolveScreenPosition( position.asFloat() ) ); mDoc.setSelection( selection ); @@ -657,16 +664,14 @@ Uint32 UICodeEditor::onMouseUp( const Vector2i& position, const Uint32& flags ) if ( getUISceneNode()->getWindow()->getInput()->isControlPressed() ) { mFontStyleConfig.CharacterSize = eemax( 4, mFontStyleConfig.CharacterSize - 1 ); } else { - mScroll.y += PixelDensity::dpToPx( mMouseWheelScroll ); - mScroll.y = eefloor( eemin( mScroll.y, getMaxScroll().y ) ); + setScrollY( mScroll.y + PixelDensity::dpToPx( mMouseWheelScroll ) ); } invalidateDraw(); } else if ( flags & EE_BUTTON_WUMASK ) { if ( getUISceneNode()->getWindow()->getInput()->isControlPressed() ) { mFontStyleConfig.CharacterSize = eemin( 96, mFontStyleConfig.CharacterSize + 1 ); } else { - mScroll.y -= PixelDensity::dpToPx( mMouseWheelScroll ); - mScroll.y = eefloor( eemax( mScroll.y, 0.f ) ); + setScrollY( mScroll.y - PixelDensity::dpToPx( mMouseWheelScroll ) ); } invalidateDraw(); } @@ -688,9 +693,9 @@ Uint32 UICodeEditor::onMouseOver( const Vector2i& position, const Uint32& flags return UIWidget::onMouseOver( position, flags ); } -Uint32 UICodeEditor::onMouseLeave( const Vector2i& Pos, const Uint32& Flags ) { +Uint32 UICodeEditor::onMouseLeave( const Vector2i& position, const Uint32& flags ) { getUISceneNode()->setCursor( Cursor::Arrow ); - return UIWidget::onMouseLeave( Pos, Flags ); + return UIWidget::onMouseLeave( position, flags ); } void UICodeEditor::onSizeChange() { @@ -704,8 +709,20 @@ void UICodeEditor::onPaddingChange() { invalidateEditor(); } +void UICodeEditor::updateScrollBar() { + mVScrollBar->setPixelsSize( 0, mSize.getHeight() ); + mVScrollBar->setPixelsPosition( mSize.getWidth() - mVScrollBar->getPixelsSize().getWidth(), 0 ); + int notVisibleLineCount = (int)mDoc.linesCount() - (int)getViewPortLineCount().y; + mVScrollBar->setPageStep( getViewPortLineCount().y / (float)mDoc.linesCount() ); + mVScrollBar->setClickStep( 0.2f ); + mVScrollBar->setEnabled( notVisibleLineCount > 0 ); + mVScrollBar->setVisible( notVisibleLineCount > 0 ); + setScrollY( mScroll.y ); +} + void UICodeEditor::updateEditor() { scrollToMakeVisible( mDoc.getSelection().start() ); + updateScrollBar(); mDirtyEditor = false; } @@ -724,6 +741,10 @@ void UICodeEditor::onDocumentSelectionChange( const Doc::TextRange& ) { invalidateDraw(); } +void UICodeEditor::onDocumentLineCountChange( const size_t&, const size_t& ) { + updateScrollBar(); +} + std::pair UICodeEditor::getVisibleLineRange() { Float lineHeight = getLineHeight(); Float minLine = eemax( 0.f, eefloor( mScroll.y / lineHeight ) ); @@ -742,8 +763,9 @@ void UICodeEditor::scrollToMakeVisible( const TextPosition& position ) { Float lineHeight = getLineHeight(); Float min = lineHeight * ( eemax( 0, position.line() - 1 ) ); Float max = lineHeight * ( position.line() + 2 ) - mSize.getHeight(); - mScroll.y = eemin( mScroll.y, min ); - mScroll.y = eefloor( eemax( mScroll.y, max ) ); + Float scrollY = eemin( mScroll.y, min ); + scrollY = eefloor( eemax( mScroll.y, max ) ); + setScrollY( scrollY ); // Horizontal Scroll Float offsetX = getXOffsetCol( position ); @@ -758,6 +780,16 @@ void UICodeEditor::scrollToMakeVisible( const TextPosition& position ) { invalidateDraw(); } +void UICodeEditor::setScrollY( const Float& val, bool emmitEvent ) { + Float oldVal = mScroll.y; + mScroll.y = eefloor( eeclamp( val, 0, getMaxScroll().y ) ); + if ( oldVal != mScroll.y ) { + invalidateDraw(); + if ( emmitEvent ) + mVScrollBar->setValue( mScroll.y / getMaxScroll().y, false ); + } +} + Float UICodeEditor::getXOffsetCol( const TextPosition& position ) const { const String& line = mDoc.line( position.line() ); Float glyphWidth = getGlyphWidth(); diff --git a/src/eepp/ui/uinode.cpp b/src/eepp/ui/uinode.cpp index c934cc812..2bdd55ff4 100644 --- a/src/eepp/ui/uinode.cpp +++ b/src/eepp/ui/uinode.cpp @@ -143,12 +143,14 @@ void UINode::setInternalSize( const Sizef& size ) { if ( s.y < mMinSize.y ) s.y = mMinSize.y; - mDpSize = size; - mSize = PixelDensity::dpToPx( s ); - mNodeFlags |= NODE_FLAG_POLYGON_DIRTY; - updateCenter(); - sendCommonEvent( Event::OnSizeChange ); - invalidateDraw(); + if ( s != mDpSize ) { + mDpSize = size; + mSize = PixelDensity::dpToPx( s ); + mNodeFlags |= NODE_FLAG_POLYGON_DIRTY; + updateCenter(); + sendCommonEvent( Event::OnSizeChange ); + invalidateDraw(); + } } void UINode::setInternalPixelsSize( const Sizef& size ) { @@ -161,12 +163,14 @@ void UINode::setInternalPixelsSize( const Sizef& size ) { if ( s.y < pMinSize.y ) s.y = pMinSize.y; - mDpSize = PixelDensity::pxToDp( s ).ceil(); - mSize = s; - mNodeFlags |= NODE_FLAG_POLYGON_DIRTY; - updateCenter(); - sendCommonEvent( Event::OnSizeChange ); - invalidateDraw(); + if ( s != mSize ) { + mDpSize = PixelDensity::pxToDp( s ).ceil(); + mSize = s; + mNodeFlags |= NODE_FLAG_POLYGON_DIRTY; + updateCenter(); + sendCommonEvent( Event::OnSizeChange ); + invalidateDraw(); + } } Node* UINode::setSize( const Sizef& Size ) { diff --git a/src/eepp/ui/uiscrollbar.cpp b/src/eepp/ui/uiscrollbar.cpp index f712edfa5..a3f776804 100644 --- a/src/eepp/ui/uiscrollbar.cpp +++ b/src/eepp/ui/uiscrollbar.cpp @@ -169,6 +169,10 @@ void UIScrollBar::onAutoSize() { } } +Uint32 UIScrollBar::onMouseOver( const Vector2i&, const Uint32& ) { + return 1; +} + void UIScrollBar::onSizeChange() { onAutoSize(); @@ -263,8 +267,8 @@ Uint32 UIScrollBar::onMessage( const NodeMessage* Msg ) { return 0; } -void UIScrollBar::setValue( Float Val ) { - mSlider->setValue( Val ); +void UIScrollBar::setValue( Float val, const bool& emmitEvent ) { + mSlider->setValue( val, emmitEvent ); } const Float& UIScrollBar::getValue() const { diff --git a/src/eepp/ui/uislider.cpp b/src/eepp/ui/uislider.cpp index 0382d16be..5d91f17cd 100644 --- a/src/eepp/ui/uislider.cpp +++ b/src/eepp/ui/uislider.cpp @@ -319,17 +319,17 @@ void UISlider::adjustSliderPos() { mOnPosChange = false; } -void UISlider::setValue( Float Val ) { - if ( Val < mMinValue ) - Val = mMinValue; - if ( Val > mMaxValue ) - Val = mMaxValue; +void UISlider::setValue( Float val, bool emmitEvent ) { + if ( val < mMinValue ) + val = mMinValue; + if ( val > mMaxValue ) + val = mMaxValue; - if ( mValue == Val ) + if ( mValue == val ) return; - if ( Val >= mMinValue && Val <= mMaxValue ) { - mValue = Val; + if ( val >= mMinValue && val <= mMaxValue ) { + mValue = val; if ( !mOnPosChange ) { mOnPosChange = true; @@ -339,7 +339,8 @@ void UISlider::setValue( Float Val ) { mOnPosChange = false; } - onValueChange(); + if ( emmitEvent ) + onValueChange(); } } diff --git a/src/tools/codeeditor/codeeditor.cpp b/src/tools/codeeditor/codeeditor.cpp index 63418df66..c0e508838 100644 --- a/src/tools/codeeditor/codeeditor.cpp +++ b/src/tools/codeeditor/codeeditor.cpp @@ -7,6 +7,24 @@ UICodeEditor* codeEditor = NULL; std::string curFile = "untitled"; const std::string& windowTitle = "eepp - Code Editor"; bool docDirtyState = false; +UIMessageBox* MsgBox = NULL; + +bool onCloseRequestCallback( EE::Window::Window* ) { + if ( NULL != codeEditor && codeEditor->isDirty() ) { + MsgBox = UIMessageBox::New( + UIMessageBox::OK_CANCEL, + "Do you really want to close the code editor?\nAll changes will be lost." ); + MsgBox->addEventListener( Event::MsgBoxConfirmClick, + []( const Event* ) { win->close(); } ); + MsgBox->addEventListener( Event::OnClose, []( const Event* ) { MsgBox = NULL; } ); + MsgBox->setTitle( "Close Code Editor?" ); + MsgBox->center(); + MsgBox->show(); + return false; + } else { + return true; + } +} void setAppTitle( const std::string& title ) { win->setTitle( windowTitle + String( title.empty() ? "" : " - " + title ) ); @@ -42,7 +60,8 @@ void mainLoop() { uiSceneNode->setDrawDebugData( !uiSceneNode->getDrawDebugData() ); } - if ( win->getInput()->isKeyUp( KEY_ESCAPE ) ) { + if ( win->getInput()->isKeyUp( KEY_ESCAPE ) && NULL == MsgBox && + onCloseRequestCallback( win ) ) { win->close(); } @@ -90,6 +109,8 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { ContextSettings( true ) ); if ( win->isOpen() ) { + win->setCloseRequestCallback( cb::Make1( onCloseRequestCallback ) ); + win->getInput()->pushCallback( []( InputEvent* event ) { if ( NULL == codeEditor ) return; @@ -134,11 +155,6 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { if ( file ) { loadFileFromPath( file.Get() ); - } else { - /*UIMessageBox::New( UIMessageBox::OK, - "To load a file add the file path as a command argument or\n" - "drop any text file into the window." ) - ->show();*/ } win->runMainLoop( &mainLoop );