// ************************************************************************************************ // // BornAgain: simulate and fit reflection and scattering // //! @file GUI/View/SampleDesigner/MultiLayerForm.cpp //! @brief Implements class MultiLayerForm //! //! @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/View/SampleDesigner/MultiLayerForm.h" #include "GUI/Model/Sample/MultiLayerItem.h" #include "GUI/Util/ActionFactory.h" #include "GUI/View/Common/DoubleSpinBox.h" #include "GUI/View/SampleDesigner/LayerForm.h" #include "GUI/View/SampleDesigner/MesoCrystalForm.h" #include "GUI/View/SampleDesigner/ParticleCompositionForm.h" #include "GUI/View/SampleDesigner/ParticleCoreShellForm.h" #include "GUI/View/SampleDesigner/ParticleForm.h" #include "GUI/View/SampleDesigner/ParticleLayoutForm.h" #include "GUI/View/Tool/GroupBoxCollapser.h" #include <QBoxLayout> #include <QLabel> #include <QLineEdit> #include <QPushButton> #include <QTextEdit> namespace { //! Widget with a button to add a layer (the "add layer" buttons shown between layers) class AddLayerWidget : public QWidget { public: AddLayerWidget(QWidget* parent, LayerItem* layer, SampleEditorController* ec) : QWidget(parent) , m_layer(layer) { auto* l = new QHBoxLayout(this); l->setContentsMargins(0, 0, 0, 0); auto* btn = new QPushButton("Add layer", this); l->addStretch(); l->addWidget(btn); l->addStretch(); connect(btn, &QPushButton::clicked, [=]() { ec->addLayer(layer); }); } LayerItem* m_layer; }; } // namespace MultiLayerForm::MultiLayerForm(QWidget* parent, MultiLayerItem* multiLayerItem, SampleEditorController* ec) : QWidget(parent) , m_multiLayerItem(multiLayerItem) , m_ec(ec) , m_useAngstrom(false) , m_useRadiant(false) { setObjectName("MultiLayerForm"); // important for style sheet addressing setAttribute(Qt::WA_StyledBackground, true); m_layout = new QVBoxLayout(this); auto* props = new QGroupBox(this); props->setTitle("Sample"); FormLayouter layouter(props, ec); layouter.setContentsMargins(6, 6, 0, 6); auto* nameEdit = new QLineEdit(props); layouter.addRow("Name:", nameEdit); nameEdit->setText(multiLayerItem->sampleName()); connect(nameEdit, &QLineEdit::textEdited, ec, &SampleEditorController::setSampleName); auto* descriptionEdit = new QTextEdit(props); descriptionEdit->setMinimumWidth(300); descriptionEdit->setMaximumHeight(100); descriptionEdit->setAcceptRichText(false); descriptionEdit->setTabChangesFocus(true); descriptionEdit->setPlainText(multiLayerItem->description()); layouter.addRow("Description:", descriptionEdit); connect(descriptionEdit, &QTextEdit::textChanged, [=]() { m_ec->setSampleDescription(descriptionEdit->toPlainText()); }); layouter.addValue(multiLayerItem->crossCorrLength()); layouter.addVector(multiLayerItem->externalFieldVector(), false); auto* collapser = GroupBoxCollapser::installIntoGroupBox(props, false); auto* showInRealSpaceAction = ActionFactory::createShowInRealSpaceAction( this, "sample", [=] { m_ec->requestViewInRealSpace(m_multiLayerItem); }); collapser->addAction(showInRealSpaceAction); m_layout->addWidget(props); for (auto* layer : multiLayerItem->layers()) { m_layout->addWidget(new AddLayerWidget(this, layer, m_ec)); m_layout->addWidget(new LayerForm(this, layer, m_ec)); } m_layout->addWidget(new AddLayerWidget(this, nullptr, m_ec)); m_layout->setSizeConstraint(QLayout::SetMinimumSize); m_layout->addStretch(1); } void MultiLayerForm::showInlineEditButtons(bool b) { m_showInlineEditButtons = b; updateRowVisibilities(); } void MultiLayerForm::onLayerAdded(LayerItem* layerItem) { const int rowInMultiLayer = m_multiLayerItem->layers().indexOf(layerItem); const int rowInLayout = rowInMultiLayer * 2 + 1; m_layout->insertWidget(rowInLayout, new LayerForm(this, layerItem, m_ec)); // same row => button is above! m_layout->insertWidget(rowInLayout, new AddLayerWidget(this, layerItem, m_ec)); updateRowVisibilities(); } void MultiLayerForm::onLayerMoved(LayerItem* layerItem) { LayerForm* wl = nullptr; AddLayerWidget* al = nullptr; for (int index = 0; index < m_layout->count(); index++) { if (auto* w = dynamic_cast<AddLayerWidget*>(m_layout->itemAt(index)->widget())) if (w->m_layer == layerItem) { al = w; m_layout->takeAt(index); break; } } for (int index = 0; index < m_layout->count(); index++) { if (auto* w = dynamic_cast<LayerForm*>(m_layout->itemAt(index)->widget())) if (w->layerItem() == layerItem) { wl = w; m_layout->takeAt(index); break; } } const int rowInMultiLayer = m_multiLayerItem->layers().indexOf(layerItem); const int rowInLayout = rowInMultiLayer * 2 + 1; m_layout->insertWidget(rowInLayout, wl); // same row => button is above! m_layout->insertWidget(rowInLayout, al); updateRowVisibilities(); } void MultiLayerForm::onAboutToRemoveLayer(LayerItem* layerItem) { LayerForm* layerForm = nullptr; AddLayerWidget* addLayerWidget = nullptr; for (auto* c : findChildren<QWidget*>()) { if (auto* w = dynamic_cast<AddLayerWidget*>(c)) if (w->m_layer == layerItem) addLayerWidget = w; if (auto* w = dynamic_cast<LayerForm*>(c)) { if (w->layerItem() == layerItem) layerForm = w; } } if (layerForm) { // delete editors which are subscribed to SessionItems GUI::Util::Layout::clearLayout(layerForm->layout()); layerForm->hide(); layerForm->setParent(nullptr); // so it is not findable in update routines layerForm->deleteLater(); // delete later (this is the sender) } delete addLayerWidget; } void MultiLayerForm::updateRowVisibilities() { for (auto* c : findChildren<QWidget*>()) { if (auto* w = dynamic_cast<LayerForm*>(c)) w->enableStructureEditing(m_showInlineEditButtons); if (auto* w = dynamic_cast<ParticleLayoutForm*>(c)) w->enableStructureEditing(m_showInlineEditButtons); if (auto* w = dynamic_cast<ParticleForm*>(c)) w->enableStructureEditing(m_showInlineEditButtons); if (auto* w = dynamic_cast<ParticleCompositionForm*>(c)) w->enableStructureEditing(m_showInlineEditButtons); if (auto* w = dynamic_cast<ParticleCoreShellForm*>(c)) w->enableStructureEditing(m_showInlineEditButtons); if (auto* w = dynamic_cast<MesoCrystalForm*>(c)) w->enableStructureEditing(m_showInlineEditButtons); } showAddLayerButtons(m_showInlineEditButtons); for (auto* c : findChildren<LayerForm*>()) c->updateLayerPositionDependentElements(); } void MultiLayerForm::updateUnits() { const auto set = [](DoubleSpinBox* spinbox, Unit valueUnit, Unit displayUnit) { if (spinbox->baseUnit() == valueUnit) spinbox->setDisplayUnit(displayUnit); }; for (auto* editor : findChildren<DoubleSpinBox*>()) { if (m_useAngstrom) { set(editor, Unit::nanometer, Unit::angstrom); set(editor, Unit::angstrom, Unit::angstrom); set(editor, Unit::nanometer2, Unit::angstrom2); set(editor, Unit::angstrom2, Unit::angstrom2); set(editor, Unit::nanometerMinus2, Unit::angstromMinus2); set(editor, Unit::angstromMinus2, Unit::angstromMinus2); } else { set(editor, Unit::nanometer, Unit::nanometer); set(editor, Unit::angstrom, Unit::nanometer); set(editor, Unit::nanometer2, Unit::nanometer2); set(editor, Unit::angstrom2, Unit::nanometer2); set(editor, Unit::nanometerMinus2, Unit::nanometerMinus2); set(editor, Unit::angstromMinus2, Unit::nanometerMinus2); } if (m_useRadiant) { set(editor, Unit::degree, Unit::radiant); set(editor, Unit::radiant, Unit::radiant); } else { set(editor, Unit::degree, Unit::degree); set(editor, Unit::radiant, Unit::degree); } } for (auto* label : findChildren<QLabel*>()) LayerEditorUtils::updateLabelUnit(label); } void MultiLayerForm::ensureVisible(QWidget* /*w*/) { // #baLayerEditor implement ensureVisible } void MultiLayerForm::setUseAngstrom(bool angstrom) { m_useAngstrom = angstrom; updateUnits(); } bool MultiLayerForm::useAngstrom() const { return m_useAngstrom; } void MultiLayerForm::setUseRadiant(bool radiant) { m_useRadiant = radiant; updateUnits(); } bool MultiLayerForm::useRadiant() const { return m_useRadiant; } void MultiLayerForm::showAddLayerButtons(bool show) { for (auto* c : findChildren<QWidget*>()) if (dynamic_cast<AddLayerWidget*>(c)) c->setVisible(show); } LayerForm* MultiLayerForm::findNextLayerForm(QWidget* w) { while (w != nullptr && dynamic_cast<LayerForm*>(w) == nullptr) { const auto index = m_layout->indexOf(w); if (index + 1 < m_layout->count()) w = m_layout->itemAt(index + 1)->widget(); else return nullptr; } return dynamic_cast<LayerForm*>(w); }