Show relative offsets or absolute on the left side

This commit is contained in:
Sen66
2026-02-13 02:11:09 +01:00
parent 3e827194b8
commit a88b584ca0
4 changed files with 138 additions and 1 deletions

View File

@@ -1,5 +1,6 @@
#include "core.h" #include "core.h"
#include <algorithm> #include <algorithm>
#include <numeric>
namespace rcx { namespace rcx {
@@ -146,6 +147,7 @@ void composeLeaf(ComposeState& state, const NodeTree& tree,
lm.lineKind = isCont ? LineKind::Continuation : LineKind::Field; lm.lineKind = isCont ? LineKind::Continuation : LineKind::Field;
lm.nodeKind = node.kind; lm.nodeKind = node.kind;
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, isCont, state.offsetHexDigits); lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, isCont, state.offsetHexDigits);
lm.offsetAddr = tree.baseAddress + absAddr;
lm.markerMask = computeMarkers(node, prov, absAddr, isCont, depth); lm.markerMask = computeMarkers(node, prov, absAddr, isCont, depth);
lm.foldLevel = computeFoldLevel(depth, false); lm.foldLevel = computeFoldLevel(depth, false);
lm.effectiveTypeW = typeW; lm.effectiveTypeW = typeW;
@@ -196,6 +198,7 @@ void composeParent(ComposeState& state, const NodeTree& tree,
lm.depth = depth; lm.depth = depth;
lm.lineKind = LineKind::Field; lm.lineKind = LineKind::Field;
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits); lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits);
lm.offsetAddr = tree.baseAddress + absAddr;
lm.nodeKind = node.kind; lm.nodeKind = node.kind;
lm.markerMask = (1u << M_CYCLE) | (1u << M_ERR); lm.markerMask = (1u << M_CYCLE) | (1u << M_ERR);
lm.foldLevel = computeFoldLevel(depth, false); lm.foldLevel = computeFoldLevel(depth, false);
@@ -213,6 +216,7 @@ void composeParent(ComposeState& state, const NodeTree& tree,
lm.depth = depth; lm.depth = depth;
lm.lineKind = LineKind::ArrayElementSeparator; lm.lineKind = LineKind::ArrayElementSeparator;
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits); lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits);
lm.offsetAddr = tree.baseAddress + absAddr;
lm.nodeKind = node.kind; lm.nodeKind = node.kind;
lm.foldLevel = computeFoldLevel(depth, false); lm.foldLevel = computeFoldLevel(depth, false);
lm.markerMask = 0; lm.markerMask = 0;
@@ -241,6 +245,7 @@ void composeParent(ComposeState& state, const NodeTree& tree,
lm.depth = depth; lm.depth = depth;
lm.lineKind = LineKind::Header; lm.lineKind = LineKind::Header;
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits); lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits);
lm.offsetAddr = tree.baseAddress + absAddr;
lm.nodeKind = node.kind; lm.nodeKind = node.kind;
lm.isRootHeader = false; lm.isRootHeader = false;
lm.foldHead = true; lm.foldHead = true;
@@ -303,6 +308,7 @@ void composeParent(ComposeState& state, const NodeTree& tree,
lm.nodeKind = node.elementKind; lm.nodeKind = node.elementKind;
lm.isArrayElement = true; lm.isArrayElement = true;
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + elemAddr, false, state.offsetHexDigits); lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + elemAddr, false, state.offsetHexDigits);
lm.offsetAddr = tree.baseAddress + elemAddr;
lm.markerMask = computeMarkers(elem, prov, elemAddr, false, childDepth); lm.markerMask = computeMarkers(elem, prov, elemAddr, false, childDepth);
lm.foldLevel = computeFoldLevel(childDepth, false); lm.foldLevel = computeFoldLevel(childDepth, false);
lm.effectiveTypeW = eTW; lm.effectiveTypeW = eTW;
@@ -356,6 +362,7 @@ void composeParent(ComposeState& state, const NodeTree& tree,
lm.markerMask = 0; lm.markerMask = 0;
int sz = tree.structSpan(node.id, &state.childMap); int sz = tree.structSpan(node.id, &state.childMap);
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr + sz, false, state.offsetHexDigits); 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); state.emitLine(fmt::fmtStructFooter(node, depth, sz), lm);
} }
@@ -388,6 +395,7 @@ void composeNode(ComposeState& state, const NodeTree& tree,
lm.depth = depth; lm.depth = depth;
lm.lineKind = node.collapsed ? LineKind::Field : LineKind::Header; lm.lineKind = node.collapsed ? LineKind::Field : LineKind::Header;
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits); lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits);
lm.offsetAddr = tree.baseAddress + absAddr;
lm.nodeKind = node.kind; lm.nodeKind = node.kind;
lm.foldHead = true; lm.foldHead = true;
lm.foldCollapsed = node.collapsed; lm.foldCollapsed = node.collapsed;
@@ -586,6 +594,7 @@ ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewR
lm.foldLevel = SC_FOLDLEVELBASE; lm.foldLevel = SC_FOLDLEVELBASE;
lm.foldHead = false; lm.foldHead = false;
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress, false, state.offsetHexDigits); lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress, false, state.offsetHexDigits);
lm.offsetAddr = tree.baseAddress;
lm.markerMask = 0; lm.markerMask = 0;
lm.effectiveTypeW = state.typeW; lm.effectiveTypeW = state.typeW;
lm.effectiveNameW = state.nameW; 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); 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 { QSet<uint64_t> NodeTree::normalizePreferAncestors(const QSet<uint64_t>& ids) const {

View File

@@ -432,6 +432,7 @@ struct LineMeta {
int arrayCount = 0; // Array: total element count int arrayCount = 0; // Array: total element count
int arrayElementIdx = -1; // Index of this element within parent array (-1 if not array element) int arrayElementIdx = -1; // Index of this element within parent array (-1 if not array element)
QString offsetText; QString offsetText;
uint64_t offsetAddr = 0; // Raw absolute address (for margin toggle)
uint32_t markerMask = 0; uint32_t markerMask = 0;
bool dataChanged = false; // true if any byte in this node changed since last refresh 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 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 typeW = 14; // Effective type column width (default = kColType)
int nameW = 22; // Effective name column width (default = kColName) int nameW = 22; // Effective name column width (default = kColName)
int offsetHexDigits = 8; // Hex digits for offset margin (4/8/12/16) int offsetHexDigits = 8; // Hex digits for offset margin (4/8/12/16)
uint64_t baseAddress = 0; // Base address for relative offset computation
}; };
// ── ComposeResult ── // ── ComposeResult ──

View File

@@ -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_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_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_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"; static QString g_fontName = "JetBrains Mono";
@@ -171,6 +172,10 @@ void RcxEditor::setupScintilla() {
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETSTYLE, m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETSTYLE,
IND_HINT_GREEN, 17 /*INDIC_TEXTFORE*/); 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() { void RcxEditor::setupLexer() {
@@ -303,6 +308,8 @@ void RcxEditor::applyTheme(const Theme& theme) {
IND_CLASS_NAME, theme.syntaxType); IND_CLASS_NAME, theme.syntaxType);
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE, m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
IND_HINT_GREEN, theme.indHintGreen); IND_HINT_GREEN, theme.indHintGreen);
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
IND_LOCAL_OFF, theme.textFaint);
// Lexer colors // Lexer colors
m_lexer->setColor(theme.syntaxKeyword, QsciLexerCPP::Keyword); m_lexer->setColor(theme.syntaxKeyword, QsciLexerCPP::Keyword);
@@ -409,6 +416,9 @@ void RcxEditor::applyDocument(const ComposeResult& result) {
} }
void RcxEditor::applyMarginText(const QVector<LineMeta>& meta) { void RcxEditor::applyMarginText(const QVector<LineMeta>& meta) {
if (m_relativeOffsets)
return reformatMargins();
m_sci->clearMarginText(-1); m_sci->clearMarginText(-1);
for (int i = 0; i < meta.size(); i++) { 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) { void RcxEditor::applyMarkers(const QVector<LineMeta>& meta) {
for (int m = M_CONT; m <= M_STRUCT_BG; m++) { for (int m = M_CONT; m <= M_STRUCT_BG; m++) {
m_sci->markerDeleteAll(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) 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 // Double-click during edit mode: select entire editable text
if (obj == m_sci->viewport() && m_editState.active if (obj == m_sci->viewport() && m_editState.active
&& event->type() == QEvent::MouseButtonDblClick) { && event->type() == QEvent::MouseButtonDblClick) {

View File

@@ -77,6 +77,9 @@ private:
QVector<LineMeta> m_meta; QVector<LineMeta> m_meta;
LayoutInfo m_layout; // cached from ComposeResult LayoutInfo m_layout; // cached from ComposeResult
// ── Toggle: absolute vs relative offset margin
bool m_relativeOffsets = false;
int m_marginStyleBase = -1; int m_marginStyleBase = -1;
int m_hintLine = -1; int m_hintLine = -1;
@@ -138,6 +141,7 @@ private:
void allocateMarginStyles(); void allocateMarginStyles();
void applyMarginText(const QVector<LineMeta>& meta); void applyMarginText(const QVector<LineMeta>& meta);
void reformatMargins();
void applyMarkers(const QVector<LineMeta>& meta); void applyMarkers(const QVector<LineMeta>& meta);
void applyFoldLevels(const QVector<LineMeta>& meta); void applyFoldLevels(const QVector<LineMeta>& meta);
void applyHexDimming(const QVector<LineMeta>& meta); void applyHexDimming(const QVector<LineMeta>& meta);