mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
Tab titles show root struct name instead of Untitled, sync MDI tab font with editor, rename MCP pipe/exe to ReclassMcpBridge
This commit is contained in:
committed by
sysadmin
parent
b153665059
commit
c86a6dbc73
@@ -62,6 +62,7 @@ add_executable(Reclass
|
|||||||
src/mainwindow.h
|
src/mainwindow.h
|
||||||
src/mcp/mcp_bridge.h
|
src/mcp/mcp_bridge.h
|
||||||
src/mcp/mcp_bridge.cpp
|
src/mcp/mcp_bridge.cpp
|
||||||
|
$<$<PLATFORM_ID:Windows>:src/app.rc>
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(Reclass PRIVATE src)
|
target_include_directories(Reclass PRIVATE src)
|
||||||
@@ -79,8 +80,8 @@ if(WIN32)
|
|||||||
target_link_libraries(Reclass PRIVATE dbghelp dwmapi psapi)
|
target_link_libraries(Reclass PRIVATE dbghelp dwmapi psapi)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(rcx-mcp-stdio tools/rcx-mcp-stdio.cpp)
|
add_executable(ReclassMcpBridge tools/rcx-mcp-stdio.cpp)
|
||||||
target_link_libraries(rcx-mcp-stdio PRIVATE ${QT}::Core ${QT}::Network)
|
target_link_libraries(ReclassMcpBridge PRIVATE ${QT}::Core ${QT}::Network)
|
||||||
|
|
||||||
include(deploy)
|
include(deploy)
|
||||||
|
|
||||||
|
|||||||
1
src/app.rc
Normal file
1
src/app.rc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
IDI_ICON1 ICON "icons/class.ico"
|
||||||
BIN
src/icons/class.ico
Normal file
BIN
src/icons/class.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 970 B |
BIN
src/icons/class.png
Normal file
BIN
src/icons/class.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 918 B |
57
src/main.cpp
57
src/main.cpp
@@ -253,6 +253,14 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) {
|
|||||||
.arg(t.background.name(), t.textMuted.name(), t.text.name(),
|
.arg(t.background.name(), t.textMuted.name(), t.text.name(),
|
||||||
t.backgroundAlt.name(), t.hover.name()));
|
t.backgroundAlt.name(), t.hover.name()));
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
QSettings settings("Reclass", "Reclass");
|
||||||
|
QString fontName = settings.value("font", "JetBrains Mono").toString();
|
||||||
|
QFont f(fontName, 12);
|
||||||
|
f.setFixedPitch(true);
|
||||||
|
if (auto* tb = m_mdiArea->findChild<QTabBar*>())
|
||||||
|
tb->setFont(f);
|
||||||
|
}
|
||||||
setCentralWidget(m_mdiArea);
|
setCentralWidget(m_mdiArea);
|
||||||
|
|
||||||
createWorkspaceDock();
|
createWorkspaceDock();
|
||||||
@@ -488,13 +496,32 @@ RcxEditor* MainWindow::activePaneEditor() {
|
|||||||
return pane ? pane->editor : nullptr;
|
return pane ? pane->editor : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QString rootName(const NodeTree& tree, uint64_t viewRootId = 0) {
|
||||||
|
if (viewRootId != 0) {
|
||||||
|
int idx = tree.indexOfId(viewRootId);
|
||||||
|
if (idx >= 0) {
|
||||||
|
const auto& n = tree.nodes[idx];
|
||||||
|
if (!n.structTypeName.isEmpty()) return n.structTypeName;
|
||||||
|
if (!n.name.isEmpty()) return n.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& n : tree.nodes) {
|
||||||
|
if (n.parentId == 0 && n.kind == NodeKind::Struct) {
|
||||||
|
if (!n.structTypeName.isEmpty()) return n.structTypeName;
|
||||||
|
if (!n.name.isEmpty()) return n.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QStringLiteral("Untitled");
|
||||||
|
}
|
||||||
|
|
||||||
QMdiSubWindow* MainWindow::createTab(RcxDocument* doc) {
|
QMdiSubWindow* MainWindow::createTab(RcxDocument* doc) {
|
||||||
auto* splitter = new QSplitter(Qt::Horizontal);
|
auto* splitter = new QSplitter(Qt::Horizontal);
|
||||||
auto* ctrl = new RcxController(doc, splitter);
|
auto* ctrl = new RcxController(doc, splitter);
|
||||||
|
|
||||||
auto* sub = m_mdiArea->addSubWindow(splitter);
|
auto* sub = m_mdiArea->addSubWindow(splitter);
|
||||||
|
sub->setWindowIcon(QIcon()); // suppress app icon in MDI tabs
|
||||||
sub->setWindowTitle(doc->filePath.isEmpty()
|
sub->setWindowTitle(doc->filePath.isEmpty()
|
||||||
? "Untitled" : QFileInfo(doc->filePath).fileName());
|
? rootName(doc->tree) : QFileInfo(doc->filePath).fileName());
|
||||||
sub->setAttribute(Qt::WA_DeleteOnClose);
|
sub->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
sub->showMaximized();
|
sub->showMaximized();
|
||||||
|
|
||||||
@@ -553,8 +580,13 @@ QMdiSubWindow* MainWindow::createTab(RcxDocument* doc) {
|
|||||||
if (it != m_tabs.end())
|
if (it != m_tabs.end())
|
||||||
QTimer::singleShot(0, this, [this, sub]() {
|
QTimer::singleShot(0, this, [this, sub]() {
|
||||||
auto it2 = m_tabs.find(sub);
|
auto it2 = m_tabs.find(sub);
|
||||||
if (it2 != m_tabs.end()) updateAllRenderedPanes(*it2);
|
if (it2 != m_tabs.end()) {
|
||||||
|
updateAllRenderedPanes(*it2);
|
||||||
|
if (it2->doc->filePath.isEmpty())
|
||||||
|
sub->setWindowTitle(rootName(it2->doc->tree, it2->ctrl->viewRootId()));
|
||||||
|
}
|
||||||
rebuildWorkspaceModel();
|
rebuildWorkspaceModel();
|
||||||
|
updateWindowTitle();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
connect(&doc->undoStack, &QUndoStack::indexChanged,
|
connect(&doc->undoStack, &QUndoStack::indexChanged,
|
||||||
@@ -563,7 +595,12 @@ QMdiSubWindow* MainWindow::createTab(RcxDocument* doc) {
|
|||||||
if (it != m_tabs.end())
|
if (it != m_tabs.end())
|
||||||
QTimer::singleShot(0, this, [this, sub]() {
|
QTimer::singleShot(0, this, [this, sub]() {
|
||||||
auto it2 = m_tabs.find(sub);
|
auto it2 = m_tabs.find(sub);
|
||||||
if (it2 != m_tabs.end()) updateAllRenderedPanes(*it2);
|
if (it2 != m_tabs.end()) {
|
||||||
|
updateAllRenderedPanes(*it2);
|
||||||
|
if (it2->doc->filePath.isEmpty())
|
||||||
|
sub->setWindowTitle(rootName(it2->doc->tree, it2->ctrl->viewRootId()));
|
||||||
|
}
|
||||||
|
updateWindowTitle();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -677,7 +714,7 @@ void MainWindow::newDocument() {
|
|||||||
emit doc->documentChanged();
|
emit doc->documentChanged();
|
||||||
|
|
||||||
auto* sub = m_mdiArea->activeSubWindow();
|
auto* sub = m_mdiArea->activeSubWindow();
|
||||||
if (sub) sub->setWindowTitle("Untitled");
|
if (sub) sub->setWindowTitle(rootName(doc->tree, ctrl->viewRootId()));
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
rebuildWorkspaceModel();
|
rebuildWorkspaceModel();
|
||||||
}
|
}
|
||||||
@@ -835,7 +872,7 @@ void MainWindow::toggleMcp() {
|
|||||||
} else {
|
} else {
|
||||||
m_mcp->start();
|
m_mcp->start();
|
||||||
m_mcpAction->setText("Stop &MCP Server");
|
m_mcpAction->setText("Stop &MCP Server");
|
||||||
m_statusLabel->setText("MCP server listening on pipe: rcx-mcp");
|
m_statusLabel->setText("MCP server listening on pipe: ReclassMcpBridge");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -916,6 +953,9 @@ void MainWindow::setEditorFont(const QString& fontName) {
|
|||||||
m_workspaceTree->setFont(f);
|
m_workspaceTree->setFont(f);
|
||||||
// Sync status bar font
|
// Sync status bar font
|
||||||
statusBar()->setFont(f);
|
statusBar()->setFont(f);
|
||||||
|
// Sync MDI tab bar font
|
||||||
|
if (auto* tb = m_mdiArea->findChild<QTabBar*>())
|
||||||
|
tb->setFont(f);
|
||||||
// Sync menu bar / menu font via global stylesheet
|
// Sync menu bar / menu font via global stylesheet
|
||||||
applyGlobalTheme(ThemeManager::instance().current());
|
applyGlobalTheme(ThemeManager::instance().current());
|
||||||
}
|
}
|
||||||
@@ -947,7 +987,8 @@ void MainWindow::updateWindowTitle() {
|
|||||||
auto* sub = m_mdiArea->activeSubWindow();
|
auto* sub = m_mdiArea->activeSubWindow();
|
||||||
if (sub && m_tabs.contains(sub)) {
|
if (sub && m_tabs.contains(sub)) {
|
||||||
auto& tab = m_tabs[sub];
|
auto& tab = m_tabs[sub];
|
||||||
QString name = tab.doc->filePath.isEmpty() ? "Untitled"
|
QString name = tab.doc->filePath.isEmpty()
|
||||||
|
? rootName(tab.doc->tree, tab.ctrl->viewRootId())
|
||||||
: QFileInfo(tab.doc->filePath).fileName();
|
: QFileInfo(tab.doc->filePath).fileName();
|
||||||
if (tab.doc->modified) name += " *";
|
if (tab.doc->modified) name += " *";
|
||||||
setWindowTitle(name + " - Reclass");
|
setWindowTitle(name + " - Reclass");
|
||||||
@@ -1325,7 +1366,8 @@ void MainWindow::rebuildWorkspaceModel() {
|
|||||||
|
|
||||||
TabState& tab = m_tabs[sub];
|
TabState& tab = m_tabs[sub];
|
||||||
QString tabName = tab.doc->filePath.isEmpty()
|
QString tabName = tab.doc->filePath.isEmpty()
|
||||||
? "Untitled" : QFileInfo(tab.doc->filePath).fileName();
|
? rootName(tab.doc->tree, tab.ctrl->viewRootId())
|
||||||
|
: QFileInfo(tab.doc->filePath).fileName();
|
||||||
|
|
||||||
buildWorkspaceModel(m_workspaceModel, tab.doc->tree, tabName,
|
buildWorkspaceModel(m_workspaceModel, tab.doc->tree, tabName,
|
||||||
static_cast<void*>(sub));
|
static_cast<void*>(sub));
|
||||||
@@ -1461,6 +1503,7 @@ int main(int argc, char* argv[]) {
|
|||||||
applyGlobalTheme(rcx::ThemeManager::instance().current());
|
applyGlobalTheme(rcx::ThemeManager::instance().current());
|
||||||
|
|
||||||
rcx::MainWindow window;
|
rcx::MainWindow window;
|
||||||
|
window.setWindowIcon(QIcon(":/icons/class.png"));
|
||||||
|
|
||||||
bool screenshotMode = app.arguments().contains("--screenshot");
|
bool screenshotMode = app.arguments().contains("--screenshot");
|
||||||
if (screenshotMode)
|
if (screenshotMode)
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ void McpBridge::start() {
|
|||||||
m_server->setSocketOptions(QLocalServer::WorldAccessOption);
|
m_server->setSocketOptions(QLocalServer::WorldAccessOption);
|
||||||
|
|
||||||
// Remove stale socket (Linux/Mac leave files behind)
|
// Remove stale socket (Linux/Mac leave files behind)
|
||||||
QLocalServer::removeServer("rcx-mcp");
|
QLocalServer::removeServer("ReclassMcpBridge");
|
||||||
|
|
||||||
if (!m_server->listen("rcx-mcp")) {
|
if (!m_server->listen("ReclassMcpBridge")) {
|
||||||
qWarning() << "[MCP] Failed to start server:" << m_server->errorString();
|
qWarning() << "[MCP] Failed to start server:" << m_server->errorString();
|
||||||
delete m_server;
|
delete m_server;
|
||||||
m_server = nullptr;
|
m_server = nullptr;
|
||||||
@@ -39,7 +39,7 @@ void McpBridge::start() {
|
|||||||
|
|
||||||
connect(m_server, &QLocalServer::newConnection,
|
connect(m_server, &QLocalServer::newConnection,
|
||||||
this, &McpBridge::onNewConnection);
|
this, &McpBridge::onNewConnection);
|
||||||
qDebug() << "[MCP] Server listening on pipe: rcx-mcp";
|
qDebug() << "[MCP] Server listening on pipe: ReclassMcpBridge";
|
||||||
}
|
}
|
||||||
|
|
||||||
void McpBridge::stop() {
|
void McpBridge::stop() {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<qresource prefix="/icons">
|
<qresource prefix="/icons">
|
||||||
<file alias="chevron-right.png">icons/chevron-right.png</file>
|
<file alias="chevron-right.png">icons/chevron-right.png</file>
|
||||||
<file alias="chevron-down.png">icons/chevron-down.png</file>
|
<file alias="chevron-down.png">icons/chevron-down.png</file>
|
||||||
|
<file alias="class.png">icons/class.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/fonts">
|
<qresource prefix="/fonts">
|
||||||
<file alias="JetBrainsMono.ttf">fonts/JetBrainsMono.ttf</file>
|
<file alias="JetBrainsMono.ttf">fonts/JetBrainsMono.ttf</file>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// rcx-mcp-stdio: Bridges stdin/stdout to QLocalSocket for MCP transport.
|
// ReclassMcpBridge: Bridges stdin/stdout to QLocalSocket for MCP transport.
|
||||||
// Claude Desktop spawns this process; it connects to the rcx-mcp named pipe
|
// Claude Desktop spawns this process; it connects to the ReclassMcpBridge named pipe
|
||||||
// inside the running Reclass application.
|
// inside the running Reclass application.
|
||||||
//
|
//
|
||||||
// stdin (from Claude) → QLocalSocket → McpBridge (in Reclass)
|
// stdin (from Claude) → QLocalSocket → McpBridge (in Reclass)
|
||||||
@@ -43,7 +43,7 @@ int main(int argc, char* argv[]) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
QObject::connect(socket, &QLocalSocket::disconnected, [&]() {
|
QObject::connect(socket, &QLocalSocket::disconnected, [&]() {
|
||||||
fprintf(stderr, "[rcx-mcp-stdio] Disconnected from server\n");
|
fprintf(stderr, "[ReclassMcpBridge] Disconnected from server\n");
|
||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -52,19 +52,19 @@ int main(int argc, char* argv[]) {
|
|||||||
#else
|
#else
|
||||||
QObject::connect(socket, QOverload<QLocalSocket::LocalSocketError>::of(&QLocalSocket::error), [&](QLocalSocket::LocalSocketError err) {
|
QObject::connect(socket, QOverload<QLocalSocket::LocalSocketError>::of(&QLocalSocket::error), [&](QLocalSocket::LocalSocketError err) {
|
||||||
#endif
|
#endif
|
||||||
fprintf(stderr, "[rcx-mcp-stdio] Socket error %d: %s\n",
|
fprintf(stderr, "[ReclassMcpBridge] Socket error %d: %s\n",
|
||||||
(int)err, socket->errorString().toUtf8().constData());
|
(int)err, socket->errorString().toUtf8().constData());
|
||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect to the named pipe
|
// Connect to the named pipe
|
||||||
socket->connectToServer("rcx-mcp");
|
socket->connectToServer("ReclassMcpBridge");
|
||||||
if (!socket->waitForConnected(5000)) {
|
if (!socket->waitForConnected(5000)) {
|
||||||
fprintf(stderr, "[rcx-mcp-stdio] Failed to connect to rcx-mcp pipe: %s\n",
|
fprintf(stderr, "[ReclassMcpBridge] Failed to connect to ReclassMcpBridge pipe: %s\n",
|
||||||
socket->errorString().toUtf8().constData());
|
socket->errorString().toUtf8().constData());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
fprintf(stderr, "[rcx-mcp-stdio] Connected to rcx-mcp\n");
|
fprintf(stderr, "[ReclassMcpBridge] Connected to ReclassMcpBridge\n");
|
||||||
|
|
||||||
// Stdin → socket: poll stdin with a timer (stdin isn't a socket on Windows)
|
// Stdin → socket: poll stdin with a timer (stdin isn't a socket on Windows)
|
||||||
QByteArray stdinBuf;
|
QByteArray stdinBuf;
|
||||||
|
|||||||
Reference in New Issue
Block a user