mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
feat: value history timestamps, Ctrl+F search, base address fixes
- Add timestamps to ValueHistory ring buffer, expose via new MCP tool
node.history, show relative time in popup ("26s ago", "2m ago")
- Add "Clear Value History" right-click menu for single and multi-select
- Add Ctrl+F find bar to RcxEditor with live search, Enter-to-next, wrap
- Fix Ctrl+F in workspace dock to auto-focus search field
- Add "Change to float" quick-convert for Hex32 right-click menu
- Sort workspace explorer by children count descending (most fields first)
- Fix provider->base() overwriting saved base address from .rcx files
- Add formula support to MCP change_base operation
- Re-evaluate baseAddressFormula on provider attach in selectSource()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1581,6 +1581,8 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
|
||||
} else if (commonKind == NodeKind::Hex32) {
|
||||
menu.addAction("Change to uint32_t", [this, collectIndices]() {
|
||||
batchChangeKind(collectIndices(), NodeKind::UInt32); });
|
||||
menu.addAction("Change to float", [this, collectIndices]() {
|
||||
batchChangeKind(collectIndices(), NodeKind::Float); });
|
||||
addedQuickConvert = true;
|
||||
} else if (commonKind == NodeKind::Hex16) {
|
||||
menu.addAction("Change to int16_t", [this, collectIndices]() {
|
||||
@@ -1629,6 +1631,18 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
|
||||
act->setChecked(m_trackValues);
|
||||
connect(act, &QAction::toggled, this, &RcxController::setTrackValues);
|
||||
}
|
||||
{
|
||||
auto* act = menu.addAction("Clear Value History");
|
||||
act->setToolTip(QStringLiteral("Reset change tracking for selected nodes"));
|
||||
connect(act, &QAction::triggered, this, [this, ids]() {
|
||||
for (uint64_t id : ids) {
|
||||
m_valueHistory[id].clear();
|
||||
for (auto& lm : m_lastResult.meta)
|
||||
if (lm.nodeId == id) lm.heatLevel = 0;
|
||||
}
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
menu.addSeparator();
|
||||
|
||||
// Check if all selected nodes share the same parent (required for grouping)
|
||||
@@ -1723,6 +1737,10 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
|
||||
int ni = m_doc->tree.indexOfId(nodeId);
|
||||
if (ni >= 0) changeNodeKind(ni, NodeKind::UInt32);
|
||||
});
|
||||
menu.addAction("Change to float", [this, nodeId]() {
|
||||
int ni = m_doc->tree.indexOfId(nodeId);
|
||||
if (ni >= 0) changeNodeKind(ni, NodeKind::Float);
|
||||
});
|
||||
addedQuickConvert = true;
|
||||
} else if (node.kind == NodeKind::Hex16) {
|
||||
menu.addAction("Change to int16_t", [this, nodeId]() {
|
||||
@@ -1812,6 +1830,17 @@ void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx,
|
||||
act->setChecked(m_trackValues);
|
||||
connect(act, &QAction::toggled, this, &RcxController::setTrackValues);
|
||||
}
|
||||
{
|
||||
auto* act = menu.addAction("Clear Value History");
|
||||
act->setToolTip(QStringLiteral("Reset change tracking for this node"));
|
||||
act->setEnabled(m_valueHistory.contains(nodeId) && m_valueHistory[nodeId].uniqueCount() > 0);
|
||||
connect(act, &QAction::triggered, this, [this, nodeId]() {
|
||||
m_valueHistory[nodeId].clear();
|
||||
for (auto& lm : m_lastResult.meta)
|
||||
if (lm.nodeId == nodeId) lm.heatLevel = 0;
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
menu.addSeparator();
|
||||
|
||||
// Convert to Hex nodes (decompose non-hex types into Hex64/32/16/8)
|
||||
@@ -2934,7 +2963,32 @@ void RcxController::selectSource(const QString& text) {
|
||||
m_doc->undoStack.clear();
|
||||
m_doc->provider = std::move(provider);
|
||||
m_doc->dataPath.clear();
|
||||
m_doc->tree.baseAddress = (newBase != 0) ? newBase : m_doc->tree.baseAddress;
|
||||
m_doc->tree.pointerSize = m_doc->provider->pointerSize();
|
||||
|
||||
// Re-evaluate formula if present (mirrors attachViaPlugin)
|
||||
if (!m_doc->tree.baseAddressFormula.isEmpty()) {
|
||||
AddressParserCallbacks cbs;
|
||||
auto* prov = m_doc->provider.get();
|
||||
cbs.resolveModule = [prov](const QString& name, bool* ok) -> uint64_t {
|
||||
uint64_t base = prov->symbolToAddress(name);
|
||||
*ok = (base != 0);
|
||||
return base;
|
||||
};
|
||||
int ptrSz = m_doc->tree.pointerSize;
|
||||
cbs.readPointer = [prov, ptrSz](uint64_t addr, bool* ok) -> uint64_t {
|
||||
uint64_t val = 0;
|
||||
*ok = prov->read(addr, &val, ptrSz);
|
||||
return val;
|
||||
};
|
||||
auto result = AddressParser::evaluate(
|
||||
m_doc->tree.baseAddressFormula, ptrSz, &cbs);
|
||||
if (result.ok)
|
||||
m_doc->tree.baseAddress = result.value;
|
||||
} else if (newBase != 0 && m_doc->tree.baseAddress == 0x00400000) {
|
||||
// Only apply provider base for fresh/default projects.
|
||||
// If user loaded an .rcx with a custom base, preserve it.
|
||||
m_doc->tree.baseAddress = newBase;
|
||||
}
|
||||
resetSnapshot();
|
||||
emit m_doc->documentChanged();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user