#pragma once #include "core.h" #include "themes/theme.h" #include #include #include #include class QsciScintilla; class QsciLexerCPP; namespace rcx { struct SavedSourceDisplay { QString text; bool active = false; }; class RcxEditor : public QWidget { Q_OBJECT public: explicit RcxEditor(QWidget* parent = nullptr); ~RcxEditor() override; void applyDocument(const ComposeResult& result); ViewState saveViewState() const; void restoreViewState(const ViewState& vs); QsciScintilla* scintilla() const { return m_sci; } QWidget* structPreviewPopup() const { return m_structPreviewPopup; } const LineMeta* metaForLine(int line) const; int currentNodeIndex() const; void scrollToNodeId(uint64_t nodeId); // ── Column span computation ── static ColumnSpan typeSpan(const LineMeta& lm, int typeW = kColType); static ColumnSpan nameSpan(const LineMeta& lm, int typeW = kColType, int nameW = kColName); static ColumnSpan valueSpan(const LineMeta& lm, int lineLength, int typeW = kColType, int nameW = kColName); // ── Multi-selection ── QSet selectedNodeIndices() const; // ── Inline editing ── bool isEditing() const { return m_editState.active; } bool beginInlineEdit(EditTarget target, int line = -1, int col = -1); void cancelInlineEdit(); void applySelectionOverlay(const QSet& selIds); void setCommandRowText(const QString& line); void setEditorFont(const QString& fontName); static void setGlobalFontName(const QString& fontName); static QString globalFontName(); void applyTheme(const Theme& theme); // Custom type names (struct types from the tree) shown in type picker + lexer GlobalClass coloring QString textWithMargins() const; void setCustomTypeNames(const QStringList& names); void setValueHistoryRef(const QHash* ref) { m_valueHistory = ref; } void setProviderRef(const Provider* prov, const Provider* realProv, const NodeTree* tree) { m_disasmProvider = prov; m_disasmRealProv = realProv; m_disasmTree = tree; } // Saved sources for quick-switch in source picker void setSavedSources(const QVector& sources) { m_savedSourceDisplay = sources; } signals: void marginClicked(int margin, int line, Qt::KeyboardModifiers mods); void contextMenuRequested(int line, int nodeIdx, int subLine, QPoint globalPos); void keywordConvertRequested(const QString& newKeyword); void nodeClicked(int line, uint64_t nodeId, Qt::KeyboardModifiers mods); void inlineEditCommitted(int nodeIdx, int subLine, EditTarget target, const QString& text, uint64_t resolvedAddr = 0); void inlineEditCancelled(); void typeSelectorRequested(); void typePickerRequested(EditTarget target, int nodeIdx, QPoint globalPos); protected: bool eventFilter(QObject* obj, QEvent* event) override; private: QsciScintilla* m_sci = nullptr; QsciLexerCPP* m_lexer = nullptr; QVector m_meta; LayoutInfo m_layout; // cached from ComposeResult // ── Toggle: absolute vs relative offset margin bool m_relativeOffsets = true; int m_marginStyleBase = -1; int m_hintLine = -1; // ── Hover cursor + highlight ── QPoint m_lastHoverPos; bool m_hoverInside = false; uint64_t m_hoveredNodeId = 0; int m_hoveredLine = -1; uint64_t m_prevHoveredNodeId = 0; // for incremental marker update int m_prevHoveredLine = -1; // for incremental marker update QSet m_currentSelIds; QVector m_hoverSpanLines; // Lines with hover span indicators // ── nodeId → display-line index (built in applyDocument) ── QHash> m_nodeLineIndex; // ── Drag selection ── bool m_dragging = false; bool m_dragStarted = false; // true once drag threshold exceeded int m_dragLastLine = -1; QPoint m_dragStartPos; // viewport coords at press Qt::KeyboardModifiers m_dragInitMods = Qt::NoModifier; // ── Deferred click (protects multi-select on double-click) ── uint64_t m_pendingClickNodeId = 0; int m_pendingClickLine = -1; Qt::KeyboardModifiers m_pendingClickMods = Qt::NoModifier; // ── Inline edit state ── struct InlineEditState { bool active = false; int line = -1; int nodeIdx = -1; int subLine = 0; EditTarget target = EditTarget::Name; int spanStart = 0; int linelenAfterReplace = 0; QString original; long posStart = 0; // Scintilla position of edit start long posEnd = 0; // Scintilla position of edit end NodeKind editKind = NodeKind::Int32; int commentCol = -1; // fixed comment column (stored at edit start) bool lastValidationOk = true; // track state to avoid redundant updates }; InlineEditState m_editState; // ── Tab cycling state ── EditTarget m_lastTabTarget = EditTarget::Value; // ── Custom type names for type picker ── QStringList m_customTypeNames; // ── Saved sources for quick-switch ── QVector m_savedSourceDisplay; // ── Value history ref (owned by controller) ── const QHash* m_valueHistory = nullptr; QWidget* m_historyPopup = nullptr; // ValueHistoryPopup (file-local class in editor.cpp) QWidget* m_disasmPopup = nullptr; // DisasmPopup (file-local class in editor.cpp) QWidget* m_structPreviewPopup = nullptr; // StructPreviewPopup (file-local class in editor.cpp) const Provider* m_disasmProvider = nullptr; // snapshot or real — for reading tree data const Provider* m_disasmRealProv = nullptr; // real process provider — for reading code at arbitrary addresses const NodeTree* m_disasmTree = nullptr; // ── Reentrancy guards ── bool m_applyingDocument = false; bool m_clampingSelection = false; bool m_updatingComment = false; void setupScintilla(); void setupLexer(); void setupMargins(); void setupFolding(); void setupMarkers(); void allocateMarginStyles(); void applyMarginText(const QVector& meta); void reformatMargins(); void applyMarkers(const QVector& meta); void applyFoldLevels(const QVector& meta); void applyHexDimming(const QVector& meta); void applyHeatmapHighlight(const QVector& meta); void applySymbolColoring(const QVector& meta); void applyBaseAddressColoring(const QVector& meta); void applyCommandRowPills(); void commitInlineEdit(); int editEndCol() const; bool handleNormalKey(QKeyEvent* ke); bool handleEditKey(QKeyEvent* ke); void showTypeAutocomplete(); void showSourcePicker(); void showTypeListFiltered(const QString& filter); void updateTypeListFilter(); void showPointerTargetPicker(); void showPointerTargetListFiltered(const QString& filter); void updatePointerTargetFilter(); void paintEditableSpans(int line); void updateEditableIndicators(int line); void applyHoverCursor(); void applyHoverHighlight(); void validateEditLive(); void setEditComment(const QString& comment); void clampEditSelection(); // ── Refactored helpers ── struct HitInfo { int line = -1; int col = -1; uint64_t nodeId = 0; bool inFoldCol = false; }; HitInfo hitTest(const QPoint& viewportPos) const; struct EndEditInfo { int nodeIdx; int subLine; EditTarget target; }; EndEditInfo endInlineEdit(); struct NormalizedSpan { int start = 0; int end = 0; bool valid = false; }; NormalizedSpan normalizeSpan(const ColumnSpan& raw, const QString& lineText, EditTarget target, bool skipPrefixes) const; // ── Indicator helpers (dedupe + UTF-8 safe) ── void clearIndicatorLine(int indic, int line); void fillIndicatorCols(int indic, int line, int colA, int colB); bool resolvedSpanFor(int line, EditTarget t, NormalizedSpan& out, QString* lineTextOut = nullptr) const; }; } // namespace rcx