From 437ca02651223e5230765c27dc940e8e1042359f Mon Sep 17 00:00:00 2001 From: santiago Date: Wed, 21 Sep 2022 20:44:22 +0200 Subject: [PATCH 01/46] Allow word mode backspace and delete --- TextEditor.cpp | 84 +++++++++++++++++++++++++++++++++----------------- TextEditor.h | 9 ++++-- 2 files changed, 62 insertions(+), 31 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 8d16d619..782fce3f 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -751,10 +751,10 @@ void TextEditor::HandleKeyboardInputs() MoveHome(shift); else if (!alt && !ctrl && !super && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End))) MoveEnd(shift); - else if (!IsReadOnly() && !alt && !ctrl && !shift && !super && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) - Delete(); - else if (!IsReadOnly() && !alt && !ctrl && !shift && !super && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) - Backspace(); + else if (!IsReadOnly() && !alt && !shift && !super && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) + Delete(ctrl); + else if (!IsReadOnly() && !alt && !shift && !super && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) + Backspace(ctrl); else if (!alt && !ctrl && !shift && !super && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) mOverwrite ^= true; else if (isCtrlOnly && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) @@ -1796,7 +1796,7 @@ void TextEditor::MoveEnd(bool aSelect) } } -void TextEditor::Delete() +void TextEditor::Delete(bool aWordMode) { assert(!mReadOnly); @@ -1835,14 +1835,26 @@ void TextEditor::Delete() } else { - auto cindex = GetCharacterIndex(pos); - u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); - u.mRemovedEnd.mColumn++; - u.mRemoved = GetText(u.mRemovedStart, u.mRemovedEnd); + if (aWordMode) + { + Coordinates end = FindWordEnd(mState.mCursorPosition); + u.mRemoved = GetText(mState.mCursorPosition, end); + u.mRemovedStart = mState.mCursorPosition; + u.mRemovedEnd = end; + DeleteRange(mState.mCursorPosition, end); + int charactersDeleted = end.mColumn - mState.mCursorPosition.mColumn; + } + else + { + auto cindex = GetCharacterIndex(pos); + u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); + u.mRemovedEnd.mColumn++; + u.mRemoved = GetText(u.mRemovedStart, u.mRemovedEnd); - auto d = UTF8CharLength(line[cindex].mChar); - while (d-- > 0 && cindex < (int)line.size()) - line.erase(line.begin() + cindex); + auto d = UTF8CharLength(line[cindex].mChar); + while (d-- > 0 && cindex < (int)line.size()) + line.erase(line.begin() + cindex); + } } mTextChanged = true; @@ -1854,7 +1866,7 @@ void TextEditor::Delete() AddUndo(u); } -void TextEditor::Backspace() +void TextEditor::Backspace(bool aWordMode) { assert(!mReadOnly); @@ -1903,26 +1915,40 @@ void TextEditor::Backspace() else { auto& line = mLines[mState.mCursorPosition.mLine]; - auto cindex = GetCharacterIndex(pos) - 1; - auto cend = cindex + 1; - while (cindex > 0 && IsUTFSequence(line[cindex].mChar)) - --cindex; - //if (cindex > 0 && UTF8CharLength(line[cindex].mChar) > 1) - // --cindex; + if (aWordMode) + { + Coordinates start = FindWordStart(mState.mCursorPosition - Coordinates(0, 1)); + u.mRemoved = GetText(start, mState.mCursorPosition); + u.mRemovedStart = start; + u.mRemovedEnd = mState.mCursorPosition; + DeleteRange(start, mState.mCursorPosition); + int charactersDeleted = mState.mCursorPosition.mColumn - start.mColumn; + mState.mCursorPosition.mColumn -= charactersDeleted; + } + else + { + auto cindex = GetCharacterIndex(pos) - 1; + auto cend = cindex + 1; + while (cindex > 0 && IsUTFSequence(line[cindex].mChar)) + --cindex; - u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); - --u.mRemovedStart.mColumn; + //if (cindex > 0 && UTF8CharLength(line[cindex].mChar) > 1) + // --cindex; - if (line[cindex].mChar == '\t') - mState.mCursorPosition.mColumn -= mTabSize; - else - --mState.mCursorPosition.mColumn; + u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); + --u.mRemovedStart.mColumn; - while (cindex < line.size() && cend-- > cindex) - { - u.mRemoved += line[cindex].mChar; - line.erase(line.begin() + cindex); + if (line[cindex].mChar == '\t') + mState.mCursorPosition.mColumn -= mTabSize; + else + --mState.mCursorPosition.mColumn; + + while (cindex < line.size() && cend-- > cindex) + { + u.mRemoved += line[cindex].mChar; + line.erase(line.begin() + cindex); + } } } diff --git a/TextEditor.h b/TextEditor.h index f4d5dc2c..8872647a 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -117,6 +117,11 @@ class TextEditor return mLine > o.mLine; return mColumn >= o.mColumn; } + + Coordinates operator -(const Coordinates& o) + { + return Coordinates(mLine - o.mLine, mColumn - o.mColumn); + } }; struct Identifier @@ -258,7 +263,7 @@ class TextEditor void Copy(); void Cut(); void Paste(); - void Delete(); + void Delete(bool aWordMode = false); bool CanUndo() const; bool CanRedo() const; @@ -341,7 +346,7 @@ class TextEditor void RemoveLine(int aIndex); Line& InsertLine(int aIndex); void EnterCharacter(ImWchar aChar, bool aShift); - void Backspace(); + void Backspace(bool aWordMode = false); void DeleteSelection(); std::string GetWordUnderCursor() const; std::string GetWordAt(const Coordinates& aCoords) const; From 958d071c3165747a41cd047523d3cd4baa00b957 Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 24 Sep 2022 11:15:50 +0200 Subject: [PATCH 02/46] merging some of the pull request changes --- TextEditor.cpp | 33 +++++++++++++++++++-------------- TextEditor.h | 4 ++-- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 782fce3f..4982f4ca 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -42,13 +42,13 @@ TextEditor::TextEditor() , mColorRangeMax(0) , mSelectionMode(SelectionMode::Normal) , mCheckComments(true) - , mLastClick(-1.0f) , mHandleKeyboardInputs(true) , mHandleMouseInputs(true) , mIgnoreImGuiChild(false) , mShowWhitespaces(true) , mShowShortTabGlyphs(false) , mStartTime(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + , mLastClick(-1.0f) { SetPalette(GetDarkPalette()); SetLanguageDefinition(LanguageDefinition::HLSL()); @@ -343,9 +343,11 @@ TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPositi int delta = 0; // First we find the hovered column coord. - while (mTextStart + columnX - (aInsertionMode ? 0.5f : 0.0f) * columnWidth < local.x && (size_t)columnIndex < line.size()) + for (size_t columnIndex = 0; columnIndex < line.size(); ++columnIndex) { - columnCoord += delta; + float columnWidth = 0.0f; + int delta = 0; + if (line[columnIndex].mChar == '\t') { float oldX = columnX; @@ -365,7 +367,11 @@ TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPositi columnX += columnWidth; delta = 1; } - ++columnIndex; + + if (mTextStart + columnX - (aInsertionMode ? 0.5f : 0.0f) * columnWidth < local.x) + columnCoord += delta; + else + break; } // Then we reduce by 1 column coord if cursor is on the left side of the hovered column. @@ -1113,7 +1119,7 @@ void TextEditor::Render() } // Draw a tooltip on known identifiers/preprocessor symbols - if (ImGui::IsMousePosValid()) + if (ImGui::IsMousePosValid() && ImGui::IsWindowHovered()) { auto mpos = ImGui::GetMousePos(); ImVec2 origin = ImGui::GetCursorScreenPos(); @@ -1122,7 +1128,7 @@ void TextEditor::Render() if (local.x >= mTextStart) { auto pos = ScreenPosToCoordinates(mpos); - printf("Coord(%d, %d)\n", pos.mLine, pos.mColumn); + //printf("Coord(%d, %d)\n", pos.mLine, pos.mColumn); auto id = GetWordAt(pos); if (!id.empty()) { @@ -1148,13 +1154,12 @@ void TextEditor::Render() } } - + ImGui::SetCursorPos(ImVec2(0, 0)); ImGui::Dummy(ImVec2((longest + 2), mLines.size() * mCharAdvance.y)); if (mScrollToCursor) { EnsureCursorVisible(); - ImGui::SetWindowFocus(); mScrollToCursor = false; } } @@ -1168,7 +1173,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::ColorConvertU32ToFloat4(mPalette[(int)PaletteIndex::Background])); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); if (!mIgnoreImGuiChild) - ImGui::BeginChild(aTitle, aSize, aBorder, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoMove); + ImGui::BeginChild(aTitle, aSize, aBorder, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNavInputs); if (mHandleKeyboardInputs) { @@ -2077,8 +2082,8 @@ void TextEditor::Redo(int aSteps) const TextEditor::Palette & TextEditor::GetDarkPalette() { const static Palette p = { { - 0xff7f7f7f, // Default - 0xffd69c56, // Keyword + 0xffb0b0b0, // Default + 0xffd69c56, // Keyword 0xff00ff00, // Number 0xff7070e0, // String 0xff70a0e0, // Char literal @@ -2105,7 +2110,7 @@ const TextEditor::Palette & TextEditor::GetDarkPalette() const TextEditor::Palette & TextEditor::GetLightPalette() { const static Palette p = { { - 0xff7f7f7f, // None + 0xff404040, // None 0xffff0c06, // Keyword 0xff008000, // Number 0xff2020a0, // String @@ -2119,7 +2124,7 @@ const TextEditor::Palette & TextEditor::GetLightPalette() 0xff405020, // Comment (multi line) 0xffffffff, // Background 0xff000000, // Cursor - 0x80600000, // Selection + 0x40600000, // Selection 0xa00010ff, // ErrorMarker 0x80f08000, // Breakpoint 0xff505000, // Line number @@ -2415,7 +2420,7 @@ void TextEditor::ColorizeInternal() withinSingleLineComment = true; } - inComment = inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); + inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); line[currentIndex].mMultiLineComment = inComment; line[currentIndex].mComment = withinSingleLineComment; diff --git a/TextEditor.h b/TextEditor.h index 8872647a..bc767fd4 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -10,7 +10,7 @@ #include #include "imgui.h" -class TextEditor +class IMGUI_API TextEditor { public: enum class PaletteIndex @@ -224,7 +224,7 @@ class TextEditor void SetCursorPosition(const Coordinates& aPosition); inline void SetHandleMouseInputs (bool aValue){ mHandleMouseInputs = aValue;} - inline bool IsHandleMouseInputsEnabled() const { return mHandleKeyboardInputs; } + inline bool IsHandleMouseInputsEnabled() const { return mHandleMouseInputs; } inline void SetHandleKeyboardInputs (bool aValue){ mHandleKeyboardInputs = aValue;} inline bool IsHandleKeyboardInputsEnabled() const { return mHandleKeyboardInputs; } From a5959efd3a2d623d24e1a345ad765fd5f2d0f178 Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 24 Sep 2022 11:35:33 +0200 Subject: [PATCH 03/46] fix move right --- TextEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 4982f4ca..6b6b9614 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -1695,7 +1695,7 @@ void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) cindex += UTF8CharLength(line[cindex].mChar); mState.mCursorPosition = Coordinates(lindex, GetCharacterColumn(lindex, cindex)); if (aWordMode) - mState.mCursorPosition = FindNextWord(mState.mCursorPosition); + mState.mCursorPosition = FindWordEnd(mState.mCursorPosition); } } From 3cb389b34d7b3a9e5a848768841b858dd38fc8aa Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 24 Sep 2022 12:15:59 +0200 Subject: [PATCH 04/46] selection fixes --- TextEditor.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 6b6b9614..2dfe2dab 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -835,6 +835,8 @@ void TextEditor::HandleMouseInputs() if (!ctrl) { mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos())); + mInteractiveStart = FindWordStart(mState.mCursorPosition); + mState.mCursorPosition = mInteractiveEnd = FindWordEnd(mState.mCursorPosition); if (mSelectionMode == SelectionMode::Line) mSelectionMode = SelectionMode::Normal; else @@ -1477,14 +1479,8 @@ void TextEditor::SetSelection(const Coordinates & aStart, const Coordinates & aE switch (aMode) { case TextEditor::SelectionMode::Normal: - break; case TextEditor::SelectionMode::Word: - { - mState.mSelectionStart = FindWordStart(mState.mSelectionStart); - if (!IsOnWordBoundary(mState.mSelectionEnd)) - mState.mSelectionEnd = FindWordEnd(FindWordStart(mState.mSelectionEnd)); break; - } case TextEditor::SelectionMode::Line: { const auto lineNo = mState.mSelectionEnd.mLine; @@ -1661,7 +1657,11 @@ void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode) } } else + { + if (HasSelection()) + mState.mCursorPosition = mInteractiveStart; mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + } SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); EnsureCursorVisible(); @@ -1712,7 +1712,11 @@ void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) } } else + { + if (HasSelection()) + mState.mCursorPosition = mInteractiveEnd; mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + } SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); EnsureCursorVisible(); From 1aa2d610624ad4b3b0e586e86909a2769e41a8c3 Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 24 Sep 2022 12:24:39 +0200 Subject: [PATCH 05/46] selection detail --- TextEditor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 2dfe2dab..04c15acb 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -1658,7 +1658,7 @@ void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode) } else { - if (HasSelection()) + if (HasSelection() && !aWordMode) mState.mCursorPosition = mInteractiveStart; mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; } @@ -1713,7 +1713,7 @@ void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) } else { - if (HasSelection()) + if (HasSelection() && !aWordMode) mState.mCursorPosition = mInteractiveEnd; mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; } From 7872672687c4a7715c349034c0b77327bf5286d9 Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 24 Sep 2022 12:24:59 +0200 Subject: [PATCH 06/46] non blinking cursor --- TextEditor.cpp | 45 +++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 04c15acb..15735c3a 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -997,36 +997,29 @@ void TextEditor::Render() // Render the cursor if (focused) { - auto timeEnd = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - auto elapsed = timeEnd - mStartTime; - if (elapsed > 400) - { - float width = 1.0f; - auto cindex = GetCharacterIndex(mState.mCursorPosition); - float cx = TextDistanceToLineStart(mState.mCursorPosition); + float width = 1.0f; + auto cindex = GetCharacterIndex(mState.mCursorPosition); + float cx = TextDistanceToLineStart(mState.mCursorPosition); - if (mOverwrite && cindex < (int)line.size()) + if (mOverwrite && cindex < (int)line.size()) + { + auto c = line[cindex].mChar; + if (c == '\t') { - auto c = line[cindex].mChar; - if (c == '\t') - { - auto x = (1.0f + std::floor((1.0f + cx) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); - width = x - cx; - } - else - { - char buf2[2]; - buf2[0] = line[cindex].mChar; - buf2[1] = '\0'; - width = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf2).x; - } + auto x = (1.0f + std::floor((1.0f + cx) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); + width = x - cx; + } + else + { + char buf2[2]; + buf2[0] = line[cindex].mChar; + buf2[1] = '\0'; + width = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf2).x; } - ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y); - ImVec2 cend(textScreenPos.x + cx + width, lineStartScreenPos.y + mCharAdvance.y); - drawList->AddRectFilled(cstart, cend, mPalette[(int)PaletteIndex::Cursor]); - if (elapsed > 800) - mStartTime = timeEnd; } + ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y); + ImVec2 cend(textScreenPos.x + cx + width, lineStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(cstart, cend, mPalette[(int)PaletteIndex::Cursor]); } } From 380ea4fd2925c3329449c762ea65a6ba969f5bb5 Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 24 Sep 2022 12:35:13 +0200 Subject: [PATCH 07/46] better line selection --- TextEditor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 15735c3a..54918f07 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -1479,7 +1479,8 @@ void TextEditor::SetSelection(const Coordinates & aStart, const Coordinates & aE const auto lineNo = mState.mSelectionEnd.mLine; const auto lineSize = (size_t)lineNo < mLines.size() ? mLines[lineNo].size() : 0; mState.mSelectionStart = Coordinates(mState.mSelectionStart.mLine, 0); - mState.mSelectionEnd = Coordinates(lineNo, GetLineMaxColumn(lineNo)); + mState.mSelectionEnd = mLines.size() > lineNo + 1 ? Coordinates(lineNo + 1, 0) : Coordinates(lineNo, GetLineMaxColumn(lineNo)); + mState.mCursorPosition = mState.mSelectionEnd; break; } default: From 1d3636481c5eaadfbb7c78dec962ea82220da5b4 Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 24 Sep 2022 12:56:48 +0200 Subject: [PATCH 08/46] remove redundant SanitizeCoordinates calls --- TextEditor.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 54918f07..00ca4444 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -818,7 +818,7 @@ void TextEditor::HandleMouseInputs() { if (!ctrl) { - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos())); + mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); mSelectionMode = SelectionMode::Line; SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); } @@ -834,7 +834,7 @@ void TextEditor::HandleMouseInputs() { if (!ctrl) { - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos())); + mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); mInteractiveStart = FindWordStart(mState.mCursorPosition); mState.mCursorPosition = mInteractiveEnd = FindWordEnd(mState.mCursorPosition); if (mSelectionMode == SelectionMode::Line) @@ -852,7 +852,7 @@ void TextEditor::HandleMouseInputs() */ else if (click) { - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite)); + mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite); if (ctrl) mSelectionMode = SelectionMode::Word; else @@ -865,7 +865,7 @@ void TextEditor::HandleMouseInputs() else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) { io.WantCaptureMouse = true; - mState.mCursorPosition = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite)); + mState.mCursorPosition = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite); SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); } } From 918ac097d3c089d45ace51630f31b19ca3364371 Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 24 Sep 2022 13:18:30 +0200 Subject: [PATCH 09/46] line selection by clicking on line numbers --- TextEditor.cpp | 13 ++++++++++--- TextEditor.h | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 00ca4444..51c208b0 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -322,10 +322,14 @@ void TextEditor::AddUndo(UndoRecord& aValue) ++mUndoIndex; } -TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPosition, bool aInsertionMode) const +TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPosition, bool aInsertionMode, bool* isOverLineNumber) const { ImVec2 origin = ImGui::GetCursorScreenPos(); ImVec2 local(aPosition.x - origin.x + 3.0f, aPosition.y - origin.y); + + if (isOverLineNumber != nullptr) + *isOverLineNumber = local.x < mTextStart; + float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ").x; int lineNo = std::max(0, (int)floor(local.y / mCharAdvance.y)); @@ -852,8 +856,11 @@ void TextEditor::HandleMouseInputs() */ else if (click) { - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite); - if (ctrl) + bool isOverLineNumber; + mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite, &isOverLineNumber); + if (isOverLineNumber) + mSelectionMode = SelectionMode::Line; + else if (ctrl) mSelectionMode = SelectionMode::Word; else mSelectionMode = SelectionMode::Normal; diff --git a/TextEditor.h b/TextEditor.h index bc767fd4..cbb0b296 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -333,7 +333,7 @@ class IMGUI_API TextEditor void DeleteRange(const Coordinates& aStart, const Coordinates& aEnd); int InsertTextAt(Coordinates& aWhere, const char* aValue); void AddUndo(UndoRecord& aValue); - Coordinates ScreenPosToCoordinates(const ImVec2& aPosition, bool aInsertionMode = false) const; + Coordinates ScreenPosToCoordinates(const ImVec2& aPosition, bool aInsertionMode = false, bool* isOverLineNumber = nullptr) const; Coordinates FindWordStart(const Coordinates& aFrom) const; Coordinates FindWordEnd(const Coordinates& aFrom) const; Coordinates FindNextWord(const Coordinates& aFrom) const; From 21db0c136ad2d268bd7afb650342959e02873303 Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 24 Sep 2022 14:22:30 +0200 Subject: [PATCH 10/46] delete line option --- TextEditor.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ TextEditor.h | 1 + 2 files changed, 43 insertions(+) diff --git a/TextEditor.cpp b/TextEditor.cpp index 51c208b0..12a45bf6 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -765,6 +765,8 @@ void TextEditor::HandleKeyboardInputs() Delete(ctrl); else if (!IsReadOnly() && !alt && !shift && !super && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) Backspace(ctrl); + else if (!IsReadOnly() && !alt && ctrl && shift && !super && ImGui::IsKeyPressed('K')) + DeleteCurrentLine(); else if (!alt && !ctrl && !shift && !super && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) mOverwrite ^= true; else if (isCtrlOnly && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) @@ -1539,6 +1541,46 @@ void TextEditor::DeleteSelection() Colorize(mState.mSelectionStart.mLine, 1); } +void TextEditor::DeleteCurrentLine() +{ + UndoRecord u; + u.mBefore = mState; + + int currentLine = mState.mCursorPosition.mLine; + int nextLine = currentLine + 1; + int prevLine = currentLine - 1; + + Coordinates toDeleteStart, toDeleteEnd; + if (mLines.size() > nextLine) // next line exists + { + toDeleteStart = Coordinates(currentLine, 0); + toDeleteEnd = Coordinates(nextLine, 0); + mState.mCursorPosition.mColumn = 0; + } + else if (prevLine > -1) // previous line exists + { + toDeleteStart = Coordinates(prevLine, GetLineMaxColumn(prevLine)); + toDeleteEnd = Coordinates(currentLine, GetLineMaxColumn(currentLine)); + mState.mCursorPosition.mLine--; + mState.mCursorPosition.mColumn = 0; + } + else + { + toDeleteStart = Coordinates(currentLine, 0); + toDeleteEnd = Coordinates(currentLine, GetLineMaxColumn(currentLine)); + mState.mCursorPosition.mColumn = 0; + } + + u.mRemovedStart = toDeleteStart; + u.mRemovedEnd = toDeleteEnd; + u.mRemoved = GetText(toDeleteStart, toDeleteEnd); + + DeleteRange(toDeleteStart, toDeleteEnd); + + u.mAfter = mState; + AddUndo(u); +} + void TextEditor::MoveUp(int aAmount, bool aSelect) { auto oldPos = mState.mCursorPosition; diff --git a/TextEditor.h b/TextEditor.h index cbb0b296..861df2dd 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -348,6 +348,7 @@ class IMGUI_API TextEditor void EnterCharacter(ImWchar aChar, bool aShift); void Backspace(bool aWordMode = false); void DeleteSelection(); + void DeleteCurrentLine(); std::string GetWordUnderCursor() const; std::string GetWordAt(const Coordinates& aCoords) const; ImU32 GetGlyphColor(const Glyph& aGlyph) const; From 34d1d3c3f303cf3c7f0bbcbfca2645d904ef857a Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 24 Sep 2022 20:17:44 +0200 Subject: [PATCH 11/46] shift + click to select --- TextEditor.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 12a45bf6..bb4fcbee 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -809,9 +809,9 @@ void TextEditor::HandleMouseInputs() if (ImGui::IsWindowHovered()) { + auto click = ImGui::IsMouseClicked(0); if (!shift && !alt) { - auto click = ImGui::IsMouseClicked(0); auto doubleClick = ImGui::IsMouseDoubleClicked(0); auto t = ImGui::GetTime(); auto tripleClick = click && !doubleClick && (mLastClick != -1.0f && (t - mLastClick) < io.MouseDoubleClickTime); @@ -878,6 +878,17 @@ void TextEditor::HandleMouseInputs() SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); } } + else if (shift) + { + if (click) + { + Coordinates newSelectionEnd = ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite); + mState.mCursorPosition = newSelectionEnd; + SetSelectionEnd(newSelectionEnd); + mInteractiveEnd = mState.mSelectionEnd; + mInteractiveStart = mState.mSelectionStart; + } + } } } From c447f03b80f6583915e54b929dda23a7c3f6945f Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 24 Sep 2022 21:05:33 +0200 Subject: [PATCH 12/46] fix bug with interactive selection after deleting selection --- TextEditor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TextEditor.cpp b/TextEditor.cpp index bb4fcbee..d21bf089 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -1549,6 +1549,8 @@ void TextEditor::DeleteSelection() SetSelection(mState.mSelectionStart, mState.mSelectionStart); SetCursorPosition(mState.mSelectionStart); + mInteractiveStart = mState.mSelectionStart; + mInteractiveEnd = mState.mSelectionEnd; Colorize(mState.mSelectionStart.mLine, 1); } From af36b3534fcc98c5c4604df208bf83bc75885c9d Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 24 Sep 2022 23:36:10 +0200 Subject: [PATCH 13/46] palette changes from pr in original repo --- TextEditor.cpp | 23 +++++++++++++++-------- TextEditor.h | 2 ++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index d21bf089..c1486b57 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -892,12 +892,8 @@ void TextEditor::HandleMouseInputs() } } -void TextEditor::Render() +void TextEditor::UpdatePalette() { - /* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/ - const float fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x; - mCharAdvance = ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * mLineSpacing); - /* Update palette with the current alpha from style */ for (int i = 0; i < (int)PaletteIndex::Max; ++i) { @@ -905,6 +901,13 @@ void TextEditor::Render() color.w *= ImGui::GetStyle().Alpha; mPalette[i] = ImGui::ColorConvertFloat4ToU32(color); } +} + +void TextEditor::Render() +{ + /* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/ + const float fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x; + mCharAdvance = ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * mLineSpacing); assert(mLineBuffer.empty()); @@ -1097,9 +1100,9 @@ void TextEditor::Render() p4 = ImVec2(x2 - s * 0.2f, y + s * 0.2f); } - drawList->AddLine(p1, p2, 0x90909090); - drawList->AddLine(p2, p3, 0x90909090); - drawList->AddLine(p2, p4, 0x90909090); + drawList->AddLine(p1, p2, mPalette[(int)PaletteIndex::ControlCharacter]); + drawList->AddLine(p2, p3, mPalette[(int)PaletteIndex::ControlCharacter]); + drawList->AddLine(p2, p4, mPalette[(int)PaletteIndex::ControlCharacter]); } } else if (glyph.mChar == ' ') @@ -1185,6 +1188,8 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) mTextChanged = false; mCursorPositionChanged = false; + UpdatePalette(); + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::ColorConvertU32ToFloat4(mPalette[(int)PaletteIndex::Background])); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); if (!mIgnoreImGuiChild) @@ -2158,6 +2163,7 @@ const TextEditor::Palette & TextEditor::GetDarkPalette() 0xffe0e0e0, // Cursor 0x80a06020, // Selection 0x800020ff, // ErrorMarker + 0x90909090, // ControlCharacter 0x40f08000, // Breakpoint 0xff707000, // Line number 0x40000000, // Current line fill @@ -2186,6 +2192,7 @@ const TextEditor::Palette & TextEditor::GetLightPalette() 0xff000000, // Cursor 0x40600000, // Selection 0xa00010ff, // ErrorMarker + 0x90909090, // ControlCharacter 0x80f08000, // Breakpoint 0xff505000, // Line number 0x40000000, // Current line fill diff --git a/TextEditor.h b/TextEditor.h index 861df2dd..7a3aa4b8 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -31,6 +31,7 @@ class IMGUI_API TextEditor Cursor, Selection, ErrorMarker, + ControlCharacter, Breakpoint, LineNumber, CurrentLineFill, @@ -355,6 +356,7 @@ class IMGUI_API TextEditor void HandleKeyboardInputs(); void HandleMouseInputs(); + void UpdatePalette(); void Render(); float mLineSpacing; From bc6d6deea3015a36ac5eae4d551bc61e9672b442 Mon Sep 17 00:00:00 2001 From: santiago Date: Sun, 25 Sep 2022 00:10:45 +0200 Subject: [PATCH 14/46] use rgba order in u32 instead of abgr --- TextEditor.cpp | 120 ++++++++++++++++++++++++------------------------- TextEditor.h | 9 ++++ 2 files changed, 69 insertions(+), 60 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index c1486b57..357d5660 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -897,7 +897,7 @@ void TextEditor::UpdatePalette() /* Update palette with the current alpha from style */ for (int i = 0; i < (int)PaletteIndex::Max; ++i) { - auto color = ImGui::ColorConvertU32ToFloat4(mPaletteBase[i]); + auto color = U32ColorToVec4(mPaletteBase[i]); color.w *= ImGui::GetStyle().Alpha; mPalette[i] = ImGui::ColorConvertFloat4ToU32(color); } @@ -2147,28 +2147,28 @@ void TextEditor::Redo(int aSteps) const TextEditor::Palette & TextEditor::GetDarkPalette() { const static Palette p = { { - 0xffb0b0b0, // Default - 0xffd69c56, // Keyword - 0xff00ff00, // Number - 0xff7070e0, // String - 0xff70a0e0, // Char literal + 0xb0b0b0ff, // Default + 0x569cd6ff, // Keyword + 0x00ff00ff, // Number + 0xe07070ff, // String + 0xe0a070ff, // Char literal 0xffffffff, // Punctuation - 0xff408080, // Preprocessor - 0xffaaaaaa, // Identifier - 0xff9bc64d, // Known identifier - 0xffc040a0, // Preproc identifier - 0xff206020, // Comment (single line) - 0xff406020, // Comment (multi line) - 0xff101010, // Background - 0xffe0e0e0, // Cursor - 0x80a06020, // Selection - 0x800020ff, // ErrorMarker + 0x808040ff, // Preprocessor + 0xaaaaaaff, // Identifier + 0x4dc69bff, // Known identifier + 0xa040c0ff, // Preproc identifier + 0x206020ff, // Comment (single line) + 0x206040ff, // Comment (multi line) + 0x101010ff, // Background + 0xe0e0e0ff, // Cursor + 0x2060a080, // Selection + 0xff200080, // ErrorMarker 0x90909090, // ControlCharacter - 0x40f08000, // Breakpoint - 0xff707000, // Line number - 0x40000000, // Current line fill - 0x40808080, // Current line fill (inactive) - 0x40a0a0a0, // Current line edge + 0x0080f040, // Breakpoint + 0x007070ff, // Line number + 0x00000040, // Current line fill + 0x80808040, // Current line fill (inactive) + 0xa0a0a040, // Current line edge } }; return p; } @@ -2176,28 +2176,28 @@ const TextEditor::Palette & TextEditor::GetDarkPalette() const TextEditor::Palette & TextEditor::GetLightPalette() { const static Palette p = { { - 0xff404040, // None - 0xffff0c06, // Keyword - 0xff008000, // Number - 0xff2020a0, // String - 0xff304070, // Char literal - 0xff000000, // Punctuation - 0xff406060, // Preprocessor - 0xff404040, // Identifier - 0xff606010, // Known identifier - 0xffc040a0, // Preproc identifier - 0xff205020, // Comment (single line) - 0xff405020, // Comment (multi line) + 0x404040ff, // None + 0x060cffff, // Keyword + 0x008000ff, // Number + 0xa02020ff, // String + 0x704030ff, // Char literal + 0x000000ff, // Punctuation + 0x606040ff, // Preprocessor + 0x404040ff, // Identifier + 0x106060ff, // Known identifier + 0xa040c0ff, // Preproc identifier + 0x205020ff, // Comment (single line) + 0x205040ff, // Comment (multi line) 0xffffffff, // Background - 0xff000000, // Cursor - 0x40600000, // Selection - 0xa00010ff, // ErrorMarker + 0x000000ff, // Cursor + 0x00006040, // Selection + 0xff1000a0, // ErrorMarker 0x90909090, // ControlCharacter - 0x80f08000, // Breakpoint - 0xff505000, // Line number - 0x40000000, // Current line fill - 0x40808080, // Current line fill (inactive) - 0x40000000, // Current line edge + 0x0080f080, // Breakpoint + 0x005050ff, // Line number + 0x00000040, // Current line fill + 0x80808040, // Current line fill (inactive) + 0x00000040, // Current line edge } }; return p; } @@ -2205,27 +2205,27 @@ const TextEditor::Palette & TextEditor::GetLightPalette() const TextEditor::Palette & TextEditor::GetRetroBluePalette() { const static Palette p = { { - 0xff00ffff, // None - 0xffffff00, // Keyword - 0xff00ff00, // Number - 0xff808000, // String - 0xff808000, // Char literal + 0xffff00ff, // None + 0x00ffffff, // Keyword + 0x00ff00ff, // Number + 0x008080ff, // String + 0x008080ff, // Char literal 0xffffffff, // Punctuation - 0xff008000, // Preprocessor - 0xff00ffff, // Identifier + 0x008000ff, // Preprocessor + 0xffff00ff, // Identifier 0xffffffff, // Known identifier - 0xffff00ff, // Preproc identifier - 0xff808080, // Comment (single line) - 0xff404040, // Comment (multi line) - 0xff800000, // Background - 0xff0080ff, // Cursor - 0x80ffff00, // Selection - 0xa00000ff, // ErrorMarker - 0x80ff8000, // Breakpoint - 0xff808000, // Line number - 0x40000000, // Current line fill - 0x40808080, // Current line fill (inactive) - 0x40000000, // Current line edge + 0xff00ffff, // Preproc identifier + 0x808080ff, // Comment (single line) + 0x404040ff, // Comment (multi line) + 0x000080ff, // Background + 0xff8000ff, // Cursor + 0x00ffff80, // Selection + 0xff0000a0, // ErrorMarker + 0x0080ff80, // Breakpoint + 0x008080ff, // Line number + 0x00000040, // Current line fill + 0x80808040, // Current line fill (inactive) + 0x00000040, // Current line edge } }; return p; } diff --git a/TextEditor.h b/TextEditor.h index 7a3aa4b8..8d19a75d 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -239,6 +239,15 @@ class IMGUI_API TextEditor inline void SetShowShortTabGlyphs(bool aValue) { mShowShortTabGlyphs = aValue; } inline bool IsShowingShortTabGlyphs() const { return mShowShortTabGlyphs; } + inline ImVec4 U32ColorToVec4(ImU32 in) { + float s = 1.0f / 255.0f; + return ImVec4( + ((in >> IM_COL32_A_SHIFT) & 0xFF) * s, + ((in >> IM_COL32_B_SHIFT) & 0xFF) * s, + ((in >> IM_COL32_G_SHIFT) & 0xFF) * s, + ((in >> IM_COL32_R_SHIFT) & 0xFF) * s); + } + void SetTabSize(int aValue); inline int GetTabSize() const { return mTabSize; } From 58f0fcb55bd3abb7ee6f589010179dff26ec07eb Mon Sep 17 00:00:00 2001 From: santiago Date: Sun, 25 Sep 2022 00:52:11 +0200 Subject: [PATCH 15/46] add mariana theme --- TextEditor.cpp | 31 ++++++++++++++++++++++++++++++- TextEditor.h | 1 + 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 357d5660..05c15ef2 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -50,7 +50,7 @@ TextEditor::TextEditor() , mStartTime(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) , mLastClick(-1.0f) { - SetPalette(GetDarkPalette()); + SetPalette(GetMarianaPalette()); SetLanguageDefinition(LanguageDefinition::HLSL()); mLines.push_back(Line()); } @@ -2173,6 +2173,35 @@ const TextEditor::Palette & TextEditor::GetDarkPalette() return p; } +const TextEditor::Palette& TextEditor::GetMarianaPalette() +{ + const static Palette p = { { + 0xffffffff, // Default + 0xc695c6ff, // Keyword + 0xf9ae58ff, // Number + 0x99c794ff, // String + 0xe0a070ff, // Char literal + 0x5fb4b4ff, // Punctuation + 0x808040ff, // Preprocessor + 0xffffffff, // Identifier + 0x4dc69bff, // Known identifier + 0xe0a0ffff, // Preproc identifier + 0xa6acb9ff, // Comment (single line) + 0xa6acb9ff, // Comment (multi line) + 0x303841ff, // Background + 0xe0e0e0ff, // Cursor + 0x4e5a6580, // Selection + 0xec5f6680, // ErrorMarker + 0xffffff30, // ControlCharacter + 0x0080f040, // Breakpoint + 0xffffffb0, // Line number + 0x4e5a6580, // Current line fill + 0x4e5a6530, // Current line fill (inactive) + 0x4e5a65b0, // Current line edge + } }; + return p; +} + const TextEditor::Palette & TextEditor::GetLightPalette() { const static Palette p = { { diff --git a/TextEditor.h b/TextEditor.h index 8d19a75d..625efb95 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -280,6 +280,7 @@ class IMGUI_API TextEditor void Undo(int aSteps = 1); void Redo(int aSteps = 1); + static const Palette& GetMarianaPalette(); static const Palette& GetDarkPalette(); static const Palette& GetLightPalette(); static const Palette& GetRetroBluePalette(); From 02593eea0cc39f330660f6c2b22fbe973850fd7b Mon Sep 17 00:00:00 2001 From: santiago Date: Sun, 25 Sep 2022 23:10:22 +0200 Subject: [PATCH 16/46] unfinished --- TextEditor.cpp | 1163 +++++++++++++++++++++++++++--------------------- TextEditor.h | 112 +++-- 2 files changed, 739 insertions(+), 536 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 05c15ef2..932058ef 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -8,6 +8,7 @@ #define IMGUI_DEFINE_MATH_OPERATORS #include "imgui.h" // for imGui::GetCurrentWindow() +#include // TODO // - multiline comments vs single-line: latter is blocking start of a ML @@ -112,9 +113,12 @@ std::string TextEditor::GetText(const Coordinates & aStart, const Coordinates & return result; } -TextEditor::Coordinates TextEditor::GetActualCursorCoordinates() const +TextEditor::Coordinates TextEditor::GetActualCursorCoordinates(int aCursor) const { - return SanitizeCoordinates(mState.mCursorPosition); + if (aCursor == -1) + return SanitizeCoordinates(mState.mCursors[mState.mCurrentCursor].mCursorPosition); + else + return SanitizeCoordinates(mState.mCursors[aCursor].mCursorPosition); } TextEditor::Coordinates TextEditor::SanitizeCoordinates(const Coordinates & aValue) const @@ -824,9 +828,9 @@ void TextEditor::HandleMouseInputs() { if (!ctrl) { - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); + mState.mCursors[mState.mCurrentCursor].mCursorPosition = mState.mCursors[mState.mCurrentCursor].mInteractiveStart = mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); mSelectionMode = SelectionMode::Line; - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + SetSelection(mState.mCursors[mState.mCurrentCursor].mInteractiveStart, mState.mCursors[mState.mCurrentCursor].mInteractiveEnd, mSelectionMode); } mLastClick = -1.0f; @@ -840,14 +844,14 @@ void TextEditor::HandleMouseInputs() { if (!ctrl) { - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); - mInteractiveStart = FindWordStart(mState.mCursorPosition); - mState.mCursorPosition = mInteractiveEnd = FindWordEnd(mState.mCursorPosition); + mState.mCursors[mState.mCurrentCursor].mCursorPosition = mState.mCursors[mState.mCurrentCursor].mInteractiveStart = mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); + mState.mCursors[mState.mCurrentCursor].mInteractiveStart = FindWordStart(mState.mCursors[mState.mCurrentCursor].mCursorPosition); + mState.mCursors[mState.mCurrentCursor].mCursorPosition = mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = FindWordEnd(mState.mCursors[mState.mCurrentCursor].mCursorPosition); if (mSelectionMode == SelectionMode::Line) mSelectionMode = SelectionMode::Normal; else mSelectionMode = SelectionMode::Word; - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + SetSelection(mState.mCursors[mState.mCurrentCursor].mInteractiveStart, mState.mCursors[mState.mCurrentCursor].mInteractiveEnd, mSelectionMode); } mLastClick = (float)ImGui::GetTime(); @@ -858,15 +862,20 @@ void TextEditor::HandleMouseInputs() */ else if (click) { + if (ctrl) + mState.AddCursor(); + else + mState.mCurrentCursor = 0; + bool isOverLineNumber; - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite, &isOverLineNumber); + mState.mCursors[mState.mCurrentCursor].mCursorPosition = mState.mCursors[mState.mCurrentCursor].mInteractiveStart = mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite, &isOverLineNumber); if (isOverLineNumber) mSelectionMode = SelectionMode::Line; else if (ctrl) mSelectionMode = SelectionMode::Word; else mSelectionMode = SelectionMode::Normal; - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + SetSelection(mState.mCursors[mState.mCurrentCursor].mInteractiveStart, mState.mCursors[mState.mCurrentCursor].mInteractiveEnd, mSelectionMode); mLastClick = (float)ImGui::GetTime(); } @@ -874,8 +883,19 @@ void TextEditor::HandleMouseInputs() else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) { io.WantCaptureMouse = true; - mState.mCursorPosition = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite); - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + mState.mCursors[mState.mCurrentCursor].mCursorPosition = mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite); + SetSelection(mState.mCursors[mState.mCurrentCursor].mInteractiveStart, mState.mCursors[mState.mCurrentCursor].mInteractiveEnd, mSelectionMode); + } + else if (ImGui::IsMouseReleased(0)) + { + std::cout << "RELEASEINGN\n"; + + // sort from cursors from top to bottom + std::sort(mState.mCursors.begin(), mState.mCursors.begin() + (mState.mCurrentCursor + 1), [](const Cursor& a, const Cursor& b) -> bool + { + return a.mSelectionStart < b.mSelectionStart; + }); + // merge cursors if they overlap } } else if (shift) @@ -883,10 +903,10 @@ void TextEditor::HandleMouseInputs() if (click) { Coordinates newSelectionEnd = ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite); - mState.mCursorPosition = newSelectionEnd; + mState.mCursors[mState.mCurrentCursor].mCursorPosition = newSelectionEnd; SetSelectionEnd(newSelectionEnd); - mInteractiveEnd = mState.mSelectionEnd; - mInteractiveStart = mState.mSelectionStart; + mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = mState.mCursors[mState.mCurrentCursor].mSelectionEnd; + mState.mCursors[mState.mCurrentCursor].mInteractiveStart = mState.mCursors[mState.mCurrentCursor].mSelectionStart; } } } @@ -950,23 +970,26 @@ void TextEditor::Render() Coordinates lineEndCoord(lineNo, GetLineMaxColumn(lineNo)); // Draw selection for the current line - float sstart = -1.0f; - float ssend = -1.0f; + for (int c = 0; c <= mState.mCurrentCursor; c++) + { + float sstart = -1.0f; + float ssend = -1.0f; - assert(mState.mSelectionStart <= mState.mSelectionEnd); - if (mState.mSelectionStart <= lineEndCoord) - sstart = mState.mSelectionStart > lineStartCoord ? TextDistanceToLineStart(mState.mSelectionStart) : 0.0f; - if (mState.mSelectionEnd > lineStartCoord) - ssend = TextDistanceToLineStart(mState.mSelectionEnd < lineEndCoord ? mState.mSelectionEnd : lineEndCoord); + assert(mState.mCursors[c].mSelectionStart <= mState.mCursors[c].mSelectionEnd); + if (mState.mCursors[c].mSelectionStart <= lineEndCoord) + sstart = mState.mCursors[c].mSelectionStart > lineStartCoord ? TextDistanceToLineStart(mState.mCursors[c].mSelectionStart) : 0.0f; + if (mState.mCursors[c].mSelectionEnd > lineStartCoord) + ssend = TextDistanceToLineStart(mState.mCursors[c].mSelectionEnd < lineEndCoord ? mState.mCursors[c].mSelectionEnd : lineEndCoord); - if (mState.mSelectionEnd.mLine > lineNo) - ssend += mCharAdvance.x; + if (mState.mCursors[c].mSelectionEnd.mLine > lineNo) + ssend += mCharAdvance.x; - if (sstart != -1 && ssend != -1 && sstart < ssend) - { - ImVec2 vstart(lineStartScreenPos.x + mTextStart + sstart, lineStartScreenPos.y); - ImVec2 vend(lineStartScreenPos.x + mTextStart + ssend, lineStartScreenPos.y + mCharAdvance.y); - drawList->AddRectFilled(vstart, vend, mPalette[(int)PaletteIndex::Selection]); + if (sstart != -1 && ssend != -1 && sstart < ssend) + { + ImVec2 vstart(lineStartScreenPos.x + mTextStart + sstart, lineStartScreenPos.y); + ImVec2 vend(lineStartScreenPos.x + mTextStart + ssend, lineStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(vstart, vend, mPalette[(int)PaletteIndex::Selection]); + } } // Draw breakpoints @@ -1005,7 +1028,13 @@ void TextEditor::Render() auto lineNoWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x; drawList->AddText(ImVec2(lineStartScreenPos.x + mTextStart - lineNoWidth, lineStartScreenPos.y), mPalette[(int)PaletteIndex::LineNumber], buf); - if (mState.mCursorPosition.mLine == lineNo) + std::vector cursorCoordsInThisLine; + for (int c = 0; c <= mState.mCurrentCursor; c++) + { + if (mState.mCursors[c].mCursorPosition.mLine == lineNo) + cursorCoordsInThisLine.push_back(mState.mCursors[c].mCursorPosition); + } + if (cursorCoordsInThisLine.size() > 0) { auto focused = ImGui::IsWindowFocused(); @@ -1017,32 +1046,35 @@ void TextEditor::Render() drawList->AddRect(start, end, mPalette[(int)PaletteIndex::CurrentLineEdge], 1.0f); } - // Render the cursor + // Render the cursors if (focused) { - float width = 1.0f; - auto cindex = GetCharacterIndex(mState.mCursorPosition); - float cx = TextDistanceToLineStart(mState.mCursorPosition); - - if (mOverwrite && cindex < (int)line.size()) + for (const auto& cursorCoords : cursorCoordsInThisLine) { - auto c = line[cindex].mChar; - if (c == '\t') - { - auto x = (1.0f + std::floor((1.0f + cx) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); - width = x - cx; - } - else + float width = 1.0f; + auto cindex = GetCharacterIndex(cursorCoords); + float cx = TextDistanceToLineStart(cursorCoords); + + if (mOverwrite && cindex < (int)line.size()) { - char buf2[2]; - buf2[0] = line[cindex].mChar; - buf2[1] = '\0'; - width = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf2).x; + auto c = line[cindex].mChar; + if (c == '\t') + { + auto x = (1.0f + std::floor((1.0f + cx) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); + width = x - cx; + } + else + { + char buf2[2]; + buf2[0] = line[cindex].mChar; + buf2[1] = '\0'; + width = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf2).x; + } } + ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y); + ImVec2 cend(textScreenPos.x + cx + width, lineStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(cstart, cend, mPalette[(int)PaletteIndex::Cursor]); } - ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y); - ImVec2 cend(textScreenPos.x + cx + width, lineStartScreenPos.y + mCharAdvance.y); - drawList->AddRectFilled(cstart, cend, mPalette[(int)PaletteIndex::Cursor]); } } @@ -1219,7 +1251,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) mWithinRender = false; } -void TextEditor::SetText(const std::string & aText) +void TextEditor::SetText(const std::string& aText) { mLines.clear(); mLines.emplace_back(Line()); @@ -1246,7 +1278,7 @@ void TextEditor::SetText(const std::string & aText) Colorize(); } -void TextEditor::SetTextLines(const std::vector & aLines) +void TextEditor::SetTextLines(const std::vector& aLines) { mLines.clear(); @@ -1260,7 +1292,7 @@ void TextEditor::SetTextLines(const std::vector & aLines) for (size_t i = 0; i < aLines.size(); ++i) { - const std::string & aLine = aLines[i]; + const std::string& aLine = aLines[i]; mLines[i].reserve(aLine.size()); for (size_t j = 0; j < aLine.size(); ++j) @@ -1287,166 +1319,176 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) if (HasSelection()) { - if (aChar == '\t' && mState.mSelectionStart.mLine != mState.mSelectionEnd.mLine) + auto selections = GetSelectedText(); + for (int i = selections.size() - 1; i > -1; i--) { + if (aChar == '\t' && selections[i].mStart.mLine != selections[i].mEnd.mLine) + { - auto start = mState.mSelectionStart; - auto end = mState.mSelectionEnd; - auto originalEnd = end; + auto start = selections[i].mStart; + auto end = selections[i].mEnd; + auto originalEnd = end; - if (start > end) - std::swap(start, end); - start.mColumn = 0; - // end.mColumn = end.mLine < mLines.size() ? mLines[end.mLine].size() : 0; - if (end.mColumn == 0 && end.mLine > 0) - --end.mLine; - if (end.mLine >= (int)mLines.size()) - end.mLine = mLines.empty() ? 0 : (int)mLines.size() - 1; - end.mColumn = GetLineMaxColumn(end.mLine); + if (start > end) + std::swap(start, end); + start.mColumn = 0; + // end.mColumn = end.mLine < mLines.size() ? mLines[end.mLine].size() : 0; + if (end.mColumn == 0 && end.mLine > 0) + --end.mLine; + if (end.mLine >= (int)mLines.size()) + end.mLine = mLines.empty() ? 0 : (int)mLines.size() - 1; + end.mColumn = GetLineMaxColumn(end.mLine); - //if (end.mColumn >= GetLineMaxColumn(end.mLine)) - // end.mColumn = GetLineMaxColumn(end.mLine) - 1; + //if (end.mColumn >= GetLineMaxColumn(end.mLine)) + // end.mColumn = GetLineMaxColumn(end.mLine) - 1; - u.mRemovedStart = start; - u.mRemovedEnd = end; - u.mRemoved = GetText(start, end); + u.mRemoved.push_back({ GetText(start, end) , start, end }); - bool modified = false; + bool modified = false; - for (int i = start.mLine; i <= end.mLine; i++) - { - auto& line = mLines[i]; - if (aShift) + for (int i = start.mLine; i <= end.mLine; i++) { - if (!line.empty()) + auto& line = mLines[i]; + if (aShift) { - if (line.front().mChar == '\t') + if (!line.empty()) { - line.erase(line.begin()); - modified = true; - } - else - { - for (int j = 0; j < mTabSize && !line.empty() && line.front().mChar == ' '; j++) + if (line.front().mChar == '\t') { line.erase(line.begin()); modified = true; } + else + { + for (int j = 0; j < mTabSize && !line.empty() && line.front().mChar == ' '; j++) + { + line.erase(line.begin()); + modified = true; + } + } } } + else + { + line.insert(line.begin(), Glyph('\t', TextEditor::PaletteIndex::Background)); + modified = true; + } } - else - { - line.insert(line.begin(), Glyph('\t', TextEditor::PaletteIndex::Background)); - modified = true; - } - } - if (modified) - { - start = Coordinates(start.mLine, GetCharacterColumn(start.mLine, 0)); - Coordinates rangeEnd; - if (originalEnd.mColumn != 0) - { - end = Coordinates(end.mLine, GetLineMaxColumn(end.mLine)); - rangeEnd = end; - u.mAdded = GetText(start, end); - } - else + if (modified) { - end = Coordinates(originalEnd.mLine, 0); - rangeEnd = Coordinates(end.mLine - 1, GetLineMaxColumn(end.mLine - 1)); - u.mAdded = GetText(start, rangeEnd); - } + start = Coordinates(start.mLine, GetCharacterColumn(start.mLine, 0)); + Coordinates rangeEnd; + std::string addedText; + if (originalEnd.mColumn != 0) + { + end = Coordinates(end.mLine, GetLineMaxColumn(end.mLine)); + rangeEnd = end; + addedText = GetText(start, end); + } + else + { + end = Coordinates(originalEnd.mLine, 0); + rangeEnd = Coordinates(end.mLine - 1, GetLineMaxColumn(end.mLine - 1)); + addedText = GetText(start, rangeEnd); + } - u.mAddedStart = start; - u.mAddedEnd = rangeEnd; - u.mAfter = mState; + u.mAdded.push_back({ addedText , start, rangeEnd }); + u.mAfter = mState; - mState.mSelectionStart = start; - mState.mSelectionEnd = end; - AddUndo(u); + mState.mCursors[mState.mCurrentCursor].mSelectionStart = start; + mState.mCursors[mState.mCurrentCursor].mSelectionEnd = end; + AddUndo(u); - mTextChanged = true; + mTextChanged = true; - EnsureCursorVisible(); - } + EnsureCursorVisible(); + } - return; - } // c == '\t' - else - { - u.mRemoved = GetSelectedText(); - u.mRemovedStart = mState.mSelectionStart; - u.mRemovedEnd = mState.mSelectionEnd; - DeleteSelection(); + return; + } // c == '\t' + else + { + u.mRemoved.push_back({ selections[i] }); + DeleteSelection(selections[i]); + } } } // HasSelection - auto coord = GetActualCursorCoordinates(); - u.mAddedStart = coord; - - assert(!mLines.empty()); - - if (aChar == '\n') + std::vector coords; + for (int c = mState.mCurrentCursor; c > -1; c--) { - InsertLine(coord.mLine + 1); - auto& line = mLines[coord.mLine]; - auto& newLine = mLines[coord.mLine + 1]; + auto coord = GetActualCursorCoordinates(c); + coords.push_back(coord); + Selection added, removed; + added.mStart = coord; - if (mLanguageDefinition.mAutoIndentation) - for (size_t it = 0; it < line.size() && isascii(line[it].mChar) && isblank(line[it].mChar); ++it) - newLine.push_back(line[it]); + assert(!mLines.empty()); - const size_t whitespaceSize = newLine.size(); - auto cindex = GetCharacterIndex(coord); - newLine.insert(newLine.end(), line.begin() + cindex, line.end()); - line.erase(line.begin() + cindex, line.begin() + line.size()); - SetCursorPosition(Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize))); - u.mAdded = (char)aChar; - } - else - { - char buf[7]; - int e = ImTextCharToUtf8(buf, 7, aChar); - if (e > 0) + if (aChar == '\n') { - buf[e] = '\0'; + InsertLine(coord.mLine + 1); auto& line = mLines[coord.mLine]; - auto cindex = GetCharacterIndex(coord); + auto& newLine = mLines[coord.mLine + 1]; - if (mOverwrite && cindex < (int)line.size()) - { - auto d = UTF8CharLength(line[cindex].mChar); + if (mLanguageDefinition.mAutoIndentation) + for (size_t it = 0; it < line.size() && isascii(line[it].mChar) && isblank(line[it].mChar); ++it) + newLine.push_back(line[it]); - u.mRemovedStart = mState.mCursorPosition; - u.mRemovedEnd = Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex + d)); + const size_t whitespaceSize = newLine.size(); + auto cindex = GetCharacterIndex(coord); + newLine.insert(newLine.end(), line.begin() + cindex, line.end()); + line.erase(line.begin() + cindex, line.begin() + line.size()); + SetCursorPosition(Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize)), c); + added.mText = (char)aChar; + OnLineAdded(coord.mLine + 1); + } + else + { + char buf[7]; + int e = ImTextCharToUtf8(buf, 7, aChar); + if (e > 0) + { + buf[e] = '\0'; + auto& line = mLines[coord.mLine]; + auto cindex = GetCharacterIndex(coord); - while (d-- > 0 && cindex < (int)line.size()) + if (mOverwrite && cindex < (int)line.size()) { - u.mRemoved += line[cindex].mChar; - line.erase(line.begin() + cindex); + auto d = UTF8CharLength(line[cindex].mChar); + + removed.mStart = mState.mCursors[c].mCursorPosition; + removed.mEnd = Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex + d)); + + while (d-- > 0 && cindex < (int)line.size()) + { + removed.mText += line[cindex].mChar; + line.erase(line.begin() + cindex); + } } - } - for (auto p = buf; *p != '\0'; p++, ++cindex) - line.insert(line.begin() + cindex, Glyph(*p, PaletteIndex::Default)); - u.mAdded = buf; + for (auto p = buf; *p != '\0'; p++, ++cindex) + line.insert(line.begin() + cindex, Glyph(*p, PaletteIndex::Default)); + added.mText = buf; - SetCursorPosition(Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex))); + SetCursorPosition(Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex)), c); + } + else + return; } - else - return; - } - mTextChanged = true; + mTextChanged = true; - u.mAddedEnd = GetActualCursorCoordinates(); - u.mAfter = mState; + added.mEnd = GetActualCursorCoordinates(c); + u.mAdded.push_back(added); + u.mRemoved.push_back(removed); + u.mAfter = mState; + } AddUndo(u); - Colorize(coord.mLine - 1, 3); + for (const auto& coord : coords) + Colorize(coord.mLine - 1, 3); EnsureCursorVisible(); } @@ -1460,39 +1502,51 @@ void TextEditor::SetColorizerEnable(bool aValue) mColorizerEnabled = aValue; } -void TextEditor::SetCursorPosition(const Coordinates & aPosition) +void TextEditor::SetCursorPosition(const Coordinates& aPosition, int aCursor) { - if (mState.mCursorPosition != aPosition) + if (aCursor == -1) + aCursor = mState.mCurrentCursor; + + if (mState.mCursors[aCursor].mCursorPosition != aPosition) { - mState.mCursorPosition = aPosition; + mState.mCursors[aCursor].mCursorPosition = aPosition; mCursorPositionChanged = true; EnsureCursorVisible(); } } -void TextEditor::SetSelectionStart(const Coordinates & aPosition) +void TextEditor::SetSelectionStart(const Coordinates& aPosition, int aCursor) { - mState.mSelectionStart = SanitizeCoordinates(aPosition); - if (mState.mSelectionStart > mState.mSelectionEnd) - std::swap(mState.mSelectionStart, mState.mSelectionEnd); + if (aCursor == -1) + aCursor = mState.mCurrentCursor; + + mState.mCursors[aCursor].mSelectionStart = SanitizeCoordinates(aPosition); + if (mState.mCursors[aCursor].mSelectionStart > mState.mCursors[aCursor].mSelectionEnd) + std::swap(mState.mCursors[aCursor].mSelectionStart, mState.mCursors[aCursor].mSelectionEnd); } -void TextEditor::SetSelectionEnd(const Coordinates & aPosition) +void TextEditor::SetSelectionEnd(const Coordinates& aPosition, int aCursor) { - mState.mSelectionEnd = SanitizeCoordinates(aPosition); - if (mState.mSelectionStart > mState.mSelectionEnd) - std::swap(mState.mSelectionStart, mState.mSelectionEnd); + if (aCursor == -1) + aCursor = mState.mCurrentCursor; + + mState.mCursors[aCursor].mSelectionEnd = SanitizeCoordinates(aPosition); + if (mState.mCursors[aCursor].mSelectionStart > mState.mCursors[aCursor].mSelectionEnd) + std::swap(mState.mCursors[aCursor].mSelectionStart, mState.mCursors[aCursor].mSelectionEnd); } -void TextEditor::SetSelection(const Coordinates & aStart, const Coordinates & aEnd, SelectionMode aMode) +void TextEditor::SetSelection(const Coordinates& aStart, const Coordinates& aEnd, SelectionMode aMode, int aCursor) { - auto oldSelStart = mState.mSelectionStart; - auto oldSelEnd = mState.mSelectionEnd; + if (aCursor == -1) + aCursor = mState.mCurrentCursor; - mState.mSelectionStart = SanitizeCoordinates(aStart); - mState.mSelectionEnd = SanitizeCoordinates(aEnd); - if (mState.mSelectionStart > mState.mSelectionEnd) - std::swap(mState.mSelectionStart, mState.mSelectionEnd); + auto oldSelStart = mState.mCursors[aCursor].mSelectionStart; + auto oldSelEnd = mState.mCursors[aCursor].mSelectionEnd; + + mState.mCursors[aCursor].mSelectionStart = SanitizeCoordinates(aStart); + mState.mCursors[aCursor].mSelectionEnd = SanitizeCoordinates(aEnd); + if (mState.mCursors[aCursor].mSelectionStart > mState.mCursors[aCursor].mSelectionEnd) + std::swap(mState.mCursors[aCursor].mSelectionStart, mState.mCursors[aCursor].mSelectionEnd); switch (aMode) { @@ -1501,19 +1555,19 @@ void TextEditor::SetSelection(const Coordinates & aStart, const Coordinates & aE break; case TextEditor::SelectionMode::Line: { - const auto lineNo = mState.mSelectionEnd.mLine; + const auto lineNo = mState.mCursors[aCursor].mSelectionEnd.mLine; const auto lineSize = (size_t)lineNo < mLines.size() ? mLines[lineNo].size() : 0; - mState.mSelectionStart = Coordinates(mState.mSelectionStart.mLine, 0); - mState.mSelectionEnd = mLines.size() > lineNo + 1 ? Coordinates(lineNo + 1, 0) : Coordinates(lineNo, GetLineMaxColumn(lineNo)); - mState.mCursorPosition = mState.mSelectionEnd; + mState.mCursors[aCursor].mSelectionStart = Coordinates(mState.mCursors[aCursor].mSelectionStart.mLine, 0); + mState.mCursors[aCursor].mSelectionEnd = mLines.size() > lineNo + 1 ? Coordinates(lineNo + 1, 0) : Coordinates(lineNo, GetLineMaxColumn(lineNo)); + mState.mCursors[aCursor].mCursorPosition = mState.mCursors[aCursor].mSelectionEnd; break; } default: break; } - if (mState.mSelectionStart != oldSelStart || - mState.mSelectionEnd != oldSelEnd) + if (mState.mCursors[aCursor].mSelectionStart != oldSelStart || + mState.mCursors[aCursor].mSelectionEnd != oldSelEnd) mCursorPositionChanged = true; } @@ -1522,18 +1576,18 @@ void TextEditor::SetTabSize(int aValue) mTabSize = std::max(0, std::min(32, aValue)); } -void TextEditor::InsertText(const std::string & aValue) +void TextEditor::InsertText(const std::string& aValue) { InsertText(aValue.c_str()); } -void TextEditor::InsertText(const char * aValue) +void TextEditor::InsertText(const char* aValue) { if (aValue == nullptr) return; auto pos = GetActualCursorCoordinates(); - auto start = std::min(pos, mState.mSelectionStart); + auto start = std::min(pos, mState.mCursors[mState.mCurrentCursor].mSelectionStart); int totalLines = pos.mLine - start.mLine; totalLines += InsertTextAt(pos, aValue); @@ -1543,20 +1597,21 @@ void TextEditor::InsertText(const char * aValue) Colorize(start.mLine - 1, totalLines + 2); } -void TextEditor::DeleteSelection() +void TextEditor::DeleteSelection(const Selection& selection) { - assert(mState.mSelectionEnd >= mState.mSelectionStart); + assert(selection.mEnd >= selection.mStart); - if (mState.mSelectionEnd == mState.mSelectionStart) + if (selection.mEnd == selection.mStart) return; - DeleteRange(mState.mSelectionStart, mState.mSelectionEnd); + DeleteRange(selection.mStart, selection.mEnd); - SetSelection(mState.mSelectionStart, mState.mSelectionStart); - SetCursorPosition(mState.mSelectionStart); - mInteractiveStart = mState.mSelectionStart; - mInteractiveEnd = mState.mSelectionEnd; - Colorize(mState.mSelectionStart.mLine, 1); + SetSelection(selection.mStart, selection.mStart); + SetCursorPosition(selection.mStart); + // need to track cursors somehow + mState.mCursors[mState.mCurrentCursor].mInteractiveStart = selection.mStart; + mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = selection.mEnd; + Colorize(selection.mStart.mLine, 1); } void TextEditor::DeleteCurrentLine() @@ -1564,7 +1619,7 @@ void TextEditor::DeleteCurrentLine() UndoRecord u; u.mBefore = mState; - int currentLine = mState.mCursorPosition.mLine; + int currentLine = mState.mCursors[mState.mCurrentCursor].mCursorPosition.mLine; int nextLine = currentLine + 1; int prevLine = currentLine - 1; @@ -1573,25 +1628,23 @@ void TextEditor::DeleteCurrentLine() { toDeleteStart = Coordinates(currentLine, 0); toDeleteEnd = Coordinates(nextLine, 0); - mState.mCursorPosition.mColumn = 0; + mState.mCursors[mState.mCurrentCursor].mCursorPosition.mColumn = 0; } else if (prevLine > -1) // previous line exists { toDeleteStart = Coordinates(prevLine, GetLineMaxColumn(prevLine)); toDeleteEnd = Coordinates(currentLine, GetLineMaxColumn(currentLine)); - mState.mCursorPosition.mLine--; - mState.mCursorPosition.mColumn = 0; + mState.mCursors[mState.mCurrentCursor].mCursorPosition.mLine--; + mState.mCursors[mState.mCurrentCursor].mCursorPosition.mColumn = 0; } else { toDeleteStart = Coordinates(currentLine, 0); toDeleteEnd = Coordinates(currentLine, GetLineMaxColumn(currentLine)); - mState.mCursorPosition.mColumn = 0; + mState.mCursors[mState.mCurrentCursor].mCursorPosition.mColumn = 0; } - u.mRemovedStart = toDeleteStart; - u.mRemovedEnd = toDeleteEnd; - u.mRemoved = GetText(toDeleteStart, toDeleteEnd); + u.mRemoved.push_back({ GetText(toDeleteStart, toDeleteEnd), toDeleteStart, toDeleteEnd }); DeleteRange(toDeleteStart, toDeleteEnd); @@ -1601,56 +1654,61 @@ void TextEditor::DeleteCurrentLine() void TextEditor::MoveUp(int aAmount, bool aSelect) { - auto oldPos = mState.mCursorPosition; - mState.mCursorPosition.mLine = std::max(0, mState.mCursorPosition.mLine - aAmount); - if (oldPos != mState.mCursorPosition) + for (int c = 0; c <= mState.mCurrentCursor; c++) { - if (aSelect) + auto oldPos = mState.mCursors[c].mCursorPosition; + mState.mCursors[c].mCursorPosition.mLine = std::max(0, mState.mCursors[c].mCursorPosition.mLine - aAmount); + if (oldPos != mState.mCursors[c].mCursorPosition) { - if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else if (oldPos == mInteractiveEnd) - mInteractiveEnd = mState.mCursorPosition; - else + if (aSelect) { - mInteractiveStart = mState.mCursorPosition; - mInteractiveEnd = oldPos; + if (oldPos == mState.mCursors[c].mInteractiveStart) + mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mCursorPosition; + else if (oldPos == mState.mCursors[c].mInteractiveEnd) + mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; + else + { + mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mCursorPosition; + mState.mCursors[c].mInteractiveEnd = oldPos; + } } + else + mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; + SetSelection(mState.mCursors[c].mInteractiveStart, mState.mCursors[c].mInteractiveEnd); } - else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd); - - EnsureCursorVisible(); } + EnsureCursorVisible(); } void TextEditor::MoveDown(int aAmount, bool aSelect) { - assert(mState.mCursorPosition.mColumn >= 0); - auto oldPos = mState.mCursorPosition; - mState.mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + aAmount)); - - if (mState.mCursorPosition != oldPos) + for (int c = 0; c <= mState.mCurrentCursor; c++) { - if (aSelect) + assert(mState.mCursors[c].mCursorPosition.mColumn >= 0); + auto oldPos = mState.mCursors[c].mCursorPosition; + mState.mCursors[c].mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursors[c].mCursorPosition.mLine + aAmount)); + + if (mState.mCursors[c].mCursorPosition != oldPos) { - if (oldPos == mInteractiveEnd) - mInteractiveEnd = mState.mCursorPosition; - else if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else + if (aSelect) { - mInteractiveStart = oldPos; - mInteractiveEnd = mState.mCursorPosition; + if (oldPos == mState.mCursors[c].mInteractiveEnd) + mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; + else if (oldPos == mState.mCursors[c].mInteractiveStart) + mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mCursorPosition; + else + { + mState.mCursors[c].mInteractiveStart = oldPos; + mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; + } } - } - else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd); + else + mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; + SetSelection(mState.mCursors[c].mInteractiveStart, mState.mCursors[c].mInteractiveEnd); - EnsureCursorVisible(); + } } + EnsureCursorVisible(); } static bool IsUTFSequence(char c) @@ -1663,206 +1721,224 @@ void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode) if (mLines.empty()) return; - auto oldPos = mState.mCursorPosition; - mState.mCursorPosition = GetActualCursorCoordinates(); - auto line = mState.mCursorPosition.mLine; - auto cindex = GetCharacterIndex(mState.mCursorPosition); - - while (aAmount-- > 0) + for (int c = 0; c <= mState.mCurrentCursor; c++) { - if (cindex == 0) + int amount = aAmount; + auto oldPos = mState.mCursors[c].mCursorPosition; + mState.mCursors[c].mCursorPosition = GetActualCursorCoordinates(c); + auto line = mState.mCursors[c].mCursorPosition.mLine; + auto cindex = GetCharacterIndex(mState.mCursors[c].mCursorPosition); + + while (amount-- > 0) { - if (line > 0) + if (cindex == 0) { - --line; - if ((int)mLines.size() > line) - cindex = (int)mLines[line].size(); - else - cindex = 0; + if (line > 0) + { + --line; + if ((int)mLines.size() > line) + cindex = (int)mLines[line].size(); + else + cindex = 0; + } } - } - else - { - --cindex; - if (cindex > 0) + else { - if ((int)mLines.size() > line) + --cindex; + if (cindex > 0) { - while (cindex > 0 && IsUTFSequence(mLines[line][cindex].mChar)) - --cindex; + if ((int)mLines.size() > line) + { + while (cindex > 0 && IsUTFSequence(mLines[line][cindex].mChar)) + --cindex; + } } } - } - mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex)); - if (aWordMode) - { - mState.mCursorPosition = FindWordStart(mState.mCursorPosition); - cindex = GetCharacterIndex(mState.mCursorPosition); + mState.mCursors[c].mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex)); + if (aWordMode) + { + mState.mCursors[c].mCursorPosition = FindWordStart(mState.mCursors[c].mCursorPosition); + cindex = GetCharacterIndex(mState.mCursors[c].mCursorPosition); + } } - } - mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex)); + mState.mCursors[c].mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex)); + std::cout << "changed from " << oldPos.mColumn << " to " << mState.mCursors[c].mCursorPosition.mColumn << std::endl; - assert(mState.mCursorPosition.mColumn >= 0); - if (aSelect) - { - if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else if (oldPos == mInteractiveEnd) - mInteractiveEnd = mState.mCursorPosition; + assert(mState.mCursors[c].mCursorPosition.mColumn >= 0); + if (aSelect) + { + if (oldPos == mState.mCursors[c].mInteractiveStart) + mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mCursorPosition; + else if (oldPos == mState.mCursors[c].mInteractiveEnd) + mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; + else + { + mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mCursorPosition; + mState.mCursors[c].mInteractiveEnd = oldPos; + } + } else { - mInteractiveStart = mState.mCursorPosition; - mInteractiveEnd = oldPos; + if (HasSelection() && !aWordMode) + mState.mCursors[c].mCursorPosition = mState.mCursors[c].mInteractiveStart; + mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; } + std::cout << "Setting selection for " << c << std::endl; + SetSelection(mState.mCursors[c].mInteractiveStart, mState.mCursors[c].mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal, c); } - else - { - if (HasSelection() && !aWordMode) - mState.mCursorPosition = mInteractiveStart; - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - } - SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); - EnsureCursorVisible(); } void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) { - auto oldPos = mState.mCursorPosition; - - if (mLines.empty() || oldPos.mLine >= mLines.size()) + if (mLines.empty()) return; - auto cindex = GetCharacterIndex(mState.mCursorPosition); - while (aAmount-- > 0) + for (int c = 0; c <= mState.mCurrentCursor; c++) { - auto lindex = mState.mCursorPosition.mLine; - auto& line = mLines[lindex]; + auto oldPos = mState.mCursors[c].mCursorPosition; + if (oldPos.mLine >= mLines.size()) + continue; - if (cindex >= line.size()) + int amount = aAmount; + auto cindex = GetCharacterIndex(mState.mCursors[c].mCursorPosition); + while (amount-- > 0) { - if (mState.mCursorPosition.mLine < mLines.size() - 1) + auto lindex = mState.mCursors[c].mCursorPosition.mLine; + auto& line = mLines[lindex]; + + if (cindex >= line.size()) { - mState.mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + 1)); - mState.mCursorPosition.mColumn = 0; + if (mState.mCursors[c].mCursorPosition.mLine < mLines.size() - 1) + { + mState.mCursors[c].mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursors[c].mCursorPosition.mLine + 1)); + mState.mCursors[c].mCursorPosition.mColumn = 0; + } + else + return; } else - return; + { + cindex += UTF8CharLength(line[cindex].mChar); + mState.mCursors[c].mCursorPosition = Coordinates(lindex, GetCharacterColumn(lindex, cindex)); + if (aWordMode) + mState.mCursors[c].mCursorPosition = FindWordEnd(mState.mCursors[c].mCursorPosition); + } } - else + + if (aSelect) { - cindex += UTF8CharLength(line[cindex].mChar); - mState.mCursorPosition = Coordinates(lindex, GetCharacterColumn(lindex, cindex)); - if (aWordMode) - mState.mCursorPosition = FindWordEnd(mState.mCursorPosition); + if (oldPos == mState.mCursors[c].mInteractiveEnd) + mState.mCursors[c].mInteractiveEnd = SanitizeCoordinates(mState.mCursors[c].mCursorPosition); + else if (oldPos == mState.mCursors[c].mInteractiveStart) + mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mCursorPosition; + else + { + mState.mCursors[c].mInteractiveStart = oldPos; + mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; + } } - } - - if (aSelect) - { - if (oldPos == mInteractiveEnd) - mInteractiveEnd = SanitizeCoordinates(mState.mCursorPosition); - else if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; else { - mInteractiveStart = oldPos; - mInteractiveEnd = mState.mCursorPosition; + if (HasSelection() && !aWordMode) + mState.mCursors[c].mCursorPosition = mState.mCursors[c].mInteractiveEnd; + mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; } + SetSelection(mState.mCursors[c].mInteractiveStart, mState.mCursors[c].mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal, c); } - else - { - if (HasSelection() && !aWordMode) - mState.mCursorPosition = mInteractiveEnd; - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - } - SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); - EnsureCursorVisible(); } void TextEditor::MoveTop(bool aSelect) { - auto oldPos = mState.mCursorPosition; + mState.mCurrentCursor = 0; + auto oldPos = mState.mCursors[mState.mCurrentCursor].mCursorPosition; SetCursorPosition(Coordinates(0, 0)); - if (mState.mCursorPosition != oldPos) + if (mState.mCursors[mState.mCurrentCursor].mCursorPosition != oldPos) { if (aSelect) { - mInteractiveEnd = oldPos; - mInteractiveStart = mState.mCursorPosition; + mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = oldPos; + mState.mCursors[mState.mCurrentCursor].mInteractiveStart = mState.mCursors[mState.mCurrentCursor].mCursorPosition; } else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd); + mState.mCursors[mState.mCurrentCursor].mInteractiveStart = mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = mState.mCursors[mState.mCurrentCursor].mCursorPosition; + SetSelection(mState.mCursors[mState.mCurrentCursor].mInteractiveStart, mState.mCursors[mState.mCurrentCursor].mInteractiveEnd); } } void TextEditor::TextEditor::MoveBottom(bool aSelect) { + mState.mCurrentCursor = 0; auto oldPos = GetCursorPosition(); auto newPos = Coordinates((int)mLines.size() - 1, 0); SetCursorPosition(newPos); if (aSelect) { - mInteractiveStart = oldPos; - mInteractiveEnd = newPos; + mState.mCursors[mState.mCurrentCursor].mInteractiveStart = oldPos; + mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = newPos; } else - mInteractiveStart = mInteractiveEnd = newPos; - SetSelection(mInteractiveStart, mInteractiveEnd); + mState.mCursors[mState.mCurrentCursor].mInteractiveStart = mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = newPos; + SetSelection(mState.mCursors[mState.mCurrentCursor].mInteractiveStart, mState.mCursors[mState.mCurrentCursor].mInteractiveEnd); } void TextEditor::MoveHome(bool aSelect) { - auto oldPos = mState.mCursorPosition; - SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, 0)); - - if (mState.mCursorPosition != oldPos) + for (int c = 0; c <= mState.mCurrentCursor; c++) { - if (aSelect) + auto oldPos = mState.mCursors[c].mCursorPosition; + SetCursorPosition(Coordinates(mState.mCursors[c].mCursorPosition.mLine, 0), c); + + if (mState.mCursors[c].mCursorPosition != oldPos) { - if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else if (oldPos == mInteractiveEnd) - mInteractiveEnd = mState.mCursorPosition; - else + if (aSelect) { - mInteractiveStart = mState.mCursorPosition; - mInteractiveEnd = oldPos; + if (oldPos == mState.mCursors[c].mInteractiveStart) + mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mCursorPosition; + else if (oldPos == mState.mCursors[c].mInteractiveEnd) + mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; + else + { + mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mCursorPosition; + mState.mCursors[c].mInteractiveEnd = oldPos; + } } + else + mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; + SetSelection(mState.mCursors[c].mInteractiveStart, mState.mCursors[c].mInteractiveEnd, SelectionMode::Normal, c); } - else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd); } } void TextEditor::MoveEnd(bool aSelect) { - auto oldPos = mState.mCursorPosition; - SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, GetLineMaxColumn(oldPos.mLine))); - - if (mState.mCursorPosition != oldPos) + for (int c = 0; c <= mState.mCurrentCursor; c++) { - if (aSelect) + auto oldPos = mState.mCursors[c].mCursorPosition; + SetCursorPosition(Coordinates(mState.mCursors[c].mCursorPosition.mLine, GetLineMaxColumn(oldPos.mLine)), c); + + if (mState.mCursors[c].mCursorPosition != oldPos) { - if (oldPos == mInteractiveEnd) - mInteractiveEnd = mState.mCursorPosition; - else if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else + if (aSelect) { - mInteractiveStart = oldPos; - mInteractiveEnd = mState.mCursorPosition; + if (oldPos == mState.mCursors[c].mInteractiveEnd) + mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; + else if (oldPos == mState.mCursors[c].mInteractiveStart) + mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mCursorPosition; + else + { + mState.mCursors[c].mInteractiveStart = oldPos; + mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; + } } + else + mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; + SetSelection(mState.mCursors[c].mInteractiveStart, mState.mCursors[c].mInteractiveEnd, SelectionMode::Normal, c); } - else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd); } } @@ -1878,58 +1954,68 @@ void TextEditor::Delete(bool aWordMode) if (HasSelection()) { - u.mRemoved = GetSelectedText(); - u.mRemovedStart = mState.mSelectionStart; - u.mRemovedEnd = mState.mSelectionEnd; - - DeleteSelection(); + std::vector selections = GetSelectedText(); + for (int i = selections.size() - 1; i > -1; i--) + { + u.mRemoved.push_back({ selections[i].mText, selections[i].mStart, selections[i].mEnd }); + DeleteSelection(selections[i]); + } } else { - auto pos = GetActualCursorCoordinates(); - SetCursorPosition(pos); - auto& line = mLines[pos.mLine]; - - if (pos.mColumn == GetLineMaxColumn(pos.mLine)) + std::vector positions; + for (int c = 0; c <= mState.mCurrentCursor; c++) { - if (pos.mLine == (int)mLines.size() - 1) - return; + auto pos = GetActualCursorCoordinates(c); + positions.push_back(pos); + SetCursorPosition(pos, c); + auto& line = mLines[pos.mLine]; - u.mRemoved = '\n'; - u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); - Advance(u.mRemovedEnd); - - auto& nextLine = mLines[pos.mLine + 1]; - line.insert(line.end(), nextLine.begin(), nextLine.end()); - RemoveLine(pos.mLine + 1); - } - else - { - if (aWordMode) + if (pos.mColumn == GetLineMaxColumn(pos.mLine)) { - Coordinates end = FindWordEnd(mState.mCursorPosition); - u.mRemoved = GetText(mState.mCursorPosition, end); - u.mRemovedStart = mState.mCursorPosition; - u.mRemovedEnd = end; - DeleteRange(mState.mCursorPosition, end); - int charactersDeleted = end.mColumn - mState.mCursorPosition.mColumn; + if (pos.mLine == (int)mLines.size() - 1) + return; + + auto currentCoords = GetActualCursorCoordinates(c); + u.mRemoved.push_back({ "\n", currentCoords , currentCoords }); + Advance(currentCoords); + + auto& nextLine = mLines[pos.mLine + 1]; + line.insert(line.end(), nextLine.begin(), nextLine.end()); + RemoveLine(pos.mLine + 1); + OnLineDeleted(pos.mLine); } else { - auto cindex = GetCharacterIndex(pos); - u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); - u.mRemovedEnd.mColumn++; - u.mRemoved = GetText(u.mRemovedStart, u.mRemovedEnd); - - auto d = UTF8CharLength(line[cindex].mChar); - while (d-- > 0 && cindex < (int)line.size()) - line.erase(line.begin() + cindex); + if (aWordMode) + { + Coordinates end = FindWordEnd(mState.mCursors[c].mCursorPosition); + u.mRemoved.push_back({ GetText(mState.mCursors[c].mCursorPosition, end), mState.mCursors[c].mCursorPosition , end }); + DeleteRange(mState.mCursors[c].mCursorPosition, end); + int charactersDeleted = end.mColumn - mState.mCursors[c].mCursorPosition.mColumn; + } + else + { + auto cindex = GetCharacterIndex(pos); + + Coordinates start = GetActualCursorCoordinates(c); + Coordinates end = start; + end.mColumn++; + if (GetText(start, end).compare("\n") == 0) + std::cout << "asdf\n"; + u.mRemoved.push_back({ GetText(start, end), start, end }); + + auto d = UTF8CharLength(line[cindex].mChar); + while (d-- > 0 && cindex < (int)line.size()) + line.erase(line.begin() + cindex); + } } } mTextChanged = true; - Colorize(pos.mLine, 1); + for (const auto& pos : positions) + Colorize(pos.mLine, 1); } u.mAfter = mState; @@ -1948,76 +2034,80 @@ void TextEditor::Backspace(bool aWordMode) if (HasSelection()) { - u.mRemoved = GetSelectedText(); - u.mRemovedStart = mState.mSelectionStart; - u.mRemovedEnd = mState.mSelectionEnd; - - DeleteSelection(); + std::vector selections = GetSelectedText(); + for (int i = selections.size() - 1; i > -1; i--) + { + u.mRemoved.push_back({ selections[i].mText, selections[i].mStart, selections[i].mEnd }); + DeleteSelection(selections[i]); + } } else { - auto pos = GetActualCursorCoordinates(); - SetCursorPosition(pos); - - if (mState.mCursorPosition.mColumn == 0) - { - if (mState.mCursorPosition.mLine == 0) - return; - - u.mRemoved = '\n'; - u.mRemovedStart = u.mRemovedEnd = Coordinates(pos.mLine - 1, GetLineMaxColumn(pos.mLine - 1)); - Advance(u.mRemovedEnd); - - auto& line = mLines[mState.mCursorPosition.mLine]; - auto& prevLine = mLines[mState.mCursorPosition.mLine - 1]; - auto prevSize = GetLineMaxColumn(mState.mCursorPosition.mLine - 1); - prevLine.insert(prevLine.end(), line.begin(), line.end()); - - ErrorMarkers etmp; - for (auto& i : mErrorMarkers) - etmp.insert(ErrorMarkers::value_type(i.first - 1 == mState.mCursorPosition.mLine ? i.first - 1 : i.first, i.second)); - mErrorMarkers = std::move(etmp); - - RemoveLine(mState.mCursorPosition.mLine); - --mState.mCursorPosition.mLine; - mState.mCursorPosition.mColumn = prevSize; - } - else + for (int c = 0; c <= mState.mCurrentCursor; c++) { - auto& line = mLines[mState.mCursorPosition.mLine]; + auto pos = GetActualCursorCoordinates(c); + SetCursorPosition(pos, c); - if (aWordMode) + if (mState.mCursors[c].mCursorPosition.mColumn == 0) { - Coordinates start = FindWordStart(mState.mCursorPosition - Coordinates(0, 1)); - u.mRemoved = GetText(start, mState.mCursorPosition); - u.mRemovedStart = start; - u.mRemovedEnd = mState.mCursorPosition; - DeleteRange(start, mState.mCursorPosition); - int charactersDeleted = mState.mCursorPosition.mColumn - start.mColumn; - mState.mCursorPosition.mColumn -= charactersDeleted; + if (mState.mCursors[c].mCursorPosition.mLine == 0) + return; + + Coordinates coords = Coordinates(pos.mLine - 1, GetLineMaxColumn(pos.mLine - 1)); + u.mRemoved.push_back({ "\n", coords, coords }); + Advance(coords); + + auto& line = mLines[mState.mCursors[c].mCursorPosition.mLine]; + auto& prevLine = mLines[mState.mCursors[c].mCursorPosition.mLine - 1]; + auto prevSize = GetLineMaxColumn(mState.mCursors[c].mCursorPosition.mLine - 1); + prevLine.insert(prevLine.end(), line.begin(), line.end()); + + ErrorMarkers etmp; + for (auto& i : mErrorMarkers) + etmp.insert(ErrorMarkers::value_type(i.first - 1 == mState.mCursors[c].mCursorPosition.mLine ? i.first - 1 : i.first, i.second)); + mErrorMarkers = std::move(etmp); + + RemoveLine(mState.mCursors[c].mCursorPosition.mLine); + --mState.mCursors[c].mCursorPosition.mLine; + mState.mCursors[c].mCursorPosition.mColumn = prevSize; } else { - auto cindex = GetCharacterIndex(pos) - 1; - auto cend = cindex + 1; - while (cindex > 0 && IsUTFSequence(line[cindex].mChar)) - --cindex; + auto& line = mLines[mState.mCursors[c].mCursorPosition.mLine]; + + if (aWordMode) + { + Coordinates start = FindWordStart(mState.mCursors[c].mCursorPosition - Coordinates(0, 1)); + u.mRemoved.push_back({ GetText(start, mState.mCursors[c].mCursorPosition) , start, mState.mCursors[c].mCursorPosition }); + DeleteRange(start, mState.mCursors[c].mCursorPosition); + int charactersDeleted = mState.mCursors[c].mCursorPosition.mColumn - start.mColumn; + mState.mCursors[c].mCursorPosition.mColumn -= charactersDeleted; + } + else + { + auto cindex = GetCharacterIndex(pos) - 1; + auto cend = cindex + 1; + while (cindex > 0 && IsUTFSequence(line[cindex].mChar)) + --cindex; - //if (cindex > 0 && UTF8CharLength(line[cindex].mChar) > 1) - // --cindex; + //if (cindex > 0 && UTF8CharLength(line[cindex].mChar) > 1) + // --cindex; - u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); - --u.mRemovedStart.mColumn; + Selection removed; + removed.mStart = removed.mEnd = GetActualCursorCoordinates(c); + --removed.mStart.mColumn; - if (line[cindex].mChar == '\t') - mState.mCursorPosition.mColumn -= mTabSize; - else - --mState.mCursorPosition.mColumn; + if (line[cindex].mChar == '\t') + mState.mCursors[c].mCursorPosition.mColumn -= mTabSize; + else + --mState.mCursors[c].mCursorPosition.mColumn; - while (cindex < line.size() && cend-- > cindex) - { - u.mRemoved += line[cindex].mChar; - line.erase(line.begin() + cindex); + while (cindex < line.size() && cend-- > cindex) + { + removed.mText += line[cindex].mChar; + line.erase(line.begin() + cindex); + } + u.mRemoved.push_back(removed); } } } @@ -2025,7 +2115,8 @@ void TextEditor::Backspace(bool aWordMode) mTextChanged = true; EnsureCursorVisible(); - Colorize(mState.mCursorPosition.mLine, 1); + for (int c = 0; c <= mState.mCurrentCursor; c++) + Colorize(mState.mCursors[c].mCursorPosition.mLine, 1); } u.mAfter = mState; @@ -2045,14 +2136,18 @@ void TextEditor::SelectAll() bool TextEditor::HasSelection() const { - return mState.mSelectionEnd > mState.mSelectionStart; + for (int c = 0; c <= mState.mCurrentCursor; c++) + if (mState.mCursors[c].mSelectionEnd > mState.mCursors[c].mSelectionStart) + return true; + return false; } void TextEditor::Copy() { if (HasSelection()) { - ImGui::SetClipboardText(GetSelectedText().c_str()); + mClipboardInfo = GetClipboardInfo(); + ImGui::SetClipboardText(mClipboardInfo.text.c_str()); } else { @@ -2079,12 +2174,13 @@ void TextEditor::Cut() { UndoRecord u; u.mBefore = mState; - u.mRemoved = GetSelectedText(); - u.mRemovedStart = mState.mSelectionStart; - u.mRemovedEnd = mState.mSelectionEnd; + std::vector selections = GetSelectedText(); + for (int i = 0; i < selections.size(); i++) + u.mRemoved.push_back(selections[i]); Copy(); - DeleteSelection(); + for (int i = selections.size() - 1; i > -1; i--) + DeleteSelection(selections[i]); u.mAfter = mState; AddUndo(u); @@ -2097,6 +2193,7 @@ void TextEditor::Paste() if (IsReadOnly()) return; + // should do multicursor paste if possible auto clipText = ImGui::GetClipboardText(); if (clipText != nullptr && strlen(clipText) > 0) { @@ -2105,18 +2202,19 @@ void TextEditor::Paste() if (HasSelection()) { - u.mRemoved = GetSelectedText(); - u.mRemovedStart = mState.mSelectionStart; - u.mRemovedEnd = mState.mSelectionEnd; - DeleteSelection(); + std::vector selections = GetSelectedText(); + for (int i = selections.size() - 1; i > -1; i--) + { + u.mRemoved.push_back({ selections[i].mText, selections[i].mStart, selections[i].mEnd }); + DeleteSelection(selections[i]); + } } - u.mAdded = clipText; - u.mAddedStart = GetActualCursorCoordinates(); + Coordinates start = GetActualCursorCoordinates(); InsertText(clipText); - u.mAddedEnd = GetActualCursorCoordinates(); + u.mAdded.push_back({ clipText, start, GetActualCursorCoordinates() }); u.mAfter = mState; AddUndo(u); } @@ -2288,17 +2386,46 @@ std::vector TextEditor::GetTextLines() const return result; } -std::string TextEditor::GetSelectedText() const +TextEditor::ClipboardInfo TextEditor::GetClipboardInfo() const { - return GetText(mState.mSelectionStart, mState.mSelectionEnd); + ClipboardInfo result; + for (int c = 0; c <= mState.mCurrentCursor; c++) + { + if (mState.mCursors[c].mSelectionStart < mState.mCursors[c].mSelectionEnd) + { + if (result.text.length() != 0) + { + result.text += '\n'; + result.dividers.push_back(result.text.length() - 1); + } + result.text += GetText(mState.mCursors[c].mSelectionStart, mState.mCursors[c].mSelectionEnd); + } + } + return result; +} + +std::vector TextEditor::GetSelectedText() const +{ + std::vector result; + for (int c = 0; c <= mState.mCurrentCursor; c++) + { + if (mState.mCursors[c].mSelectionStart < mState.mCursors[c].mSelectionEnd) + { + result.emplace_back(); + result.back().mText = GetText(mState.mCursors[c].mSelectionStart, mState.mCursors[c].mSelectionEnd); + result.back().mStart = mState.mCursors[c].mSelectionStart; + result.back().mEnd = mState.mCursors[c].mSelectionEnd; + } + } + return result; } std::string TextEditor::GetCurrentLineText()const { - auto lineLength = GetLineMaxColumn(mState.mCursorPosition.mLine); + auto lineLength = GetLineMaxColumn(mState.mCursors[mState.mCurrentCursor].mCursorPosition.mLine); return GetText( - Coordinates(mState.mCursorPosition.mLine, 0), - Coordinates(mState.mCursorPosition.mLine, lineLength)); + Coordinates(mState.mCursors[mState.mCurrentCursor].mCursorPosition.mLine, 0), + Coordinates(mState.mCursors[mState.mCurrentCursor].mCursorPosition.mLine, lineLength)); } void TextEditor::ProcessInputs() @@ -2592,8 +2719,11 @@ float TextEditor::TextDistanceToLineStart(const Coordinates& aFrom) const return distance; } -void TextEditor::EnsureCursorVisible() +void TextEditor::EnsureCursorVisible(int aCursor) { + if (aCursor == -1) + aCursor = mState.mCurrentCursor; + if (!mWithinRender) { mScrollToCursor = true; @@ -2612,7 +2742,7 @@ void TextEditor::EnsureCursorVisible() auto left = (int)ceil(scrollX / mCharAdvance.x); auto right = (int)ceil((scrollX + width) / mCharAdvance.x); - auto pos = GetActualCursorCoordinates(); + auto pos = GetActualCursorCoordinates(aCursor); auto len = TextDistanceToLineStart(pos); if (pos.mLine < top) @@ -2632,40 +2762,45 @@ int TextEditor::GetPageSize() const } TextEditor::UndoRecord::UndoRecord( - const std::string& aAdded, - const TextEditor::Coordinates aAddedStart, - const TextEditor::Coordinates aAddedEnd, - const std::string& aRemoved, - const TextEditor::Coordinates aRemovedStart, - const TextEditor::Coordinates aRemovedEnd, + const std::vector& aAdded, + const std::vector& aRemoved, TextEditor::EditorState& aBefore, TextEditor::EditorState& aAfter) : mAdded(aAdded) - , mAddedStart(aAddedStart) - , mAddedEnd(aAddedEnd) , mRemoved(aRemoved) - , mRemovedStart(aRemovedStart) - , mRemovedEnd(aRemovedEnd) , mBefore(aBefore) , mAfter(aAfter) { - assert(mAddedStart <= mAddedEnd); - assert(mRemovedStart <= mRemovedEnd); + for (const Selection& added : mAdded) + assert(added.mStart <= added.mEnd); + for (const Selection& removed : mRemoved) + assert(removed.mStart <= removed.mEnd); } void TextEditor::UndoRecord::Undo(TextEditor * aEditor) { - if (!mAdded.empty()) + //for (int i = mAdded.size() - 1; i > -1; i--) + for (int i = 0; i < mAdded.size(); i++) { - aEditor->DeleteRange(mAddedStart, mAddedEnd); - aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 2); + const Selection& added = mAdded[i]; + if (!added.mText.empty()) + { + aEditor->DeleteRange(added.mStart, added.mEnd); + aEditor->Colorize(added.mStart.mLine - 1, added.mEnd.mLine - added.mStart.mLine + 2); + } } - if (!mRemoved.empty()) + //for (int i = 0; i < mRemoved.size(); i++) + for (int i = mRemoved.size() - 1; i > -1; i--) { - auto start = mRemovedStart; - aEditor->InsertTextAt(start, mRemoved.c_str()); - aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 2); + //ok + const Selection& removed = mRemoved[i]; + if (!removed.mText.empty()) + { + auto start = removed.mStart; + aEditor->InsertTextAt(start, removed.mText.c_str()); + aEditor->Colorize(removed.mStart.mLine - 1, removed.mEnd.mLine - removed.mStart.mLine + 2); + } } aEditor->mState = mBefore; @@ -2675,17 +2810,27 @@ void TextEditor::UndoRecord::Undo(TextEditor * aEditor) void TextEditor::UndoRecord::Redo(TextEditor * aEditor) { - if (!mRemoved.empty()) + for (int i = 0; i < mRemoved.size(); i++) { - aEditor->DeleteRange(mRemovedStart, mRemovedEnd); - aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 1); + //ok + const Selection& removed = mRemoved[i]; + if (!removed.mText.empty()) + { + aEditor->DeleteRange(removed.mStart, removed.mEnd); + aEditor->Colorize(removed.mStart.mLine - 1, removed.mEnd.mLine - removed.mStart.mLine + 1); + } } - if (!mAdded.empty()) + //for (int i = 0; i < mAdded.size(); i++) + for (int i = mAdded.size() - 1; i > -1; i--) { - auto start = mAddedStart; - aEditor->InsertTextAt(start, mAdded.c_str()); - aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 1); + const Selection& added = mAdded[i]; + if (!added.mText.empty()) + { + auto start = added.mStart; + aEditor->InsertTextAt(start, added.mText.c_str()); + aEditor->Colorize(added.mStart.mLine - 1, added.mEnd.mLine - added.mStart.mLine + 1); + } } aEditor->mState = mAfter; diff --git a/TextEditor.h b/TextEditor.h index 625efb95..bc1a4f93 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -188,6 +188,35 @@ class IMGUI_API TextEditor static const LanguageDefinition& Lua(); }; + struct ClipboardInfo + { + std::string text = ""; + std::vector dividers; + std::string GetText(int index = 0) + { + if (dividers.size() == 0) + return text; + + if (index == 0) + return text.substr(0, dividers[0]); + if (index >= dividers.size()) + return text.substr(dividers[dividers.size() - 1] + 1); + else + { + int q = dividers[index - 1] + 1; + int p = dividers[index]; + return text.substr(q, p - q); + } + } + }; + + struct Selection + { + std::string mText; + TextEditor::Coordinates mStart; + TextEditor::Coordinates mEnd; + }; + TextEditor(); ~TextEditor(); @@ -207,7 +236,8 @@ class IMGUI_API TextEditor void SetTextLines(const std::vector& aLines); std::vector GetTextLines() const; - std::string GetSelectedText() const; + ClipboardInfo GetClipboardInfo() const; + std::vector GetSelectedText() const; std::string GetCurrentLineText()const; int GetTotalLines() const { return (int)mLines.size(); } @@ -222,7 +252,24 @@ class IMGUI_API TextEditor void SetColorizerEnable(bool aValue); Coordinates GetCursorPosition() const { return GetActualCursorCoordinates(); } - void SetCursorPosition(const Coordinates& aPosition); + void SetCursorPosition(const Coordinates& aPosition, int aCursor = -1); + + inline void OnLineDeleted(int lineNumber) + { + for (int c = 0; c <= mState.mCurrentCursor; c++) + { + if (mState.mCursors[c].mCursorPosition.mLine > lineNumber) + mState.mCursors[c].mCursorPosition.mLine--; + } + } + inline void OnLineAdded(int lineNumber) + { + for (int c = 0; c <= mState.mCurrentCursor; c++) + { + if (mState.mCursors[c].mCursorPosition.mLine > lineNumber) + mState.mCursors[c].mCursorPosition.mLine++; + } + } inline void SetHandleMouseInputs (bool aValue){ mHandleMouseInputs = aValue;} inline bool IsHandleMouseInputsEnabled() const { return mHandleMouseInputs; } @@ -263,9 +310,9 @@ class IMGUI_API TextEditor void MoveHome(bool aSelect = false); void MoveEnd(bool aSelect = false); - void SetSelectionStart(const Coordinates& aPosition); - void SetSelectionEnd(const Coordinates& aPosition); - void SetSelection(const Coordinates& aStart, const Coordinates& aEnd, SelectionMode aMode = SelectionMode::Normal); + void SetSelectionStart(const Coordinates& aPosition, int aCursor = -1); + void SetSelectionEnd(const Coordinates& aPosition, int aCursor = -1); + void SetSelection(const Coordinates& aStart, const Coordinates& aEnd, SelectionMode aMode = SelectionMode::Normal, int aCursor = -1); void SelectWordUnderCursor(); void SelectAll(); bool HasSelection() const; @@ -285,14 +332,28 @@ class IMGUI_API TextEditor static const Palette& GetLightPalette(); static const Palette& GetRetroBluePalette(); -private: +public: typedef std::vector> RegexList; + struct Cursor + { + Coordinates mCursorPosition = { 0, 0 }; + Coordinates mSelectionStart = { 0,0 }; + Coordinates mSelectionEnd = { 0,0 }; + Coordinates mInteractiveStart = { 0,0 }; + Coordinates mInteractiveEnd = { 0,0 }; + }; + + //typedef std::vector EditorState; struct EditorState { - Coordinates mSelectionStart; - Coordinates mSelectionEnd; - Coordinates mCursorPosition; + int mCurrentCursor = 0; + std::vector mCursors = { {{0,0}, {0,0}, {0,0}, {0,0}} }; + void AddCursor() + { + mCurrentCursor++; + mCursors.resize(mCurrentCursor + 1); + } }; class UndoRecord @@ -302,27 +363,23 @@ class IMGUI_API TextEditor ~UndoRecord() {} UndoRecord( - const std::string& aAdded, - const TextEditor::Coordinates aAddedStart, - const TextEditor::Coordinates aAddedEnd, - - const std::string& aRemoved, - const TextEditor::Coordinates aRemovedStart, - const TextEditor::Coordinates aRemovedEnd, - + const std::vector& aAdded, + const std::vector& aRemoved, TextEditor::EditorState& aBefore, TextEditor::EditorState& aAfter); void Undo(TextEditor* aEditor); void Redo(TextEditor* aEditor); - std::string mAdded; - Coordinates mAddedStart; - Coordinates mAddedEnd; + std::vector mAdded; + //std::string mAdded; + //Coordinates mAddedStart; + //Coordinates mAddedEnd; - std::string mRemoved; - Coordinates mRemovedStart; - Coordinates mRemovedEnd; + std::vector mRemoved; + //std::string mRemoved; + //Coordinates mRemovedStart; + //Coordinates mRemovedEnd; EditorState mBefore; EditorState mAfter; @@ -335,10 +392,10 @@ class IMGUI_API TextEditor void ColorizeRange(int aFromLine = 0, int aToLine = 0); void ColorizeInternal(); float TextDistanceToLineStart(const Coordinates& aFrom) const; - void EnsureCursorVisible(); + void EnsureCursorVisible(int aCursor = -1); int GetPageSize() const; std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const; - Coordinates GetActualCursorCoordinates() const; + Coordinates GetActualCursorCoordinates(int aCursor = -1) const; Coordinates SanitizeCoordinates(const Coordinates& aValue) const; void Advance(Coordinates& aCoordinates) const; void DeleteRange(const Coordinates& aStart, const Coordinates& aEnd); @@ -358,7 +415,7 @@ class IMGUI_API TextEditor Line& InsertLine(int aIndex); void EnterCharacter(ImWchar aChar, bool aShift); void Backspace(bool aWordMode = false); - void DeleteSelection(); + void DeleteSelection(const Selection& selection); void DeleteCurrentLine(); std::string GetWordUnderCursor() const; std::string GetWordAt(const Coordinates& aCoords) const; @@ -403,9 +460,10 @@ class IMGUI_API TextEditor Breakpoints mBreakpoints; ErrorMarkers mErrorMarkers; ImVec2 mCharAdvance; - Coordinates mInteractiveStart, mInteractiveEnd; std::string mLineBuffer; uint64_t mStartTime; float mLastClick; + + ClipboardInfo mClipboardInfo; }; From bbb978ae6dfbeecbdc9be56fe5cc8ca6746be1ae Mon Sep 17 00:00:00 2001 From: santiago Date: Sun, 25 Sep 2022 23:42:01 +0200 Subject: [PATCH 17/46] fixes --- TextEditor.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 932058ef..e987545b 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -648,6 +648,8 @@ void TextEditor::RemoveLine(int aIndex) assert(!mLines.empty()); mTextChanged = true; + + OnLineDeleted(aIndex); } TextEditor::Line& TextEditor::InsertLine(int aIndex) @@ -666,6 +668,8 @@ TextEditor::Line& TextEditor::InsertLine(int aIndex) btmp.insert(i >= aIndex ? i + 1 : i); mBreakpoints = std::move(btmp); + OnLineAdded(aIndex); + return result; } @@ -1441,7 +1445,6 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) line.erase(line.begin() + cindex, line.begin() + line.size()); SetCursorPosition(Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize)), c); added.mText = (char)aChar; - OnLineAdded(coord.mLine + 1); } else { @@ -1964,7 +1967,7 @@ void TextEditor::Delete(bool aWordMode) else { std::vector positions; - for (int c = 0; c <= mState.mCurrentCursor; c++) + for (int c = mState.mCurrentCursor; c > -1; c--) { auto pos = GetActualCursorCoordinates(c); positions.push_back(pos); @@ -1983,7 +1986,6 @@ void TextEditor::Delete(bool aWordMode) auto& nextLine = mLines[pos.mLine + 1]; line.insert(line.end(), nextLine.begin(), nextLine.end()); RemoveLine(pos.mLine + 1); - OnLineDeleted(pos.mLine); } else { @@ -2043,7 +2045,7 @@ void TextEditor::Backspace(bool aWordMode) } else { - for (int c = 0; c <= mState.mCurrentCursor; c++) + for (int c = mState.mCurrentCursor; c > -1; c--) { auto pos = GetActualCursorCoordinates(c); SetCursorPosition(pos, c); From 9228e5633e0f7ace89e71f577d0d70c31b5e6666 Mon Sep 17 00:00:00 2001 From: santiago Date: Mon, 26 Sep 2022 20:42:16 +0200 Subject: [PATCH 18/46] more fixes --- TextEditor.cpp | 97 +++++++++++++++++++++++--------------------------- TextEditor.h | 20 ++++++++--- 2 files changed, 59 insertions(+), 58 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index e987545b..6131d094 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -8,7 +8,6 @@ #define IMGUI_DEFINE_MATH_OPERATORS #include "imgui.h" // for imGui::GetCurrentWindow() -#include // TODO // - multiline comments vs single-line: latter is blocking start of a ML @@ -256,7 +255,7 @@ void TextEditor::DeleteRange(const Coordinates & aStart, const Coordinates & aEn firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end()); if (aStart.mLine < aEnd.mLine) - RemoveLine(aStart.mLine + 1, aEnd.mLine + 1); + RemoveLines(aStart.mLine + 1, aEnd.mLine + 1); } mTextChanged = true; @@ -589,7 +588,7 @@ bool TextEditor::IsOnWordBoundary(const Coordinates & aAt) const return isspace(line[cindex].mChar) != isspace(line[cindex - 1].mChar); } -void TextEditor::RemoveLine(int aStart, int aEnd) +void TextEditor::RemoveLines(int aStart, int aEnd) { assert(!mReadOnly); assert(aEnd >= aStart); @@ -618,6 +617,8 @@ void TextEditor::RemoveLine(int aStart, int aEnd) assert(!mLines.empty()); mTextChanged = true; + + OnLinesDeleted(aStart, aEnd); } void TextEditor::RemoveLine(int aIndex) @@ -1323,14 +1324,12 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) if (HasSelection()) { - auto selections = GetSelectedText(); - for (int i = selections.size() - 1; i > -1; i--) + for (int c = mState.mCurrentCursor; c > -1; c--) { - if (aChar == '\t' && selections[i].mStart.mLine != selections[i].mEnd.mLine) + if (aChar == '\t' && mState.mCursors[c].mSelectionStart.mLine != mState.mCursors[c].mSelectionEnd.mLine) { - - auto start = selections[i].mStart; - auto end = selections[i].mEnd; + auto start = mState.mCursors[c].mSelectionStart; + auto end = mState.mCursors[c].mSelectionEnd; auto originalEnd = end; if (start > end) @@ -1356,6 +1355,7 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) if (aShift) { if (!line.empty()) + { if (line.front().mChar == '\t') { @@ -1400,8 +1400,8 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) u.mAdded.push_back({ addedText , start, rangeEnd }); u.mAfter = mState; - mState.mCursors[mState.mCurrentCursor].mSelectionStart = start; - mState.mCursors[mState.mCurrentCursor].mSelectionEnd = end; + mState.mCursors[c].mSelectionStart = start; + mState.mCursors[c].mSelectionEnd = end; AddUndo(u); mTextChanged = true; @@ -1413,8 +1413,8 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) } // c == '\t' else { - u.mRemoved.push_back({ selections[i] }); - DeleteSelection(selections[i]); + u.mRemoved.push_back({ GetSelectedText(), mState.mCursors[c].mSelectionStart, mState.mCursors[mState.mCurrentCursor].mSelectionEnd }); + DeleteSelection(c); } } } // HasSelection @@ -1600,21 +1600,23 @@ void TextEditor::InsertText(const char* aValue) Colorize(start.mLine - 1, totalLines + 2); } -void TextEditor::DeleteSelection(const Selection& selection) +void TextEditor::DeleteSelection(int aCursor) { - assert(selection.mEnd >= selection.mStart); + if (aCursor == -1) + aCursor = mState.mCurrentCursor; - if (selection.mEnd == selection.mStart) + assert(mState.mCursors[aCursor].mSelectionEnd >= mState.mCursors[aCursor].mSelectionStart); + + if (mState.mCursors[aCursor].mSelectionEnd == mState.mCursors[aCursor].mSelectionStart) return; - DeleteRange(selection.mStart, selection.mEnd); + DeleteRange(mState.mCursors[aCursor].mSelectionStart, mState.mCursors[aCursor].mSelectionEnd); - SetSelection(selection.mStart, selection.mStart); - SetCursorPosition(selection.mStart); - // need to track cursors somehow - mState.mCursors[mState.mCurrentCursor].mInteractiveStart = selection.mStart; - mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = selection.mEnd; - Colorize(selection.mStart.mLine, 1); + SetSelection(mState.mCursors[aCursor].mSelectionStart, mState.mCursors[aCursor].mSelectionStart, SelectionMode::Normal, aCursor); + SetCursorPosition(mState.mCursors[aCursor].mSelectionStart, aCursor); + mState.mCursors[aCursor].mInteractiveStart = mState.mCursors[aCursor].mSelectionStart; + mState.mCursors[aCursor].mInteractiveEnd = mState.mCursors[aCursor].mSelectionEnd; + Colorize(mState.mCursors[aCursor].mSelectionStart.mLine, 1); } void TextEditor::DeleteCurrentLine() @@ -1957,11 +1959,10 @@ void TextEditor::Delete(bool aWordMode) if (HasSelection()) { - std::vector selections = GetSelectedText(); - for (int i = selections.size() - 1; i > -1; i--) + for (int c = mState.mCurrentCursor; c > -1; c--) { - u.mRemoved.push_back({ selections[i].mText, selections[i].mStart, selections[i].mEnd }); - DeleteSelection(selections[i]); + u.mRemoved.push_back({ GetSelectedText(c), mState.mCursors[c].mSelectionStart, mState.mCursors[c].mSelectionEnd }); + DeleteSelection(c); } } else @@ -2036,11 +2037,10 @@ void TextEditor::Backspace(bool aWordMode) if (HasSelection()) { - std::vector selections = GetSelectedText(); - for (int i = selections.size() - 1; i > -1; i--) + for (int c = mState.mCurrentCursor; c > -1; c--) { - u.mRemoved.push_back({ selections[i].mText, selections[i].mStart, selections[i].mEnd }); - DeleteSelection(selections[i]); + u.mRemoved.push_back({ GetSelectedText(c), mState.mCursors[c].mSelectionStart, mState.mCursors[c].mSelectionEnd }); + DeleteSelection(c); } } else @@ -2176,13 +2176,13 @@ void TextEditor::Cut() { UndoRecord u; u.mBefore = mState; - std::vector selections = GetSelectedText(); - for (int i = 0; i < selections.size(); i++) - u.mRemoved.push_back(selections[i]); Copy(); - for (int i = selections.size() - 1; i > -1; i--) - DeleteSelection(selections[i]); + for (int c = mState.mCurrentCursor; c > -1; c--) + { + u.mRemoved.push_back({ GetSelectedText(c), mState.mCursors[c].mSelectionStart, mState.mCursors[c].mSelectionEnd }); + DeleteSelection(c); + } u.mAfter = mState; AddUndo(u); @@ -2204,11 +2204,10 @@ void TextEditor::Paste() if (HasSelection()) { - std::vector selections = GetSelectedText(); - for (int i = selections.size() - 1; i > -1; i--) + for (int c = 0; c <= mState.mCurrentCursor; c++) { - u.mRemoved.push_back({ selections[i].mText, selections[i].mStart, selections[i].mEnd }); - DeleteSelection(selections[i]); + u.mRemoved.push_back({ GetSelectedText(c), mState.mCursors[c].mSelectionStart, mState.mCursors[c].mSelectionEnd }); + DeleteSelection(c); } } @@ -2406,20 +2405,12 @@ TextEditor::ClipboardInfo TextEditor::GetClipboardInfo() const return result; } -std::vector TextEditor::GetSelectedText() const +std::string TextEditor::GetSelectedText(int aCursor) const { - std::vector result; - for (int c = 0; c <= mState.mCurrentCursor; c++) - { - if (mState.mCursors[c].mSelectionStart < mState.mCursors[c].mSelectionEnd) - { - result.emplace_back(); - result.back().mText = GetText(mState.mCursors[c].mSelectionStart, mState.mCursors[c].mSelectionEnd); - result.back().mStart = mState.mCursors[c].mSelectionStart; - result.back().mEnd = mState.mCursors[c].mSelectionEnd; - } - } - return result; + if (aCursor == -1) + aCursor = mState.mCurrentCursor; + + return GetText(mState.mCursors[aCursor].mSelectionStart, mState.mCursors[aCursor].mSelectionEnd); } std::string TextEditor::GetCurrentLineText()const diff --git a/TextEditor.h b/TextEditor.h index bc1a4f93..d5ea7906 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -237,7 +238,8 @@ class IMGUI_API TextEditor std::vector GetTextLines() const; ClipboardInfo GetClipboardInfo() const; - std::vector GetSelectedText() const; + //std::vector GetSelectedText() const; + std::string GetSelectedText(int aCursor = -1) const; std::string GetCurrentLineText()const; int GetTotalLines() const { return (int)mLines.size(); } @@ -262,11 +264,19 @@ class IMGUI_API TextEditor mState.mCursors[c].mCursorPosition.mLine--; } } - inline void OnLineAdded(int lineNumber) + inline void OnLinesDeleted(int aFirstLineIndex, int aLastLineIndex) { for (int c = 0; c <= mState.mCurrentCursor; c++) { - if (mState.mCursors[c].mCursorPosition.mLine > lineNumber) + if (mState.mCursors[c].mCursorPosition.mLine > aLastLineIndex) + mState.mCursors[c].mCursorPosition.mLine -= aLastLineIndex - aFirstLineIndex; + } + } + inline void OnLineAdded(int aLineIndex) + { + for (int c = 0; c <= mState.mCurrentCursor; c++) + { + if (mState.mCursors[c].mCursorPosition.mLine > aLineIndex) mState.mCursors[c].mCursorPosition.mLine++; } } @@ -410,12 +420,12 @@ class IMGUI_API TextEditor int GetLineCharacterCount(int aLine) const; int GetLineMaxColumn(int aLine) const; bool IsOnWordBoundary(const Coordinates& aAt) const; - void RemoveLine(int aStart, int aEnd); + void RemoveLines(int aStart, int aEnd); void RemoveLine(int aIndex); Line& InsertLine(int aIndex); void EnterCharacter(ImWchar aChar, bool aShift); void Backspace(bool aWordMode = false); - void DeleteSelection(const Selection& selection); + void DeleteSelection(int aCursor = -1); void DeleteCurrentLine(); std::string GetWordUnderCursor() const; std::string GetWordAt(const Coordinates& aCoords) const; From e34afbfd756059e2397935826407c221c100f6ae Mon Sep 17 00:00:00 2001 From: santiago Date: Mon, 26 Sep 2022 23:04:12 +0200 Subject: [PATCH 19/46] fix issue with DeleteRange when redoing tab deletions --- TextEditor.cpp | 30 +++++++++++++++++++++++++++++- TextEditor.h | 1 + 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 05c15ef2..3d0d45d2 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -228,7 +228,7 @@ void TextEditor::DeleteRange(const Coordinates & aStart, const Coordinates & aEn if (aEnd == aStart) return; - auto start = GetCharacterIndex(aStart); + auto start = GetCharacterIndexLeftSide(aStart); auto end = GetCharacterIndex(aEnd); if (aStart.mLine == aEnd.mLine) @@ -503,6 +503,34 @@ TextEditor::Coordinates TextEditor::FindNextWord(const Coordinates & aFrom) cons return at; } +int TextEditor::GetCharacterIndexLeftSide(const Coordinates& aCoordinates) const +{ + if (aCoordinates.mLine >= mLines.size()) + return -1; + + auto& line = mLines[aCoordinates.mLine]; + int c = 0; + int i = 0; + int tabCoordsLeft = 0; + + for (; i < line.size() && c < aCoordinates.mColumn;) + { + if (line[i].mChar == '\t') + { + if (tabCoordsLeft == 0) + tabCoordsLeft = mTabSize - (c % mTabSize); + if (tabCoordsLeft > 0) + tabCoordsLeft--; + c++; + } + else + ++c; + if (tabCoordsLeft == 0) + i += UTF8CharLength(line[i].mChar); + } + return i; +} + int TextEditor::GetCharacterIndex(const Coordinates& aCoordinates) const { if (aCoordinates.mLine >= mLines.size()) diff --git a/TextEditor.h b/TextEditor.h index 625efb95..77365e98 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -348,6 +348,7 @@ class IMGUI_API TextEditor Coordinates FindWordStart(const Coordinates& aFrom) const; Coordinates FindWordEnd(const Coordinates& aFrom) const; Coordinates FindNextWord(const Coordinates& aFrom) const; + int GetCharacterIndexLeftSide(const Coordinates& aCoordinates) const; int GetCharacterIndex(const Coordinates& aCoordinates) const; int GetCharacterColumn(int aLine, int aIndex) const; int GetLineCharacterCount(int aLine) const; From 9773266666d47c515b2118494860f3f311ae0863 Mon Sep 17 00:00:00 2001 From: santiago Date: Mon, 26 Sep 2022 23:52:10 +0200 Subject: [PATCH 20/46] fix bug when deleting tabs and later when undoing --- TextEditor.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 3d0d45d2..cc6e776f 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -2035,12 +2035,19 @@ void TextEditor::Backspace(bool aWordMode) // --cindex; u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); - --u.mRemovedStart.mColumn; if (line[cindex].mChar == '\t') - mState.mCursorPosition.mColumn -= mTabSize; + { + int tabStartColumn = GetCharacterColumn(u.mRemovedStart.mLine, cindex); + int tabLength = u.mRemovedStart.mColumn - tabStartColumn; + mState.mCursorPosition.mColumn -= tabLength; + u.mRemovedStart.mColumn -= tabLength; + } else + { --mState.mCursorPosition.mColumn; + --u.mRemovedStart.mColumn; + } while (cindex < line.size() && cend-- > cindex) { From bfc2728d954944af27b4fc045c9a7a2fc9276d37 Mon Sep 17 00:00:00 2001 From: santiago Date: Tue, 27 Sep 2022 19:32:30 +0200 Subject: [PATCH 21/46] fix issue when ctrl backspace a tab character --- TextEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index cc6e776f..ace288fd 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -393,7 +393,7 @@ TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates & aFrom) con return at; auto& line = mLines[at.mLine]; - auto cindex = GetCharacterIndex(at); + auto cindex = GetCharacterIndexLeftSide(at); if (cindex >= (int)line.size()) return at; From 2152ebc00e77e75ff5ab8b487f937eacb8866b05 Mon Sep 17 00:00:00 2001 From: santiago Date: Tue, 27 Sep 2022 20:02:25 +0200 Subject: [PATCH 22/46] fix stupid mistake with Advance method --- TextEditor.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 6131d094..2284602c 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -1980,9 +1980,10 @@ void TextEditor::Delete(bool aWordMode) if (pos.mLine == (int)mLines.size() - 1) return; - auto currentCoords = GetActualCursorCoordinates(c); - u.mRemoved.push_back({ "\n", currentCoords , currentCoords }); - Advance(currentCoords); + Coordinates startCoords = GetActualCursorCoordinates(c); + Coordinates endCoords = startCoords; + Advance(endCoords); + u.mRemoved.push_back({ "\n", startCoords , endCoords }); auto& nextLine = mLines[pos.mLine + 1]; line.insert(line.end(), nextLine.begin(), nextLine.end()); @@ -2055,9 +2056,10 @@ void TextEditor::Backspace(bool aWordMode) if (mState.mCursors[c].mCursorPosition.mLine == 0) return; - Coordinates coords = Coordinates(pos.mLine - 1, GetLineMaxColumn(pos.mLine - 1)); - u.mRemoved.push_back({ "\n", coords, coords }); - Advance(coords); + Coordinates startCoords = Coordinates(pos.mLine - 1, GetLineMaxColumn(pos.mLine - 1)); + Coordinates endCoords = startCoords; + Advance(endCoords); + u.mRemoved.push_back({ "\n", startCoords, endCoords }); auto& line = mLines[mState.mCursors[c].mCursorPosition.mLine]; auto& prevLine = mLines[mState.mCursors[c].mCursorPosition.mLine - 1]; From f2b0a8a6fbe60b8d5a2cf6dc6a70b4cee8caa3fa Mon Sep 17 00:00:00 2001 From: santiago Date: Tue, 27 Sep 2022 20:03:09 +0200 Subject: [PATCH 23/46] fixes from dev --- TextEditor.cpp | 43 +++++++++++++++++++++++++++++++++++++++---- TextEditor.h | 1 + 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 2284602c..86eb0f2c 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -231,7 +231,7 @@ void TextEditor::DeleteRange(const Coordinates & aStart, const Coordinates & aEn if (aEnd == aStart) return; - auto start = GetCharacterIndex(aStart); + auto start = GetCharacterIndexLeftSide(aStart); auto end = GetCharacterIndex(aEnd); if (aStart.mLine == aEnd.mLine) @@ -396,7 +396,7 @@ TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates & aFrom) con return at; auto& line = mLines[at.mLine]; - auto cindex = GetCharacterIndex(at); + auto cindex = GetCharacterIndexLeftSide(at); if (cindex >= (int)line.size()) return at; @@ -506,6 +506,34 @@ TextEditor::Coordinates TextEditor::FindNextWord(const Coordinates & aFrom) cons return at; } +int TextEditor::GetCharacterIndexLeftSide(const Coordinates& aCoordinates) const +{ + if (aCoordinates.mLine >= mLines.size()) + return -1; + + auto& line = mLines[aCoordinates.mLine]; + int c = 0; + int i = 0; + int tabCoordsLeft = 0; + + for (; i < line.size() && c < aCoordinates.mColumn;) + { + if (line[i].mChar == '\t') + { + if (tabCoordsLeft == 0) + tabCoordsLeft = mTabSize - (c % mTabSize); + if (tabCoordsLeft > 0) + tabCoordsLeft--; + c++; + } + else + ++c; + if (tabCoordsLeft == 0) + i += UTF8CharLength(line[i].mChar); + } + return i; +} + int TextEditor::GetCharacterIndex(const Coordinates& aCoordinates) const { if (aCoordinates.mLine >= mLines.size()) @@ -2099,12 +2127,19 @@ void TextEditor::Backspace(bool aWordMode) Selection removed; removed.mStart = removed.mEnd = GetActualCursorCoordinates(c); - --removed.mStart.mColumn; if (line[cindex].mChar == '\t') - mState.mCursors[c].mCursorPosition.mColumn -= mTabSize; + { + int tabStartColumn = GetCharacterColumn(removed.mStart.mLine, cindex); + int tabLength = removed.mStart.mColumn - tabStartColumn; + mState.mCursors[c].mCursorPosition.mColumn -= tabLength; + removed.mStart.mColumn -= tabLength; + } else + { --mState.mCursors[c].mCursorPosition.mColumn; + --removed.mStart.mColumn; + } while (cindex < line.size() && cend-- > cindex) { diff --git a/TextEditor.h b/TextEditor.h index d5ea7906..bfd65707 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -415,6 +415,7 @@ class IMGUI_API TextEditor Coordinates FindWordStart(const Coordinates& aFrom) const; Coordinates FindWordEnd(const Coordinates& aFrom) const; Coordinates FindNextWord(const Coordinates& aFrom) const; + int GetCharacterIndexLeftSide(const Coordinates& aCoordinates) const; int GetCharacterIndex(const Coordinates& aCoordinates) const; int GetCharacterColumn(int aLine, int aIndex) const; int GetLineCharacterCount(int aLine) const; From f4cc17201c68a570ff4769f72acd9c2ffd7f75bf Mon Sep 17 00:00:00 2001 From: santiago Date: Tue, 27 Sep 2022 20:07:51 +0200 Subject: [PATCH 24/46] more fixes --- TextEditor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 86eb0f2c..7a93cab0 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -1707,7 +1707,7 @@ void TextEditor::MoveUp(int aAmount, bool aSelect) } else mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; - SetSelection(mState.mCursors[c].mInteractiveStart, mState.mCursors[c].mInteractiveEnd); + SetSelection(mState.mCursors[c].mInteractiveStart, mState.mCursors[c].mInteractiveEnd, SelectionMode::Normal, c); } } EnsureCursorVisible(); @@ -1737,7 +1737,7 @@ void TextEditor::MoveDown(int aAmount, bool aSelect) } else mState.mCursors[c].mInteractiveStart = mState.mCursors[c].mInteractiveEnd = mState.mCursors[c].mCursorPosition; - SetSelection(mState.mCursors[c].mInteractiveStart, mState.mCursors[c].mInteractiveEnd); + SetSelection(mState.mCursors[c].mInteractiveStart, mState.mCursors[c].mInteractiveEnd, SelectionMode::Normal, c); } } From 575c6b0a891c0103222c42b8134a8572156c251b Mon Sep 17 00:00:00 2001 From: santiago Date: Tue, 27 Sep 2022 20:18:10 +0200 Subject: [PATCH 25/46] fix issue with ScreenPosToCoordinates function --- TextEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index ace288fd..b688cd1f 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -357,7 +357,7 @@ TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPositi float oldX = columnX; columnX = (1.0f + std::floor((1.0f + columnX) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); columnWidth = columnX - oldX; - delta = columnCoord - (columnCoord / mTabSize) * mTabSize + mTabSize; + delta = mTabSize - (columnCoord % mTabSize); } else { From 3e1d891913fc25bbed963aeaaed99a9f9fc2a2de Mon Sep 17 00:00:00 2001 From: santiago Date: Tue, 27 Sep 2022 22:46:10 +0200 Subject: [PATCH 26/46] fix from dev --- TextEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 7a93cab0..2be144a9 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -360,7 +360,7 @@ TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPositi float oldX = columnX; columnX = (1.0f + std::floor((1.0f + columnX) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); columnWidth = columnX - oldX; - delta = columnCoord - (columnCoord / mTabSize) * mTabSize + mTabSize; + delta = mTabSize - (columnCoord % mTabSize); } else { From 23dca3195de34fc60420f72c45364edc7470ad3e Mon Sep 17 00:00:00 2001 From: santiago Date: Wed, 28 Sep 2022 20:02:07 +0200 Subject: [PATCH 27/46] fix for shift click --- TextEditor.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index b688cd1f..3f99db41 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -910,11 +910,14 @@ void TextEditor::HandleMouseInputs() { if (click) { - Coordinates newSelectionEnd = ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite); - mState.mCursorPosition = newSelectionEnd; - SetSelectionEnd(newSelectionEnd); - mInteractiveEnd = mState.mSelectionEnd; + Coordinates newSelection = ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite); + if (newSelection > mState.mCursorPosition) + SetSelectionEnd(newSelection); + else + SetSelectionStart(newSelection); mInteractiveStart = mState.mSelectionStart; + mInteractiveEnd = mState.mSelectionEnd; + mState.mCursorPosition = newSelection; } } } From 46f00331d81226b4bda7005d029cefef2b9b1d7c Mon Sep 17 00:00:00 2001 From: santiago Date: Thu, 29 Sep 2022 18:58:24 +0200 Subject: [PATCH 28/46] fix issue with autoindentation and redo --- TextEditor.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 3f99db41..a2132363 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -1425,16 +1425,20 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) auto& line = mLines[coord.mLine]; auto& newLine = mLines[coord.mLine + 1]; + u.mAdded = ""; + u.mAdded += (char)aChar; if (mLanguageDefinition.mAutoIndentation) for (size_t it = 0; it < line.size() && isascii(line[it].mChar) && isblank(line[it].mChar); ++it) + { newLine.push_back(line[it]); + u.mAdded += line[it].mChar; + } const size_t whitespaceSize = newLine.size(); auto cindex = GetCharacterIndex(coord); newLine.insert(newLine.end(), line.begin() + cindex, line.end()); line.erase(line.begin() + cindex, line.begin() + line.size()); SetCursorPosition(Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize))); - u.mAdded = (char)aChar; } else { From 0530a35289fff90fd5320232d3db947cd47790e1 Mon Sep 17 00:00:00 2001 From: santiago Date: Thu, 29 Sep 2022 19:39:07 +0200 Subject: [PATCH 29/46] fix undo redo issues --- TextEditor.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 2be144a9..16ce3ebb 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -2809,8 +2809,7 @@ TextEditor::UndoRecord::UndoRecord( void TextEditor::UndoRecord::Undo(TextEditor * aEditor) { - //for (int i = mAdded.size() - 1; i > -1; i--) - for (int i = 0; i < mAdded.size(); i++) + for (int i = mAdded.size() - 1; i > -1; i--) { const Selection& added = mAdded[i]; if (!added.mText.empty()) @@ -2820,10 +2819,8 @@ void TextEditor::UndoRecord::Undo(TextEditor * aEditor) } } - //for (int i = 0; i < mRemoved.size(); i++) for (int i = mRemoved.size() - 1; i > -1; i--) { - //ok const Selection& removed = mRemoved[i]; if (!removed.mText.empty()) { @@ -2842,7 +2839,6 @@ void TextEditor::UndoRecord::Redo(TextEditor * aEditor) { for (int i = 0; i < mRemoved.size(); i++) { - //ok const Selection& removed = mRemoved[i]; if (!removed.mText.empty()) { @@ -2851,8 +2847,7 @@ void TextEditor::UndoRecord::Redo(TextEditor * aEditor) } } - //for (int i = 0; i < mAdded.size(); i++) - for (int i = mAdded.size() - 1; i > -1; i--) + for (int i = 0; i < mAdded.size(); i++) { const Selection& added = mAdded[i]; if (!added.mText.empty()) From 038d45d0e97d9d8ae911afe28357b054409a7580 Mon Sep 17 00:00:00 2001 From: santiago Date: Thu, 29 Sep 2022 20:50:32 +0200 Subject: [PATCH 30/46] fix current line deletion --- TextEditor.cpp | 84 +++++++++++++++++++++++++++----------------------- TextEditor.h | 2 +- 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 16ce3ebb..0a5c4a15 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -681,6 +681,50 @@ void TextEditor::RemoveLine(int aIndex) OnLineDeleted(aIndex); } +void TextEditor::RemoveCurrentLines() +{ + UndoRecord u; + u.mBefore = mState; + + for (int c = mState.mCurrentCursor; c > -1; c--) + { + int currentLine = mState.mCursors[c].mCursorPosition.mLine; + int nextLine = currentLine + 1; + int prevLine = currentLine - 1; + + Coordinates toDeleteStart, toDeleteEnd; + if (mLines.size() > nextLine) // next line exists + { + toDeleteStart = Coordinates(currentLine, 0); + toDeleteEnd = Coordinates(nextLine, 0); + mState.mCursors[c].mCursorPosition.mColumn = 0; + } + else if (prevLine > -1) // previous line exists + { + toDeleteStart = Coordinates(prevLine, GetLineMaxColumn(prevLine)); + toDeleteEnd = Coordinates(currentLine, GetLineMaxColumn(currentLine)); + mState.mCursors[c].mCursorPosition.mLine--; + mState.mCursors[c].mCursorPosition.mColumn = 0; + } + else + { + toDeleteStart = Coordinates(currentLine, 0); + toDeleteEnd = Coordinates(currentLine, GetLineMaxColumn(currentLine)); + mState.mCursors[c].mCursorPosition.mColumn = 0; + } + + u.mRemoved.push_back({ GetText(toDeleteStart, toDeleteEnd), toDeleteStart, toDeleteEnd }); + + if (toDeleteStart.mLine != toDeleteEnd.mLine) + RemoveLine(currentLine); + else + DeleteRange(toDeleteStart, toDeleteEnd); + } + + u.mAfter = mState; + AddUndo(u); +} + TextEditor::Line& TextEditor::InsertLine(int aIndex) { assert(!mReadOnly); @@ -803,7 +847,7 @@ void TextEditor::HandleKeyboardInputs() else if (!IsReadOnly() && !alt && !shift && !super && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) Backspace(ctrl); else if (!IsReadOnly() && !alt && ctrl && shift && !super && ImGui::IsKeyPressed('K')) - DeleteCurrentLine(); + RemoveCurrentLines(); else if (!alt && !ctrl && !shift && !super && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) mOverwrite ^= true; else if (isCtrlOnly && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) @@ -1647,44 +1691,6 @@ void TextEditor::DeleteSelection(int aCursor) Colorize(mState.mCursors[aCursor].mSelectionStart.mLine, 1); } -void TextEditor::DeleteCurrentLine() -{ - UndoRecord u; - u.mBefore = mState; - - int currentLine = mState.mCursors[mState.mCurrentCursor].mCursorPosition.mLine; - int nextLine = currentLine + 1; - int prevLine = currentLine - 1; - - Coordinates toDeleteStart, toDeleteEnd; - if (mLines.size() > nextLine) // next line exists - { - toDeleteStart = Coordinates(currentLine, 0); - toDeleteEnd = Coordinates(nextLine, 0); - mState.mCursors[mState.mCurrentCursor].mCursorPosition.mColumn = 0; - } - else if (prevLine > -1) // previous line exists - { - toDeleteStart = Coordinates(prevLine, GetLineMaxColumn(prevLine)); - toDeleteEnd = Coordinates(currentLine, GetLineMaxColumn(currentLine)); - mState.mCursors[mState.mCurrentCursor].mCursorPosition.mLine--; - mState.mCursors[mState.mCurrentCursor].mCursorPosition.mColumn = 0; - } - else - { - toDeleteStart = Coordinates(currentLine, 0); - toDeleteEnd = Coordinates(currentLine, GetLineMaxColumn(currentLine)); - mState.mCursors[mState.mCurrentCursor].mCursorPosition.mColumn = 0; - } - - u.mRemoved.push_back({ GetText(toDeleteStart, toDeleteEnd), toDeleteStart, toDeleteEnd }); - - DeleteRange(toDeleteStart, toDeleteEnd); - - u.mAfter = mState; - AddUndo(u); -} - void TextEditor::MoveUp(int aAmount, bool aSelect) { for (int c = 0; c <= mState.mCurrentCursor; c++) diff --git a/TextEditor.h b/TextEditor.h index bfd65707..9715daab 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -423,11 +423,11 @@ class IMGUI_API TextEditor bool IsOnWordBoundary(const Coordinates& aAt) const; void RemoveLines(int aStart, int aEnd); void RemoveLine(int aIndex); + void RemoveCurrentLines(); Line& InsertLine(int aIndex); void EnterCharacter(ImWchar aChar, bool aShift); void Backspace(bool aWordMode = false); void DeleteSelection(int aCursor = -1); - void DeleteCurrentLine(); std::string GetWordUnderCursor() const; std::string GetWordAt(const Coordinates& aCoords) const; ImU32 GetGlyphColor(const Glyph& aGlyph) const; From 9b5a0dc7d70ea9ce98c83e2a659672f9d2b5b35a Mon Sep 17 00:00:00 2001 From: santiago Date: Fri, 30 Sep 2022 20:13:53 +0200 Subject: [PATCH 31/46] fixes for multiple cursors in the same line --- TextEditor.cpp | 70 ++++++++++++++++++++++++++++++++++---------------- TextEditor.h | 30 ++++++++++++++++++++++ 2 files changed, 78 insertions(+), 22 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 0a5c4a15..142bbfdd 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -236,23 +236,21 @@ void TextEditor::DeleteRange(const Coordinates & aStart, const Coordinates & aEn if (aStart.mLine == aEnd.mLine) { - auto& line = mLines[aStart.mLine]; auto n = GetLineMaxColumn(aStart.mLine); if (aEnd.mColumn >= n) - line.erase(line.begin() + start, line.end()); + RemoveGlyphsFromLine(aStart.mLine, start); // from start to end of line else - line.erase(line.begin() + start, line.begin() + end); + RemoveGlyphsFromLine(aStart.mLine, start, end); } else { + RemoveGlyphsFromLine(aStart.mLine, start); // from start to end of line + RemoveGlyphsFromLine(aEnd.mLine, 0, end); auto& firstLine = mLines[aStart.mLine]; auto& lastLine = mLines[aEnd.mLine]; - firstLine.erase(firstLine.begin() + start, firstLine.end()); - lastLine.erase(lastLine.begin(), lastLine.begin() + end); - if (aStart.mLine < aEnd.mLine) - firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end()); + AddGlyphsToLine(aStart.mLine, firstLine.size(), lastLine.begin(), lastLine.end()); if (aStart.mLine < aEnd.mLine) RemoveLines(aStart.mLine + 1, aEnd.mLine + 1); @@ -282,8 +280,8 @@ int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char * aValu { auto& newLine = InsertLine(aWhere.mLine + 1); auto& line = mLines[aWhere.mLine]; - newLine.insert(newLine.begin(), line.begin() + cindex, line.end()); - line.erase(line.begin() + cindex, line.end()); + AddGlyphsToLine(aWhere.mLine + 1, 0, line.begin() + cindex, line.end()); + RemoveGlyphsFromLine(aWhere.mLine, cindex); } else { @@ -300,7 +298,7 @@ int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char * aValu auto& line = mLines[aWhere.mLine]; auto d = UTF8CharLength(*aValue); while (d-- > 0 && *aValue != '\0') - line.insert(line.begin() + cindex++, Glyph(*aValue++, PaletteIndex::Default)); + AddGlyphToLine(aWhere.mLine, cindex++, Glyph(*aValue++, PaletteIndex::Default)); aWhere.mColumn = GetCharacterColumn(aWhere.mLine, cindex); } @@ -725,6 +723,34 @@ void TextEditor::RemoveCurrentLines() AddUndo(u); } +void TextEditor::RemoveGlyphsFromLine(int aLine, int aStartChar, int aEndChar) +{ + int column = GetCharacterColumn(aLine, aStartChar); + int deltaX = GetCharacterColumn(aLine, aEndChar) - column; + auto& line = mLines[aLine]; + line.erase(line.begin() + aStartChar, aEndChar == -1 ? line.end() : line.begin() + aEndChar); + OnCharactersDeletedFromLine(aLine, column, deltaX); +} + +void TextEditor::AddGlyphsToLine(int aLine, int aTargetIndex, Line::iterator aSourceStart, Line::iterator aSourceEnd) +{ + int targetColumn = GetCharacterColumn(aLine, aTargetIndex); + int charsInserted = std::distance(aSourceStart, aSourceEnd); + auto& line = mLines[aLine]; + line.insert(line.begin() + aTargetIndex, aSourceStart, aSourceEnd); + int deltaX = GetCharacterColumn(aLine, aTargetIndex + charsInserted) - targetColumn; + OnCharactersInsertedToLine(aLine, targetColumn, deltaX); +} + +void TextEditor::AddGlyphToLine(int aLine, int aTargetIndex, Glyph aGlyph) +{ + int targetColumn = GetCharacterColumn(aLine, aTargetIndex); + auto& line = mLines[aLine]; + line.insert(line.begin() + aTargetIndex, aGlyph); + int deltaX = GetCharacterColumn(aLine, aTargetIndex + 1) - targetColumn; + OnCharactersInsertedToLine(aLine, targetColumn, deltaX); +} + TextEditor::Line& TextEditor::InsertLine(int aIndex) { assert(!mReadOnly); @@ -1431,14 +1457,14 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) { if (line.front().mChar == '\t') { - line.erase(line.begin()); + RemoveGlyphsFromLine(i, 0, 1); modified = true; } else { for (int j = 0; j < mTabSize && !line.empty() && line.front().mChar == ' '; j++) { - line.erase(line.begin()); + RemoveGlyphsFromLine(i, 0, 1); modified = true; } } @@ -1446,7 +1472,7 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) } else { - line.insert(line.begin(), Glyph('\t', TextEditor::PaletteIndex::Background)); + AddGlyphToLine(i, 0, Glyph('\t', TextEditor::PaletteIndex::Background)); modified = true; } } @@ -1492,7 +1518,7 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) } // HasSelection std::vector coords; - for (int c = mState.mCurrentCursor; c > -1; c--) + for (int c = 0; c <= mState.mCurrentCursor; c++) // order important here for typing tabs in the same line at the same time { auto coord = GetActualCursorCoordinates(c); coords.push_back(coord); @@ -1513,8 +1539,8 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) const size_t whitespaceSize = newLine.size(); auto cindex = GetCharacterIndex(coord); - newLine.insert(newLine.end(), line.begin() + cindex, line.end()); - line.erase(line.begin() + cindex, line.begin() + line.size()); + AddGlyphsToLine(coord.mLine + 1, newLine.size(), line.begin() + cindex, line.end()); + RemoveGlyphsFromLine(coord.mLine, cindex); SetCursorPosition(Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize)), c); added.mText = (char)aChar; } @@ -1538,12 +1564,12 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) while (d-- > 0 && cindex < (int)line.size()) { removed.mText += line[cindex].mChar; - line.erase(line.begin() + cindex); + RemoveGlyphsFromLine(coord.mLine, cindex, cindex + 1); } } for (auto p = buf; *p != '\0'; p++, ++cindex) - line.insert(line.begin() + cindex, Glyph(*p, PaletteIndex::Default)); + AddGlyphToLine(coord.mLine, cindex, Glyph(*p, PaletteIndex::Default)); added.mText = buf; SetCursorPosition(Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex)), c); @@ -2020,7 +2046,7 @@ void TextEditor::Delete(bool aWordMode) u.mRemoved.push_back({ "\n", startCoords , endCoords }); auto& nextLine = mLines[pos.mLine + 1]; - line.insert(line.end(), nextLine.begin(), nextLine.end()); + AddGlyphsToLine(pos.mLine, line.size(), nextLine.begin(), nextLine.end()); RemoveLine(pos.mLine + 1); } else @@ -2045,7 +2071,7 @@ void TextEditor::Delete(bool aWordMode) auto d = UTF8CharLength(line[cindex].mChar); while (d-- > 0 && cindex < (int)line.size()) - line.erase(line.begin() + cindex); + RemoveGlyphsFromLine(pos.mLine, cindex, cindex + 1); } } } @@ -2098,7 +2124,7 @@ void TextEditor::Backspace(bool aWordMode) auto& line = mLines[mState.mCursors[c].mCursorPosition.mLine]; auto& prevLine = mLines[mState.mCursors[c].mCursorPosition.mLine - 1]; auto prevSize = GetLineMaxColumn(mState.mCursors[c].mCursorPosition.mLine - 1); - prevLine.insert(prevLine.end(), line.begin(), line.end()); + AddGlyphsToLine(mState.mCursors[c].mCursorPosition.mLine - 1, prevLine.size(), line.begin(), line.end()); ErrorMarkers etmp; for (auto& i : mErrorMarkers) @@ -2150,7 +2176,7 @@ void TextEditor::Backspace(bool aWordMode) while (cindex < line.size() && cend-- > cindex) { removed.mText += line[cindex].mChar; - line.erase(line.begin() + cindex); + RemoveGlyphsFromLine(mState.mCursors[c].mCursorPosition.mLine, cindex, cindex + 1); } u.mRemoved.push_back(removed); } diff --git a/TextEditor.h b/TextEditor.h index 9715daab..d33155ae 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -281,6 +281,33 @@ class IMGUI_API TextEditor } } + inline void OnCharactersDeletedFromLine(int aLine, int aColumn, int aDeltaX) + { + //std::cout << "CHARACTERS DELETED: " << aLine << ", " << aColumn << ", " << aDeltaX << std::endl; + + for (int c = 0; c <= mState.mCurrentCursor; c++) + { + if (mState.mCursors[c].mCursorPosition.mLine == aLine) + { + if (mState.mCursors[c].mCursorPosition.mColumn > aColumn) + mState.mCursors[c].mCursorPosition.mColumn -= aDeltaX; + } + } + } + inline void OnCharactersInsertedToLine(int aLine, int aColumn, int aDeltaX) + { + //std::cout << "CHARACTERS INSERTED: " << aLine << ", " << aColumn << ", " << aDeltaX << std::endl; + + for (int c = 0; c <= mState.mCurrentCursor; c++) + { + if (mState.mCursors[c].mCursorPosition.mLine == aLine) + { + if (mState.mCursors[c].mCursorPosition.mColumn > aColumn) + mState.mCursors[c].mCursorPosition.mColumn += aDeltaX; + } + } + } + inline void SetHandleMouseInputs (bool aValue){ mHandleMouseInputs = aValue;} inline bool IsHandleMouseInputsEnabled() const { return mHandleMouseInputs; } @@ -424,6 +451,9 @@ class IMGUI_API TextEditor void RemoveLines(int aStart, int aEnd); void RemoveLine(int aIndex); void RemoveCurrentLines(); + void RemoveGlyphsFromLine(int aLine, int aStartChar, int aEndChar = -1); + void AddGlyphsToLine(int aLine, int aTargetIndex, Line::iterator aSourceStart, Line::iterator aSourceEnd); + void AddGlyphToLine(int aLine, int aTargetIndex, Glyph aGlyph); Line& InsertLine(int aIndex); void EnterCharacter(ImWchar aChar, bool aShift); void Backspace(bool aWordMode = false); From 6166f1c828220d4fb1b806f6601ab5534efd0eeb Mon Sep 17 00:00:00 2001 From: santiago Date: Fri, 30 Sep 2022 20:50:37 +0200 Subject: [PATCH 32/46] autoindentation fix from dev --- TextEditor.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 142bbfdd..b6f651b9 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -1533,16 +1533,20 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) auto& line = mLines[coord.mLine]; auto& newLine = mLines[coord.mLine + 1]; + added.mText = ""; + added.mText += (char)aChar; if (mLanguageDefinition.mAutoIndentation) for (size_t it = 0; it < line.size() && isascii(line[it].mChar) && isblank(line[it].mChar); ++it) + { newLine.push_back(line[it]); + added.mText += line[it].mChar; + } const size_t whitespaceSize = newLine.size(); auto cindex = GetCharacterIndex(coord); AddGlyphsToLine(coord.mLine + 1, newLine.size(), line.begin() + cindex, line.end()); RemoveGlyphsFromLine(coord.mLine, cindex); SetCursorPosition(Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize)), c); - added.mText = (char)aChar; } else { From c7241ee8aa36790fb00a662337479456751ad930 Mon Sep 17 00:00:00 2001 From: santiago Date: Fri, 30 Sep 2022 20:59:15 +0200 Subject: [PATCH 33/46] shift click fix from dev --- TextEditor.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index b6f651b9..26329312 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -1005,11 +1005,16 @@ void TextEditor::HandleMouseInputs() { if (click) { - Coordinates newSelectionEnd = ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite); - mState.mCursors[mState.mCurrentCursor].mCursorPosition = newSelectionEnd; - SetSelectionEnd(newSelectionEnd); + Coordinates oldCursorPosition = mState.mCursors[mState.mCurrentCursor].mCursorPosition; + Coordinates newSelection = ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite); + if (newSelection > mState.mCursors[mState.mCurrentCursor].mCursorPosition) + SetSelectionEnd(newSelection); + else + SetSelectionStart(newSelection); mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = mState.mCursors[mState.mCurrentCursor].mSelectionEnd; mState.mCursors[mState.mCurrentCursor].mInteractiveStart = mState.mCursors[mState.mCurrentCursor].mSelectionStart; + mState.mCursors[mState.mCurrentCursor].mCursorPosition = newSelection; + mState.mCursors[mState.mCurrentCursor].mCursorPositionChanged = oldCursorPosition != newSelection; } } } From 68e7c6aeddb7736799cabbe1d1a93a654ad538b9 Mon Sep 17 00:00:00 2001 From: santiago Date: Fri, 30 Sep 2022 23:00:09 +0200 Subject: [PATCH 34/46] fix ctrl double and triple click --- TextEditor.cpp | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 26329312..f54e6797 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -929,12 +929,14 @@ void TextEditor::HandleMouseInputs() if (tripleClick) { - if (!ctrl) - { - mState.mCursors[mState.mCurrentCursor].mCursorPosition = mState.mCursors[mState.mCurrentCursor].mInteractiveStart = mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); - mSelectionMode = SelectionMode::Line; - SetSelection(mState.mCursors[mState.mCurrentCursor].mInteractiveStart, mState.mCursors[mState.mCurrentCursor].mInteractiveEnd, mSelectionMode); - } + if (ctrl) + mState.AddCursor(); + else + mState.mCurrentCursor = 0; + + mState.mCursors[mState.mCurrentCursor].mCursorPosition = mState.mCursors[mState.mCurrentCursor].mInteractiveStart = mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); + mSelectionMode = SelectionMode::Line; + SetSelection(mState.mCursors[mState.mCurrentCursor].mInteractiveStart, mState.mCursors[mState.mCurrentCursor].mInteractiveEnd, mSelectionMode); mLastClick = -1.0f; } @@ -945,17 +947,19 @@ void TextEditor::HandleMouseInputs() else if (doubleClick) { - if (!ctrl) - { - mState.mCursors[mState.mCurrentCursor].mCursorPosition = mState.mCursors[mState.mCurrentCursor].mInteractiveStart = mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); - mState.mCursors[mState.mCurrentCursor].mInteractiveStart = FindWordStart(mState.mCursors[mState.mCurrentCursor].mCursorPosition); - mState.mCursors[mState.mCurrentCursor].mCursorPosition = mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = FindWordEnd(mState.mCursors[mState.mCurrentCursor].mCursorPosition); - if (mSelectionMode == SelectionMode::Line) - mSelectionMode = SelectionMode::Normal; - else - mSelectionMode = SelectionMode::Word; - SetSelection(mState.mCursors[mState.mCurrentCursor].mInteractiveStart, mState.mCursors[mState.mCurrentCursor].mInteractiveEnd, mSelectionMode); - } + if (ctrl) + mState.AddCursor(); + else + mState.mCurrentCursor = 0; + + mState.mCursors[mState.mCurrentCursor].mCursorPosition = mState.mCursors[mState.mCurrentCursor].mInteractiveStart = mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); + mState.mCursors[mState.mCurrentCursor].mInteractiveStart = FindWordStart(mState.mCursors[mState.mCurrentCursor].mCursorPosition); + mState.mCursors[mState.mCurrentCursor].mCursorPosition = mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = FindWordEnd(mState.mCursors[mState.mCurrentCursor].mCursorPosition); + if (mSelectionMode == SelectionMode::Line) + mSelectionMode = SelectionMode::Normal; + else + mSelectionMode = SelectionMode::Word; + SetSelection(mState.mCursors[mState.mCurrentCursor].mInteractiveStart, mState.mCursors[mState.mCurrentCursor].mInteractiveEnd, mSelectionMode); mLastClick = (float)ImGui::GetTime(); } From 0e976e3f95ce18247e8f44f2adbcd840a6d92cc5 Mon Sep 17 00:00:00 2001 From: santiago Date: Fri, 30 Sep 2022 23:38:22 +0200 Subject: [PATCH 35/46] fix issue when deleting line with multiple cursors --- TextEditor.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/TextEditor.cpp b/TextEditor.cpp index f54e6797..7ff1c29d 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -2138,6 +2138,13 @@ void TextEditor::Backspace(bool aWordMode) auto& prevLine = mLines[mState.mCursors[c].mCursorPosition.mLine - 1]; auto prevSize = GetLineMaxColumn(mState.mCursors[c].mCursorPosition.mLine - 1); AddGlyphsToLine(mState.mCursors[c].mCursorPosition.mLine - 1, prevLine.size(), line.begin(), line.end()); + for (int otherCursor = c + 1; + otherCursor <= mState.mCurrentCursor && mState.mCursors[otherCursor].mCursorPosition.mLine == mState.mCursors[c].mCursorPosition.mLine; + otherCursor++) // move up cursors in same line + { + auto targetCoords = Coordinates(mState.mCursors[c].mCursorPosition.mLine - 1, prevSize + mState.mCursors[otherCursor].mCursorPosition.mColumn); + SetCursorPosition(targetCoords, otherCursor); + } ErrorMarkers etmp; for (auto& i : mErrorMarkers) From 4b4307cf5a8aa89c242fdbad6b92699d0b0b4d6d Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 1 Oct 2022 15:51:59 +0200 Subject: [PATCH 36/46] better handling of multiple cursors in the same line --- TextEditor.cpp | 36 +++++++++++++++++++++++++++++++----- TextEditor.h | 28 +--------------------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 7ff1c29d..f660a6d9 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -723,13 +723,39 @@ void TextEditor::RemoveCurrentLines() AddUndo(u); } +void TextEditor::OnLineChanged(bool aBeforeChange, int aLine, int aColumn, int aCharCount, bool aDeleted) +{ + static std::unordered_map cursorCharIndices; + if (aBeforeChange) + { + cursorCharIndices.clear(); + for (int c = 0; c <= mState.mCurrentCursor; c++) + { + if (mState.mCursors[c].mCursorPosition.mLine == aLine) + { + if (mState.mCursors[c].mCursorPosition.mColumn > aColumn) + { + cursorCharIndices[c] = GetCharacterIndex({ aLine, mState.mCursors[c].mCursorPosition.mColumn }); + cursorCharIndices[c] += aDeleted ? -aCharCount : aCharCount; + } + } + } + } + else + { + for (auto& item : cursorCharIndices) + SetCursorPosition({ aLine, GetCharacterColumn(aLine, item.second) }, item.first); + } +} + void TextEditor::RemoveGlyphsFromLine(int aLine, int aStartChar, int aEndChar) { int column = GetCharacterColumn(aLine, aStartChar); int deltaX = GetCharacterColumn(aLine, aEndChar) - column; auto& line = mLines[aLine]; + OnLineChanged(true, aLine, column, aEndChar - aStartChar, true); line.erase(line.begin() + aStartChar, aEndChar == -1 ? line.end() : line.begin() + aEndChar); - OnCharactersDeletedFromLine(aLine, column, deltaX); + OnLineChanged(false, aLine, column, aEndChar - aStartChar, true); } void TextEditor::AddGlyphsToLine(int aLine, int aTargetIndex, Line::iterator aSourceStart, Line::iterator aSourceEnd) @@ -737,18 +763,18 @@ void TextEditor::AddGlyphsToLine(int aLine, int aTargetIndex, Line::iterator aSo int targetColumn = GetCharacterColumn(aLine, aTargetIndex); int charsInserted = std::distance(aSourceStart, aSourceEnd); auto& line = mLines[aLine]; + OnLineChanged(true, aLine, targetColumn, charsInserted, false); line.insert(line.begin() + aTargetIndex, aSourceStart, aSourceEnd); - int deltaX = GetCharacterColumn(aLine, aTargetIndex + charsInserted) - targetColumn; - OnCharactersInsertedToLine(aLine, targetColumn, deltaX); + OnLineChanged(false, aLine, targetColumn, charsInserted, false); } void TextEditor::AddGlyphToLine(int aLine, int aTargetIndex, Glyph aGlyph) { int targetColumn = GetCharacterColumn(aLine, aTargetIndex); auto& line = mLines[aLine]; + OnLineChanged(true, aLine, targetColumn, 1, false); line.insert(line.begin() + aTargetIndex, aGlyph); - int deltaX = GetCharacterColumn(aLine, aTargetIndex + 1) - targetColumn; - OnCharactersInsertedToLine(aLine, targetColumn, deltaX); + OnLineChanged(false, aLine, targetColumn, 1, false); } TextEditor::Line& TextEditor::InsertLine(int aIndex) diff --git a/TextEditor.h b/TextEditor.h index d33155ae..6b1a5183 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -281,33 +281,6 @@ class IMGUI_API TextEditor } } - inline void OnCharactersDeletedFromLine(int aLine, int aColumn, int aDeltaX) - { - //std::cout << "CHARACTERS DELETED: " << aLine << ", " << aColumn << ", " << aDeltaX << std::endl; - - for (int c = 0; c <= mState.mCurrentCursor; c++) - { - if (mState.mCursors[c].mCursorPosition.mLine == aLine) - { - if (mState.mCursors[c].mCursorPosition.mColumn > aColumn) - mState.mCursors[c].mCursorPosition.mColumn -= aDeltaX; - } - } - } - inline void OnCharactersInsertedToLine(int aLine, int aColumn, int aDeltaX) - { - //std::cout << "CHARACTERS INSERTED: " << aLine << ", " << aColumn << ", " << aDeltaX << std::endl; - - for (int c = 0; c <= mState.mCurrentCursor; c++) - { - if (mState.mCursors[c].mCursorPosition.mLine == aLine) - { - if (mState.mCursors[c].mCursorPosition.mColumn > aColumn) - mState.mCursors[c].mCursorPosition.mColumn += aDeltaX; - } - } - } - inline void SetHandleMouseInputs (bool aValue){ mHandleMouseInputs = aValue;} inline bool IsHandleMouseInputsEnabled() const { return mHandleMouseInputs; } @@ -451,6 +424,7 @@ class IMGUI_API TextEditor void RemoveLines(int aStart, int aEnd); void RemoveLine(int aIndex); void RemoveCurrentLines(); + void OnLineChanged(bool aBeforeChange, int aLine, int aColumn, int aCharCount, bool aDeleted); void RemoveGlyphsFromLine(int aLine, int aStartChar, int aEndChar = -1); void AddGlyphsToLine(int aLine, int aTargetIndex, Line::iterator aSourceStart, Line::iterator aSourceEnd); void AddGlyphToLine(int aLine, int aTargetIndex, Glyph aGlyph); From 568f23208fbc192a22d4c8feefc384b2c30fb9cf Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 1 Oct 2022 16:55:13 +0200 Subject: [PATCH 37/46] fix more tab related issues on line deletion --- TextEditor.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index f660a6d9..f612060c 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -2067,7 +2067,7 @@ void TextEditor::Delete(bool aWordMode) else { std::vector positions; - for (int c = mState.mCurrentCursor; c > -1; c--) + for (int c = 0; c <= mState.mCurrentCursor; c++) { auto pos = GetActualCursorCoordinates(c); positions.push_back(pos); @@ -2086,6 +2086,15 @@ void TextEditor::Delete(bool aWordMode) auto& nextLine = mLines[pos.mLine + 1]; AddGlyphsToLine(pos.mLine, line.size(), nextLine.begin(), nextLine.end()); + for (int otherCursor = c + 1; + otherCursor <= mState.mCurrentCursor && mState.mCursors[otherCursor].mCursorPosition.mLine == pos.mLine + 1; + otherCursor++) // move up cursors in next line + { + int otherCursorCharIndex = GetCharacterIndex(mState.mCursors[otherCursor].mCursorPosition); + int otherCursorNewCharIndex = GetCharacterIndex(pos) + otherCursorCharIndex; + auto targetCoords = Coordinates(pos.mLine, GetCharacterColumn(pos.mLine, otherCursorNewCharIndex)); + SetCursorPosition(targetCoords, otherCursor); + } RemoveLine(pos.mLine + 1); } else @@ -2145,7 +2154,7 @@ void TextEditor::Backspace(bool aWordMode) } else { - for (int c = mState.mCurrentCursor; c > -1; c--) + for (int c = 0; c <= mState.mCurrentCursor; c++) { auto pos = GetActualCursorCoordinates(c); SetCursorPosition(pos, c); @@ -2161,14 +2170,17 @@ void TextEditor::Backspace(bool aWordMode) u.mRemoved.push_back({ "\n", startCoords, endCoords }); auto& line = mLines[mState.mCursors[c].mCursorPosition.mLine]; - auto& prevLine = mLines[mState.mCursors[c].mCursorPosition.mLine - 1]; - auto prevSize = GetLineMaxColumn(mState.mCursors[c].mCursorPosition.mLine - 1); - AddGlyphsToLine(mState.mCursors[c].mCursorPosition.mLine - 1, prevLine.size(), line.begin(), line.end()); + int prevLineIndex = mState.mCursors[c].mCursorPosition.mLine - 1; + auto& prevLine = mLines[prevLineIndex]; + auto prevSize = GetLineMaxColumn(prevLineIndex); + AddGlyphsToLine(prevLineIndex, prevLine.size(), line.begin(), line.end()); for (int otherCursor = c + 1; otherCursor <= mState.mCurrentCursor && mState.mCursors[otherCursor].mCursorPosition.mLine == mState.mCursors[c].mCursorPosition.mLine; otherCursor++) // move up cursors in same line { - auto targetCoords = Coordinates(mState.mCursors[c].mCursorPosition.mLine - 1, prevSize + mState.mCursors[otherCursor].mCursorPosition.mColumn); + int otherCursorCharIndex = GetCharacterIndex(mState.mCursors[otherCursor].mCursorPosition); + int otherCursorNewCharIndex = GetCharacterIndex({ prevLineIndex, prevSize }) + otherCursorCharIndex; + auto targetCoords = Coordinates(prevLineIndex, GetCharacterColumn(prevLineIndex, otherCursorNewCharIndex)); SetCursorPosition(targetCoords, otherCursor); } From 4198bd44bd612a581a16c2079eb1e450ba91d58a Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 1 Oct 2022 18:48:22 +0200 Subject: [PATCH 38/46] fix OnLinesDeleted function --- TextEditor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TextEditor.h b/TextEditor.h index 6b1a5183..40f784fb 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -268,7 +268,7 @@ class IMGUI_API TextEditor { for (int c = 0; c <= mState.mCurrentCursor; c++) { - if (mState.mCursors[c].mCursorPosition.mLine > aLastLineIndex) + if (mState.mCursors[c].mCursorPosition.mLine >= aLastLineIndex) mState.mCursors[c].mCursorPosition.mLine -= aLastLineIndex - aFirstLineIndex; } } From b8f3a0a2ffb3c5ab8b8bb796687bfbd9d9db475c Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 1 Oct 2022 19:44:27 +0200 Subject: [PATCH 39/46] fix OnLinesDeleted function --- TextEditor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TextEditor.h b/TextEditor.h index 40f784fb..bd76972d 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -268,7 +268,7 @@ class IMGUI_API TextEditor { for (int c = 0; c <= mState.mCurrentCursor; c++) { - if (mState.mCursors[c].mCursorPosition.mLine >= aLastLineIndex) + if (mState.mCursors[c].mCursorPosition.mLine >= aFirstLineIndex) mState.mCursors[c].mCursorPosition.mLine -= aLastLineIndex - aFirstLineIndex; } } From 1e6bf517919086241c377a598f07407a4134a632 Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 1 Oct 2022 20:11:18 +0200 Subject: [PATCH 40/46] handle selection when deleting current lines --- TextEditor.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/TextEditor.cpp b/TextEditor.cpp index f612060c..0afd4e60 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -684,6 +684,15 @@ void TextEditor::RemoveCurrentLines() UndoRecord u; u.mBefore = mState; + if (HasSelection()) + { + for (int c = mState.mCurrentCursor; c > -1; c--) + { + u.mRemoved.push_back({ GetSelectedText(c), mState.mCursors[c].mSelectionStart, mState.mCursors[c].mSelectionEnd }); + DeleteSelection(c); + } + } + for (int c = mState.mCurrentCursor; c > -1; c--) { int currentLine = mState.mCursors[c].mCursorPosition.mLine; From 57ac90af1a060756b78891b779bff625e8121ca0 Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 1 Oct 2022 20:12:26 +0200 Subject: [PATCH 41/46] submitted this by accident --- TextEditor.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 0afd4e60..b4ef5589 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -1053,7 +1053,6 @@ void TextEditor::HandleMouseInputs() mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = mState.mCursors[mState.mCurrentCursor].mSelectionEnd; mState.mCursors[mState.mCurrentCursor].mInteractiveStart = mState.mCursors[mState.mCurrentCursor].mSelectionStart; mState.mCursors[mState.mCurrentCursor].mCursorPosition = newSelection; - mState.mCursors[mState.mCurrentCursor].mCursorPositionChanged = oldCursorPosition != newSelection; } } } From cd4c4b142610f34795f183a37ae840b822e9a9c3 Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 1 Oct 2022 20:39:46 +0200 Subject: [PATCH 42/46] remove old stuff --- TextEditor.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/TextEditor.h b/TextEditor.h index bd76972d..c3aec3f7 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -382,14 +382,7 @@ class IMGUI_API TextEditor void Redo(TextEditor* aEditor); std::vector mAdded; - //std::string mAdded; - //Coordinates mAddedStart; - //Coordinates mAddedEnd; - std::vector mRemoved; - //std::string mRemoved; - //Coordinates mRemovedStart; - //Coordinates mRemovedEnd; EditorState mBefore; EditorState mAfter; From e99de70e1e059f9f1f4c4f3ded3f10a88b4a9ac5 Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 1 Oct 2022 20:44:19 +0200 Subject: [PATCH 43/46] more accurate focus detection --- TextEditor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index b4ef5589..c4b743ab 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -852,7 +852,7 @@ ImU32 TextEditor::GetGlyphColor(const Glyph & aGlyph) const void TextEditor::HandleKeyboardInputs() { - if (ImGui::IsWindowFocused()) + if (ImGui::IsWindowFocused() || ImGui::IsRootWindowFocused()) { if (ImGui::IsWindowHovered()) ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput); @@ -1182,7 +1182,7 @@ void TextEditor::Render() } if (cursorCoordsInThisLine.size() > 0) { - auto focused = ImGui::IsWindowFocused(); + auto focused = ImGui::IsWindowFocused() || ImGui::IsRootWindowFocused(); // Highlight the current line (where the cursor is) if (!HasSelection()) From 9c0b711ef218559dfaeb264c5b52a591af26cae8 Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 1 Oct 2022 20:57:50 +0200 Subject: [PATCH 44/46] cursor merging --- TextEditor.cpp | 85 +++++++++++++++++++++++++++++++++++++++++++++----- TextEditor.h | 10 ++++-- 2 files changed, 84 insertions(+), 11 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index c4b743ab..702f7e9c 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -37,7 +37,6 @@ TextEditor::TextEditor() , mColorizerEnabled(true) , mTextStart(20.0f) , mLeftMargin(10) - , mCursorPositionChanged(false) , mColorRangeMin(0) , mColorRangeMax(0) , mSelectionMode(SelectionMode::Normal) @@ -1017,27 +1016,28 @@ void TextEditor::HandleMouseInputs() mSelectionMode = SelectionMode::Word; else mSelectionMode = SelectionMode::Normal; - SetSelection(mState.mCursors[mState.mCurrentCursor].mInteractiveStart, mState.mCursors[mState.mCurrentCursor].mInteractiveEnd, mSelectionMode); + SetSelection(mState.mCursors[mState.mCurrentCursor].mInteractiveStart, mState.mCursors[mState.mCurrentCursor].mInteractiveEnd, mSelectionMode, -1, ctrl); mLastClick = (float)ImGui::GetTime(); } // Mouse left button dragging (=> update selection) else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) { + mDraggingSelection = true; io.WantCaptureMouse = true; mState.mCursors[mState.mCurrentCursor].mCursorPosition = mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos(), !mOverwrite); SetSelection(mState.mCursors[mState.mCurrentCursor].mInteractiveStart, mState.mCursors[mState.mCurrentCursor].mInteractiveEnd, mSelectionMode); } else if (ImGui::IsMouseReleased(0)) { - std::cout << "RELEASEINGN\n"; + mDraggingSelection = false; // sort from cursors from top to bottom std::sort(mState.mCursors.begin(), mState.mCursors.begin() + (mState.mCurrentCursor + 1), [](const Cursor& a, const Cursor& b) -> bool { return a.mSelectionStart < b.mSelectionStart; }); - // merge cursors if they overlap + MergeCursorsIfPossible(); } } else if (shift) @@ -1053,6 +1053,7 @@ void TextEditor::HandleMouseInputs() mState.mCursors[mState.mCurrentCursor].mInteractiveEnd = mState.mCursors[mState.mCurrentCursor].mSelectionEnd; mState.mCursors[mState.mCurrentCursor].mInteractiveStart = mState.mCursors[mState.mCurrentCursor].mSelectionStart; mState.mCursors[mState.mCurrentCursor].mCursorPosition = newSelection; + mState.mCursors[mState.mCurrentCursor].mCursorPositionChanged = oldCursorPosition != newSelection; } } } @@ -1362,9 +1363,15 @@ void TextEditor::Render() void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) { + for (int c = 0; c <= mState.mCurrentCursor; c++) + { + if (mState.mCursors[c].mCursorPositionChanged) + OnCursorPositionChanged(c); + mState.mCursors[c].mCursorPositionChanged = false; + } + mWithinRender = true; mTextChanged = false; - mCursorPositionChanged = false; UpdatePalette(); @@ -1645,6 +1652,19 @@ void TextEditor::SetReadOnly(bool aValue) mReadOnly = aValue; } +void TextEditor::OnCursorPositionChanged(int aCursor) +{ + if (mDraggingSelection) + return; + + // sort from cursors from top to bottom + std::sort(mState.mCursors.begin(), mState.mCursors.begin() + (mState.mCurrentCursor + 1), [](const Cursor& a, const Cursor& b) -> bool + { + return a.mSelectionStart < b.mSelectionStart; + }); + MergeCursorsIfPossible(); +} + void TextEditor::SetColorizerEnable(bool aValue) { mColorizerEnabled = aValue; @@ -1658,7 +1678,7 @@ void TextEditor::SetCursorPosition(const Coordinates& aPosition, int aCursor) if (mState.mCursors[aCursor].mCursorPosition != aPosition) { mState.mCursors[aCursor].mCursorPosition = aPosition; - mCursorPositionChanged = true; + mState.mCursors[aCursor].mCursorPositionChanged = true; EnsureCursorVisible(); } } @@ -1683,7 +1703,7 @@ void TextEditor::SetSelectionEnd(const Coordinates& aPosition, int aCursor) std::swap(mState.mCursors[aCursor].mSelectionStart, mState.mCursors[aCursor].mSelectionEnd); } -void TextEditor::SetSelection(const Coordinates& aStart, const Coordinates& aEnd, SelectionMode aMode, int aCursor) +void TextEditor::SetSelection(const Coordinates& aStart, const Coordinates& aEnd, SelectionMode aMode, int aCursor, bool isSpawningNewCursor) { if (aCursor == -1) aCursor = mState.mCurrentCursor; @@ -1716,7 +1736,8 @@ void TextEditor::SetSelection(const Coordinates& aStart, const Coordinates& aEnd if (mState.mCursors[aCursor].mSelectionStart != oldSelStart || mState.mCursors[aCursor].mSelectionEnd != oldSelEnd) - mCursorPositionChanged = true; + if (!isSpawningNewCursor) + mState.mCursors[aCursor].mCursorPositionChanged = true; } void TextEditor::SetTabSize(int aValue) @@ -2200,6 +2221,7 @@ void TextEditor::Backspace(bool aWordMode) RemoveLine(mState.mCursors[c].mCursorPosition.mLine); --mState.mCursors[c].mCursorPosition.mLine; mState.mCursors[c].mCursorPosition.mColumn = prevSize; + mState.mCursors[c].mCursorPositionChanged = true; } else { @@ -2246,6 +2268,7 @@ void TextEditor::Backspace(bool aWordMode) } u.mRemoved.push_back(removed); } + mState.mCursors[c].mCursorPositionChanged = true; } } @@ -2493,6 +2516,52 @@ const TextEditor::Palette & TextEditor::GetRetroBluePalette() return p; } +void TextEditor::MergeCursorsIfPossible() +{ + // requires the cursors to be sorted from top to bottom + std::unordered_set cursorsToDelete; + if (HasSelection()) + { + // merge cursors if they overlap + for (int c = mState.mCurrentCursor; c > 0; c--)// iterate backwards through pairs + { + int pc = c - 1; + + bool pcContainsC = mState.mCursors[pc].mSelectionEnd >= mState.mCursors[c].mSelectionEnd; + bool pcContainsStartOfC = mState.mCursors[pc].mSelectionEnd >= mState.mCursors[c].mSelectionStart; + + if (pcContainsC) + { + cursorsToDelete.insert(c); + } + else if (pcContainsStartOfC) + { + mState.mCursors[pc].mSelectionEnd = mState.mCursors[c].mSelectionEnd; + mState.mCursors[pc].mInteractiveEnd = mState.mCursors[c].mSelectionEnd; + mState.mCursors[pc].mInteractiveStart = mState.mCursors[pc].mSelectionStart; + mState.mCursors[pc].mCursorPosition = mState.mCursors[c].mSelectionEnd; + cursorsToDelete.insert(c); + } + } + } + else + { + // merge cursors if they are at the same position + for (int c = mState.mCurrentCursor; c > 0; c--)// iterate backwards through pairs + { + int pc = c - 1; + if (mState.mCursors[pc].mCursorPosition == mState.mCursors[c].mCursorPosition) + cursorsToDelete.insert(c); + } + } + for (int c = mState.mCurrentCursor; c > -1; c--)// iterate backwards through each of them + { + if (cursorsToDelete.find(c) != cursorsToDelete.end()) + mState.mCursors.erase(mState.mCursors.begin() + c); + } + mState.mCurrentCursor -= cursorsToDelete.size(); +} + std::string TextEditor::GetText() const { diff --git a/TextEditor.h b/TextEditor.h index c3aec3f7..b07b4d9b 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -248,7 +248,8 @@ class IMGUI_API TextEditor void SetReadOnly(bool aValue); bool IsReadOnly() const { return mReadOnly; } bool IsTextChanged() const { return mTextChanged; } - bool IsCursorPositionChanged() const { return mCursorPositionChanged; } + + void OnCursorPositionChanged(int aCursor); bool IsColorizerEnabled() const { return mColorizerEnabled; } void SetColorizerEnable(bool aValue); @@ -322,7 +323,7 @@ class IMGUI_API TextEditor void SetSelectionStart(const Coordinates& aPosition, int aCursor = -1); void SetSelectionEnd(const Coordinates& aPosition, int aCursor = -1); - void SetSelection(const Coordinates& aStart, const Coordinates& aEnd, SelectionMode aMode = SelectionMode::Normal, int aCursor = -1); + void SetSelection(const Coordinates& aStart, const Coordinates& aEnd, SelectionMode aMode = SelectionMode::Normal, int aCursor = -1, bool isSpawningNewCursor = false); void SelectWordUnderCursor(); void SelectAll(); bool HasSelection() const; @@ -352,6 +353,7 @@ class IMGUI_API TextEditor Coordinates mSelectionEnd = { 0,0 }; Coordinates mInteractiveStart = { 0,0 }; Coordinates mInteractiveEnd = { 0,0 }; + bool mCursorPositionChanged = false; }; //typedef std::vector EditorState; @@ -366,6 +368,8 @@ class IMGUI_API TextEditor } }; + void MergeCursorsIfPossible(); + class UndoRecord { public: @@ -450,7 +454,6 @@ class IMGUI_API TextEditor bool mColorizerEnabled; float mTextStart; // position (in pixels) where a code line starts relative to the left of the TextEditor. int mLeftMargin; - bool mCursorPositionChanged; int mColorRangeMin, mColorRangeMax; SelectionMode mSelectionMode; bool mHandleKeyboardInputs; @@ -458,6 +461,7 @@ class IMGUI_API TextEditor bool mIgnoreImGuiChild; bool mShowWhitespaces; bool mShowShortTabGlyphs; + bool mDraggingSelection = false; Palette mPaletteBase; Palette mPalette; From 5aacceb6879a2405a75200d6f0f5a94932551602 Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 1 Oct 2022 21:00:10 +0200 Subject: [PATCH 45/46] fix copy and pasting --- TextEditor.cpp | 77 ++++++++++++++++++++++++++++++++++---------------- TextEditor.h | 31 ++------------------ 2 files changed, 55 insertions(+), 53 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 702f7e9c..8e6809a1 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -1745,24 +1745,26 @@ void TextEditor::SetTabSize(int aValue) mTabSize = std::max(0, std::min(32, aValue)); } -void TextEditor::InsertText(const std::string& aValue) +void TextEditor::InsertText(const std::string& aValue, int aCursor) { - InsertText(aValue.c_str()); + InsertText(aValue.c_str(), aCursor); } -void TextEditor::InsertText(const char* aValue) +void TextEditor::InsertText(const char* aValue, int aCursor) { if (aValue == nullptr) return; + if (aCursor == -1) + aCursor = mState.mCurrentCursor; - auto pos = GetActualCursorCoordinates(); - auto start = std::min(pos, mState.mCursors[mState.mCurrentCursor].mSelectionStart); + auto pos = GetActualCursorCoordinates(aCursor); + auto start = std::min(pos, mState.mCursors[aCursor].mSelectionStart); int totalLines = pos.mLine - start.mLine; totalLines += InsertTextAt(pos, aValue); - SetSelection(pos, pos); - SetCursorPosition(pos); + SetSelection(pos, pos, SelectionMode::Normal, aCursor); + SetCursorPosition(pos, aCursor); Colorize(start.mLine - 1, totalLines + 2); } @@ -2306,8 +2308,8 @@ void TextEditor::Copy() { if (HasSelection()) { - mClipboardInfo = GetClipboardInfo(); - ImGui::SetClipboardText(mClipboardInfo.text.c_str()); + std::string clipboardText = GetClipboardText(); + ImGui::SetClipboardText(clipboardText.c_str()); } else { @@ -2353,27 +2355,55 @@ void TextEditor::Paste() if (IsReadOnly()) return; - // should do multicursor paste if possible - auto clipText = ImGui::GetClipboardText(); - if (clipText != nullptr && strlen(clipText) > 0) + // check if we should do multicursor paste + std::string clipText = ImGui::GetClipboardText(); + bool canPasteToMultipleCursors = false; + std::vector> clipTextLines; + if (mState.mCurrentCursor > 0) + { + clipTextLines.push_back({ 0,0 }); + for (int i = 0; i < clipText.length(); i++) + { + if (clipText[i] == '\n') + { + clipTextLines.back().second = i; + clipTextLines.push_back({ i + 1, 0 }); + } + } + clipTextLines.back().second = clipText.length(); + canPasteToMultipleCursors = clipTextLines.size() == mState.mCurrentCursor + 1; + } + + if (clipText.length() > 0) { UndoRecord u; u.mBefore = mState; if (HasSelection()) { - for (int c = 0; c <= mState.mCurrentCursor; c++) + for (int c = mState.mCurrentCursor; c > -1; c--) { u.mRemoved.push_back({ GetSelectedText(c), mState.mCursors[c].mSelectionStart, mState.mCursors[c].mSelectionEnd }); DeleteSelection(c); } } - Coordinates start = GetActualCursorCoordinates(); - - InsertText(clipText); + for (int c = mState.mCurrentCursor; c > -1; c--) + { + Coordinates start = GetActualCursorCoordinates(c); + if (canPasteToMultipleCursors) + { + std::string clipSubText = clipText.substr(clipTextLines[c].first, clipTextLines[c].second - clipTextLines[c].first); + InsertText(clipSubText, c); + u.mAdded.push_back({ clipSubText, start, GetActualCursorCoordinates(c) }); + } + else + { + InsertText(clipText, c); + u.mAdded.push_back({ clipText, start, GetActualCursorCoordinates(c) }); + } + } - u.mAdded.push_back({ clipText, start, GetActualCursorCoordinates() }); u.mAfter = mState; AddUndo(u); } @@ -2591,19 +2621,16 @@ std::vector TextEditor::GetTextLines() const return result; } -TextEditor::ClipboardInfo TextEditor::GetClipboardInfo() const +std::string TextEditor::GetClipboardText() const { - ClipboardInfo result; + std::string result; for (int c = 0; c <= mState.mCurrentCursor; c++) { if (mState.mCursors[c].mSelectionStart < mState.mCursors[c].mSelectionEnd) { - if (result.text.length() != 0) - { - result.text += '\n'; - result.dividers.push_back(result.text.length() - 1); - } - result.text += GetText(mState.mCursors[c].mSelectionStart, mState.mCursors[c].mSelectionEnd); + if (result.length() != 0) + result += '\n'; + result += GetText(mState.mCursors[c].mSelectionStart, mState.mCursors[c].mSelectionEnd); } } return result; diff --git a/TextEditor.h b/TextEditor.h index b07b4d9b..90d7a8cf 100644 --- a/TextEditor.h +++ b/TextEditor.h @@ -189,28 +189,6 @@ class IMGUI_API TextEditor static const LanguageDefinition& Lua(); }; - struct ClipboardInfo - { - std::string text = ""; - std::vector dividers; - std::string GetText(int index = 0) - { - if (dividers.size() == 0) - return text; - - if (index == 0) - return text.substr(0, dividers[0]); - if (index >= dividers.size()) - return text.substr(dividers[dividers.size() - 1] + 1); - else - { - int q = dividers[index - 1] + 1; - int p = dividers[index]; - return text.substr(q, p - q); - } - } - }; - struct Selection { std::string mText; @@ -237,8 +215,7 @@ class IMGUI_API TextEditor void SetTextLines(const std::vector& aLines); std::vector GetTextLines() const; - ClipboardInfo GetClipboardInfo() const; - //std::vector GetSelectedText() const; + std::string GetClipboardText() const; std::string GetSelectedText(int aCursor = -1) const; std::string GetCurrentLineText()const; @@ -309,8 +286,8 @@ class IMGUI_API TextEditor void SetTabSize(int aValue); inline int GetTabSize() const { return mTabSize; } - void InsertText(const std::string& aValue); - void InsertText(const char* aValue); + void InsertText(const std::string& aValue, int aCursor = -1); + void InsertText(const char* aValue, int aCursor = -1); void MoveUp(int aAmount = 1, bool aSelect = false); void MoveDown(int aAmount = 1, bool aSelect = false); @@ -476,6 +453,4 @@ class IMGUI_API TextEditor uint64_t mStartTime; float mLastClick; - - ClipboardInfo mClipboardInfo; }; From a4407f7c82259aeafd386ba687c4541cb7f88b0c Mon Sep 17 00:00:00 2001 From: santiago Date: Sat, 1 Oct 2022 23:15:08 +0200 Subject: [PATCH 46/46] remove line highlight --- TextEditor.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/TextEditor.cpp b/TextEditor.cpp index 8e6809a1..b973efa5 100644 --- a/TextEditor.cpp +++ b/TextEditor.cpp @@ -1185,14 +1185,6 @@ void TextEditor::Render() { auto focused = ImGui::IsWindowFocused() || ImGui::IsRootWindowFocused(); - // Highlight the current line (where the cursor is) - if (!HasSelection()) - { - auto end = ImVec2(start.x + contentSize.x + scrollX, start.y + mCharAdvance.y); - drawList->AddRectFilled(start, end, mPalette[(int)(focused ? PaletteIndex::CurrentLineFill : PaletteIndex::CurrentLineFillInactive)]); - drawList->AddRect(start, end, mPalette[(int)PaletteIndex::CurrentLineEdge], 1.0f); - } - // Render the cursors if (focused) {