From b446d7fd4aa944b8032c92379f9e5ae614d5e1af Mon Sep 17 00:00:00 2001 From: paxcut <53811119+paxcut@users.noreply.github.com> Date: Tue, 17 Dec 2024 14:44:40 -0700 Subject: [PATCH] impr: Make scrolling in pattern editor feel less janky (#2009) I have implemented a fix that makes the scrollbars behave like they do in VScode. Vertically you can scroll past the end of the file until only the last line can be seen at the top. The horizontal bar behaves as if every line was the same length which is the length of the longest line in the file. Not only this creates a better user experience, but it also fixes the annoying flicker that occurs when scrolling through large files. Also, I have switched all the old draw calls to render text to regular TextUnformatted calls which adds extra stability to the resulting display. To implement the behavior I added a dummy widget with the desired dimensions. --- .../ColorTextEditor/include/TextEditor.h | 2 ++ .../ColorTextEditor/source/TextEditor.cpp | 23 +++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h index 184a940e1..4675c0bbc 100644 --- a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h +++ b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h @@ -559,6 +559,7 @@ private: int GetCharacterColumn(int aLine, int aIndex) const; int GetLineCharacterCount(int aLine) const; int Utf8CharsToBytes(const Coordinates &aCoordinates) const; + int GetLongestLineLength() const; unsigned long long GetLineByteCount(int aLine) const; int GetStringCharacterCount(std::string str) const; int GetLineMaxColumn(int aLine) const; @@ -595,6 +596,7 @@ private: bool mScrollToTop; bool mTextChanged; bool mColorizerEnabled; + float mLongest; float mTextStart; // position (in pixels) where a code line starts relative to the left of the TextEditor. int mLeftMargin; bool mCursorPositionChanged; diff --git a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp index 144094394..c15dcae66 100644 --- a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp +++ b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp @@ -846,6 +846,20 @@ void TextEditor::HandleMouseInputs() { } } +int TextEditor::GetLongestLineLength() const { + int result = 0; + for (int i = 0; i < (int)mLines.size(); i++) + result = std::max(result, GetLineCharacterCount(i)); + return result; +} + +inline void TextUnformattedColoredAt(const ImVec2 &pos, const ImU32 &color, const char *text) { + ImGui::SetCursorScreenPos(pos); + ImGui::PushStyleColor(ImGuiCol_Text,color); + ImGui::TextUnformatted(text); + ImGui::PopStyleColor(); +} + void TextEditor::Render() { /* Compute mCharAdvance regarding scaled font size (Ctrl + mouse wheel)*/ const float fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x; @@ -881,6 +895,7 @@ void TextEditor::Render() { auto lineNo = (int)(std::floor(scrollY / mCharAdvance.y)); auto globalLineMax = (int)mLines.size(); auto lineMax = std::max(0, std::min((int)mLines.size() - 1, lineNo + (int)std::ceil((scrollY + contentSize.y) / mCharAdvance.y))); + mLongest = GetLongestLineLength() * mCharAdvance.x; // Deduce mTextStart by evaluating mLines size (global lineMax) plus two spaces as text width char buf[16]; @@ -930,7 +945,7 @@ void TextEditor::Render() { snprintf(buf, 16, "%d ", lineNo + 1); 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); + TextUnformattedColoredAt(ImVec2(lineStartScreenPos.x + mTextStart - lineNoWidth, lineStartScreenPos.y),mPalette[(int) PaletteIndex::LineNumber],buf); } // Draw breakpoints @@ -1040,7 +1055,7 @@ void TextEditor::Render() { if ((color != prevColor || glyph.mChar == '\t' || glyph.mChar == ' ') && !mLineBuffer.empty()) { const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y); - drawList->AddText(newOffset, prevColor, mLineBuffer.c_str()); + TextUnformattedColoredAt(newOffset, prevColor, mLineBuffer.c_str()); auto textSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, mLineBuffer.c_str(), nullptr, nullptr); bufferOffset.x += textSize.x; mLineBuffer.clear(); @@ -1112,7 +1127,7 @@ void TextEditor::Render() { if (!mLineBuffer.empty()) { const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y); - drawList->AddText(newOffset, prevColor, mLineBuffer.c_str()); + TextUnformattedColoredAt(newOffset, prevColor, mLineBuffer.c_str()); mLineBuffer.clear(); } @@ -1123,7 +1138,7 @@ void TextEditor::Render() { } - ImGui::Dummy(ImVec2((longest + 2), mLines.size() * mCharAdvance.y)); + ImGui::Dummy(ImVec2(mLongest, (globalLineMax - lineMax - 2) * mCharAdvance.y + ImGui::GetCurrentWindow()->InnerClipRect.GetHeight())); if (mScrollToCursor) { EnsureCursorVisible();