diff --git a/CHANGELOG.md b/CHANGELOG.md index 915a136fb..aa8f9a419 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Version counting is based on semantic versioning (Major.Feature.Patch) * Add an optional circular magnifying glass, with an optional ring drawn around it. The configured size is kept as a rectangle, so switching back and forth doesn't lose it. * Add optional edge easing for the magnifying glass. The magnified region is pushed toward the edges of the view, so content near the border can be inspected without pushing the cursor all the way into the corner. * Require a full wheel notch before the magnifying glass changes size or zoom, so a light trackpad gesture no longer resizes it. +* Add a setting to control what the Escape key does. It can keep quitting the reader, as before, or instead cancel the topmost active mode: magnifying glass, dictionary, go to flow and then fullscreen. ### YACReaderLibrary * Add a library repair function to restore missing covers and rescan files that previously failed to be added. diff --git a/YACReader/configuration.h b/YACReader/configuration.h index 58fb39b3c..74f4a58e3 100644 --- a/YACReader/configuration.h +++ b/YACReader/configuration.h @@ -32,6 +32,11 @@ enum MouseMode { HotAreas }; +enum EscapeKeyBehavior { + EscapeQuits = 0, + EscapeCancelsMode = 1 +}; + class Configuration : public QObject { Q_OBJECT @@ -123,6 +128,9 @@ class Configuration : public QObject MouseMode getMouseMode() { return static_cast(settings->value(MOUSE_MODE, MouseMode::Normal).toInt()); } void setMouseMode(MouseMode mouseMode) { settings->setValue(MOUSE_MODE, static_cast(mouseMode)); } + EscapeKeyBehavior getEscapeKeyBehavior() { return static_cast(settings->value(ESCAPE_KEY_BEHAVIOR, EscapeKeyBehavior::EscapeQuits).toInt()); } + void setEscapeKeyBehavior(EscapeKeyBehavior behavior) { settings->setValue(ESCAPE_KEY_BEHAVIOR, static_cast(behavior)); } + ScaleMethod getScalingMethod() { return static_cast(settings->value(SCALING_METHOD, static_cast(ScaleMethod::Lanczos)).toInt()); } void setScalingMethod(ScaleMethod method) { settings->setValue(SCALING_METHOD, static_cast(method)); } }; diff --git a/YACReader/main_window_viewer.cpp b/YACReader/main_window_viewer.cpp index 19cecd344..1372cc05a 100644 --- a/YACReader/main_window_viewer.cpp +++ b/YACReader/main_window_viewer.cpp @@ -218,6 +218,7 @@ MainWindowViewer::~MainWindowViewer() delete showShorcutsAction; delete showInfoAction; delete closeAction; + delete exitAction; delete showDictionaryAction; delete adjustToFullSizeAction; delete fitToPageAction; @@ -565,10 +566,19 @@ void MainWindowViewer::createActions() showInfoAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SHOW_INFO_ACTION_Y)); connect(showInfoAction, &QAction::triggered, viewer, &Viewer::informationSwitch); - closeAction = new QAction(tr("Close"), this); + // closeAction owns the Escape key. Depending on the EscapeKeyBehavior setting it either + // quits (default) or cancels the topmost active mode. The File▸Close menu command lives + // on exitAction below, which always quits regardless of the setting. + closeAction = new QAction(tr("Escape"), this); + // The shortcuts editor lists actions by toolTip(), which otherwise falls back to the + // action text; name the behaviour rather than the key, which it already shows. + closeAction->setToolTip(tr("Escape key: quit, or cancel the active mode")); closeAction->setData(CLOSE_ACTION_Y); closeAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(CLOSE_ACTION_Y)); - connect(closeAction, &QAction::triggered, this, &QWidget::close); + connect(closeAction, &QAction::triggered, this, &MainWindowViewer::onEscapePressed); + + exitAction = new QAction(tr("Close"), this); + connect(exitAction, &QAction::triggered, this, &QWidget::close); showDictionaryAction = new QAction(tr("Show Dictionary"), this); // showDictionaryAction->setCheckable(true); @@ -787,7 +797,7 @@ void MainWindowViewer::createToolBars() fileMenu->addMenu(recentmenu); fileMenu->addSeparator(); - fileMenu->addAction(closeAction); + fileMenu->addAction(exitAction); auto editMenu = new QMenu(tr("Edit")); editMenu->addAction(leftRotationAction); @@ -1187,6 +1197,45 @@ void MainWindowViewer::toggleFullScreen() Configuration::getConfiguration().setFullScreen(fullscreen = !fullscreen); } +void MainWindowViewer::onEscapePressed() +{ + if (Configuration::getConfiguration().getEscapeKeyBehavior() == EscapeCancelsMode) { + // Cancel the topmost active mode; if none is active this is a no-op (does not quit). + cancelActiveMode(); + return; + } + + close(); +} + +bool MainWindowViewer::cancelActiveMode() +{ + // This order is documented to users in the Options ▸ General ▸ "Escape key" tooltip; + // keep the two in sync when adding or reordering modes. + if (viewer->magnifyingGlassIsVisible()) { + viewer->hideMagnifyingGlass(); + showMagnifyingGlassAction->setChecked(false); + return true; + } + + if (viewer->translatorIsVisible()) { + viewer->animateHideTranslator(); + return true; + } + + if (viewer->goToFlowIsVisible()) { + viewer->animateHideGoToFlow(); + return true; + } + + if (fullscreen) { + toggleFullScreen(); + return true; + } + + return false; +} + void MainWindowViewer::toFullScreen() { fromMaximized = this->isMaximized(); @@ -1724,6 +1773,7 @@ void MainWindowViewer::applyTheme(const Theme &theme) setIcon(showShorcutsAction, toolbarTheme.showShorcutsAction, toolbarTheme.showShorcutsAction18x18); setIcon(showInfoAction, toolbarTheme.showInfoAction, toolbarTheme.showInfoAction18x18); setIcon(closeAction, toolbarTheme.closeAction, toolbarTheme.closeAction18x18); + setIcon(exitAction, toolbarTheme.closeAction, toolbarTheme.closeAction18x18); setIcon(showDictionaryAction, toolbarTheme.showDictionaryAction, toolbarTheme.showDictionaryAction18x18); setIcon(adjustToFullSizeAction, toolbarTheme.adjustToFullSizeAction, toolbarTheme.adjustToFullSizeAction18x18); setIcon(fitToPageAction, toolbarTheme.fitToPageAction, toolbarTheme.fitToPageAction18x18); diff --git a/YACReader/main_window_viewer.h b/YACReader/main_window_viewer.h index c1d10caf4..d960422d6 100644 --- a/YACReader/main_window_viewer.h +++ b/YACReader/main_window_viewer.h @@ -81,6 +81,10 @@ public slots: void toggleFitToWidthSlider(); + // Escape key handling: either quits (default) or cancels the topmost active mode, + // depending on the EscapeKeyBehavior setting. + void onEscapePressed(); + /*void viewComic(); void prev(); void next(); @@ -135,7 +139,12 @@ public slots: QAction *leftRotationAction; QAction *rightRotationAction; QAction *showInfoAction; - QAction *closeAction; + QAction *closeAction; // owns the Escape key; dispatches quit vs. cancel-mode + QAction *exitAction; // File▸Close menu command, always quits + + // Cancels the topmost active mode (magnifier, translator, go-to-flow, fullscreen). + // Returns true if a mode was cancelled, false if none was active. + bool cancelActiveMode(); QAction *doublePageAction; QAction *doubleMangaPageAction; QAction *continuousScrollAction; diff --git a/YACReader/options_dialog.cpp b/YACReader/options_dialog.cpp index af7d8c493..3ef1235e0 100644 --- a/YACReader/options_dialog.cpp +++ b/YACReader/options_dialog.cpp @@ -131,6 +131,31 @@ OptionsDialog::OptionsDialog(QWidget *parent) mouseModeBox->setLayout(mouseModeLayout); + auto escapeKeyBox = new QGroupBox(tr("Escape key")); + auto escapeKeyLayout = new QVBoxLayout(); + + escapeQuitsRadioButton = new QRadioButton(tr("Quit the reader")); + escapeCancelsModeRadioButton = new QRadioButton(tr("Cancel the active mode")); + + escapeQuitsRadioButton->setToolTip(tr("Escape closes the reader, even while a mode is active.")); + + // Keep this list in sync with MainWindowViewer::cancelActiveMode(), which implements the order. + //: Tooltip listing the order in which modes are cancelled. Only the first + //: active mode in the list is cancelled per Escape keypress. + escapeCancelsModeRadioButton->setToolTip(tr("Escape cancels the first of these that is active:\n" + "\n" + "1. Magnifying glass\n" + "2. Dictionary\n" + "3. Go to page bar\n" + "4. Fullscreen\n" + "\n" + "If none is active, Escape does nothing.")); + + escapeKeyLayout->addWidget(escapeQuitsRadioButton); + escapeKeyLayout->addWidget(escapeCancelsModeRadioButton); + + escapeKeyBox->setLayout(escapeKeyLayout); + layoutGeneral->addWidget(pathBox); layoutGeneral->addWidget(languageBox); layoutGeneral->addWidget(displayBox); @@ -140,6 +165,7 @@ OptionsDialog::OptionsDialog(QWidget *parent) layoutGeneral->addWidget(colorBox); layoutGeneral->addWidget(scrollBox); layoutGeneral->addWidget(mouseModeBox); + layoutGeneral->addWidget(escapeKeyBox); layoutGeneral->addWidget(shortcutsBox); layoutGeneral->addStretch(); @@ -354,6 +380,9 @@ void OptionsDialog::saveOptions() } Configuration::getConfiguration().setMouseMode(mouseMode); + Configuration::getConfiguration().setEscapeKeyBehavior( + escapeCancelsModeRadioButton->isChecked() ? EscapeCancelsMode : EscapeQuits); + Configuration::getConfiguration().setScalingMethod(static_cast(scalingMethodCombo->currentIndex())); emit changedImageOptions(); @@ -431,6 +460,11 @@ void OptionsDialog::restoreOptions(QSettings *settings) hotAreasMouseModeRadioButton->setChecked(true); break; } + + if (Configuration::getConfiguration().getEscapeKeyBehavior() == EscapeCancelsMode) + escapeCancelsModeRadioButton->setChecked(true); + else + escapeQuitsRadioButton->setChecked(true); } void OptionsDialog::updateColor(const QColor &color) diff --git a/YACReader/options_dialog.h b/YACReader/options_dialog.h index a078cbd32..b75a8adb9 100644 --- a/YACReader/options_dialog.h +++ b/YACReader/options_dialog.h @@ -75,6 +75,9 @@ class OptionsDialog : public YACReaderOptionsDialog, protected Themable QRadioButton *leftRightNavigationMouseModeRadioButton; QRadioButton *hotAreasMouseModeRadioButton; + QRadioButton *escapeQuitsRadioButton; + QRadioButton *escapeCancelsModeRadioButton; + public slots: void saveOptions() override; void restoreOptions(QSettings *settings) override; diff --git a/YACReader/viewer.cpp b/YACReader/viewer.cpp index 0050a7f2d..20fdb872c 100644 --- a/YACReader/viewer.cpp +++ b/YACReader/viewer.cpp @@ -1276,6 +1276,16 @@ void Viewer::translatorSwitch() translator->isVisible() ? animateHideTranslator() : animateShowTranslator(); } +bool Viewer::translatorIsVisible() const +{ + return translator->isVisible(); +} + +bool Viewer::goToFlowIsVisible() const +{ + return goToFlow->isVisible(); +} + void Viewer::showGoToFlow() { if (render->hasLoadedComic()) { diff --git a/YACReader/viewer.h b/YACReader/viewer.h index 138f12824..9d2770bfd 100644 --- a/YACReader/viewer.h +++ b/YACReader/viewer.h @@ -85,6 +85,8 @@ public slots: void rotateLeft(); void rotateRight(); bool magnifyingGlassIsVisible() const { return magnifyingGlassShown; } + bool translatorIsVisible() const; + bool goToFlowIsVisible() const; void setBookmark(bool); void save(); void doublePageSwitch(); diff --git a/common/yacreader_global_gui.h b/common/yacreader_global_gui.h index 80376a1a9..d671b30ea 100644 --- a/common/yacreader_global_gui.h +++ b/common/yacreader_global_gui.h @@ -44,6 +44,7 @@ #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 ESCAPE_KEY_BEHAVIOR "ESCAPE_KEY_BEHAVIOR" #define SCALING_METHOD "SCALING_METHOD" #define SAVE_RENDERED_PAGE_DIRECTORY "SAVE_RENDERED_PAGE_DIRECTORY" #define EXTRACT_PAGE_DIRECTORY "EXTRACT_PAGE_DIRECTORY"