pineapple-pictures/mainwindow.cpp

398 lines
13 KiB
C++
Raw Normal View History

2019-09-28 01:18:08 +08:00
#include "mainwindow.h"
2019-10-06 14:12:52 +08:00
#include "toolbutton.h"
2019-09-29 01:40:19 +08:00
#include "bottombuttongroup.h"
2019-09-28 01:18:08 +08:00
#include "graphicsview.h"
2019-10-03 17:57:14 +08:00
#include "navigatorview.h"
2019-09-29 15:52:35 +08:00
#include "graphicsscene.h"
2019-09-28 01:18:08 +08:00
#include <QMouseEvent>
#include <QMovie>
#include <QDebug>
#include <QGraphicsTextItem>
2019-09-29 01:40:19 +08:00
#include <QApplication>
2019-09-29 15:52:35 +08:00
#include <QStyle>
#include <QScreen>
2019-10-05 13:21:49 +08:00
#include <QMenu>
2019-09-29 01:40:19 +08:00
#ifdef _WIN32
#include <windows.h>
#endif // _WIN32
2019-09-28 01:18:08 +08:00
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
2019-10-02 22:16:20 +08:00
this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
2019-09-28 01:18:08 +08:00
this->setAttribute(Qt::WA_TranslucentBackground, true);
2019-09-29 22:18:38 +08:00
this->setMinimumSize(710, 530);
2019-10-01 11:44:35 +08:00
this->setWindowIcon(QIcon(":/icons/app-icon.svg"));
2019-09-28 01:18:08 +08:00
m_fadeOutAnimation = new QPropertyAnimation(this, "windowOpacity");
m_fadeOutAnimation->setDuration(300);
m_fadeOutAnimation->setStartValue(1);
m_fadeOutAnimation->setEndValue(0);
m_floatUpAnimation = new QPropertyAnimation(this, "geometry");
m_floatUpAnimation->setDuration(300);
m_floatUpAnimation->setEasingCurve(QEasingCurve::OutCirc);
m_exitAnimationGroup = new QParallelAnimationGroup;
m_exitAnimationGroup->addAnimation(m_fadeOutAnimation);
m_exitAnimationGroup->addAnimation(m_floatUpAnimation);
connect(m_exitAnimationGroup, &QParallelAnimationGroup::finished,
this, &QMainWindow::close);
2019-09-29 15:52:35 +08:00
GraphicsScene * scene = new GraphicsScene(this);
2019-09-28 01:18:08 +08:00
m_graphicsView = new GraphicsView(this);
m_graphicsView->setScene(scene);
this->setCentralWidget(m_graphicsView);
2019-09-28 01:18:08 +08:00
2019-10-03 17:57:14 +08:00
m_gv = new NavigatorView(this);
2019-10-04 21:34:20 +08:00
m_gv->setFixedSize(220, 160);
2019-10-03 17:57:14 +08:00
m_gv->setScene(scene);
2019-10-04 09:54:13 +08:00
m_gv->setMainView(m_graphicsView);
2019-10-03 17:57:14 +08:00
m_gv->fitInView(m_gv->sceneRect(), Qt::KeepAspectRatio);
connect(m_graphicsView, &GraphicsView::navigatorViewRequired,
this, [ = ](bool required, qreal angle){
m_gv->resetTransform();
m_gv->rotate(angle);
m_gv->fitInView(m_gv->sceneRect(), Qt::KeepAspectRatio);
m_gv->setVisible(required);
m_gv->updateMainViewportRegion();
2019-10-03 17:57:14 +08:00
});
2019-10-04 09:54:13 +08:00
connect(m_graphicsView, &GraphicsView::viewportRectChanged,
m_gv, &NavigatorView::updateMainViewportRegion);
2019-10-06 14:12:52 +08:00
m_closeButton = new ToolButton(m_graphicsView);
m_closeButton->setIcon(QIcon(":/icons/window-close"));
m_closeButton->setIconSize(QSize(50, 50));
2019-09-28 01:18:08 +08:00
connect(m_closeButton, &QAbstractButton::clicked,
this, &MainWindow::closeWindow);
2019-09-29 01:40:19 +08:00
m_bottomButtonGroup = new BottomButtonGroup(this);
2019-09-29 15:52:35 +08:00
2019-09-30 23:02:44 +08:00
connect(m_bottomButtonGroup, &BottomButtonGroup::resetToOriginalBtnClicked,
2019-10-02 16:04:50 +08:00
this, [ = ](){ m_graphicsView->resetScale(); });
2019-10-04 21:34:20 +08:00
connect(m_bottomButtonGroup, &BottomButtonGroup::toggleWindowMaximum,
this, [ = ](){
if (isMaximized()) {
showNormal();
} else {
showMaximized();
}
});
2019-09-30 23:02:44 +08:00
connect(m_bottomButtonGroup, &BottomButtonGroup::zoomInBtnClicked,
2019-10-02 16:04:50 +08:00
this, [ = ](){ m_graphicsView->zoomView(1.25); });
2019-09-30 23:02:44 +08:00
connect(m_bottomButtonGroup, &BottomButtonGroup::zoomOutBtnClicked,
2019-10-02 16:04:50 +08:00
this, [ = ](){ m_graphicsView->zoomView(0.75); });
2019-09-30 23:02:44 +08:00
connect(m_bottomButtonGroup, &BottomButtonGroup::toggleCheckerboardBtnClicked,
this, [ = ](){ m_graphicsView->toggleCheckerboard(); });
2019-10-02 14:31:24 +08:00
connect(m_bottomButtonGroup, &BottomButtonGroup::rotateRightBtnClicked,
this, [ = ](){
2019-10-02 16:04:50 +08:00
m_graphicsView->resetScale();
m_graphicsView->rotateView(90);
2019-10-02 14:31:24 +08:00
m_graphicsView->checkAndDoFitInView();
2019-10-03 17:57:14 +08:00
m_gv->setVisible(false);
2019-10-02 14:31:24 +08:00
});
2019-09-30 23:02:44 +08:00
2019-10-06 14:58:01 +08:00
m_bottomButtonGroup->setOpacity(0, false);
m_gv->setOpacity(0, false);
m_closeButton->setOpacity(0, false);
2019-10-03 11:00:28 +08:00
2019-10-01 11:44:35 +08:00
centerWindow();
2019-09-28 01:18:08 +08:00
}
MainWindow::~MainWindow()
{
}
void MainWindow::showUrls(const QList<QUrl> &urls)
{
m_graphicsView->showFromUrlList(urls);
2019-10-03 17:57:14 +08:00
m_gv->fitInView(m_gv->sceneRect(), Qt::KeepAspectRatio);
}
2019-10-01 11:44:35 +08:00
void MainWindow::adjustWindowSizeBySceneRect()
{
QSize sceneSize = m_graphicsView->sceneRect().toRect().size();
QSize sceneSizeWithMargins = sceneSize + QSize(130, 125);
if (m_graphicsView->scaleFactor() < 1 || size().expandedTo(sceneSizeWithMargins) != size()) {
2019-10-01 11:44:35 +08:00
// if it scaled down by the resize policy:
QSize screenSize = qApp->screenAt(QCursor::pos())->availableSize();
if (screenSize.expandedTo(sceneSize) == screenSize) {
// we can show the picture by increase the window size.
if (screenSize.expandedTo(sceneSizeWithMargins) == screenSize) {
this->resize(sceneSizeWithMargins);
} else {
this->resize(screenSize);
}
2019-10-05 12:55:09 +08:00
// We're sure the window can display the whole thing with 1:1 scale.
// The old window size may cause fitInView call from resize() and the
// above resize() call won't reset the scale back to 1:1, so we
// just call resetScale() here to ensure the thing is no longer scaled.
m_graphicsView->resetScale();
2019-10-01 11:44:35 +08:00
centerWindow();
} else {
// toggle maximum
showMaximized();
}
}
}
2019-09-29 01:40:19 +08:00
void MainWindow::showEvent(QShowEvent *event)
{
updateWidgetsPosition();
return QMainWindow::showEvent(event);
}
2019-10-03 11:00:28 +08:00
void MainWindow::enterEvent(QEvent *event)
{
2019-10-06 14:58:01 +08:00
m_bottomButtonGroup->setOpacity(1);
m_gv->setOpacity(1);
2019-10-03 11:00:28 +08:00
2019-10-06 14:58:01 +08:00
m_closeButton->setOpacity(1);
2019-10-06 14:12:52 +08:00
2019-10-03 11:00:28 +08:00
return QMainWindow::enterEvent(event);
}
void MainWindow::leaveEvent(QEvent *event)
{
2019-10-06 14:58:01 +08:00
m_bottomButtonGroup->setOpacity(0);
m_gv->setOpacity(0);
2019-10-03 11:00:28 +08:00
2019-10-06 14:58:01 +08:00
m_closeButton->setOpacity(0);
2019-10-06 14:12:52 +08:00
2019-10-03 11:00:28 +08:00
return QMainWindow::leaveEvent(event);
}
2019-09-28 01:18:08 +08:00
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton) {
m_clickedOnWindow = true;
m_oldMousePos = event->pos();
2019-10-02 14:31:24 +08:00
qDebug() << m_oldMousePos << m_graphicsView->transform().m11()
2019-10-02 16:04:50 +08:00
<< m_graphicsView->transform().m22() << m_graphicsView->matrix().m12();
2019-09-28 01:18:08 +08:00
event->accept();
}
return QMainWindow::mousePressEvent(event);
}
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton && m_clickedOnWindow) {
move(event->globalPos() - m_oldMousePos);
event->accept();
}
return QMainWindow::mouseMoveEvent(event);
}
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
m_clickedOnWindow = false;
return QMainWindow::mouseReleaseEvent(event);
}
void MainWindow::mouseDoubleClickEvent(QMouseEvent *event)
{
2019-10-05 12:55:09 +08:00
if (!m_protectedMode) {
closeWindow();
}
2019-09-28 01:18:08 +08:00
return QMainWindow::mouseDoubleClickEvent(event);
}
2019-10-04 09:54:13 +08:00
void MainWindow::wheelEvent(QWheelEvent *event)
{
if (event->delta() > 0) {
m_graphicsView->zoomView(1.25);
} else {
m_graphicsView->zoomView(0.8);
}
}
2019-09-28 01:18:08 +08:00
void MainWindow::resizeEvent(QResizeEvent *event)
{
2019-09-29 01:40:19 +08:00
updateWidgetsPosition();
2019-09-28 01:18:08 +08:00
return QMainWindow::resizeEvent(event);
}
2019-10-05 13:21:49 +08:00
void MainWindow::contextMenuEvent(QContextMenuEvent *event)
{
QMenu * menu = new QMenu;
2019-10-06 12:57:38 +08:00
QAction * stayOnTopMode = new QAction(tr("Stay on top"));
connect(stayOnTopMode, &QAction::triggered, this, [ = ](){
toggleStayOnTop();
});
stayOnTopMode->setCheckable(true);
stayOnTopMode->setChecked(stayOnTop());
2019-10-05 13:21:49 +08:00
QAction * protectedMode = new QAction(tr("Protected mode"));
connect(protectedMode, &QAction::triggered, this, [ = ](){
toggleProtectedMode();
});
protectedMode->setCheckable(true);
protectedMode->setChecked(m_protectedMode);
QAction * helpAction = new QAction(tr("Help"));
connect(helpAction, &QAction::triggered, this, [ = ](){
QStringList sl {
tr("Launch application with image file path as argument to load the file."),
tr("Drag and drop image file onto the window is also supported."),
"",
tr("Context menu option explanation:"),
(tr("Stay on top") + " : " + tr("Make window stay on top of all other windows.")),
(tr("Protected mode") + " : " + tr("Avoid close window accidentally. (eg. by double clicking the window)"))
};
m_graphicsView->showText(sl.join('\n'));
});
2019-10-06 12:57:38 +08:00
menu->addAction(stayOnTopMode);
2019-10-05 13:21:49 +08:00
menu->addAction(protectedMode);
menu->addSeparator();
menu->addAction(helpAction);
2019-10-05 13:21:49 +08:00
menu->exec(mapToGlobal(event->pos()));
menu->deleteLater();
return QMainWindow::contextMenuEvent(event);
}
2019-09-29 01:40:19 +08:00
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
#ifdef _WIN32
2019-10-03 17:57:14 +08:00
// https://stackoverflow.com/questions/43505580/qt-windows-resizable-frameless-window
2019-09-29 01:40:19 +08:00
// Too lazy to do this now.. just stackoverflow it and did a copy and paste..
2019-10-06 14:58:01 +08:00
Q_UNUSED(eventType)
2019-09-29 01:40:19 +08:00
MSG* msg = static_cast<MSG*>(message);
if (msg->message == WM_NCHITTEST) {
if (isMaximized()) {
return false;
}
*result = 0;
const LONG borderWidth = 8;
RECT winrect;
GetWindowRect(reinterpret_cast<HWND>(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
2019-09-29 09:31:18 +08:00
return QMainWindow::nativeEvent(eventType, message, result);
2019-09-29 01:40:19 +08:00
#endif // _WIN32
}
2019-10-01 11:44:35 +08:00
void MainWindow::centerWindow()
{
this->setGeometry(
QStyle::alignedRect(
Qt::LeftToRight,
Qt::AlignCenter,
this->size(),
qApp->screenAt(QCursor::pos())->geometry()
)
);
}
2019-09-28 01:18:08 +08:00
void MainWindow::closeWindow()
{
QRect windowRect(this->geometry());
m_floatUpAnimation->setStartValue(windowRect);
m_floatUpAnimation->setEndValue(windowRect.adjusted(0, -80, 0, 0));
m_floatUpAnimation->setStartValue(QRect(this->geometry().x(), this->geometry().y(), this->geometry().width(), this->geometry().height()));
m_floatUpAnimation->setEndValue(QRect(this->geometry().x(), this->geometry().y()-80, this->geometry().width(), this->geometry().height()));
m_exitAnimationGroup->start();
}
2019-09-29 01:40:19 +08:00
void MainWindow::updateWidgetsPosition()
{
m_closeButton->move(width() - m_closeButton->width(), 0);
m_bottomButtonGroup->move((width() - m_bottomButtonGroup->width()) / 2,
height() - m_bottomButtonGroup->height());
2019-10-03 17:57:14 +08:00
m_gv->move(width() - m_gv->width(), height() - m_gv->height());
2019-09-29 01:40:19 +08:00
}
2019-10-05 13:21:49 +08:00
void MainWindow::toggleProtectedMode()
{
m_protectedMode = !m_protectedMode;
m_closeButton->setVisible(!m_protectedMode);
}
2019-10-06 12:57:38 +08:00
void MainWindow::toggleStayOnTop()
{
setWindowFlag(Qt::WindowStaysOnTopHint, !stayOnTop());
show();
2019-10-06 12:57:38 +08:00
}
bool MainWindow::stayOnTop()
{
return windowFlags().testFlag(Qt::WindowStaysOnTopHint);
}