Unify right-click menu: all items available everywhere

Right-click menu is now one unified menu. Node-specific actions
(edit value, rename, change type, add field below, duplicate, delete,
copy address/offset) appear when clicking on a node. Creation actions
(Add Hex64, Add Struct, Append 128 bytes), Undo/Redo, and Copy All
as Text are always present regardless of where you click.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ccccc
2026-02-06 18:02:25 -07:00
committed by sysadmin
parent 3cf9a311d2
commit 6232577f87

View File

@@ -742,52 +742,21 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
int subLine, const QPoint& globalPos) { int subLine, const QPoint& globalPos) {
auto icon = [](const char* name) { return QIcon(QStringLiteral(":/vsicons/%1").arg(name)); }; auto icon = [](const char* name) { return QIcon(QStringLiteral(":/vsicons/%1").arg(name)); };
// Empty area or CommandRow: show full creation menu const bool hasNode = nodeIdx >= 0 && nodeIdx < m_doc->tree.nodes.size();
if (nodeIdx < 0 || nodeIdx >= m_doc->tree.nodes.size()) {
QMenu menu;
menu.addAction(icon("add.svg"), "&Add Hex64 Field", [this]() {
insertNode(0, -1, NodeKind::Hex64, "newField");
});
menu.addAction(icon("symbol-structure.svg"), "Add &Struct", [this]() {
insertNode(0, -1, NodeKind::Struct, "NewClass");
});
menu.addSeparator();
menu.addAction(icon("diff-added.svg"), "Append &128 bytes", [this]() {
m_suppressRefresh = true;
m_doc->undoStack.beginMacro(QStringLiteral("Append 128 bytes"));
for (int i = 0; i < 16; i++)
insertNode(0, -1, NodeKind::Hex64,
QStringLiteral("field_%1").arg(i));
m_doc->undoStack.endMacro();
m_suppressRefresh = false;
refresh();
});
menu.addSeparator();
menu.addAction(icon("arrow-left.svg"), "&Undo", [this]() {
m_doc->undoStack.undo();
})->setEnabled(m_doc->undoStack.canUndo());
menu.addAction(icon("arrow-right.svg"), "&Redo", [this]() {
m_doc->undoStack.redo();
})->setEnabled(m_doc->undoStack.canRedo());
menu.addSeparator();
menu.addAction(icon("clippy.svg"), "Copy All as &Text", [editor]() {
QApplication::clipboard()->setText(editor->scintilla()->text());
});
menu.exec(globalPos);
return;
}
uint64_t clickedId = m_doc->tree.nodes[nodeIdx].id;
// Right-click selection policy: if not in selection, select only this node // Selection policy
if (hasNode) {
uint64_t clickedId = m_doc->tree.nodes[nodeIdx].id;
if (!m_selIds.contains(clickedId)) { if (!m_selIds.contains(clickedId)) {
m_selIds.clear(); m_selIds.clear();
m_selIds.insert(clickedId); m_selIds.insert(clickedId);
m_anchorLine = line; m_anchorLine = line;
applySelectionOverlays(); applySelectionOverlays();
} }
}
// Multi-select batch menu // Multi-select batch actions at top
if (m_selIds.size() > 1) { if (hasNode && m_selIds.size() > 1) {
QMenu menu; QMenu menu;
int count = m_selIds.size(); int count = m_selIds.size();
QSet<uint64_t> ids = m_selIds; QSet<uint64_t> ids = m_selIds;
@@ -819,13 +788,14 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
return; return;
} }
QMenu menu;
// ── Node-specific actions (only when clicking on a node) ──
if (hasNode) {
const Node& node = m_doc->tree.nodes[nodeIdx]; const Node& node = m_doc->tree.nodes[nodeIdx];
uint64_t nodeId = node.id; uint64_t nodeId = node.id;
uint64_t parentId = node.parentId; uint64_t parentId = node.parentId;
QMenu menu;
// Inline edit actions — position cursor on the right-clicked line
bool isEditable = node.kind != NodeKind::Struct && node.kind != NodeKind::Array bool isEditable = node.kind != NodeKind::Struct && node.kind != NodeKind::Array
&& node.kind != NodeKind::Padding && node.kind != NodeKind::Mat4x4 && node.kind != NodeKind::Padding && node.kind != NodeKind::Mat4x4
&& m_doc->provider->isWritable(); && m_doc->provider->isWritable();
@@ -893,7 +863,40 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
QStringLiteral("+0x") + QString::number(off, 16).toUpper().rightJustified(4, '0')); QStringLiteral("+0x") + QString::number(off, 16).toUpper().rightJustified(4, '0'));
}); });
menu.addAction(icon("clippy.svg"), "Copy All as &Text", [editor]() { menu.addSeparator();
}
// ── Always-available actions ──
menu.addAction(icon("add.svg"), "Add Hex64 at Root", [this]() {
insertNode(0, -1, NodeKind::Hex64, "newField");
});
menu.addAction(icon("symbol-structure.svg"), "Add Struct at Root", [this]() {
insertNode(0, -1, NodeKind::Struct, "NewClass");
});
menu.addAction(icon("diff-added.svg"), "Append 128 bytes", [this]() {
m_suppressRefresh = true;
m_doc->undoStack.beginMacro(QStringLiteral("Append 128 bytes"));
for (int i = 0; i < 16; i++)
insertNode(0, -1, NodeKind::Hex64,
QStringLiteral("field_%1").arg(i));
m_doc->undoStack.endMacro();
m_suppressRefresh = false;
refresh();
});
menu.addSeparator();
menu.addAction(icon("arrow-left.svg"), "Undo", [this]() {
m_doc->undoStack.undo();
})->setEnabled(m_doc->undoStack.canUndo());
menu.addAction(icon("arrow-right.svg"), "Redo", [this]() {
m_doc->undoStack.redo();
})->setEnabled(m_doc->undoStack.canRedo());
menu.addSeparator();
menu.addAction(icon("clippy.svg"), "Copy All as Text", [editor]() {
QApplication::clipboard()->setText(editor->scintilla()->text()); QApplication::clipboard()->setText(editor->scintilla()->text());
}); });