Dim command row address, fix source picker zoom/styling, lowercase menu items, margin spacing

- Apply IND_HEX_DIM to base address on command row so it matches source/struct grey
- Source picker QMenu respects Scintilla zoom level for font size
- Style source picker with dark theme selection color instead of system blue
- Disable blue selection background for picker-based edit targets (Source, Type, etc.)
- Rename menu items File/Process to file/process
- Add trailing space to margin offset text for breathing room
This commit is contained in:
Bojangles
2026-02-08 14:10:26 -07:00
committed by sysadmin
parent 6a9641edc5
commit f27848bf1a
10 changed files with 282 additions and 383 deletions

View File

@@ -39,10 +39,9 @@ struct ComposeState {
if (currentLine > 0) text += '\n';
// 3-char fold indicator column: " - " expanded, " + " collapsed, " " other
// CommandRow has no fold prefix (flush left)
if (lm.lineKind == LineKind::CommandRow || lm.lineKind == LineKind::Blank
|| lm.lineKind == LineKind::CommandRow2
if (lm.lineKind == LineKind::CommandRow
|| (lm.lineKind == LineKind::Footer && lm.isRootHeader)) {
// no prefix — flush left like CommandRow2
// no prefix — flush left
} else if (lm.foldHead)
text += lm.foldCollapsed ? QStringLiteral(" \u25B8 ") : QStringLiteral(" \u25BE ");
else
@@ -218,13 +217,13 @@ void composeParent(ComposeState& state, const NodeTree& tree,
}
// Detect root header: first root-level struct — suppressed from display
// (CommandRow2 already shows the root class type + name)
// (CommandRow already shows the root class type + name)
bool isRootHeader = (node.parentId == 0 && node.kind == NodeKind::Struct && !state.baseEmitted);
if (isRootHeader)
state.baseEmitted = true;
// Header line (skip for array element structs and root struct)
// Root struct header is on CommandRow2 (type + name + {)
// Root struct header is on CommandRow (type + name + {)
if (!isArrayChild && !isRootHeader) {
// Get per-scope widths for this header's parent scope
int typeW = state.effectiveTypeW(scopeId);
@@ -474,8 +473,8 @@ ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewR
state.scopeNameW[0] = qBound(kMinNameW, rootMaxName, kMaxNameW);
}
// Emit CommandRow as line 0 (synthetic UI line)
const QString cmdRowText = QStringLiteral("source\u25BE \u203A 0x0");
// Emit CommandRow as line 0 (combined: source + address + root class type + name)
const QString cmdRowText = QStringLiteral("source\u25BE \u00B7 0x0 \u00B7 struct\u25BE <no class> {");
{
LineMeta lm;
lm.nodeIdx = -1;
@@ -484,48 +483,13 @@ ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewR
lm.lineKind = LineKind::CommandRow;
lm.foldLevel = SC_FOLDLEVELBASE;
lm.foldHead = false;
lm.offsetText.clear();
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress, false);
lm.markerMask = 0;
lm.effectiveTypeW = state.typeW;
lm.effectiveNameW = state.nameW;
state.emitLine(cmdRowText, lm);
}
// Emit dotted separator (line 1) — fixed-width spaced dots in text + margin
{
// ~20 dots in text area, ~10 dots in margin, using middle dot · (U+00B7)
QString dots(20, QChar(0x00B7));
QString marginDots(10, QChar(0x00B7));
LineMeta lm;
lm.nodeIdx = -1;
lm.nodeId = kCommandRowId;
lm.depth = 0;
lm.lineKind = LineKind::Blank;
lm.foldLevel = SC_FOLDLEVELBASE;
lm.foldHead = false;
lm.offsetText = marginDots;
lm.markerMask = 0;
lm.effectiveTypeW = state.typeW;
lm.effectiveNameW = state.nameW;
state.emitLine(dots, lm);
}
// Emit CommandRow2 as line 2 (root class type + name)
{
LineMeta lm;
lm.nodeIdx = -1;
lm.nodeId = kCommandRow2Id;
lm.depth = 0;
lm.lineKind = LineKind::CommandRow2;
lm.foldLevel = SC_FOLDLEVELBASE;
lm.foldHead = false;
lm.offsetText.clear();
lm.markerMask = 0;
lm.effectiveTypeW = state.typeW;
lm.effectiveNameW = state.nameW;
state.emitLine(QStringLiteral("struct\u25BE <no class> {"), lm);
}
QVector<int> roots = state.childMap.value(0);
std::sort(roots.begin(), roots.end(), [&](int a, int b) {
return tree.nodes[a].offset < tree.nodes[b].offset;

View File

@@ -57,7 +57,7 @@ static QString crumbFor(const rcx::NodeTree& t, uint64_t nodeId) {
std::reverse(parts.begin(), parts.end());
if (parts.size() > 4)
parts = {parts.front(), QStringLiteral("\u2026"), parts[parts.size() - 2], parts.back()};
return parts.join(QStringLiteral(" \u203A "));
return parts.join(QStringLiteral(" \u00B7 "));
}
// ── RcxDocument ──
@@ -205,7 +205,7 @@ void RcxController::connectEditor(RcxEditor* editor) {
// Inline editing signals
connect(editor, &RcxEditor::inlineEditCommitted,
this, [this](int nodeIdx, int subLine, EditTarget target, const QString& text) {
// CommandRow BaseAddress/Source edit has nodeIdx=-1; CommandRow2 edits too
// CommandRow BaseAddress/Source/RootClass edit has nodeIdx=-1
if (nodeIdx < 0 && target != EditTarget::BaseAddress && target != EditTarget::Source
&& target != EditTarget::RootClassType && target != EditTarget::RootClassName) { refresh(); return; }
switch (target) {
@@ -332,7 +332,7 @@ void RcxController::connectEditor(RcxEditor* editor) {
if (text.startsWith(QStringLiteral("#saved:"))) {
int idx = text.mid(7).toInt();
switchToSavedSource(idx);
} else if (text == QStringLiteral("File")) {
} else if (text == QStringLiteral("file")) {
auto* w = qobject_cast<QWidget*>(parent());
QString path = QFileDialog::getOpenFileName(w, "Load Binary Data", {}, "All Files (*)");
if (!path.isEmpty()) {
@@ -366,7 +366,7 @@ void RcxController::connectEditor(RcxEditor* editor) {
refresh();
}
}
else if (text == QStringLiteral("Process")) {
else if (text == QStringLiteral("process")) {
#ifdef _WIN32
auto* w = qobject_cast<QWidget*>(parent());
ProcessPicker picker(w);
@@ -1234,7 +1234,7 @@ void RcxController::handleNodeClick(RcxEditor* source, int line,
int to = qMax(m_anchorLine, line);
for (int i = from; i <= to && i < m_lastResult.meta.size(); i++) {
uint64_t nid = m_lastResult.meta[i].nodeId;
if (nid != 0 && nid != kCommandRowId && nid != kCommandRow2Id) m_selIds.insert(effectiveId(i, nid));
if (nid != 0 && nid != kCommandRowId) m_selIds.insert(effectiveId(i, nid));
}
}
} else { // Ctrl+Shift
@@ -1246,7 +1246,7 @@ void RcxController::handleNodeClick(RcxEditor* source, int line,
int to = qMax(m_anchorLine, line);
for (int i = from; i <= to && i < m_lastResult.meta.size(); i++) {
uint64_t nid = m_lastResult.meta[i].nodeId;
if (nid != 0 && nid != kCommandRowId && nid != kCommandRow2Id) m_selIds.insert(effectiveId(i, nid));
if (nid != 0 && nid != kCommandRowId) m_selIds.insert(effectiveId(i, nid));
}
}
}
@@ -1397,10 +1397,10 @@ void RcxController::updateCommandRow() {
// Build the row. If we have a symbol, append it after the address.
QString row;
if (sym.isEmpty()) {
row = QStringLiteral("%1 \u203A %2")
row = QStringLiteral("%1 \u00B7 %2")
.arg(elide(src, 40), elide(addr, 24));
} else {
row = QStringLiteral("%1 \u203A %2 %3")
row = QStringLiteral("%1 \u00B7 %2 %3")
.arg(elide(src, 40), elide(addr, 24), elide(sym, 40));
}
@@ -1419,9 +1419,10 @@ void RcxController::updateCommandRow() {
if (row2.isEmpty())
row2 = QStringLiteral("struct\u25BE <no class> {");
QString combined = row + QStringLiteral(" \u00B7 ") + row2;
for (auto* ed : m_editors) {
ed->setCommandRowText(row);
ed->setCommandRow2Text(row2);
ed->setCommandRowText(combined);
}
emit selectionChanged(m_selIds.size());
}

View File

@@ -393,18 +393,14 @@ struct NodeTree {
// ── LineMeta ──
enum class LineKind : uint8_t {
CommandRow, // line 0: source + address
Blank, // line 1: dotted separator
CommandRow2, // line 2: root class type + name
CommandRow, // line 0: source + address + root class type + name
Blank, // (unused — kept for enum stability)
Header, Field, Continuation, Footer, ArrayElementSeparator
};
static constexpr uint64_t kCommandRowId = UINT64_MAX;
static constexpr uint64_t kCommandRow2Id = UINT64_MAX - 1;
static constexpr int kCommandRowLine = 0;
static constexpr int kBlankLine = 1;
static constexpr int kCommandRow2Line = 2;
static constexpr int kFirstDataLine = 3;
static constexpr int kFirstDataLine = 1;
static constexpr uint64_t kFooterIdBit = 0x8000000000000000ULL;
struct LineMeta {
@@ -435,7 +431,7 @@ struct LineMeta {
};
inline bool isSyntheticLine(const LineMeta& lm) {
return lm.lineKind == LineKind::CommandRow || lm.lineKind == LineKind::Blank || lm.lineKind == LineKind::CommandRow2;
return lm.lineKind == LineKind::CommandRow;
}
// ── Layout Info ──
@@ -573,10 +569,10 @@ inline ColumnSpan commentSpanFor(const LineMeta& lm, int lineLength, int typeW =
}
// ── CommandRow spans ──
// Line format: "source▾ 0x140000000"
// Line format: "source▾ · 0x140000000"
inline ColumnSpan commandRowSrcSpan(const QString& lineText) {
int idx = lineText.indexOf(QStringLiteral(" \u203A"));
int idx = lineText.indexOf(QStringLiteral(" \u00B7"));
if (idx < 0) return {};
int start = 0;
while (start < idx && !lineText[start].isLetterOrNumber()
@@ -590,38 +586,48 @@ inline ColumnSpan commandRowSrcSpan(const QString& lineText) {
}
inline ColumnSpan commandRowAddrSpan(const QString& lineText) {
int tag = lineText.indexOf(QStringLiteral(" \u203A"));
int tag = lineText.indexOf(QStringLiteral(" \u00B7"));
if (tag < 0) return {};
int start = tag + 3; // after " "
int start = tag + 3; // after " · "
int end = start;
while (end < lineText.size() && !lineText[end].isSpace()) end++;
if (end <= start) return {};
return {start, end, true};
}
// ── CommandRow2 spans ──
// Line format: "struct▾ ClassName {"
// ── CommandRow root-class spans ──
// Combined CommandRow format ends with: " struct▾ ClassName {"
inline ColumnSpan commandRow2TypeSpan(const QString& lineText) {
int start = 0;
while (start < lineText.size() && lineText[start].isSpace()) start++;
if (start >= lineText.size()) return {};
inline int commandRowRootStart(const QString& lineText) {
int best = -1;
int i;
i = lineText.lastIndexOf(QStringLiteral("struct\u25BE"));
if (i > best) best = i;
i = lineText.lastIndexOf(QStringLiteral("class\u25BE"));
if (i > best) best = i;
i = lineText.lastIndexOf(QStringLiteral("enum\u25BE"));
if (i > best) best = i;
return best;
}
inline ColumnSpan commandRowRootTypeSpan(const QString& lineText) {
int start = commandRowRootStart(lineText);
if (start < 0) return {};
int end = start;
while (end < lineText.size() && lineText[end] != QChar(' ') && lineText[end] != QChar(0x25BE)) end++;
if (end <= start) return {start, (int)lineText.size(), true};
while (end < lineText.size() && lineText[end] != QChar(' ')
&& lineText[end] != QChar(0x25BE)) end++;
if (end <= start) return {};
return {start, end, true};
}
inline ColumnSpan commandRow2NameSpan(const QString& lineText) {
// Format: "keyword name {" — extract just the name part (before " {")
int start = 0;
while (start < lineText.size() && lineText[start].isSpace()) start++;
int space = lineText.indexOf(' ', start);
inline ColumnSpan commandRowRootNameSpan(const QString& lineText) {
int base = commandRowRootStart(lineText);
if (base < 0) return {};
int space = lineText.indexOf(' ', base);
if (space < 0) return {};
int nameStart = space + 1;
while (nameStart < lineText.size() && lineText[nameStart].isSpace()) nameStart++;
if (nameStart >= lineText.size()) return {};
// Stop before trailing " {"
int nameEnd = lineText.indexOf(QStringLiteral(" {"), nameStart);
if (nameEnd < 0) nameEnd = lineText.size();
while (nameEnd > nameStart && lineText[nameEnd - 1].isSpace()) nameEnd--;

View File

@@ -24,12 +24,12 @@ static const QColor kFgMarginDim("#505050");
static constexpr int IND_EDITABLE = 8;
static constexpr int IND_HEX_DIM = 9;
static constexpr int IND_BASE_ADDR = 10; // Green color for base address
static constexpr int IND_BASE_ADDR = 10; // Default text color override for command row address
static constexpr int IND_HOVER_SPAN = 11; // Blue text on hover (link-like)
static constexpr int IND_CMD_PILL = 12; // Rounded chip behind command row spans
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_ULINE = 15; // Underline for root class name
static constexpr int IND_HINT_GREEN = 15; // Green text for hint/comment text
static QString g_fontName = "Consolas";
@@ -148,11 +148,11 @@ void RcxEditor::setupScintilla() {
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
IND_HEX_DIM, QColor("#505050"));
// Base address indicator — green like comments
// Base address indicator — default text color to override lexer green on command row
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETSTYLE,
IND_BASE_ADDR, 17 /*INDIC_TEXTFORE*/);
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
IND_BASE_ADDR, QColor("#5a8248"));
IND_BASE_ADDR, QColor("#d4d4d4"));
// Hover span indicator — muted teal text (distinct from blue keywords)
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETSTYLE,
@@ -182,11 +182,11 @@ void RcxEditor::setupScintilla() {
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
IND_CLASS_NAME, QColor("#4EC9B0"));
// Root class name underline
// Green text for hint/comment annotations
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETSTYLE,
IND_CLASS_ULINE, (long)0 /*INDIC_PLAIN*/);
IND_HINT_GREEN, 17 /*INDIC_TEXTFORE*/);
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
IND_CLASS_ULINE, QColor(124, 180, 226));
IND_HINT_GREEN, QColor("#5a8248"));
}
@@ -395,9 +395,6 @@ void RcxEditor::applyMarkers(const QVector<LineMeta>& meta) {
m_sci->markerAdd(i, M_CMD_ROW);
continue;
}
if (meta[i].lineKind == LineKind::CommandRow2) {
continue; // regular background (no special marker)
}
uint32_t mask = meta[i].markerMask;
for (int m = M_CONT; m <= M_STRUCT_BG; m++) {
if (mask & (1u << m)) {
@@ -636,7 +633,11 @@ void RcxEditor::applyBaseAddressColoring(const QVector<LineMeta>& meta) {
if (meta.isEmpty() || meta[0].lineKind != LineKind::CommandRow) return;
clearIndicatorLine(IND_BASE_ADDR, 0);
// Address in command bar is not colored green (only field values get green)
// Override lexer's green number coloring on the address with default text color
QString t = getLineText(m_sci, 0);
ColumnSpan addr = commandRowAddrSpan(t);
if (addr.valid)
fillIndicatorCols(IND_BASE_ADDR, 0, addr.start, addr.end);
}
void RcxEditor::applyCommandRowPills() {
@@ -645,20 +646,10 @@ void RcxEditor::applyCommandRowPills() {
constexpr int line = 0;
QString t = getLineText(m_sci, line);
clearIndicatorLine(IND_CMD_PILL, line);
clearIndicatorLine(IND_HEX_DIM, line);
clearIndicatorLine(IND_CLASS_NAME, line);
auto fillPadded = [&](ColumnSpan s) {
if (!s.valid) return;
int a = qMax(0, s.start - 1);
int b = qMin(t.size(), s.end + 1);
fillIndicatorCols(IND_CMD_PILL, line, a, b);
};
fillPadded(commandRowSrcSpan(t));
fillPadded(commandRowAddrSpan(t));
// Dim label text: source arrow/placeholder and ", address="
// Dim label text: source arrow/placeholder + its ▾ dropdown arrow
ColumnSpan srcSpan = commandRowSrcSpan(t);
if (srcSpan.valid) {
int quotePos = t.indexOf('\'', srcSpan.start);
@@ -666,35 +657,37 @@ void RcxEditor::applyCommandRowPills() {
while (kindEnd > srcSpan.start && t[kindEnd - 1].isSpace()) kindEnd--;
if (kindEnd > srcSpan.start)
fillIndicatorCols(IND_HEX_DIM, line, srcSpan.start, kindEnd);
// Dim the source ▾ dropdown arrow to match (like struct▾)
int srcDrop = t.indexOf(QChar(0x25BE));
int rootStart = commandRowRootStart(t);
if (srcDrop >= 0 && (rootStart < 0 || srcDrop < rootStart))
fillIndicatorCols(IND_HEX_DIM, line, srcDrop, srcDrop + 1);
}
int addrTag = t.indexOf(QStringLiteral(" \u203A"));
if (addrTag >= 0)
fillIndicatorCols(IND_HEX_DIM, line, addrTag, addrTag + 3);
// Style CommandRow2 (line kCommandRow2Line) if present
if (m_meta.size() > kCommandRow2Line && m_meta[kCommandRow2Line].lineKind == LineKind::CommandRow2) {
int line2 = kCommandRow2Line;
QString t2 = getLineText(m_sci, line2);
clearIndicatorLine(IND_HEX_DIM, line2);
clearIndicatorLine(IND_CLASS_NAME, line2);
clearIndicatorLine(IND_CLASS_ULINE, line2);
ColumnSpan typeSpan = commandRow2TypeSpan(t2);
if (typeSpan.valid)
fillIndicatorCols(IND_HEX_DIM, line2, typeSpan.start, typeSpan.end);
ColumnSpan nameSpan = commandRow2NameSpan(t2);
if (nameSpan.valid) {
fillIndicatorCols(IND_CLASS_NAME, line2, nameSpan.start, nameSpan.end);
}
// Dim all " · " separators
int searchFrom = 0;
while (true) {
int tag = t.indexOf(QStringLiteral(" \u00B7"), searchFrom);
if (tag < 0) break;
fillIndicatorCols(IND_HEX_DIM, line, tag, tag + 3);
searchFrom = tag + 3;
}
// Dim the dotted separator line
if (m_meta.size() > kBlankLine && m_meta[kBlankLine].lineKind == LineKind::Blank) {
QString sep = getLineText(m_sci, kBlankLine);
if (!sep.isEmpty())
fillIndicatorCols(IND_HEX_DIM, kBlankLine, 0, sep.size());
// Dim base address to match source/struct grey
ColumnSpan addrSpan = commandRowAddrSpan(t);
if (addrSpan.valid)
fillIndicatorCols(IND_HEX_DIM, line, addrSpan.start, addrSpan.end);
// Root class styling (type dim + class-name teal, no underline)
ColumnSpan rt = commandRowRootTypeSpan(t);
if (rt.valid) {
fillIndicatorCols(IND_HEX_DIM, line, rt.start, rt.end);
int drop = t.indexOf(QChar(0x25BE), rt.start);
if (drop >= 0)
fillIndicatorCols(IND_HEX_DIM, line, drop, qMin(drop + 2, t.size()));
}
ColumnSpan rn = commandRowRootNameSpan(t);
if (rn.valid) {
fillIndicatorCols(IND_CLASS_NAME, line, rn.start, rn.end);
}
}
@@ -841,26 +834,17 @@ bool RcxEditor::resolvedSpanFor(int line, EditTarget t,
const LineMeta* lm = metaForLine(line);
if (!lm) return false;
// CommandRow: BaseAddress (ADDR) and Source (SRC) editing
// CommandRow: Source / BaseAddress / Root class (type+name) editing
if (lm->lineKind == LineKind::CommandRow) {
if (t != EditTarget::BaseAddress && t != EditTarget::Source) return false;
QString lineText = getLineText(m_sci, line);
ColumnSpan s = (t == EditTarget::Source)
? commandRowSrcSpan(lineText)
: commandRowAddrSpan(lineText);
out = normalizeSpan(s, lineText, t, /*skipPrefixes=*/(t == EditTarget::BaseAddress));
if (lineTextOut) *lineTextOut = lineText;
return out.valid;
}
// CommandRow2: root class type and name
if (lm->lineKind == LineKind::CommandRow2) {
if (t != EditTarget::RootClassType && t != EditTarget::RootClassName) return false;
if (t != EditTarget::BaseAddress && t != EditTarget::Source
&& t != EditTarget::RootClassType && t != EditTarget::RootClassName) return false;
QString lineText = getLineText(m_sci, line);
ColumnSpan s;
if (t == EditTarget::RootClassType) s = commandRow2TypeSpan(lineText);
else s = commandRow2NameSpan(lineText);
out = normalizeSpan(s, lineText, t, false);
if (t == EditTarget::Source) s = commandRowSrcSpan(lineText);
else if (t == EditTarget::BaseAddress) s = commandRowAddrSpan(lineText);
else if (t == EditTarget::RootClassType) s = commandRowRootTypeSpan(lineText);
else s = commandRowRootNameSpan(lineText);
out = normalizeSpan(s, lineText, t, /*skipPrefixes=*/(t == EditTarget::BaseAddress));
if (lineTextOut) *lineTextOut = lineText;
return out.valid;
}
@@ -974,21 +958,17 @@ static bool hitTestTarget(QsciScintilla* sci,
return s.valid && col >= s.start && col < s.end;
};
// CommandRow: SRC and ADDR fields are interactive
// CommandRow: interactive SRC/ADDR + root class (type+name)
if (lm.lineKind == LineKind::CommandRow) {
ColumnSpan ss = commandRowSrcSpan(lineText);
if (inSpan(ss)) { outTarget = EditTarget::Source; outLine = line; return true; }
ColumnSpan as = commandRowAddrSpan(lineText);
if (inSpan(as)) { outTarget = EditTarget::BaseAddress; outLine = line; return true; }
return false;
}
// CommandRow2: root class type and name
if (lm.lineKind == LineKind::CommandRow2) {
ColumnSpan ts = commandRow2TypeSpan(lineText);
if (inSpan(ts)) { outTarget = EditTarget::RootClassType; outLine = line; return true; }
ColumnSpan ns = commandRow2NameSpan(lineText);
if (inSpan(ns)) { outTarget = EditTarget::RootClassName; outLine = line; return true; }
ColumnSpan rts = commandRowRootTypeSpan(lineText);
if (inSpan(rts)) { outTarget = EditTarget::RootClassType; outLine = line; return true; }
ColumnSpan rns = commandRowRootNameSpan(lineText);
if (inSpan(rns)) { outTarget = EditTarget::RootClassName; outLine = line; return true; }
return false;
}
@@ -1122,7 +1102,7 @@ bool RcxEditor::eventFilter(QObject* obj, QEvent* event) {
return true;
}
// CommandRow: try ADDR edit or consume
if (h.nodeId == kCommandRowId || h.nodeId == kCommandRow2Id) {
if (h.nodeId == kCommandRowId) {
int tLine; EditTarget t;
if (hitTestTarget(m_sci, m_meta, me->pos(), tLine, t))
beginInlineEdit(t, tLine);
@@ -1217,7 +1197,7 @@ bool RcxEditor::eventFilter(QObject* obj, QEvent* event) {
m_pendingClickNodeId = 0; // cancel deferred selection change
// Narrow selection to this node before editing
auto h = hitTest(me->pos());
if (h.nodeId != 0 && h.nodeId != kCommandRowId && h.nodeId != kCommandRow2Id)
if (h.nodeId != 0 && h.nodeId != kCommandRowId)
emit nodeClicked(h.line, h.nodeId, Qt::NoModifier);
return beginInlineEdit(t, line);
}
@@ -1403,11 +1383,10 @@ bool RcxEditor::beginInlineEdit(EditTarget target, int line) {
m_sci->getCursorPosition(&line, &col);
auto* lm = metaForLine(line);
if (!lm) return false;
// Allow nodeIdx=-1 only for CommandRow/CommandRow2 editing
// Allow nodeIdx=-1 only for CommandRow editing (command bar)
if (lm->nodeIdx < 0 && !(lm->lineKind == LineKind::CommandRow &&
(target == EditTarget::BaseAddress || target == EditTarget::Source))
&& !(lm->lineKind == LineKind::CommandRow2 &&
(target == EditTarget::RootClassType || target == EditTarget::RootClassName)))
(target == EditTarget::BaseAddress || target == EditTarget::Source
|| target == EditTarget::RootClassType || target == EditTarget::RootClassName)))
return false;
// Padding: reject value editing (display-only hex bytes)
if (target == EditTarget::Value && lm->nodeKind == NodeKind::Padding)
@@ -1493,10 +1472,15 @@ bool RcxEditor::beginInlineEdit(EditTarget target, int line) {
m_sci->viewport()->setCursor(Qt::IBeamCursor);
}
// Re-enable selection rendering for inline edit
// Re-enable selection rendering for inline edit (skip for picker-based targets)
bool isPicker = (target == EditTarget::Type || target == EditTarget::Source
|| target == EditTarget::ArrayElementType
|| target == EditTarget::PointerTarget
|| target == EditTarget::RootClassType);
m_sci->SendScintilla(QsciScintillaBase::SCI_SETSELFORE, (long)0, (long)0);
m_sci->SendScintilla(QsciScintillaBase::SCI_SETSELBACK, (long)1,
QColor("#264f78"));
if (!isPicker)
m_sci->SendScintilla(QsciScintillaBase::SCI_SETSELBACK, (long)1,
QColor("#264f78"));
// Use correct UTF-8 position conversion (not lineStart + col!)
m_editState.posStart = posFromCol(m_sci, line, norm.start);
@@ -1671,8 +1655,15 @@ void RcxEditor::showSourcePicker() {
if (!m_editState.active || m_editState.target != EditTarget::Source)
return;
QMenu menu;
menu.addAction("File");
menu.addAction("Process");
QFont menuFont = editorFont();
int zoom = (int)m_sci->SendScintilla(QsciScintillaBase::SCI_GETZOOM);
menuFont.setPointSize(menuFont.pointSize() + zoom);
menu.setFont(menuFont);
menu.setStyleSheet(QStringLiteral(
"QMenu { background: #252526; color: #d4d4d4; }"
"QMenu::item:selected { background: #232323; }"));
menu.addAction("file");
menu.addAction("process");
// Saved sources below separator (with checkmarks)
if (!m_savedSourceDisplay.isEmpty()) {
@@ -1795,18 +1786,13 @@ void RcxEditor::updatePointerTargetFilter() {
void RcxEditor::paintEditableSpans(int line) {
const LineMeta* lm = metaForLine(line);
if (!lm) return;
// CommandRow: paint Source and BaseAddress spans
// CommandRow: paint Source/BaseAddress + root class (type+name) spans
if (lm->lineKind == LineKind::CommandRow) {
NormalizedSpan norm;
if (resolvedSpanFor(line, EditTarget::Source, norm))
fillIndicatorCols(IND_EDITABLE, line, norm.start, norm.end);
if (resolvedSpanFor(line, EditTarget::BaseAddress, norm))
fillIndicatorCols(IND_EDITABLE, line, norm.start, norm.end);
return;
}
// CommandRow2: paint RootClassType and RootClassName spans
if (lm->lineKind == LineKind::CommandRow2) {
NormalizedSpan norm;
if (resolvedSpanFor(line, EditTarget::RootClassType, norm))
fillIndicatorCols(IND_EDITABLE, line, norm.start, norm.end);
if (resolvedSpanFor(line, EditTarget::RootClassName, norm))
@@ -1991,8 +1977,8 @@ void RcxEditor::setEditComment(const QString& comment) {
m_sci->SendScintilla(QsciScintillaBase::SCI_REPLACETARGET,
(uintptr_t)utf8.size(), utf8.constData());
// Apply green color to hint text (reuse IND_BASE_ADDR which is green)
m_sci->SendScintilla(QsciScintillaBase::SCI_SETINDICATORCURRENT, IND_BASE_ADDR);
// Apply green color to hint text
m_sci->SendScintilla(QsciScintillaBase::SCI_SETINDICATORCURRENT, IND_HINT_GREEN);
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICATORFILLRANGE, posA, posB - posA);
m_updatingComment = false;
@@ -2065,42 +2051,6 @@ void RcxEditor::setCommandRowText(const QString& line) {
applyCommandRowPills();
}
void RcxEditor::setCommandRow2Text(const QString& line) {
if (m_sci->lines() <= kCommandRow2Line) return;
QString s = line;
s.replace('\n', ' ');
s.replace('\r', ' ');
bool wasReadOnly = m_sci->isReadOnly();
bool wasModified = m_sci->SendScintilla(QsciScintillaBase::SCI_GETMODIFY);
long savedPos = m_sci->SendScintilla(QsciScintillaBase::SCI_GETCURRENTPOS);
long savedAnchor = m_sci->SendScintilla(QsciScintillaBase::SCI_GETANCHOR);
m_sci->SendScintilla(QsciScintillaBase::SCI_SETUNDOCOLLECTION, 0);
m_sci->setReadOnly(false);
long start = m_sci->SendScintilla(QsciScintillaBase::SCI_POSITIONFROMLINE, kCommandRow2Line);
long end = m_sci->SendScintilla(QsciScintillaBase::SCI_GETLINEENDPOSITION, kCommandRow2Line);
long oldLen = end - start;
QByteArray utf8 = s.toUtf8();
m_sci->SendScintilla(QsciScintillaBase::SCI_SETTARGETSTART, start);
m_sci->SendScintilla(QsciScintillaBase::SCI_SETTARGETEND, end);
m_sci->SendScintilla(QsciScintillaBase::SCI_REPLACETARGET, (uintptr_t)utf8.size(), utf8.constData());
// Adjust saved cursor/anchor for length change in CommandRow2 line
long delta = (long)utf8.size() - oldLen;
if (savedPos > end) savedPos += delta;
if (savedAnchor > end) savedAnchor += delta;
if (wasReadOnly) m_sci->setReadOnly(true);
m_sci->SendScintilla(QsciScintillaBase::SCI_SETUNDOCOLLECTION, 1);
if (!wasModified) m_sci->SendScintilla(QsciScintillaBase::SCI_SETSAVEPOINT);
m_sci->SendScintilla(QsciScintillaBase::SCI_SETCURRENTPOS, savedPos);
m_sci->SendScintilla(QsciScintillaBase::SCI_SETANCHOR, savedAnchor);
m_sci->SendScintilla(QsciScintillaBase::SCI_COLOURISE, start, start + utf8.size());
applyCommandRowPills();
}
void RcxEditor::setEditorFont(const QString& fontName) {
g_fontName = fontName;
QFont f = editorFont();

View File

@@ -45,7 +45,6 @@ public:
void applySelectionOverlay(const QSet<uint64_t>& selIds);
void setCommandRowText(const QString& line);
void setCommandRow2Text(const QString& line);
void setEditorFont(const QString& fontName);
static void setGlobalFontName(const QString& fontName);

View File

@@ -112,8 +112,8 @@ QString indent(int depth) {
// ── Offset margin ──
QString fmtOffsetMargin(uint64_t absoluteOffset, bool isContinuation) {
if (isContinuation) return QStringLiteral(" \u00B7");
return QString::number(absoluteOffset, 16).toUpper();
if (isContinuation) return QStringLiteral(" \u00B7 ");
return QString::number(absoluteOffset, 16).toUpper() + QChar(' ');
}
// ── Struct type name (for width calculation) ──