// ************************************************************************************************ // // BornAgain: simulate and fit reflection and scattering // //! @file GUI/View/Sample/LayerStackForm.cpp //! @brief Implements class LayerStackForm. //! //! @homepage http://www.bornagainproject.org //! @license GNU General Public License v3 or higher (see COPYING) //! @copyright Forschungszentrum Jülich GmbH 2024 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) // // ************************************************************************************************ #include "GUI/View/Sample/LayerStackForm.h" #include "Base/Util/Vec.h" #include "GUI/Model/Sample/LayerItem.h" #include "GUI/Model/Sample/LayerStackItem.h" #include "GUI/Model/Sample/SampleItem.h" #include "GUI/View/Base/LayoutUtil.h" #include "GUI/View/Sample/HeinzFormLayout.h" #include "GUI/View/Sample/LayerForm.h" #include "GUI/View/Sample/SampleEditorController.h" #include <QPushButton> 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(LayerStackForm* parentStackForm, const ItemWithLayers* itemBefore, SampleEditorController* ec) : QWidget(parentStackForm) , 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(*parentStackForm, itemBefore); }); l->addSpacing(10); auto* stack_btn = new QPushButton("Add stack", this); l->addWidget(stack_btn); connect(stack_btn, &QPushButton::clicked, [=] { ec->addLayerStackItem(*parentStackForm, itemBefore); }); l->addStretch(); } const ItemWithLayers* m_item; }; } // namespace LayerStackForm::LayerStackForm(QWidget* parent, LayerStackItem* stack, SampleEditorController* ec) : LayerContainerForm(parent, stack, ec, "stack") , m_stack_layout(new QVBoxLayout) { m_layout->addRow(m_stack_layout); for (auto* item : stack->componentItems()) { m_stack_layout->addWidget(new AddLayerContainerWidget(this, item, m_ec), 0, Qt::AlignTop); if (auto* layer = dynamic_cast<LayerItem*>(item)) m_stack_layout->addWidget(new LayerForm(this, layer, m_ec), 0, Qt::AlignTop); else if (auto* stack = dynamic_cast<LayerStackItem*>(item)) m_stack_layout->addWidget(new LayerStackForm(this, stack, m_ec), 0, Qt::AlignTop); else ASSERT_NEVER; } m_stack_layout->addWidget(new AddLayerContainerWidget(this, nullptr, m_ec), 0, Qt::AlignTop); m_stack_layout->setSizeConstraint(QLayout::SetMinimumSize); updatePositionDependentElements(); if (isOuterStack()) m_title_widget->hide(); } LayerStackItem& LayerStackForm::stackItem() const { return dynamic_cast<LayerStackItem&>(*m_item); } void LayerStackForm::addComponentForm(ItemWithLayers* componentItem) { LayerContainerForm* newForm; if (auto* layer = dynamic_cast<LayerItem*>(componentItem)) newForm = new LayerForm(this, layer, m_ec); else if (auto* stack = dynamic_cast<LayerStackItem*>(componentItem)) newForm = new LayerStackForm(this, stack, m_ec); else ASSERT_NEVER; const int row_in_layout = rowInLayout(componentItem); // same row => button is above! m_stack_layout->insertWidget( row_in_layout, new AddLayerContainerWidget(this, componentItem, m_ec), 0, Qt::AlignTop); m_stack_layout->insertWidget(row_in_layout, newForm, 0, Qt::AlignTop); } void LayerStackForm::onComponentMoved(const ItemWithLayers* componentItem) { LayerContainerForm* wl = nullptr; AddLayerContainerWidget* al = nullptr; for (int index = 0; index < m_stack_layout->count(); index++) { if (auto* w = dynamic_cast<AddLayerContainerWidget*>(m_stack_layout->itemAt(index)->widget())) if (w->m_item == componentItem) { al = w; m_stack_layout->takeAt(index); break; } } for (int index = 0; index < m_stack_layout->count(); index++) { if (auto* w = dynamic_cast<LayerContainerForm*>(m_stack_layout->itemAt(index)->widget())) if (w->item() == componentItem) { wl = w; m_stack_layout->takeAt(index); break; } } const int row_in_layout = rowInLayout(componentItem); m_stack_layout->insertWidget(row_in_layout, wl, 0, Qt::AlignTop); // same row => button is above! m_stack_layout->insertWidget(row_in_layout, al, 0, Qt::AlignTop); } void LayerStackForm::removeComponentForm(ItemWithLayers* componentItem) { LayerContainerForm* componentForm = nullptr; AddLayerContainerWidget* addLayerWidget = nullptr; for (auto* c : findChildren<QWidget*>()) { if (auto* w = dynamic_cast<AddLayerContainerWidget*>(c)) if (w->m_item == componentItem) addLayerWidget = w; if (auto* w = dynamic_cast<LayerContainerForm*>(c)) { if (w->item() == componentItem) componentForm = w; } } ASSERT(componentForm); ASSERT(addLayerWidget); // delete editors which are subscribed to SessionItems GUI::Util::Layout::clearLayout(componentForm->layout()); componentForm->hide(); componentForm->setParent(nullptr); // so it is not findable in update routines componentForm->deleteLater(); // delete later (this is the sender) delete addLayerWidget; } void LayerStackForm::updatePositionDependentElements() { LayerContainerForm::updatePositionDependentElements(); updateTitle(); } void LayerStackForm::updateTitle() { const SampleItem* sampleItem = m_ec->sampleItem(); ASSERT(sampleItem); auto unique_layers_inside = stackItem().uniqueLayerItems(); if (!unique_layers_inside.size()) { setTitle("Empty stack"); return; } LayerItem* first_layer = unique_layers_inside.front(); LayerItem* last_layer = unique_layers_inside.back(); auto unique_layers_global = sampleItem->uniqueLayerItems(); int i_first = Vec::indexOfPtr(first_layer, unique_layers_global); int i_last = Vec::indexOfPtr(last_layer, unique_layers_global); setTitle("Stack of layers from " + QString::number(i_first) + " to " + QString::number(i_last)); } bool LayerStackForm::isOuterStack() const { return &stackItem() == &(m_ec->sampleItem()->outerStackItem()); } int LayerStackForm::rowInLayout(const ItemWithLayers* componentItem) const { return stackItem().indexOfComponent(componentItem) * 2 + 1; }