1
0
mirror of synced 2025-01-10 21:41:53 +01:00

feat: Added goto line popup to text editors (#1984)

Since the popup is fairly small I opted for a straight addition parallel
to the find/replace. To make code more clear the functions that create
each popup were coalesced and made their interface simpler. That forced
a reorganization of the data processing which translates to a larger
number of changes than usual. Most of those changes are just moving some
action from one function to another.

The old method to identify popups using the size and position of the
window was dropped in favor of one based on child windows and using
their names for a much easier and robust identification.

Added specialized functions to text editor to jump to a line or to given
coordinates with a simple interface that simplifies older code that
performed the same task.

Because this PR modifies heavily the same code as the previous PR (1983)
it is also included here to make merging easier.

---------

Co-authored-by: Nik <werwolv98@gmail.com>
This commit is contained in:
paxcut 2024-12-14 08:50:45 -07:00 committed by GitHub
parent 093310a9e5
commit 8d123da847
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 245 additions and 134 deletions

View File

@ -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;

View File

@ -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);
}

View File

@ -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<std::fs::path, std::string> 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<std::string,256> 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<std::string, 256> &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();

View File

@ -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",

View File

@ -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<int>(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;