BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
WidgetMoverButton.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/View/SampleDesigner/WidgetMoverButton.cpp
6 //! @brief Implements class WidgetMoverButton
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2021
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
16 #include "Base/Util/Assert.h"
17 #include <QLayout>
18 #include <QMouseEvent>
19 #include <QPropertyAnimation>
20 #include <QScrollArea>
21 #include <QScrollBar>
22 
23 WidgetMoverButton::WidgetMoverButton(QWidget* parent, QWidget* widgetToMove, int ignoreOnTop)
24  : QToolButton(parent)
25  , m_widgetToMove(widgetToMove)
26  , m_dropAboveThisWidget(nullptr)
27  , m_ignoreOnTop(ignoreOnTop)
28  , m_scrollArea(nullptr)
29 {
30  setIcon(QIcon(":/images/move_up_down.svg"));
31  m_dragScrollTimer.setSingleShot(false);
32 }
33 
34 void WidgetMoverButton::mousePressEvent(QMouseEvent* event)
35 {
36  if (m_scrollArea == nullptr) {
37  QWidget* p = parentWidget();
38  do {
39  m_scrollArea = dynamic_cast<QScrollArea*>(p);
40  p = p->parentWidget();
41  } while (p != nullptr && m_scrollArea == nullptr);
42  }
43 
44  ASSERT(m_scrollArea);
45  m_globalMouseDownY = event->globalY();
46  m_hotSpot = event->globalPos()
47  - m_widgetToMove->parentWidget()->mapToGlobal(m_widgetToMove->geometry().topLeft());
48 }
49 
51 {
52  if (!m_started)
53  return;
54 
55  qDeleteAll(m_animations.values());
56  m_animations.clear();
57 
58  m_dragScrollTimer.stop();
59  m_started = false;
60  if (m_layoutToDeactivate != nullptr) {
61  m_layoutToDeactivate->setEnabled(true);
62  m_layoutToDeactivate->update();
63  }
65 }
66 
67 void WidgetMoverButton::mouseMoveEvent(QMouseEvent* event)
68 {
69  int dy = event->globalY() - m_globalMouseDownY;
70  if (abs(dy) > 5 && !m_started) {
71  m_started = true;
72 
73  m_layoutToDeactivate = m_widgetToMove->parentWidget()->layout();
74 
75  m_yOfFirstRelevantWidget = m_layoutToDeactivate->itemAt(m_ignoreOnTop)->geometry().top();
76  m_layoutToDeactivate->setEnabled(false);
78  m_widgetToMove->raise();
79  emit startingToMove();
80  m_widgetToMove->move(m_widgetToMove->x() + 5, m_widgetToMove->y());
81  m_widgetToMove->resize(m_widgetToMove->width() - 10, m_widgetToMove->height());
82  }
83 
84  if (m_started) {
85  // -- move the dragged widget to the new position
86  QPoint newPos =
87  m_widgetToMove->parentWidget()->mapFromGlobal(event->globalPos()) - m_hotSpot;
88  m_widgetToMove->move(m_widgetToMove->x(), newPos.y());
89 
90  // -- move other widgets to make room
91  m_dropAboveThisWidget = nullptr;
92  int yTopOfLayoutItem = m_yOfFirstRelevantWidget;
93 
94  for (int i = m_ignoreOnTop; i < m_layoutToDeactivate->count(); ++i) {
95  auto* layoutItem = m_layoutToDeactivate->itemAt(i);
96  if (layoutItem->widget() == m_widgetToMove)
97  continue;
98 
99  const bool movedWidgetIsAboveThisLayoutItem =
100  m_widgetToMove->y() < yTopOfLayoutItem + layoutItem->geometry().height() / 2;
101  if (movedWidgetIsAboveThisLayoutItem && layoutItem->widget() != nullptr
102  && m_dropAboveThisWidget == nullptr)
103  m_dropAboveThisWidget = layoutItem->widget();
104 
105  QRect r = layoutItem->geometry();
106  if (movedWidgetIsAboveThisLayoutItem)
107  r.moveTop(yTopOfLayoutItem + m_widgetToMove->height()
108  + m_layoutToDeactivate->spacing());
109  else
110  r.moveTop(yTopOfLayoutItem);
111 
112  if (r != layoutItem->geometry()) {
113  QWidget* w = layoutItem->widget();
114  if (w == nullptr)
115  layoutItem->setGeometry(r);
116  else if (!m_animations.contains(w)) {
117  auto* animation = new QPropertyAnimation(w, "geometry");
118  animation->setDuration(100);
119  animation->setEasingCurve(QEasingCurve::OutQuad);
120  animation->setStartValue(w->geometry());
121  animation->setEndValue(r);
122  animation->start();
123  m_animations[w] = animation;
124  } else {
125  auto* animation = m_animations[w];
126  if (animation->endValue() != r) {
127  animation->stop();
128  animation->setStartValue(w->geometry());
129  animation->setEndValue(r);
130  animation->start();
131  }
132  }
133  }
134 
135  yTopOfLayoutItem += r.height() + m_layoutToDeactivate->spacing();
136  }
137 
138  // -- check whether scrolling is necessary
139  QPoint parPos = m_scrollArea->mapFromGlobal(mapToGlobal(event->pos()));
140  if (parPos.y() < 20) {
141  m_dragScrollTimer.setInterval(10);
142  m_dragScrollTimer.disconnect();
143  connect(&m_dragScrollTimer, &QTimer::timeout, [=]() { scrollParent(true); });
144  m_dragScrollTimer.start();
145  } else if (parPos.y() > m_scrollArea->height() - 20) {
146  m_dragScrollTimer.setInterval(10);
147  m_dragScrollTimer.disconnect();
148  connect(&m_dragScrollTimer, &QTimer::timeout, [=]() { scrollParent(false); });
149  m_dragScrollTimer.start();
150  } else
151  m_dragScrollTimer.stop();
152  }
153 }
154 
156 {
157  auto* scrollbar = m_scrollArea->verticalScrollBar();
158  if (!scrollbar->isVisible())
159  return;
160 
161  const int oldScrollValue = scrollbar->value();
162  scrollbar->setValue(oldScrollValue + (up ? -5 : 5));
163 
164  // move the dragged widget to the new position to avoid jitter effects
165  // use the real change of value, not +/-5: scrolling may have been unsuccessful
166  m_widgetToMove->move(m_widgetToMove->x(),
167  m_widgetToMove->y() + scrollbar->value() - oldScrollValue);
168 }
Defines class WidgetMoverButton.
void mouseMoveEvent(QMouseEvent *event) override
void mousePressEvent(QMouseEvent *event) override
WidgetMoverButton(QWidget *parent, QWidget *widgetToMove, int ignoreOnTop=0)
Create a widget mover button.
void mouseReleaseEvent(QMouseEvent *event) override
QScrollArea * m_scrollArea
QLayout * m_layoutToDeactivate
void finishedMoving(QWidget *widgetToMove, QWidget *moveAboveThisWidget)
QMap< QWidget *, QPropertyAnimation * > m_animations
QWidget * m_dropAboveThisWidget
void scrollParent(bool up)
QPoint m_hotSpot
The mouse-down coordinates in the widget to move.