diff --git a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h index ff782ff65..0896a2c1d 100644 --- a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h +++ b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h @@ -576,7 +576,7 @@ private: void HandleKeyboardInputs(); void HandleMouseInputs(); - void Render(); + void RenderText(const char *aTitle, const ImVec2 &lineNumbersStartPos, const ImVec2 &textEditorSize); float mLineSpacing = 1.0F; Lines mLines; @@ -597,6 +597,7 @@ private: bool mScrollToTop = false; bool mTextChanged = false; bool mColorizerEnabled = true; + float mLineNumberFieldWidth = 0.0F; float mLongest = 0.0F; float mTextStart = 20.0F; // position (in pixels) where a code line starts relative to the left of the TextEditor. int mLeftMargin = 10; diff --git a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp index 518f30394..c99b72995 100644 --- a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp +++ b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp @@ -328,7 +328,7 @@ TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2 &aPositi float oldX = columnX; float newColumnX = (1.0f + std::floor((1.0f + columnX) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); columnWidth = newColumnX - oldX; - if (mTextStart + columnX + columnWidth * 0.5f > local.x) + if (columnX + columnWidth > local.x) break; columnX = newColumnX; columnCoord = (columnCoord / mTabSize) * mTabSize + mTabSize; @@ -341,7 +341,7 @@ TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2 &aPositi buf[i++] = line[columnIndex++].mChar; buf[i] = '\0'; columnWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf).x; - if (mTextStart + columnX + columnWidth * 0.5f > local.x) + if (columnX + columnWidth > local.x) break; columnX += columnWidth; columnCoord++; @@ -349,7 +349,7 @@ TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2 &aPositi } } - return SanitizeCoordinates(Coordinates(lineNo, columnCoord)); + return SanitizeCoordinates(Coordinates(lineNo, columnCoord - (columnCoord != 0))); } void TextEditor::DeleteWordLeft() { @@ -860,7 +860,7 @@ inline void TextUnformattedColoredAt(const ImVec2 &pos, const ImU32 &color, cons ImGui::PopStyleColor(); } -void TextEditor::Render() { +void TextEditor::RenderText(const char *aTitle, const ImVec2 &lineNumbersStartPos, const ImVec2 &textEditorSize) { /* Compute mCharAdvance regarding 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); @@ -874,7 +874,7 @@ void TextEditor::Render() { IM_ASSERT(mLineBuffer.empty()); - auto contentSize = ImGui::GetCurrentWindowRead()->ContentRegionRect.Max - ImGui::GetWindowPos(); + auto contentSize = textEditorSize; auto drawList = ImGui::GetWindowDrawList(); mNumberOfLinesDisplayed = GetPageSize(); @@ -888,16 +888,18 @@ void TextEditor::Render() { ImGui::SetScrollY(ImGui::GetScrollMaxY()); } - ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos() + ImVec2(0, mTopMargin); + ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos(); + ImVec2 position = lineNumbersStartPos; auto scrollX = ImGui::GetScrollX(); auto scrollY = ImGui::GetScrollY(); if (mSetTopLine) SetTopLine(); else - mTopLine = std::max(0, std::floor((scrollY-mTopMargin) / mCharAdvance.y) - 1); + mTopLine = std::max(0, std::floor((scrollY-mTopMargin) / mCharAdvance.y)); auto lineNo = mTopLine; int globalLineMax = mLines.size(); auto lineMax = std::clamp(lineNo + mNumberOfLinesDisplayed, 0, globalLineMax - 1); + int totalDigitCount = std::floor(std::log10(globalLineMax)) + 1; mLongest = GetLongestLineLength() * mCharAdvance.x; // Deduce mTextStart by evaluating mLines size (global lineMax) plus two spaces as text width @@ -909,12 +911,12 @@ void TextEditor::Render() { buf[0] = '\0'; mTextStart = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x + mLeftMargin; - if (lineNo <= lineMax) { + if (!mLines.empty()) { float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x; while (lineNo <= lineMax) { - ImVec2 lineStartScreenPos = ImVec2(cursorScreenPos.x, cursorScreenPos.y + lineNo * mCharAdvance.y); - ImVec2 textScreenPos = ImVec2(lineStartScreenPos.x + mTextStart, lineStartScreenPos.y); + ImVec2 lineStartScreenPos = ImVec2(cursorScreenPos.x + mLeftMargin, cursorScreenPos.y + lineNo * mCharAdvance.y); + ImVec2 textScreenPos = lineStartScreenPos; auto &line = mLines[lineNo]; auto columnNo = 0; @@ -935,40 +937,57 @@ void TextEditor::Render() { 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); + ImVec2 vstart(lineStartScreenPos.x + sstart, lineStartScreenPos.y); + ImVec2 vend(lineStartScreenPos.x + ssend, lineStartScreenPos.y + mCharAdvance.y); drawList->AddRectFilled(vstart, vend, mPalette[(int)PaletteIndex::Selection]); } - - auto start = ImVec2(lineStartScreenPos.x + scrollX, lineStartScreenPos.y); - + float startPos = 0; + if (scrollY < mTopMargin) + startPos = mTopMargin - scrollY; + ImVec2 lineNoStartScreenPos = ImVec2(position.x, startPos + position.y + (lineNo - mTopLine) * mCharAdvance.y); + auto start = ImVec2(lineNoStartScreenPos.x + mLineNumberFieldWidth, lineNoStartScreenPos.y); + bool focused = ImGui::IsWindowFocused(); + if (!mIgnoreImGuiChild) + ImGui::EndChild(); // Draw line number (right aligned) if (mShowLineNumbers) { - snprintf(buf, 16, "%d ", lineNo + 1); + ImGui::SetCursorScreenPos(position); + if (!mIgnoreImGuiChild) + ImGui::BeginChild("##lineNumbers"); - auto lineNoWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x; - TextUnformattedColoredAt(ImVec2(lineStartScreenPos.x + mTextStart - lineNoWidth, lineStartScreenPos.y),mPalette[(int) PaletteIndex::LineNumber],buf); + int padding = totalDigitCount - std::floor(std::log10(lineNo + 1)) - 1; + std::string space = " "; + while (padding-- > 0) { + space += " "; + } + std::string lineNoStr = space + std::to_string(lineNo + 1); + TextUnformattedColoredAt(ImVec2(mLeftMargin + lineNoStartScreenPos.x, lineNoStartScreenPos.y), mPalette[(int) PaletteIndex::LineNumber], lineNoStr.c_str()); } // Draw breakpoints if (mBreakpoints.count(lineNo + 1) != 0) { - auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y); - drawList->AddRectFilled(start + ImVec2(mTextStart, 0), end, mPalette[(int)PaletteIndex::Breakpoint]); + auto end = ImVec2(lineNoStartScreenPos.x + contentSize.x + mLineNumberFieldWidth, lineNoStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(ImVec2(lineNumbersStartPos.x, lineNoStartScreenPos.y), end, mPalette[(int)PaletteIndex::Breakpoint]); drawList->AddCircleFilled(start + ImVec2(0, mCharAdvance.y) / 2, mCharAdvance.y / 3, mPalette[(int)PaletteIndex::Breakpoint]); drawList->AddCircle(start + ImVec2(0, mCharAdvance.y) / 2, mCharAdvance.y / 3, mPalette[(int)PaletteIndex::Default]); } if (mState.mCursorPosition.mLine == lineNo && mShowCursor) { - bool focused = ImGui::IsWindowFocused(); // 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); + auto end = ImVec2(lineNoStartScreenPos.x + contentSize.x + mLineNumberFieldWidth, lineNoStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(ImVec2(lineNumbersStartPos.x, lineNoStartScreenPos.y), end, mPalette[(int)(focused ? PaletteIndex::CurrentLineFill : PaletteIndex::CurrentLineFillInactive)]); + drawList->AddRect(ImVec2(lineNumbersStartPos.x, lineNoStartScreenPos.y), end, mPalette[(int)PaletteIndex::CurrentLineEdge], 1.0f); } + } + if (mShowLineNumbers && !mIgnoreImGuiChild) + ImGui::EndChild(); + if (!mIgnoreImGuiChild) + ImGui::BeginChild(aTitle); + if (mState.mCursorPosition.mLine == lineNo && mShowCursor) { // Render the cursor if (focused) { auto timeEnd = ImGui::GetTime() * 1000; @@ -990,8 +1009,8 @@ void TextEditor::Render() { 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); + ImVec2 cstart(lineStartScreenPos.x + cx, lineStartScreenPos.y); + ImVec2 cend(lineStartScreenPos.x + cx + width, lineStartScreenPos.y + mCharAdvance.y); drawList->AddRectFilled(cstart, cend, mPalette[(int)PaletteIndex::Cursor]); if (elapsed > sCursorBlinkInterval) mStartTime = timeEnd; @@ -1135,10 +1154,22 @@ void TextEditor::Render() { ++lineNo; } - } + if (!mIgnoreImGuiChild) + ImGui::EndChild(); - ImGui::Dummy(ImVec2(mLongest, (globalLineMax - lineMax - 2) * mCharAdvance.y + ImGui::GetCurrentWindow()->InnerClipRect.GetHeight())); + if (mShowLineNumbers && !mIgnoreImGuiChild) { + ImGui::BeginChild("##lineNumbers"); + ImGui::Dummy(ImVec2(mLineNumberFieldWidth, (globalLineMax - lineMax - 1) * mCharAdvance.y + ImGui::GetCurrentWindow()->InnerClipRect.GetHeight() - mCharAdvance.y)); + ImGui::EndChild(); + } + if (!mIgnoreImGuiChild) + ImGui::BeginChild(aTitle); + + if (mShowLineNumbers) + ImGui::Dummy(ImVec2(mLongest, (globalLineMax - lineMax - 2) * mCharAdvance.y + ImGui::GetCurrentWindow()->InnerClipRect.GetHeight())); + else + ImGui::Dummy(ImVec2(mLongest, (globalLineMax - 1 - lineMax + GetPageSize() - 1) * mCharAdvance.y)); if (mScrollToCursor) EnsureCursorVisible(); @@ -1174,39 +1205,75 @@ void TextEditor::Render() { } 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) { - ImGui::BeginChild(aTitle, aSize, aBorder, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoMove); - mWithinRender = true; + auto scrollBg = ImGui::GetStyleColorVec4(ImGuiCol_ScrollbarBg); + scrollBg.w = 0.0f; + auto scrollBarSize = ImGui::GetStyle().ScrollbarSize; + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::ColorConvertU32ToFloat4(mPalette[(int) PaletteIndex::Background])); + ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImGui::ColorConvertFloat4ToU32(scrollBg)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarRounding,0); + ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize,scrollBarSize); + + auto position = ImGui::GetCursorScreenPos(); + if (mShowLineNumbers ) { + std::string lineNumber = " " + std::to_string(mLines.size()) + " "; + mLineNumberFieldWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, lineNumber.c_str(), nullptr, nullptr).x + mLeftMargin; + ImGui::SetCursorScreenPos(position); + auto lineNoSize = ImVec2(mLineNumberFieldWidth, aSize.y); + if (!mIgnoreImGuiChild) { + ImGui::BeginChild("##lineNumbers", lineNoSize, false, ImGuiWindowFlags_NoScrollbar); + ImGui::EndChild(); + } + } else { + mLineNumberFieldWidth = 0; + } + + ImVec2 textEditorSize = aSize; + textEditorSize.x -= mLineNumberFieldWidth; + mLongest = GetLongestLineLength() * mCharAdvance.x; + bool scroll_x = mLongest > textEditorSize.x; + bool scroll_y = mLines.size() > 1; + if (!aBorder && scroll_y) + textEditorSize.x -= scrollBarSize; + ImGui::SetCursorScreenPos(ImVec2(position.x + mLineNumberFieldWidth, position.y)); + ImGuiChildFlags childFlags = aBorder ? ImGuiChildFlags_Borders : ImGuiChildFlags_None; + ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove; + if (!mIgnoreImGuiChild) + ImGui::BeginChild(aTitle, textEditorSize, childFlags, windowFlags); + auto window = ImGui::GetCurrentWindow(); + window->ScrollbarSizes = ImVec2(scrollBarSize * scroll_x, scrollBarSize * scroll_y); + ImGui::GetCurrentWindowRead()->ScrollbarSizes = ImVec2(scrollBarSize * scroll_y, scrollBarSize * scroll_x); + if (scroll_y) { + ImGui::GetCurrentWindow()->ScrollbarY= true; + ImGui::Scrollbar(ImGuiAxis_Y); + } + if (scroll_x) { + ImGui::GetCurrentWindow()->ScrollbarX= true; + ImGui::Scrollbar(ImGuiAxis_X); } if (mHandleKeyboardInputs) { HandleKeyboardInputs(); - ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, false); } if (mHandleMouseInputs) HandleMouseInputs(); ColorizeInternal(); - Render(); + RenderText(aTitle, position, textEditorSize); - if (mHandleKeyboardInputs) - ImGui::PopItemFlag(); - - if (!mIgnoreImGuiChild) { - mScrollY = ImGui::GetScrollY(); - mWithinRender = false; + if (!mIgnoreImGuiChild) ImGui::EndChild(); - } - ImGui::PopStyleVar(); - ImGui::PopStyleColor(); + ImGui::PopStyleVar(3); + ImGui::PopStyleColor(2); + mWithinRender = false; + ImGui::SetCursorScreenPos(ImVec2(position.x,position.y+aSize.y-1)); } void TextEditor::SetText(const std::string &aText) { @@ -2998,7 +3065,7 @@ void TextEditor::EnsureCursorVisible() { } int TextEditor::GetPageSize() const { - auto height = ImGui::GetWindowHeight() - 2.0f * ImGui::GetStyle().WindowPadding.y - mTopMargin; + auto height = ImGui::GetCurrentWindow()->InnerClipRect.GetHeight() - mTopMargin - ImGui::GetStyle().FramePadding.y; return (int)floor(height / mCharAdvance.y); } diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index ebd2dcc48..3eaac305d 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -322,23 +322,22 @@ namespace hex::plugin::builtin { if (ImHexApi::Provider::isValid() && provider->isAvailable()) { static float height = 0; static bool dragging = false; - - const auto availableSize = ImGui::GetContentRegionAvail(); + const ImGuiContext& g = *GImGui; + if (g.CurrentWindow->Appearing) + return; + const auto availableSize = g.CurrentWindow->Size; const auto windowPosition = ImGui::GetCursorScreenPos(); auto textEditorSize = availableSize; textEditorSize.y *= 3.5 / 5.0; textEditorSize.y -= ImGui::GetTextLineHeightWithSpacing(); - textEditorSize.y += height; + textEditorSize.y = std::clamp(textEditorSize.y + height,200.0F, availableSize.y-200.0F); - if (availableSize.y > 1) - textEditorSize.y = std::clamp(textEditorSize.y, 1.0F, std::max(1.0F, availableSize.y - ImGui::GetTextLineHeightWithSpacing() * 3)); - const ImGuiContext& g = *GImGui; if (g.NavWindow != nullptr) { std::string name = g.NavWindow->Name; if (name.contains(textEditorView) || name.contains(consoleView)) m_focusedSubWindowName = name; } - m_textEditor.Render("hex.builtin.view.pattern_editor.name"_lang, textEditorSize, true); + m_textEditor.Render("hex.builtin.view.pattern_editor.name"_lang, textEditorSize, false); m_textEditorHoverBox = ImRect(windowPosition,windowPosition+textEditorSize); m_consoleHoverBox = ImRect(ImVec2(windowPosition.x,windowPosition.y+textEditorSize.y),windowPosition+availableSize); TextEditor::FindReplaceHandler *findReplaceHandler = m_textEditor.GetFindReplaceHandler();