mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
Add context menu icons, expand empty-area menu, add test_context_menu
- Right-click on empty area: Add Hex64, Add Struct, Append 128 bytes, Undo/Redo, Copy All as Text (with icons for each) - Right-click on node: icons for Edit Value, Rename, Change Type, Add Field, Add Child, Expand/Collapse, Duplicate, Delete, Copy Address, Copy Offset, Copy All as Text - Multi-select menu: icons for batch delete and batch change type - Add test_context_menu.cpp (20 tests): insert adds exactly one node, auto-offset placement, duplicate adds exactly one copy, duplicate preserves original, duplicate undo, struct duplicate no-op, root insert, append 128 bytes (16 nodes), append undo (macro), insert child, remove+undo, multiple duplicates, parent correctness, struct with children, batch remove, invalid parent, invalid index guards - Add vsicons to qrc: trash, clippy, link, diff-added, expand-all, collapse-all, rename, whole-word, list-selection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -740,10 +740,19 @@ void RcxController::duplicateNode(int nodeIdx) {
|
||||
|
||||
void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
|
||||
int subLine, const QPoint& globalPos) {
|
||||
// Empty area or CommandRow: show limited menu
|
||||
auto icon = [](const char* name) { return QIcon(QStringLiteral(":/vsicons/%1").arg(name)); };
|
||||
|
||||
// Empty area or CommandRow: show full creation menu
|
||||
if (nodeIdx < 0 || nodeIdx >= m_doc->tree.nodes.size()) {
|
||||
QMenu menu;
|
||||
menu.addAction("Append 128 bytes", [this]() {
|
||||
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++)
|
||||
@@ -753,6 +762,17 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
|
||||
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;
|
||||
}
|
||||
@@ -771,7 +791,7 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
|
||||
QMenu menu;
|
||||
int count = m_selIds.size();
|
||||
QSet<uint64_t> ids = m_selIds;
|
||||
menu.addAction(QString("Delete %1 nodes").arg(count), [this, ids]() {
|
||||
menu.addAction(icon("trash.svg"), QString("Delete %1 nodes").arg(count), [this, ids]() {
|
||||
QVector<int> indices;
|
||||
for (uint64_t id : ids) {
|
||||
int idx = m_doc->tree.indexOfId(id);
|
||||
@@ -779,7 +799,7 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
|
||||
}
|
||||
batchRemoveNodes(indices);
|
||||
});
|
||||
menu.addAction(QString("Change type of %1 nodes...").arg(count),
|
||||
menu.addAction(icon("symbol-structure.svg"), QString("Change type of %1 nodes...").arg(count),
|
||||
[this, ids]() {
|
||||
QStringList types;
|
||||
for (const auto& e : kKindMeta) types << e.name;
|
||||
@@ -810,48 +830,54 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
|
||||
&& node.kind != NodeKind::Padding && node.kind != NodeKind::Mat4x4
|
||||
&& m_doc->provider->isWritable();
|
||||
if (isEditable) {
|
||||
menu.addAction("Edit &Value\tEnter", [editor, line]() {
|
||||
menu.addAction(icon("edit.svg"), "Edit &Value\tEnter", [editor, line]() {
|
||||
editor->beginInlineEdit(EditTarget::Value, line);
|
||||
});
|
||||
}
|
||||
|
||||
menu.addAction("Re&name\tF2", [editor, line]() {
|
||||
menu.addAction(icon("rename.svg"), "Re&name\tF2", [editor, line]() {
|
||||
editor->beginInlineEdit(EditTarget::Name, line);
|
||||
});
|
||||
|
||||
menu.addAction("Change &Type\tT", [editor, line]() {
|
||||
menu.addAction(icon("symbol-structure.svg"), "Change &Type\tT", [editor, line]() {
|
||||
editor->beginInlineEdit(EditTarget::Type, line);
|
||||
});
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
menu.addAction("&Add Field Below\tInsert", [this, parentId]() {
|
||||
menu.addAction(icon("add.svg"), "&Add Field Below\tInsert", [this, parentId]() {
|
||||
insertNode(parentId, -1, NodeKind::Hex64, "newField");
|
||||
});
|
||||
|
||||
if (node.kind == NodeKind::Struct || node.kind == NodeKind::Array) {
|
||||
menu.addAction("Add &Child", [this, nodeId]() {
|
||||
menu.addAction(icon("diff-added.svg"), "Add &Child", [this, nodeId]() {
|
||||
insertNode(nodeId, 0, NodeKind::Hex64, "newField");
|
||||
});
|
||||
QString colText = node.collapsed ? "&Expand" : "&Collapse";
|
||||
menu.addAction(colText, [this, nodeId]() {
|
||||
int ni = m_doc->tree.indexOfId(nodeId);
|
||||
if (ni >= 0) toggleCollapse(ni);
|
||||
});
|
||||
if (node.collapsed) {
|
||||
menu.addAction(icon("expand-all.svg"), "&Expand", [this, nodeId]() {
|
||||
int ni = m_doc->tree.indexOfId(nodeId);
|
||||
if (ni >= 0) toggleCollapse(ni);
|
||||
});
|
||||
} else {
|
||||
menu.addAction(icon("collapse-all.svg"), "&Collapse", [this, nodeId]() {
|
||||
int ni = m_doc->tree.indexOfId(nodeId);
|
||||
if (ni >= 0) toggleCollapse(ni);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
menu.addAction("D&uplicate\tCtrl+D", [this, nodeId]() {
|
||||
menu.addAction(icon("files.svg"), "D&uplicate\tCtrl+D", [this, nodeId]() {
|
||||
int ni = m_doc->tree.indexOfId(nodeId);
|
||||
if (ni >= 0) duplicateNode(ni);
|
||||
});
|
||||
menu.addAction("&Delete\tDelete", [this, nodeId]() {
|
||||
menu.addAction(icon("trash.svg"), "&Delete\tDelete", [this, nodeId]() {
|
||||
int ni = m_doc->tree.indexOfId(nodeId);
|
||||
if (ni >= 0) removeNode(ni);
|
||||
});
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
menu.addAction("Copy &Address", [this, nodeId]() {
|
||||
menu.addAction(icon("link.svg"), "Copy &Address", [this, nodeId]() {
|
||||
int ni = m_doc->tree.indexOfId(nodeId);
|
||||
if (ni < 0) return;
|
||||
uint64_t addr = m_doc->tree.baseAddress + m_doc->tree.computeOffset(ni);
|
||||
@@ -859,7 +885,7 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
|
||||
QStringLiteral("0x") + QString::number(addr, 16).toUpper());
|
||||
});
|
||||
|
||||
menu.addAction("Copy &Offset", [this, nodeId]() {
|
||||
menu.addAction(icon("whole-word.svg"), "Copy &Offset", [this, nodeId]() {
|
||||
int ni = m_doc->tree.indexOfId(nodeId);
|
||||
if (ni < 0) return;
|
||||
int off = m_doc->tree.nodes[ni].offset;
|
||||
@@ -867,7 +893,7 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
|
||||
QStringLiteral("+0x") + QString::number(off, 16).toUpper().rightJustified(4, '0'));
|
||||
});
|
||||
|
||||
menu.addAction("Copy All as &Text", [editor]() {
|
||||
menu.addAction(icon("clippy.svg"), "Copy All as &Text", [editor]() {
|
||||
QApplication::clipboard()->setText(editor->scintilla()->text());
|
||||
});
|
||||
|
||||
|
||||
@@ -31,5 +31,14 @@
|
||||
<file alias="code.svg">vsicons/code.svg</file>
|
||||
<file alias="export.svg">vsicons/export.svg</file>
|
||||
<file alias="preview.svg">vsicons/preview.svg</file>
|
||||
<file alias="trash.svg">vsicons/trash.svg</file>
|
||||
<file alias="clippy.svg">vsicons/clippy.svg</file>
|
||||
<file alias="link.svg">vsicons/link.svg</file>
|
||||
<file alias="diff-added.svg">vsicons/diff-added.svg</file>
|
||||
<file alias="expand-all.svg">vsicons/expand-all.svg</file>
|
||||
<file alias="collapse-all.svg">vsicons/collapse-all.svg</file>
|
||||
<file alias="rename.svg">vsicons/rename.svg</file>
|
||||
<file alias="whole-word.svg">vsicons/whole-word.svg</file>
|
||||
<file alias="selection.svg">vsicons/list-selection.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
Reference in New Issue
Block a user