impr: Better top margin handling for text editor. (#2021)
The top margin changes when popups block some portion on the screen. Previously it was implemented adding blank lines at the bottom which created problems if the windows was scrolled all the way to the bottom and the popups were issued. The newer implementation doesn't use spaces and simply resets the top of the window to be higher making the popup interaction with the window more natural (no text disappears on the lines the popup overlaps the text). changes can be identified form the variables mTopMargin,mOldTopMargin and mNewTopMargin. Unfortunately those changes revealed problems in cursor navigation that needed to be addressed. Namely, the window will scroll up and down on the line before last/second line at the top and similar problems for the left and right. Those changes correspond to the function EnsureCUrsorVisible() Fixing those reveled yet more problems with scrolling past the end of the file using the keyboard which required adding some more variables and functions to support the correct handling, You find those changes in the function MoveDown. I also renamed some variables that had the wrong casing and fixed the pasting but that is missing one line.
This commit is contained in:
parent
810cd40ccd
commit
2cb4462274
@ -306,7 +306,7 @@ public:
|
||||
auto text = GetText();
|
||||
return text.empty() || text == "\n";
|
||||
}
|
||||
|
||||
void SetTopLine();
|
||||
void SetTextLines(const std::vector<std::string>& aLines);
|
||||
std::vector<std::string> GetTextLines() const;
|
||||
|
||||
@ -586,6 +586,7 @@ private:
|
||||
bool mScrollToBottom;
|
||||
float mTopMargin;
|
||||
float mNewTopMargin;
|
||||
float mOldTopMargin;
|
||||
bool mTopMarginChanged=false;
|
||||
|
||||
int mTabSize;
|
||||
@ -599,6 +600,8 @@ private:
|
||||
float mLongest;
|
||||
float mTextStart; // position (in pixels) where a code line starts relative to the left of the TextEditor.
|
||||
int mLeftMargin;
|
||||
int mTopLine;
|
||||
bool mSetTopLine;
|
||||
bool mCursorPositionChanged;
|
||||
bool mBreakPointsChanged;
|
||||
int mColorRangeMin, mColorRangeMax;
|
||||
@ -624,10 +627,10 @@ private:
|
||||
uint64_t mStartTime;
|
||||
std::vector<std::string> mDefines;
|
||||
TextEditor *mSourceCodeEditor=nullptr;
|
||||
float m_linesAdded = 0;
|
||||
float m_savedScrollY = 0;
|
||||
float m_pixelsAdded = 0;
|
||||
float m_shiftedScrollY = 0;
|
||||
float mSavedScrollY = 0;
|
||||
float mShiftedScrollY = 0;
|
||||
float mScrollY = 0;
|
||||
int mNumberOfLinesDisplayed;
|
||||
float mLastClick;
|
||||
bool mShowCursor;
|
||||
bool mShowLineNumbers;
|
||||
|
@ -874,16 +874,16 @@ void TextEditor::Render() {
|
||||
|
||||
IM_ASSERT(mLineBuffer.empty());
|
||||
|
||||
auto contentSize = ImGui::GetCurrentWindowRead()->ContentRegionRect.Max - ImGui::GetWindowPos() - ImVec2(0,mTopMargin);
|
||||
auto contentSize = ImGui::GetCurrentWindowRead()->ContentRegionRect.Max - ImGui::GetWindowPos();
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
float longest(mTextStart);
|
||||
mNumberOfLinesDisplayed = GetPageSize();
|
||||
|
||||
if (mScrollToTop) {
|
||||
mScrollToTop = false;
|
||||
ImGui::SetScrollY(0.f);
|
||||
}
|
||||
|
||||
if ( mScrollToBottom && ImGui::GetScrollMaxY() > ImGui::GetScrollY()) {
|
||||
if ( mScrollToBottom && ImGui::GetScrollMaxY() >= ImGui::GetScrollY()) {
|
||||
mScrollToBottom = false;
|
||||
ImGui::SetScrollY(ImGui::GetScrollMaxY());
|
||||
}
|
||||
@ -891,10 +891,13 @@ void TextEditor::Render() {
|
||||
ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos() + ImVec2(0, mTopMargin);
|
||||
auto scrollX = ImGui::GetScrollX();
|
||||
auto scrollY = ImGui::GetScrollY();
|
||||
|
||||
auto lineNo = (int)(std::floor(scrollY / mCharAdvance.y));
|
||||
if (mSetTopLine)
|
||||
SetTopLine();
|
||||
else
|
||||
mTopLine = std::max(0,(int)(std::floor((scrollY-mTopMargin) / mCharAdvance.y)));
|
||||
auto lineNo = mTopLine;
|
||||
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)));
|
||||
auto lineMax = std::clamp(lineNo + mNumberOfLinesDisplayed, 0, globalLineMax - 1);
|
||||
mLongest = GetLongestLineLength() * mCharAdvance.x;
|
||||
|
||||
// Deduce mTextStart by evaluating mLines size (global lineMax) plus two spaces as text width
|
||||
@ -914,7 +917,6 @@ void TextEditor::Render() {
|
||||
ImVec2 textScreenPos = ImVec2(lineStartScreenPos.x + mTextStart, lineStartScreenPos.y);
|
||||
|
||||
auto &line = mLines[lineNo];
|
||||
longest = std::max(mTextStart + TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), longest);
|
||||
auto columnNo = 0;
|
||||
Coordinates lineStartCoord(lineNo, 0);
|
||||
Coordinates lineEndCoord(lineNo, GetLineMaxColumn(lineNo));
|
||||
@ -1133,89 +1135,54 @@ void TextEditor::Render() {
|
||||
|
||||
++lineNo;
|
||||
}
|
||||
if (lineNo < mLines.size() && ImGui::GetScrollMaxX() > 0.0f)
|
||||
longest = std::max(mTextStart + TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), longest);
|
||||
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(mLongest, (globalLineMax - lineMax - 2) * mCharAdvance.y + ImGui::GetCurrentWindow()->InnerClipRect.GetHeight()));
|
||||
|
||||
if (mScrollToCursor) {
|
||||
if (mScrollToCursor)
|
||||
EnsureCursorVisible();
|
||||
mScrollToCursor = false;
|
||||
}
|
||||
|
||||
|
||||
if (mTopMarginChanged) {
|
||||
mTopMarginChanged = false;
|
||||
if (mTopMargin == 0)
|
||||
m_savedScrollY = ImGui::GetScrollY();
|
||||
mSavedScrollY = ImGui::GetScrollY();
|
||||
auto window = ImGui::GetCurrentWindow();
|
||||
auto maxScroll = window->ScrollMax.y;
|
||||
if (maxScroll > 0) {
|
||||
float lineCount;
|
||||
float pixelCount;
|
||||
if (mNewTopMargin > mTopMargin) {
|
||||
pixelCount = mNewTopMargin - mTopMargin;
|
||||
lineCount = pixelCount / mCharAdvance.y;
|
||||
} else if (mNewTopMargin > 0) {
|
||||
pixelCount = mTopMargin - mNewTopMargin;
|
||||
lineCount = pixelCount / mCharAdvance.y;
|
||||
} else {
|
||||
pixelCount = mTopMargin;
|
||||
lineCount = std::round(m_linesAdded);
|
||||
}
|
||||
auto state = mState;
|
||||
auto oldScrollY = ImGui::GetScrollY();
|
||||
int lineCountInt;
|
||||
|
||||
if (mNewTopMargin > mTopMargin) {
|
||||
lineCountInt = std::round(lineCount + m_linesAdded - std::floor(m_linesAdded));
|
||||
} else
|
||||
lineCountInt = std::round(lineCount);
|
||||
for (int i = 0; i < lineCountInt; i++) {
|
||||
if (mNewTopMargin > mTopMargin)
|
||||
mLines.insert(mLines.begin() + mLines.size(), Line());
|
||||
else
|
||||
mLines.erase(mLines.begin() + mLines.size() - 1);
|
||||
}
|
||||
if (mNewTopMargin > mTopMargin) {
|
||||
m_linesAdded += lineCount;
|
||||
m_pixelsAdded += pixelCount;
|
||||
} else if (mNewTopMargin > 0) {
|
||||
m_linesAdded -= lineCount;
|
||||
m_pixelsAdded -= pixelCount;
|
||||
} else {
|
||||
m_linesAdded = 0;
|
||||
m_pixelsAdded = 0;
|
||||
}
|
||||
if (oldScrollY + pixelCount < maxScroll) {
|
||||
if (mNewTopMargin > mTopMargin)
|
||||
m_shiftedScrollY = oldScrollY + pixelCount;
|
||||
else if (mNewTopMargin > 0)
|
||||
m_shiftedScrollY = oldScrollY - pixelCount;
|
||||
else if (ImGui::GetScrollY() == m_shiftedScrollY)
|
||||
m_shiftedScrollY = m_savedScrollY;
|
||||
else
|
||||
m_shiftedScrollY = ImGui::GetScrollY() - pixelCount;
|
||||
ImGui::SetScrollY(m_shiftedScrollY);
|
||||
} else {
|
||||
if (mNewTopMargin > mTopMargin)
|
||||
mScrollToBottom = true;
|
||||
}
|
||||
if (mNewTopMargin > mTopMargin)
|
||||
mShiftedScrollY = oldScrollY + pixelCount;
|
||||
else if (mNewTopMargin > 0)
|
||||
mShiftedScrollY = oldScrollY - pixelCount;
|
||||
else
|
||||
mShiftedScrollY = mSavedScrollY;
|
||||
ImGui::SetScrollY(mShiftedScrollY);
|
||||
mTopMargin = mNewTopMargin;
|
||||
mState = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextEditor::Render(const char *aTitle, const ImVec2 &aSize, bool aBorder) {
|
||||
mWithinRender = true;
|
||||
mTextChanged = false;
|
||||
mCursorPositionChanged = false;
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::ColorConvertU32ToFloat4(mPalette[(int)PaletteIndex::Background]));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
|
||||
if (!mIgnoreImGuiChild)
|
||||
if (!mIgnoreImGuiChild) {
|
||||
ImGui::BeginChild(aTitle, aSize, aBorder, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoMove);
|
||||
mWithinRender = true;
|
||||
}
|
||||
|
||||
if (mHandleKeyboardInputs) {
|
||||
HandleKeyboardInputs();
|
||||
@ -1231,13 +1198,15 @@ void TextEditor::Render(const char *aTitle, const ImVec2 &aSize, bool aBorder) {
|
||||
if (mHandleKeyboardInputs)
|
||||
ImGui::PopItemFlag();
|
||||
|
||||
if (!mIgnoreImGuiChild)
|
||||
if (!mIgnoreImGuiChild) {
|
||||
mScrollY = ImGui::GetScrollY();
|
||||
mWithinRender = false;
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
mWithinRender = false;
|
||||
}
|
||||
|
||||
void TextEditor::SetText(const std::string &aText) {
|
||||
@ -1598,8 +1567,6 @@ void TextEditor::DeleteSelection() {
|
||||
void TextEditor::JumpToLine(int line) {
|
||||
auto newPos = Coordinates(line, 0);
|
||||
JumpToCoords(newPos);
|
||||
|
||||
setFocusAtCoords(newPos);
|
||||
}
|
||||
|
||||
void TextEditor::JumpToCoords(const Coordinates &aNewPos) {
|
||||
@ -1636,7 +1603,14 @@ void TextEditor::MoveDown(int aAmount, bool aSelect) {
|
||||
IM_ASSERT(mState.mCursorPosition.mColumn >= 0);
|
||||
ResetCursorBlinkTime();
|
||||
auto oldPos = mState.mCursorPosition;
|
||||
mState.mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + aAmount));
|
||||
mState.mCursorPosition.mLine = std::clamp(mState.mCursorPosition.mLine + aAmount, 0, (int)mLines.size() - 1);
|
||||
if (oldPos.mLine == (mLines.size() - 1)) {
|
||||
mTopLine += aAmount;
|
||||
mTopLine = std::clamp(mTopLine, 0, (int)mLines.size() - 1);
|
||||
SetTopLine();
|
||||
EnsureCursorVisible();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mState.mCursorPosition != oldPos) {
|
||||
if (aSelect) {
|
||||
@ -1791,7 +1765,7 @@ void TextEditor::MoveTop(bool aSelect) {
|
||||
void TextEditor::TextEditor::MoveBottom(bool aSelect) {
|
||||
ResetCursorBlinkTime();
|
||||
auto oldPos = GetCursorPosition();
|
||||
auto newPos = Coordinates((int)mLines.size() - 1, 0);
|
||||
auto newPos = Coordinates((int)mLines.size() - 1, GetLineMaxColumn((int)mLines.size() - 1));
|
||||
SetCursorPosition(newPos);
|
||||
if (aSelect) {
|
||||
mInteractiveStart = oldPos;
|
||||
@ -2072,15 +2046,17 @@ std::string TextEditor::ReplaceTabsWithSpaces(const std::string& string, uint32_
|
||||
auto stringVector = SplitString(string, "\n", false);
|
||||
auto size = stringVector.size();
|
||||
std::string result;
|
||||
for (size_t i = 0; i < size - 1; i++) {
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
auto &line = stringVector[i];
|
||||
std::size_t pos = 0;
|
||||
while ((pos = line.find('\t', pos)) != std::string::npos) {
|
||||
auto spaces = tabSize - (pos % tabSize);
|
||||
line.replace(pos, 1, std::string(spaces, ' '));
|
||||
pos += tabSize;
|
||||
pos += tabSize - 1;
|
||||
}
|
||||
result += line + "\n";
|
||||
result += line;
|
||||
if (i < size - 1)
|
||||
result += "\n";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -2958,43 +2934,71 @@ float TextEditor::TextDistanceToLineStart(const Coordinates &aFrom) const {
|
||||
return distance;
|
||||
}
|
||||
|
||||
void TextEditor::SetTopLine() {
|
||||
if (!mWithinRender) {
|
||||
mSetTopLine = true;
|
||||
return;
|
||||
} else {
|
||||
mSetTopLine = false;
|
||||
ImGui::SetScrollY(mTopLine * mCharAdvance.y);
|
||||
EnsureCursorVisible();
|
||||
}
|
||||
}
|
||||
|
||||
void TextEditor::EnsureCursorVisible() {
|
||||
if (!mWithinRender) {
|
||||
mScrollToCursor = true;
|
||||
return;
|
||||
}
|
||||
|
||||
auto scrollBarSize = ImGui::GetStyle().ScrollbarSize;
|
||||
float scrollX = ImGui::GetScrollX();
|
||||
float scrollY = ImGui::GetScrollY();
|
||||
|
||||
auto windowPadding = ImGui::GetStyle().WindowPadding * 2.0f;
|
||||
auto windowPadding = ImGui::GetStyle().FramePadding * 2.0f;
|
||||
|
||||
auto height = ImGui::GetWindowHeight() - mTopMargin - windowPadding.y;
|
||||
auto width = ImGui::GetWindowWidth() - windowPadding.x;
|
||||
auto height = ImGui::GetWindowHeight() - mTopMargin - scrollBarSize - mCharAdvance.y;
|
||||
auto width = ImGui::GetWindowWidth() - windowPadding.x - scrollBarSize;
|
||||
|
||||
auto top = (int)ceil(scrollY / mCharAdvance.y);
|
||||
auto bottom = (int)ceil((scrollY + height) / mCharAdvance.y);
|
||||
auto top = (int)rint((mTopMargin > scrollY ? mTopMargin -scrollY : scrollY) / mCharAdvance.y);
|
||||
auto bottom = top + (int)rint(height / mCharAdvance.y);
|
||||
|
||||
auto left = scrollX;
|
||||
auto right = scrollX + width;
|
||||
auto left = (int)rint(scrollX / mCharAdvance.x);
|
||||
auto right = left + (int)rint(width / mCharAdvance.x);
|
||||
|
||||
auto pos = GetActualCursorCoordinates();
|
||||
auto len = TextDistanceToLineStart(pos);
|
||||
pos.mColumn = (int)rint(TextDistanceToLineStart(pos) / mCharAdvance.x);
|
||||
|
||||
if (pos.mLine <= top + 1)
|
||||
ImGui::SetScrollY(std::max(0.0f, (pos.mLine - 1) * mCharAdvance.y));
|
||||
if (pos.mLine >= bottom - 2)
|
||||
ImGui::SetScrollY(std::max(0.0f, (pos.mLine + 2) * mCharAdvance.y - height));
|
||||
if (len == 0)
|
||||
ImGui::SetScrollX(0);
|
||||
else if (len + mTextStart <= left + 4)
|
||||
ImGui::SetScrollX(std::max(0.0f, len + mTextStart - 4));
|
||||
if (len + mTextStart + mCharAdvance.x * 2 >= right - 4)
|
||||
ImGui::SetScrollX(std::max(0.0f, len + mTextStart + 4 - width + mCharAdvance.x * 2));
|
||||
bool mScrollToCursorX = true;
|
||||
bool mScrollToCursorY = true;
|
||||
|
||||
if (pos.mLine >= top && pos.mLine <= bottom)
|
||||
mScrollToCursorY = false;
|
||||
if ((pos.mColumn >= left) && (pos.mColumn <= right))
|
||||
mScrollToCursorX = false;
|
||||
if (!mScrollToCursorX && !mScrollToCursorY && mOldTopMargin == mTopMargin) {
|
||||
mScrollToCursor = false;
|
||||
mOldTopMargin = mTopMargin;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mScrollToCursorY) {
|
||||
if (pos.mLine < top)
|
||||
ImGui::SetScrollY(std::max(0.0f, pos.mLine * mCharAdvance.y));
|
||||
if (pos.mLine > bottom)
|
||||
ImGui::SetScrollY(std::max(0.0f, pos.mLine * mCharAdvance.y - height));
|
||||
}
|
||||
if (mScrollToCursorX) {
|
||||
if (pos.mColumn < left)
|
||||
ImGui::SetScrollX(std::max(0.0f, pos.mColumn * mCharAdvance.x ));
|
||||
if (pos.mColumn > right)
|
||||
ImGui::SetScrollX(std::max(0.0f, pos.mColumn * mCharAdvance.x - width));
|
||||
}
|
||||
mOldTopMargin = mTopMargin;
|
||||
}
|
||||
|
||||
int TextEditor::GetPageSize() const {
|
||||
auto height = ImGui::GetWindowHeight() - 20.0f - mTopMargin;
|
||||
auto height = ImGui::GetWindowHeight() - 2.0f * ImGui::GetStyle().WindowPadding.y - mTopMargin;
|
||||
return (int)floor(height / mCharAdvance.y);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user