diff --git a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h index 39e57c96d..9b6693bcc 100644 --- a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h +++ b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h @@ -321,7 +321,6 @@ public: return this; } - class FindReplaceHandler; public: @@ -334,6 +333,10 @@ public: FindReplaceHandler *GetFindReplaceHandler() { return &mFindReplaceHandler; } int GetTotalLines() const { return (int)mLines.size(); } bool IsOverwrite() const { return mOverwrite; } + void SetTopMarginChanged(int newMargin) { + mNewTopMargin = newMargin; + mTopMarginChanged = true; + } void setFocusAtCoords(const Coordinates &coords) { mFocusAtCoords = coords; mUpdateFocus = true; @@ -576,6 +579,8 @@ private: int mUndoIndex; bool mScrollToBottom; float mTopMargin; + float mNewTopMargin; + bool mTopMarginChanged=false; int mTabSize; bool mOverwrite; diff --git a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp index 8f7e5868e..cb628180a 100644 --- a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp +++ b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp @@ -1129,63 +1129,43 @@ void TextEditor::Render() { EnsureCursorVisible(); mScrollToCursor = false; } - - ImGuiPopupFlags_ popup_flags = ImGuiPopupFlags_None; - ImGuiContext& g = *GImGui; - auto oldTopMargin = mTopMargin; - auto popupStack = g.OpenPopupStack; - if (popupStack.Size > 0) { - for (int n = 0; n < popupStack.Size; n++){ - if (auto window = popupStack[n].Window; window != nullptr) { - if (window->Size.x == mFindReplaceHandler.GetFindWindowSize().x && - window->Size.y == mFindReplaceHandler.GetFindWindowSize().y && - window->Pos.x == mFindReplaceHandler.GetFindWindowPos().x && - window->Pos.y == mFindReplaceHandler.GetFindWindowPos().y) { - mTopMargin = mFindReplaceHandler.GetFindWindowSize().y; - } - } - } - } else { - mTopMargin = 0; - } - - - if (mTopMargin != oldTopMargin) { - if (oldTopMargin == 0) + if (mTopMarginChanged) { + mTopMarginChanged = false; + if (mTopMargin == 0) m_savedScrollY = ImGui::GetScrollY(); auto window = ImGui::GetCurrentWindow(); auto maxScroll = window->ScrollMax.y; if (maxScroll > 0) { float lineCount; float pixelCount; - if (mTopMargin > oldTopMargin) { - pixelCount = mTopMargin - oldTopMargin; + if (mNewTopMargin > mTopMargin) { + pixelCount = mNewTopMargin - mTopMargin; lineCount = pixelCount / mCharAdvance.y; - } else if (mTopMargin > 0) { - pixelCount = oldTopMargin - mTopMargin; + } else if (mNewTopMargin > 0) { + pixelCount = mTopMargin - mNewTopMargin; lineCount = pixelCount / mCharAdvance.y; } else { - pixelCount = oldTopMargin; + pixelCount = mTopMargin; lineCount = std::round(m_linesAdded); } auto state = mState; auto oldScrollY = ImGui::GetScrollY(); int lineCountInt; - if (mTopMargin > oldTopMargin) { + 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 (mTopMargin > oldTopMargin) + if (mNewTopMargin > mTopMargin) mLines.insert(mLines.begin() + mLines.size(), Line()); else mLines.erase(mLines.begin() + mLines.size() - 1); } - if (mTopMargin > oldTopMargin) { + if (mNewTopMargin > mTopMargin) { m_linesAdded += lineCount; m_pixelsAdded += pixelCount; - } else if (mTopMargin > 0) { + } else if (mNewTopMargin > 0) { m_linesAdded -= lineCount; m_pixelsAdded -= pixelCount; } else { @@ -1193,9 +1173,9 @@ void TextEditor::Render() { m_pixelsAdded = 0; } if (oldScrollY + pixelCount < maxScroll) { - if (mTopMargin > oldTopMargin) + if (mNewTopMargin > mTopMargin) m_shiftedScrollY = oldScrollY + pixelCount; - else if (mTopMargin > 0) + else if (mNewTopMargin > 0) m_shiftedScrollY = oldScrollY - pixelCount; else if (ImGui::GetScrollY() == m_shiftedScrollY) m_shiftedScrollY = m_savedScrollY; @@ -1203,9 +1183,10 @@ void TextEditor::Render() { m_shiftedScrollY = ImGui::GetScrollY() - pixelCount; ImGui::SetScrollY(m_shiftedScrollY); } else { - if (mTopMargin > oldTopMargin) + if (mNewTopMargin > mTopMargin) mScrollToBottom = true; } + mTopMargin = mNewTopMargin; mState = state; } } @@ -1600,6 +1581,7 @@ void TextEditor::DeleteSelection() { void TextEditor::JumpToLine(int line) { auto newPos = Coordinates(line, 0); JumpToCoords(newPos); + setFocusAtCoords(newPos); } @@ -1607,6 +1589,7 @@ void TextEditor::JumpToCoords(const Coordinates &aNewPos) { SetSelection(aNewPos, aNewPos); SetCursorPosition(aNewPos); EnsureCursorVisible(); + setFocusAtCoords(aNewPos); } diff --git a/plugins/builtin/include/content/views/view_pattern_editor.hpp b/plugins/builtin/include/content/views/view_pattern_editor.hpp index 074ce86ca..703b0d471 100644 --- a/plugins/builtin/include/content/views/view_pattern_editor.hpp +++ b/plugins/builtin/include/content/views/view_pattern_editor.hpp @@ -73,8 +73,9 @@ namespace hex::plugin::builtin { return ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; } - public: std::string preprocessText(const std::string &code); + void setPopupWindowHeight(u32 height) { m_popupWindowHeight = height; } + u32 getPopupWindowHeight() const { return m_popupWindowHeight; } struct VirtualFile { std::fs::path path; @@ -282,12 +283,16 @@ namespace hex::plugin::builtin { bool m_parentHighlightingEnabled = true; bool m_replaceMode = false; bool m_openFindReplacePopUp = false; - + bool m_openGotoLinePopUp = false; std::map m_patternNames; ImRect m_textEditorHoverBox; ImRect m_consoleHoverBox; std::string m_focusedSubWindowName; + float m_popupWindowHeight = 0; + float m_popupWindowHeightChange = 0; + bool m_frPopupIsClosed = true; + bool m_gotoPopupIsClosed = true; static inline std::array m_findHistory; static inline u32 m_findHistorySize = 0; @@ -306,7 +311,8 @@ namespace hex::plugin::builtin { void drawPatternTooltip(pl::ptrn::Pattern *pattern); - void drawFindReplaceDialog(TextEditor *textEditor, std::string &findWord, bool &requestFocus, u64 &position, u64 &count, bool &updateCount, bool canReplace); + void drawTextEditorFindReplacePopup(TextEditor *textEditor); + void drawTextEditorGotoLinePopup(TextEditor *textEditor); void historyInsert(std::array &history, u32 &size, u32 &index, const std::string &value); @@ -317,6 +323,7 @@ namespace hex::plugin::builtin { TextEditor *getEditorFromFocusedWindow(); void setupFindReplace(TextEditor *editor); + void setupGotoLine(TextEditor *editor); void registerEvents(); void registerMenuItems(); diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index f92f92e35..0fd6bb5fc 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -935,6 +935,7 @@ "hex.builtin.view.pattern_editor.evaluating": "Evaluating...", "hex.builtin.view.pattern_editor.find_hint": "Find", "hex.builtin.view.pattern_editor.find_hint_history": " for history)", + "hex.builtin.view.pattern_editor.goto_line": "Goto line: ", "hex.builtin.view.pattern_editor.menu.edit.place_pattern": "Place pattern...", "hex.builtin.view.pattern_editor.menu.edit.place_pattern.builtin": "Built-in Type", "hex.builtin.view.pattern_editor.menu.edit.place_pattern.builtin.array": "Array", @@ -945,6 +946,7 @@ "hex.builtin.view.pattern_editor.menu.find": "Find...", "hex.builtin.view.pattern_editor.menu.find_next": "Find Next", "hex.builtin.view.pattern_editor.menu.find_previous": "Find Previous", + "hex.builtin.view.pattern_editor.menu.goto_line": "Goto line...", "hex.builtin.view.pattern_editor.menu.replace": "Replace...", "hex.builtin.view.pattern_editor.menu.replace_next": "Replace Next", "hex.builtin.view.pattern_editor.menu.replace_previous": "Replace Previous", @@ -961,6 +963,7 @@ "hex.builtin.view.pattern_editor.sections.view": "View content", "hex.builtin.view.pattern_editor.sections.export": "Export content", "hex.builtin.view.pattern_editor.settings": "Settings", + "hex.builtin.view.pattern_editor.shortcut.goto_line": "Goto line ...", "hex.builtin.view.pattern_editor.shortcut.find": "Search ...", "hex.builtin.view.pattern_editor.shortcut.replace": "Replace ...", "hex.builtin.view.pattern_editor.shortcut.find_next": "Find Next", diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index cc31901f4..67b168a6c 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -238,98 +238,82 @@ namespace hex::plugin::builtin { } void ViewPatternEditor::setupFindReplace(TextEditor *editor) { - - // Context menu entries that open the find/replace popup - ImGui::PushID(editor); - static std::string findWord; - static bool requestFocus = false; - static u64 position = 0; - static u64 count = 0; - static bool updateCount = false; - TextEditor::FindReplaceHandler *findReplaceHandler = editor->GetFindReplaceHandler(); - static bool canReplace = true; + if (editor == nullptr) + return; if (m_openFindReplacePopUp) { m_openFindReplacePopUp = false; - // Place the popup at the top right of the window - auto windowSize = ImGui::GetWindowSize(); auto style = ImGui::GetStyle(); - // Set the scrollbar size only if it is visible - float scrollbarSize = 0; - - // Calculate the number of lines to display in the text editor - auto totalTextHeight = editor->GetTotalLines() * editor->GetCharAdvance().y; - - // Compare it to the window height - if (totalTextHeight > windowSize.y) - scrollbarSize = style.ScrollbarSize; - auto labelSize = ImGui::CalcTextSize(ICON_VS_WHOLE_WORD); // Six icon buttons - auto popupSize = ImVec2({(labelSize.x + style.FramePadding.x * 2.0F) * 6.0F, - labelSize.y + style.FramePadding.y * 2.0F + style.WindowPadding.y + 3 }); + auto popupSizeX = (labelSize.x + style.FramePadding.x * 2.0F) * 6.0F; - // 2 * 11 spacings in between elements - popupSize.x += style.FramePadding.x * 22.0F; + // 2 * 9 spacings in between elements + popupSizeX += style.FramePadding.x * 18.0F; // Text input fields are set to 12 characters wide - popupSize.x += ImGui::GetFontSize() * 12.0F; + popupSizeX += ImGui::GetFontSize() * 12.0F; // IndexOfCount text - popupSize.x += ImGui::CalcTextSize("2000 of 2000").x + 2.0F; - popupSize.x += scrollbarSize; + popupSizeX += ImGui::CalcTextSize("2000 of 2000").x + 2.0F; + popupSizeX += style.FramePadding.x * 2.0F; // Position of popup relative to parent window - ImVec2 windowPosForPopup = ImGui::GetWindowPos(); + ImVec2 windowPosForPopup; + windowPosForPopup.x = m_textEditorHoverBox.Max.x - style.ScrollbarSize - popupSizeX; - // Not the window height but the content height - windowPosForPopup.y += popupSize.y; + if (m_focusedSubWindowName.contains(consoleView)) + windowPosForPopup.y = m_consoleHoverBox.Min.y; + else if (m_focusedSubWindowName.contains(textEditorView)) + windowPosForPopup.y = m_textEditorHoverBox.Min.y; + else + return; + ImGui::SetNextWindowPos(windowPosForPopup); + ImGui::OpenPopup("##text_editor_view_find_replace_popup"); + } + drawTextEditorFindReplacePopup(editor); + + } + + void ViewPatternEditor::setupGotoLine(TextEditor *editor) { + + // Context menu entries that open the goto line popup + if (m_openGotoLinePopUp) { + m_openGotoLinePopUp = false; + // Place the popup at the top right of the window + auto style = ImGui::GetStyle(); + + + auto labelSize = ImGui::CalcTextSize("hex.builtin.view.pattern_editor.goto_line"_lang); + + auto popupSizeX = (labelSize.x + style.FramePadding.x * 2.0F); + + + // Text input fields are set to 8 characters wide + popupSizeX += ImGui::CalcTextSize("00000000").x; + + popupSizeX += style.WindowPadding.x * 2.0F; + // Position of popup relative to parent window + ImVec2 windowPosForPopup; - // Add remaining window height - popupSize.y += style.WindowPadding.y + 1; // Move to the right so as not to overlap the scrollbar - windowPosForPopup.x += windowSize.x - popupSize.x; - findReplaceHandler->SetFindWindowPos(windowPosForPopup); - - if (m_replaceMode) { - // Remove one window padding - popupSize.y -= style.WindowPadding.y; - // Add the replace window height - popupSize.y *= 2; - } + windowPosForPopup.x = m_textEditorHoverBox.Max.x - style.ScrollbarSize - popupSizeX; if (m_focusedSubWindowName.contains(consoleView)) { windowPosForPopup.y = m_consoleHoverBox.Min.y; - canReplace = false; - } else if (m_focusedSubWindowName.contains(textEditorView)) - canReplace = true; + } else if (m_focusedSubWindowName.contains(textEditorView)) { + windowPosForPopup.y = m_textEditorHoverBox.Min.y; + } else { + return; + } ImGui::SetNextWindowPos(windowPosForPopup); - ImGui::SetNextWindowSize(popupSize); - ImGui::OpenPopup("##pattern_editor_find_replace"); + ImGui::OpenPopup("##text_editor_view_goto_line_popup"); - findReplaceHandler->resetMatches(); - - // Use selection as find word if there is one, otherwise use the word under the cursor - if (!editor->HasSelection()) - editor->SelectWordUnderCursor(); - - findWord = editor->GetSelectedText(); - - // Find all matches to findWord - findReplaceHandler->FindAllMatches(editor,findWord); - position = findReplaceHandler->FindPosition(editor,editor->GetCursorPosition(), true); - count = findReplaceHandler->GetMatches().size(); - findReplaceHandler->SetFindWord(editor,findWord); - requestFocus = true; - updateCount = true; } - - drawFindReplaceDialog(editor, findWord, requestFocus, position, count, updateCount, canReplace); - - ImGui::PopID(); + drawTextEditorGotoLinePopup(editor); } void ViewPatternEditor::drawContent() { @@ -359,11 +343,11 @@ namespace hex::plugin::builtin { m_consoleHoverBox = ImRect(ImVec2(windowPosition.x,windowPosition.y+textEditorSize.y),windowPosition+availableSize); TextEditor::FindReplaceHandler *findReplaceHandler = m_textEditor.GetFindReplaceHandler(); if (m_textEditor.RaiseContextMenu()) { - ImGui::OpenPopup("##pattern_editor_context_menu"); + ImGui::OpenPopup("##text_editor_context_menu"); m_textEditor.ClearRaiseContextMenu(); } - if (ImGui::BeginPopup("##pattern_editor_context_menu")) { + if (ImGui::BeginPopup("##text_editor_context_menu")) { // no shortcut for this if (ImGui::MenuItem("hex.builtin.menu.file.import.pattern_file"_lang, nullptr, false)) m_importPatternFile(); @@ -422,14 +406,19 @@ namespace hex::plugin::builtin { if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.replace_all"_lang, "",false,!findReplaceHandler->GetReplaceWord().empty())) findReplaceHandler->ReplaceAll(&m_textEditor); + if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.goto_line"_lang, Shortcut(ALT + Keys::G).toString().c_str())) + m_openGotoLinePopUp = true; + if (ImGui::IsKeyPressed(ImGuiKey_Escape, false)) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } - if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) { setupFindReplace(editor); + setupGotoLine(editor); + } ImGui::Button("##settings_drag_bar", ImVec2(ImGui::GetContentRegionAvail().x, 2_scaled)); if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, 0)) { @@ -640,20 +629,52 @@ namespace hex::plugin::builtin { } } - void ViewPatternEditor::drawFindReplaceDialog(TextEditor *textEditor, std::string &findWord, bool &requestFocus, u64 &position, u64 &count, bool &updateCount, bool canReplace) { + void ViewPatternEditor::drawTextEditorFindReplacePopup(TextEditor *textEditor) { + ImGuiWindowFlags popupFlags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar; + if (ImGui::BeginPopup("##text_editor_view_find_replace_popup", popupFlags)) { + static std::string findWord; + static bool requestFocus = false; + static u64 position = 0; + static u64 count = 0; + static bool updateCount = false; + static bool canReplace = true; + TextEditor::FindReplaceHandler *findReplaceHandler = textEditor->GetFindReplaceHandler(); + if (ImGui::IsWindowAppearing()) { + findReplaceHandler->resetMatches(); - TextEditor::FindReplaceHandler *findReplaceHandler = textEditor->GetFindReplaceHandler(); + // Use selection as find word if there is one, otherwise use the word under the cursor + if (!textEditor->HasSelection()) + textEditor->SelectWordUnderCursor(); - bool enter = ImGui::IsKeyPressed(ImGuiKey_Enter, false) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter, false); - bool upArrow = ImGui::IsKeyPressed(ImGuiKey_UpArrow, false) || ImGui::IsKeyPressed(ImGuiKey_Keypad8, false); - bool downArrow = ImGui::IsKeyPressed(ImGuiKey_DownArrow, false) || ImGui::IsKeyPressed(ImGuiKey_Keypad2, false); - bool shift = ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift); - bool alt = ImGui::IsKeyDown(ImGuiKey_LeftAlt) || ImGui::IsKeyDown(ImGuiKey_RightAlt); + findWord = textEditor->GetSelectedText(); - if (ImGui::BeginPopup("##pattern_editor_find_replace")) { + // Find all matches to findWord + findReplaceHandler->FindAllMatches(textEditor, findWord); + position = findReplaceHandler->FindPosition(textEditor, textEditor->GetCursorPosition(), true); + count = findReplaceHandler->GetMatches().size(); + findReplaceHandler->SetFindWord(textEditor, findWord); + requestFocus = true; + updateCount = true; + if (m_focusedSubWindowName.contains(consoleView)) + canReplace = false; + else if (m_focusedSubWindowName.contains(textEditorView)) + canReplace = true; + } + bool enter = ImGui::IsKeyPressed(ImGuiKey_Enter, false) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter, false); + bool upArrow = ImGui::IsKeyPressed(ImGuiKey_UpArrow, false) || ImGui::IsKeyPressed(ImGuiKey_Keypad8, false); + bool downArrow = ImGui::IsKeyPressed(ImGuiKey_DownArrow, false) || ImGui::IsKeyPressed(ImGuiKey_Keypad2, false); + bool shift = ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift); + bool alt = ImGui::IsKeyDown(ImGuiKey_LeftAlt) || ImGui::IsKeyDown(ImGuiKey_RightAlt); + std::string childName; + if (m_focusedSubWindowName.contains(consoleView)) + childName = "##console_find_replace"; + else if (m_focusedSubWindowName.contains(textEditorView)) + childName = "##text_editor_find_replace"; + else + return; + ImGui::BeginChild(childName.c_str(), ImVec2(), ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY); - if (ImGui::BeginTable("##pattern_editor_find_replace_table", 2, - ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_BordersInnerH)) { + if (ImGui::BeginTable("##text_editor_find_replace_table", 2, ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_BordersInnerH)) { ImGui::TableNextRow(); ImGui::TableNextColumn(); static bool requestFocusFind = false; @@ -904,9 +925,8 @@ namespace hex::plugin::builtin { requestFocusFind = true; updateCount = true; } - findReplaceHandler->SetFindWindowSize(ImGui::GetWindowSize()); - } else - findReplaceHandler->SetFindWindowSize(ImGui::GetWindowSize()); + } + ImGui::EndTable(); if ((ImGui::IsKeyPressed(ImGuiKey_F3, false)) || downArrowFind || upArrowFind || enterPressedFind) { historyInsert(m_findHistory, m_findHistorySize, m_findHistoryIndex, findWord); @@ -918,14 +938,92 @@ namespace hex::plugin::builtin { requestFocusFind = true; enterPressedFind = false; } - - ImGui::EndTable(); } // Escape key to close the popup - if (ImGui::IsKeyPressed(ImGuiKey_Escape, false)) + if (ImGui::IsKeyPressed(ImGuiKey_Escape, false)) { + m_popupWindowHeight = 0; + m_textEditor.SetTopMarginChanged(0); ImGui::CloseCurrentPopup(); + } + ImGui::EndChild(); + if (m_focusedSubWindowName.contains(textEditorView)) { + if (auto window = ImGui::GetCurrentWindow(); window != nullptr) { + auto height = window->Size.y; + auto heightChange = height - m_popupWindowHeight; + auto heightChangeChange = heightChange - m_popupWindowHeightChange; + if (std::fabs(heightChange) < 0.5 && std::fabs(heightChangeChange) > 1.0) { + m_textEditor.SetTopMarginChanged(height); + } + m_popupWindowHeightChange = heightChange; + m_popupWindowHeight = height; + } + } ImGui::EndPopup(); + m_frPopupIsClosed = false; + } else if (!m_frPopupIsClosed) { + m_frPopupIsClosed = true; + m_popupWindowHeight = 0; + m_textEditor.SetTopMarginChanged(0); + } + } + + void ViewPatternEditor::drawTextEditorGotoLinePopup(TextEditor *textEditor) { + ImGuiWindowFlags popupFlags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar; + if (ImGui::BeginPopup("##text_editor_view_goto_line_popup", popupFlags)) { + std::string childName; + if (m_focusedSubWindowName.contains(consoleView)) + childName = "##console_goto_line"; + else if (m_focusedSubWindowName.contains(textEditorView)) + childName = "##text_editor_goto_line"; + else + return; + ImGui::BeginChild(childName.c_str(), ImVec2(), ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY); + bool enter = ImGui::IsKeyPressed(ImGuiKey_Enter, false) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter, false); + static i32 line = 1; + + std::string hint = "hex.builtin.view.pattern_editor.goto_line"_lang.operator std::string(); + ImGui::TextUnformatted(hint.c_str()); + ImGui::SameLine(); + ImGui::PushItemWidth(ImGui::CalcTextSize("00000000").x); + + if (ImGui::InputInt("###text_editor_goto_line", &line, 0, 0, ImGuiInputTextFlags_CharsDecimal)) { + enter = false; + } + ImGui::SetKeyboardFocusHere(-1); + ImGui::PopItemWidth(); + if (enter) { + ImGui::CloseCurrentPopup(); + if (line < 0) + line = textEditor->GetTotalLines() + line + 1; + line = std::clamp(line, 1, textEditor->GetTotalLines()); + textEditor->JumpToLine(line-1); + } + if (ImGui::IsKeyPressed(ImGuiKey_Escape, false)) { + m_popupWindowHeight = 0; + m_textEditor.SetTopMarginChanged(0); + ImGui::CloseCurrentPopup(); + } + + ImGui::EndChild(); + if (m_focusedSubWindowName.contains(textEditorView)) { + if (auto window = ImGui::GetCurrentWindow(); window != nullptr) { + auto height = window->Size.y; + auto heightChange = height - m_popupWindowHeight; + auto heightChangeChange = heightChange - m_popupWindowHeightChange; + if (std::fabs(heightChange) < 0.5 && std::fabs(heightChangeChange) > 1.0) { + m_textEditor.SetTopMarginChanged(height); + } + m_popupWindowHeightChange = heightChange; + m_popupWindowHeight = height; + } + } + ImGui::EndPopup(); + m_gotoPopupIsClosed = false; + } else if (!m_gotoPopupIsClosed) { + m_gotoPopupIsClosed = true; + m_popupWindowHeight = 0; + m_textEditor.SetTopMarginChanged(0); } } @@ -957,23 +1055,34 @@ namespace hex::plugin::builtin { if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.find_previous"_lang, Shortcut(SHIFT + Keys::F3).toString().c_str(),false,!findReplaceHandler->GetFindWord().empty())) findReplaceHandler->FindMatch(&m_consoleEditor,false); + + ImGui::Separator(); + + if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.goto_line"_lang, Shortcut(ALT + Keys::G).toString().c_str())) + m_openGotoLinePopUp = true; + + ImGui::EndPopup(); } if (m_consoleNeedsUpdate) { std::scoped_lock lock(m_logMutex); - - auto lineCount = m_consoleEditor.GetTextLines().size() - 1; - if (m_console->size() < lineCount) { + bool skipNewLine = false; + auto lineCount = m_consoleEditor.GetTextLines().size(); + if (m_console->size() < lineCount || (lineCount == 1 && m_consoleEditor.GetLineText(0).empty())) { m_consoleEditor.SetText(""); lineCount = 0; + skipNewLine = true; } - m_consoleEditor.SetCursorPosition({ int(lineCount + 1), 0 }); - + m_consoleEditor.JumpToLine(lineCount); const auto linesToAdd = m_console->size() - lineCount; + + for (size_t i = 0; i < linesToAdd; i += 1) { + if (!skipNewLine) + m_consoleEditor.InsertText("\n"); + skipNewLine = false; m_consoleEditor.InsertText(m_console->at(lineCount + i)); - m_consoleEditor.InsertText("\n"); } m_consoleNeedsUpdate = false; @@ -1326,7 +1435,7 @@ namespace hex::plugin::builtin { m_resetDebuggerVariables = false; if (pauseLine.has_value()) - m_textEditor.SetCursorPosition({ int(pauseLine.value() - 1), 0 }); + m_textEditor.JumpToLine(pauseLine.value() - 1); } const auto &currScope = evaluator->getScope(-m_debuggerScopeIndex); @@ -1786,7 +1895,7 @@ namespace hex::plugin::builtin { m_resetDebuggerVariables = true; auto optPauseLine = runtime.getInternals().evaluator->getPauseLine(); if (optPauseLine.has_value()) - m_textEditor.SetCursorPosition({ static_cast(optPauseLine.value())-1, 0 }); + m_textEditor.JumpToLine(optPauseLine.value() - 1); while (*m_breakpointHit) { std::this_thread::sleep_for(std::chrono::milliseconds(100LL)); } @@ -1968,7 +2077,7 @@ namespace hex::plugin::builtin { } void ViewPatternEditor::appendEditorText(const std::string &text) { - m_textEditor.SetCursorPosition(TextEditor::Coordinates { m_textEditor.GetTotalLines(), 0 }); + m_textEditor.JumpToLine(m_textEditor.GetTotalLines()); m_textEditor.InsertText(hex::format("\n{0}", text)); m_triggerEvaluation = true; } @@ -2183,6 +2292,10 @@ namespace hex::plugin::builtin { } }); + ShortcutManager::addShortcut(this, CTRL + Keys::G + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.goto_line", [this] { + m_openGotoLinePopUp = true; + }); + ShortcutManager::addShortcut(this, CTRL + Keys::F + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.find", [this] { m_openFindReplacePopUp = true; m_replaceMode = false;