diff --git a/CHANGELOG.md b/CHANGELOG.md index 919e2c75..7c99c4d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Version counting is based on semantic versioning (Major.Feature.Patch) ### YACReader * Don't use scroll animations on macos by default, it where hdpi scroll is most likely to be used. * New toolbar on macos. +* New mouse modes to turn pages. You can setup the app to use the left/right buttons to turn pages directly or you can click on the left/right part of the screen to turn pages. ### YACReaderLibrary * Improve flexibility of the open comic in third party app setting so more complex commands can be used. e.g. `open -a "/Applications/My Reader.app" "{comic_file_path}"`. diff --git a/YACReader/YACReader.pro b/YACReader/YACReader.pro index b57683da..d50940f8 100644 --- a/YACReader/YACReader.pro +++ b/YACReader/YACReader.pro @@ -80,6 +80,7 @@ HEADERS += ../common/comic.h \ goto_dialog.h \ magnifying_glass.h \ main_window_viewer.h \ + mouse_handler.h \ viewer.h \ goto_flow.h \ options_dialog.h \ @@ -119,6 +120,7 @@ SOURCES += ../common/comic.cpp \ goto_dialog.cpp \ magnifying_glass.cpp \ main_window_viewer.cpp \ + mouse_handler.cpp \ viewer.cpp \ goto_flow.cpp \ options_dialog.cpp \ diff --git a/YACReader/configuration.h b/YACReader/configuration.h index fe29803e..f4afdbad 100644 --- a/YACReader/configuration.h +++ b/YACReader/configuration.h @@ -1,5 +1,6 @@ #ifndef __CONFIGURATION_H #define __CONFIGURATION_H + #include #include #include @@ -15,6 +16,21 @@ using namespace YACReader; +namespace YACReader { + +enum FitMode { + ToWidth = 0x01, + ToHeight = 0x02, + FullRes = 0x03, + FullPage = 0x04 +}; + +enum MouseMode { + Normal, + LeftRightNavigation, + HotAreas +}; + class Configuration : public QObject { Q_OBJECT @@ -94,6 +110,11 @@ public: return settings->value(DISABLE_SCROLL_ANIMATION, defaultValue).toBool(); } + + MouseMode getMouseMode() { return static_cast(settings->value(MOUSE_MODE, MouseMode::Normal).toInt()); } + void setMouseMode(MouseMode mouseMode) { settings->setValue(MOUSE_MODE, static_cast(mouseMode)); } }; +} + #endif diff --git a/YACReader/main_window_viewer.cpp b/YACReader/main_window_viewer.cpp index fdbd31c5..9d33de47 100644 --- a/YACReader/main_window_viewer.cpp +++ b/YACReader/main_window_viewer.cpp @@ -625,7 +625,7 @@ void MainWindowViewer::createToolBars() viewer->addAction(closeAction); - viewer->setContextMenuPolicy(Qt::ActionsContextMenu); + updateContextMenuPolicy(); // MacOSX app menus #ifdef Q_OS_MACOS @@ -777,9 +777,25 @@ void MainWindowViewer::openComicFromRecentAction(QAction *action) void MainWindowViewer::reloadOptions() { + updateContextMenuPolicy(); viewer->updateConfig(settings); } +void MainWindowViewer::updateContextMenuPolicy() +{ + auto mouseMode = Configuration::getConfiguration().getMouseMode(); + switch (mouseMode) { + + case Normal: + case HotAreas: + viewer->setContextMenuPolicy(Qt::ActionsContextMenu); + break; + case LeftRightNavigation: + viewer->setContextMenuPolicy(Qt::NoContextMenu); + break; + } +} + void MainWindowViewer::open() { QFileDialog openDialog; @@ -970,9 +986,11 @@ void MainWindowViewer::disablePreviousNextComicActions() void MainWindowViewer::mouseDoubleClickEvent(QMouseEvent *event) { - if (event->button() == Qt::LeftButton) { - toggleFullScreen(); - event->accept(); + if (Configuration::getConfiguration().getMouseMode() == MouseMode::Normal) { + if (event->button() == Qt::LeftButton) { + toggleFullScreen(); + event->accept(); + } } } diff --git a/YACReader/main_window_viewer.h b/YACReader/main_window_viewer.h index 17011c36..b2d08663 100644 --- a/YACReader/main_window_viewer.h +++ b/YACReader/main_window_viewer.h @@ -67,6 +67,7 @@ public slots: void increasePageZoomLevel(); void decreasePageZoomLevel(); void reloadOptions(); + void updateContextMenuPolicy(); void fitToWidth(); void fitToHeight(); void toggleWidthHeight(); diff --git a/YACReader/mouse_handler.cpp b/YACReader/mouse_handler.cpp new file mode 100644 index 00000000..d4b7a6c1 --- /dev/null +++ b/YACReader/mouse_handler.cpp @@ -0,0 +1,149 @@ +#include "mouse_handler.h" + +#include + +#include "configuration.h" +#include "magnifying_glass.h" +#include "render.h" +#include "viewer.h" + +#include "goto_flow.h" +#ifndef NO_OPENGL +#include "goto_flow_gl.h" +#else +#include +#endif + +using namespace YACReader; + +YACReader::MouseHandler::MouseHandler(Viewer *viewer) + : viewer(viewer) +{ +} + +void YACReader::MouseHandler::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + viewer->drag = true; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + auto position = event->position(); +#else + auto position = QPointF(event->x(), event->y()); +#endif + dragOrigin = dragLatestPosition = position; + viewer->setCursor(Qt::ClosedHandCursor); + event->accept(); + return; + } +} + +void YACReader::MouseHandler::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() == Qt::ForwardButton) { + viewer->right(); + event->accept(); + return; + } + + if (event->button() == Qt::BackButton) { + viewer->left(); + event->accept(); + return; + } + + auto wasDragging = viewer->drag; + + if (event->button() == Qt::LeftButton) { + viewer->drag = false; + viewer->setCursor(Qt::OpenHandCursor); + event->accept(); + } + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + auto position = event->position(); +#else + auto position = QPointF(event->x(), event->y()); +#endif + auto dragDistance = QLineF(position, dragOrigin).length(); + + auto mouseMode = Configuration::getConfiguration().getMouseMode(); + switch (mouseMode) { + case Normal: + return; + case LeftRightNavigation: + if (wasDragging && (dragDistance > 25)) { + return; + } + + if (event->button() == Qt::LeftButton) { + viewer->left(); + event->accept(); + return; + } + + if (event->button() == Qt::RightButton) { + viewer->right(); + event->accept(); + return; + } + + break; + case HotAreas: + if (wasDragging && (dragDistance > 25)) { + return; + } + + if (event->button() == Qt::LeftButton) { + if (position.x() < viewer->width() / 2) { + viewer->left(); + } else { + viewer->right(); + } + } + + break; + }; +} + +void YACReader::MouseHandler::mouseMoveEvent(QMouseEvent *event) +{ + viewer->showCursor(); + viewer->hideCursorTimer->start(2500); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + auto position = event->position(); +#else + auto position = QPointF(event->x(), event->y()); +#endif + + if (viewer->magnifyingGlassShown) + viewer->mglass->move(static_cast(position.x() - float(viewer->mglass->width()) / 2), static_cast(position.y() - float(viewer->mglass->height()) / 2)); + + if (viewer->render->hasLoadedComic()) { + if (viewer->showGoToFlowAnimation->state() != QPropertyAnimation::Running) { + if (Configuration::getConfiguration().getDisableShowOnMouseOver() == false) { + if (viewer->goToFlow->isVisible()) { + QPoint gtfPos = viewer->goToFlow->mapFrom(this->viewer, event->pos()); + if (gtfPos.y() < 0 || gtfPos.x() < 0 || gtfPos.x() > viewer->goToFlow->width()) // TODO this extra check is for Mavericks (mouseMove over goToFlowGL seems to be broken) + viewer->animateHideGoToFlow(); + // goToFlow->hide(); + } else { + int umbral = (viewer->width() - viewer->goToFlow->width()) / 2; + if ((position.y() > viewer->height() - 15) && (position.x() > umbral) && (position.x() < viewer->width() - umbral)) { + + viewer->animateShowGoToFlow(); + viewer->hideCursorTimer->stop(); + } + } + } + } + + if (viewer->drag) { + int currentPosY = viewer->verticalScrollBar()->sliderPosition(); + int currentPosX = viewer->horizontalScrollBar()->sliderPosition(); + viewer->verticalScrollBar()->setSliderPosition(currentPosY + (dragLatestPosition.y() - position.y())); + viewer->horizontalScrollBar()->setSliderPosition(currentPosX + (dragLatestPosition.x() - position.x())); + dragLatestPosition = position; + } + } +} diff --git a/YACReader/mouse_handler.h b/YACReader/mouse_handler.h new file mode 100644 index 00000000..1c5345b8 --- /dev/null +++ b/YACReader/mouse_handler.h @@ -0,0 +1,25 @@ +#ifndef MOUSE_HANDLER_H +#define MOUSE_HANDLER_H + +#include + +class Viewer; + +namespace YACReader { +class MouseHandler +{ +public: + MouseHandler(Viewer *viewer); + + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + +private: + Viewer *viewer; + QPointF dragOrigin; + QPointF dragLatestPosition; +}; +} + +#endif // MOUSE_HANDLER_H diff --git a/YACReader/options_dialog.cpp b/YACReader/options_dialog.cpp index 6f443dbb..ec5b2744 100644 --- a/YACReader/options_dialog.cpp +++ b/YACReader/options_dialog.cpp @@ -81,11 +81,25 @@ OptionsDialog::OptionsDialog(QWidget *parent) scrollBox->setLayout(scrollLayout); + auto mouseModeBox = new QGroupBox(tr("Mouse mode")); + auto mouseModeLayout = new QVBoxLayout(); + + normalMouseModeRadioButton = new QRadioButton(tr("Only Back/Forward buttons can turn pages")); + leftRightNavigationMouseModeRadioButton = new QRadioButton(tr("Use the Left/Right buttons to turn pages.")); + hotAreasMouseModeRadioButton = new QRadioButton(tr("Click left or right half of the screen to turn pages.")); + + mouseModeLayout->addWidget(normalMouseModeRadioButton); + mouseModeLayout->addWidget(leftRightNavigationMouseModeRadioButton); + mouseModeLayout->addWidget(hotAreasMouseModeRadioButton); + + mouseModeBox->setLayout(mouseModeLayout); + layoutGeneral->addWidget(pathBox); layoutGeneral->addWidget(slideSizeBox); // layoutGeneral->addWidget(fitBox); layoutGeneral->addWidget(colorBox); layoutGeneral->addWidget(scrollBox); + layoutGeneral->addWidget(mouseModeBox); layoutGeneral->addWidget(shortcutsBox); layoutGeneral->addStretch(); @@ -246,6 +260,18 @@ void OptionsDialog::saveOptions() settings->setValue(USE_SINGLE_SCROLL_STEP_TO_TURN_PAGE, useSingleScrollStepToTurnPage->isChecked()); settings->setValue(DISABLE_SCROLL_ANIMATION, disableScrollAnimations->isChecked()); + // get checked radio button to get the mouse mode + YACReader::MouseMode mouseMode = Normal; + if (normalMouseModeRadioButton->isChecked()) { + mouseMode = Normal; + ; + } else if (leftRightNavigationMouseModeRadioButton->isChecked()) { + mouseMode = LeftRightNavigation; + } else if (hotAreasMouseModeRadioButton->isChecked()) { + mouseMode = HotAreas; + } + Configuration::getConfiguration().setMouseMode(mouseMode); + YACReaderOptionsDialog::saveOptions(); } @@ -293,6 +319,20 @@ void OptionsDialog::restoreOptions(QSettings *settings) auto defaultDisableScrollAnimationsValue = false; #endif disableScrollAnimations->setChecked(settings->value(DISABLE_SCROLL_ANIMATION, defaultDisableScrollAnimationsValue).toBool()); + + auto mouseMode = Configuration::getConfiguration().getMouseMode(); + + switch (mouseMode) { + case Normal: + normalMouseModeRadioButton->setChecked(true); + break; + case LeftRightNavigation: + leftRightNavigationMouseModeRadioButton->setChecked(true); + break; + case HotAreas: + hotAreasMouseModeRadioButton->setChecked(true); + break; + } } void OptionsDialog::updateColor(const QColor &color) diff --git a/YACReader/options_dialog.h b/YACReader/options_dialog.h index db1e858c..a3f04cc1 100644 --- a/YACReader/options_dialog.h +++ b/YACReader/options_dialog.h @@ -51,6 +51,11 @@ private: YACReaderSpinSliderWidget *gammaS; QColor currentColor; + + QRadioButton *normalMouseModeRadioButton; + QRadioButton *leftRightNavigationMouseModeRadioButton; + QRadioButton *hotAreasMouseModeRadioButton; + public slots: void saveOptions() override; void restoreOptions(QSettings *settings) override; diff --git a/YACReader/viewer.cpp b/YACReader/viewer.cpp index 7d6fe242..e3552e8f 100644 --- a/YACReader/viewer.cpp +++ b/YACReader/viewer.cpp @@ -1,5 +1,4 @@ #include "viewer.h" -#include "magnifying_glass.h" #include "configuration.h" #include "magnifying_glass.h" #include "goto_flow.h" @@ -38,7 +37,8 @@ Viewer::Viewer(QWidget *parent) shouldOpenNext(false), shouldOpenPrevious(false), magnifyingGlassShown(false), - restoreMagnifyingGlass(false) + restoreMagnifyingGlass(false), + mouseHandler(std::make_unique(this)) { translator = new YACReaderTranslator(this); translator->hide(); @@ -260,6 +260,10 @@ void Viewer::processCRCError(QString message) void Viewer::next() { + if (!render->hasLoadedComic()) { + return; + } + direction = 1; if (doublePage && render->currentPageIsDoublePage()) { render->nextDoublePage(); @@ -272,6 +276,10 @@ void Viewer::next() void Viewer::left() { + if (!render->hasLoadedComic()) { + return; + } + if (doubleMangaPage) { next(); } else { @@ -281,6 +289,10 @@ void Viewer::left() void Viewer::right() { + if (!render->hasLoadedComic()) { + return; + } + if (doubleMangaPage) { prev(); } else { @@ -290,6 +302,10 @@ void Viewer::right() void Viewer::prev() { + if (!render->hasLoadedComic()) { + return; + } + direction = -1; if (doublePage && render->previousPageIsDoublePage()) { render->previousDoublePage(); @@ -767,44 +783,6 @@ void Viewer::resizeEvent(QResizeEvent *event) QScrollArea::resizeEvent(event); } -void Viewer::mouseMoveEvent(QMouseEvent *event) -{ - showCursor(); - hideCursorTimer->start(2500); - - if (magnifyingGlassShown) - mglass->move(static_cast(event->x() - float(mglass->width()) / 2), static_cast(event->y() - float(mglass->height()) / 2)); - - if (render->hasLoadedComic()) { - if (showGoToFlowAnimation->state() != QPropertyAnimation::Running) { - if (Configuration::getConfiguration().getDisableShowOnMouseOver() == false) { - if (goToFlow->isVisible()) { - QPoint gtfPos = goToFlow->mapFrom(this, event->pos()); - if (gtfPos.y() < 0 || gtfPos.x() < 0 || gtfPos.x() > goToFlow->width()) // TODO this extra check is for Mavericks (mouseMove over goToFlowGL seems to be broken) - animateHideGoToFlow(); - // goToFlow->hide(); - } else { - int umbral = (width() - goToFlow->width()) / 2; - if ((event->y() > height() - 15) && (event->x() > umbral) && (event->x() < width() - umbral)) { - - animateShowGoToFlow(); - hideCursorTimer->stop(); - } - } - } - } - - if (drag) { - int currentPosY = verticalScrollBar()->sliderPosition(); - int currentPosX = horizontalScrollBar()->sliderPosition(); - verticalScrollBar()->setSliderPosition(currentPosY + (yDragOrigin - event->y())); - horizontalScrollBar()->setSliderPosition(currentPosX + (xDragOrigin - event->x())); - yDragOrigin = event->y(); - xDragOrigin = event->x(); - } - } -} - QPixmap Viewer::pixmap() const { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) @@ -1093,36 +1071,17 @@ void Viewer::animateHideTranslator() void Viewer::mousePressEvent(QMouseEvent *event) { - if (event->button() == Qt::LeftButton) { - drag = true; - yDragOrigin = event->y(); - xDragOrigin = event->x(); - setCursor(Qt::ClosedHandCursor); - event->accept(); - return; - } + mouseHandler->mousePressEvent(event); } void Viewer::mouseReleaseEvent(QMouseEvent *event) { - if (event->button() == Qt::LeftButton) { - drag = false; - setCursor(Qt::OpenHandCursor); - event->accept(); - return; - } + mouseHandler->mouseReleaseEvent(event); +} - if (event->button() == Qt::ForwardButton) { - right(); - event->accept(); - return; - } - - if (event->button() == Qt::BackButton) { - left(); - event->accept(); - return; - } +void Viewer::mouseMoveEvent(QMouseEvent *event) +{ + mouseHandler->mouseMoveEvent(event); } void Viewer::updateZoomRatio(int ratio) diff --git a/YACReader/viewer.h b/YACReader/viewer.h index 5dd43348..c2bb183b 100644 --- a/YACReader/viewer.h +++ b/YACReader/viewer.h @@ -17,6 +17,7 @@ #include #include "scroll_management.h" +#include "mouse_handler.h" class ComicDB; class Comic; @@ -147,9 +148,6 @@ private: int translatorXPos; QPropertyAnimation *translatorAnimation; - int yDragOrigin; - int xDragOrigin; - NotificationsLabelWidget *notificationsLabel; bool shouldOpenNext; @@ -185,6 +183,9 @@ private: int animationDuration() const; void animateScroll(QPropertyAnimation &scroller, const QScrollBar &scrollBar, int delta); + //! Mouse handler + std::unique_ptr mouseHandler; + public: Viewer(QWidget *parent = nullptr); ~Viewer(); @@ -213,6 +214,8 @@ signals: void magnifyingGlassZoomIn(); void magnifyingGlassZoomOut(); void resetMagnifyingGlass(); + + friend class YACReader::MouseHandler; }; #endif diff --git a/common/yacreader_global_gui.h b/common/yacreader_global_gui.h index 9e9b3b66..0b61d42f 100644 --- a/common/yacreader_global_gui.h +++ b/common/yacreader_global_gui.h @@ -36,6 +36,7 @@ #define DO_NOT_TURN_PAGE_ON_SCROLL "DO_NOT_TURN_PAGE_ON_SCROLL" #define USE_SINGLE_SCROLL_STEP_TO_TURN_PAGE "USE_SINGLE_SCROLL_STEP_TO_TURN_PAGE" #define DISABLE_SCROLL_ANIMATION "DISABLE_SCROLL_ANIMATION" +#define MOUSE_MODE "MOUSE_MODE" #define FLOW_TYPE_GL "FLOW_TYPE_GL" #define Y_POSITION "Y_POSITION" @@ -97,14 +98,6 @@ enum ComicsViewStatus { Info }; -enum FitMode { - ToWidth = 0x01, - ToHeight = 0x02, - FullRes = 0x03, - FullPage = 0x04 //, - // Text=0x05 -}; - enum LibraryUITheme { Light, Dark