1 /*
2     Copyright © 2015-2019 by The qTox Project Contributors
3 
4     This file is part of qTox, a Qt-based graphical interface for Tox.
5 
6     qTox is libre software: you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10 
11     qTox is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with qTox.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "movablewidget.h"
21 #include <QGraphicsOpacityEffect>
22 #include <QMouseEvent>
23 #include <cmath>
24 
MovableWidget(QWidget * parent)25 MovableWidget::MovableWidget(QWidget* parent)
26     : QWidget(parent)
27 {
28     setMinimumHeight(64);
29     setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
30     actualSize = minimumSize();
31     boundaryRect = QRect(0, 0, 0, 0);
32     setRatio(1.0f);
33     resize(minimumSize());
34     actualPos = QPoint(0, 0);
35 }
36 
resetBoundary(QRect newBoundary)37 void MovableWidget::resetBoundary(QRect newBoundary)
38 {
39     boundaryRect = newBoundary;
40     resize(QSize(round(actualSize.width()), round(actualSize.height())));
41 
42     QPoint moveTo = QPoint(round(actualPos.x()), round(actualPos.y()));
43     checkBoundary(moveTo);
44     move(moveTo);
45     actualPos = moveTo;
46 }
47 
setBoundary(QRect newBoundary)48 void MovableWidget::setBoundary(QRect newBoundary)
49 {
50     if (boundaryRect.isNull()) {
51         boundaryRect = newBoundary;
52         return;
53     }
54 
55     float changeX = newBoundary.width() / static_cast<float>(boundaryRect.width());
56     float changeY = newBoundary.height() / static_cast<float>(boundaryRect.height());
57 
58     float percentageX = (x() - boundaryRect.x()) / static_cast<float>(boundaryRect.width() - width());
59     float percentageY =
60         (y() - boundaryRect.y()) / static_cast<float>(boundaryRect.height() - height());
61 
62     actualSize.setWidth(actualSize.width() * changeX);
63     actualSize.setHeight(actualSize.height() * changeY);
64 
65     if (actualSize.width() == 0)
66         actualSize.setWidth(1);
67 
68     if (actualSize.height() == 0)
69         actualSize.setHeight(1);
70 
71     resize(QSize(round(actualSize.width()), round(actualSize.height())));
72 
73     actualPos = QPointF(percentageX * (newBoundary.width() - width()),
74                         percentageY * (newBoundary.height() - height()));
75     actualPos += QPointF(newBoundary.topLeft());
76 
77     QPoint moveTo = QPoint(round(actualPos.x()), round(actualPos.y()));
78     move(moveTo);
79 
80     boundaryRect = newBoundary;
81 }
82 
getRatio() const83 float MovableWidget::getRatio() const
84 {
85     return ratio;
86 }
87 
setRatio(float r)88 void MovableWidget::setRatio(float r)
89 {
90     ratio = r;
91     resize(width(), width() / ratio);
92     QPoint position = QPoint(actualPos.x(), actualPos.y());
93     checkBoundary(position);
94     move(position);
95 
96     actualPos = pos();
97     actualSize = size();
98 }
99 
mousePressEvent(QMouseEvent * event)100 void MovableWidget::mousePressEvent(QMouseEvent* event)
101 {
102     if (event->buttons() & Qt::LeftButton) {
103         if (!(mode & Resize))
104             mode |= Moving;
105 
106         lastPoint = event->globalPos();
107     }
108 }
109 
mouseMoveEvent(QMouseEvent * event)110 void MovableWidget::mouseMoveEvent(QMouseEvent* event)
111 {
112     if (mode & Moving) {
113         QPoint moveTo = pos() - (lastPoint - event->globalPos());
114         checkBoundary(moveTo);
115 
116         move(moveTo);
117         lastPoint = event->globalPos();
118 
119         actualPos = pos();
120     } else {
121         if (!(event->buttons() & Qt::LeftButton)) {
122             if (event->x() < 6)
123                 mode |= ResizeLeft;
124             else
125                 mode &= ~ResizeLeft;
126 
127             if (event->y() < 6)
128                 mode |= ResizeUp;
129             else
130                 mode &= ~ResizeUp;
131 
132             if (event->x() > width() - 6)
133                 mode |= ResizeRight;
134             else
135                 mode &= ~ResizeRight;
136 
137             if (event->y() > height() - 6)
138                 mode |= ResizeDown;
139             else
140                 mode &= ~ResizeDown;
141         }
142 
143         if (mode & Resize) {
144             const Modes ResizeUpRight = ResizeUp | ResizeRight;
145             const Modes ResizeUpLeft = ResizeUp | ResizeLeft;
146             const Modes ResizeDownRight = ResizeDown | ResizeRight;
147             const Modes ResizeDownLeft = ResizeDown | ResizeLeft;
148 
149             if ((mode & ResizeUpRight) == ResizeUpRight || (mode & ResizeDownLeft) == ResizeDownLeft)
150                 setCursor(Qt::SizeBDiagCursor);
151             else if ((mode & ResizeUpLeft) == ResizeUpLeft || (mode & ResizeDownRight) == ResizeDownRight)
152                 setCursor(Qt::SizeFDiagCursor);
153             else if (mode & (ResizeLeft | ResizeRight))
154                 setCursor(Qt::SizeHorCursor);
155             else
156                 setCursor(Qt::SizeVerCursor);
157 
158             if (event->buttons() & Qt::LeftButton) {
159                 QPoint lastPosition = pos();
160                 QPoint displacement = lastPoint - event->globalPos();
161                 QSize lastSize = size();
162 
163 
164                 if (mode & ResizeUp) {
165                     lastSize.setHeight(height() + displacement.y());
166 
167                     if (lastSize.height() > maximumHeight())
168                         lastPosition.setY(y() - displacement.y()
169                                           + (lastSize.height() - maximumHeight()));
170                     else
171                         lastPosition.setY(y() - displacement.y());
172                 }
173 
174                 if (mode & ResizeLeft) {
175                     lastSize.setWidth(width() + displacement.x());
176                     if (lastSize.width() > maximumWidth())
177                         lastPosition.setX(x() - displacement.x() + (lastSize.width() - maximumWidth()));
178                     else
179                         lastPosition.setX(x() - displacement.x());
180                 }
181 
182                 if (mode & ResizeRight)
183                     lastSize.setWidth(width() - displacement.x());
184 
185                 if (mode & ResizeDown)
186                     lastSize.setHeight(height() - displacement.y());
187 
188                 if (lastSize.height() > maximumHeight())
189                     lastSize.setHeight(maximumHeight());
190 
191                 if (lastSize.width() > maximumWidth())
192                     lastSize.setWidth(maximumWidth());
193 
194                 if (mode & (ResizeLeft | ResizeRight)) {
195                     if (mode & (ResizeUp | ResizeDown)) {
196                         int height = lastSize.width() / getRatio();
197 
198                         if (!(mode & ResizeDown))
199                             lastPosition.setY(lastPosition.y() - (height - lastSize.height()));
200 
201                         resize(lastSize.width(), height);
202 
203                         if (lastSize.width() < minimumWidth())
204                             lastPosition.setX(pos().x());
205 
206                         if (height < minimumHeight())
207                             lastPosition.setY(pos().y());
208                     } else {
209                         resize(lastSize.width(), lastSize.width() / getRatio());
210                     }
211                 } else {
212                     resize(lastSize.height() * getRatio(), lastSize.height());
213                 }
214 
215                 updateGeometry();
216 
217                 checkBoundary(lastPosition);
218 
219                 move(lastPosition);
220 
221                 lastPoint = event->globalPos();
222                 actualSize = size();
223                 actualPos = pos();
224             }
225         } else {
226             unsetCursor();
227         }
228     }
229 }
230 
mouseReleaseEvent(QMouseEvent * event)231 void MovableWidget::mouseReleaseEvent(QMouseEvent* event)
232 {
233     if (!(event->buttons() & Qt::LeftButton))
234         mode = 0;
235 }
236 
mouseDoubleClickEvent(QMouseEvent * event)237 void MovableWidget::mouseDoubleClickEvent(QMouseEvent* event)
238 {
239     if (!(event->buttons() & Qt::LeftButton))
240         return;
241 
242     if (!graphicsEffect()) {
243         QGraphicsOpacityEffect* opacityEffect = new QGraphicsOpacityEffect(this);
244         opacityEffect->setOpacity(0.5);
245         setGraphicsEffect(opacityEffect);
246     } else {
247         setGraphicsEffect(nullptr);
248     }
249 }
250 
checkBoundary(QPoint & point) const251 void MovableWidget::checkBoundary(QPoint& point) const
252 {
253     int x1, y1, x2, y2;
254     boundaryRect.getCoords(&x1, &y1, &x2, &y2);
255 
256     if (point.x() < boundaryRect.left())
257         point.setX(boundaryRect.left());
258 
259     if (point.y() < boundaryRect.top())
260         point.setY(boundaryRect.top());
261 
262     if (point.x() + width() > boundaryRect.right() + 1)
263         point.setX(boundaryRect.right() - width() + 1);
264 
265     if (point.y() + height() > boundaryRect.bottom() + 1)
266         point.setY(boundaryRect.bottom() - height() + 1);
267 }
268