From badf417918200ca17dc7a505ff95bf3209193670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Wed, 25 Jan 2023 02:59:44 -0300 Subject: [PATCH] More multi cursor improvements. --- include/eepp/ui/doc/textrange.hpp | 2 +- include/eepp/ui/doc/undostack.hpp | 10 ++++---- src/eepp/ui/doc/textdocument.cpp | 28 ++++++++++++++++++-- src/eepp/ui/doc/undostack.cpp | 8 +++--- src/eepp/ui/uicodeeditor.cpp | 12 +++++---- src/tools/ecode/docsearchcontroller.cpp | 34 ++++++++++++++++++++----- src/tools/ecode/docsearchcontroller.hpp | 5 +++- src/tools/ecode/ecode.cpp | 3 ++- 8 files changed, 76 insertions(+), 26 deletions(-) diff --git a/include/eepp/ui/doc/textrange.hpp b/include/eepp/ui/doc/textrange.hpp index 3ff0c3bb8..17674f417 100644 --- a/include/eepp/ui/doc/textrange.hpp +++ b/include/eepp/ui/doc/textrange.hpp @@ -36,7 +36,7 @@ class EE_API TextRange { return *this; } - TextRange reversed() { return TextRange( mEnd, mStart ); } + TextRange reversed() const { return TextRange( mEnd, mStart ); } void setStart( const TextPosition& position ) { mStart = position; } diff --git a/include/eepp/ui/doc/undostack.hpp b/include/eepp/ui/doc/undostack.hpp index 285cdb31c..0774ed6a7 100644 --- a/include/eepp/ui/doc/undostack.hpp +++ b/include/eepp/ui/doc/undostack.hpp @@ -67,15 +67,15 @@ class EE_API TextUndoCommandRemove : public TextUndoCommand { class EE_API TextUndoCommandSelection : public TextUndoCommand { public: - TextUndoCommandSelection( const Uint64& id, const size_t& cursorIdx, const TextRange& selection, - const Time& timestamp ); + TextUndoCommandSelection( const Uint64& id, const size_t& cursorIdx, + const TextRanges& selection, const Time& timestamp ); - const TextRange& getSelection() const; + const TextRanges& getSelection() const; size_t getCursorIdx() const; protected: - TextRange mSelection; + TextRanges mSelection; size_t mCursorIdx; }; @@ -128,7 +128,7 @@ class EE_API UndoStack { const Time& time ); void pushSelection( UndoStackContainer& undoStack, const size_t& cursorIdx, - const TextRange& selection, const Time& time ); + const TextRanges& selection, const Time& time ); UndoStackContainer& getUndoStackContainer(); diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index af94f49be..bc17e0536 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -685,7 +685,11 @@ TextRange TextDocument::addSelection( const TextPosition& selection ) { } TextRange TextDocument::addSelection( TextRange selection ) { + if ( mSelection.exists( selection ) ) + return {}; selection = sanitizeRange( selection ); + if ( mSelection.exists( selection ) ) + return {}; mSelection.push_back( selection ); mSelection.sort(); mergeSelection(); @@ -849,7 +853,7 @@ TextPosition TextDocument::insert( const size_t& cursorIdx, TextPosition positio TextPosition cursor = positionOffset( position, text.size() ); - mUndoStack.pushSelection( undoStack, cursorIdx, getSelectionIndex( cursorIdx ), time ); + mUndoStack.pushSelection( undoStack, cursorIdx, mSelection, time ); mUndoStack.pushRemove( undoStack, cursorIdx, { position, cursor }, time ); notifyTextChanged( { { position, position }, text } ); @@ -901,7 +905,7 @@ size_t TextDocument::remove( const size_t& cursorIdx, TextRange range, } TextRange originalRange = range; - mUndoStack.pushSelection( undoStack, cursorIdx, getSelectionIndex( cursorIdx ), time ); + mUndoStack.pushSelection( undoStack, cursorIdx, mSelection, time ); mUndoStack.pushInsert( undoStack, getText( range ), cursorIdx, range.start(), time ); size_t linesRemoved = 0; @@ -960,6 +964,26 @@ size_t TextDocument::remove( const size_t& cursorIdx, TextRange range, if ( lines().empty() ) mLines.emplace_back( String( "\n" ) ); + if ( mSelection.size() > 1 ) { + for ( auto& sel : mSelection ) { + auto selNorm( sel.normalized() ); + auto ranNorm( range.normalized() ); + + if ( selNorm.start().line() < ranNorm.end().line() ) + break; + + if ( selNorm.end().line() == ranNorm.end().line() && + ranNorm.end().column() < selNorm.start().column() ) { + auto colRem = ranNorm.start().line() == ranNorm.end().line() + ? ranNorm.end().column() - ranNorm.start().column() + : ranNorm.end().column(); + sel.start().setColumn( sel.start().column() - colRem ); + sel.end().setColumn( sel.end().column() - colRem ); + sel = sanitizeRange( sel ); + } + } + } + notifyTextChanged( { originalRange, "" } ); notifyLineChanged( range.start().line() ); diff --git a/src/eepp/ui/doc/undostack.cpp b/src/eepp/ui/doc/undostack.cpp index 7b1efe078..500bdc8a9 100644 --- a/src/eepp/ui/doc/undostack.cpp +++ b/src/eepp/ui/doc/undostack.cpp @@ -59,13 +59,13 @@ size_t TextUndoCommandRemove::getCursorIdx() const { } TextUndoCommandSelection::TextUndoCommandSelection( const Uint64& id, const size_t& cursorIdx, - const TextRange& selection, + const TextRanges& selection, const Time& timestamp ) : TextUndoCommand( id, TextUndoCommandType::Selection, timestamp ), mSelection( selection ), mCursorIdx( cursorIdx ) {} -const TextRange& TextUndoCommandSelection::getSelection() const { +const TextRanges& TextUndoCommandSelection::getSelection() const { return mSelection; } @@ -124,7 +124,7 @@ void UndoStack::pushRemove( UndoStackContainer& undoStack, const size_t& cursorI } void UndoStack::pushSelection( UndoStackContainer& undoStack, const size_t& cursorIdx, - const TextRange& selection, const Time& time ) { + const TextRanges& selection, const Time& time ) { pushUndo( undoStack, eeNew( TextUndoCommandSelection, ( ++mChangeIdCounter, cursorIdx, selection, time ) ) ); } @@ -152,7 +152,7 @@ void UndoStack::popUndo( UndoStackContainer& undoStack, UndoStackContainer& redo } case TextUndoCommandType::Selection: { TextUndoCommandSelection* selection = static_cast( cmd ); - mDoc->setSelection( selection->getCursorIdx(), selection->getSelection() ); + mDoc->resetSelection( selection->getSelection() ); break; } } diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 4a0405faf..4d0aeff6e 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -3297,11 +3297,13 @@ void UICodeEditor::drawMinimap( const Vector2f& start, } } - Float selectionY = - rect.Top + ( mDoc->getSelection().start().line() - minimapStartLine ) * lineSpacing; - primitives.setColor( Color( mMinimapCurrentLineColor ).blendAlpha( mAlpha ) ); - primitives.drawRectangle( { { rect.Left, selectionY }, { rect.getWidth(), lineSpacing } } ); - + for ( size_t i = 0; i < mDoc->getSelections().size(); ++i ) { + Float selectionY = + rect.Top + + ( mDoc->getSelectionIndex( i ).start().line() - minimapStartLine ) * lineSpacing; + primitives.setColor( Color( mMinimapCurrentLineColor ).blendAlpha( mAlpha ) ); + primitives.drawRectangle( { { rect.Left, selectionY }, { rect.getWidth(), lineSpacing } } ); + } primitives.setForceDraw( true ); } diff --git a/src/tools/ecode/docsearchcontroller.cpp b/src/tools/ecode/docsearchcontroller.cpp index 0508506be..1eabb19d5 100644 --- a/src/tools/ecode/docsearchcontroller.cpp +++ b/src/tools/ecode/docsearchcontroller.cpp @@ -29,7 +29,8 @@ void DocSearchController::initSearchBar( kbind.addKeybindsString( { { mApp->getKeybind( "repeat-find" ), "repeat-find" }, { mApp->getKeybind( "find-prev" ), "find-prev" }, - { mApp->getKeybind( "open-global-search" ), "open-global-search" } } ); + { mApp->getKeybind( "open-global-search" ), "open-global-search" }, + { mApp->getKeybind( "select-all-results" ), "select-all-results" } } ); kbind.addKeybindsStringUnordered( keybindings ); mFindInput = mSearchBarLayout->find( "search_find" ); @@ -44,6 +45,7 @@ void DocSearchController::initSearchBar( UIPushButton* findNextButton = mSearchBarLayout->find( "find_next" ); UIPushButton* replaceButton = mSearchBarLayout->find( "replace" ); UIPushButton* findReplaceButton = mSearchBarLayout->find( "replace_find" ); + UIPushButton* selectAllButton = mSearchBarLayout->find( "select_all" ); UIWidget* closeButton = mSearchBarLayout->find( "searchbar_close" ); mCaseSensitiveChk->setChecked( searchBarConfig.caseSensitive ); mLuaPatternChk->setChecked( searchBarConfig.luaPattern ); @@ -56,7 +58,7 @@ void DocSearchController::initSearchBar( std::string kbindEscape = kbind.getCommandKeybindString( "change-escape-sequence" ); if ( !kbindEscape.empty() ) mEscapeSequenceChk->setTooltipText( mEscapeSequenceChk->getTooltipText() + " (" + - kbindEscape + ")" ); + kbindEscape + ")" ); mCaseSensitiveChk->addEventListener( Event::OnValueChange, [&]( const Event* ) { mSearchState.caseSensitive = mCaseSensitiveChk->isChecked(); @@ -72,7 +74,7 @@ void DocSearchController::initSearchBar( mLuaPatternChk->addEventListener( Event::OnValueChange, [&]( const Event* ) { mSearchState.type = mLuaPatternChk->isChecked() ? TextDocument::FindReplaceType::LuaPattern - : TextDocument::FindReplaceType::Normal; + : TextDocument::FindReplaceType::Normal; } ); mFindInput->addEventListener( Event::OnTextChanged, [&]( const Event* ) { @@ -118,6 +120,7 @@ void DocSearchController::initSearchBar( String::format( "Replaced %zu occurrences.", count ) ); mReplaceInput->setFocus(); } ); + mSearchBarLayout->setCommand( "select-all-results", [this] { selectAll( mSearchState ); } ); mSearchBarLayout->setCommand( "find-and-replace", [this] { findAndReplace( mSearchState, mReplaceInput->getText() ); } ); mSearchBarLayout->setCommand( "find-prev", [this] { findPrevText( mSearchState ); } ); @@ -126,8 +129,8 @@ void DocSearchController::initSearchBar( } ); mSearchBarLayout->setCommand( "change-case", [&] { mCaseSensitiveChk->setChecked( !mCaseSensitiveChk->isChecked() ); } ); - mSearchBarLayout->setCommand( "change-whole-word", - [&] { mWholeWordChk->setChecked( !mWholeWordChk->isChecked() ); } ); + mSearchBarLayout->setCommand( + "change-whole-word", [&] { mWholeWordChk->setChecked( !mWholeWordChk->isChecked() ); } ); mSearchBarLayout->setCommand( "change-escape-sequence", [&] { mEscapeSequenceChk->setChecked( !mEscapeSequenceChk->isChecked() ); } ); @@ -139,12 +142,13 @@ void DocSearchController::initSearchBar( addReturnListener( mReplaceInput, "find-and-replace" ); addClickListener( findPrevButton, "find-prev" ); addClickListener( findNextButton, "repeat-find" ); + addClickListener( selectAllButton, "select-all-results" ); addClickListener( replaceButton, "replace-selection" ); addClickListener( findReplaceButton, "find-and-replace" ); addClickListener( replaceAllButton, "replace-all" ); addClickListener( closeButton, "close-searchbar" ); mReplaceInput->addEventListener( Event::OnTabNavigate, - [&]( const Event* ) { mFindInput->setFocus(); } ); + [&]( const Event* ) { mFindInput->setFocus(); } ); } void DocSearchController::showFindView() { @@ -160,7 +164,7 @@ void DocSearchController::showFindView() { mSearchState.wholeWord = mWholeWordChk->isChecked(); mSearchState.escapeSequences = mEscapeSequenceChk->isChecked(); mSearchState.type = mLuaPatternChk->isChecked() ? TextDocument::FindReplaceType::LuaPattern - : TextDocument::FindReplaceType::Normal; + : TextDocument::FindReplaceType::Normal; mSearchBarLayout->setEnabled( true )->setVisible( true ); mFindInput->getDocument().selectAll(); @@ -274,6 +278,22 @@ bool DocSearchController::replaceSelection( SearchState& search, const String& r return true; } +void DocSearchController::selectAll( SearchState& search ) { + if ( !search.editor || !mEditorSplitter->editorExists( search.editor ) ) + return; + if ( search.text.empty() ) + search.text = mLastSearch; + if ( search.text.empty() ) + return; + search.editor->getDocument().setActiveClient( search.editor ); + mLastSearch = search.text; + TextDocument& doc = search.editor->getDocument(); + TextRanges ranges = doc.findAll( search.text, search.caseSensitive, search.wholeWord, + search.type, search.range ); + for ( const auto& range : ranges ) + doc.addSelection( range.reversed() ); +} + int DocSearchController::replaceAll( SearchState& search, const String& replace ) { if ( !search.editor || !mEditorSplitter->editorExists( search.editor ) ) return 0; diff --git a/src/tools/ecode/docsearchcontroller.hpp b/src/tools/ecode/docsearchcontroller.hpp index bb767b36b..1b97e8b4c 100644 --- a/src/tools/ecode/docsearchcontroller.hpp +++ b/src/tools/ecode/docsearchcontroller.hpp @@ -36,7 +36,8 @@ class DocSearchController { { "mod+w", "change-whole-word" }, { "mod+l", "toggle-lua-pattern" }, { "mod+e", "change-escape-sequence" }, - { "mod+shift+g", "find-prev" } }; + { "mod+shift+g", "find-prev" }, + { "mod+shift+a", "select-all-results" } }; } DocSearchController( UICodeEditorSplitter*, App* app ); @@ -65,6 +66,8 @@ class DocSearchController { SearchBarConfig getSearchBarConfig() const; + void selectAll( SearchState& search ); + protected: UICodeEditorSplitter* mEditorSplitter{ nullptr }; UITextInput* mFindInput{ nullptr }; diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 5f3f180a0..e5b9de219 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -2439,7 +2439,8 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe - + " +