fix: sync tab title on keyword convert, add new screenshots

- Update dock tab title when converting enum/class via workspace menu
- Add tooltip and source picker screenshots to README
This commit is contained in:
IChooseYou
2026-03-14 09:14:28 -06:00
committed by IChooseYou
parent 54bee5022b
commit 5ded192990
4 changed files with 96 additions and 0 deletions

View File

@@ -22,6 +22,10 @@ Built with C++17, Qt 6 (Qt 5 also supported), and QScintilla. The entire editor
## Screenshots ## Screenshots
![Base address tooltip with expression cheat sheet](docs/README_PIC5.png)
![Data source picker with saved sources](docs/README_PIC4.png)
![Windows — VTable with value history popup](docs/README_PIC1.png) ![Windows — VTable with value history popup](docs/README_PIC1.png)
![macOS — project tree with kernel struct inspection](docs/README_PIC2.png) ![macOS — project tree with kernel struct inspection](docs/README_PIC2.png)

BIN
docs/README_PIC4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/README_PIC5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

@@ -4433,6 +4433,10 @@ void MainWindow::createWorkspaceDock() {
? QStringLiteral("struct") : QStringLiteral("class"); ? QStringLiteral("struct") : QStringLiteral("class");
tab.doc->undoStack.push(new rcx::RcxCommand(tab.ctrl, tab.doc->undoStack.push(new rcx::RcxCommand(tab.ctrl,
rcx::cmd::ChangeClassKeyword{item.structId, item.keyword, newKw})); rcx::cmd::ChangeClassKeyword{item.structId, item.keyword, newKw}));
// Sync all dock titles that share this document
for (auto it = m_tabs.begin(); it != m_tabs.end(); ++it)
if (it->doc == tab.doc)
it.key()->setWindowTitle(tabTitle(*it));
rebuildWorkspaceModel(); rebuildWorkspaceModel();
} else if (chosen && chosen == actPin) { } else if (chosen && chosen == actPin) {
@@ -4839,6 +4843,94 @@ void MainWindow::createSymbolsDock() {
styleTree(m_modulesTree); styleTree(m_modulesTree);
m_modulesTree->setExpandsOnDoubleClick(false); m_modulesTree->setExpandsOnDoubleClick(false);
// Double-click: set base address and trigger PDB import
connect(m_modulesTree, &QTreeView::doubleClicked, this, [this](const QModelIndex& idx) {
auto* item = m_modulesModel->itemFromIndex(idx);
if (!item) return;
uint64_t base = item->data(Qt::UserRole).toULongLong();
QString name = item->data(Qt::UserRole + 1).toString();
QString fullPath = item->data(Qt::UserRole + 2).toString();
auto* ctrl = activeController();
if (!ctrl) return;
// Set base address
ctrl->document()->tree.baseAddress = base;
ctrl->document()->tree.baseAddressFormula.clear();
ctrl->resetChangeTracking();
ctrl->refresh();
// Already have symbols for this module?
QString canonical = rcx::SymbolStore::instance().resolveAlias(name);
if (rcx::SymbolStore::instance().moduleData(canonical)) {
setAppStatus(QStringLiteral("Base set to %1 (0x%2) — symbols already loaded")
.arg(name).arg(base, 0, 16));
return;
}
// Try to load symbols: local PDB → cache → download
auto prov = ctrl->document()->provider;
if (!prov) return;
auto info = rcx::extractPdbDebugInfo(*prov, base);
if (!info.valid) {
setAppStatus(QStringLiteral("Base set to %1 (0x%2) — no debug info")
.arg(name).arg(base, 0, 16));
return;
}
// Helper to load a PDB file into the symbol store
auto loadPdb = [this, name](const QString& pdbPath) -> bool {
QString symErr;
auto result = rcx::extractPdbSymbols(pdbPath, &symErr);
if (result.symbols.isEmpty()) return false;
QVector<QPair<QString, uint32_t>> pairs;
pairs.reserve(result.symbols.size());
for (const auto& s : result.symbols)
pairs.append({s.name, s.rva});
int count = rcx::SymbolStore::instance().addModule(
result.moduleName, pdbPath, pairs);
setAppStatus(QStringLiteral("Loaded %1 symbols for %2").arg(count).arg(name));
rebuildSymbolsModel();
if (auto* c = activeController()) c->refresh();
return true;
};
// Check local
QString localPdb = rcx::SymbolDownloader::findLocal(fullPath, info.pdbName);
if (!localPdb.isEmpty() && loadPdb(localPdb)) return;
// Check cache
if (!m_symDownloader)
m_symDownloader = new rcx::SymbolDownloader(this);
rcx::SymbolDownloader::DownloadRequest req;
req.moduleName = name;
req.pdbName = info.pdbName;
req.guidString = info.guidString;
req.age = info.age;
QString cached = m_symDownloader->findCached(req);
if (!cached.isEmpty() && loadPdb(cached)) return;
// Download
setAppStatus(QStringLiteral("Downloading symbols for %1...").arg(name));
// One-shot connection for this download
auto conn = std::make_shared<QMetaObject::Connection>();
*conn = connect(m_symDownloader, &rcx::SymbolDownloader::finished,
this, [this, conn, loadPdb](const QString& mod, const QString& localPath,
bool success, const QString& error) {
disconnect(*conn);
if (!success) {
setAppStatus(QStringLiteral("Failed to download %1: %2").arg(mod, error));
return;
}
loadPdb(localPath);
});
m_symDownloader->download(req);
});
// Context menu for modules // Context menu for modules
m_modulesTree->setContextMenuPolicy(Qt::CustomContextMenu); m_modulesTree->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_modulesTree, &QWidget::customContextMenuRequested, this, [this](const QPoint& pos) { connect(m_modulesTree, &QWidget::customContextMenuRequested, this, [this](const QPoint& pos) {