mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
Show relative offsets or absolute on the left side
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "core.h"
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
namespace rcx {
|
||||
|
||||
@@ -146,6 +147,7 @@ void composeLeaf(ComposeState& state, const NodeTree& tree,
|
||||
lm.lineKind = isCont ? LineKind::Continuation : LineKind::Field;
|
||||
lm.nodeKind = node.kind;
|
||||
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, isCont, state.offsetHexDigits);
|
||||
lm.offsetAddr = tree.baseAddress + absAddr;
|
||||
lm.markerMask = computeMarkers(node, prov, absAddr, isCont, depth);
|
||||
lm.foldLevel = computeFoldLevel(depth, false);
|
||||
lm.effectiveTypeW = typeW;
|
||||
@@ -196,6 +198,7 @@ void composeParent(ComposeState& state, const NodeTree& tree,
|
||||
lm.depth = depth;
|
||||
lm.lineKind = LineKind::Field;
|
||||
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits);
|
||||
lm.offsetAddr = tree.baseAddress + absAddr;
|
||||
lm.nodeKind = node.kind;
|
||||
lm.markerMask = (1u << M_CYCLE) | (1u << M_ERR);
|
||||
lm.foldLevel = computeFoldLevel(depth, false);
|
||||
@@ -213,6 +216,7 @@ void composeParent(ComposeState& state, const NodeTree& tree,
|
||||
lm.depth = depth;
|
||||
lm.lineKind = LineKind::ArrayElementSeparator;
|
||||
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits);
|
||||
lm.offsetAddr = tree.baseAddress + absAddr;
|
||||
lm.nodeKind = node.kind;
|
||||
lm.foldLevel = computeFoldLevel(depth, false);
|
||||
lm.markerMask = 0;
|
||||
@@ -241,6 +245,7 @@ void composeParent(ComposeState& state, const NodeTree& tree,
|
||||
lm.depth = depth;
|
||||
lm.lineKind = LineKind::Header;
|
||||
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits);
|
||||
lm.offsetAddr = tree.baseAddress + absAddr;
|
||||
lm.nodeKind = node.kind;
|
||||
lm.isRootHeader = false;
|
||||
lm.foldHead = true;
|
||||
@@ -303,6 +308,7 @@ void composeParent(ComposeState& state, const NodeTree& tree,
|
||||
lm.nodeKind = node.elementKind;
|
||||
lm.isArrayElement = true;
|
||||
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + elemAddr, false, state.offsetHexDigits);
|
||||
lm.offsetAddr = tree.baseAddress + elemAddr;
|
||||
lm.markerMask = computeMarkers(elem, prov, elemAddr, false, childDepth);
|
||||
lm.foldLevel = computeFoldLevel(childDepth, false);
|
||||
lm.effectiveTypeW = eTW;
|
||||
@@ -356,6 +362,7 @@ void composeParent(ComposeState& state, const NodeTree& tree,
|
||||
lm.markerMask = 0;
|
||||
int sz = tree.structSpan(node.id, &state.childMap);
|
||||
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr + sz, false, state.offsetHexDigits);
|
||||
lm.offsetAddr = tree.baseAddress + absAddr + sz;
|
||||
state.emitLine(fmt::fmtStructFooter(node, depth, sz), lm);
|
||||
}
|
||||
|
||||
@@ -388,6 +395,7 @@ void composeNode(ComposeState& state, const NodeTree& tree,
|
||||
lm.depth = depth;
|
||||
lm.lineKind = node.collapsed ? LineKind::Field : LineKind::Header;
|
||||
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits);
|
||||
lm.offsetAddr = tree.baseAddress + absAddr;
|
||||
lm.nodeKind = node.kind;
|
||||
lm.foldHead = true;
|
||||
lm.foldCollapsed = node.collapsed;
|
||||
@@ -586,6 +594,7 @@ ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewR
|
||||
lm.foldLevel = SC_FOLDLEVELBASE;
|
||||
lm.foldHead = false;
|
||||
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress, false, state.offsetHexDigits);
|
||||
lm.offsetAddr = tree.baseAddress;
|
||||
lm.markerMask = 0;
|
||||
lm.effectiveTypeW = state.typeW;
|
||||
lm.effectiveNameW = state.nameW;
|
||||
@@ -604,7 +613,7 @@ ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewR
|
||||
composeNode(state, tree, prov, idx, 0);
|
||||
}
|
||||
|
||||
return { state.text, state.meta, LayoutInfo{state.typeW, state.nameW, state.offsetHexDigits} };
|
||||
return { state.text, state.meta, LayoutInfo{state.typeW, state.nameW, state.offsetHexDigits, tree.baseAddress} };
|
||||
}
|
||||
|
||||
QSet<uint64_t> NodeTree::normalizePreferAncestors(const QSet<uint64_t>& ids) const {
|
||||
|
||||
@@ -432,6 +432,7 @@ struct LineMeta {
|
||||
int arrayCount = 0; // Array: total element count
|
||||
int arrayElementIdx = -1; // Index of this element within parent array (-1 if not array element)
|
||||
QString offsetText;
|
||||
uint64_t offsetAddr = 0; // Raw absolute address (for margin toggle)
|
||||
uint32_t markerMask = 0;
|
||||
bool dataChanged = false; // true if any byte in this node changed since last refresh
|
||||
QVector<int> changedByteIndices; // Hex preview: which byte indices (0-based) changed on this line
|
||||
@@ -452,6 +453,7 @@ struct LayoutInfo {
|
||||
int typeW = 14; // Effective type column width (default = kColType)
|
||||
int nameW = 22; // Effective name column width (default = kColName)
|
||||
int offsetHexDigits = 8; // Hex digits for offset margin (4/8/12/16)
|
||||
uint64_t baseAddress = 0; // Base address for relative offset computation
|
||||
};
|
||||
|
||||
// ── ComposeResult ──
|
||||
|
||||
122
src/editor.cpp
122
src/editor.cpp
@@ -26,6 +26,7 @@ static constexpr int IND_CMD_PILL = 12; // Rounded chip behind command row sp
|
||||
static constexpr int IND_DATA_CHANGED = 13; // Amber text for changed data values
|
||||
static constexpr int IND_CLASS_NAME = 14; // Teal text for root class name
|
||||
static constexpr int IND_HINT_GREEN = 15; // Green text for hint/comment text
|
||||
static constexpr int IND_LOCAL_OFF = 16; // Dim text for inline local offset in relative mode
|
||||
|
||||
static QString g_fontName = "JetBrains Mono";
|
||||
|
||||
@@ -171,6 +172,10 @@ void RcxEditor::setupScintilla() {
|
||||
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETSTYLE,
|
||||
IND_HINT_GREEN, 17 /*INDIC_TEXTFORE*/);
|
||||
|
||||
// Local offset text color (dim, like margin text)
|
||||
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETSTYLE,
|
||||
IND_LOCAL_OFF, 17 /*INDIC_TEXTFORE*/);
|
||||
|
||||
}
|
||||
|
||||
void RcxEditor::setupLexer() {
|
||||
@@ -303,6 +308,8 @@ void RcxEditor::applyTheme(const Theme& theme) {
|
||||
IND_CLASS_NAME, theme.syntaxType);
|
||||
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
|
||||
IND_HINT_GREEN, theme.indHintGreen);
|
||||
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
|
||||
IND_LOCAL_OFF, theme.textFaint);
|
||||
|
||||
// Lexer colors
|
||||
m_lexer->setColor(theme.syntaxKeyword, QsciLexerCPP::Keyword);
|
||||
@@ -409,6 +416,9 @@ void RcxEditor::applyDocument(const ComposeResult& result) {
|
||||
}
|
||||
|
||||
void RcxEditor::applyMarginText(const QVector<LineMeta>& meta) {
|
||||
if (m_relativeOffsets)
|
||||
return reformatMargins();
|
||||
|
||||
m_sci->clearMarginText(-1);
|
||||
|
||||
for (int i = 0; i < meta.size(); i++) {
|
||||
@@ -424,6 +434,107 @@ void RcxEditor::applyMarginText(const QVector<LineMeta>& meta) {
|
||||
}
|
||||
}
|
||||
|
||||
void RcxEditor::reformatMargins() {
|
||||
uint64_t base = m_layout.baseAddress;
|
||||
int hexDigits = m_layout.offsetHexDigits;
|
||||
|
||||
// ── Pass 1: margin text (global offset only) ──
|
||||
m_sci->clearMarginText(-1);
|
||||
for (int i = 0; i < m_meta.size(); i++) {
|
||||
auto& lm = m_meta[i];
|
||||
|
||||
if (lm.isContinuation) {
|
||||
lm.offsetText = QStringLiteral(" \u00B7 ");
|
||||
} else if (lm.offsetText.isEmpty()) {
|
||||
continue;
|
||||
} else if (m_relativeOffsets) {
|
||||
if (lm.lineKind == LineKind::Footer ||
|
||||
lm.lineKind == LineKind::ArrayElementSeparator ||
|
||||
lm.lineKind == LineKind::CommandRow) {
|
||||
lm.offsetText = QString(hexDigits + 1, ' ');
|
||||
} else {
|
||||
uint64_t rel = lm.offsetAddr >= base ? lm.offsetAddr - base : 0;
|
||||
lm.offsetText = (QStringLiteral("+") +
|
||||
QString::number(rel, 16).toUpper())
|
||||
.rightJustified(hexDigits, ' ') + QChar(' ');
|
||||
}
|
||||
} else {
|
||||
lm.offsetText = QString::number(lm.offsetAddr, 16).toUpper()
|
||||
.rightJustified(hexDigits, '0') + QChar(' ');
|
||||
}
|
||||
|
||||
QByteArray text = lm.offsetText.toUtf8();
|
||||
m_sci->SendScintilla(QsciScintillaBase::SCI_MARGINSETTEXT,
|
||||
(uintptr_t)i, text.constData());
|
||||
QByteArray styles(text.size(), '\0');
|
||||
m_sci->SendScintilla(QsciScintillaBase::SCI_MARGINSETSTYLES,
|
||||
(uintptr_t)i, styles.constData());
|
||||
}
|
||||
|
||||
// ── Pass 2: inline local offsets in the text indent area ──
|
||||
m_sci->setReadOnly(false);
|
||||
for (int i = 0; i < m_meta.size(); i++) {
|
||||
const auto& lm = m_meta[i];
|
||||
if (lm.depth <= 1 || lm.isContinuation) continue;
|
||||
if (lm.lineKind != LineKind::Field && lm.lineKind != LineKind::Header)
|
||||
continue;
|
||||
|
||||
// Place offset in the parent's indent slot (one level above the field's own indent)
|
||||
// so the field's own 3-char indent acts as visual separator from the type column
|
||||
int col = kFoldCol + (lm.depth - 2) * 3;
|
||||
int slotWidth = 3;
|
||||
|
||||
auto pos = [&](int c) -> long {
|
||||
return m_sci->SendScintilla(QsciScintillaBase::SCI_FINDCOLUMN,
|
||||
(unsigned long)i, (long)c);
|
||||
};
|
||||
|
||||
if (m_relativeOffsets) {
|
||||
// Derive local offset: find enclosing header or array element separator
|
||||
uint64_t parentAddr = base;
|
||||
for (int j = i - 1; j >= 0; j--) {
|
||||
const auto& pLm = m_meta[j];
|
||||
if (pLm.lineKind == LineKind::Header && pLm.depth < lm.depth) {
|
||||
parentAddr = pLm.offsetAddr;
|
||||
break;
|
||||
}
|
||||
if (pLm.lineKind == LineKind::ArrayElementSeparator && pLm.depth <= lm.depth) {
|
||||
parentAddr = pLm.offsetAddr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint64_t localOff = lm.offsetAddr >= parentAddr ? lm.offsetAddr - parentAddr : 0;
|
||||
|
||||
QString off = QStringLiteral("+") +
|
||||
QString::number(localOff, 16).toUpper();
|
||||
QString padded = off.size() <= slotWidth
|
||||
? off.rightJustified(slotWidth, ' ')
|
||||
: off;
|
||||
long posA = pos(col);
|
||||
long posB = pos(col + slotWidth);
|
||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETTARGETSTART, posA);
|
||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETTARGETEND, posB);
|
||||
QByteArray utf8 = padded.left(slotWidth).toUtf8();
|
||||
m_sci->SendScintilla(QsciScintillaBase::SCI_REPLACETARGET,
|
||||
(uintptr_t)utf8.size(), utf8.constData());
|
||||
// Color the local offset dim
|
||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETINDICATORCURRENT, IND_LOCAL_OFF);
|
||||
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICATORFILLRANGE,
|
||||
posA, posB - posA);
|
||||
} else {
|
||||
// Restore spaces when toggling off
|
||||
long posA = pos(col);
|
||||
long posB = pos(col + slotWidth);
|
||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETTARGETSTART, posA);
|
||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETTARGETEND, posB);
|
||||
QByteArray spaces(slotWidth, ' ');
|
||||
m_sci->SendScintilla(QsciScintillaBase::SCI_REPLACETARGET,
|
||||
(uintptr_t)spaces.size(), spaces.constData());
|
||||
}
|
||||
}
|
||||
m_sci->setReadOnly(true);
|
||||
}
|
||||
|
||||
void RcxEditor::applyMarkers(const QVector<LineMeta>& meta) {
|
||||
for (int m = M_CONT; m <= M_STRUCT_BG; m++) {
|
||||
m_sci->markerDeleteAll(m);
|
||||
@@ -1285,6 +1396,17 @@ bool RcxEditor::eventFilter(QObject* obj, QEvent* event) {
|
||||
}
|
||||
return true; // consume release (prevent QScintilla from acting on it)
|
||||
}
|
||||
// Double-click on offset margin → toggle absolute/relative
|
||||
if (obj == m_sci->viewport() && event->type() == QEvent::MouseButtonDblClick) {
|
||||
auto* me = static_cast<QMouseEvent*>(event);
|
||||
int margin0Width = (int)m_sci->SendScintilla(
|
||||
QsciScintillaBase::SCI_GETMARGINWIDTHN, 0UL, 0L);
|
||||
if ((int)me->position().x() < margin0Width) {
|
||||
m_relativeOffsets = !m_relativeOffsets;
|
||||
reformatMargins();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Double-click during edit mode: select entire editable text
|
||||
if (obj == m_sci->viewport() && m_editState.active
|
||||
&& event->type() == QEvent::MouseButtonDblClick) {
|
||||
|
||||
@@ -77,6 +77,9 @@ private:
|
||||
QVector<LineMeta> m_meta;
|
||||
LayoutInfo m_layout; // cached from ComposeResult
|
||||
|
||||
// ── Toggle: absolute vs relative offset margin
|
||||
bool m_relativeOffsets = false;
|
||||
|
||||
int m_marginStyleBase = -1;
|
||||
int m_hintLine = -1;
|
||||
|
||||
@@ -138,6 +141,7 @@ private:
|
||||
void allocateMarginStyles();
|
||||
|
||||
void applyMarginText(const QVector<LineMeta>& meta);
|
||||
void reformatMargins();
|
||||
void applyMarkers(const QVector<LineMeta>& meta);
|
||||
void applyFoldLevels(const QVector<LineMeta>& meta);
|
||||
void applyHexDimming(const QVector<LineMeta>& meta);
|
||||
|
||||
Reference in New Issue
Block a user