feat: option to have class opening brace on new line

This commit is contained in:
Sen66
2026-03-05 13:43:41 +01:00
parent ed1bfd04cd
commit 0697ce4853
7 changed files with 88 additions and 16 deletions

View File

@@ -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<bool> 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<int>& roots = childIndices(state, 0);
for (int idx : roots) {

View File

@@ -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);

View File

@@ -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 ──

View File

@@ -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

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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<QTreeWidgetItem*, QStringList> m_pageKeywords;