1
0
mirror of synced 2025-02-17 18:59:21 +01:00

impr: Various fixes and an enhancement for the pattern editor (#1528)

Fixed console error messages using doc comment syntax highlights. Fixed
results of find not updating when march case was toggled. Fixed syntax
highlights of nested ifdefs. Fixed editor cursor blinks if OS focus goes
to another window. Fixed Highlights of "\\\"" was incorrectly handled.

---------

Co-authored-by: Nik <werwolv98@gmail.com>
This commit is contained in:
paxcut 2024-03-21 05:58:20 -07:00 committed by GitHub
parent f5987fde5a
commit 3b3701135f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 126 additions and 93 deletions

View File

@ -175,7 +175,7 @@ public:
bool mCaseSensitive; bool mCaseSensitive;
LanguageDefinition() LanguageDefinition()
: mGlobalDocComment("/*!"), mDocComment("/**"), mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true) : mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true)
{ {
} }

View File

@ -604,7 +604,7 @@ ImU32 TextEditor::GetGlyphColor(const Glyph &aGlyph) const {
return mPalette[(int)PaletteIndex::Comment]; return mPalette[(int)PaletteIndex::Comment];
if (aGlyph.mMultiLineComment) if (aGlyph.mMultiLineComment)
return mPalette[(int)PaletteIndex::MultiLineComment]; return mPalette[(int)PaletteIndex::MultiLineComment];
if (aGlyph.mDeactivated && !aGlyph.mPreprocessor) if (aGlyph.mDeactivated)
return mPalette[(int)PaletteIndex::PreprocessorDeactivated]; return mPalette[(int)PaletteIndex::PreprocessorDeactivated];
auto const color = mPalette[(int)aGlyph.mColorIndex]; auto const color = mPalette[(int)aGlyph.mColorIndex];
if (aGlyph.mPreprocessor) { if (aGlyph.mPreprocessor) {
@ -621,8 +621,18 @@ ImU32 TextEditor::GetGlyphColor(const Glyph &aGlyph) const {
void TextEditor::HandleKeyboardInputs() { void TextEditor::HandleKeyboardInputs() {
ImGuiIO &io = ImGui::GetIO(); ImGuiIO &io = ImGui::GetIO();
auto shift = io.KeyShift; auto shift = io.KeyShift;
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; auto left = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow));
auto right = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow));
auto up = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow));
auto down = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow));
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl;
auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
auto home = io.ConfigMacOSXBehaviors ? io.KeySuper && left : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home));
auto end = io.ConfigMacOSXBehaviors ? io.KeySuper && right : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End));
auto top = io.ConfigMacOSXBehaviors ? io.KeySuper && up : ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home));
auto bottom = io.ConfigMacOSXBehaviors ? io.KeySuper && down : ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End));
auto pageUp = io.ConfigMacOSXBehaviors ? ctrl && up : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp));
auto pageDown = io.ConfigMacOSXBehaviors ? ctrl && down : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown));
if (ImGui::IsWindowFocused()) { if (ImGui::IsWindowFocused()) {
if (ImGui::IsWindowHovered()) if (ImGui::IsWindowHovered())
@ -638,25 +648,25 @@ void TextEditor::HandleKeyboardInputs() {
Undo(); Undo();
else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y))) else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y)))
Redo(); Redo();
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow))) else if (!ctrl && !alt && up)
MoveUp(1, shift); MoveUp(1, shift);
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow))) else if (!ctrl && !alt && down)
MoveDown(1, shift); MoveDown(1, shift);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow))) else if (!alt && left)
MoveLeft(1, shift, ctrl); MoveLeft(1, shift, ctrl);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow))) else if (!alt && right)
MoveRight(1, shift, ctrl); MoveRight(1, shift, ctrl);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp))) else if (!alt && pageUp)
MoveUp(GetPageSize() - 4, shift); MoveUp(GetPageSize() - 4, shift);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown))) else if (!alt && pageDown)
MoveDown(GetPageSize() - 4, shift); MoveDown(GetPageSize() - 4, shift);
else if (!alt && ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home))) else if (!alt && top)
MoveTop(shift); MoveTop(shift);
else if (ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End))) else if (!alt && bottom)
MoveBottom(shift); MoveBottom(shift);
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home))) else if (!ctrl && !alt && home)
MoveHome(shift); MoveHome(shift);
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End))) else if (!ctrl && !alt && end)
MoveEnd(shift); MoveEnd(shift);
else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)))
Delete(); Delete();
@ -722,7 +732,7 @@ void TextEditor::HandleKeyboardInputs() {
void TextEditor::HandleMouseInputs() { void TextEditor::HandleMouseInputs() {
ImGuiIO &io = ImGui::GetIO(); ImGuiIO &io = ImGui::GetIO();
auto shift = io.KeyShift; auto shift = io.KeyShift;
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; auto ctrl = io.ConfigMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl;
auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
if (ImGui::IsWindowHovered()) { if (ImGui::IsWindowHovered()) {
@ -905,7 +915,10 @@ void TextEditor::Render() {
} }
if (mState.mCursorPosition.mLine == lineNo && mShowCursor) { if (mState.mCursorPosition.mLine == lineNo && mShowCursor) {
auto focused = ImGui::IsWindowFocused(); bool focused = false;
ImGuiViewport *viewport = ImGui::GetWindowViewport();
if (viewport->PlatformUserData != NULL && ImGui::GetPlatformIO().Platform_GetWindowFocus(viewport))
focused = ImGui::IsWindowFocused();
// Highlight the current line (where the cursor is) // Highlight the current line (where the cursor is)
if (!HasSelection()) { if (!HasSelection()) {
@ -2505,6 +2518,7 @@ void TextEditor::ColorizeInternal() {
auto firstChar = true; // there is no other non-whitespace characters in the line before auto firstChar = true; // there is no other non-whitespace characters in the line before
auto currentLine = 0; auto currentLine = 0;
auto currentIndex = 0; auto currentIndex = 0;
auto commentLength = 0;
auto &startStr = mLanguageDefinition.mCommentStart; auto &startStr = mLanguageDefinition.mCommentStart;
auto &singleStartStr = mLanguageDefinition.mSingleLineComment; auto &singleStartStr = mLanguageDefinition.mSingleLineComment;
auto &docStartStr = mLanguageDefinition.mDocComment; auto &docStartStr = mLanguageDefinition.mDocComment;
@ -2516,6 +2530,14 @@ void TextEditor::ColorizeInternal() {
while (currentLine < endLine || currentIndex < endIndex) { while (currentLine < endLine || currentIndex < endIndex) {
auto &line = mLines[currentLine]; auto &line = mLines[currentLine];
auto setGlyphFlags = [&](int index) {
line[index].mMultiLineComment = withinComment;
line[index].mComment = withinSingleLineComment;
line[index].mDocComment = withinDocComment;
line[index].mGlobalDocComment = withinGlobalDocComment;
line[index].mDeactivated = withinNotDef;
};
if (currentIndex == 0) { if (currentIndex == 0) {
withinSingleLineComment = false; withinSingleLineComment = false;
withinPreproc = false; withinPreproc = false;
@ -2532,24 +2554,12 @@ void TextEditor::ColorizeInternal() {
bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));
if (withinString) { if (withinString) {
line[currentIndex].mMultiLineComment = withinComment; setGlyphFlags(currentIndex);
line[currentIndex].mComment = withinSingleLineComment; if (c == '\\') {
line[currentIndex].mDocComment = withinDocComment; currentIndex++;
line[currentIndex].mGlobalDocComment = withinGlobalDocComment; setGlyphFlags(currentIndex);
line[currentIndex].mDeactivated = withinNotDef; } else if (c == '\"')
if (c == '\"') {
if (currentIndex > 2 && line[currentIndex - 1].mChar == '\\' && line[currentIndex - 2].mChar != '\\') {
currentIndex += 1;
if (currentIndex < (int)line.size()) {
line[currentIndex].mMultiLineComment = withinComment;
line[currentIndex].mComment = withinSingleLineComment;
line[currentIndex].mDocComment = withinDocComment;
line[currentIndex].mGlobalDocComment = withinGlobalDocComment;
line[currentIndex].mDeactivated = withinNotDef;
}
} else
withinString = false; withinString = false;
}
} else { } else {
if (firstChar && c == mLanguageDefinition.mPreprocChar) { if (firstChar && c == mLanguageDefinition.mPreprocChar) {
withinPreproc = true; withinPreproc = true;
@ -2594,7 +2604,6 @@ void TextEditor::ColorizeInternal() {
} }
if (!withinNotDef) { if (!withinNotDef) {
bool isConditionMet = std::find(mDefines.begin(),mDefines.end(),identifier) != mDefines.end(); bool isConditionMet = std::find(mDefines.begin(),mDefines.end(),identifier) != mDefines.end();
withinNotDef = !isConditionMet;
ifDefs.push_back(isConditionMet); ifDefs.push_back(isConditionMet);
} else } else
ifDefs.push_back(false); ifDefs.push_back(false);
@ -2608,7 +2617,6 @@ void TextEditor::ColorizeInternal() {
} }
if (!withinNotDef) { if (!withinNotDef) {
bool isConditionMet = std::find(mDefines.begin(),mDefines.end(),identifier) == mDefines.end(); bool isConditionMet = std::find(mDefines.begin(),mDefines.end(),identifier) == mDefines.end();
withinNotDef = !isConditionMet;
ifDefs.push_back(isConditionMet); ifDefs.push_back(isConditionMet);
} else } else
ifDefs.push_back(false); ifDefs.push_back(false);
@ -2616,20 +2624,17 @@ void TextEditor::ColorizeInternal() {
} }
} else { } else {
if (directive == "endif") { if (directive == "endif") {
if (ifDefs.size() > 1) if (ifDefs.size() > 1) {
ifDefs.pop_back(); ifDefs.pop_back();
withinNotDef = !ifDefs.back(); withinNotDef = !ifDefs.back();
} }
} }
} }
}
if (c == '\"') { if (c == '\"') {
withinString = true; withinString = true;
line[currentIndex].mMultiLineComment = withinComment; setGlyphFlags(currentIndex);
line[currentIndex].mComment = withinSingleLineComment;
line[currentIndex].mDocComment = withinDocComment;
line[currentIndex].mGlobalDocComment = withinGlobalDocComment;
line[currentIndex].mDeactivated = withinNotDef;
} else { } else {
auto pred = [](const char &a, const Glyph &b) { return a == b.mChar; }; auto pred = [](const char &a, const Glyph &b) { return a == b.mChar; };
@ -2653,29 +2658,30 @@ void TextEditor::ColorizeInternal() {
if (isGlobalDocComment || isDocComment || isComment) { if (isGlobalDocComment || isDocComment || isComment) {
commentStartLine = currentLine; commentStartLine = currentLine;
commentStartIndex = currentIndex; commentStartIndex = currentIndex;
if (isGlobalDocComment) if (isGlobalDocComment) {
withinGlobalDocComment = true; withinGlobalDocComment = true;
else if (isDocComment) commentLength = 3;
} else if (isDocComment) {
withinDocComment = true; withinDocComment = true;
else commentLength = 3;
} else {
withinComment = true; withinComment = true;
commentLength = 2;
}
} }
} }
inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));
} }
line[currentIndex].mGlobalDocComment = withinGlobalDocComment; setGlyphFlags(currentIndex);
line[currentIndex].mDocComment = withinDocComment;
line[currentIndex].mMultiLineComment = withinComment;
line[currentIndex].mComment = withinSingleLineComment;
line[currentIndex].mDeactivated = withinNotDef;
auto &endStr = mLanguageDefinition.mCommentEnd; auto &endStr = mLanguageDefinition.mCommentEnd;
if (compareBack(endStr, line)) { if (compareBack(endStr, line) && ((commentStartLine != currentLine) || (commentStartIndex + commentLength < currentIndex))) {
withinComment = false; withinComment = false;
withinDocComment = false; withinDocComment = false;
withinGlobalDocComment = false; withinGlobalDocComment = false;
commentStartLine = endLine; commentStartLine = endLine;
commentStartIndex = endIndex; commentStartIndex = endIndex;
commentLength = 0;
} }
} }
} }
@ -2684,6 +2690,7 @@ void TextEditor::ColorizeInternal() {
currentIndex += UTF8CharLength(c); currentIndex += UTF8CharLength(c);
if (currentIndex >= (int)line.size()) { if (currentIndex >= (int)line.size()) {
withinNotDef = !ifDefs.back();
currentIndex = 0; currentIndex = 0;
++currentLine; ++currentLine;
} }

View File

@ -14,6 +14,8 @@
#include <functional> #include <functional>
#include <TextEditor.h> #include <TextEditor.h>
#include "popups/popup_file_chooser.hpp"
#include "hex/api/achievement_manager.hpp"
namespace pl::ptrn { class Pattern; } namespace pl::ptrn { class Pattern; }
@ -255,6 +257,39 @@ namespace hex::plugin::builtin {
void registerMenuItems(); void registerMenuItems();
void registerHandlers(); void registerHandlers();
std::function<void()> importPatternFile = [&] {
auto provider = ImHexApi::Provider::get();
const auto basePaths = fs::getDefaultPaths(fs::ImHexPath::Patterns);
std::vector<std::fs::path> paths;
for (const auto &imhexPath : basePaths) {
if (!wolv::io::fs::exists(imhexPath)) continue;
std::error_code error;
for (auto &entry : std::fs::recursive_directory_iterator(imhexPath, error)) {
if (entry.is_regular_file() && entry.path().extension() == ".hexpat")
paths.push_back(entry.path());
}
}
ui::PopupFileChooser::open(
basePaths, paths, std::vector<hex::fs::ItemFilter>{ { "Pattern File", "hexpat" } }, false,
[this, provider](const std::fs::path &path) {
this->loadPatternFile(path, provider);
AchievementManager::unlockAchievement("hex.builtin.achievement.patterns", "hex.builtin.achievement.patterns.load_existing.name");
}
);
};
std::function<void()> exportPatternFile = [&] {
fs::openFileBrowser(
fs::DialogMode::Save, { {"Pattern", "hexpat"} },
[this](const auto &path) {
wolv::io::File file(path, wolv::io::File::Mode::Create);
file.writeString(wolv::util::trim(m_textEditor.GetText()));
}
);
};
void appendEditorText(const std::string &text); void appendEditorText(const std::string &text);
void appendVariable(const std::string &type); void appendVariable(const std::string &type);
void appendArray(const std::string &type, size_t size); void appendArray(const std::string &type, size_t size);

View File

@ -135,6 +135,7 @@
"hex.builtin.menu.file.export.ips32": "IPS32 Patch", "hex.builtin.menu.file.export.ips32": "IPS32 Patch",
"hex.builtin.menu.file.export.bookmark": "Bookmark", "hex.builtin.menu.file.export.bookmark": "Bookmark",
"hex.builtin.menu.file.export.pattern": "Pattern File", "hex.builtin.menu.file.export.pattern": "Pattern File",
"hex.builtin.menu.file.export.pattern_file": "Export pattern File",
"hex.builtin.menu.file.export.data_processor": "Data Processor Workspace", "hex.builtin.menu.file.export.data_processor": "Data Processor Workspace",
"hex.builtin.menu.file.export.popup.create": "Cannot export data. Failed to create file!", "hex.builtin.menu.file.export.popup.create": "Cannot export data. Failed to create file!",
"hex.builtin.menu.file.export.report": "Report", "hex.builtin.menu.file.export.report": "Report",
@ -147,6 +148,7 @@
"hex.builtin.menu.file.import.modified_file": "Modified File", "hex.builtin.menu.file.import.modified_file": "Modified File",
"hex.builtin.menu.file.import.bookmark": "Bookmark", "hex.builtin.menu.file.import.bookmark": "Bookmark",
"hex.builtin.menu.file.import.pattern": "Pattern File", "hex.builtin.menu.file.import.pattern": "Pattern File",
"hex.builtin.menu.file.import.pattern_file": "Import pattern File",
"hex.builtin.menu.file.import.data_processor": "Data Processor Workspace", "hex.builtin.menu.file.import.data_processor": "Data Processor Workspace",
"hex.builtin.menu.file.import.custom_encoding": "Custom Encoding File", "hex.builtin.menu.file.import.custom_encoding": "Custom Encoding File",
"hex.builtin.menu.file.import.modified_file.popup.invalid_size": "File selected do not have the same size as the current file. Both sizes must match", "hex.builtin.menu.file.import.modified_file.popup.invalid_size": "File selected do not have the same size as the current file. Both sizes must match",

View File

@ -3,13 +3,11 @@
#include <hex/api/content_registry.hpp> #include <hex/api/content_registry.hpp>
#include <hex/api/project_file_manager.hpp> #include <hex/api/project_file_manager.hpp>
#include <hex/api/achievement_manager.hpp>
#include <pl/patterns/pattern.hpp> #include <pl/patterns/pattern.hpp>
#include <pl/core/preprocessor.hpp> #include <pl/core/preprocessor.hpp>
#include <pl/core/parser.hpp> #include <pl/core/parser.hpp>
#include <pl/core/ast/ast_node_variable_decl.hpp> #include <pl/core/ast/ast_node_variable_decl.hpp>
#include <pl/core/ast/ast_node_type_decl.hpp>
#include <pl/core/ast/ast_node_builtin_type.hpp> #include <pl/core/ast/ast_node_builtin_type.hpp>
#include <hex/helpers/fs.hpp> #include <hex/helpers/fs.hpp>
@ -22,11 +20,9 @@
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <fmt/chrono.h> #include <fmt/chrono.h>
#include <popups/popup_file_chooser.hpp>
#include <popups/popup_question.hpp> #include <popups/popup_question.hpp>
#include <toasts/toast_notification.hpp> #include <toasts/toast_notification.hpp>
#include <nlohmann/json.hpp>
#include <chrono> #include <chrono>
#include <wolv/io/file.hpp> #include <wolv/io/file.hpp>
@ -34,6 +30,8 @@
#include <wolv/utils/guards.hpp> #include <wolv/utils/guards.hpp>
#include <wolv/utils/lock.hpp> #include <wolv/utils/lock.hpp>
#include <content/global_actions.hpp>
namespace hex::plugin::builtin { namespace hex::plugin::builtin {
using namespace hex::literals; using namespace hex::literals;
@ -88,6 +86,9 @@ namespace hex::plugin::builtin {
langDef.mAutoIndentation = true; langDef.mAutoIndentation = true;
langDef.mPreprocChar = '#'; langDef.mPreprocChar = '#';
langDef.mGlobalDocComment = "/*!";
langDef.mDocComment = "/**";
langDef.mName = "Pattern Language"; langDef.mName = "Pattern Language";
initialized = true; initialized = true;
@ -236,6 +237,14 @@ namespace hex::plugin::builtin {
bool clickedMenuFind = false; bool clickedMenuFind = false;
bool clickedMenuReplace = false; bool clickedMenuReplace = false;
if (ImGui::BeginPopup("##pattern_editor_context_menu")) { if (ImGui::BeginPopup("##pattern_editor_context_menu")) {
// no shortcut for this
if (ImGui::MenuItem("hex.builtin.menu.file.import.pattern_file"_lang, nullptr, false))
importPatternFile();
if (ImGui::MenuItem("hex.builtin.menu.file.export.pattern_file"_lang, nullptr, false))
exportPatternFile();
ImGui::Separator();
const bool hasSelection = m_textEditor.HasSelection(); const bool hasSelection = m_textEditor.HasSelection();
if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.cut"_lang, Shortcut(CTRLCMD + Keys::X).toString().c_str(), false, hasSelection)) { if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.cut"_lang, Shortcut(CTRLCMD + Keys::X).toString().c_str(), false, hasSelection)) {
m_textEditor.Cut(); m_textEditor.Cut();
@ -306,6 +315,12 @@ namespace hex::plugin::builtin {
m_replaceMode = true; m_replaceMode = true;
openFindPopup = 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 std::string findWord;
@ -685,6 +700,8 @@ namespace hex::plugin::builtin {
if (altCPressed) if (altCPressed)
matchCase = !matchCase; matchCase = !matchCase;
findReplaceHandler->SetMatchCase(&m_textEditor,matchCase); findReplaceHandler->SetMatchCase(&m_textEditor,matchCase);
position = findReplaceHandler->FindPosition(&m_textEditor,m_textEditor.GetCursorPosition(), true);
count = findReplaceHandler->GetMatches().size();
updateCount = true; updateCount = true;
requestFocusFind = true; requestFocusFind = true;
} }
@ -1733,39 +1750,11 @@ namespace hex::plugin::builtin {
void ViewPatternEditor::registerMenuItems() { void ViewPatternEditor::registerMenuItems() {
/* Import Pattern */ /* 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, 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,
[this] { importPatternFile, ImHexApi::Provider::isValid);
auto provider = ImHexApi::Provider::get();
const auto basePaths = fs::getDefaultPaths(fs::ImHexPath::Patterns);
std::vector<std::fs::path> paths;
for (const auto &imhexPath : basePaths) {
if (!wolv::io::fs::exists(imhexPath)) continue;
std::error_code error;
for (auto &entry : std::fs::recursive_directory_iterator(imhexPath, error)) {
if (entry.is_regular_file() && entry.path().extension() == ".hexpat") {
paths.push_back(entry.path());
}
}
}
ui::PopupFileChooser::open(basePaths, paths, std::vector<hex::fs::ItemFilter>{ { "Pattern File", "hexpat" } }, false,
[this, provider](const std::fs::path &path) {
this->loadPatternFile(path, provider);
AchievementManager::unlockAchievement("hex.builtin.achievement.patterns", "hex.builtin.achievement.patterns.load_existing.name");
});
}, ImHexApi::Provider::isValid);
/* Export Pattern */ /* Export Pattern */
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.export", "hex.builtin.menu.file.export.pattern" }, ICON_VS_FILE_CODE, 7050, Shortcut::None, ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.export", "hex.builtin.menu.file.export.pattern" }, ICON_VS_FILE_CODE, 7050, Shortcut::None,
[this] { exportPatternFile, [this] {
fs::openFileBrowser(fs::DialogMode::Save, { {"Pattern", "hexpat"} },
[this](const auto &path) {
wolv::io::File file(path, wolv::io::File::Mode::Create);
file.writeString(wolv::util::trim(m_textEditor.GetText()));
});
}, [this] {
return !wolv::util::trim(m_textEditor.GetText()).empty() && ImHexApi::Provider::isValid(); return !wolv::util::trim(m_textEditor.GetText()).empty() && ImHexApi::Provider::isValid();
} }
); );