diff --git a/lib/libimhex/include/hex/api/content_registry.hpp b/lib/libimhex/include/hex/api/content_registry.hpp index 3dd540812..8957f185b 100644 --- a/lib/libimhex/include/hex/api/content_registry.hpp +++ b/lib/libimhex/include/hex/api/content_registry.hpp @@ -369,6 +369,9 @@ EXPORT_MODULE namespace hex { using OnChangeCallback = std::function; u64 onChange(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const OnChangeCallback &callback); + using OnSaveCallback = std::function; + u64 onSave(const OnSaveCallback &callback); + } /* Command Palette Command Registry. Allows adding of new commands to the command palette */ diff --git a/lib/libimhex/source/api/content_registry.cpp b/lib/libimhex/source/api/content_registry.cpp index 8a79ad1e1..e4fb9bf2d 100644 --- a/lib/libimhex/source/api/content_registry.cpp +++ b/lib/libimhex/source/api/content_registry.cpp @@ -37,7 +37,13 @@ namespace hex { OnChangeCallback callback; }; + struct OnSave { + u64 id; + OnSaveCallback callback; + }; + static AutoReset>>> s_onChangeCallbacks; + static AutoReset> s_onSaveCallbacks; static void runAllOnChangeCallbacks() { for (const auto &[category, rest] : *impl::s_onChangeCallbacks) { @@ -152,6 +158,20 @@ namespace hex { } void store() { + thread_local bool isRunningCallbacks = false; + + if (isRunningCallbacks) + return; + isRunningCallbacks = true; + for (const auto &[id, callback] : *s_onSaveCallbacks) { + try { + callback(); + } catch (const std::exception &e) { + log::error("Failed to run onSave handler for setting: {}", e.what()); + } + } + isRunningCallbacks = false; + if (!s_settings.isValid()) return; @@ -281,6 +301,16 @@ namespace hex { } } + u64 onSave(const OnSaveCallback &callback) { + static u64 id = 1; + impl::s_onSaveCallbacks->emplace_back(id, callback); + + auto result = id; + id += 1; + + return result; + } + namespace Widgets { bool Checkbox::draw(const std::string &name) { diff --git a/main/gui/source/init/run/common.cpp b/main/gui/source/init/run/common.cpp index d5bf89120..c2d1fced1 100644 --- a/main/gui/source/init/run/common.cpp +++ b/main/gui/source/init/run/common.cpp @@ -42,8 +42,8 @@ namespace hex::init { } void initializationFinished() { - ContentRegistry::Settings::impl::store(); ContentRegistry::Settings::impl::load(); + ContentRegistry::Settings::impl::store(); EventImHexStartupFinished::post(); diff --git a/plugins/builtin/source/content/views/view_hex_editor.cpp b/plugins/builtin/source/content/views/view_hex_editor.cpp index e8b2dce21..3aa900333 100644 --- a/plugins/builtin/source/content/views/view_hex_editor.cpp +++ b/plugins/builtin/source/content/views/view_hex_editor.cpp @@ -573,6 +573,40 @@ namespace hex::plugin::builtin { showHighlights = value.get(true); }); + ContentRegistry::Settings::onChange("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.gray_out_zeros", [this](const ContentRegistry::Settings::SettingsValue &value) { + m_hexEditor.enableGrayOutZeros(value.get(true)); + }); + + ContentRegistry::Settings::onChange("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.upper_case_hex", [this](const ContentRegistry::Settings::SettingsValue &value) { + m_hexEditor.enableUpperCaseHex(value.get(true)); + }); + + ContentRegistry::Settings::onChange("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.show_ascii", [this](const ContentRegistry::Settings::SettingsValue &value) { + m_hexEditor.enableShowAscii(value.get(true)); + }); + + ContentRegistry::Settings::onChange("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.show_extended_ascii", [this](const ContentRegistry::Settings::SettingsValue &value) { + m_hexEditor.enableShowExtendedAscii(value.get(true)); + }); + + ContentRegistry::Settings::onChange("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.minimap", [this](const ContentRegistry::Settings::SettingsValue &value) { + m_hexEditor.setMiniMapVisualizer(value.get("")); + }); + + ContentRegistry::Settings::onChange("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.minimap_width", [this](const ContentRegistry::Settings::SettingsValue &value) { + m_hexEditor.setMiniMapWidth(value.get(m_hexEditor.getMiniMapWidth())); + }); + + ContentRegistry::Settings::onSave([this] { + ContentRegistry::Settings::write("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.bytes_per_row", m_hexEditor.getBytesPerRow()); + ContentRegistry::Settings::write("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.gray_out_zeros", m_hexEditor.shouldGrayOutZeros()); + ContentRegistry::Settings::write("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.upper_case_hex", m_hexEditor.shouldUpperCaseHex()); + ContentRegistry::Settings::write("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.show_ascii", m_hexEditor.shouldShowAscii()); + ContentRegistry::Settings::write("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.show_extended_ascii", m_hexEditor.shouldShowExtendedAscii()); + ContentRegistry::Settings::write("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.minimap", m_hexEditor.getMiniMapVisualizer().value_or("")); + ContentRegistry::Settings::write("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.minimap_width", m_hexEditor.getMiniMapWidth()); + }); + m_hexEditor.setBackgroundHighlightCallback([this](u64 address, const u8 *data, size_t size) -> std::optional { if (!showHighlights) return std::nullopt; @@ -659,8 +693,7 @@ namespace hex::plugin::builtin { EventProviderChanged::unsubscribe(this); EventProviderOpened::unsubscribe(this); EventHighlightingChanged::unsubscribe(this); - - ContentRegistry::Settings::write("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.bytes_per_row", m_hexEditor.getBytesPerRow()); + EventImHexClosing::unsubscribe(this); } void ViewHexEditor::drawPopup() { @@ -1138,7 +1171,9 @@ namespace hex::plugin::builtin { } }); - m_hexEditor.setBytesPerRow(ContentRegistry::Settings::read("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.bytes_per_row", m_hexEditor.getBytesPerRow())); + ContentRegistry::Settings::onChange("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.bytes_per_row", [this](const ContentRegistry::Settings::SettingsValue &value) { + m_hexEditor.setBytesPerRow(value.get(m_hexEditor.getBytesPerRow())); + }); ContentRegistry::Settings::onChange("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.highlight_color", [this](const ContentRegistry::Settings::SettingsValue &value) { m_hexEditor.setSelectionColor(value.get(0x60C08080)); }); diff --git a/plugins/ui/include/ui/hex_editor.hpp b/plugins/ui/include/ui/hex_editor.hpp index 9e5209b45..5d66775ea 100644 --- a/plugins/ui/include/ui/hex_editor.hpp +++ b/plugins/ui/include/ui/hex_editor.hpp @@ -233,14 +233,34 @@ namespace hex::ui { m_upperCaseHex = upperCaseHex; } + bool shouldUpperCaseHex() const { + return m_upperCaseHex; + } + void enableGrayOutZeros(bool grayOutZeros) { m_grayOutZero = grayOutZeros; } + bool shouldGrayOutZeros() const { + return m_grayOutZero; + } + void enableShowAscii(bool showAscii) { m_showAscii = showAscii; } + bool shouldShowAscii() const { + return m_showAscii; + } + + void enableShowExtendedAscii(bool showExtendedAscii) { + m_showExtendedAscii = showExtendedAscii; + } + + bool shouldShowExtendedAscii() const { + return m_showExtendedAscii; + } + void enableSyncScrolling(bool syncScrolling) { m_scrollPosition.setSynced(syncScrolling); } @@ -253,6 +273,39 @@ namespace hex::ui { m_characterCellPadding = characterCellPadding; } + void setMiniMapWidth(int miniMapWidth) { + m_miniMapWidth = miniMapWidth; + } + + [[nodiscard]] bool isMiniMapVisible() const { + return m_showMiniMap; + } + + int getMiniMapWidth() const { + return m_miniMapWidth; + } + + std::optional getMiniMapVisualizer() const { + if (!m_showMiniMap) + return std::nullopt; + return m_miniMapVisualizer->unlocalizedName; + } + + void setMiniMapVisualizer(const UnlocalizedString &miniMapVisualizerName) { + if (miniMapVisualizerName.empty()) { + m_showMiniMap = false; + m_miniMapVisualizer = nullptr; + } else { + for (const auto &visualizer : ContentRegistry::HexEditor::impl::getMiniMapVisualizers()) { + if (visualizer->unlocalizedName == miniMapVisualizerName) { + m_miniMapVisualizer = visualizer; + m_showMiniMap = true; + return; + } + } + } + } + [[nodiscard]] const std::optional& getCustomEncoding() const { return m_currCustomEncoding; }