diff --git a/GUI/Models/DoubleDescriptor.h b/GUI/Models/DoubleDescriptor.h
index 80fc254783d314a890b17603690287f944b08bcc..9a6f2cfa381efe40d85d8a1ae9b59be58f6d52dd 100644
--- a/GUI/Models/DoubleDescriptor.h
+++ b/GUI/Models/DoubleDescriptor.h
@@ -76,7 +76,7 @@ public:
     RealLimits limits;                    //!< Limits of the value.
     function<void(double)> set = nullptr; //!< function to set the value
     function<double()> get = nullptr;     //!< function to get the current value
-    variant<QString, Unit> unit = Unit::undefined; //!< Unit of the value (internal unit only!)
+    variant<QString, Unit> unit = Unit::unitless; //!< Unit of the value (internal unit only!)
     function<QString()> path = nullptr; //<! Path describing  this value. Used e.g. for undo/redo
 };
 
diff --git a/GUI/Models/LayerItem.cpp b/GUI/Models/LayerItem.cpp
index e41af63646274542a5bc8ecc4738ff8ebee034e2..c5d7c075620f9c76983fbfd8a333b906e11e4c10 100644
--- a/GUI/Models/LayerItem.cpp
+++ b/GUI/Models/LayerItem.cpp
@@ -41,6 +41,8 @@ LayerItem::LayerItem() : SessionGraphicsItem(M_TYPE), ItemWithMaterial(M_TYPE)
         ->setLimits(RealLimits::lowerLimited(0.0))
         .setToolTip(layer_nslices_tooltip);
 
+    addProperty(P_COLOR, QColor()); // initially no color
+
     GroupInfo info;
     info.add(LayerBasicRoughnessItem::M_TYPE, "Basic");
     info.add(LayerZeroRoughnessItem::M_TYPE, "No");
@@ -124,6 +126,16 @@ void LayerItem::removeLayout(ParticleLayoutItem* layout)
     model()->removeItem(layout);
 }
 
+QColor LayerItem::color() const
+{
+    return getItemValue(P_COLOR).value<QColor>();
+}
+
+void LayerItem::setColor(const QColor& color)
+{
+    setItemValue(P_COLOR, color);
+}
+
 void LayerItem::updateAppearance(SessionItem* new_parent)
 {
     if (!new_parent) {
diff --git a/GUI/Models/LayerItem.h b/GUI/Models/LayerItem.h
index 64dcf035bb5728dc00171e3f2702af87f3357d1e..913325bf14924a83fcc34fb36e7638e1fc0fd333 100644
--- a/GUI/Models/LayerItem.h
+++ b/GUI/Models/LayerItem.h
@@ -31,6 +31,7 @@ private:
     static constexpr auto P_ROUGHNESS{"Top roughness"};
     static constexpr auto P_NSLICES{"Number of slices"};
     static constexpr auto T_LAYOUTS{"Layout tag"};
+    static constexpr auto P_COLOR{"Color"};
 
 public:
     static constexpr auto M_TYPE{"Layer"};
@@ -53,6 +54,9 @@ public:
     QVector<ParticleLayoutItem*> layouts() const;
     void removeLayout(ParticleLayoutItem* layout);
 
+    QColor color() const;
+    void setColor(const QColor& color);
+
 private:
     void updateAppearance(SessionItem* new_parent);
 
diff --git a/GUI/Models/UIntDescriptor.h b/GUI/Models/UIntDescriptor.h
index b7ea127b8f87b2b807e047cb2c28d0d7d76d7715..e0433fde72d575134bda90829358ba0fc198a610 100644
--- a/GUI/Models/UIntDescriptor.h
+++ b/GUI/Models/UIntDescriptor.h
@@ -60,12 +60,12 @@ public:
     //! Return the current value of the handled parameter.
     operator uint() const;
 
-    QString label;                                 //!< A label text (short, no trailing colon)
-    QString tooltip;                               //!< Tooltip text
-    RealLimits limits;                             //!< Limits of the value.
-    function<void(uint)> set = nullptr;            //!< function to set the value
-    function<uint()> get = nullptr;                //!< function to get the current value
-    variant<QString, Unit> unit = Unit::undefined; //!< Unit of the value (internal unit only!)
+    QString label;                                //!< A label text (short, no trailing colon)
+    QString tooltip;                              //!< Tooltip text
+    RealLimits limits;                            //!< Limits of the value.
+    function<void(uint)> set = nullptr;           //!< function to set the value
+    function<uint()> get = nullptr;               //!< function to get the current value
+    variant<QString, Unit> unit = Unit::unitless; //!< Unit of the value (internal unit only!)
     function<QString()> path = nullptr; //<! Path describing  this value. Used e.g. for undo/redo
 };
 
diff --git a/GUI/Models/Unit.cpp b/GUI/Models/Unit.cpp
index 87bf0902c54c8bf4e2c737a445b15b9cad2a3cf9..e49eab8d544d687fb0cc4ecb907f8cf2559dcb54 100644
--- a/GUI/Models/Unit.cpp
+++ b/GUI/Models/Unit.cpp
@@ -15,10 +15,11 @@
 #include "GUI/Models/Unit.h"
 #include "Base/Const/Units.h"
 #include "Base/Utils/Assert.h"
+#include <QString>
 
 double convert(double d, Unit from, Unit to)
 {
-    if (from == Unit::undefined || to == Unit::undefined || from == to)
+    if (from == to)
         return d;
 
     if (from == Unit::angstrom && to == Unit::nanometer)
@@ -45,6 +46,41 @@ double convert(double d, Unit from, Unit to)
     if (from == Unit::degree && to == Unit::radiant)
         return Units::deg2rad(d);
 
+    if (from == Unit::other || to == Unit::other) {
+        ASSERT(false); // no conversion possible
+        return d;
+    }
+
     ASSERT(false); // no conversion implemented
     return d;
 }
+
+QString unitAsString(const Unit& unit)
+{
+    switch (unit) {
+    case Unit::unitless:
+        return "";
+    case Unit::nanometer:
+        return "nm";
+    case Unit::nanometerPower2:
+        return "nm²";
+    case Unit::nanometerPowerMinus2:
+        return "1/nm²";
+    case Unit::angstrom:
+        return "\303\205";
+    case Unit::angstromPower2:
+        return "\303\205²";
+    case Unit::angstromPowerMinus2:
+        return "1/\303\205²";
+    case Unit::degree:
+        return "°";
+    case Unit::radiant:
+        return "rad";
+    case Unit::other:
+        ASSERT(false); // this function should not be called for Unit::other
+        return "";
+    default:
+        ASSERT(false); // no string implemented
+        return "";
+    }
+}
diff --git a/GUI/Models/Unit.h b/GUI/Models/Unit.h
index 661f6aa0a898810e67b53ac77b3c7b36f75d1bb7..597935db447756e8f0a5db7f6bbe5038edce4b2b 100644
--- a/GUI/Models/Unit.h
+++ b/GUI/Models/Unit.h
@@ -15,13 +15,15 @@
 #ifndef BORNAGAIN_GUI_MODELS_UNIT_H
 #define BORNAGAIN_GUI_MODELS_UNIT_H
 
+class QString;
+
 //! Defines units, mainly to be able to convert between units.
+//!
 //! E.g. internal unit is nanometer, displayed unit is angstrom.
 //! Units which do not support conversion do not have to be
 //! part of the enum, since the relevant code parts support defining a
 //! unit via enum or via string
 enum class Unit {
-    undefined,
     unitless,
     nanometer,
     nanometerPower2,
@@ -30,11 +32,15 @@ enum class Unit {
     angstromPower2,
     angstromPowerMinus2,
     degree,
-    radiant
+    radiant,
+    other //!< The unit has no enum value defined in here (e.g. when defined as an explicit string)
 };
 
 //! Convert the given value d from unit "from" to unit "to"
 //! Throws an exception if no conversion possible.
 double convert(double d, Unit from, Unit to);
 
+//! Returns the string for the given unit
+QString unitAsString(const Unit& unit);
+
 #endif // BORNAGAIN_GUI_MODELS_UNIT_H
diff --git a/GUI/Views/CommonWidgets/DoubleLineEdit.cpp b/GUI/Views/CommonWidgets/DoubleLineEdit.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2bff7cdcfde8991b1b8791fd18d5db85bf3c4181
--- /dev/null
+++ b/GUI/Views/CommonWidgets/DoubleLineEdit.cpp
@@ -0,0 +1,46 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/Views/CommonWidgets/DoubleLineEdit.cpp
+//! @brief     Implements class DoubleLineEdit
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2021
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#include "GUI/Views/CommonWidgets/DoubleLineEdit.h"
+#include "GUI/Views/CommonWidgets/GUIHelpers.h"
+#include <QDoubleValidator>
+
+DoubleLineEdit::DoubleLineEdit(QWidget* parent, const DoubleDescriptor& d)
+    : QLineEdit(parent), m_valueDescriptor(d)
+{
+    m_validator = new QDoubleValidator(0.0, 1e+200, 1000, this);
+    m_validator->setNotation(QDoubleValidator::ScientificNotation);
+    const double minimum =
+        d.limits.hasLowerLimit() ? std::max(d.limits.lowerLimit(), -1e+200) : -1e+200;
+    const double maximum =
+        d.limits.hasUpperLimit() ? std::min(d.limits.upperLimit(), +1e+200) : +1e+200;
+    m_validator->setRange(minimum, maximum, 1000);
+    setValidator(m_validator);
+
+    setBaseValue(m_valueDescriptor.get());
+
+    connect(this, &QLineEdit::editingFinished, this, &DoubleLineEdit::onEditingFinished);
+}
+
+void DoubleLineEdit::setBaseValue(double baseValue)
+{
+    setText(QString::number(baseValue, 'g'));
+}
+
+void DoubleLineEdit::onEditingFinished()
+{
+    const double new_value = text().toDouble();
+    if (new_value != m_valueDescriptor.get())
+        emit baseValueChanged(new_value);
+}
diff --git a/GUI/Views/CommonWidgets/DoubleLineEdit.h b/GUI/Views/CommonWidgets/DoubleLineEdit.h
new file mode 100644
index 0000000000000000000000000000000000000000..4aff9d7dfcede0b980f55445819303242a3fbb93
--- /dev/null
+++ b/GUI/Views/CommonWidgets/DoubleLineEdit.h
@@ -0,0 +1,51 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/Views/CommonWidgets/DoubleLineEdit.h
+//! @brief     Defines class DoubleLineEdit
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2021
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#ifndef BORNAGAIN_GUI_VIEWS_COMMONWIDGETS_DOUBLELINEEDIT_H
+#define BORNAGAIN_GUI_VIEWS_COMMONWIDGETS_DOUBLELINEEDIT_H
+
+#include "GUI/Models/DoubleDescriptor.h"
+#include <QLineEdit>
+
+class QDoubleValidator;
+
+//! LineEdit to edit values in a scientific notation, operating on a DoubleDescriptor.
+//!
+//! In the future it can be enhanced to support units. At the moment, no DoubleDescriptor with units
+//! is used with a DoubleLineEdit, therefore the handling of units is not implemented yet. Only the
+//! naming is prepared already (also to have a naming alike to DoubleSpinBox).
+class DoubleLineEdit : public QLineEdit {
+    Q_OBJECT
+public:
+    DoubleLineEdit(QWidget* parent, const DoubleDescriptor& d);
+
+    //! Set the base value (unit is the one of the contained descriptor).
+    void setBaseValue(double baseValue);
+
+signals:
+    //! Emitted whenever the value changes.
+    //!
+    //! newBaseValue is in the unit of the valueDescriptor.
+    void baseValueChanged(double newBaseValue);
+
+private slots:
+    void onEditingFinished();
+
+private:
+    QDoubleValidator* m_validator;
+    DoubleDescriptor m_valueDescriptor;
+};
+
+
+#endif // BORNAGAIN_GUI_VIEWS_COMMONWIDGETS_DOUBLELINEEDIT_H
diff --git a/GUI/Views/CommonWidgets/DoubleSpinBox.cpp b/GUI/Views/CommonWidgets/DoubleSpinBox.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f08d4180743283fab736b21ef39ce1dde038e586
--- /dev/null
+++ b/GUI/Views/CommonWidgets/DoubleSpinBox.cpp
@@ -0,0 +1,89 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/Views/CommonWidgets/DoubleSpinBox.cpp
+//! @brief     Implements class DoubleSpinBox
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2021
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#include "GUI/Views/CommonWidgets/DoubleSpinBox.h"
+#include "GUI/Views/CommonWidgets/GUIHelpers.h"
+
+DoubleSpinBox::DoubleSpinBox(QWidget* parent, const DoubleDescriptor& d)
+    : QDoubleSpinBox(parent), m_valueDescriptor(d)
+{
+    GUI::View::Helpers::configSpinbox(this, d.decimals, d.limits);
+    setToolTip(d.tooltip);
+    setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+    if (std::holds_alternative<QString>(m_valueDescriptor.unit))
+        setDisplayUnit(Unit::other);
+    else
+        setDisplayUnit(std::get<Unit>(m_valueDescriptor.unit));
+
+    QObject::connect(this, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
+                     &DoubleSpinBox::onDisplayValueChanged);
+}
+
+void DoubleSpinBox::setDisplayUnit(Unit displayUnit)
+{
+    m_displayUnit = displayUnit;
+
+    if (m_showUnitAsSuffix) {
+        const QString suffix = displayUnitAsString();
+        if (suffix.isEmpty())
+            setSuffix("");
+        else
+            setSuffix(" " + suffix);
+    }
+
+    QSignalBlocker b(this);
+    setValue(toDisplayValue(m_valueDescriptor.get()));
+}
+
+double DoubleSpinBox::toDisplayValue(double baseValue) const
+{
+    return convert(baseValue, baseUnit(), m_displayUnit);
+}
+
+double DoubleSpinBox::toBaseValue(double displayValue) const
+{
+    return convert(displayValue, m_displayUnit, baseUnit());
+}
+
+QString DoubleSpinBox::displayUnitAsString() const
+{
+    if (std::holds_alternative<QString>(m_valueDescriptor.unit))
+        return std::get<QString>(m_valueDescriptor.unit);
+
+    return unitAsString(m_displayUnit);
+}
+
+const DoubleDescriptor& DoubleSpinBox::valueDescriptor() const
+{
+    return m_valueDescriptor;
+}
+
+void DoubleSpinBox::setBaseValue(double baseValue)
+{
+    setValue(toDisplayValue(baseValue));
+}
+
+void DoubleSpinBox::onDisplayValueChanged(double newDisplayValue)
+{
+    emit baseValueChanged(toBaseValue(newDisplayValue));
+}
+
+Unit DoubleSpinBox::baseUnit() const
+{
+    if (std::holds_alternative<QString>(m_valueDescriptor.unit))
+        return Unit::other;
+
+    return std::get<Unit>(m_valueDescriptor.unit);
+}
diff --git a/GUI/Views/CommonWidgets/DoubleSpinBox.h b/GUI/Views/CommonWidgets/DoubleSpinBox.h
new file mode 100644
index 0000000000000000000000000000000000000000..103ede224bd195f8e0f6a44dc982e73927f49605
--- /dev/null
+++ b/GUI/Views/CommonWidgets/DoubleSpinBox.h
@@ -0,0 +1,73 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/Views/CommonWidgets/DoubleSpinBox.h
+//! @brief     Defines class DoubleSpinBox
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2021
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#ifndef BORNAGAIN_GUI_VIEWS_COMMONWIDGETS_DOUBLESPINBOX_H
+#define BORNAGAIN_GUI_VIEWS_COMMONWIDGETS_DOUBLESPINBOX_H
+
+#include "GUI/Models/DoubleDescriptor.h"
+#include <QDoubleSpinBox>
+
+//! SpinBox for DoubleDescriptors, supporting units.
+class DoubleSpinBox : public QDoubleSpinBox {
+    Q_OBJECT
+public:
+    DoubleSpinBox(QWidget* parent, const DoubleDescriptor& d);
+
+    //! Set a display unit.
+    //!
+    //! The caller has to make sure that the new display unit has a conversion to/from the contained
+    //! base value unit.
+    void setDisplayUnit(Unit displayUnit);
+
+    //! Set the base value (unit is the one of the contained descriptor).
+    void setBaseValue(double baseValue);
+
+    //! The display unit as human readable string.
+    QString displayUnitAsString() const;
+
+    //! The descriptor on which this spinbox operates.
+    const DoubleDescriptor& valueDescriptor() const;
+
+    //! Returns the unit of the contained DoubleDescriptor.
+    //!
+    //! If the unit is defined as a string, this method returns Unit::other. To get the string, use
+    //! valueDescriptor().unit
+    Unit baseUnit() const;
+
+signals:
+    //! Emitted whenever the value changes.
+    //!
+    //! newBaseValue is in the unit of the valueDescriptor.
+    void baseValueChanged(double newBaseValue);
+
+private:
+    void onDisplayValueChanged(double newValue);
+
+    double toDisplayValue(double baseValue) const;
+    double toBaseValue(double displayValue) const;
+
+    using QDoubleSpinBox::setValue; // To hide from usage
+
+private:
+    Unit m_displayUnit = Unit::unitless;
+
+    DoubleDescriptor m_valueDescriptor;
+
+    //! it was decided to not show the unit as a suffix. However, this may be user
+    //! selectable once, therefore the code is kept and controlled by this flag
+    bool m_showUnitAsSuffix = false;
+};
+
+
+#endif // BORNAGAIN_GUI_VIEWS_COMMONWIDGETS_DOUBLESPINBOX_H
diff --git a/GUI/mainwindow/actionmanager.cpp b/GUI/mainwindow/actionmanager.cpp
index 4e0f6dcad20938c5b089aecb9167f51fdda35d46..84e373bc7faf90c65de053719ddc14097c699c90 100644
--- a/GUI/mainwindow/actionmanager.cpp
+++ b/GUI/mainwindow/actionmanager.cpp
@@ -226,6 +226,8 @@ void ActionManager::onAboutToShowFileMenu()
 void ActionManager::onAboutToShowSettingsMenu()
 {
     m_settingsMenu->clear();
+    m_settingsMenu->setToolTipsVisible(true);
+
     QSettings settings;
 
     settings.beginGroup(GUI::Constants::S_SESSIONMODELVIEW);
@@ -250,8 +252,6 @@ void ActionManager::onAboutToShowSettingsMenu()
     connect(action, &QAction::toggled,
             [](bool b) { baApp->settings().setCreateNewProjectOnStartup(b); });
 
-    m_settingsMenu->setToolTipsVisible(true);
-
     m_settingsMenu->addSeparator();
 
     QActionGroup* styleActions = new QActionGroup(this);
diff --git a/Tests/Unit/GUI/TestComponentProxyModel.cpp b/Tests/Unit/GUI/TestComponentProxyModel.cpp
index b2fef3e50731bff9730c14422521590750b1cfce..3cbd9e2c445e22e6f544298d16218b140b84b5b5 100644
--- a/Tests/Unit/GUI/TestComponentProxyModel.cpp
+++ b/Tests/Unit/GUI/TestComponentProxyModel.cpp
@@ -373,7 +373,7 @@ TEST_F(TestComponentProxyModel, test_setRootIndexLayer)
     EXPECT_FALSE(multilayerProxyIndex.isValid());
 
     QModelIndex layerProxyIndex = proxy.mapFromSource(model.indexOfItem(layer1));
-    EXPECT_EQ(proxy.rowCount(layerProxyIndex), 4); // thickness, material, slices, roughness
+    EXPECT_EQ(proxy.rowCount(layerProxyIndex), 5); // thickness, material, slices, roughness, color
     EXPECT_EQ(proxy.columnCount(layerProxyIndex), 2);
     EXPECT_TRUE(layerProxyIndex.isValid());
     EXPECT_TRUE(layerProxyIndex.parent() == QModelIndex());