diff --git a/src/compose.cpp b/src/compose.cpp index 2d91894..7c396f3 100644 --- a/src/compose.cpp +++ b/src/compose.cpp @@ -25,6 +25,7 @@ struct ComposeState { bool baseEmitted = false; // only first root struct shows base address bool compactColumns = false; // compact column mode: cap type width, overflow long types bool treeLines = false; // draw Unicode tree connectors in indentation + bool braceWrap = false; // opening brace on its own line QVector siblingStack; // per-depth: true = more siblings follow at this level uint64_t currentPtrBase = 0; // absolute addr of current pointer expansion target @@ -319,7 +320,24 @@ void composeParent(ComposeState& state, const NodeTree& tree, lm.effectiveNameW = nameW; headerText = fmt::fmtStructHeader(node, depth, node.collapsed, typeW, nameW, state.compactColumns); } - state.emitLine(headerText, lm); + // Brace wrapping: move trailing '{' to its own line + if (state.braceWrap && !node.collapsed && headerText.endsWith(QChar('{'))) { + headerText.chop(1); + // Remove trailing separator spaces + while (headerText.endsWith(' ')) headerText.chop(1); + state.emitLine(headerText, lm); + // Emit standalone brace line + LineMeta braceLm; + braceLm.nodeIdx = nodeIdx; + braceLm.nodeId = node.id; + braceLm.depth = depth; + braceLm.lineKind = LineKind::Header; + braceLm.foldLevel = computeFoldLevel(depth, true); + braceLm.markerMask = (1u << M_STRUCT_BG); + state.emitLine(fmt::indent(depth) + QStringLiteral("{"), braceLm); + } else { + state.emitLine(headerText, lm); + } } if (!node.collapsed || isArrayChild || isRootHeader) { @@ -840,9 +858,26 @@ void composeNode(ComposeState& state, const NodeTree& tree, lm.effectiveTypeW = ptrOverflow ? ptrTypeOverride.size() : typeW; lm.effectiveNameW = nameW; lm.pointerTargetName = ptrTargetName; - state.emitLine(fmt::fmtPointerHeader(node, depth, effectiveCollapsed, - prov, absAddr, ptrTypeOverride, - typeW, nameW, state.compactColumns), lm); + { + QString ptrText = fmt::fmtPointerHeader(node, depth, effectiveCollapsed, + prov, absAddr, ptrTypeOverride, + typeW, nameW, state.compactColumns); + if (state.braceWrap && !effectiveCollapsed && ptrText.endsWith(QChar('{'))) { + ptrText.chop(1); + while (ptrText.endsWith(' ')) ptrText.chop(1); + state.emitLine(ptrText, lm); + LineMeta braceLm; + braceLm.nodeIdx = nodeIdx; + braceLm.nodeId = node.id; + braceLm.depth = depth; + braceLm.lineKind = LineKind::Header; + braceLm.foldLevel = computeFoldLevel(depth, true); + braceLm.markerMask = lm.markerMask; + state.emitLine(fmt::indent(depth) + QStringLiteral("{"), braceLm); + } else { + state.emitLine(ptrText, lm); + } + } } if (!effectiveCollapsed) { @@ -936,10 +971,11 @@ void composeNode(ComposeState& state, const NodeTree& tree, } // anonymous namespace ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewRootId, - bool compactColumns, bool treeLines) { + bool compactColumns, bool treeLines, bool braceWrap) { ComposeState state; state.compactColumns = compactColumns; state.treeLines = treeLines; + state.braceWrap = braceWrap; // Precompute parent→children map for (int i = 0; i < tree.nodes.size(); i++) @@ -1082,6 +1118,18 @@ ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewR state.emitLine(cmdRowText, lm); } + // Brace wrapping: emit standalone "{" after CommandRow + if (state.braceWrap) { + LineMeta braceLm; + braceLm.nodeIdx = -1; + braceLm.nodeId = kCommandRowId; + braceLm.depth = 0; + braceLm.lineKind = LineKind::Header; + braceLm.foldLevel = SC_FOLDLEVELBASE; + braceLm.markerMask = 0; + state.emitLine(QStringLiteral("{"), braceLm); + } + const QVector& roots = childIndices(state, 0); for (int idx : roots) { diff --git a/src/controller.cpp b/src/controller.cpp index a5b7650..97e5fee 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -73,8 +73,8 @@ RcxDocument::RcxDocument(QObject* parent) } ComposeResult RcxDocument::compose(uint64_t viewRootId, bool compactColumns, - bool treeLines) const { - return rcx::compose(tree, *provider, viewRootId, compactColumns, treeLines); + bool treeLines, bool braceWrap) const { + return rcx::compose(tree, *provider, viewRootId, compactColumns, treeLines, braceWrap); } bool RcxDocument::save(const QString& path) { @@ -558,9 +558,9 @@ void RcxController::refresh() { // Compose against snapshot provider if active, otherwise real provider if (m_snapshotProv) - m_lastResult = rcx::compose(m_doc->tree, *m_snapshotProv, m_viewRootId, m_compactColumns, m_treeLines); + m_lastResult = rcx::compose(m_doc->tree, *m_snapshotProv, m_viewRootId, m_compactColumns, m_treeLines, m_braceWrap); else - m_lastResult = m_doc->compose(m_viewRootId, m_compactColumns, m_treeLines); + m_lastResult = m_doc->compose(m_viewRootId, m_compactColumns, m_treeLines, m_braceWrap); s_composeDoc = nullptr; @@ -2444,6 +2444,7 @@ void RcxController::updateCommandRow() { .arg(elide(src, 40), elide(addr, 24)); // Build row 2: root class type + name (uses current view root) + QString brace = m_braceWrap ? QString() : QStringLiteral(" {"); QString row2; if (m_viewRootId != 0) { int vi = m_doc->tree.indexOfId(m_viewRootId); @@ -2451,8 +2452,8 @@ void RcxController::updateCommandRow() { const auto& n = m_doc->tree.nodes[vi]; QString keyword = n.resolvedClassKeyword(); QString className = n.structTypeName.isEmpty() ? n.name : n.structTypeName; - row2 = QStringLiteral("%1 %2 {") - .arg(keyword, className.isEmpty() ? QStringLiteral("NoName") : className); + row2 = QStringLiteral("%1 %2%3") + .arg(keyword, className.isEmpty() ? QStringLiteral("NoName") : className, brace); } } if (row2.isEmpty()) { @@ -2462,14 +2463,14 @@ void RcxController::updateCommandRow() { if (n.parentId == 0 && n.kind == NodeKind::Struct) { QString keyword = n.resolvedClassKeyword(); QString className = n.structTypeName.isEmpty() ? n.name : n.structTypeName; - row2 = QStringLiteral("%1 %2 {") - .arg(keyword, className.isEmpty() ? QStringLiteral("NoName") : className); + row2 = QStringLiteral("%1 %2%3") + .arg(keyword, className.isEmpty() ? QStringLiteral("NoName") : className, brace); break; } } } if (row2.isEmpty()) - row2 = QStringLiteral("struct NoName {"); + row2 = QStringLiteral("struct NoName") + brace; QString combined = QStringLiteral("[\u25B8] ") + row + QStringLiteral(" ") + row2; @@ -3261,6 +3262,11 @@ void RcxController::setTreeLines(bool v) { refresh(); } +void RcxController::setBraceWrap(bool v) { + m_braceWrap = v; + refresh(); +} + void RcxController::setupAutoRefresh() { int ms = QSettings("Reclass", "Reclass").value("refreshMs", 660).toInt(); m_refreshTimer = new QTimer(this); diff --git a/src/controller.h b/src/controller.h index 2823293..0641dc4 100644 --- a/src/controller.h +++ b/src/controller.h @@ -41,7 +41,7 @@ public: } ComposeResult compose(uint64_t viewRootId = 0, bool compactColumns = false, - bool treeLines = false) const; + bool treeLines = false, bool braceWrap = false) const; bool save(const QString& path); bool load(const QString& path); void loadData(const QString& binaryPath); @@ -130,6 +130,7 @@ public: void setRefreshInterval(int ms); void setCompactColumns(bool v); void setTreeLines(bool v); + void setBraceWrap(bool v); void resetProvider(); // MCP bridge accessors @@ -169,6 +170,7 @@ private: bool m_suppressRefresh = false; bool m_compactColumns = false; bool m_treeLines = false; + bool m_braceWrap = false; uint64_t m_viewRootId = 0; // ── Saved sources for quick-switch ── diff --git a/src/core.h b/src/core.h index c0bfdf7..33717eb 100644 --- a/src/core.h +++ b/src/core.h @@ -1031,6 +1031,7 @@ namespace fmt { // ── Compose function forward declaration ── ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewRootId = 0, - bool compactColumns = false, bool treeLines = false); + bool compactColumns = false, bool treeLines = false, + bool braceWrap = false); } // namespace rcx diff --git a/src/main.cpp b/src/main.cpp index 2378764..e7d80fa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1450,6 +1450,7 @@ QDockWidget* MainWindow::createTab(RcxDocument* doc) { // Apply global compact columns setting to new tab ctrl->setCompactColumns(QSettings("Reclass", "Reclass").value("compactColumns", true).toBool()); ctrl->setTreeLines(QSettings("Reclass", "Reclass").value("treeLines", false).toBool()); + ctrl->setBraceWrap(QSettings("Reclass", "Reclass").value("braceWrap", false).toBool()); // Give every controller the shared document list for cross-tab type visibility ctrl->setProjectDocuments(&m_allDocs); @@ -2182,6 +2183,7 @@ void MainWindow::showOptionsDialog() { current.autoStartMcp = QSettings("Reclass", "Reclass").value("autoStartMcp", true).toBool(); current.refreshMs = QSettings("Reclass", "Reclass").value("refreshMs", 660).toInt(); current.generatorAsserts = QSettings("Reclass", "Reclass").value("generatorAsserts", false).toBool(); + current.braceWrap = QSettings("Reclass", "Reclass").value("braceWrap", false).toBool(); OptionsDialog dlg(current, this); if (dlg.exec() != QDialog::Accepted) return; // OptionsDialog doesn't apply anything. Only apply on OK @@ -2216,6 +2218,12 @@ void MainWindow::showOptionsDialog() { if (r.generatorAsserts != current.generatorAsserts) QSettings("Reclass", "Reclass").setValue("generatorAsserts", r.generatorAsserts); + + if (r.braceWrap != current.braceWrap) { + QSettings("Reclass", "Reclass").setValue("braceWrap", r.braceWrap); + for (auto& tab : m_tabs) + tab.ctrl->setBraceWrap(r.braceWrap); + } } void MainWindow::setEditorFont(const QString& fontName) { diff --git a/src/optionsdialog.cpp b/src/optionsdialog.cpp index 0af5d76..9d54a58 100644 --- a/src/optionsdialog.cpp +++ b/src/optionsdialog.cpp @@ -122,6 +122,10 @@ OptionsDialog::OptionsDialog(const OptionsResult& current, QWidget* parent) m_showIconCheck->setChecked(current.showIcon); visualLayout->addRow(m_showIconCheck); + m_braceWrapCheck = new QCheckBox("Opening brace on new line"); + m_braceWrapCheck->setChecked(current.braceWrap); + visualLayout->addRow(m_braceWrapCheck); + generalLayout->addWidget(visualGroup); generalLayout->addStretch(); @@ -212,6 +216,7 @@ OptionsResult OptionsDialog::result() const { r.autoStartMcp = m_autoMcpCheck->isChecked(); r.refreshMs = m_refreshSpin->value(); r.generatorAsserts = m_assertCheck->isChecked(); + r.braceWrap = m_braceWrapCheck->isChecked(); return r; } diff --git a/src/optionsdialog.h b/src/optionsdialog.h index aaa4d92..b690615 100644 --- a/src/optionsdialog.h +++ b/src/optionsdialog.h @@ -18,6 +18,7 @@ struct OptionsResult { bool autoStartMcp = true; int refreshMs = 660; bool generatorAsserts = false; + bool braceWrap = false; }; class OptionsDialog : public QDialog { @@ -41,6 +42,7 @@ private: QCheckBox* m_autoMcpCheck = nullptr; QSpinBox* m_refreshSpin = nullptr; QCheckBox* m_assertCheck = nullptr; + QCheckBox* m_braceWrapCheck = nullptr; // searchable keywords per leaf tree item QHash m_pageKeywords;