#pragma once #include #include #include #include #include #include namespace hex::plugin::builtin { class ViewHexEditor : public View { public: ViewHexEditor(); void drawContent() override; private: constexpr static auto InvalidSelection = std::numeric_limits::max(); void registerShortcuts(); void registerEvents(); void registerMenuItems(); void drawCell(u64 address, u8 *data, size_t size, bool hovered); void drawPopup(); void drawSelectionFrame(u32 x, u32 y, u64 byteAddress, u16 bytesPerCell, const ImVec2 &cellPos, const ImVec2 &cellSize); public: void setSelection(const Region ®ion) { this->setSelection(region.getStartAddress(), region.getEndAddress()); } void setSelection(u128 start, u128 end) { if (!ImHexApi::Provider::isValid()) return; if (start == InvalidSelection && end == InvalidSelection) return; if (start == InvalidSelection) start = end; if (end == InvalidSelection) end = start; auto provider = ImHexApi::Provider::get(); const size_t maxAddress = provider->getActualSize() + provider->getBaseAddress() - 1; this->m_selectionChanged = this->m_selectionStart != start || this->m_selectionEnd != end; this->m_selectionStart = std::clamp(start, 0, maxAddress); this->m_selectionEnd = std::clamp(end, 0, maxAddress); if (this->m_selectionChanged) { EventManager::post(this->getSelection()); } } [[nodiscard]] Region getSelection() const { const auto start = std::min(this->m_selectionStart, this->m_selectionEnd); const auto end = std::max(this->m_selectionStart, this->m_selectionEnd); const size_t size = end - start + 1; return { start, size }; } [[nodiscard]] bool isSelectionValid() const { return this->m_selectionStart != InvalidSelection && this->m_selectionEnd != InvalidSelection; } void jumpToSelection() { this->m_shouldJumpToSelection = true; } void scrollToSelection() { this->m_shouldScrollToSelection = true; } void jumpIfOffScreen() { this->m_shouldJumpWhenOffScreen = true; } public: class Popup { public: virtual ~Popup() = default; virtual void draw(ViewHexEditor *editor) = 0; }; [[nodiscard]] bool isAnyPopupOpen() const { return this->m_currPopup != nullptr; } template T> [[nodiscard]] bool isPopupOpen() const { return dynamic_cast(this->m_currPopup.get()) != nullptr; } template T, typename ... Args> void openPopup(Args && ...args) { this->m_currPopup = std::make_unique(std::forward(args)...); this->m_shouldOpenPopup = true; } void closePopup() { this->m_currPopup.reset(); } private: void drawEditor(const ImVec2 &size); void drawFooter(const ImVec2 &size); void handleSelection(u64 address, u32 bytesPerCell, const u8 *data, bool cellHovered); private: u16 m_bytesPerRow = 16; ContentRegistry::HexEditor::DataVisualizer *m_currDataVisualizer; bool m_shouldJumpToSelection = false; bool m_shouldScrollToSelection = false; bool m_shouldJumpWhenOffScreen = false; bool m_selectionChanged = false; u64 m_selectionStart = InvalidSelection; u64 m_selectionEnd = InvalidSelection; u16 m_visibleRowCount = 0; std::optional m_editingAddress; bool m_shouldModifyValue = false; bool m_enteredEditingMode = false; bool m_shouldUpdateEditingValue = false; std::vector m_editingBytes; color_t m_selectionColor = 0x00; bool m_upperCaseHex = true; bool m_grayOutZero = true; bool m_showAscii = true; bool m_shouldOpenPopup = false; std::unique_ptr m_currPopup; std::optional m_currCustomEncoding; }; }