diff --git a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h index 4e3b50c0f..f6172f080 100644 --- a/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h +++ b/lib/third_party/imgui/ColorTextEditor/include/TextEditor.h @@ -215,6 +215,7 @@ public: FindReplaceHandler *GetFindReplaceHandler() { return &mFindReplaceHandler; } int GetTotalLines() const { return (int)mLines.size(); } bool IsOverwrite() const { return mOverwrite; } + void SetOverwrite(bool aValue) { mOverwrite = aValue; } void SetReadOnly(bool aValue); bool IsReadOnly() const { return mReadOnly; } @@ -268,6 +269,7 @@ public: void Cut(); void Paste(); void Delete(); + int32_t GetPageSize() const; ImVec2 &GetCharAdvance() { return mCharAdvance; } @@ -276,6 +278,10 @@ public: void Undo(int aSteps = 1); void Redo(int aSteps = 1); + void DeleteWordLeft(); + void DeleteWordRight(); + void Backspace(); + static const Palette& GetDarkPalette(); static const Palette& GetLightPalette(); static const Palette& GetRetroBluePalette(); @@ -400,7 +406,6 @@ private: void ColorizeInternal(); float TextDistanceToLineStart(const Coordinates& aFrom) const; void EnsureCursorVisible(); - int GetPageSize() const; std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const; Coordinates GetActualCursorCoordinates() const; Coordinates SanitizeCoordinates(const Coordinates& aValue) const; @@ -425,7 +430,6 @@ private: void RemoveLine(int aIndex); Line& InsertLine(int aIndex); void EnterCharacter(ImWchar aChar, bool aShift); - void Backspace(); void DeleteSelection(); std::string GetWordUnderCursor() const; std::string GetWordAt(const Coordinates& aCoords) const; diff --git a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp index 413f4f145..0d3c714e2 100644 --- a/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp +++ b/lib/third_party/imgui/ColorTextEditor/source/TextEditor.cpp @@ -304,6 +304,22 @@ TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2 &aPositi return SanitizeCoordinates(Coordinates(lineNo, columnCoord)); } +void TextEditor::DeleteWordLeft() { + const auto wordEnd = GetCursorPosition(); + MoveLeft(); + const auto wordStart = FindWordStart(GetCursorPosition()); + SetSelection(wordStart, wordEnd); + Backspace(); +} + +void TextEditor::DeleteWordRight() { + const auto wordStart = GetCursorPosition(); + MoveRight(); + const auto wordEnd = FindWordEnd(GetCursorPosition()); + SetSelection(wordStart, wordEnd); + Backspace(); +} + bool isWordChar(char c) { auto asUChar = static_cast(c); return std::isalnum(asUChar) || c == '_' || asUChar > 0x7F; @@ -640,21 +656,21 @@ void TextEditor::HandleKeyboardInputs() { // command => Ctrl // control => Super // option => Alt - + auto ctrl = io.KeyCtrl; + auto alt = io.KeyAlt; auto shift = io.KeyShift; - auto left = ImGui::IsKeyPressed(ImGuiKey_LeftArrow); + /* auto left = ImGui::IsKeyPressed(ImGuiKey_LeftArrow); auto right = ImGui::IsKeyPressed(ImGuiKey_RightArrow); auto up = ImGui::IsKeyPressed(ImGuiKey_UpArrow); auto down = ImGui::IsKeyPressed(ImGuiKey_DownArrow); - auto ctrl = io.KeyCtrl; - auto alt = io.KeyAlt; + auto home = io.ConfigMacOSXBehaviors ? io.KeySuper && left : ImGui::IsKeyPressed(ImGuiKey_Home); auto end = io.ConfigMacOSXBehaviors ? io.KeySuper && right : ImGui::IsKeyPressed(ImGuiKey_End); auto top = io.ConfigMacOSXBehaviors ? io.KeySuper && up : ctrl && ImGui::IsKeyPressed(ImGuiKey_Home); auto bottom = io.ConfigMacOSXBehaviors ? io.KeySuper && down : ctrl && ImGui::IsKeyPressed(ImGuiKey_End); auto pageUp = io.ConfigMacOSXBehaviors ? ctrl && up : ImGui::IsKeyPressed(ImGuiKey_PageUp); auto pageDown = io.ConfigMacOSXBehaviors ? ctrl && down : ImGui::IsKeyPressed(ImGuiKey_PageDown); - +*/ if (ImGui::IsWindowFocused()) { if (ImGui::IsWindowHovered()) ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput); @@ -663,87 +679,10 @@ void TextEditor::HandleKeyboardInputs() { io.WantCaptureKeyboard = true; io.WantTextInput = true; - bool handledKeyEvent = true; - - if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Z)) - Undo(); - else if (!IsReadOnly() && !ctrl && !shift && alt && ImGui::IsKeyPressed(ImGuiKey_Backspace)) - Undo(); - else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Y)) - Redo(); - else if (!ctrl && !alt && up) - MoveUp(1, shift); - else if (!ctrl && !alt && down) - MoveDown(1, shift); - else if (!alt && left) - MoveLeft(1, shift, ctrl); - else if (!alt && right) - MoveRight(1, shift, ctrl); - else if (!alt && pageUp) - MoveUp(GetPageSize() - 4, shift); - else if (!alt && pageDown) - MoveDown(GetPageSize() - 4, shift); - else if (!alt && top) - MoveTop(shift); - else if (!alt && bottom) - MoveBottom(shift); - else if (!ctrl && !alt && home) - MoveHome(shift); - else if (!ctrl && !alt && end) - MoveEnd(shift); - else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Delete)) - Delete(); - else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Delete)) { - auto wordStart = GetCursorPosition(); - MoveRight(); - auto wordEnd = FindWordEnd(GetCursorPosition()); - SetSelection(wordStart, wordEnd); - Backspace(); - } - else if (!IsReadOnly() && !ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_Backspace)) - Backspace(); - else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Backspace)) { - auto wordEnd = GetCursorPosition(); - MoveLeft(); - auto wordStart = FindWordStart(GetCursorPosition()); - SetSelection(wordStart, wordEnd); - Backspace(); - } - else if (!ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert)) - mOverwrite = !mOverwrite; - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert)) - Copy(); - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_C)) - Copy(); - else if (!IsReadOnly() && !ctrl && shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert)) - Paste(); - else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_V)) - Paste(); - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_X)) - Cut(); - else if (!ctrl && shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Delete)) - Cut(); - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_A)) - SelectAll(); - else if (!IsReadOnly() && !ctrl && !shift && !alt && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) + if (!IsReadOnly() && !ctrl && !shift && !alt && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) EnterCharacter('\n', false); else if (!IsReadOnly() && !ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_Tab)) EnterCharacter('\t', shift); - else if (!ctrl && !alt && !shift && ImGui::IsKeyPressed(ImGuiKey_F3)) - mFindReplaceHandler.FindMatch(this, true); - else if (!ctrl && !alt && shift && ImGui::IsKeyPressed(ImGuiKey_F3)) - mFindReplaceHandler.FindMatch(this, false); - else if (!ctrl && alt && !shift && ImGui::IsKeyPressed(ImGuiKey_C)) - mFindReplaceHandler.SetMatchCase(this,!mFindReplaceHandler.GetMatchCase()); - else if (!ctrl && alt && !shift && ImGui::IsKeyPressed(ImGuiKey_R)) - mFindReplaceHandler.SetFindRegEx(this,!mFindReplaceHandler.GetFindRegEx()); - else if (!ctrl && alt && !shift && ImGui::IsKeyPressed(ImGuiKey_W)) - mFindReplaceHandler.SetWholeWord(this,!mFindReplaceHandler.GetWholeWord()); - else - handledKeyEvent = false; - - if (handledKeyEvent) - ResetCursorBlinkTime(); if (!IsReadOnly() && !io.InputQueueCharacters.empty()) { for (int i = 0; i < io.InputQueueCharacters.Size; i++) { diff --git a/main/gui/source/window/window.cpp b/main/gui/source/window/window.cpp index 5e27102a8..de4282f6b 100644 --- a/main/gui/source/window/window.cpp +++ b/main/gui/source/window/window.cpp @@ -635,12 +635,12 @@ namespace hex { EventViewOpened::post(view.get()); } - ImGui::End(); - } + // Pass on currently pressed keys to the shortcut handler + for (const auto &key : m_pressedKeys) { + ShortcutManager::process(view.get(), io.KeyCtrl, io.KeyAlt, io.KeyShift, io.KeySuper, focused, key); + } - // Pass on currently pressed keys to the shortcut handler - for (const auto &key : m_pressedKeys) { - ShortcutManager::process(view.get(), io.KeyCtrl, io.KeyAlt, io.KeyShift, io.KeySuper, focused, key); + ImGui::End(); } } } diff --git a/plugins/builtin/include/content/views/view_pattern_editor.hpp b/plugins/builtin/include/content/views/view_pattern_editor.hpp index 751e259ba..f32b04646 100644 --- a/plugins/builtin/include/content/views/view_pattern_editor.hpp +++ b/plugins/builtin/include/content/views/view_pattern_editor.hpp @@ -22,6 +22,15 @@ namespace pl::ptrn { class Pattern; } namespace hex::plugin::builtin { + + constexpr static auto textEditorView = "/Pattern editor_"; + constexpr static auto consoleView = "/##console_"; + constexpr static auto variablesView = "/##env_vars_"; + constexpr static auto settingsView = "/##settings_"; + constexpr static auto sectionsView = "/##sections_table_"; + constexpr static auto virtualFilesView = "/Virtual File Tree_"; + constexpr static auto debuggerView = "/##debugger_"; + class PatternSourceCode { public: const std::string& get(prv::Provider *provider) { @@ -260,9 +269,13 @@ namespace hex::plugin::builtin { u32 m_accessHistoryIndex = 0; bool m_parentHighlightingEnabled = true; bool m_replaceMode = false; + bool m_openFindReplacePopUp = false; std::map m_patternNames; + ImRect m_textEditorHoverBox; + ImRect m_consoleHoverBox; + std::string m_focusedSubWindowName; static inline std::array m_findHistory; static inline u32 m_findHistorySize = 0; @@ -281,7 +294,7 @@ namespace hex::plugin::builtin { void drawPatternTooltip(pl::ptrn::Pattern *pattern); - void drawFindReplaceDialog(std::string &findWord, bool &requestFocus, u64 &position, u64 &count, bool &updateCount); + void drawFindReplaceDialog(TextEditor *textEditor, std::string &findWord, bool &requestFocus, u64 &position, u64 &count, bool &updateCount, bool canReplace); void historyInsert(std::array &history, u32 &size, u32 &index, const std::string &value); @@ -290,6 +303,9 @@ namespace hex::plugin::builtin { void parsePattern(const std::string &code, prv::Provider *provider); void evaluatePattern(const std::string &code, prv::Provider *provider); + TextEditor *getEditorFromFocusedWindow(); + void setupFindReplace(TextEditor *editor); + void registerEvents(); void registerMenuItems(); void registerHandlers(); diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 7364c7368..51e2730c6 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -920,6 +920,7 @@ "hex.builtin.view.pattern_editor.auto": "Auto evaluate", "hex.builtin.view.pattern_editor.breakpoint_hit": "Halted at line {0}", "hex.builtin.view.pattern_editor.console": "Console", + "hex.builtin.view.pattern_editor.console.shortcut.copy": "Copy", "hex.builtin.view.pattern_editor.dangerous_function.desc": "This pattern tried to call a dangerous function.\nAre you sure you want to trust this pattern?", "hex.builtin.view.pattern_editor.dangerous_function.name": "Allow dangerous function?", "hex.builtin.view.pattern_editor.debugger": "Debugger", @@ -958,6 +959,51 @@ "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.find": "Search ...", + "hex.builtin.view.pattern_editor.shortcut.replace": "Replace ...", + "hex.builtin.view.pattern_editor.shortcut.find_next": "Find Next", + "hex.builtin.view.pattern_editor.shortcut.find_previous": "Find Previous", + "hex.builtin.view.pattern_editor.shortcut.match_case_toggle": "Toggle Case Sensitive Search", + "hex.builtin.view.pattern_editor.shortcut.regex_toggle": "Toggle Regular Expression Search/Replace", + "hex.builtin.view.pattern_editor.shortcut.whole_word_toggle": "Toggle Whole Word Search", + "hex.builtin.view.pattern_editor.shortcut.save_project": "Save Project", + "hex.builtin.view.pattern_editor.shortcut.open_project": "Open Project ...", + "hex.builtin.view.pattern_editor.shortcut.save_project_as": "Save Project As ...", + "hex.builtin.view.pattern_editor.shortcut.copy": "Copy Selection to the Clipboard", + "hex.builtin.view.pattern_editor.shortcut.cut": "Copy Selection to the Clipboard and Delete it", + "hex.builtin.view.pattern_editor.shortcut.paste": "Paste Clipboard Contents at the Cursor Position", + "hex.builtin.view.pattern_editor.shortcut.undo": "Undo", + "hex.builtin.view.pattern_editor.shortcut.redo": "Redo", + "hex.builtin.view.pattern_editor.shortcut.toggle_insert": "Toggle Write Over", + "hex.builtin.view.pattern_editor.shortcut.delete": "Delete One Character at the Cursor Position", + "hex.builtin.view.pattern_editor.shortcut.backspace": "Delete One Character to the Left of Cursor", + "hex.builtin.view.pattern_editor.shortcut.select_all": "Select Entire File", + "hex.builtin.view.pattern_editor.shortcut.select_left": "Extend Selection One Character to the Left of the Cursor", + "hex.builtin.view.pattern_editor.shortcut.select_right": "Extend Selection One Character to the Right of the Cursor", + "hex.builtin.view.pattern_editor.shortcut.select_word_left": "Extend Selection One Word to the Left of the Cursor", + "hex.builtin.view.pattern_editor.shortcut.select_word_right": "Extend Selection One Word to the Right of the Cursor", + "hex.builtin.view.pattern_editor.shortcut.select_up": "Extend Selection One Line Up from the Cursor", + "hex.builtin.view.pattern_editor.shortcut.select_down": "Extend Selection One Line Down from the Cursor", + "hex.builtin.view.pattern_editor.shortcut.select_page_up": "Extend Selection One Page Up from the Cursor", + "hex.builtin.view.pattern_editor.shortcut.select_page_down": "Extend Selection One Page Down from the Cursor", + "hex.builtin.view.pattern_editor.shortcut.select_home": "Extend Selection to the Start of the Line", + "hex.builtin.view.pattern_editor.shortcut.select_end": "Extend Selection to the End of the Line", + "hex.builtin.view.pattern_editor.shortcut.select_top": "Extend Selection to the Start of the File", + "hex.builtin.view.pattern_editor.shortcut.select_bottom": "Extend Selection to the End of the File", + "hex.builtin.view.pattern_editor.shortcut.move_left": "Move Cursor One Character to the Left", + "hex.builtin.view.pattern_editor.shortcut.move_right": "Move Cursor One Character to the Right", + "hex.builtin.view.pattern_editor.shortcut.move_word_left": "Move Cursor One Word to the Left", + "hex.builtin.view.pattern_editor.shortcut.move_word_right": "Move Cursor One Word to the Right", + "hex.builtin.view.pattern_editor.shortcut.move_up": "Move Cursor One Line Up", + "hex.builtin.view.pattern_editor.shortcut.move_down": "Move Cursor One Line Down", + "hex.builtin.view.pattern_editor.shortcut.move_page_up": "Move Cursor One Page Up", + "hex.builtin.view.pattern_editor.shortcut.move_page_down": "Move Cursor One Page Down", + "hex.builtin.view.pattern_editor.shortcut.move_home": "Move Cursor to the Start of the Line", + "hex.builtin.view.pattern_editor.shortcut.move_end": "Move Cursor to the End of the Line", + "hex.builtin.view.pattern_editor.shortcut.move_top": "Move Cursor to the Start of the File", + "hex.builtin.view.pattern_editor.shortcut.move_bottom": "Move Cursor to the End of the File", + "hex.builtin.view.pattern_editor.shortcut.delete_word_left": "Delete One Word to the Left of the Cursor", + "hex.builtin.view.pattern_editor.shortcut.delete_word_right": "Delete One Word to the Right of the Cursor", "hex.builtin.view.pattern_editor.shortcut.run_pattern": "Run Pattern", "hex.builtin.view.pattern_editor.shortcut.step_debugger": "Step Debugger", "hex.builtin.view.pattern_editor.shortcut.continue_debugger": "Continue Debugger", diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 7d96a0536..b0b086770 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -134,6 +134,12 @@ namespace hex::plugin::builtin { return langDef; } + int levelId; + + static void loadPatternAsMemoryProvider(const ViewPatternEditor::VirtualFile *file) { + ImHexApi::Provider::add(file->data, wolv::util::toUTF8String(file->path.filename())); + } + static void drawVirtualFileTree(const std::vector &virtualFiles, u32 level = 0) { ImGui::PushID(level + 1); ON_SCOPE_EXIT { ImGui::PopID(); }; @@ -148,13 +154,24 @@ namespace hex::plugin::builtin { ImGui::TableNextColumn(); ImGui::TextUnformatted(ICON_VS_FILE); + ImGui::PushID(levelId); ImGui::SameLine(); ImGui::TreeNodeEx(currSegment.c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen); - if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && ImGui::IsItemHovered()) { - ImHexApi::Provider::add(file->data, wolv::util::toUTF8String(file->path.filename())); + if (ImGui::IsMouseDown(ImGuiMouseButton_Right) && ImGui::IsItemHovered() && !ImGui::IsMouseDragging(ImGuiMouseButton_Right)) { + ImGui::OpenPopup("##virtual_files_context_menu"); } - + if (ImGui::BeginPopup("##virtual_files_context_menu")) { + if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.open_in_new_provider"_lang, nullptr, false)) { + loadPatternAsMemoryProvider(file); + } + ImGui::EndPopup(); + } + if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && ImGui::IsItemHovered()) { + loadPatternAsMemoryProvider(file); + } + ImGui::PopID(); + levelId +=1; continue; } @@ -213,6 +230,101 @@ namespace hex::plugin::builtin { RequestAddVirtualFile::unsubscribe(this); } + 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 (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 }); + + // 2 * 11 spacings in between elements + popupSize.x += style.FramePadding.x * 22.0F; + + // Text input fields are set to 12 characters wide + popupSize.x += ImGui::GetFontSize() * 12.0F; + + // IndexOfCount text + popupSize.x += ImGui::CalcTextSize("2000 of 2000").x + 2.0F; + popupSize.x += scrollbarSize; + + // Position of popup relative to parent window + ImVec2 windowPosForPopup = ImGui::GetWindowPos(); + + // Not the window height but the content height + windowPosForPopup.y += popupSize.y; + + // 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; + } + + if (m_focusedSubWindowName.contains(consoleView)) { + windowPosForPopup.y = m_consoleHoverBox.Min.y; + canReplace = false; + } else if (m_focusedSubWindowName.contains(textEditorView)) + canReplace = true; + + ImGui::SetNextWindowPos(windowPosForPopup); + ImGui::SetNextWindowSize(popupSize); + ImGui::OpenPopup("##pattern_editor_find_replace"); + + 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(); + } + void ViewPatternEditor::drawContent() { auto provider = ImHexApi::Provider::get(); @@ -221,6 +333,7 @@ namespace hex::plugin::builtin { static bool dragging = false; const auto availableSize = ImGui::GetContentRegionAvail(); + const auto windowPosition = ImGui::GetCursorScreenPos(); auto textEditorSize = availableSize; textEditorSize.y *= 3.5 / 5.0; textEditorSize.y -= ImGui::GetTextLineHeightWithSpacing(); @@ -228,15 +341,20 @@ namespace hex::plugin::builtin { 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_textEditorHoverBox = ImRect(windowPosition,windowPosition+textEditorSize); + m_consoleHoverBox = ImRect(ImVec2(windowPosition.x,windowPosition.y+textEditorSize.y),windowPosition+availableSize); TextEditor::FindReplaceHandler *findReplaceHandler = m_textEditor.GetFindReplaceHandler(); - if (ImGui::IsMouseDown(ImGuiMouseButton_Right) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) && !ImGui::IsMouseDragging(ImGuiMouseButton_Right)) { + if (ImGui::IsMouseDown(ImGuiMouseButton_Right) && ImGui::IsMouseHoveringRect(m_textEditorHoverBox.Min,m_textEditorHoverBox.Max) && !ImGui::IsMouseDragging(ImGuiMouseButton_Right)) { ImGui::OpenPopup("##pattern_editor_context_menu"); } - bool clickedMenuFind = false; - bool clickedMenuReplace = false; if (ImGui::BeginPopup("##pattern_editor_context_menu")) { // no shortcut for this if (ImGui::MenuItem("hex.builtin.menu.file.import.pattern_file"_lang, nullptr, false)) @@ -268,8 +386,11 @@ namespace hex::plugin::builtin { ImGui::Separator(); // Search and replace entries - if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.find"_lang, Shortcut(CTRLCMD + Keys::F).toString().c_str(),false,this->m_textEditor.HasSelection())) - clickedMenuFind = true; + if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.find"_lang, Shortcut(CTRLCMD + Keys::F).toString().c_str(),false, m_textEditor.HasSelection())){ + m_replaceMode = false; + m_openFindReplacePopUp = true; + } + if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.find_next"_lang, Shortcut(Keys::F3).toString().c_str(),false,!findReplaceHandler->GetFindWord().empty())) findReplaceHandler->FindMatch(&m_textEditor,true); @@ -277,8 +398,10 @@ 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_textEditor,false); - if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.replace"_lang, Shortcut(CTRLCMD + Keys::H).toString().c_str(),false,!findReplaceHandler->GetReplaceWord().empty())) - clickedMenuReplace = true; + if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.replace"_lang, Shortcut(CTRLCMD + Keys::H).toString().c_str(),false,!findReplaceHandler->GetReplaceWord().empty())) { + m_replaceMode = true; + m_openFindReplacePopUp = true; + } if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.replace_next"_lang,"",false,!findReplaceHandler->GetReplaceWord().empty())) findReplaceHandler->Replace(&m_textEditor,true); @@ -292,119 +415,8 @@ namespace hex::plugin::builtin { ImGui::EndPopup(); } - // Context menu entries that open the find/replace popup - bool openFindPopup = false; - ImGui::PushID(&this->m_textEditor); - if (clickedMenuFind) { - m_replaceMode = false; - openFindPopup = true; - } - if (clickedMenuReplace) { - m_replaceMode = true; - openFindPopup = true; - } - - // shortcuts to open the find/replace popup - if (ImGui::IsItemHovered()) { - if (ImGui::IsKeyPressed(ImGuiKey_F, false) && ImGui::GetIO().KeyCtrl) { - m_replaceMode = false; - openFindPopup = true; - } - - if (ImGui::IsKeyPressed(ImGuiKey_H, false) && ImGui::GetIO().KeyCtrl) { - m_replaceMode = true; - openFindPopup = true; - } - if (ImGui::IsKeyPressed(ImGuiKey_S, false) && ImGui::GetIO().KeyAlt) - hex::plugin::builtin::saveProject(); - if (ImGui::IsKeyPressed(ImGuiKey_O, false) && ImGui::GetIO().KeyAlt) - hex::plugin::builtin::openProject(); - if (ImGui::IsKeyPressed(ImGuiKey_S, false) && ImGui::GetIO().KeyAlt && ImGui::GetIO().KeyShift) - hex::plugin::builtin::saveProjectAs(); - } - - static std::string findWord; - static bool requestFocus = false; - static u64 position = 0; - static u64 count = 0; - static bool updateCount = false; - - if (openFindPopup) { - // 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 = m_textEditor.GetTotalLines() * m_textEditor.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 }); - - // 2 * 11 spacings in between elements - popupSize.x += style.FramePadding.x * 22.0F; - - // Text input fields are set to 12 characters wide - popupSize.x += ImGui::GetFontSize() * 12.0F; - - // IndexOfCount text - popupSize.x += ImGui::CalcTextSize("2000 of 2000").x + 2.0F; - popupSize.x += scrollbarSize; - - // Position of popup relative to parent window - ImVec2 windowPosForPopup = ImGui::GetWindowPos(); - - // Not the window height but the content height - windowPosForPopup.y += popupSize.y; - - // 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; - } - - ImGui::SetNextWindowPos(windowPosForPopup); - ImGui::SetNextWindowSize(popupSize); - ImGui::OpenPopup("##pattern_editor_find_replace"); - - findReplaceHandler->resetMatches(); - - // Use selection as find word if there is one, otherwise use the word under the cursor - if (!this->m_textEditor.HasSelection()) - this->m_textEditor.SelectWordUnderCursor(); - - findWord = this->m_textEditor.GetSelectedText(); - - // Find all matches to findWord - findReplaceHandler->FindAllMatches(&m_textEditor,findWord); - position = findReplaceHandler->FindPosition(&m_textEditor,m_textEditor.GetCursorPosition(), true); - count = findReplaceHandler->GetMatches().size(); - findReplaceHandler->SetFindWord(&m_textEditor,findWord); - requestFocus = true; - updateCount = true; - } - - drawFindReplaceDialog(findWord, requestFocus, position, count, updateCount); - - ImGui::PopID(); + setupFindReplace(getEditorFromFocusedWindow()); ImGui::Button("##settings_drag_bar", ImVec2(ImGui::GetContentRegionAvail().x, 2_scaled)); if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, 0)) { @@ -605,18 +617,9 @@ namespace hex::plugin::builtin { } } - void ViewPatternEditor::drawFindReplaceDialog(std::string &findWord, bool &requestFocus, u64 &position, u64 &count, bool &updateCount) { - static std::string replaceWord; + void ViewPatternEditor::drawFindReplaceDialog(TextEditor *textEditor, std::string &findWord, bool &requestFocus, u64 &position, u64 &count, bool &updateCount, bool canReplace) { - TextEditor::FindReplaceHandler *findReplaceHandler = m_textEditor.GetFindReplaceHandler(); - static bool requestFocusReplace = false; - static bool requestFocusFind = false; - - static bool downArrowFind = false; - static bool upArrowFind = false; - static bool downArrowReplace = false; - static bool upArrowReplace = false; - static bool enterPressedFind = false; + TextEditor::FindReplaceHandler *findReplaceHandler = textEditor->GetFindReplaceHandler(); bool enter = ImGui::IsKeyPressed(ImGuiKey_Enter, false) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter, false); bool upArrow = ImGui::IsKeyPressed(ImGuiKey_UpArrow, false) || ImGui::IsKeyPressed(ImGuiKey_Keypad8, false); @@ -630,9 +633,14 @@ namespace hex::plugin::builtin { ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_BordersInnerH)) { ImGui::TableNextRow(); ImGui::TableNextColumn(); - + static bool requestFocusFind = false; + static bool requestFocusReplace = false; bool oldReplace = m_replaceMode; - ImGuiExt::DimmedIconToggle(ICON_VS_TRIANGLE_DOWN, ICON_VS_TRIANGLE_RIGHT, &m_replaceMode); + + if (canReplace) + ImGuiExt::DimmedIconToggle(ICON_VS_TRIANGLE_DOWN, ICON_VS_TRIANGLE_RIGHT, &m_replaceMode); + else + m_replaceMode = false; if (oldReplace != m_replaceMode) { if (m_replaceMode) requestFocusReplace = true; @@ -659,16 +667,15 @@ namespace hex::plugin::builtin { hint += ICON_BI_DATA_TRANSFER_BOTH; hint += "hex.builtin.view.pattern_editor.find_hint_history"_lang.operator std::string(); } - + static bool enterPressedFind = false; ImGui::PushItemWidth(ImGui::GetFontSize() * 12); if (ImGui::InputTextWithHint("###findInputTextWidget", hint.c_str(), findWord, findFlags) || enter ) { - if (enter) { + if (enter) enterPressedFind = true; - enter = false; - } + updateCount = true; requestFocusFind = true; - findReplaceHandler->SetFindWord(&m_textEditor,findWord); + findReplaceHandler->SetFindWord(textEditor,findWord); } if ((!m_replaceMode && requestFocus) || requestFocusFind) { @@ -686,8 +693,8 @@ namespace hex::plugin::builtin { m_findHistoryIndex = (m_findHistoryIndex + 1) % m_findHistorySize; findWord = m_findHistory[m_findHistoryIndex]; - findReplaceHandler->SetFindWord(&m_textEditor,findWord); - position = findReplaceHandler->FindPosition(&m_textEditor,m_textEditor.GetCursorPosition(), true); + findReplaceHandler->SetFindWord(textEditor,findWord); + position = findReplaceHandler->FindPosition(textEditor,textEditor->GetCursorPosition(), true); count = findReplaceHandler->GetMatches().size(); updateCount = true; requestFocusFind = true; @@ -703,8 +710,8 @@ namespace hex::plugin::builtin { if (altCPressed || ImGuiExt::DimmedIconToggle(ICON_VS_CASE_SENSITIVE, &matchCase)) { if (altCPressed) matchCase = !matchCase; - findReplaceHandler->SetMatchCase(&m_textEditor,matchCase); - position = findReplaceHandler->FindPosition(&m_textEditor,m_textEditor.GetCursorPosition(), true); + findReplaceHandler->SetMatchCase(textEditor,matchCase); + position = findReplaceHandler->FindPosition(textEditor,textEditor->GetCursorPosition(), true); count = findReplaceHandler->GetMatches().size(); updateCount = true; requestFocusFind = true; @@ -719,8 +726,8 @@ namespace hex::plugin::builtin { if (altWPressed || ImGuiExt::DimmedIconToggle(ICON_VS_WHOLE_WORD, &matchWholeWord)) { if (altWPressed) matchWholeWord = !matchWholeWord; - findReplaceHandler->SetWholeWord(&m_textEditor,matchWholeWord); - position = findReplaceHandler->FindPosition(&m_textEditor,m_textEditor.GetCursorPosition(), true); + findReplaceHandler->SetWholeWord(textEditor,matchWholeWord); + position = findReplaceHandler->FindPosition(textEditor,textEditor->GetCursorPosition(), true); count = findReplaceHandler->GetMatches().size(); updateCount = true; requestFocusFind = true; @@ -735,8 +742,8 @@ namespace hex::plugin::builtin { if (altRPressed || ImGuiExt::DimmedIconToggle(ICON_VS_REGEX, &useRegex)) { if (altRPressed) useRegex = !useRegex; - findReplaceHandler->SetFindRegEx(&m_textEditor,useRegex); - position = findReplaceHandler->FindPosition(&m_textEditor,m_textEditor.GetCursorPosition(), true); + findReplaceHandler->SetFindRegEx(textEditor,useRegex); + position = findReplaceHandler->FindPosition(textEditor,textEditor->GetCursorPosition(), true); count = findReplaceHandler->GetMatches().size(); updateCount = true; requestFocusFind = true; @@ -777,10 +784,12 @@ namespace hex::plugin::builtin { ImGui::InvisibleButton("##find_result_padding", buttonSize); ImGui::SameLine(); + static bool downArrowFind = false; if (ImGuiExt::IconButton(ICON_VS_ARROW_DOWN, ImVec4(1, 1, 1, 1))) downArrowFind = true; ImGui::SameLine(); + static bool upArrowFind = false; if (ImGuiExt::IconButton(ICON_VS_ARROW_UP, ImVec4(1, 1, 1, 1))) upArrowFind = true; @@ -803,11 +812,14 @@ namespace hex::plugin::builtin { } ImGui::PushItemWidth(ImGui::GetFontSize() * 12); + static std::string replaceWord; + static bool downArrowReplace = false; + static bool upArrowReplace = false; if (ImGui::InputTextWithHint("##replaceInputTextWidget", hint.c_str(), replaceWord, replaceFlags) || downArrowReplace || upArrowReplace) { findReplaceHandler->SetReplaceWord(replaceWord); historyInsert(m_replaceHistory, m_replaceHistorySize, m_replaceHistoryIndex, replaceWord); - bool textReplaced = findReplaceHandler->Replace(&m_textEditor,!shift && !upArrowReplace); + bool textReplaced = findReplaceHandler->Replace(textEditor,!shift && !upArrowReplace); if (textReplaced) { if (count > 0) { if (position == count) @@ -863,7 +875,7 @@ namespace hex::plugin::builtin { if (ImGuiExt::IconButton(ICON_VS_REPLACE_ALL, ImVec4(1, 1, 1, 1))) { findReplaceHandler->SetReplaceWord(replaceWord); historyInsert(m_replaceHistory,m_replaceHistorySize, m_replaceHistoryIndex, replaceWord); - findReplaceHandler->ReplaceAll(&m_textEditor); + findReplaceHandler->ReplaceAll(textEditor); count = 0; position = 0; requestFocusFind = true; @@ -875,7 +887,7 @@ namespace hex::plugin::builtin { if ((ImGui::IsKeyPressed(ImGuiKey_F3, false)) || downArrowFind || upArrowFind || enterPressedFind) { historyInsert(m_findHistory, m_findHistorySize, m_findHistoryIndex, findWord); - position = findReplaceHandler->FindMatch(&m_textEditor,!shift && !upArrowFind); + position = findReplaceHandler->FindMatch(textEditor,!shift && !upArrowFind); count = findReplaceHandler->GetMatches().size(); updateCount = true; downArrowFind = false; @@ -895,6 +907,32 @@ namespace hex::plugin::builtin { } void ViewPatternEditor::drawConsole(ImVec2 size) { + auto findReplaceHandler = m_consoleEditor.GetFindReplaceHandler(); + if (ImGui::IsMouseDown(ImGuiMouseButton_Right) && ImGui::IsMouseHoveringRect(m_consoleHoverBox.Min,m_consoleHoverBox.Max) && !ImGui::IsMouseDragging(ImGuiMouseButton_Right)) { + ImGui::OpenPopup("##console_context_menu"); + } + const bool hasSelection = m_consoleEditor.HasSelection(); + if (ImGui::BeginPopup("##console_context_menu")) { + if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.copy"_lang, Shortcut(CTRLCMD + Keys::C).toString().c_str(), false, hasSelection)) { + m_consoleEditor.Copy(); + } + if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.select_all"_lang, Shortcut(CTRLCMD + Keys::A).toString().c_str())) { + m_consoleEditor.SelectAll(); + } + ImGui::Separator(); + // Search and replace entries + if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.find"_lang, Shortcut(CTRLCMD + Keys::F).toString().c_str(),false, hasSelection)) { + m_openFindReplacePopUp = true; + m_replaceMode = false; + } + + if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.find_next"_lang, Shortcut(Keys::F3).toString().c_str(),false,!findReplaceHandler->GetFindWord().empty())) + findReplaceHandler->FindMatch(&m_consoleEditor,true); + + 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::EndPopup(); + } if (m_consoleNeedsUpdate) { std::scoped_lock lock(m_logMutex); @@ -1175,7 +1213,7 @@ namespace hex::plugin::builtin { file.writeVector(runtime.getSection(id)); }); } - ImGui::SetItemTooltip("%s", (const char*)"hex.builtin.view.pattern_editor.sections.export"_lang.get()); + ImGui::SetItemTooltip("%s", "hex.builtin.view.pattern_editor.sections.export"_lang.get()); ImGui::PopID(); } @@ -1193,7 +1231,7 @@ namespace hex::plugin::builtin { if (ImGui::BeginTable("Virtual File Tree", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, size)) { ImGui::TableSetupColumn("##path", ImGuiTableColumnFlags_WidthStretch); - + levelId = 1; drawVirtualFileTree(virtualFilePointers); ImGui::EndTable(); @@ -1203,9 +1241,10 @@ namespace hex::plugin::builtin { void ViewPatternEditor::drawDebugger(ImVec2 size) { const auto &runtime = ContentRegistry::PatternLanguage::getRuntime(); - auto &evaluator = runtime.getInternals().evaluator; + if (ImGui::BeginChild("##debugger", size, true)) { + auto &evaluator = runtime.getInternals().evaluator; const auto &breakpoints = evaluator->getBreakpoints(); const auto line = m_textEditor.GetCursorPosition().mLine + 1; @@ -1372,8 +1411,8 @@ namespace hex::plugin::builtin { const auto end = value.find(']'); if (end == std::string::npos) return std::nullopt; - - value = value.substr(0, end); + value.resize(end); + //value = value.substr(0, end); value = wolv::util::trim(value); return BinaryPattern(value); @@ -1851,6 +1890,16 @@ namespace hex::plugin::builtin { appendEditorText(hex::format("{0} {0}_array_at_0x{1:02X}[0x{2:02X}] @ 0x{1:02X};", type, selection->getStartAddress(), (selection->getSize() + (size - 1)) / size)); } + TextEditor *ViewPatternEditor::getEditorFromFocusedWindow() { + if (m_focusedSubWindowName.contains(consoleView)) { + return &m_consoleEditor; + } + if (m_focusedSubWindowName.contains(textEditorView)) { + return &m_textEditor; + } + return nullptr; + } + void ViewPatternEditor::registerMenuItems() { /* Import Pattern */ ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.import", "hex.builtin.menu.file.import.pattern" }, ICON_VS_FILE_CODE, 4050, Shortcut::None, @@ -2037,6 +2086,257 @@ namespace hex::plugin::builtin { } }); + ShortcutManager::addShortcut(this, CTRL + Keys::F + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.find", [this] { + m_openFindReplacePopUp = true; + m_replaceMode = false; + }); + + ShortcutManager::addShortcut(this, CTRL + Keys::H + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.replace", [this] { + if (m_focusedSubWindowName.contains(textEditorView)) { + m_openFindReplacePopUp = true; + m_replaceMode = true; + } + }); + + ShortcutManager::addShortcut(this, Keys::F3 + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.find_next", [this] { + + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) { + TextEditor::FindReplaceHandler *findReplaceHandler = editor->GetFindReplaceHandler(); + findReplaceHandler->FindMatch(editor, true); + } + }); + + ShortcutManager::addShortcut(this, SHIFT + Keys::F3 + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.find_previous", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) { + TextEditor::FindReplaceHandler *findReplaceHandler = editor->GetFindReplaceHandler(); + findReplaceHandler->FindMatch(editor, false); + } + }); + + ShortcutManager::addShortcut(this, ALT + Keys::C + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.match_case_toggle", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) { + TextEditor::FindReplaceHandler *findReplaceHandler = editor->GetFindReplaceHandler(); + findReplaceHandler->SetMatchCase(editor, !findReplaceHandler->GetMatchCase()); + } + }); + + ShortcutManager::addShortcut(this, ALT + Keys::R + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.regex_toggle", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) { + TextEditor::FindReplaceHandler *findReplaceHandler = editor->GetFindReplaceHandler(); + findReplaceHandler->SetFindRegEx(editor, !findReplaceHandler->GetFindRegEx()); + } + }); + + ShortcutManager::addShortcut(this, ALT + Keys::W + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.whole_word_toggle", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) { + TextEditor::FindReplaceHandler *findReplaceHandler = editor->GetFindReplaceHandler(); + findReplaceHandler->SetWholeWord(editor, !findReplaceHandler->GetWholeWord()); + } + }); + + ShortcutManager::addShortcut(this, ALT + Keys::S + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.save_project", [] { + hex::plugin::builtin::saveProject(); + }); + + ShortcutManager::addShortcut(this, ALT + Keys::O + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.open_project", [] { + hex::plugin::builtin::openProject(); + }); + + ShortcutManager::addShortcut(this, ALT + SHIFT + Keys::S + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.save_project_as", [] { + hex::plugin::builtin::saveProjectAs(); + }); + + // ShortcutManager::addShortcut(this, CTRL + Keys::Insert + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.copy", [this] { + // m_textEditor.Copy(); + // }); + + ShortcutManager::addShortcut(this, CTRL + Keys::C + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.copy", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->Copy(); + }); + + // ShortcutManager::addShortcut(this, SHIFT + Keys::Insert + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.paste", [this] { + // m_textEditor.Paste(); + // }); + + ShortcutManager::addShortcut(this, CTRL + Keys::V + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.paste", [this] { + if (m_focusedSubWindowName.contains(textEditorView)) + m_textEditor.Paste(); + }); + + ShortcutManager::addShortcut(this, CTRL + Keys::X + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.cut", [this] { + if (m_focusedSubWindowName.contains(textEditorView)) + m_textEditor.Cut(); + }); + + // ShortcutManager::addShortcut(this, SHIFT + Keys::Delete + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.cut", [this] { + // m_textEditor.Cut(); + // }); + + ShortcutManager::addShortcut(this, CTRL + Keys::Z + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.undo", [this] { + if (m_focusedSubWindowName.contains(textEditorView)) + m_textEditor.Undo(); + }); + + // ShortcutManager::addShortcut(this, ALT + Keys::Backspace + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.undo", [this] { + // m_textEditor.Undo(); + // }); + + ShortcutManager::addShortcut(this, Keys::Delete + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.delete", [this] { + if (m_focusedSubWindowName.contains(textEditorView)) + m_textEditor.Delete(); + }); + + ShortcutManager::addShortcut(this, CTRL + Keys::Y + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.redo", [this] { + if (m_focusedSubWindowName.contains(textEditorView)) + m_textEditor.Redo(); + }); + + ShortcutManager::addShortcut(this, CTRL + Keys::A + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.select_all", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->SelectAll(); + }); + + ShortcutManager::addShortcut(this, SHIFT + Keys::Right + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.select_right", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveRight(1, true, false); + }); + + ShortcutManager::addShortcut(this, CTRL + SHIFT + Keys::Right + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.select_word_right", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveRight(1, true, true); + }); + + ShortcutManager::addShortcut(this, SHIFT + Keys::Left + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.select_left", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveLeft(1, true, false); + }); + + ShortcutManager::addShortcut(this, CTRL + SHIFT + Keys::Left + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.select_word_left", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveLeft(1, true, true); + }); + + ShortcutManager::addShortcut(this, SHIFT + Keys::Up + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.select_up", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveUp(1, true); + }); + + ShortcutManager::addShortcut(this, SHIFT +Keys::PageUp + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.select_page_up", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveUp(editor->GetPageSize()-4, true); + }); + + ShortcutManager::addShortcut(this, SHIFT + Keys::Down + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.select_down", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveDown(1, true); + }); + + ShortcutManager::addShortcut(this, SHIFT +Keys::PageDown + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.select_page_down", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveDown(editor->GetPageSize()-4, true); + }); + + ShortcutManager::addShortcut(this, CTRL + SHIFT + Keys::Home + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.select_top", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveTop(true); + }); + + ShortcutManager::addShortcut(this, CTRL + SHIFT + Keys::End + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.select_bottom", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveBottom(true); + }); + + ShortcutManager::addShortcut(this, SHIFT + Keys::Home + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.select_home", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveHome(true); + }); + + ShortcutManager::addShortcut(this, SHIFT + Keys::End + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.select_end", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveEnd(true); + }); + + ShortcutManager::addShortcut(this, CTRL + Keys::Delete + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.delete_word_right", [this] { + if (m_focusedSubWindowName.contains(textEditorView)) + m_textEditor.DeleteWordRight(); + }); + + ShortcutManager::addShortcut(this, CTRL + Keys::Backspace + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.delete_word_left", [this] { + if (m_focusedSubWindowName.contains(textEditorView)) + m_textEditor.DeleteWordLeft(); + }); + + ShortcutManager::addShortcut(this, Keys::Backspace + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.backspace", [this] { + if (m_focusedSubWindowName.contains(textEditorView)) + m_textEditor.Backspace(); + }); + + ShortcutManager::addShortcut(this, Keys::Insert + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.toggle_insert", [this] { + if (m_focusedSubWindowName.contains(textEditorView)) + m_textEditor.SetOverwrite(!m_textEditor.IsOverwrite()); + }); + + ShortcutManager::addShortcut(this, CTRL + Keys::Right + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.move_word_right", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveRight(1, false, true); + }); + + ShortcutManager::addShortcut(this, Keys::Right + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.move_right", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveRight(1, false, false); + }); + + ShortcutManager::addShortcut(this, CTRL + Keys::Left + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.move_word_left", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveLeft(1, false, true); + }); + + ShortcutManager::addShortcut(this, Keys::Left + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.move_left", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveLeft(1, false, false); + }); + + ShortcutManager::addShortcut(this, Keys::Up + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.move_up", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveUp(1, false); + }); + + ShortcutManager::addShortcut(this, Keys::PageUp + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.move_page_up", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveUp(editor->GetPageSize()-4, false); + }); + + ShortcutManager::addShortcut(this, Keys::Down + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.move_down", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveDown(1, false); + }); + + ShortcutManager::addShortcut(this, Keys::PageDown + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.move_page_down", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveDown(editor->GetPageSize()-4, false); + }); + + ShortcutManager::addShortcut(this, CTRL + Keys::Home + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.move_top", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveTop(false); + }); + + ShortcutManager::addShortcut(this, CTRL + Keys::End + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.move_bottom", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveBottom(false); + }); + + ShortcutManager::addShortcut(this, Keys::Home + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.move_home", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveHome(false); + }); + + ShortcutManager::addShortcut(this, Keys::End + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.move_end", [this] { + if (auto editor = getEditorFromFocusedWindow(); editor != nullptr) + editor->MoveEnd(false); + }); + ShortcutManager::addShortcut(this, Keys::F8 + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.add_breakpoint", [this] { const auto line = m_textEditor.GetCursorPosition().mLine + 1; const auto &runtime = ContentRegistry::PatternLanguage::getRuntime();