diff --git a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h index 3b8ebf7fb..ffedde538 100644 --- a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h +++ b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h @@ -9,6 +9,7 @@ #include #include #include "imgui.h" +#include "imgui_internal.h" class TextEditor { @@ -132,12 +133,73 @@ public: using Identifiers = std::unordered_map; using Keywords = std::unordered_set ; using ErrorMarkers = std::map>; - using ErrorHoverBoxes = std::map>; using Breakpoints = std::unordered_set; using Palette = std::array; using Char = uint8_t ; - struct Glyph + class ActionableBox { + + ImRect mBox; + public: + ActionableBox()=default; + explicit ActionableBox(const ImRect &box) : mBox(box) {} + std::function mCallback; + virtual bool trigger() { + return ImGui::IsMouseHoveringRect(mBox.Min,mBox.Max); + } + void setCallback(const std::function &callback) { mCallback = callback; } + }; + + class CursorChangeBox : public ActionableBox { + public: + CursorChangeBox()=default; + explicit CursorChangeBox(const ImRect &box) : ActionableBox(box) { + setCallback([]() { + ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); + }); + } + }; + + class ErrorGotoBox : public ActionableBox { + Coordinates mPos; + public: + ErrorGotoBox()=default; + ErrorGotoBox(const ImRect &box, const Coordinates &pos, TextEditor *editor) : ActionableBox(box), mPos(pos) { + setCallback( [this,editor]() { + editor->JumpToCoords(mPos); + }); + } + bool trigger() override { + return ActionableBox::trigger() && ImGui::IsMouseClicked(0); + } + }; + + using ErrorGotoBoxes = std::map; + using CursorBoxes = std::map; + + class ErrorHoverBox : public ActionableBox { + Coordinates mPos; + std::string mErrorText; + public: + ErrorHoverBox()=default; + ErrorHoverBox(const ImRect &box, const Coordinates &pos,const char *errorText) : ActionableBox(box), mPos(pos), mErrorText(errorText) { + setCallback([this]() { + ImGui::BeginTooltip(); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.2f, 1.0f)); + ImGui::Text("Error at line %d:", mPos.mLine); + ImGui::PopStyleColor(); + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.5f, 0.5f, 0.2f, 1.0f)); + ImGui::TextUnformatted(mErrorText.c_str()); + ImGui::PopStyleColor(); + ImGui::EndTooltip(); + } + ); + } + }; + using ErrorHoverBoxes = std::map; + + struct Glyph { Char mChar; PaletteIndex mColorIndex = PaletteIndex::Default; @@ -188,6 +250,23 @@ public: static const LanguageDefinition& AngelScript(); static const LanguageDefinition& Lua(); }; + void ClearErrorMarkers() { + mErrorMarkers.clear(); + mErrorHoverBoxes.clear(); + } + + void ClearGotoBoxes() { + mErrorGotoBoxes.clear(); + } + + void ClearCursorBoxes() { + mCursorBoxes.clear(); + } + void ClearActionables() { + ClearErrorMarkers(); + ClearGotoBoxes(); + ClearCursorBoxes(); + } struct Selection { Coordinates mStart; @@ -210,6 +289,8 @@ public: void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false); void SetText(const std::string& aText); + void JumpToLine(int line); + void JumpToCoords(const Coordinates &coords); std::string GetText() const; bool isEmpty() const { auto text = GetText(); @@ -221,12 +302,32 @@ public: std::string GetSelectedText() const; std::string GetCurrentLineText()const; + + std::string GetLineText(int line)const; + void SetSourceCodeEditor(TextEditor *editor) { mSourceCodeEditor = editor; } + TextEditor *GetSourceCodeEditor() { + if(mSourceCodeEditor!=nullptr) + return mSourceCodeEditor; + return this; + } + + class FindReplaceHandler; public: + void AddClickableText(std::string text) { + mClickableText.push_back(text); + } + void ClearClickableText() { + mClickableText.clear(); + } FindReplaceHandler *GetFindReplaceHandler() { return &mFindReplaceHandler; } int GetTotalLines() const { return (int)mLines.size(); } bool IsOverwrite() const { return mOverwrite; } + void setFocusAtCoords(const Coordinates &coords) { + mFocusAtCoords = coords; + mUpdateFocus = true; + } void SetOverwrite(bool aValue) { mOverwrite = aValue; } void SetReadOnly(bool aValue); @@ -244,8 +345,10 @@ public: Coordinates GetCursorPosition() const { return GetActualCursorCoordinates(); } void SetCursorPosition(const Coordinates& aPosition); + bool RaiseContextMenu() { return mRaiseContextMenu; } void ClearRaiseContextMenu() { mRaiseContextMenu = false; } + inline void SetHandleMouseInputs (bool aValue){ mHandleMouseInputs = aValue;} inline bool IsHandleMouseInputsEnabled() const { return mHandleKeyboardInputs; } @@ -491,11 +594,14 @@ private: Breakpoints mBreakpoints; ErrorMarkers mErrorMarkers; ErrorHoverBoxes mErrorHoverBoxes; + ErrorGotoBoxes mErrorGotoBoxes; + CursorBoxes mCursorBoxes; ImVec2 mCharAdvance; Coordinates mInteractiveStart, mInteractiveEnd; std::string mLineBuffer; uint64_t mStartTime; std::vector mDefines; + TextEditor *mSourceCodeEditor=nullptr; float m_linesAdded = 0; float m_savedScrollY = 0; float m_pixelsAdded = 0; @@ -504,6 +610,10 @@ private: bool mShowCursor; bool mShowLineNumbers; bool mRaiseContextMenu = false; + Coordinates mFocusAtCoords; + bool mUpdateFocus = false; + + std::vector mClickableText; static const int sCursorBlinkInterval; static const int sCursorBlinkOnTime; diff --git a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp index 33cbe5236..3bcc66c22 100644 --- a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp +++ b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp @@ -982,6 +982,48 @@ void TextEditor::Render() { } } + // Render goto buttons + auto lineText = GetLineText(lineNo); + Coordinates gotoKey = Coordinates(lineNo + 1, 0); + std::string errorLineColumn; + bool found = false; + for (auto text : mClickableText) { + if (lineText.find(text) == 0) { + errorLineColumn = lineText.substr(text.size()); + if (!errorLineColumn.empty()) { + found = true; + break; + } + } + } + if (found) { + int currLine = 0, currColumn = 0; + if (auto idx = errorLineColumn.find(":"); idx != std::string::npos) { + auto errorLine = errorLineColumn.substr(0, idx); + if (!errorLine.empty()) + currLine = std::stoi(errorLine) - 1; + auto errorColumn = errorLineColumn.substr(idx + 1); + if (!errorColumn.empty()) + currColumn = std::stoi(errorColumn) - 1; + } + TextEditor::Coordinates errorPos = {currLine, currColumn}; + ImVec2 errorStart = ImVec2(lineStartScreenPos.x, lineStartScreenPos.y); + ImVec2 errorEnd = ImVec2( lineStartScreenPos.x + TextDistanceToLineStart(Coordinates(lineNo, GetLineCharacterCount(lineNo))), lineStartScreenPos.y + mCharAdvance.y); + ErrorGotoBox box = ErrorGotoBox(ImRect({errorStart, errorEnd}), errorPos, GetSourceCodeEditor()); + mErrorGotoBoxes[gotoKey] = box; + CursorChangeBox cursorBox = CursorChangeBox(ImRect({errorStart, errorEnd})); + mCursorBoxes[gotoKey] = cursorBox; + } + if (mCursorBoxes.find(gotoKey) != mCursorBoxes.end()) { + auto box = mCursorBoxes[gotoKey]; + if (box.trigger()) box.mCallback(); + } + + if (mErrorGotoBoxes.find(gotoKey) != mErrorGotoBoxes.end()) { + auto box = mErrorGotoBoxes[gotoKey]; + if (box.trigger()) box.mCallback(); + } + // Render colorized text auto prevColor = line.empty() ? mPalette[(int)PaletteIndex::Default] : GetGlyphColor(line[0]); ImVec2 bufferOffset; @@ -1004,17 +1046,35 @@ void TextEditor::Render() { mLineBuffer.clear(); } if (underwaved) { - auto textStart = TextDistanceToLineStart(Coordinates(lineNo, i)) + mTextStart; + auto textStart = TextDistanceToLineStart(Coordinates(lineNo, i)); auto begin = ImVec2(lineStartScreenPos.x + textStart, lineStartScreenPos.y); auto errorLength = errorIt->second.first; + auto errorMessage = errorIt->second.second; if (errorLength == 0) errorLength = line.size() - i - 1; auto end = Underwaves(begin, errorLength, mPalette[(int32_t) PaletteIndex::ErrorMarker]); - mErrorHoverBoxes[Coordinates(lineNo+1,i+1)]=std::make_pair(begin,end); + Coordinates key = Coordinates(lineNo+1,i+1); + ErrorHoverBox box = ErrorHoverBox(ImRect({begin, end}), key, errorMessage.c_str()); + mErrorHoverBoxes[key] = box; + } + Coordinates key = Coordinates(lineNo + 1, i + 1); + if (mErrorHoverBoxes.find(key) != mErrorHoverBoxes.end()) { + auto box = mErrorHoverBoxes[key]; + if (box.trigger()) box.mCallback(); } prevColor = color; + if (mUpdateFocus && mFocusAtCoords == Coordinates(lineNo, i)) { + mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = mFocusAtCoords; + mSelectionMode = SelectionMode::Normal; + SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + ResetCursorBlinkTime(); + EnsureCursorVisible(); + ImGui::SetKeyboardFocusHere(-1); + mUpdateFocus = false; + } + if (glyph.mChar == '\t') { auto oldX = bufferOffset.x; bufferOffset.x = (1.0f + std::floor((1.0f + bufferOffset.x) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); @@ -1070,22 +1130,6 @@ void TextEditor::Render() { mScrollToCursor = false; } - for (auto [key,value] : mErrorMarkers) { - auto start = mErrorHoverBoxes[key].first; - auto end = mErrorHoverBoxes[key].second; - if (ImGui::IsMouseHoveringRect(start, end)) { - ImGui::BeginTooltip(); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.2f, 1.0f)); - ImGui::Text("Error at line %d:", key.mLine); - ImGui::PopStyleColor(); - ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.5f, 0.5f, 0.2f, 1.0f)); - ImGui::Text("%s", value.second.c_str()); - ImGui::PopStyleColor(); - ImGui::EndTooltip(); - } - } - ImGuiPopupFlags_ popup_flags = ImGuiPopupFlags_None; ImGuiContext& g = *GImGui; auto oldTopMargin = mTopMargin; @@ -1553,6 +1597,19 @@ void TextEditor::DeleteSelection() { Colorize(mState.mSelectionStart.mLine, 1); } +void TextEditor::JumpToLine(int line) { + auto newPos = Coordinates(line, 0); + JumpToCoords(newPos); + setFocusAtCoords(newPos); +} + +void TextEditor::JumpToCoords(const Coordinates &aNewPos) { + SetSelection(aNewPos, aNewPos); + SetCursorPosition(aNewPos); + EnsureCursorVisible(); + setFocusAtCoords(aNewPos); +} + void TextEditor::MoveUp(int aAmount, bool aSelect) { ResetCursorBlinkTime(); auto oldPos = mState.mCursorPosition; @@ -2478,10 +2535,12 @@ std::string TextEditor::GetSelectedText() const { } std::string TextEditor::GetCurrentLineText() const { - auto lineLength = GetLineMaxColumn(mState.mCursorPosition.mLine); - return GetText( - Coordinates(mState.mCursorPosition.mLine, 0), - Coordinates(mState.mCursorPosition.mLine, lineLength)); + return GetLineText(mState.mCursorPosition.mLine); +} + +std::string TextEditor::GetLineText(int line) const { + auto lineLength = GetLineCharacterCount(line); + return GetText(Coordinates(line, 0),Coordinates(line, lineLength)); } void TextEditor::ProcessInputs() { diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 72d86a7db..cc31901f4 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -214,6 +214,13 @@ namespace hex::plugin::builtin { m_consoleEditor.SetReadOnly(true); m_consoleEditor.SetShowCursor(false); m_consoleEditor.SetShowLineNumbers(false); + m_consoleEditor.SetSourceCodeEditor(&m_textEditor); + std::string sourcecode = pl::api::Source::DefaultSource; + std::string error = "E: "; + std::string end = ":"; + std::string arrow = " --> in "; + m_consoleEditor.AddClickableText(error + sourcecode + end); + m_consoleEditor.AddClickableText(error + arrow + sourcecode + end); this->registerEvents(); this->registerMenuItems(); @@ -1753,8 +1760,9 @@ namespace hex::plugin::builtin { m_runningEvaluators += 1; m_executionDone.get(provider) = false; + m_textEditor.ClearActionables(); - m_textEditor.SetErrorMarkers({}); + m_consoleEditor.ClearActionables(); m_console.get(provider).clear(); m_consoleNeedsUpdate = true;