diff --git a/GUI/View/Device/AxisPropertyForm.cpp b/GUI/View/Device/AxisPropertyForm.cpp
index 4f98d913acd01f7313b6eba4859e578bac2dee31..88fb2fdce292fa7f549cc16f5303af740cc395c1 100644
--- a/GUI/View/Device/AxisPropertyForm.cpp
+++ b/GUI/View/Device/AxisPropertyForm.cpp
@@ -17,15 +17,16 @@
 #include "GUI/Model/Project/ProjectDocument.h"
 #include "GUI/View/Numeric/DoubleSpinBox.h"
 #include "GUI/View/Numeric/NumWidgetUtil.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 
 AxisPropertyForm::AxisPropertyForm(QWidget* parent, const QString& groupTitle,
                                    AxisProperty* axisProperty, QString nbinsTooltip)
-    : QGroupBox(groupTitle, parent)
+    : CollapsibleGroupBox(groupTitle, parent, axisProperty->expandGroupBox)
     , m_axisProperty(axisProperty)
 {
-    auto* formLayout = new QFormLayout(this);
-    formLayout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
+    auto* layout = new QFormLayout;
+    body()->setLayout(layout);
+
+    layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
 
     m_nbinsSpinBox = GUI::Util::createIntSpinbox([axisProperty] { return axisProperty->nbins(); },
                                                  [this, axisProperty](int v) {
@@ -34,13 +35,10 @@ AxisPropertyForm::AxisPropertyForm(QWidget* parent, const QString& groupTitle,
                                                      gProjectDocument.value()->setModified();
                                                  },
                                                  RealLimits::nonnegative(), nbinsTooltip);
-    formLayout->addRow("# bins:", m_nbinsSpinBox);
-
-
-    m_minSpinBox = GUI::Util::createDoubleSpinBoxRow(formLayout, axisProperty->min());
-    m_maxSpinBox = GUI::Util::createDoubleSpinBoxRow(formLayout, axisProperty->max());
+    layout->addRow("# bins:", m_nbinsSpinBox);
 
-    GroupBoxCollapser::installIntoGroupBox2(this, m_axisProperty->expandGroupBox);
+    m_minSpinBox = GUI::Util::createDoubleSpinBoxRow(layout, axisProperty->min());
+    m_maxSpinBox = GUI::Util::createDoubleSpinBoxRow(layout, axisProperty->max());
 
     connect(m_minSpinBox, &DoubleSpinBox::baseValueChanged, [this](double v) {
         if (m_axisProperty->min() != v) {
diff --git a/GUI/View/Device/AxisPropertyForm.h b/GUI/View/Device/AxisPropertyForm.h
index dc0f534702883eba0ac77ea147d7cb66d5ee3ea3..caf626f9ed11787807e11a0cc58f01836a9c3fbc 100644
--- a/GUI/View/Device/AxisPropertyForm.h
+++ b/GUI/View/Device/AxisPropertyForm.h
@@ -15,18 +15,18 @@
 #ifndef BORNAGAIN_GUI_VIEW_DEVICE_AXISPROPERTYFORM_H
 #define BORNAGAIN_GUI_VIEW_DEVICE_AXISPROPERTYFORM_H
 
-#include <QGroupBox>
+#include "GUI/View/Widget/GroupBoxes.h"
 
-class QSpinBox;
 class AxisProperty;
 class DoubleSpinBox;
+class QSpinBox;
 
 //! Use this to edit an AxisProperty.
 //!
 //! An AxisProperty handles values for a EquiDivision. The values will be already written into the
 //! data element. For each change of values, a signal will be emitted. Use this e.g. to update
 //! dependent values or to set the document to modified.
-class AxisPropertyForm : public QGroupBox {
+class AxisPropertyForm : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     AxisPropertyForm(QWidget* parent, const QString& groupTitle, AxisProperty* axisProperty,
diff --git a/GUI/View/Device/BackgroundForm.cpp b/GUI/View/Device/BackgroundForm.cpp
index 1d857b492d802315f15898b9117e8d1050187cdb..be97a24f045362404101c0f86ed5fc454c26e15e 100644
--- a/GUI/View/Device/BackgroundForm.cpp
+++ b/GUI/View/Device/BackgroundForm.cpp
@@ -18,16 +18,15 @@
 #include "GUI/Model/Device/InstrumentItems.h"
 #include "GUI/View/Numeric/ComboUtil.h"
 #include "GUI/View/Numeric/DoubleSpinBox.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 #include <QFormLayout>
 
 BackgroundForm::BackgroundForm(QWidget* parent, InstrumentItem* instrument)
-    : QGroupBox("Environment", parent)
+    : CollapsibleGroupBox("Environment", parent, instrument->expandEnvironment)
     , m_instrument(instrument)
 {
-    ASSERT(instrument);
-    m_formLayout = new QFormLayout(this);
+    m_formLayout = new QFormLayout;
     m_formLayout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
+    body()->setLayout(m_formLayout);
 
     auto* backgroundTypeCombo =
         GUI::Util::createComboBoxFromProperty(instrument->backgroundSelection(), [this](int) {
@@ -37,7 +36,6 @@ BackgroundForm::BackgroundForm(QWidget* parent, InstrumentItem* instrument)
     m_formLayout->addRow("Background type:", backgroundTypeCombo);
 
     createBackgroundWidgets();
-    GroupBoxCollapser::installIntoGroupBox2(this, instrument->expandEnvironment);
 }
 
 void BackgroundForm::createBackgroundWidgets()
diff --git a/GUI/View/Device/BackgroundForm.h b/GUI/View/Device/BackgroundForm.h
index f37059e0cd5a94064b6f0a6503c16926dea6054d..54aa902f435efd294fed9ab9a48ca12c8563c189 100644
--- a/GUI/View/Device/BackgroundForm.h
+++ b/GUI/View/Device/BackgroundForm.h
@@ -15,7 +15,7 @@
 #ifndef BORNAGAIN_GUI_VIEW_DEVICE_BACKGROUNDFORM_H
 #define BORNAGAIN_GUI_VIEW_DEVICE_BACKGROUNDFORM_H
 
-#include <QGroupBox>
+#include "GUI/View/Widget/GroupBoxes.h"
 
 class InstrumentItem;
 class QFormLayout;
@@ -23,7 +23,7 @@ class QFormLayout;
 //! Environment editor (i.e. background) for instrument editors.
 //! Operates on InstrumentItem.
 
-class BackgroundForm : public QGroupBox {
+class BackgroundForm : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     BackgroundForm(QWidget* parent, InstrumentItem* instrument);
diff --git a/GUI/View/Device/FootprintForm.cpp b/GUI/View/Device/FootprintForm.cpp
index 1202b7ee5b86abceef203049871124a9995bdc6a..4afc058c7d63667a8ec4d5cd10364d038dc592c7 100644
--- a/GUI/View/Device/FootprintForm.cpp
+++ b/GUI/View/Device/FootprintForm.cpp
@@ -19,15 +19,14 @@
 #include "GUI/Model/Device/InstrumentItems.h"
 #include "GUI/View/Numeric/ComboUtil.h"
 #include "GUI/View/Numeric/DoubleSpinBox.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 #include <QFormLayout>
 
 FootprintForm::FootprintForm(QWidget* parent, SourceItem* item)
-    : QGroupBox("Footprint correction", parent)
+    : CollapsibleGroupBox("Footprint correction", parent, item->expandFootprint)
     , m_item(item)
 {
-    ASSERT(item);
-    m_formLayout = new QFormLayout(this);
+    m_formLayout = new QFormLayout;
+    body()->setLayout(m_formLayout);
     m_formLayout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
     ASSERT(item->footprintSelection().currentItem());
     auto* typeCombo =
@@ -37,8 +36,6 @@ FootprintForm::FootprintForm(QWidget* parent, SourceItem* item)
         });
     m_formLayout->addRow("Type:", typeCombo);
 
-    GroupBoxCollapser::installIntoGroupBox2(this, item->expandFootprint);
-
     updateFootprintWidgets();
 }
 
diff --git a/GUI/View/Device/FootprintForm.h b/GUI/View/Device/FootprintForm.h
index 25b9da84d5306b8fa63b3ae4416787ad0db91e09..56560be7d95c8911cc2284704645fc5819910b8b 100644
--- a/GUI/View/Device/FootprintForm.h
+++ b/GUI/View/Device/FootprintForm.h
@@ -15,15 +15,15 @@
 #ifndef BORNAGAIN_GUI_VIEW_DEVICE_FOOTPRINTFORM_H
 #define BORNAGAIN_GUI_VIEW_DEVICE_FOOTPRINTFORM_H
 
-#include <QGroupBox>
+#include "GUI/View/Widget/GroupBoxes.h"
 
-class SourceItem;
 class QFormLayout;
+class SourceItem;
 
 //! FootprintCorrection editor (i.e. background) for instrument editors.
 //! Operates on InstrumentItem.
 
-class FootprintForm : public QGroupBox {
+class FootprintForm : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     FootprintForm(QWidget* parent, SourceItem* item);
diff --git a/GUI/View/Instrument/AlphaScanEditor.cpp b/GUI/View/Instrument/AlphaScanEditor.cpp
index 5646d99a8923f7bce5d38ccf51a5445ed581ee58..744c1a98aba70888a6e70f72d0a9c4dba783913e 100644
--- a/GUI/View/Instrument/AlphaScanEditor.cpp
+++ b/GUI/View/Instrument/AlphaScanEditor.cpp
@@ -18,18 +18,16 @@
 #include "GUI/View/Device/SphericalAxisForm.h"
 #include "GUI/View/Instrument/DistributionEditor.h"
 #include "GUI/View/Instrument/DistributionPlot.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 
 AlphaScanEditor::AlphaScanEditor(QWidget* parent, GrazingScanItem* item, bool allow_distr)
-    : QGroupBox("Grazing angles", parent)
+    : CollapsibleGroupBox("Grazing angles", parent, item->expandGroupBox)
     , m_item(item)
 {
-    auto* hLayout = new QHBoxLayout(this);
-    hLayout->setSpacing(50);
+    auto* layout = new QHBoxLayout;
+    body()->setLayout(layout);
 
     auto* gform = new QFormLayout;
-    gform->setSpacing(6);
-    hLayout->addLayout(gform);
+    layout->addLayout(gform);
 
     //... axis type combo
     auto* typeComboBox = new QComboBox(this);
@@ -63,11 +61,9 @@ AlphaScanEditor::AlphaScanEditor(QWidget* parent, GrazingScanItem* item, bool al
     m_plot = new DistributionPlot(this);
     m_plot->setFixedSize(280, 170);
     m_plot->setShowMouseCoords(false);
-    hLayout->addWidget(m_plot);
-    hLayout->setAlignment(m_plot, Qt::AlignTop);
-    hLayout->addStretch(1);
-
-    GroupBoxCollapser::installIntoGroupBox2(this, item->expandGroupBox);
+    layout->addWidget(m_plot);
+    layout->setAlignment(m_plot, Qt::AlignTop);
+    layout->addStretch(1);
 
     updatePlot();
 }
diff --git a/GUI/View/Instrument/AlphaScanEditor.h b/GUI/View/Instrument/AlphaScanEditor.h
index e0ec0327b2c7f91bc1bbcb0232da3ff0c1e09fa0..b43650ae42d9f3ee45329c754ba6590d51042597 100644
--- a/GUI/View/Instrument/AlphaScanEditor.h
+++ b/GUI/View/Instrument/AlphaScanEditor.h
@@ -15,7 +15,7 @@
 #ifndef BORNAGAIN_GUI_VIEW_INSTRUMENT_ALPHASCANEDITOR_H
 #define BORNAGAIN_GUI_VIEW_INSTRUMENT_ALPHASCANEDITOR_H
 
-#include <QGroupBox>
+#include "GUI/View/Widget/GroupBoxes.h"
 
 class DistributionPlot;
 class DistributionSelector;
@@ -24,7 +24,7 @@ class SphericalAxisForm;
 
 //! Editor for scanning inclination angles
 
-class AlphaScanEditor : public QGroupBox {
+class AlphaScanEditor : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     AlphaScanEditor(QWidget* parent, GrazingScanItem* item, bool allow_distr);
diff --git a/GUI/View/Instrument/DetectorEditor.cpp b/GUI/View/Instrument/DetectorEditor.cpp
index f184e4b59d4bc28c914a9cc2b8817088734a69e3..59c43b7747c2873ed6b4cd483bbf34c7c4b3a448 100644
--- a/GUI/View/Instrument/DetectorEditor.cpp
+++ b/GUI/View/Instrument/DetectorEditor.cpp
@@ -21,7 +21,6 @@
 #include "GUI/View/Numeric/DoubleSpinBox.h"
 #include "GUI/View/Numeric/NumWidgetUtil.h"
 #include "GUI/View/Numeric/SafeSpinBox.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 #include <QFormLayout>
 
 namespace {
@@ -48,22 +47,16 @@ DoubleSpinBox* createSpinBox(QFormLayout* parentFormLayout, DoubleProperty& d,
 
 
 DetectorEditor::DetectorEditor(QWidget* parent, GISASInstrumentItem* instrItem)
-    : QGroupBox("Detector parameters", parent)
+    : CollapsibleGroupBox("Detector parameters", parent, instrItem->expandDetector)
 {
-    ASSERT(instrItem);
-
-    auto* formLayout = new QFormLayout(this);
-
-    GroupBoxCollapser::installIntoGroupBox2(this, instrItem->expandDetector);
+    auto* layout = new QVBoxLayout;
+    body()->setLayout(layout);
 
     DetectorItem* detectorItem = instrItem->detectorItem();
     ASSERT(detectorItem);
 
-    auto* bulk = new QVBoxLayout;
-    formLayout->addRow(bulk);
-
     auto* xyrow = new QHBoxLayout;
-    bulk->addLayout(xyrow);
+    layout->addLayout(xyrow);
 
     //... x-axis controls
 
@@ -87,7 +80,7 @@ DetectorEditor::DetectorEditor(QWidget* parent, GISASInstrumentItem* instrItem)
 
     ::createSpinBox(xAxisFormLayout, detectorItem->u0(), this);
 
-    auto* xAxisGroupBox = new HeinzGroupBox("X axis", this);
+    auto* xAxisGroupBox = new StaticGroupBox("X axis", this);
     xAxisGroupBox->body()->setLayout(xAxisFormLayout);
     xyrow->addWidget(xAxisGroupBox);
 
@@ -113,7 +106,7 @@ DetectorEditor::DetectorEditor(QWidget* parent, GISASInstrumentItem* instrItem)
 
     ::createSpinBox(yAxisFormLayout, detectorItem->v0(), this);
 
-    auto* yAxisGroupBox = new HeinzGroupBox("Y axis", this);
+    auto* yAxisGroupBox = new StaticGroupBox("Y axis", this);
     yAxisGroupBox->body()->setLayout(yAxisFormLayout);
     xyrow->addWidget(yAxisGroupBox);
 
@@ -122,7 +115,7 @@ DetectorEditor::DetectorEditor(QWidget* parent, GISASInstrumentItem* instrItem)
     auto resolutionLayout = new QFormLayout;
     resolutionLayout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
 
-    auto* resolutionForm = new HeinzGroupBox("Resolution function", this);
+    auto* resolutionForm = new StaticGroupBox("Resolution function", this);
     resolutionForm->body()->setLayout(resolutionLayout);
 
     auto updateResolutionForm = [parent = this, layout = resolutionLayout,
@@ -161,7 +154,7 @@ DetectorEditor::DetectorEditor(QWidget* parent, GISASInstrumentItem* instrItem)
     //... detector alignment controls
 
     auto* aliLine = new QHBoxLayout;
-    bulk->addLayout(aliLine);
+    layout->addLayout(aliLine);
 
     QFormLayout* alignmentForm = new QFormLayout;
     alignmentForm->addRow("Alignment:",
diff --git a/GUI/View/Instrument/DetectorEditor.h b/GUI/View/Instrument/DetectorEditor.h
index 8110729ccce02da865334f865eafac445c24608a..2b6be562838115a21da7e36f0346cdf6c561af5e 100644
--- a/GUI/View/Instrument/DetectorEditor.h
+++ b/GUI/View/Instrument/DetectorEditor.h
@@ -15,13 +15,13 @@
 #ifndef BORNAGAIN_GUI_VIEW_INSTRUMENT_DETECTOREDITOR_H
 #define BORNAGAIN_GUI_VIEW_INSTRUMENT_DETECTOREDITOR_H
 
-#include <QGroupBox>
+#include "GUI/View/Widget/GroupBoxes.h"
 
 class GISASInstrumentItem;
 
 //! Detector section in GISAS instrument form.
 
-class DetectorEditor : public QGroupBox {
+class DetectorEditor : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     DetectorEditor(QWidget* parent, GISASInstrumentItem* item);
diff --git a/GUI/View/Instrument/DistributionEditor.cpp b/GUI/View/Instrument/DistributionEditor.cpp
index 9a56103ee971d1be07fe5f14150d76cec8d5dc12..5349ab84cbd25db6403e167553ede871d592ce18 100644
--- a/GUI/View/Instrument/DistributionEditor.cpp
+++ b/GUI/View/Instrument/DistributionEditor.cpp
@@ -21,7 +21,6 @@
 #include "GUI/View/Numeric/DoubleSpinBox.h"
 #include "GUI/View/Numeric/NumWidgetUtil.h"
 #include "GUI/View/Numeric/ScientificSpinBox.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 
 //==================================================================================================
 // DistributionSelector
@@ -163,20 +162,20 @@ DistributionEditor::DistributionEditor(const QString& title,
                                        const std::optional<MeanConfig>& mean_config,
                                        GUI::ID::Distributions distributions, QWidget* parent,
                                        BeamDistributionItem* item, bool allow_distr)
-    : QGroupBox(title, parent)
+    : CollapsibleGroupBox(title, parent, item->expandGroupBox)
 {
-    auto* hLayout = new QVBoxLayout(this);
+    auto* layout = new QVBoxLayout;
+    body()->setLayout(layout);
+
     m_selector = new DistributionSelector(mean_config, distributions, this, item, allow_distr);
-    hLayout->addWidget(m_selector);
-    hLayout->setSpacing(50);
+    layout->addWidget(m_selector);
+    layout->setSpacing(50);
 
     m_plot = new DistributionPlot(this);
     m_plot->setFixedSize(280, 170);
     m_plot->setShowMouseCoords(false);
-    hLayout->addWidget(m_plot);
-    hLayout->addStretch(1);
-
-    GroupBoxCollapser::installIntoGroupBox2(this, item->expandGroupBox);
+    layout->addWidget(m_plot);
+    layout->addStretch(1);
 
     connect(m_selector, &DistributionSelector::distributionChanged, this,
             &DistributionEditor::distributionChanged);
diff --git a/GUI/View/Instrument/DistributionEditor.h b/GUI/View/Instrument/DistributionEditor.h
index ba6bee719f17ab1e0365ad9368959ec38ccbea11..2ddba42563894df8156807312f9fd9b9a604e171 100644
--- a/GUI/View/Instrument/DistributionEditor.h
+++ b/GUI/View/Instrument/DistributionEditor.h
@@ -16,7 +16,7 @@
 #define BORNAGAIN_GUI_VIEW_INSTRUMENT_DISTRIBUTIONEDITOR_H
 
 #include "GUI/Support/Data/ID.h"
-#include <QGroupBox>
+#include "GUI/View/Widget/GroupBoxes.h"
 #include <optional>
 
 class BeamDistributionItem;
@@ -78,7 +78,7 @@ private:
 /// DistributionSelector as above but contained in a GroupBox with a title and a button
 /// to open the distribution dialog
 
-class DistributionEditor : public QGroupBox {
+class DistributionEditor : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     DistributionEditor(const QString& title, const std::optional<MeanConfig>& mean_config,
diff --git a/GUI/View/Instrument/GISASBeamEditor.cpp b/GUI/View/Instrument/GISASBeamEditor.cpp
index a64cb072c91d7a76589d841e848aaecc060e8cbd..b7098f9773b6c3b950d1d888f48c300741f85e42 100644
--- a/GUI/View/Instrument/GISASBeamEditor.cpp
+++ b/GUI/View/Instrument/GISASBeamEditor.cpp
@@ -20,18 +20,14 @@
 #include "GUI/View/Device/FootprintForm.h"
 #include "GUI/View/Instrument/DistributionEditor.h"
 #include "GUI/View/Numeric/FixupDoubleValidator.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 #include <QFormLayout>
 #include <QLineEdit>
 
 GISASBeamEditor::GISASBeamEditor(QWidget* parent, BeamItem* item)
-    : QGroupBox("Beam parameters", parent)
+    : CollapsibleGroupBox("Beam parameters", parent, item->expandBeamParameters)
 {
-    ASSERT(item);
-
-    auto* vLayout = new QVBoxLayout(this);
-    vLayout->setContentsMargins(30, 8, 0, 0);
-    GroupBoxCollapser::installIntoGroupBox2(this, item->expandBeamParameters);
+    auto* layout = new QVBoxLayout;
+    body()->setLayout(layout);
 
     // Beam intensity row
     auto* intensityEditor = new QLineEdit(this);
@@ -59,7 +55,7 @@ GISASBeamEditor::GISASBeamEditor(QWidget* parent, BeamItem* item)
     auto* form = new QFormLayout;
     form->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
     form->addRow("Intensity:", intensityEditor);
-    vLayout->addLayout(form);
+    layout->addLayout(form);
 
     // Main row: fixed values or distributions for wavelength and angles of incidence
     auto* wavelengthEditor = new DistributionEditor(
@@ -83,9 +79,9 @@ GISASBeamEditor::GISASBeamEditor(QWidget* parent, BeamItem* item)
     mainrow->addWidget(wavelengthEditor);
     mainrow->addWidget(inclinationEditor);
     mainrow->addWidget(azimuthalEditor);
-    vLayout->addLayout(mainrow);
+    layout->addLayout(mainrow);
 
     auto* footprintEditor = new FootprintForm(this, item);
     connect(footprintEditor, &FootprintForm::dataChanged, this, &GISASBeamEditor::dataChanged);
-    vLayout->addWidget(footprintEditor);
+    layout->addWidget(footprintEditor);
 }
diff --git a/GUI/View/Instrument/GISASBeamEditor.h b/GUI/View/Instrument/GISASBeamEditor.h
index 2dcf622503169c6b7fd35fd3fb1dc2e50a505c5c..09040e1e491ec8ab19be54d9219589665266ff7a 100644
--- a/GUI/View/Instrument/GISASBeamEditor.h
+++ b/GUI/View/Instrument/GISASBeamEditor.h
@@ -15,13 +15,13 @@
 #ifndef BORNAGAIN_GUI_VIEW_INSTRUMENT_GISASBEAMEDITOR_H
 #define BORNAGAIN_GUI_VIEW_INSTRUMENT_GISASBEAMEDITOR_H
 
-#include <QGroupBox>
+#include "GUI/View/Widget/GroupBoxes.h"
 
 class BeamItem;
 
 //! GISAS beam editor. Operates on GISASInstrumentItem.
 
-class GISASBeamEditor : public QGroupBox {
+class GISASBeamEditor : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     GISASBeamEditor(QWidget* parent, BeamItem* item);
diff --git a/GUI/View/Instrument/InstrumentLibraryEditor.cpp b/GUI/View/Instrument/InstrumentLibraryEditor.cpp
index b20431e1358c433f673dd1bf787425ff12a1ab7f..82ed74983b68a4c2935abf0de2172b2797987fe9 100644
--- a/GUI/View/Instrument/InstrumentLibraryEditor.cpp
+++ b/GUI/View/Instrument/InstrumentLibraryEditor.cpp
@@ -24,7 +24,6 @@
 #include "GUI/View/Instrument/SpecularInstrumentEditor.h"
 #include "GUI/View/Item/ItemViewOverlayButtons.h"
 #include "GUI/View/Widget/ApplicationSettings.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 #include "ui_InstrumentLibraryEditor.h"
 #include <QAction>
 #include <QFormLayout>
diff --git a/GUI/View/Instrument/InstrumentLibraryEditor.h b/GUI/View/Instrument/InstrumentLibraryEditor.h
index 018aaf94238e5d4b126c4f817873c3996ea2710c..dc496fead69815978faa9d3c129a4f2c837280d6 100644
--- a/GUI/View/Instrument/InstrumentLibraryEditor.h
+++ b/GUI/View/Instrument/InstrumentLibraryEditor.h
@@ -16,6 +16,7 @@
 #define BORNAGAIN_GUI_VIEW_INSTRUMENT_INSTRUMENTLIBRARYEDITOR_H
 
 #include "GUI/View/Instrument/InstrumentsTreeModel.h"
+#include "GUI/View/Widget/GroupBoxes.h"
 #include <QDialog>
 
 class InstrumentItem;
diff --git a/GUI/View/Instrument/InstrumentView.cpp b/GUI/View/Instrument/InstrumentView.cpp
index dd194f33b44118d9e35804fe5638225c3e82ec72..a365f3515ad1aae86aa58f52d93e565e788e62f5 100644
--- a/GUI/View/Instrument/InstrumentView.cpp
+++ b/GUI/View/Instrument/InstrumentView.cpp
@@ -99,11 +99,11 @@ void InstrumentView::createWidgetsForCurrentInstrument()
 
     //... Groupbox with instrument name and description (same layout for all instrument types)
 
-    auto* g = new QGroupBox(m_scrollArea);
-    g->setTitle(QString("Information (%1 instrument)").arg(currentInstrument->instrumentType()));
+    auto title = QString("Information (%1 instrument)").arg(currentInstrument->instrumentType());
+    auto* g = new CollapsibleGroupBox(title, m_scrollArea, currentInstrument->expandInfo);
     g->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
-
-    auto* formLayout = new QFormLayout(g);
+    auto* formLayout = new QFormLayout;
+    g->body()->setLayout(formLayout);
     layout->addWidget(g);
 
     auto* nameEdit = new QLineEdit(g);
@@ -122,8 +122,6 @@ void InstrumentView::createWidgetsForCurrentInstrument()
     });
     formLayout->addRow("Description:", descriptionEdit);
 
-    GroupBoxCollapser::installIntoGroupBox2(g, currentInstrument->expandInfo);
-
     //... All remaining content depends on instrument type
 
     auto* ec = m_document->multiNotifier();
diff --git a/GUI/View/Instrument/OffspecDetectorEditor.cpp b/GUI/View/Instrument/OffspecDetectorEditor.cpp
index 18dfa6648a4721e1e8e93caf56e3defcc7bdb9f2..d6625057d90fcca0def25114f372bdf8813b9699 100644
--- a/GUI/View/Instrument/OffspecDetectorEditor.cpp
+++ b/GUI/View/Instrument/OffspecDetectorEditor.cpp
@@ -17,25 +17,12 @@
 #include "GUI/Model/Detector/OffspecDetectorItem.h"
 #include "GUI/Model/Device/InstrumentItems.h"
 #include "GUI/View/Device/AxisPropertyForm.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 #include <QFormLayout>
 
-
 OffspecDetectorEditor::OffspecDetectorEditor(QWidget* parent, OffspecInstrumentItem* instrument)
-    : QGroupBox("Detector parameters", parent)
+    : CollapsibleGroupBox("Detector parameters", parent, instrument->expandDetector)
     , m_instrument(instrument)
 {
-    ASSERT(instrument);
-    m_formLayout = new QFormLayout(this);
-
-    auto* collapser = GroupBoxCollapser::installIntoGroupBox(this);
-    collapser->setExpanded(instrument->expandDetector);
-    connect(collapser, &GroupBoxCollapser::toggled, this,
-            [instrument](bool b) { instrument->expandDetector = b; });
-
-    while (m_formLayout->rowCount() > 1)
-        m_formLayout->removeRow(1);
-
     OffspecDetectorItem* detector_item = m_instrument->detectorItem();
 
     auto* grid = new QGridLayout;
@@ -57,5 +44,5 @@ OffspecDetectorEditor::OffspecDetectorEditor(QWidget* parent, OffspecInstrumentI
 
     connect(alphaForm, &AxisPropertyForm::dataChanged, this, &OffspecDetectorEditor::dataChanged);
 
-    m_formLayout->addRow(grid);
+    body()->setLayout(grid);
 }
diff --git a/GUI/View/Instrument/OffspecDetectorEditor.h b/GUI/View/Instrument/OffspecDetectorEditor.h
index 46eed19d5bca28eec4faedb20f751bd962fa3c37..dec2f40b56919dd993e1e7dc80b26dd4045e8f1a 100644
--- a/GUI/View/Instrument/OffspecDetectorEditor.h
+++ b/GUI/View/Instrument/OffspecDetectorEditor.h
@@ -15,12 +15,12 @@
 #ifndef BORNAGAIN_GUI_VIEW_INSTRUMENT_OFFSPECDETECTOREDITOR_H
 #define BORNAGAIN_GUI_VIEW_INSTRUMENT_OFFSPECDETECTOREDITOR_H
 
-#include <QGroupBox>
+#include "GUI/View/Widget/GroupBoxes.h"
 
 class OffspecInstrumentItem;
 class QFormLayout;
 
-class OffspecDetectorEditor : public QGroupBox {
+class OffspecDetectorEditor : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     OffspecDetectorEditor(QWidget* parent, OffspecInstrumentItem* item);
diff --git a/GUI/View/Instrument/PolarizationAnalysisEditor.cpp b/GUI/View/Instrument/PolarizationAnalysisEditor.cpp
index 852a657d5ae107c796201eaf70f296eccc1aaa53..330cfb144f7a499f6ed3c1563da719e7916615e0 100644
--- a/GUI/View/Instrument/PolarizationAnalysisEditor.cpp
+++ b/GUI/View/Instrument/PolarizationAnalysisEditor.cpp
@@ -17,16 +17,14 @@
 #include "GUI/Model/Device/InstrumentItems.h"
 #include "GUI/View/Numeric/DoubleSpinBox.h"
 #include "GUI/View/Numeric/NumWidgetUtil.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 #include <QLabel>
 
 PolarizationAnalysisEditor::PolarizationAnalysisEditor(QWidget* parent, InstrumentItem* instrument)
-    : QGroupBox("Polarization analysis", parent)
+    : CollapsibleGroupBox("Polarization analysis", parent, instrument->expandPolarizerAlanyzer)
     , m_instrument(instrument)
 {
-    ASSERT(instrument);
-
-    auto* layout = new QVBoxLayout(this);
+    auto* layout = new QVBoxLayout;
+    body()->setLayout(layout);
 
     // polarizer
     {
@@ -71,10 +69,6 @@ PolarizationAnalysisEditor::PolarizationAnalysisEditor(QWidget* parent, Instrume
             emit dataChanged();
         });
     }
-
-    // collapser
-
-    GroupBoxCollapser::installIntoGroupBox2(this, instrument->expandPolarizerAlanyzer);
 }
 
 DoubleSpinBox* PolarizationAnalysisEditor::createSpinBox(DoubleProperty& d)
diff --git a/GUI/View/Instrument/PolarizationAnalysisEditor.h b/GUI/View/Instrument/PolarizationAnalysisEditor.h
index 946645c06672f90099a2e754360037433352bc25..6a2a6445e183d50010aa9063dc438b485607d4b5 100644
--- a/GUI/View/Instrument/PolarizationAnalysisEditor.h
+++ b/GUI/View/Instrument/PolarizationAnalysisEditor.h
@@ -15,8 +15,8 @@
 #ifndef BORNAGAIN_GUI_VIEW_INSTRUMENT_POLARIZATIONANALYSISEDITOR_H
 #define BORNAGAIN_GUI_VIEW_INSTRUMENT_POLARIZATIONANALYSISEDITOR_H
 
+#include "GUI/View/Widget/GroupBoxes.h"
 #include <QFormLayout>
-#include <QGroupBox>
 
 class DoubleProperty;
 class DoubleSpinBox;
@@ -26,7 +26,7 @@ class VectorProperty;
 //! Polarization analysis editor (beam polarization, analyzer properties) for instrument editors.
 //! Operates on InstrumentItem.
 
-class PolarizationAnalysisEditor : public QGroupBox {
+class PolarizationAnalysisEditor : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     PolarizationAnalysisEditor(QWidget* parent, InstrumentItem* instrument);
diff --git a/GUI/View/Instrument/ScanEditor.cpp b/GUI/View/Instrument/ScanEditor.cpp
index 818a48cb893e793563512a0327cc5610ba673ed5..38ec5471759e50e414bb3274e5bd421d1b282b16 100644
--- a/GUI/View/Instrument/ScanEditor.cpp
+++ b/GUI/View/Instrument/ScanEditor.cpp
@@ -21,20 +21,18 @@
 #include "GUI/View/Instrument/AlphaScanEditor.h"
 #include "GUI/View/Instrument/DistributionEditor.h"
 #include "GUI/View/Numeric/FixupDoubleValidator.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 #include <QFormLayout>
 #include <QLineEdit>
 
 ScanEditor::ScanEditor(QWidget* parent, ScanItem* item, InstrumentNotifier* ec, bool allow_distr)
-    : QGroupBox("Beam and scan parameters", parent)
+    : CollapsibleGroupBox("Beam and scan parameters", parent, item->expandBeamParameters)
 {
-    ASSERT(item);
+    auto* layout = new QVBoxLayout;
+    body()->setLayout(layout);
 
-    auto* vLayout = new QVBoxLayout(this);
-    vLayout->setContentsMargins(30, 8, 0, 0);
     auto* form = new QFormLayout();
     form->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
-    vLayout->addLayout(form);
+    layout->addLayout(form);
     auto* intensityLineEdit = new QLineEdit(this);
     intensityLineEdit->setToolTip("Correction factor for normalized intensity");
     auto* validator = new FixupDoubleValidator(intensityLineEdit);
@@ -46,18 +44,16 @@ ScanEditor::ScanEditor(QWidget* parent, ScanItem* item, InstrumentNotifier* ec,
     auto* wavelengthEditor =
         new DistributionEditor("Wavelength", MeanConfig{true}, GUI::ID::Distributions::Symmetric,
                                this, item->wavelengthItem(), allow_distr);
-    vLayout->addWidget(wavelengthEditor);
+    layout->addWidget(wavelengthEditor);
 
     auto* inclinationEditor = new AlphaScanEditor(this, item->grazingScanItem(), allow_distr);
-    vLayout->addWidget(inclinationEditor);
+    layout->addWidget(inclinationEditor);
 
     auto* footprintEditor = new FootprintForm(this, item);
-    vLayout->addWidget(footprintEditor);
+    layout->addWidget(footprintEditor);
 
     intensityLineEdit->setText(QString::number(item->intensity()));
 
-    GroupBoxCollapser::installIntoGroupBox2(this, item->expandBeamParameters);
-
     // order of the next two connections is important! Indicators have to be recalculated first,
     // then updated (recalculation is done in EditController)
     connect(wavelengthEditor, &DistributionEditor::distributionChanged, ec,
diff --git a/GUI/View/Instrument/ScanEditor.h b/GUI/View/Instrument/ScanEditor.h
index 2144273669a75657efd759d05ddce40528b8f906..5e9b6a38083343ad2fa84bfd200d952e5818d8ba 100644
--- a/GUI/View/Instrument/ScanEditor.h
+++ b/GUI/View/Instrument/ScanEditor.h
@@ -15,14 +15,14 @@
 #ifndef BORNAGAIN_GUI_VIEW_INSTRUMENT_SCANEDITOR_H
 #define BORNAGAIN_GUI_VIEW_INSTRUMENT_SCANEDITOR_H
 
-#include <QGroupBox>
+#include "GUI/View/Widget/GroupBoxes.h"
 
-class ScanItem;
 class InstrumentNotifier;
+class ScanItem;
 
 //! Specular beam editor. Operates on ScanItem.
 
-class ScanEditor : public QGroupBox {
+class ScanEditor : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     ScanEditor(QWidget* parent, ScanItem* item, InstrumentNotifier* ec, bool allow_distr = true);
diff --git a/GUI/View/SampleDesigner/CompoundForm.cpp b/GUI/View/SampleDesigner/CompoundForm.cpp
index ea1716eccd27829b662cfc1c84daf84f91830812..ef3b9a64e297989bf02f2353be77b6b4daed1918 100644
--- a/GUI/View/SampleDesigner/CompoundForm.cpp
+++ b/GUI/View/SampleDesigner/CompoundForm.cpp
@@ -17,24 +17,21 @@
 #include "GUI/Support/Util/ActionFactory.h"
 #include "GUI/View/SampleDesigner/FormLayouter.h"
 #include "GUI/View/SampleDesigner/LayerEditorUtil.h"
-#include "GUI/View/Widget/GroupBoxes.h"
-
 #include <QAction>
 #include <QPushButton>
 
 CompoundForm::CompoundForm(QWidget* parent, CompoundItem* compoundItem, SampleEditorController* ec,
                            bool allowRemove)
-    : QGroupBox(parent)
+    : CollapsibleGroupBox("Compund", parent, compoundItem->expandCompound)
     , m_compositionItem(compoundItem)
     , m_ec(ec)
 {
-    setTitle("Composition of particles");
-
     FormLayouter layouter(this, ec);
     layouter.setContentsMargins(30, 6, 0, 0);
     layouter.addVector(compoundItem->position(), false);
     layouter.addSelection(compoundItem->rotationSelection());
     layouter.addValue(compoundItem->abundance());
+    body()->setLayout(layouter.layout());
 
     for (auto* particle : compoundItem->itemsWithParticles())
         layouter.addRow(
@@ -47,15 +44,13 @@ CompoundForm::CompoundForm(QWidget* parent, CompoundItem* compoundItem, SampleEd
     m_structureEditingWidgets << btn;
     layouter.addStructureEditingRow(btn);
 
-    auto* collapser = GroupBoxCollapser::installIntoGroupBox2(this, compoundItem->expandCompound);
-
     // top right corner actions
     // show in real space
     {
         auto* showInRealspaceAction = ActionFactory::createShowInRealspaceAction(
             this, "particle composition",
             [ec, compoundItem] { ec->requestViewInRealspace(compoundItem); });
-        collapser->addTitleAction(showInRealspaceAction);
+        addTitleAction(showInRealspaceAction);
     }
     // duplicate
     {
@@ -63,14 +58,14 @@ CompoundForm::CompoundForm(QWidget* parent, CompoundItem* compoundItem, SampleEd
             ActionFactory::createDuplicateAction(this, "particle composition", [ec, compoundItem] {
                 ec->duplicateItemWithParticles(compoundItem);
             });
-        collapser->addTitleAction(m_duplicateAction);
+        addTitleAction(m_duplicateAction);
     }
     // remove
     {
         m_removeAction = ActionFactory::createRemoveAction(
             this, "particle composition", [ec, compoundItem] { ec->removeParticle(compoundItem); });
         if (allowRemove)
-            collapser->addTitleAction(m_removeAction);
+            addTitleAction(m_removeAction);
     }
 
     m_layout = layouter.layout();
diff --git a/GUI/View/SampleDesigner/CompoundForm.h b/GUI/View/SampleDesigner/CompoundForm.h
index ab3528da381d96136d421e62a42f45b06fee4c6f..642b33a04e9aa81bdd1af0855fd2224501badda2 100644
--- a/GUI/View/SampleDesigner/CompoundForm.h
+++ b/GUI/View/SampleDesigner/CompoundForm.h
@@ -15,15 +15,15 @@
 #ifndef BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_COMPOUNDFORM_H
 #define BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_COMPOUNDFORM_H
 
+#include "GUI/View/Widget/GroupBoxes.h"
 #include <QFormLayout>
-#include <QGroupBox>
 
 class CompoundItem;
 class ItemWithParticles;
 class SampleEditorController;
 
 //! Form for editing a particle composition
-class CompoundForm : public QGroupBox {
+class CompoundForm : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     CompoundForm(QWidget* parent, CompoundItem* compoundItem, SampleEditorController* ec,
diff --git a/GUI/View/SampleDesigner/CoreAndShellForm.cpp b/GUI/View/SampleDesigner/CoreAndShellForm.cpp
index cd822a0ad21e62a0a565859a7ac5ab4fa1cc60de..233cb85b9dba70cf79a0f14580f5ff5ca7c6c42e 100644
--- a/GUI/View/SampleDesigner/CoreAndShellForm.cpp
+++ b/GUI/View/SampleDesigner/CoreAndShellForm.cpp
@@ -22,7 +22,6 @@
 #include "GUI/View/SampleDesigner/FormLayouter.h"
 #include "GUI/View/SampleDesigner/MaterialInplaceForm.h"
 #include "GUI/View/SampleDesigner/SampleEditorController.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 #include <QAction>
 #include <QComboBox>
 #include <memory>
@@ -51,20 +50,19 @@ QComboBox* createFormFactorCombo(QWidget* parent, FormFactorItem* current)
 
 CoreAndShellForm::CoreAndShellForm(QWidget* parent, CoreAndShellItem* coreShellItem,
                                    SampleEditorController* ec, bool allowRemove)
-    : QGroupBox(parent)
+    : CollapsibleGroupBox("Core/shell particle", parent, coreShellItem->expandMain)
     , m_item(coreShellItem)
     , m_ec(ec)
 {
-    setTitle("Core/shell particle");
     FormLayouter layouter(this, ec);
-    layouter.setContentsMargins(30, 6, 0, 0);
     layouter.addVector(coreShellItem->position(), false);
     layouter.addSelection(coreShellItem->rotationSelection());
     layouter.addValue(coreShellItem->abundance());
+    body()->setLayout(layouter.layout());
 
     // - core
     {
-        auto* coreParticleGroup = new QGroupBox(this);
+        auto* coreParticleGroup = new CollapsibleGroupBox("Core", this, coreShellItem->expandCore);
         coreParticleGroup->setObjectName("Particle");
 
         core.layouter = std::make_unique<FormLayouter>(coreParticleGroup, ec);
@@ -76,14 +74,12 @@ CoreAndShellForm::CoreAndShellForm(QWidget* parent, CoreAndShellItem* coreShellI
         connect(core.formfactorCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
                 &CoreAndShellForm::onCoreComboChanged);
         core.layouter->addRow("Form factor:", core.formfactorCombo);
-        core.collapser =
-            GroupBoxCollapser::installIntoGroupBox2(coreParticleGroup, coreShellItem->expandCore);
 
         auto* showInRealspaceAction =
             ActionFactory::createShowInRealspaceAction(this, "core particle");
         connect(showInRealspaceAction, &QAction::triggered, this,
                 &CoreAndShellForm::showCoreInRealspace);
-        core.collapser->addTitleAction(showInRealspaceAction);
+        coreParticleGroup->addTitleAction(showInRealspaceAction);
 
         createCoreWidgets();
 
@@ -92,7 +88,8 @@ CoreAndShellForm::CoreAndShellForm(QWidget* parent, CoreAndShellItem* coreShellI
 
     // - shell
     {
-        auto* shellParticleGroup = new QGroupBox(this);
+        auto* shellParticleGroup =
+            new CollapsibleGroupBox("Shell", this, coreShellItem->expandShell);
         shellParticleGroup->setObjectName("Particle");
         shell.layouter = std::make_unique<FormLayouter>(shellParticleGroup, ec);
         shell.formfactorCombo = createFormFactorCombo(
@@ -102,46 +99,38 @@ CoreAndShellForm::CoreAndShellForm(QWidget* parent, CoreAndShellItem* coreShellI
         connect(shell.formfactorCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
                 &CoreAndShellForm::onShellComboChanged);
         shell.layouter->addRow("Form factor:", shell.formfactorCombo);
-        GroupBoxCollapser::installIntoGroupBox2(shellParticleGroup, coreShellItem->expandShell);
 
         auto* showInRealspaceAction =
             ActionFactory::createShowInRealspaceAction(this, "shell particle");
         connect(showInRealspaceAction, &QAction::triggered, this,
                 &CoreAndShellForm::showShellInRealspace);
-        shell.collapser->addTitleAction(showInRealspaceAction);
+        shellParticleGroup->addTitleAction(showInRealspaceAction);
 
         createShellWidgets();
 
         layouter.addRow(shellParticleGroup);
     }
 
-    auto* mainCollapser = GroupBoxCollapser::installIntoGroupBox2(this, coreShellItem->expandMain);
+    //... top right corner actions
 
-    // top right corner actions
     // show in real space
-    {
-        auto* showInRealspaceAction = ActionFactory::createShowInRealspaceAction(
-            this, "core/shell particle",
-            [ec, coreShellItem] { ec->requestViewInRealspace(coreShellItem); });
-        mainCollapser->addTitleAction(showInRealspaceAction);
-    }
+    auto* showInRealspaceAction = ActionFactory::createShowInRealspaceAction(
+        this, "core/shell particle",
+        [ec, coreShellItem] { ec->requestViewInRealspace(coreShellItem); });
+    addTitleAction(showInRealspaceAction);
+
     // duplicate
-    {
-        m_duplicateAction =
-            ActionFactory::createDuplicateAction(this, "core/shell particle", [ec, coreShellItem] {
-                ec->duplicateItemWithParticles(coreShellItem);
-            });
-        mainCollapser->addTitleAction(m_duplicateAction);
-    }
+    m_duplicateAction =
+        ActionFactory::createDuplicateAction(this, "core/shell particle", [ec, coreShellItem] {
+            ec->duplicateItemWithParticles(coreShellItem);
+        });
+    addTitleAction(m_duplicateAction);
+
     // remove
-    {
-        m_removeAction =
-            ActionFactory::createRemoveAction(this, "core/shell particle", [ec, coreShellItem] {
-                ec->removeParticle(coreShellItem);
-            });
-        if (allowRemove)
-            mainCollapser->addTitleAction(m_removeAction);
-    }
+    m_removeAction = ActionFactory::createRemoveAction(
+        this, "core/shell particle", [ec, coreShellItem] { ec->removeParticle(coreShellItem); });
+    if (allowRemove)
+        addTitleAction(m_removeAction);
 }
 
 void CoreAndShellForm::onCoreComboChanged()
@@ -189,8 +178,6 @@ void CoreAndShellForm::createCoreWidgets()
         core.layouter->addSelection(particle->rotationSelection());
         // no abundance since this is handled in CoreShell itself!
     }
-
-    core.collapser->setTitle(groupTitle);
 }
 
 void CoreAndShellForm::createShellWidgets()
@@ -208,8 +195,6 @@ void CoreAndShellForm::createShellWidgets()
         // no position vector - not allowed in CoreShell
         // no abundance since this is handled in CoreShell itself!
     }
-
-    shell.collapser->setTitle(groupTitle);
 }
 
 void CoreAndShellForm::enableStructureEditing(bool b)
diff --git a/GUI/View/SampleDesigner/CoreAndShellForm.h b/GUI/View/SampleDesigner/CoreAndShellForm.h
index 4f7599f1e9e32ffb69d8127c49be2da531490f61..00aec1b536d6d3325192331bdfa8f59dc41a7183 100644
--- a/GUI/View/SampleDesigner/CoreAndShellForm.h
+++ b/GUI/View/SampleDesigner/CoreAndShellForm.h
@@ -15,17 +15,16 @@
 #ifndef BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_COREANDSHELLFORM_H
 #define BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_COREANDSHELLFORM_H
 
+#include "GUI/View/Widget/GroupBoxes.h"
 #include <QComboBox>
-#include <QGroupBox>
 #include <memory>
 
 class CoreAndShellItem;
 class FormLayouter;
-class GroupBoxCollapser;
 class SampleEditorController;
 
 //! Form for editing a core/shell particle
-class CoreAndShellForm : public QGroupBox {
+class CoreAndShellForm : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     CoreAndShellForm(QWidget* parent, CoreAndShellItem* coreShellItem, SampleEditorController* ec,
@@ -51,7 +50,6 @@ private:
     struct Location {
         std::unique_ptr<FormLayouter> layouter;
         QComboBox* formfactorCombo = nullptr;
-        GroupBoxCollapser* collapser = nullptr;
     };
     Location core;
     Location shell;
diff --git a/GUI/View/SampleDesigner/InterferenceForm.cpp b/GUI/View/SampleDesigner/InterferenceForm.cpp
index 148584d363fb29080fe5881f6e0bba34ea7ad964..5ead2f4954ae8084a88b6f4631be12298efff466 100644
--- a/GUI/View/SampleDesigner/InterferenceForm.cpp
+++ b/GUI/View/SampleDesigner/InterferenceForm.cpp
@@ -20,19 +20,15 @@
 #include "GUI/View/Numeric/NumWidgetUtil.h"
 #include "GUI/View/SampleDesigner/FormLayouter.h"
 #include "GUI/View/SampleDesigner/LatticeTypeSelectionForm.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 
 InterferenceForm::InterferenceForm(QWidget* parent, ParticleLayoutItem* layoutItem,
                                    SampleEditorController* ec)
-    : QGroupBox(parent)
+    : CollapsibleGroupBox("Interference Function", parent, layoutItem->expandInterference)
     , m_layoutItem(layoutItem)
     , m_ec(ec)
 {
-    setTitle("Interference Function");
-
     FormLayouter layouter(this, ec);
-    layouter.setContentsMargins(6, 6, 0, 6);
-    GroupBoxCollapser::installIntoGroupBox2(this, layoutItem->expandInterference);
+    body()->setLayout(layouter.layout());
 
     m_interferenceTypeCombo = new QComboBox(this);
     WheelEventEater::install(m_interferenceTypeCombo);
@@ -136,5 +132,5 @@ void InterferenceForm::createInterferenceWidgets()
 
 void InterferenceForm::updateTitle()
 {
-    m_collapser->setTitle("Interference Function (" + m_interferenceTypeCombo->currentText() + ")");
+    setTitle("Interference Function (" + m_interferenceTypeCombo->currentText() + ")");
 }
diff --git a/GUI/View/SampleDesigner/InterferenceForm.h b/GUI/View/SampleDesigner/InterferenceForm.h
index 201053631e59c8c29c1c2da8f46e8a208906b3f7..e2fc86041b7dedd98ef8944430abc361cc2eb6ff 100644
--- a/GUI/View/SampleDesigner/InterferenceForm.h
+++ b/GUI/View/SampleDesigner/InterferenceForm.h
@@ -15,15 +15,14 @@
 #ifndef BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_INTERFERENCEFORM_H
 #define BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_INTERFERENCEFORM_H
 
-#include <QGroupBox>
+#include "GUI/View/Widget/GroupBoxes.h"
 
-class GroupBoxCollapser;
 class ParticleLayoutItem;
 class QComboBox;
 class SampleEditorController;
 
 //! Form for editing interference functions
-class InterferenceForm : public QGroupBox {
+class InterferenceForm : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     InterferenceForm(QWidget* parent, ParticleLayoutItem* layoutItem, SampleEditorController* ec);
@@ -35,7 +34,6 @@ private:
     void createInterferenceWidgets();
     void updateTitle();
 
-    GroupBoxCollapser* m_collapser = nullptr;
     QComboBox* m_interferenceTypeCombo;
     ParticleLayoutItem* m_layoutItem;
     SampleEditorController* m_ec;
diff --git a/GUI/View/SampleDesigner/LayerForm.cpp b/GUI/View/SampleDesigner/LayerForm.cpp
index 1aa5b88b6bcbf6a0d5caea3e4b47b0b7f6405f7a..e24272b25ed94bd4b8af342b406383daa7808e21 100644
--- a/GUI/View/SampleDesigner/LayerForm.cpp
+++ b/GUI/View/SampleDesigner/LayerForm.cpp
@@ -23,23 +23,21 @@
 #include "GUI/View/Numeric/NumWidgetUtil.h"
 #include "GUI/View/SampleDesigner/MaterialInplaceForm.h"
 #include "GUI/View/SampleDesigner/ParticleLayoutForm.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 #include <QLineEdit>
 #include <QMenu>
 #include <QPushButton>
 #include <memory>
 
 LayerForm::LayerForm(QWidget* parent, LayerItem* layerItem, SampleEditorController* ec)
-    : QGroupBox(parent)
+    : CollapsibleGroupBox(layerItem->layerName(), parent, layerItem->expandLayer)
     , m_layer(layerItem)
     , m_ec(ec)
 {
-    setTitle(m_layer->layerName());
     m_layouter = std::make_unique<FormLayouter>(this, ec);
-    m_layouter->setContentsMargins(6, 6, 0, 6);
-    m_collapser = GroupBoxCollapser::installIntoGroupBox2(this, layerItem->expandLayer);
+    body()->setLayout(m_layouter->layout());
+
+    //... top right corner actions
 
-    // top right corner actions
     // choose color
     {
         auto* chooseColorAction = new QAction(this);
@@ -49,7 +47,7 @@ LayerForm::LayerForm(QWidget* parent, LayerItem* layerItem, SampleEditorControll
         chooseColorAction->setToolTip("Choose a color for this layer");
         auto* menu = new QMenu(this);
         chooseColorAction->setMenu(menu);
-        m_collapser->addTitleAction(chooseColorAction);
+        addTitleAction(chooseColorAction);
 
         for (const auto& col : LayerEditorUtil::predefinedLayerColors()) {
             QPixmap p(64, 64);
@@ -67,30 +65,31 @@ LayerForm::LayerForm(QWidget* parent, LayerItem* layerItem, SampleEditorControll
         m_moveButton->setToolTip("Move layer up/down");
         connect(m_moveButton, &WidgetMoverButton::finishedMoving, ec,
                 &SampleEditorController::onStoppedToMoveLayer);
-        m_collapser->addTitleWidget(m_moveButton);
+        addTitleWidget(m_moveButton);
         m_structureEditingWidgets << m_moveButton;
     }
     // show in real space
     {
         auto* showInRealspaceAction = ActionFactory::createShowInRealspaceAction(
             this, "layer", [ec, layerItem] { ec->requestViewInRealspace(layerItem); });
-        m_collapser->addTitleAction(showInRealspaceAction);
+        addTitleAction(showInRealspaceAction);
     }
     // duplicate
     {
         m_duplicateAction = ActionFactory::createDuplicateAction(
             this, "layer", [ec, layerItem] { ec->duplicateLayerItem(layerItem); });
-        m_collapser->addTitleAction(m_duplicateAction);
+        addTitleAction(m_duplicateAction);
     }
     // remove
     {
         m_removeAction = ActionFactory::createRemoveAction(
             this, "layer", [ec, layerItem] { ec->removeLayerItem(layerItem); });
-        m_collapser->addTitleAction(m_removeAction);
+        addTitleAction(m_removeAction);
     }
 
     QColor bckgroundCol = m_layer->color();
-    setStyleSheet("QGroupBox {background-color: " + bckgroundCol.name(QColor::HexRgb) + "}");
+    setStyleSheet("CollapsibleGroupBox {background-color: " + bckgroundCol.name(QColor::HexRgb)
+                  + "}");
 
     m_layouter->addRow("Material:", new MaterialInplaceForm(this, layerItem, ec));
     m_thicknessRow = m_layouter->addValue(m_layer->thickness());
@@ -140,7 +139,7 @@ void LayerForm::enableStructureEditing(bool b)
 void LayerForm::updateColor()
 {
     QColor bckgroundCol = m_layer->color();
-    setStyleSheet("QGroupBox {background-color: " + bckgroundCol.name(QColor::HexRgb) + "}");
+    setStyleSheet("LayerForm {background-color: " + bckgroundCol.name(QColor::HexRgb) + "}");
 }
 
 void LayerForm::updateTitle()
@@ -148,13 +147,12 @@ void LayerForm::updateTitle()
     const SampleItem* sampleItem = m_ec->sampleItem();
     ASSERT(sampleItem);
     int i = sampleItem->layerItems().indexOf(m_layer);
-    m_collapser->setTitle("Layer " + QString::number(i)
-                          + "     Material: " + m_layer->materialName());
+    setTitle("Layer " + QString::number(i) + "     Material: " + m_layer->materialName());
 }
 
 void LayerForm::expand()
 {
-    m_collapser->setExpanded(true);
+    setExpanded(true);
 }
 
 void LayerForm::updateLayerPositionDependentElements()
diff --git a/GUI/View/SampleDesigner/LayerForm.h b/GUI/View/SampleDesigner/LayerForm.h
index fed2e0c4115a33c1f57afe881f5a2c0b555a43e4..3fce21c328c283f07111abcf2bdfd89a27f89719 100644
--- a/GUI/View/SampleDesigner/LayerForm.h
+++ b/GUI/View/SampleDesigner/LayerForm.h
@@ -16,14 +16,13 @@
 #define BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_LAYERFORM_H
 
 #include "GUI/View/SampleDesigner/FormLayouter.h"
-#include <QGroupBox>
+#include "GUI/View/Widget/GroupBoxes.h"
 #include <memory>
 
-class GroupBoxCollapser;
 class WidgetMoverButton;
 
 //! Form for editing a layer
-class LayerForm : public QGroupBox {
+class LayerForm : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     LayerForm(QWidget* parent, LayerItem* layerItem, SampleEditorController* ec);
@@ -39,7 +38,6 @@ public:
 private:
     void updateColor();
 
-    GroupBoxCollapser* m_collapser;
     std::unique_ptr<FormLayouter> m_layouter;
     LayerItem* m_layer;
     QAction* m_duplicateAction;
diff --git a/GUI/View/SampleDesigner/MesocrystalForm.cpp b/GUI/View/SampleDesigner/MesocrystalForm.cpp
index 170b26efdb7d00ad58daeb01b5c074218cd037fb..96b21276cf5bcb2828973c117e31b230ca389da8 100644
--- a/GUI/View/SampleDesigner/MesocrystalForm.cpp
+++ b/GUI/View/SampleDesigner/MesocrystalForm.cpp
@@ -19,19 +19,17 @@
 #include "GUI/Model/Sample/ParticleItem.h"
 #include "GUI/Support/Util/ActionFactory.h"
 #include "GUI/View/SampleDesigner/FormLayouter.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 #include <QAction>
 
 MesocrystalForm::MesocrystalForm(QWidget* parent, MesocrystalItem* mesocrystalItem,
                                  SampleEditorController* ec, bool allowRemove)
-    : QGroupBox(parent)
+    : CollapsibleGroupBox("Mesocrystal", parent, mesocrystalItem->expandMesocrystal)
     , m_item(mesocrystalItem)
     , m_ec(ec)
 {
-    setTitle("Meso crystal");
-    setObjectName("Mesocrystal");
     FormLayouter layouter(this, ec);
     m_layout = layouter.layout();
+    body()->setLayout(m_layout);
     layouter.setContentsMargins(30, 6, 0, 0);
     layouter.addVector(mesocrystalItem->position(), false);
     layouter.addSelection(mesocrystalItem->rotationSelection());
@@ -48,33 +46,27 @@ MesocrystalForm::MesocrystalForm(QWidget* parent, MesocrystalItem* mesocrystalIt
     m_rowOfBasisTypeCombo = layouter.addRow("Basis type", m_basisCombo);
     createBasisWidgets();
 
-    auto* collapser =
-        GroupBoxCollapser::installIntoGroupBox2(this, mesocrystalItem->expandMesocrystal);
+    //... top right corner actions
 
-    // top right corner actions
     // show in real space
-    {
-        auto* showInRealspaceAction =
-            ActionFactory::createShowInRealspaceAction(this, "meso crystal", [ec, mesocrystalItem] {
-                ec->requestViewInRealspace(mesocrystalItem);
-            });
-        collapser->addTitleAction(showInRealspaceAction);
-    }
+    auto* showInRealspaceAction =
+        ActionFactory::createShowInRealspaceAction(this, "meso crystal", [ec, mesocrystalItem] {
+            ec->requestViewInRealspace(mesocrystalItem);
+        });
+    addTitleAction(showInRealspaceAction);
+
     // duplicate
-    {
-        m_duplicateAction =
-            ActionFactory::createDuplicateAction(this, "meso crystal", [ec, mesocrystalItem] {
-                ec->duplicateItemWithParticles(mesocrystalItem);
-            });
-        collapser->addTitleAction(m_duplicateAction);
-    }
+    m_duplicateAction =
+        ActionFactory::createDuplicateAction(this, "meso crystal", [ec, mesocrystalItem] {
+            ec->duplicateItemWithParticles(mesocrystalItem);
+        });
+    addTitleAction(m_duplicateAction);
+
     // remove
-    {
-        m_removeAction = ActionFactory::createRemoveAction(
-            this, "meso crystal", [ec, mesocrystalItem] { ec->removeParticle(mesocrystalItem); });
-        if (allowRemove)
-            collapser->addTitleAction(m_removeAction);
-    }
+    m_removeAction = ActionFactory::createRemoveAction(
+        this, "meso crystal", [ec, mesocrystalItem] { ec->removeParticle(mesocrystalItem); });
+    if (allowRemove)
+        addTitleAction(m_removeAction);
 }
 
 QComboBox* MesocrystalForm::createBasisCombo(QWidget* parent, ItemWithParticles* current)
diff --git a/GUI/View/SampleDesigner/MesocrystalForm.h b/GUI/View/SampleDesigner/MesocrystalForm.h
index 153d303f8ec1ce5c00b5e24cdc7c93f25f13c502..43f0b81a4a53558efa892a2cdf566c39438aa467 100644
--- a/GUI/View/SampleDesigner/MesocrystalForm.h
+++ b/GUI/View/SampleDesigner/MesocrystalForm.h
@@ -15,16 +15,16 @@
 #ifndef BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_MESOCRYSTALFORM_H
 #define BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_MESOCRYSTALFORM_H
 
+#include "GUI/View/Widget/GroupBoxes.h"
 #include <QComboBox>
 #include <QFormLayout>
-#include <QGroupBox>
 
 class MesocrystalItem;
 class SampleEditorController;
 class ItemWithParticles;
 
 //! Form for editing a mesocrystal
-class MesocrystalForm : public QGroupBox {
+class MesocrystalForm : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     MesocrystalForm(QWidget* parent, MesocrystalItem* mesocrystalItem, SampleEditorController* ec,
diff --git a/GUI/View/SampleDesigner/ParticleForm.cpp b/GUI/View/SampleDesigner/ParticleForm.cpp
index 290f652c5636ed38f39216fd3f129cf9c8adefd0..a359efc3f99cb06c2ecd1791f7a8bb1f1f466723 100644
--- a/GUI/View/SampleDesigner/ParticleForm.cpp
+++ b/GUI/View/SampleDesigner/ParticleForm.cpp
@@ -19,17 +19,24 @@
 #include "GUI/Support/Util/ActionFactory.h"
 #include "GUI/View/SampleDesigner/FormLayouter.h"
 #include "GUI/View/SampleDesigner/MaterialInplaceForm.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 #include <QAction>
 
+namespace {
+
+QString titleOfItem(const ParticleItem* particleItem)
+{
+    return "Particle (" + FormFactorItemCatalog::menuEntry(particleItem->formFactorItem()) + ")";
+}
+
+} // namespace
+
 ParticleForm::ParticleForm(QWidget* parent, ParticleItem* particleItem, bool allowAbundance,
                            SampleEditorController* ec, bool allowRemove)
-    : QGroupBox(parent)
+    : CollapsibleGroupBox(::titleOfItem(particleItem), parent, particleItem->expandParticle)
 {
-    const QString formfactor = FormFactorItemCatalog::menuEntry(particleItem->formFactorItem());
-    setTitle("Particle (" + formfactor + ")");
-
     FormLayouter layouter(this, ec);
+    body()->setLayout(layouter.layout());
+
     layouter.addRow("Material", new MaterialInplaceForm(this, particleItem, ec));
     layouter.addGroupOfValues("Geometry", particleItem->formFactorItem()->geometryProperties());
     layouter.addVector(particleItem->position(), false);
@@ -37,27 +44,25 @@ ParticleForm::ParticleForm(QWidget* parent, ParticleItem* particleItem, bool all
     if (allowAbundance)
         layouter.addValue(particleItem->abundance());
 
-    auto* collapser = GroupBoxCollapser::installIntoGroupBox2(this, particleItem->expandParticle);
-
     // top right corner actions
     // show in real space
     {
         auto* showInRealspaceAction = ActionFactory::createShowInRealspaceAction(
             this, "particle", [ec, particleItem] { ec->requestViewInRealspace(particleItem); });
-        collapser->addTitleAction(showInRealspaceAction);
+        addTitleAction(showInRealspaceAction);
     }
     // duplicate
     {
         m_duplicateAction = ActionFactory::createDuplicateAction(
             this, "particle", [ec, particleItem] { ec->duplicateItemWithParticles(particleItem); });
-        collapser->addTitleAction(m_duplicateAction);
+        addTitleAction(m_duplicateAction);
     }
     // remove
     {
         m_removeAction = ActionFactory::createRemoveAction(
             this, "particle", [ec, particleItem] { ec->removeParticle(particleItem); });
         if (allowRemove)
-            collapser->addTitleAction(m_removeAction);
+            addTitleAction(m_removeAction);
     }
 }
 
diff --git a/GUI/View/SampleDesigner/ParticleForm.h b/GUI/View/SampleDesigner/ParticleForm.h
index 82c584f92bf48f6ee48b876c8c5d556475b7f5cd..b2d293c0e0ccc16bbca68ecddf8393f0ffc88351 100644
--- a/GUI/View/SampleDesigner/ParticleForm.h
+++ b/GUI/View/SampleDesigner/ParticleForm.h
@@ -15,13 +15,13 @@
 #ifndef BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_PARTICLEFORM_H
 #define BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_PARTICLEFORM_H
 
-#include <QGroupBox>
+#include "GUI/View/Widget/GroupBoxes.h"
 
 class ParticleItem;
 class SampleEditorController;
 
 //! Form for editing a particle
-class ParticleForm : public QGroupBox {
+class ParticleForm : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     ParticleForm(QWidget* parent, ParticleItem* particleItem, bool allowAbundance,
diff --git a/GUI/View/SampleDesigner/ParticleLayoutForm.cpp b/GUI/View/SampleDesigner/ParticleLayoutForm.cpp
index 3fd02cce9399a47b9a943364ee94516c3e57be01..bc2235039c8129e7305df605e545eaa615f72b4d 100644
--- a/GUI/View/SampleDesigner/ParticleLayoutForm.cpp
+++ b/GUI/View/SampleDesigner/ParticleLayoutForm.cpp
@@ -24,18 +24,17 @@
 #include "GUI/View/SampleDesigner/LayerEditorUtil.h"
 #include "GUI/View/SampleDesigner/LayerForm.h"
 #include "GUI/View/SampleDesigner/SampleEditorController.h"
-#include "GUI/View/Widget/GroupBoxes.h"
 #include <QAction>
 #include <QPushButton>
 
 ParticleLayoutForm::ParticleLayoutForm(LayerForm* form, ParticleLayoutItem* pLayoutItem,
                                        SampleEditorController* ec)
-    : QGroupBox(form)
+    : CollapsibleGroupBox("TODO", form, pLayoutItem->expandParticleLayout)
     , m_layoutItem(pLayoutItem)
     , m_ec(ec)
 {
     FormLayouter layouter(this, ec);
-    layouter.setContentsMargins(30, 6, 0, 0);
+    body()->setLayout(layouter.layout());
 
     int rowOfTotalDensity = layouter.addValue(m_layoutItem->ownDensity());
     m_totalDensitySpinBox =
@@ -55,16 +54,15 @@ ParticleLayoutForm::ParticleLayoutForm(LayerForm* form, ParticleLayoutItem* pLay
     m_structureEditingWidgets << btn;
     layouter.addStructureEditingRow(btn);
 
-    GroupBoxCollapser::installIntoGroupBox2(this, pLayoutItem->expandParticleLayout);
+    //... top right corner actions
 
-    // top right corner actions
     // show in real space
     {
         auto* showInRealspaceAction =
             ActionFactory::createShowInRealspaceAction(this, "particle layout", [ec, pLayoutItem] {
                 ec->requestViewInRealspace(pLayoutItem);
             });
-        m_collapser->addTitleAction(showInRealspaceAction);
+        addTitleAction(showInRealspaceAction);
     }
     // duplicate
     {
@@ -72,7 +70,7 @@ ParticleLayoutForm::ParticleLayoutForm(LayerForm* form, ParticleLayoutItem* pLay
             ActionFactory::createDuplicateAction(this, "particle layout", [ec, pLayoutItem, form] {
                 ec->duplicateLayoutItem(form, pLayoutItem);
             });
-        m_collapser->addTitleAction(m_duplicateAction);
+        addTitleAction(m_duplicateAction);
     }
     // remove
     {
@@ -80,7 +78,7 @@ ParticleLayoutForm::ParticleLayoutForm(LayerForm* form, ParticleLayoutItem* pLay
             ActionFactory::createRemoveAction(this, "particle layout", [ec, pLayoutItem, form] {
                 ec->removeLayoutItem(form, pLayoutItem);
             });
-        m_collapser->addTitleAction(m_removeAction);
+        addTitleAction(m_removeAction);
     }
 
     m_layout = layouter.layout();
@@ -139,8 +137,7 @@ void ParticleLayoutForm::updateTitle(const LayerItem* layerItem)
 {
     const auto layouts = layerItem->layoutItems();
     if (layouts.size() > 1)
-        m_collapser->setTitle("Particle layout "
-                              + QString::number(layouts.indexOf(m_layoutItem) + 1));
+        setTitle("Particle layout " + QString::number(layouts.indexOf(m_layoutItem) + 1));
     else
-        m_collapser->setTitle("Particle layout");
+        setTitle("Particle layout");
 }
diff --git a/GUI/View/SampleDesigner/ParticleLayoutForm.h b/GUI/View/SampleDesigner/ParticleLayoutForm.h
index ca0940a1aa8a42de57c09b19dc771abeacb49f3c..a1943c717c584511aa7c676b7b61a1cd861d5038 100644
--- a/GUI/View/SampleDesigner/ParticleLayoutForm.h
+++ b/GUI/View/SampleDesigner/ParticleLayoutForm.h
@@ -15,12 +15,11 @@
 #ifndef BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_PARTICLELAYOUTFORM_H
 #define BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_PARTICLELAYOUTFORM_H
 
+#include "GUI/View/Widget/GroupBoxes.h"
 #include <QFormLayout>
-#include <QGroupBox>
 #include <QList>
 
 class DoubleSpinBox;
-class GroupBoxCollapser;
 class ItemWithParticles;
 class LayerForm;
 class LayerItem;
@@ -28,7 +27,7 @@ class ParticleLayoutItem;
 class SampleEditorController;
 
 //! Form for editing a particle layout
-class ParticleLayoutForm : public QGroupBox {
+class ParticleLayoutForm : public CollapsibleGroupBox {
     Q_OBJECT
 public:
     ParticleLayoutForm(LayerForm* form, ParticleLayoutItem* pLayoutItem,
@@ -60,7 +59,6 @@ private:
     SampleEditorController* m_ec;
     QList<QWidget*> m_structureEditingWidgets;
     DoubleSpinBox* m_totalDensitySpinBox;
-    GroupBoxCollapser* m_collapser;
 };
 
 #endif // BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_PARTICLELAYOUTFORM_H
diff --git a/GUI/View/SampleDesigner/SampleForm.cpp b/GUI/View/SampleDesigner/SampleForm.cpp
index ae1ef5ef4449653d464fa01bf0bb240cbbe8a9c3..cea39e344bb7a22ac0f5b5eae61d0342757cb82c 100644
--- a/GUI/View/SampleDesigner/SampleForm.cpp
+++ b/GUI/View/SampleDesigner/SampleForm.cpp
@@ -65,10 +65,10 @@ SampleForm::SampleForm(QWidget* parent, SampleItem* sampleItem, SampleEditorCont
 
     m_layout = new QVBoxLayout(this);
 
-    auto* props = new QGroupBox(this);
-    props->setTitle("Sample");
+    auto* props = new CollapsibleGroupBox("Sample", this, sampleItem->expandInfo);
     FormLayouter layouter(props, ec);
     layouter.setContentsMargins(6, 6, 0, 6);
+    props->body()->setLayout(layouter.layout());
 
     auto* nameEdit = new QLineEdit(props);
     layouter.addRow("Name:", nameEdit);
@@ -92,12 +92,10 @@ SampleForm::SampleForm(QWidget* parent, SampleItem* sampleItem, SampleEditorCont
     // Processing external field is not implemented yet, so temporary disable it (see issue #654)
     // layouter.addVector(sampleItem->externalField(), false);
 
-    auto* collapser = GroupBoxCollapser::installIntoGroupBox2(props, sampleItem->expandInfo);
-
     auto* showInRealspaceAction = ActionFactory::createShowInRealspaceAction(
         this, "sample", [this] { m_ec->requestViewInRealspace(m_sampleItem); });
 
-    collapser->addTitleAction(showInRealspaceAction);
+    props->addTitleAction(showInRealspaceAction);
 
     m_layout->addWidget(props);
 
diff --git a/GUI/View/Widget/GroupBoxes.cpp b/GUI/View/Widget/GroupBoxes.cpp
index f6f452908dcf834b552f510782f68a10dddf9ee9..9532843185b0b4a8e017b50873df637d9789e291 100644
--- a/GUI/View/Widget/GroupBoxes.cpp
+++ b/GUI/View/Widget/GroupBoxes.cpp
@@ -21,10 +21,10 @@
 #include <QVariant>
 
 //  ************************************************************************************************
-//  class HeinzGroupBox
+//  class StaticGroupBox
 //  ************************************************************************************************
 
-HeinzGroupBox::HeinzGroupBox(const QString& title, QWidget* parent)
+StaticGroupBox::StaticGroupBox(const QString& title, QWidget* parent)
     : QWidget(parent)
 {
     auto* label = new QLabel(this);
@@ -44,6 +44,82 @@ HeinzGroupBox::HeinzGroupBox(const QString& title, QWidget* parent)
     setLayout(layout);
 }
 
+//  ************************************************************************************************
+//  class CollapsibleGroupBox
+//  ************************************************************************************************
+
+CollapsibleGroupBox::CollapsibleGroupBox(const QString& title, QWidget* parent, bool& expanded)
+    : QWidget(parent)
+{
+    auto* m_toggleButton = new QToolButton(this);
+    m_toggleButton->setObjectName("GroupBoxToggler");
+    m_toggleButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+    m_toggleButton->setCheckable(true);
+    m_toggleButton->setText(title);
+    m_toggleButton->setArrowType(Qt::ArrowType::DownArrow);
+    m_toggleButton->setChecked(true);
+    m_toggleButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
+
+    auto* m_titleLayout = new QHBoxLayout;
+    m_titleLayout->setContentsMargins(0, 0, 3, 0);
+    m_titleLayout->setSpacing(3);
+    m_titleLayout->setAlignment(Qt::AlignVCenter);
+    m_titleLayout->addWidget(m_toggleButton);
+
+    auto* m_titleWidget = new QWidget(this);
+    m_titleWidget->setObjectName("GroupBoxTitleWidget");
+    m_titleWidget->setAttribute(Qt::WA_StyledBackground, true);
+    m_titleWidget->setLayout(m_titleLayout);
+
+    m_body = new QWidget(this);
+    m_body->setObjectName("ContentArea"); // stylesheet -> transparent background
+    m_body->setVisible(expanded);
+
+    auto* layout = new QVBoxLayout;
+    layout->setSpacing(0);
+    layout->setContentsMargins(0, 0, 0, 0);
+    layout->setMenuBar(m_titleWidget);
+    layout->addWidget(m_body);
+
+    setLayout(layout);
+
+    connect(m_toggleButton, &QAbstractButton::clicked,
+            [tb = m_toggleButton, bo = m_body, &expanded](bool checked) {
+                tb->setArrowType(checked ? Qt::ArrowType::DownArrow : Qt::ArrowType::RightArrow);
+                bo->setVisible(checked);
+                expanded = checked;
+            });
+}
+
+void CollapsibleGroupBox::setTitle(const QString& title)
+{
+    m_toggleButton->setText(title);
+}
+
+void CollapsibleGroupBox::addTitleAction(QAction* action)
+{
+    auto* btn = new QToolButton(m_titleWidget);
+    btn->setToolButtonStyle(Qt::ToolButtonIconOnly);
+    btn->setDefaultAction(action);
+    if (action->menu() != nullptr)
+        btn->setPopupMode(QToolButton::InstantPopup);
+
+    m_titleLayout->addWidget(btn);
+
+    connect(action, &QAction::changed, [=]() { btn->setVisible(action->isVisible()); });
+}
+
+void CollapsibleGroupBox::addTitleWidget(QWidget* widget)
+{
+    m_titleLayout->addWidget(widget);
+}
+
+void CollapsibleGroupBox::setExpanded(bool expanded)
+{
+    if (m_toggleButton->isChecked() != expanded)
+        m_toggleButton->click();
+}
+
 //  ************************************************************************************************
 //  class GroupBoxCollapser
 //  ************************************************************************************************
@@ -56,7 +132,6 @@ GroupBoxCollapser* GroupBoxCollapser::installIntoGroupBox(QGroupBox* groupBox)
 GroupBoxCollapser* GroupBoxCollapser::installIntoGroupBox2(QGroupBox* groupBox, bool& expanded)
 {
     auto* result = new GroupBoxCollapser(groupBox);
-    result->setExpanded(expanded);
     connect(result, &GroupBoxCollapser::toggled, [&expanded](bool b) { expanded = b; });
     return result;
 }
@@ -96,12 +171,6 @@ QWidget* GroupBoxCollapser::contentArea() const
     return m_contentArea;
 }
 
-void GroupBoxCollapser::setExpanded(bool expanded)
-{
-    if (m_toggleButton->isChecked() != expanded)
-        m_toggleButton->click();
-}
-
 GroupBoxCollapser::GroupBoxCollapser(QGroupBox* groupBox)
     : QObject(groupBox)
 {
diff --git a/GUI/View/Widget/GroupBoxes.h b/GUI/View/Widget/GroupBoxes.h
index c12893a1e0f9ddece49ec3fe10a5dd7ac111b553..e44a8f4a90a24a8e0f0dadd96efaf5ca87adac40 100644
--- a/GUI/View/Widget/GroupBoxes.h
+++ b/GUI/View/Widget/GroupBoxes.h
@@ -22,17 +22,35 @@ class QAction;
 class QHBoxLayout;
 class QToolButton;
 
-//! A QGroupBox with given title and layout, and some standard styling.
+//! A group box with given title and layout, and some standard styling.
 
-class HeinzGroupBox : public QWidget {
+class StaticGroupBox : public QWidget {
 public:
-    HeinzGroupBox(const QString& title, QWidget* parent);
+    StaticGroupBox(const QString& title, QWidget* parent);
     QWidget* body() { return m_body; }
 
 private:
     QWidget* m_body;
 };
 
+//! A group box that can be expanded or collapsed, according to external boolean flag 'expanded'.
+
+class CollapsibleGroupBox : public QWidget {
+public:
+    CollapsibleGroupBox(const QString& title, QWidget* parent, bool& expanded);
+    QWidget* body() { return m_body; }
+    void setTitle(const QString& title);
+    void addTitleAction(QAction* action);
+    void addTitleWidget(QWidget* widget);
+    void setExpanded(bool expanded);
+
+private:
+    QWidget* m_titleWidget;      //!< widget used to present the new groupbox title
+    QHBoxLayout* m_titleLayout;  //!< layout in the title widget
+    QToolButton* m_toggleButton; //!< button to toggle between collapsed/expanded
+    QWidget* m_body;
+};
+
 
 //! Add-on to group boxes to make them collapsible.
 //!