// ************************************************************************************************ // // BornAgain: simulate and fit reflection and scattering // //! @file GUI/View/Sample/SampleForm.cpp //! @brief Implements class SampleForm. //! //! @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/Sample/SampleForm.h" #include "Base/Util/Vec.h" #include "GUI/Model/Project/ProjectDocument.h" #include "GUI/Model/Sample/LayerItem.h" #include "GUI/Model/Sample/LayerStackItem.h" #include "GUI/Model/Sample/SamplesSet.h" #include "GUI/View/Base/ActionFactory.h" #include "GUI/View/Base/LayoutUtil.h" #include "GUI/View/Sample/CompoundForm.h" #include "GUI/View/Sample/CoreAndShellForm.h" #include "GUI/View/Sample/HeinzFormLayout.h" #include "GUI/View/Sample/LayerForm.h" #include "GUI/View/Sample/LayerStackForm.h" #include "GUI/View/Sample/MesocrystalForm.h" #include "GUI/View/Sample/ParticleForm.h" #include "GUI/View/Sample/ParticleLayoutForm.h" #include "GUI/View/Sample/SampleEditorController.h" #include "GUI/View/Widget/AppConfig.h" #include "GUI/View/Widget/GroupBoxes.h" #include <QBoxLayout> #include <QLabel> #include <QLineEdit> #include <QTextEdit> namespace { //! Widget with a button to add a layer or stack (the "Add layer/add stack" buttons shown between //! items) class AddLayerContainerWidget : public QWidget { public: AddLayerContainerWidget(QWidget* parent, LayerStackItem& parentStackItem, ItemWithLayers* itemBefore, SampleEditorController* ec) : QWidget(parent) , m_item(itemBefore) { auto* l = new QHBoxLayout(this); l->setContentsMargins(0, 0, 0, 0); l->addStretch(); auto* layer_btn = new QPushButton("Add layer", this); l->addWidget(layer_btn); connect(layer_btn, &QPushButton::clicked, [&] { ec->addLayerItem(parentStackItem, itemBefore); }); auto* stack_btn = new QPushButton("Add stack", this); l->addWidget(stack_btn); connect(stack_btn, &QPushButton::clicked, [&] { ec->addLayerItem(parentStackItem, itemBefore); }); l->addStretch(); } ItemWithLayers* m_item; }; } // namespace SampleForm::SampleForm(SampleItem* sampleItem, SampleEditorController* ec) : m_layout(new QVBoxLayout(this)) , m_sample_item(sampleItem) , m_ec(ec) { setAttribute(Qt::WA_StyledBackground, true); m_layout->setAlignment(Qt::AlignTop); auto* summary = new CollapsibleGroupBox("Summary and layer-independent parameters", this, sampleItem->expandInfo); m_layout->addWidget(summary, 0, Qt::AlignTop); summary->setObjectName("SampleSummary"); auto* gLayout = new HeinzFormLayout(ec); summary->body()->setLayout(gLayout); auto* nameEdit = new QLineEdit; gLayout->addBoldRow("Name:", nameEdit); nameEdit->setText(sampleItem->name()); nameEdit->setFixedWidth(585); connect(nameEdit, &QLineEdit::textEdited, [](const QString& s) { gDoc->samplesRW()->setCurrentName(s); }); auto* descriptionEdit = new QTextEdit; gLayout->addBoldRow("Description:", descriptionEdit); descriptionEdit->setFixedWidth(585); descriptionEdit->setFixedHeight(60); descriptionEdit->setAcceptRichText(false); descriptionEdit->setTabChangesFocus(true); descriptionEdit->setPlainText(sampleItem->description()); connect(descriptionEdit, &QTextEdit::textChanged, [descriptionEdit] { gDoc->samplesRW()->setCurrentDescription(descriptionEdit->toPlainText()); }); // Processing external field is not implemented yet, so temporary disable it (see issue #654) // m_layout->addVector(sampleItem->externalField(), false); auto* showInRealspaceAction = ActionFactory::createShowInRealspaceAction( this, "sample", [this] { m_ec->requestViewInRealspace(m_sample_item); }); summary->addTitleAction(showInRealspaceAction); for (auto* item : sampleItem->componentItems()) { m_layout->addWidget( new AddLayerContainerWidget(this, m_sample_item->outerStackItem(), item, m_ec), 0, Qt::AlignTop); if (auto* layer = dynamic_cast<LayerItem*>(item)) m_layout->addWidget(new LayerForm(this, layer, m_ec), 0, Qt::AlignTop); else if (auto* stack = dynamic_cast<LayerStackItem*>(item)) m_layout->addWidget(new LayerStackForm(this, stack, m_ec), 0, Qt::AlignTop); else ASSERT_NEVER; } m_layout->addWidget( new AddLayerContainerWidget(this, m_sample_item->outerStackItem(), nullptr, m_ec), 0, Qt::AlignTop); m_layout->setSizeConstraint(QLayout::SetMinimumSize); } void SampleForm::onLayerAdded(LayerItem* layerItem) { const int rowInMultiLayer = Vec::indexOfPtr(layerItem, m_sample_item->uniqueLayerItems()); const int rowInLayout = rowInMultiLayer * 2 + 1; m_layout->insertWidget(rowInLayout, new LayerForm(this, layerItem, m_ec), 0, Qt::AlignTop); // same row => button is above! m_layout->insertWidget( rowInLayout, new AddLayerContainerWidget(this, m_sample_item->outerStackItem(), layerItem, m_ec), 0, Qt::AlignTop); updateRowVisibilities(); } void SampleForm::onLayerMoved(ItemWithLayers* item) { LayerContainerForm* wl = nullptr; AddLayerContainerWidget* al = nullptr; for (int index = 0; index < m_layout->count(); index++) { if (auto* w = dynamic_cast<AddLayerContainerWidget*>(m_layout->itemAt(index)->widget())) if (w->m_item == item) { al = w; m_layout->takeAt(index); break; } } for (int index = 0; index < m_layout->count(); index++) { if (auto* w = dynamic_cast<LayerContainerForm*>(m_layout->itemAt(index)->widget())) if (w->item() == item) { wl = w; m_layout->takeAt(index); break; } } const int rowInMultiLayer = Vec::indexOfPtr(item, m_sample_item->uniqueLayerItems()); const int rowInLayout = rowInMultiLayer * 2 + 1; m_layout->insertWidget(rowInLayout, wl, 0, Qt::AlignTop); // same row => button is above! m_layout->insertWidget(rowInLayout, al, 0, Qt::AlignTop); updateRowVisibilities(); } void SampleForm::onAboutToRemoveLayerContainer(ItemWithLayers* itemWithLayers) { LayerContainerForm* layerContainerForm = nullptr; AddLayerContainerWidget* addLayerWidget = nullptr; for (auto* c : findChildren<QWidget*>()) { if (auto* w = dynamic_cast<AddLayerContainerWidget*>(c)) if (w->m_item == itemWithLayers) addLayerWidget = w; if (auto* w = dynamic_cast<LayerContainerForm*>(c)) { if (w->item() == itemWithLayers) layerContainerForm = w; } } if (layerContainerForm) { // delete editors which are subscribed to SessionItems GUI::Util::Layout::clearLayout(layerContainerForm->layout()); layerContainerForm->hide(); layerContainerForm->setParent(nullptr); // so it is not findable in update routines layerContainerForm->deleteLater(); // delete later (this is the sender) } delete addLayerWidget; } void SampleForm::updateRowVisibilities() { for (auto* c : findChildren<LayerForm*>()) c->updateLayerPositionDependentElements(); } LayerForm* SampleForm::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); }