feat: inline hex byte and ASCII preview editors for hex nodes

Right-click context menu adds "Edit Hex Bytes" and "Edit ASCII" for
hex nodes (Hex8/16/32/64). Both are fixed-length overwrite-mode editors
with space-skipping, input validation, and IND_HEX_DIM indicator
preservation.
This commit is contained in:
IChooseYou
2026-03-11 16:01:37 -06:00
committed by IChooseYou
parent 3b7ed682ac
commit 3aeb1a80d5
3 changed files with 332 additions and 18 deletions

View File

@@ -1321,6 +1321,10 @@ void RcxController::applyCommand(const Command& command, bool isUndo) {
int idx = tree.indexOfId(c.nodeId);
if (idx >= 0)
tree.nodes[idx].isStatic = isUndo ? c.oldVal : c.newVal;
} else if constexpr (std::is_same_v<T, cmd::ToggleRelative>) {
int idx = tree.indexOfId(c.nodeId);
if (idx >= 0)
tree.nodes[idx].isRelative = isUndo ? c.oldVal : c.newVal;
}
}, command);
@@ -2006,6 +2010,19 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
if (addedQuickConvert)
menu.addSeparator();
// ── Hex byte / ASCII inline editing ──
if (isHexNode(node.kind) && m_doc->provider->isWritable()) {
menu.addAction(icon("edit.svg"), "Edit He&x Bytes", [editor, line]() {
editor->setHexEditPending(true);
editor->beginInlineEdit(EditTarget::Value, line);
});
menu.addAction(icon("edit.svg"), "Edit &ASCII", [editor, line]() {
editor->setHexEditPending(true);
editor->beginInlineEdit(EditTarget::Name, line);
});
menu.addSeparator();
}
// ── Edit Value / Rename / Change Type ──
bool isEditable = node.kind != NodeKind::Struct && node.kind != NodeKind::Array
&& !isHexNode(node.kind)
@@ -2016,9 +2033,11 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
});
}
menu.addAction(icon("rename.svg"), "Re&name\tF2", [editor, line]() {
editor->beginInlineEdit(EditTarget::Name, line);
});
if (!isHexNode(node.kind)) {
menu.addAction(icon("rename.svg"), "Re&name\tF2", [editor, line]() {
editor->beginInlineEdit(EditTarget::Name, line);
});
}
menu.addAction("Change &Type\tT", [editor, line]() {
editor->beginInlineEdit(EditTarget::Type, line);
@@ -2088,6 +2107,21 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
hasConvert = true;
}
// Toggle relative pointer (RVA: target = base + value)
if (node.kind == NodeKind::Pointer64 || node.kind == NodeKind::Pointer32) {
QString label = node.isRelative
? QStringLiteral("Pointer is Absolute")
: QStringLiteral("Pointer is &Relative (base + value)");
convertMenu->addAction(label, [this, nodeId]() {
int ni = m_doc->tree.indexOfId(nodeId);
if (ni < 0) return;
const Node& n = m_doc->tree.nodes[ni];
m_doc->undoStack.push(new RcxCommand(this,
cmd::ToggleRelative{n.id, n.isRelative, !n.isRelative}));
});
hasConvert = true;
}
// Split hex node into two half-sized hex nodes
if (node.kind == NodeKind::Hex64) {
convertMenu->addAction("Split to hex32+hex32", [this, nodeId]() {
@@ -3462,6 +3496,10 @@ void RcxController::collectPointerRanges(
: m_snapshotProv->readU64(ptrAddr);
if (ptrVal == 0 || ptrVal == UINT64_MAX) continue;
// Relative pointer (RVA): target = base + value
if (child.isRelative)
ptrVal += memBase;
uint64_t pBase = ptrVal;
collectPointerRanges(child.refId, pBase, depth + 1, maxDepth,
visited, ranges);