Merge pull request #472 from YACReader/add_mouse_nativation_modes

Add mouse navigation modes
This commit is contained in:
Luis Ángel San Martín 2025-05-04 12:56:43 +02:00 committed by GitHub
commit b81b7908f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 297 additions and 80 deletions

View File

@ -6,6 +6,7 @@ Version counting is based on semantic versioning (Major.Feature.Patch)
### YACReader ### YACReader
* Don't use scroll animations on macos by default, it where hdpi scroll is most likely to be used. * Don't use scroll animations on macos by default, it where hdpi scroll is most likely to be used.
* New toolbar on macos. * 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 ### 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}"`. * 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}"`.

View File

@ -80,6 +80,7 @@ HEADERS += ../common/comic.h \
goto_dialog.h \ goto_dialog.h \
magnifying_glass.h \ magnifying_glass.h \
main_window_viewer.h \ main_window_viewer.h \
mouse_handler.h \
viewer.h \ viewer.h \
goto_flow.h \ goto_flow.h \
options_dialog.h \ options_dialog.h \
@ -119,6 +120,7 @@ SOURCES += ../common/comic.cpp \
goto_dialog.cpp \ goto_dialog.cpp \
magnifying_glass.cpp \ magnifying_glass.cpp \
main_window_viewer.cpp \ main_window_viewer.cpp \
mouse_handler.cpp \
viewer.cpp \ viewer.cpp \
goto_flow.cpp \ goto_flow.cpp \
options_dialog.cpp \ options_dialog.cpp \

View File

@ -1,5 +1,6 @@
#ifndef __CONFIGURATION_H #ifndef __CONFIGURATION_H
#define __CONFIGURATION_H #define __CONFIGURATION_H
#include <QByteArray> #include <QByteArray>
#include <QString> #include <QString>
#include <QSize> #include <QSize>
@ -15,6 +16,21 @@
using namespace YACReader; using namespace YACReader;
namespace YACReader {
enum FitMode {
ToWidth = 0x01,
ToHeight = 0x02,
FullRes = 0x03,
FullPage = 0x04
};
enum MouseMode {
Normal,
LeftRightNavigation,
HotAreas
};
class Configuration : public QObject class Configuration : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -94,6 +110,11 @@ public:
return settings->value(DISABLE_SCROLL_ANIMATION, defaultValue).toBool(); return settings->value(DISABLE_SCROLL_ANIMATION, defaultValue).toBool();
} }
MouseMode getMouseMode() { return static_cast<MouseMode>(settings->value(MOUSE_MODE, MouseMode::Normal).toInt()); }
void setMouseMode(MouseMode mouseMode) { settings->setValue(MOUSE_MODE, static_cast<int>(mouseMode)); }
}; };
}
#endif #endif

View File

@ -625,7 +625,7 @@ void MainWindowViewer::createToolBars()
viewer->addAction(closeAction); viewer->addAction(closeAction);
viewer->setContextMenuPolicy(Qt::ActionsContextMenu); updateContextMenuPolicy();
// MacOSX app menus // MacOSX app menus
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
@ -777,9 +777,25 @@ void MainWindowViewer::openComicFromRecentAction(QAction *action)
void MainWindowViewer::reloadOptions() void MainWindowViewer::reloadOptions()
{ {
updateContextMenuPolicy();
viewer->updateConfig(settings); 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() void MainWindowViewer::open()
{ {
QFileDialog openDialog; QFileDialog openDialog;
@ -970,10 +986,12 @@ void MainWindowViewer::disablePreviousNextComicActions()
void MainWindowViewer::mouseDoubleClickEvent(QMouseEvent *event) void MainWindowViewer::mouseDoubleClickEvent(QMouseEvent *event)
{ {
if (Configuration::getConfiguration().getMouseMode() == MouseMode::Normal) {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
toggleFullScreen(); toggleFullScreen();
event->accept(); event->accept();
} }
}
} }
void MainWindowViewer::toggleFullScreen() void MainWindowViewer::toggleFullScreen()

View File

@ -67,6 +67,7 @@ public slots:
void increasePageZoomLevel(); void increasePageZoomLevel();
void decreasePageZoomLevel(); void decreasePageZoomLevel();
void reloadOptions(); void reloadOptions();
void updateContextMenuPolicy();
void fitToWidth(); void fitToWidth();
void fitToHeight(); void fitToHeight();
void toggleWidthHeight(); void toggleWidthHeight();

149
YACReader/mouse_handler.cpp Normal file
View File

@ -0,0 +1,149 @@
#include "mouse_handler.h"
#include <QtWidgets>
#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 <QtWidgets>
#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<int>(position.x() - float(viewer->mglass->width()) / 2), static_cast<int>(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;
}
}
}

25
YACReader/mouse_handler.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef MOUSE_HANDLER_H
#define MOUSE_HANDLER_H
#include <QMouseEvent>
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

View File

@ -81,11 +81,25 @@ OptionsDialog::OptionsDialog(QWidget *parent)
scrollBox->setLayout(scrollLayout); 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(pathBox);
layoutGeneral->addWidget(slideSizeBox); layoutGeneral->addWidget(slideSizeBox);
// layoutGeneral->addWidget(fitBox); // layoutGeneral->addWidget(fitBox);
layoutGeneral->addWidget(colorBox); layoutGeneral->addWidget(colorBox);
layoutGeneral->addWidget(scrollBox); layoutGeneral->addWidget(scrollBox);
layoutGeneral->addWidget(mouseModeBox);
layoutGeneral->addWidget(shortcutsBox); layoutGeneral->addWidget(shortcutsBox);
layoutGeneral->addStretch(); layoutGeneral->addStretch();
@ -246,6 +260,18 @@ void OptionsDialog::saveOptions()
settings->setValue(USE_SINGLE_SCROLL_STEP_TO_TURN_PAGE, useSingleScrollStepToTurnPage->isChecked()); settings->setValue(USE_SINGLE_SCROLL_STEP_TO_TURN_PAGE, useSingleScrollStepToTurnPage->isChecked());
settings->setValue(DISABLE_SCROLL_ANIMATION, disableScrollAnimations->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(); YACReaderOptionsDialog::saveOptions();
} }
@ -293,6 +319,20 @@ void OptionsDialog::restoreOptions(QSettings *settings)
auto defaultDisableScrollAnimationsValue = false; auto defaultDisableScrollAnimationsValue = false;
#endif #endif
disableScrollAnimations->setChecked(settings->value(DISABLE_SCROLL_ANIMATION, defaultDisableScrollAnimationsValue).toBool()); 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) void OptionsDialog::updateColor(const QColor &color)

View File

@ -51,6 +51,11 @@ private:
YACReaderSpinSliderWidget *gammaS; YACReaderSpinSliderWidget *gammaS;
QColor currentColor; QColor currentColor;
QRadioButton *normalMouseModeRadioButton;
QRadioButton *leftRightNavigationMouseModeRadioButton;
QRadioButton *hotAreasMouseModeRadioButton;
public slots: public slots:
void saveOptions() override; void saveOptions() override;
void restoreOptions(QSettings *settings) override; void restoreOptions(QSettings *settings) override;

View File

@ -1,5 +1,4 @@
#include "viewer.h" #include "viewer.h"
#include "magnifying_glass.h"
#include "configuration.h" #include "configuration.h"
#include "magnifying_glass.h" #include "magnifying_glass.h"
#include "goto_flow.h" #include "goto_flow.h"
@ -38,7 +37,8 @@ Viewer::Viewer(QWidget *parent)
shouldOpenNext(false), shouldOpenNext(false),
shouldOpenPrevious(false), shouldOpenPrevious(false),
magnifyingGlassShown(false), magnifyingGlassShown(false),
restoreMagnifyingGlass(false) restoreMagnifyingGlass(false),
mouseHandler(std::make_unique<YACReader::MouseHandler>(this))
{ {
translator = new YACReaderTranslator(this); translator = new YACReaderTranslator(this);
translator->hide(); translator->hide();
@ -260,6 +260,10 @@ void Viewer::processCRCError(QString message)
void Viewer::next() void Viewer::next()
{ {
if (!render->hasLoadedComic()) {
return;
}
direction = 1; direction = 1;
if (doublePage && render->currentPageIsDoublePage()) { if (doublePage && render->currentPageIsDoublePage()) {
render->nextDoublePage(); render->nextDoublePage();
@ -272,6 +276,10 @@ void Viewer::next()
void Viewer::left() void Viewer::left()
{ {
if (!render->hasLoadedComic()) {
return;
}
if (doubleMangaPage) { if (doubleMangaPage) {
next(); next();
} else { } else {
@ -281,6 +289,10 @@ void Viewer::left()
void Viewer::right() void Viewer::right()
{ {
if (!render->hasLoadedComic()) {
return;
}
if (doubleMangaPage) { if (doubleMangaPage) {
prev(); prev();
} else { } else {
@ -290,6 +302,10 @@ void Viewer::right()
void Viewer::prev() void Viewer::prev()
{ {
if (!render->hasLoadedComic()) {
return;
}
direction = -1; direction = -1;
if (doublePage && render->previousPageIsDoublePage()) { if (doublePage && render->previousPageIsDoublePage()) {
render->previousDoublePage(); render->previousDoublePage();
@ -767,44 +783,6 @@ void Viewer::resizeEvent(QResizeEvent *event)
QScrollArea::resizeEvent(event); QScrollArea::resizeEvent(event);
} }
void Viewer::mouseMoveEvent(QMouseEvent *event)
{
showCursor();
hideCursorTimer->start(2500);
if (magnifyingGlassShown)
mglass->move(static_cast<int>(event->x() - float(mglass->width()) / 2), static_cast<int>(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 QPixmap Viewer::pixmap() const
{ {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
@ -1093,36 +1071,17 @@ void Viewer::animateHideTranslator()
void Viewer::mousePressEvent(QMouseEvent *event) void Viewer::mousePressEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton) { mouseHandler->mousePressEvent(event);
drag = true;
yDragOrigin = event->y();
xDragOrigin = event->x();
setCursor(Qt::ClosedHandCursor);
event->accept();
return;
}
} }
void Viewer::mouseReleaseEvent(QMouseEvent *event) void Viewer::mouseReleaseEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton) { mouseHandler->mouseReleaseEvent(event);
drag = false; }
setCursor(Qt::OpenHandCursor);
event->accept();
return;
}
if (event->button() == Qt::ForwardButton) { void Viewer::mouseMoveEvent(QMouseEvent *event)
right(); {
event->accept(); mouseHandler->mouseMoveEvent(event);
return;
}
if (event->button() == Qt::BackButton) {
left();
event->accept();
return;
}
} }
void Viewer::updateZoomRatio(int ratio) void Viewer::updateZoomRatio(int ratio)

View File

@ -17,6 +17,7 @@
#include <QSettings> #include <QSettings>
#include "scroll_management.h" #include "scroll_management.h"
#include "mouse_handler.h"
class ComicDB; class ComicDB;
class Comic; class Comic;
@ -147,9 +148,6 @@ private:
int translatorXPos; int translatorXPos;
QPropertyAnimation *translatorAnimation; QPropertyAnimation *translatorAnimation;
int yDragOrigin;
int xDragOrigin;
NotificationsLabelWidget *notificationsLabel; NotificationsLabelWidget *notificationsLabel;
bool shouldOpenNext; bool shouldOpenNext;
@ -185,6 +183,9 @@ private:
int animationDuration() const; int animationDuration() const;
void animateScroll(QPropertyAnimation &scroller, const QScrollBar &scrollBar, int delta); void animateScroll(QPropertyAnimation &scroller, const QScrollBar &scrollBar, int delta);
//! Mouse handler
std::unique_ptr<YACReader::MouseHandler> mouseHandler;
public: public:
Viewer(QWidget *parent = nullptr); Viewer(QWidget *parent = nullptr);
~Viewer(); ~Viewer();
@ -213,6 +214,8 @@ signals:
void magnifyingGlassZoomIn(); void magnifyingGlassZoomIn();
void magnifyingGlassZoomOut(); void magnifyingGlassZoomOut();
void resetMagnifyingGlass(); void resetMagnifyingGlass();
friend class YACReader::MouseHandler;
}; };
#endif #endif

View File

@ -36,6 +36,7 @@
#define DO_NOT_TURN_PAGE_ON_SCROLL "DO_NOT_TURN_PAGE_ON_SCROLL" #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 USE_SINGLE_SCROLL_STEP_TO_TURN_PAGE "USE_SINGLE_SCROLL_STEP_TO_TURN_PAGE"
#define DISABLE_SCROLL_ANIMATION "DISABLE_SCROLL_ANIMATION" #define DISABLE_SCROLL_ANIMATION "DISABLE_SCROLL_ANIMATION"
#define MOUSE_MODE "MOUSE_MODE"
#define FLOW_TYPE_GL "FLOW_TYPE_GL" #define FLOW_TYPE_GL "FLOW_TYPE_GL"
#define Y_POSITION "Y_POSITION" #define Y_POSITION "Y_POSITION"
@ -97,14 +98,6 @@ enum ComicsViewStatus {
Info Info
}; };
enum FitMode {
ToWidth = 0x01,
ToHeight = 0x02,
FullRes = 0x03,
FullPage = 0x04 //,
// Text=0x05
};
enum LibraryUITheme { enum LibraryUITheme {
Light, Light,
Dark Dark