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 "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 {
|
||||||
|
|||||||
@@ -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 ──
|
||||||
|
|||||||
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_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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user