From c906965aa9d78a0f80ae34c467d7285eaa7db123 Mon Sep 17 00:00:00 2001 From: Matthias Puchner <github@mpuchner.de> Date: Wed, 20 Oct 2021 22:06:01 +0200 Subject: [PATCH] introduce WidgetMoverButton (to realize the "move layer" feature in the upcoming layer oriented sample editor) --- .../SampleDesigner/WidgetMoverButton.cpp | 103 ++++++++++++++++++ GUI/Views/SampleDesigner/WidgetMoverButton.h | 62 +++++++++++ 2 files changed, 165 insertions(+) create mode 100644 GUI/Views/SampleDesigner/WidgetMoverButton.cpp create mode 100644 GUI/Views/SampleDesigner/WidgetMoverButton.h diff --git a/GUI/Views/SampleDesigner/WidgetMoverButton.cpp b/GUI/Views/SampleDesigner/WidgetMoverButton.cpp new file mode 100644 index 00000000000..6a428f4b155 --- /dev/null +++ b/GUI/Views/SampleDesigner/WidgetMoverButton.cpp @@ -0,0 +1,103 @@ +// ************************************************************************************************ +// +// BornAgain: simulate and fit reflection and scattering +// +//! @file GUI/Views/SampleDesigner/WidgetMoverButton.cpp +//! @brief Implements class WidgetMoverButton +//! +//! @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/SampleDesigner/WidgetMoverButton.h" +#include <QLayout> +#include <QMouseEvent> +#include <QPropertyAnimation> + +WidgetMoverButton::WidgetMoverButton(QWidget* parent, QWidget* widgetToMove, int ignoreOnTop) + : QToolButton(parent), m_widgetToMove(widgetToMove), m_ignoreOnTop(ignoreOnTop) +{ + setIcon(QIcon(":/images/toolbar24dark_hambar.svg")); +} + +void WidgetMoverButton::mousePressEvent(QMouseEvent* event) +{ + m_mouseDownY = event->globalY(); +} + +void WidgetMoverButton::mouseReleaseEvent(QMouseEvent* event) +{ + m_started = false; + if (m_layoutToDeactivate != nullptr) { + m_layoutToDeactivate->setEnabled(true); + m_layoutToDeactivate->update(); + } + emit finishedMoving(m_widgetToMove, m_dropAboveThisWidget); +} + +void WidgetMoverButton::mouseMoveEvent(QMouseEvent* event) +{ + int dy = event->globalY() - m_mouseDownY; + if (abs(dy) > 5 && !m_started) { + m_started = true; + + m_layoutToDeactivate = m_widgetToMove->parentWidget()->layout(); + + m_yOfFirstRelevantWidget = m_layoutToDeactivate->itemAt(m_ignoreOnTop)->geometry().top(); + m_layoutToDeactivate->setEnabled(false); + m_originalWidgetY = m_widgetToMove->y(); + m_widgetToMove->raise(); + emit startingToMove(); + m_widgetToMove->move(m_widgetToMove->x() + 5, m_widgetToMove->y()); + m_widgetToMove->resize(m_widgetToMove->width() - 10, m_widgetToMove->height()); + } + + if (m_started) { + + // -- move the dragged widget to the new position + m_widgetToMove->move(m_widgetToMove->x(), m_originalWidgetY + dy); + + // -- move other widgets to make room + m_dropAboveThisWidget = nullptr; + int yTopOfLayoutItem = m_yOfFirstRelevantWidget; + + for (int i = m_ignoreOnTop; i < m_layoutToDeactivate->count(); ++i) { + auto* layoutItem = m_layoutToDeactivate->itemAt(i); + if (layoutItem->widget() == m_widgetToMove) + continue; + + const bool movedWidgetIsAboveThisLayoutItem = + m_widgetToMove->y() < yTopOfLayoutItem + layoutItem->geometry().height() / 2; + if (movedWidgetIsAboveThisLayoutItem && layoutItem->widget() != nullptr + && m_dropAboveThisWidget == nullptr) + m_dropAboveThisWidget = layoutItem->widget(); + + QRect r = layoutItem->geometry(); + if (movedWidgetIsAboveThisLayoutItem) + r.moveTop(yTopOfLayoutItem + m_widgetToMove->height() + + m_layoutToDeactivate->spacing()); + else + r.moveTop(yTopOfLayoutItem); + + if (r != layoutItem->geometry()) { + QWidget* w = layoutItem->widget(); + if (w == nullptr) { + layoutItem->setGeometry(r); + } else { + QPropertyAnimation* animation = new QPropertyAnimation(w, "geometry"); + animation->setDuration(100); + animation->setEasingCurve(QEasingCurve::OutQuad); + animation->setStartValue(w->geometry()); + animation->setEndValue(r); + + animation->start(); + } + } + + yTopOfLayoutItem += r.height() + m_layoutToDeactivate->spacing(); + } + } +} diff --git a/GUI/Views/SampleDesigner/WidgetMoverButton.h b/GUI/Views/SampleDesigner/WidgetMoverButton.h new file mode 100644 index 00000000000..1f0e29002c1 --- /dev/null +++ b/GUI/Views/SampleDesigner/WidgetMoverButton.h @@ -0,0 +1,62 @@ +// ************************************************************************************************ +// +// BornAgain: simulate and fit reflection and scattering +// +//! @file GUI/Views/SampleDesigner/WidgetMoverButton.h +//! @brief Defines class WidgetMoverButton +//! +//! @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_SAMPLEDESIGNER_WIDGETMOVERBUTTON_H +#define BORNAGAIN_GUI_VIEWS_SAMPLEDESIGNER_WIDGETMOVERBUTTON_H + +#include <QToolButton> + +//! Button to move a widget vertically in a layout. +//! +//! With this button, the "move layer" feature in the layer oriented sample editor is realized +//! (it is the button on each LayerForm). +//! +//! When pressing the button, the parent layout of the widget is deactivated, and the widget is +//! moved to the position where the pressed mouse cursor is moved. +//! When the mouse is released, the layout is again activated and a signal finishedMoving() is +//! emitted. Any reordering of the widgets in the layout then has to be handled outside - no +//! reordering is done in here. +class WidgetMoverButton : public QToolButton { + Q_OBJECT +public: + //! Create a widget mover button. + //! + //! If the widget shall not be able to be dragged on the top position, this can be defined by + //! ignoreOnTop (the number of widgets at the top of the layout which shall not be affected by + //! the reordering). In the case of the layer moving, the topmost Form (the multilayer + //! properties) shall not be part of reordering. + WidgetMoverButton(QWidget* parent, QWidget* widgetToMove, int ignoreOnTop = 0); + +protected: + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + virtual void mouseMoveEvent(QMouseEvent* event) override; + +signals: + void startingToMove(); + void finishedMoving(QWidget* widgetToMove, QWidget* moveAboveThisWidget); + +private: + bool m_started = false; + int m_mouseDownY; + QLayout* m_layoutToDeactivate = nullptr; + QWidget* m_widgetToMove; + QWidget* m_dropAboveThisWidget; + int m_originalWidgetY; + int m_yOfFirstRelevantWidget; + int m_ignoreOnTop; +}; + + +#endif // BORNAGAIN_GUI_VIEWS_SAMPLEDESIGNER_WIDGETMOVERBUTTON_H -- GitLab