diff --git a/CMakeLists.txt b/CMakeLists.txt index c776517..5336c08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ endif () #LibExiv2_FOUND set (PPIC_CPP_FILES app/main.cpp + app/framelesshandler.cpp app/framelesswindow.cpp app/mainwindow.cpp app/graphicsview.cpp @@ -45,6 +46,7 @@ set (PPIC_CPP_FILES ) set (PPIC_HEADER_FILES + app/framelesshandler.h app/framelesswindow.h app/mainwindow.h app/graphicsview.h diff --git a/app/framelesshandler.cpp b/app/framelesshandler.cpp new file mode 100644 index 0000000..e71c425 --- /dev/null +++ b/app/framelesshandler.cpp @@ -0,0 +1,149 @@ +#include "framelesshandler.h" + +#include +#include +#include +#include +#include + +FramelessHandler::FramelessHandler(QWidget *parent) + : QObject(parent) + , m_parentWidget(parent) +{ + m_parentWidget->setMouseTracking(true); + m_parentWidget->installEventFilter(this); +} + +bool FramelessHandler::eventFilter(QObject *o, QEvent *evt) +{ + if (evt->type() != QEvent::MouseButtonPress && + evt->type() != QEvent::MouseButtonRelease && + evt->type() != QEvent::MouseMove) { + + return false; + } + + Q_ASSERT(o == m_parentWidget); + + if (QApplication::activePopupWidget()) { + return false; + } + + switch (evt->type()) { + case QEvent::MouseButtonPress: { + if (m_parentWidget->isMaximized()) break; + QMouseEvent *e = static_cast(evt); + const QRect widgetRect = m_parentWidget->rect(); + const QPoint cursorPoint = m_parentWidget->mapFromGlobal(e->globalPos()); + if (!widgetRect.contains(cursorPoint)) return false; + if (e->button() & Qt::LeftButton) { + m_oldMousePos = e->pos(); + m_clickedOnWindow = true; + mouseMoveEvent(e); + return true; + } + } break; + case QEvent::MouseButtonRelease: { + if (m_parentWidget->isMaximized()) break; + QMouseEvent *e = static_cast(evt); + if (e->button() & Qt::LeftButton) { + qDebug() << "released"; + m_clickedOnWindow = false; + return true; + } + } break; + case QEvent::MouseMove: { + if (m_parentWidget->isMaximized()) break; + QMouseEvent *e = static_cast(evt); + mouseMoveEvent(e); + return true; + } break; + default: + break; + } + + return false; +} + +void FramelessHandler::mouseMoveEvent(QMouseEvent * evt) +{ + QPoint pos = m_parentWidget->mapFromGlobal(evt->globalPos()); + Qt::Edges mode = Qt::Edges(); + if (pos.x() <= m_borderWidth) { + mode.setFlag(Qt::LeftEdge); + } else if (pos.x() >= m_parentWidget->width() - m_borderWidth) { + mode.setFlag(Qt::RightEdge); + } + + if (pos.y() <= m_borderWidth) { + mode.setFlag(Qt::TopEdge); + } else if (pos.y() >= m_parentWidget->height() - m_borderWidth) { + mode.setFlag(Qt::BottomEdge); + } + + setMouseCursor(mode); + + if ((evt->button() & Qt::LeftButton) == Qt::NoButton && !m_clickedOnWindow) { + return; + } + +qDebug() << evt->button() << m_clickedOnWindow; + + if (mode) { + startSystemResize(mode); + } else { + if (!startSystemMove()) { + m_parentWidget->move(evt->globalPos() - m_oldMousePos); + } + } + + return; +} + +void FramelessHandler::setMouseCursor(Qt::Edges edge) +{ + switch (edge) { + case Qt::TopEdge: + case Qt::BottomEdge: + m_parentWidget->setCursor(Qt::SizeVerCursor); + m_parentWidget->window()->setCursor(Qt::SizeVerCursor); + break; + case Qt::LeftEdge: + case Qt::RightEdge: + m_parentWidget->setCursor(Qt::SizeHorCursor); + m_parentWidget->window()->setCursor(Qt::SizeHorCursor); + break; + case Qt::TopEdge | Qt::LeftEdge: + case Qt::BottomEdge | Qt::RightEdge: + m_parentWidget->setCursor(Qt::SizeFDiagCursor); + m_parentWidget->window()->setCursor(Qt::SizeFDiagCursor); + break; + case Qt::BottomEdge | Qt::LeftEdge: + case Qt::TopEdge | Qt::RightEdge: + m_parentWidget->setCursor(Qt::SizeBDiagCursor); + m_parentWidget->window()->setCursor(Qt::SizeBDiagCursor); + break; + default: + m_parentWidget->setCursor(Qt::ArrowCursor); + m_parentWidget->window()->setCursor(Qt::ArrowCursor); + break; + } + + // qDebug() << "setMouseCursor" << edge; +} + +bool FramelessHandler::startSystemResize(Qt::Edges edges) +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + return m_parentWidget->window()->windowHandle()->startSystemResize(edges); +#endif // QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + return false; +} + +bool FramelessHandler::startSystemMove() +{ +#if not QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + return m_parentWidget->window()->windowHandle()->startSystemMove(); +#endif // QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + return false; +} diff --git a/app/framelesshandler.h b/app/framelesshandler.h new file mode 100644 index 0000000..8a2ab32 --- /dev/null +++ b/app/framelesshandler.h @@ -0,0 +1,36 @@ +#ifndef FRAMELESSHANDLER_H +#define FRAMELESSHANDLER_H + +#include +#include + +// This concept takes from Qt's QWidgetResizeHandler, but it's not +// a public Qt API so we cannot relay on it... + +QT_BEGIN_NAMESPACE +class QMouseEvent; +QT_END_NAMESPACE + +class FramelessHandler : public QObject +{ + Q_OBJECT +public: + explicit FramelessHandler(QWidget *parent = nullptr); + +protected: + bool eventFilter(QObject *o, QEvent *event) override; + + void mouseMoveEvent(QMouseEvent *evt); + void setMouseCursor(Qt::Edges edge); + + bool startSystemResize(Qt::Edges edges); + bool startSystemMove(); + +private: + QWidget * m_parentWidget = nullptr; + QPoint m_oldMousePos; + bool m_clickedOnWindow = false; + static constexpr int m_borderWidth = 8; +}; + +#endif // FRAMELESSHANDLER_H diff --git a/app/framelesswindow.cpp b/app/framelesswindow.cpp index d2fdee9..f58dede 100644 --- a/app/framelesswindow.cpp +++ b/app/framelesswindow.cpp @@ -1,10 +1,12 @@ #include "framelesswindow.h" +#include "framelesshandler.h" #include FramelessWindow::FramelessWindow(QWidget *parent) : QWidget(parent) , m_centralLayout(new QVBoxLayout(this)) + , m_framelessHandler(new FramelessHandler(this)) { this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint); diff --git a/app/framelesswindow.h b/app/framelesswindow.h index 28ab8cc..c1d3080 100644 --- a/app/framelesswindow.h +++ b/app/framelesswindow.h @@ -7,6 +7,7 @@ QT_BEGIN_NAMESPACE class QVBoxLayout; QT_END_NAMESPACE +class FramelessHandler; class FramelessWindow : public QWidget { Q_OBJECT @@ -20,6 +21,7 @@ signals: private: QVBoxLayout * m_centralLayout = nullptr; QWidget * m_centralWidget = nullptr; // just a pointer, doesn't take the ownership. + FramelessHandler * m_framelessHandler = nullptr; }; #endif // FRAMELESSWINDOW_H diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp index ef87b3c..05e2de8 100644 --- a/app/mainwindow.cpp +++ b/app/mainwindow.cpp @@ -315,42 +315,6 @@ void MainWindow::leaveEvent(QEvent *event) return FramelessWindow::leaveEvent(event); } -void MainWindow::mousePressEvent(QMouseEvent *event) -{ - if (event->buttons() & Qt::LeftButton && !isMaximized()) { - m_clickedOnWindow = true; - m_oldMousePos = event->pos(); -// qDebug() << m_oldMousePos << m_graphicsView->transform().m11() -// << m_graphicsView->transform().m22() << m_graphicsView->matrix().m12(); - event->accept(); - } - - return FramelessWindow::mousePressEvent(event); -} - -void MainWindow::mouseMoveEvent(QMouseEvent *event) -{ - if (event->buttons() & Qt::LeftButton && m_clickedOnWindow && !isMaximized()) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) - if (!window()->windowHandle()->startSystemMove()) { - move(event->globalPos() - m_oldMousePos); - } -#else - move(event->globalPos() - m_oldMousePos); -#endif // QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) - event->accept(); - } - - return FramelessWindow::mouseMoveEvent(event); -} - -void MainWindow::mouseReleaseEvent(QMouseEvent *event) -{ - m_clickedOnWindow = false; - - return FramelessWindow::mouseReleaseEvent(event); -} - void MainWindow::mouseDoubleClickEvent(QMouseEvent *event) { switch (Settings::instance()->doubleClickBehavior()) { @@ -515,93 +479,6 @@ void MainWindow::contextMenuEvent(QContextMenuEvent *event) return FramelessWindow::contextMenuEvent(event); } -bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) -{ -#ifdef _WIN32 - // https://stackoverflow.com/questions/43505580/qt-windows-resizable-frameless-window - // Too lazy to do this now.. just stackoverflow it and did a copy and paste.. - Q_UNUSED(eventType) - MSG* msg = static_cast(message); - - if (msg->message == WM_NCHITTEST) { - if (isMaximized()) { - return false; - } - - *result = 0; - const LONG borderWidth = 8; - RECT winrect; - GetWindowRect(reinterpret_cast(winId()), &winrect); - - // must be short to correctly work with multiple monitors (negative coordinates) - short x = msg->lParam & 0x0000FFFF; - short y = (msg->lParam & 0xFFFF0000) >> 16; - - bool resizeWidth = minimumWidth() != maximumWidth(); - bool resizeHeight = minimumHeight() != maximumHeight(); - if (resizeWidth) { - //left border - if (x >= winrect.left && x < winrect.left + borderWidth) { - *result = HTLEFT; - } - //right border - if (x < winrect.right && x >= winrect.right - borderWidth) { - *result = HTRIGHT; - } - } - if (resizeHeight) { - //bottom border - if (y < winrect.bottom && y >= winrect.bottom - borderWidth) { - *result = HTBOTTOM; - } - //top border - if (y >= winrect.top && y < winrect.top + borderWidth) { - *result = HTTOP; - } - } - if (resizeWidth && resizeHeight) { - //bottom left corner - if (x >= winrect.left && x < winrect.left + borderWidth && - y < winrect.bottom && y >= winrect.bottom - borderWidth) - { - *result = HTBOTTOMLEFT; - } - //bottom right corner - if (x < winrect.right && x >= winrect.right - borderWidth && - y < winrect.bottom && y >= winrect.bottom - borderWidth) - { - *result = HTBOTTOMRIGHT; - } - //top left corner - if (x >= winrect.left && x < winrect.left + borderWidth && - y >= winrect.top && y < winrect.top + borderWidth) - { - *result = HTTOPLEFT; - } - //top right corner - if (x < winrect.right && x >= winrect.right - borderWidth && - y >= winrect.top && y < winrect.top + borderWidth) - { - *result = HTTOPRIGHT; - } - } - - if (*result != 0) - return true; - - QWidget *action = QApplication::widgetAt(QCursor::pos()); - if (action == this) { - *result = HTCAPTION; - return true; - } - } - - return false; -#else - return FramelessWindow::nativeEvent(eventType, message, result); -#endif // _WIN32 -} - QSize MainWindow::sizeHint() const { return QSize(710, 530); diff --git a/app/mainwindow.h b/app/mainwindow.h index a62c587..93739a5 100644 --- a/app/mainwindow.h +++ b/app/mainwindow.h @@ -41,16 +41,11 @@ protected slots: void showEvent(QShowEvent *event) override; void enterEvent(QEvent *event) override; void leaveEvent(QEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; void mouseDoubleClickEvent(QMouseEvent *event) override; void wheelEvent(QWheelEvent *event) override; void resizeEvent(QResizeEvent *event) override; void contextMenuEvent(QContextMenuEvent *event) override; - bool nativeEvent(const QByteArray& eventType, void* message, long* result) override; - QSize sizeHint() const override; void centerWindow(); @@ -64,7 +59,6 @@ protected slots: void toggleMaximize(); private: - QPoint m_oldMousePos; QPropertyAnimation *m_fadeOutAnimation; QPropertyAnimation *m_floatUpAnimation; QParallelAnimationGroup *m_exitAnimationGroup; @@ -75,7 +69,6 @@ private: NavigatorView *m_gv; BottomButtonGroup *m_bottomButtonGroup; bool m_protectedMode = false; - bool m_clickedOnWindow = false; QList m_files; int m_currentFileIndex = -1; diff --git a/translations/PineapplePictures.ts b/translations/PineapplePictures.ts index e30befb..34ae1cb 100644 --- a/translations/PineapplePictures.ts +++ b/translations/PineapplePictures.ts @@ -200,59 +200,59 @@ MainWindow - + File url list is empty - + &Copy - + Copy P&ixmap - + Copy &File Path - + &Paste Image - + &Paste Image File - + Properties - + Stay on top - + Protected mode - + Configure... - + Help diff --git a/translations/PineapplePictures_de.ts b/translations/PineapplePictures_de.ts index db92eb5..4501df4 100644 --- a/translations/PineapplePictures_de.ts +++ b/translations/PineapplePictures_de.ts @@ -204,59 +204,59 @@ MainWindow - + File url list is empty Die Datei-URL-Liste ist leer - + &Copy &Kopieren - + Copy P&ixmap P&ixmap kopieren - + Copy &File Path &Dateipfad kopieren - + &Paste Image Bild &einfügen - + &Paste Image File Bilddatei &einfügen - + Properties Eigenschaften - + Stay on top Oben bleiben - + Protected mode Geschützter Modus - + Configure... Konfigurieren … - + Help Hilfe diff --git a/translations/PineapplePictures_fr.ts b/translations/PineapplePictures_fr.ts index 9d03612..200d381 100644 --- a/translations/PineapplePictures_fr.ts +++ b/translations/PineapplePictures_fr.ts @@ -204,59 +204,59 @@ MainWindow - + File url list is empty La liste des URL de fichiers est vide - + &Copy &Copier - + Copy P&ixmap Copier P&ixmap - + Copy &File Path Copier le &chemin du fichier - + &Paste Image &Coller l'image - + &Paste Image File &Coller le fichier d'image - + Properties Propriétés - + Stay on top Rester en-haut - + Protected mode Mode protégé - + Configure... Configurer… - + Help Aide diff --git a/translations/PineapplePictures_nb_NO.ts b/translations/PineapplePictures_nb_NO.ts index 9bc0d8f..ad571c2 100644 --- a/translations/PineapplePictures_nb_NO.ts +++ b/translations/PineapplePictures_nb_NO.ts @@ -204,59 +204,59 @@ MainWindow - + File url list is empty Listen over filnettadresser er ugyldig - + &Copy &Kopier - + Copy P&ixmap - + Copy &File Path Kopier %filsti - + &Paste Image &Lim inn bilde - + &Paste Image File &Lim inn bildefil - + Properties Egenskaper - + Stay on top Behold øverst - + Protected mode Beskyttet modus - + Configure... Sett opp … - + Help Hjelp diff --git a/translations/PineapplePictures_ru.ts b/translations/PineapplePictures_ru.ts index d9ee76f..a4c1c32 100644 --- a/translations/PineapplePictures_ru.ts +++ b/translations/PineapplePictures_ru.ts @@ -200,59 +200,59 @@ MainWindow - + File url list is empty - + &Copy &Копировать - + Copy P&ixmap - + Copy &File Path Копировать &Путь к файлу - + &Paste Image &Вставить изображение - + &Paste Image File &Вставить файл изображения - + Properties Свойства - + Stay on top - + Protected mode Защищенный режим - + Configure... - + Help Помощь diff --git a/translations/PineapplePictures_zh_CN.ts b/translations/PineapplePictures_zh_CN.ts index 8bbfc11..a6dc8b7 100644 --- a/translations/PineapplePictures_zh_CN.ts +++ b/translations/PineapplePictures_zh_CN.ts @@ -204,59 +204,59 @@ MainWindow - + File url list is empty 文件 URL 列表为空 - + &Copy 复制(&C) - + Copy P&ixmap 复制位图(&I) - + Copy &File Path 复制文件路径(&F) - + &Paste Image 粘贴图像(&P) - + &Paste Image File 粘贴图像文件(&P) - + Properties 属性 - + Stay on top 总在最前 - + Protected mode 保护模式 - + Configure... 设置... - + Help 帮助