| 
									
										
										
										
											2022-04-22 18:21:24 +01:00
										 |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							| 
									
										
										
										
											2023-11-19 13:31:45 +01:00
										 |  |  | // Copyright (C) 2022-2023 Jon Beniston, M7RCE <jon@beniston.com>                //
 | 
					
						
							|  |  |  | // Copyright (C) 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com>               //
 | 
					
						
							| 
									
										
										
										
											2022-04-22 18:21:24 +01:00
										 |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // This program is free software; you can redistribute it and/or modify          //
 | 
					
						
							|  |  |  | // it under the terms of the GNU General Public License as published by          //
 | 
					
						
							|  |  |  | // the Free Software Foundation as version 3 of the License, or                  //
 | 
					
						
							|  |  |  | // (at your option) any later version.                                           //
 | 
					
						
							|  |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful,               //
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of                //
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
 | 
					
						
							|  |  |  | // GNU General Public License V3 for more details.                               //
 | 
					
						
							|  |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU General Public License             //
 | 
					
						
							|  |  |  | // along with this program. If not, see <http://www.gnu.org/licenses/>.          //
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <QGuiApplication>
 | 
					
						
							|  |  |  | #include <QLayout>
 | 
					
						
							| 
									
										
										
										
											2023-11-13 20:51:03 +00:00
										 |  |  | #include <QTableWidget>
 | 
					
						
							| 
									
										
										
										
											2022-04-22 18:21:24 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "framelesswindowresizer.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | FramelessWindowResizer::FramelessWindowResizer(QWidget *widget) : | 
					
						
							|  |  |  |     m_widget(widget), | 
					
						
							|  |  |  |     m_vResizing(false), | 
					
						
							|  |  |  |     m_hResizing(false), | 
					
						
							|  |  |  |     m_vMove(false), | 
					
						
							|  |  |  |     m_hMove(false), | 
					
						
							|  |  |  |     m_cursor(nullptr), | 
					
						
							|  |  |  |     m_vCursor(Qt::SizeVerCursor), | 
					
						
							|  |  |  |     m_hCursor(Qt::SizeHorCursor), | 
					
						
							|  |  |  |     m_bCursor(Qt::SizeBDiagCursor), | 
					
						
							|  |  |  |     m_fCursor(Qt::SizeFDiagCursor) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-25 15:01:59 +01:00
										 |  |  | void FramelessWindowResizer::enableChildMouseTracking() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QList<QWidget *> widgets = m_widget->findChildren<QWidget *>(); | 
					
						
							|  |  |  |     for (auto widget : widgets) { | 
					
						
							|  |  |  |         widget->setMouseTracking(true); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-13 20:51:03 +00:00
										 |  |  |     // QTableWidgets don't send us mouseMoveEvents for some unknown reason
 | 
					
						
							|  |  |  |     // so install an event filter on their viewport
 | 
					
						
							|  |  |  |     QList<QTableWidget *> tables = m_widget->findChildren<QTableWidget *>(); | 
					
						
							|  |  |  |     for (auto table : tables) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         table->viewport()->setMouseTracking(true); | 
					
						
							|  |  |  |         table->viewport()->installEventFilter(this); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-25 15:01:59 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 18:21:24 +01:00
										 |  |  | bool FramelessWindowResizer::mouseOnTopBorder(QPoint pos) const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-11-09 16:15:35 +00:00
										 |  |  |     return ((pos.y() >= 0) && (pos.y() < m_gripSize) | 
					
						
							|  |  |  |          && (m_widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed)); | 
					
						
							| 
									
										
										
										
											2022-04-22 18:21:24 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool FramelessWindowResizer::mouseOnBottomBorder(QPoint pos) const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-11-09 16:15:35 +00:00
										 |  |  |     return ((pos.y() > m_widget->height() - 1 - m_gripSize) && (pos.y() < m_widget->height()) | 
					
						
							|  |  |  |          && (m_widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed)); | 
					
						
							| 
									
										
										
										
											2022-04-22 18:21:24 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool FramelessWindowResizer::mouseOnLeftBorder(QPoint pos) const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-11-09 16:15:35 +00:00
										 |  |  |     return ((pos.x() >= 0) && (pos.x() < m_gripSize) | 
					
						
							|  |  |  |          && (m_widget->sizePolicy().horizontalPolicy() != QSizePolicy::Fixed)); | 
					
						
							| 
									
										
										
										
											2022-04-22 18:21:24 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool FramelessWindowResizer::mouseOnRightBorder(QPoint pos) const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-11-09 16:15:35 +00:00
										 |  |  |     return ((pos.x() > m_widget->width() - 1 - m_gripSize) && (pos.x() < m_widget->width()) | 
					
						
							|  |  |  |          && (m_widget->sizePolicy().horizontalPolicy() != QSizePolicy::Fixed)); | 
					
						
							| 
									
										
										
										
											2022-04-22 18:21:24 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool FramelessWindowResizer::mouseOnBorder(QPoint pos) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return mouseOnTopBorder(pos) || mouseOnBottomBorder(pos) | 
					
						
							|  |  |  |         || mouseOnLeftBorder(pos) || mouseOnRightBorder(pos); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FramelessWindowResizer::setCursor(const QCursor &cursor) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_cursor != &cursor) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (m_cursor != nullptr) { | 
					
						
							|  |  |  |             QGuiApplication::restoreOverrideCursor(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         QGuiApplication::setOverrideCursor(cursor); | 
					
						
							|  |  |  |         m_cursor = &cursor; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FramelessWindowResizer::clearCursor() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_cursor) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         QGuiApplication::restoreOverrideCursor(); | 
					
						
							|  |  |  |         m_cursor = nullptr; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FramelessWindowResizer::mousePressEvent(QMouseEvent* event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if ((event->buttons() & Qt::LeftButton) && mouseOnBorder(event->pos())) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (mouseOnTopBorder(event->pos()) || mouseOnBottomBorder(event->pos())) { | 
					
						
							|  |  |  |             m_vResizing = true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (mouseOnLeftBorder(event->pos()) || mouseOnRightBorder(event->pos())) { | 
					
						
							|  |  |  |             m_hResizing = true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (mouseOnTopBorder(event->pos())) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             m_vMove = true; | 
					
						
							|  |  |  |             // Calculate difference between mouse position and top left corner of widget
 | 
					
						
							|  |  |  |             m_mouseOffsetToPos = event->globalPos() - m_widget->pos(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (mouseOnLeftBorder(event->pos())) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             m_hMove = true; | 
					
						
							|  |  |  |             m_mouseOffsetToPos = event->globalPos() - m_widget->pos(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // Save coords of bottom/right of widget
 | 
					
						
							|  |  |  |         m_origBottomRight.setX(m_widget->pos().x() + m_widget->width()); | 
					
						
							|  |  |  |         m_origBottomRight.setY(m_widget->pos().y() + m_widget->height()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         m_initialMousePos = event->globalPos(); | 
					
						
							|  |  |  |         m_initRect = m_widget->rect(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         event->accept(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FramelessWindowResizer::mouseReleaseEvent(QMouseEvent* event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!(event->buttons() & Qt::LeftButton)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         m_vResizing = false; | 
					
						
							|  |  |  |         m_hResizing = false; | 
					
						
							|  |  |  |         m_vMove = false; | 
					
						
							|  |  |  |         m_hMove = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-23 13:57:37 +02:00
										 |  |  | void FramelessWindowResizer::leaveEvent(QEvent*) | 
					
						
							| 
									
										
										
										
											2022-04-22 18:21:24 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     clearCursor(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-13 20:51:03 +00:00
										 |  |  | bool FramelessWindowResizer::eventFilter(QObject *obj, QEvent *event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (event->type() == QEvent::MouseMove) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // Mouse moving over child table widget
 | 
					
						
							|  |  |  |         clearCursor(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return QObject::eventFilter(obj, event); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 18:21:24 +01:00
										 |  |  | void FramelessWindowResizer::mouseMoveEvent(QMouseEvent* event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_vResizing || m_hResizing) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         int x, y; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Calculate resize requested
 | 
					
						
							|  |  |  |         int w = m_initRect.width(); | 
					
						
							|  |  |  |         int h = m_initRect.height(); | 
					
						
							|  |  |  |         QPoint delta = event->globalPos() - m_initialMousePos; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         x = delta.x(); | 
					
						
							|  |  |  |         y = delta.y(); | 
					
						
							|  |  |  |         if (m_hMove) { | 
					
						
							|  |  |  |             x = -x; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (m_vMove) { | 
					
						
							|  |  |  |             y = -y; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (m_hResizing) { | 
					
						
							|  |  |  |             w += x; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (m_vResizing) { | 
					
						
							|  |  |  |             h += y; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         QSize reqSize(w, h); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Get min and max size we can resize to
 | 
					
						
							|  |  |  |         QSize minSize, maxSize; | 
					
						
							| 
									
										
										
										
											2022-04-23 13:57:37 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 18:21:24 +01:00
										 |  |  |         if (m_widget->layout()) | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2022-09-16 08:53:12 +01:00
										 |  |  |             //minSize = m_widget->layout()->minimumSize();
 | 
					
						
							| 
									
										
										
										
											2022-04-22 18:21:24 +01:00
										 |  |  |             maxSize = m_widget->layout()->maximumSize(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2022-09-16 08:53:12 +01:00
										 |  |  |             //minSize = m_widget->minimumSize();
 | 
					
						
							| 
									
										
										
										
											2022-04-22 18:21:24 +01:00
										 |  |  |             maxSize = m_widget->maximumSize(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-09-16 08:53:12 +01:00
										 |  |  |         minSize = m_widget->minimumSizeHint(); // Need to use minimumSizeHint for FlowLayout to work
 | 
					
						
							| 
									
										
										
										
											2022-04-22 18:21:24 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Limit requested to size to allowed min/max
 | 
					
						
							|  |  |  |         QSize size = reqSize; | 
					
						
							|  |  |  |         size = size.expandedTo(minSize); | 
					
						
							|  |  |  |         size = size.boundedTo(maxSize); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-23 13:57:37 +02:00
										 |  |  |         // Prevent vertical expansion of vertically fixed widgets
 | 
					
						
							|  |  |  |         if (m_widget->sizePolicy().verticalPolicy() == QSizePolicy::Fixed) { | 
					
						
							| 
									
										
										
										
											2022-09-16 08:53:12 +01:00
										 |  |  |             size.setHeight(m_widget->sizeHint().height()); | 
					
						
							| 
									
										
										
										
											2022-04-23 13:57:37 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-23 18:46:08 +02:00
										 |  |  |         // Prevent horizontal expansion of horizontal fixed widgets
 | 
					
						
							|  |  |  |         if (m_widget->sizePolicy().horizontalPolicy() == QSizePolicy::Fixed) { | 
					
						
							| 
									
										
										
										
											2022-09-16 08:53:12 +01:00
										 |  |  |             size.setWidth(m_widget->sizeHint().width()); | 
					
						
							| 
									
										
										
										
											2022-04-23 18:46:08 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 18:21:24 +01:00
										 |  |  |         // Move
 | 
					
						
							|  |  |  |         if (m_vMove || m_hMove) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (m_hMove) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 x = event->globalPos().x() - m_mouseOffsetToPos.x(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (x + minSize.width() > m_origBottomRight.x()) { | 
					
						
							|  |  |  |                     x = m_origBottomRight.x() - minSize.width(); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 x = m_widget->pos().x(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (m_vMove) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 y = event->globalPos().y() - m_mouseOffsetToPos.y(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (y + minSize.height() > m_origBottomRight.y()) { | 
					
						
							|  |  |  |                     y = m_origBottomRight.y() - minSize.height(); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 y = m_widget->pos().y(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             m_widget->move(x, y); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         m_widget->resize(size); | 
					
						
							|  |  |  |         event->accept(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         QPoint pos = event->pos(); | 
					
						
							|  |  |  |         if (mouseOnBorder(pos)) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // Set cursor according to edge or corner mouse is over
 | 
					
						
							|  |  |  |             if (mouseOnTopBorder(pos) && mouseOnRightBorder(pos)) { | 
					
						
							|  |  |  |                 setCursor(m_bCursor); | 
					
						
							|  |  |  |             } else if (mouseOnTopBorder(pos) && mouseOnLeftBorder(pos)) { | 
					
						
							|  |  |  |                 setCursor(m_fCursor); | 
					
						
							|  |  |  |             } else if (mouseOnBottomBorder(pos) && mouseOnRightBorder(pos)) { | 
					
						
							|  |  |  |                 setCursor(m_fCursor); | 
					
						
							|  |  |  |             } else if (mouseOnBottomBorder(pos) && mouseOnLeftBorder(pos)) { | 
					
						
							|  |  |  |                 setCursor(m_bCursor); | 
					
						
							|  |  |  |             } else if (mouseOnTopBorder(pos) || mouseOnBottomBorder(pos)) { | 
					
						
							|  |  |  |                 setCursor(m_vCursor); | 
					
						
							|  |  |  |             } else if (mouseOnLeftBorder(pos) || mouseOnRightBorder(pos)) { | 
					
						
							|  |  |  |                 setCursor(m_hCursor); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             clearCursor(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |