SettingsTable

SettingsTable is a QTableWidget subclass that renders a compact, borderless two-column “Setting / Value” grid. It is the standard building block for Blackchirp’s configuration surfaces: the hardware settings widgets, the overlay-configuration panels, the LIF and FTMW view-dock panels, the experiment-type setup pages, and the ZoomPanPlot curve-appearance editor all build their forms by appending rows to one of these tables rather than hand-rolling a QGridLayout of nested QGroupBox and QFormLayout blocks.

The table is non-selectable, read-only at the item level (value widgets are still interactive), sizes itself to its contents, and never shows a vertical scrollbar. Section heading bands are shaded with ThemeColors so they track the active light or dark palette. A new configuration UI should reach for SettingsTable before composing form layouts by hand; doing so keeps spacing, the borderless contract, and the heading-band styling consistent across every settings surface.

Row types

Rows are appended in document order and come in three flavors:

  • Value rows (addSettingRow) — a left-column label and a value widget in the right column. The two-widget overload places a pair of widgets side by side in the value cell (for example a line edit and a browse button, or a checkbox and a spin box); a horizontally expanding widget fills the cell while a fixed one keeps its hint.

  • Section rows (addSectionRow) — a bold, centered, theme-shaded band spanning both columns. It replaces the title of a former QGroupBox without nesting another frame.

  • Checkable section rows (addCheckableSectionRow) — a section band with a leading checkbox. Value rows bound to it with bindSectionRows collapse (via setRowHidden) when the box is unchecked and reappear when it is checked, reproducing a checkable-group-box without the frame.

Checkable sections

A checkable section row is flexible enough that a single row can stand in for several states of the form it replaces:

  • setSectionCheckable swaps the leading checkbox for a plain centered heading (and back) without destroying the underlying QCheckBox — so signal/slot connections and the bound-row wiring survive the mode change.

  • setBoundRowsEnabled greys a section’s rows out in place, the disabled counterpart of the hide-on-uncheck collapse.

  • setSectionVisible shows or hides a whole section as a unit, and is collapse-aware so a plain container section can wrap nested collapsible sub-sections without fighting their state.

  • sectionCheckBox returns the backing checkbox so a caller can drive or observe the collapse directly.

The first user-initiated expand of a section that started collapsed grows the enclosing window by exactly the revealed rows’ height so the new rows are not clipped behind the suppressed scrollbar; the growth happens once and is never reversed, so repeated toggling does not creep the window size.

API Reference

class SettingsTable : public QTableWidget

Compact, borderless two-column “Setting / Value” table.

Non-selectable, read-only, sizes to its contents, and never shows a vertical scrollbar. Rows come in three flavors:

  • a label + value-widget row (single widget, or a pair laid out side by side in the value cell);

  • a bold, spanned, theme-shaded section/heading row;

  • a spanned checkable section row whose bound child rows collapse (via setRowHidden) when the box is unchecked.

A checkable section row can be retitled, switched between a checkbox and a plain centered heading, and have its bound rows enabled or disabled without hiding them, so one row can serve both a non-checkable and a checkable state. The backing QCheckBox is created once and outlives every mode change, so external connections to it survive.

Public Functions

explicit SettingsTable(QWidget *parent = nullptr)
QSize minimumSizeHint() const override

Floor the vertical minimum at the content height.

The table never shows a vertical scrollbar, so it must never be laid out shorter than its (visible) rows or the bottom rows are silently clipped. This matters inside a QMainWindow dock area, where a panel shown next to an existing one is otherwise pinned at the widget minimum. Width still defers to the base class.

int addSettingRow(const QString &label, QWidget *value, const QString &tooltip = {})

Append a label + single value widget.

Returns:

the new row index.

int addSettingRow(const QString &label, QWidget *first, QWidget *second, const QString &tooltip = {})

Append a label + two widgets laid side by side in the value cell (e.g. checkbox + spinbox, input + button).

Returns:

the new row index.

int addSectionRow(const QString &title)

Append a bold, spanned, theme-shaded heading row.

Rendered through the same centered cell-widget mechanism as a checkable section row (a plain QLabel instead of a QCheckBox), so the band color is identical for checkable and non-checkable headings. The row is tracked like a checkable section (with a null checkbox) so it can be retitled, have rows bound to it, and be shown/hidden as a unit.

Returns:

the new row index.

int addCheckableSectionRow(const QString &title, bool checked, QCheckBox **outBox = nullptr)

Append a spanned heading row with a leading checkbox.

Rows registered with bindSectionRows() are shown/hidden whenever the box is toggled.

Parameters:

outBox – optional out-pointer to the created checkbox.

Returns:

the new row index.

void bindSectionRows(int sectionRow, const QList<int> &rows)

Bind value rows to a checkable section row created earlier and apply the box’s current state immediately.

void setSectionTitle(int sectionRow, const QString &title)

Retitle a section row in place (works for both the checkbox and the plain-heading rendering).

void setSectionCheckable(int sectionRow, bool checkable)

Switch a checkable section row between a leading checkbox and a plain centered heading.

The underlying QCheckBox is kept alive across the change (only the displayed cell content swaps), so connections to it and the bound-row wiring survive. A non-checkable section never collapses; its bound rows’ visibility is left to the caller / bound-row machinery (a plain heading does not itself hide anything).

void setBoundRowsEnabled(int sectionRow, bool enabled)

Enable or disable a section’s bound rows (and the section heading itself) without changing visibility — the disabled counterpart of the hide-on-uncheck collapse.

void setSectionVisible(int sectionRow, bool visible)

Show or hide a whole section as a unit: the heading row and every bound row.

Collapse-aware: when re-showing, a bound row that also belongs to a nested checkable section whose box is unchecked stays hidden, so a plain container section can wrap nested collapsible sub-sections without fighting their collapse state. Does not grow the enclosing window (used for programmatic context switches, not user toggles).

void applySectionVisibility(int sectionRow)

Re-apply the hidden state of a section’s bound rows from the checkbox’s current state, without growing the window.

Used after a signal-blocked programmatic setChecked() so the collapse stays consistent without the user-toggle window growth.

QCheckBox *sectionCheckBox(int sectionRow) const

The checkbox backing a checkable section row, or nullptr.

Stable across setSectionCheckable() mode changes.

Private Functions

void styleSectionItem(int row)

Paint the section-heading band on the row’s QTableWidgetItem (AlternateBase fill + EmphasisText, bold, centered). Both plain and checkable headings go through this single mechanism so the band is byte-for-byte identical; for a checkable row the transparent cell widget sits on top of this item.

void styleSectionText(QWidget *textWidget)

Emphasize a heading’s visible text widget (the QLabel or QCheckBox that sits over the band) to match the item styling.

void growEnclosingWindow(int extraHeight)

Add extraHeight px to the enclosing top-level window so newly-shown section rows are not clipped. Grow-only by construction (called only on expand). No-op without a window.

Private Members

QHash<int, Section> d_sections
struct Section

Public Members

QCheckBox *box = nullptr

always alive, even when plain

QWidget *wrap = nullptr

centered cell host

QLabel *plainLabel = nullptr

shown when non-checkable

QString title
bool checkable = true
QList<int> boundRows
bool growPending = false

One-shot: the enclosing window is grown on the first expand only, and only when the section started collapsed. Cleared once consumed so repeated toggling never re-grows.