From ba84489c6897eb45a09f44e80b6c42f52d86593a Mon Sep 17 00:00:00 2001 From: Le Tan Date: Mon, 10 Jul 2017 21:15:04 +0800 Subject: [PATCH] support custom shortcuts --- src/resources/docs/shortcuts_en.md | 32 ++++++++- src/resources/docs/shortcuts_zh.md | 31 +++++++++ src/resources/icons/close_note_tb.svg | 10 +++ src/resources/styles/default.mdhl | 2 +- src/resources/vnote.ini | 24 +++++++ src/vconfigmanager.cpp | 93 ++++++++++++++++++++++++++- src/vconfigmanager.h | 25 +++++++ src/vmainwindow.cpp | 77 ++++++++++++++++++---- src/vmainwindow.h | 1 + src/vnote.qrc | 1 + 10 files changed, 281 insertions(+), 15 deletions(-) create mode 100644 src/resources/icons/close_note_tb.svg diff --git a/src/resources/docs/shortcuts_en.md b/src/resources/docs/shortcuts_en.md index cdb6e1f5..883d7c3f 100644 --- a/src/resources/docs/shortcuts_en.md +++ b/src/resources/docs/shortcuts_en.md @@ -72,6 +72,36 @@ Expand the selection to the beginning or end of current line. - `Ctrl+Shift+Home`, `Ctrl+Shift+End` Expand the selection to the beginning or end of current note. +## Custom Shortcuts +VNote supports customing some standard shortcuts, though it is not recommended. VNote stores shortcuts' configuration information in the `[shortcuts]` section of user configuration file `vnote.ini`. + +For example, the default configruation may look like this: + +```ini +[shortcuts] +1\operation=NewNote +1\keysequence=Ctrl+N +2\operation=SaveNote +2\keysequence=Ctrl+S +3\operation=SaveAndRead +3\keysequence=Ctrl+T +4\operation=EditNote +4\keysequence=Ctrl+W +5\operation=CloseNote +5\keysequence= +6\operation=Find +6\keysequence=Ctrl+F +7\operation=FindNext +7\keysequence=F3 +8\operation=FindPrevious +8\keysequence=Shift+F3 +size=8 +``` + +`size=8` tells VNote that there are 8 shotcuts defined here, with each beginning with the number sequence. You could change the `keysequence` value to change the default key sequence of a specified operation. Leave the `keysequence` empty (`keysequence=`) to disable shortcut for that operation. + +Pay attention that `Ctrl+E` is reserved for *Captain Mode* and `Ctrl+Q` is reserved for quitting VNote. + # Captain Mode To efficiently utilize the shortcuts, VNote supports the **Captain Mode**. @@ -117,7 +147,7 @@ Move current tab one split window right. Display shortcuts documentation. ## Navigation Mode -Within the Captain MOde, `W` will turn VNote into **Navigation Mode**. In this mode, VNote will display at most two characters on some major widgets, and then pressing corresponding characters will jump to that widget. +Within the Captain Mode, `W` will turn VNote into **Navigation Mode**. In this mode, VNote will display at most two characters on some major widgets, and then pressing corresponding characters will jump to that widget. # Vim Mode VNote supports a simple but useful Vim mode, including **Normal**, **Insert**, **Visual**, and **VisualLine** modes. diff --git a/src/resources/docs/shortcuts_zh.md b/src/resources/docs/shortcuts_zh.md index e1a4f0c5..47bd8a8e 100644 --- a/src/resources/docs/shortcuts_zh.md +++ b/src/resources/docs/shortcuts_zh.md @@ -72,6 +72,37 @@ - `Ctrl+Shift+Home`, `Ctrl+Shift+End` 扩展选定到笔记开始或结尾处。 +## 自定义快捷键 +VNote支持自定义部分标准快捷键(但并不建议这么做)。VNote将快捷键信息保存在用户配置文件`vnote.ini`中的`[shortcuts]`小节。 + +例如,默认的配置可能是这样子的: + + +```ini +[shortcuts] +1\operation=NewNote +1\keysequence=Ctrl+N +2\operation=SaveNote +2\keysequence=Ctrl+S +3\operation=SaveAndRead +3\keysequence=Ctrl+T +4\operation=EditNote +4\keysequence=Ctrl+W +5\operation=CloseNote +5\keysequence= +6\operation=Find +6\keysequence=Ctrl+F +7\operation=FindNext +7\keysequence=F3 +8\operation=FindPrevious +8\keysequence=Shift+F3 +size=8 +``` + +`size=8` 告诉VNote这里定义了8组快捷键,每组快捷键都以一个数字序号开始。通过改变每组快捷键中`keysequence`的值来改变某个操作的默认快捷键。将`keysequence`设置为空(`keysequence=`)则会禁用该操作的任何快捷键。 + +注意,`Ctrl+E`保留作为*舰长模式*的前导键,`Ctrl+Q`保留为退出VNote。 + # 舰长模式 为了更有效地利用快捷键,VNote支持 **舰长模式**。 diff --git a/src/resources/icons/close_note_tb.svg b/src/resources/icons/close_note_tb.svg new file mode 100644 index 00000000..2e111a9e --- /dev/null +++ b/src/resources/icons/close_note_tb.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/resources/styles/default.mdhl b/src/resources/styles/default.mdhl index 6af90e9b..0668915d 100644 --- a/src/resources/styles/default.mdhl +++ b/src/resources/styles/default.mdhl @@ -13,7 +13,7 @@ editor # Do not use "" to quote the name font-family: Hiragino Sans GB, 冬青黑体, Microsoft YaHei, 微软雅黑, Microsoft YaHei UI, WenQuanYi Micro Hei, 文泉驿雅黑, Dengxian, 等线体, STXihei, 华文细黑, Liberation Sans, Droid Sans, NSimSun, 新宋体, SimSun, 宋体, Helvetica, sans-serif, Tahoma, Arial, Verdana, Geneva, Georgia, Times New Roman # [VNote] Style for trailing space -trailing-space: ffebee +trailing-space: a8a8a8 font-size: 12 line-number-background: bdbdbd line-number-foreground: 424242 diff --git a/src/resources/vnote.ini b/src/resources/vnote.ini index 8cb2df1c..556e1f4c 100644 --- a/src/resources/vnote.ini +++ b/src/resources/vnote.ini @@ -70,3 +70,27 @@ tools_dock_checked=true 4\name=LightGrey 4\rgb=D3D3D3 size=4 + +[shortcuts] +; Define shortcuts here, with each item in the form "operation->keysequence". +; Leave keysequence empty to disable the shortcut of an operation. +; Custom shortcuts may conflict with some key bindings in edit mode or Vim mode. +; Ctrl+E is reserved for Captain Mode. +; Ctrl+Q is reserved for quitting VNote. +1\operation=NewNote +1\keysequence=Ctrl+N +2\operation=SaveNote +2\keysequence=Ctrl+S +3\operation=SaveAndRead +3\keysequence=Ctrl+T +4\operation=EditNote +4\keysequence=Ctrl+W +5\operation=CloseNote +5\keysequence= +6\operation=Find +6\keysequence=Ctrl+F +7\operation=FindNext +7\keysequence=F3 +8\operation=FindPrevious +8\keysequence=Shift+F3 +size=8 diff --git a/src/vconfigmanager.cpp b/src/vconfigmanager.cpp index aa02e694..9ca31811 100644 --- a/src/vconfigmanager.cpp +++ b/src/vconfigmanager.cpp @@ -153,6 +153,8 @@ void VConfigManager::initialize() m_editorLineNumber = getConfigFromSettings("global", "editor_line_number").toInt(); + + readShortcutsFromSettings(); } void VConfigManager::readPredefinedColorsFromSettings() @@ -338,7 +340,7 @@ void VConfigManager::updateMarkdownEditStyle() static const QString defaultVimInsertBg = "#CDC0B0"; static const QString defaultVimVisualBg = "#90CAF9"; static const QString defaultVimReplaceBg = "#F8BBD0"; - static const QString defaultTrailingSpaceBackground = "#FFEBEE"; + static const QString defaultTrailingSpaceBackground = "#A8A8A8"; static const QString defaultLineNumberBg = "#BDBDBD"; static const QString defaultLineNumberFg = "#424242"; @@ -483,6 +485,13 @@ QString VConfigManager::getConfigFolder() const return VUtils::basePathFromPath(iniPath); } +QString VConfigManager::getConfigFilePath() const +{ + V_ASSERT(userSettings); + + return userSettings->fileName(); +} + QString VConfigManager::getStyleConfigFolder() const { return getConfigFolder() + QDir::separator() + c_styleConfigFolder; @@ -690,3 +699,85 @@ QString VConfigManager::getVnoteNotebookFolderPath() { return QDir::home().filePath(c_vnoteNotebookFolderName); } + +bool VConfigManager::isValidKeySequence(const QString &p_seq) +{ + QString lower = p_seq.toLower(); + return lower != "ctrl+q" && lower != "ctrl+e"; +} + +void VConfigManager::readShortcutsFromSettings() +{ + m_shortcuts.clear(); + int size = defaultSettings->beginReadArray("shortcuts"); + for (int i = 0; i < size; ++i) { + defaultSettings->setArrayIndex(i); + QString op = defaultSettings->value("operation").toString(); + QString seq = defaultSettings->value("keysequence").toString().trimmed(); + + if (isValidKeySequence(seq)) { + qDebug() << "read shortcut config" << op << seq; + m_shortcuts[op] = seq; + } + } + + defaultSettings->endArray(); + + // Whether we need to update user settings. + bool needUpdate = false; + size = userSettings->beginReadArray("shortcuts"); + QSet matched; + matched.reserve(m_shortcuts.size()); + for (int i = 0; i < size; ++i) { + userSettings->setArrayIndex(i); + QString op = userSettings->value("operation").toString(); + QString seq = userSettings->value("keysequence").toString().trimmed(); + + if (isValidKeySequence(seq)) { + qDebug() << "read user shortcut config" << op << seq; + auto it = m_shortcuts.find(op); + if (it == m_shortcuts.end()) { + // Could not find this in default settings. + needUpdate = true; + } else { + matched.insert(op); + *it = seq; + } + } + } + + userSettings->endArray(); + + if (needUpdate || matched.size() < m_shortcuts.size()) { + // Write the combined config to user settings. + writeShortcutsToSettings(); + } +} + +void VConfigManager::writeShortcutsToSettings() +{ + // Clear it first + userSettings->beginGroup("shortcuts"); + userSettings->remove(""); + userSettings->endGroup(); + + userSettings->beginWriteArray("shortcuts"); + int idx = 0; + for (auto it = m_shortcuts.begin(); it != m_shortcuts.end(); ++it, ++idx) { + userSettings->setArrayIndex(idx); + userSettings->setValue("operation", it.key()); + userSettings->setValue("keysequence", it.value()); + } + + userSettings->endArray(); +} + +QString VConfigManager::getShortcutKeySequence(const QString &p_operation) const +{ + auto it = m_shortcuts.find(p_operation); + if (it == m_shortcuts.end()) { + return QString(); + } + + return *it; +} diff --git a/src/vconfigmanager.h b/src/vconfigmanager.h index 048e77fa..4bc3c1e8 100644 --- a/src/vconfigmanager.h +++ b/src/vconfigmanager.h @@ -205,9 +205,16 @@ public: const QString &getEditorLineNumberBg() const; const QString &getEditorLineNumberFg() const; + // Return the configured key sequence of @p_operation. + // Return empty if there is no corresponding config. + QString getShortcutKeySequence(const QString &p_operation) const; + // Get the folder the ini file exists. QString getConfigFolder() const; + // Get the ini config file path. + QString getConfigFilePath() const; + // Get the folder c_styleConfigFolder in the config folder. QString getStyleConfigFolder() const; @@ -248,6 +255,20 @@ private: // the new one; if not, use the c_dirConfigFile. static QString fetchDirConfigFilePath(const QString &p_path); + // Read the [shortcuts] section in settings to init m_shortcuts. + // Will remove invalid config items. + // First read the config in default settings; + // Second read the config in user settings and overwrite the default ones; + // If there is any config in deafult settings that is absent in user settings, + // write the combined configs to user settings. + void readShortcutsFromSettings(); + + // Write m_shortcuts to the [shortcuts] section in the user settings. + void writeShortcutsToSettings(); + + // Whether @p_seq is a valid key sequence for shortcuts. + bool isValidKeySequence(const QString &p_seq); + // Default font and palette. QFont m_defaultEditFont; QPalette m_defaultEditPalette; @@ -379,6 +400,10 @@ private: // The foreground color of the line number area. QString m_editorLineNumberFg; + // Shortcuts config. + // Operation -> KeySequence. + QHash m_shortcuts; + // The name of the config file in each directory, obsolete. // Use c_dirConfigFile instead. static const QString c_obsoleteDirConfigFile; diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index c76b8f63..077c0390 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -301,7 +301,9 @@ void VMainWindow::initFileToolBar() newNoteAct = new QAction(QIcon(":/resources/icons/create_note_tb.svg"), tr("New &Note"), this); newNoteAct->setStatusTip(tr("Create a note in current folder")); - newNoteAct->setShortcut(QKeySequence::New); + QString keySeq = vconfig.getShortcutKeySequence("NewNote"); + qDebug() << "set NewNote shortcut to" << keySeq; + newNoteAct->setShortcut(QKeySequence(keySeq)); connect(newNoteAct, &QAction::triggered, fileList, &VFileList::newFile); @@ -317,10 +319,25 @@ void VMainWindow::initFileToolBar() connect(deleteNoteAct, &QAction::triggered, this, &VMainWindow::deleteCurNote); + m_closeNoteAct = new QAction(QIcon(":/resources/icons/close_note_tb.svg"), + tr("&Close Note"), this); + m_closeNoteAct->setStatusTip(tr("Close current note")); + keySeq = vconfig.getShortcutKeySequence("CloseNote"); + qDebug() << "set CloseNote shortcut to" << keySeq; + m_closeNoteAct->setShortcut(QKeySequence(keySeq)); + connect(m_closeNoteAct, &QAction::triggered, + this, [this](){ + if (m_curFile) { + editArea->closeFile(m_curFile, false); + } + }); + editNoteAct = new QAction(QIcon(":/resources/icons/edit_note.svg"), tr("&Edit"), this); editNoteAct->setStatusTip(tr("Edit current note")); - editNoteAct->setShortcut(QKeySequence("Ctrl+W")); + keySeq = vconfig.getShortcutKeySequence("EditNote"); + qDebug() << "set EditNote shortcut to" << keySeq; + editNoteAct->setShortcut(QKeySequence(keySeq)); connect(editNoteAct, &QAction::triggered, editArea, &VEditArea::editFile); @@ -339,21 +356,26 @@ void VMainWindow::initFileToolBar() tr("Save Changes And Read (Ctrl+T)"), this); saveExitAct->setStatusTip(tr("Save changes and exit edit mode")); saveExitAct->setMenu(exitEditMenu); - saveExitAct->setShortcut(QKeySequence("Ctrl+T")); + keySeq = vconfig.getShortcutKeySequence("SaveAndRead"); + qDebug() << "set SaveAndRead shortcut to" << keySeq; + saveExitAct->setShortcut(QKeySequence(keySeq)); connect(saveExitAct, &QAction::triggered, editArea, &VEditArea::saveAndReadFile); saveNoteAct = new QAction(QIcon(":/resources/icons/save_note.svg"), tr("Save"), this); saveNoteAct->setStatusTip(tr("Save changes to current note")); - saveNoteAct->setShortcut(QKeySequence::Save); + keySeq = vconfig.getShortcutKeySequence("SaveNote"); + qDebug() << "set SaveNote shortcut to" << keySeq; + saveNoteAct->setShortcut(QKeySequence(keySeq)); connect(saveNoteAct, &QAction::triggered, editArea, &VEditArea::saveFile); newRootDirAct->setEnabled(false); newNoteAct->setEnabled(false); - noteInfoAct->setEnabled(false); - deleteNoteAct->setEnabled(false); + noteInfoAct->setVisible(false); + deleteNoteAct->setVisible(false); + m_closeNoteAct->setVisible(false); editNoteAct->setVisible(false); saveExitAct->setVisible(false); discardExitAct->setVisible(false); @@ -361,9 +383,10 @@ void VMainWindow::initFileToolBar() fileToolBar->addAction(newRootDirAct); fileToolBar->addAction(newNoteAct); + fileToolBar->addSeparator(); fileToolBar->addAction(noteInfoAct); fileToolBar->addAction(deleteNoteAct); - fileToolBar->addSeparator(); + fileToolBar->addAction(m_closeNoteAct); fileToolBar->addAction(editNoteAct); fileToolBar->addAction(saveExitAct); fileToolBar->addAction(saveNoteAct); @@ -634,6 +657,29 @@ void VMainWindow::initFileMenu() fileMenu->addAction(settingsAct); + QAction *customShortcutAct = new QAction(tr("Custom Shortcuts"), this); + customShortcutAct->setToolTip(tr("Custom some standard shortcuts")); + connect(customShortcutAct, &QAction::triggered, + this, [this](){ + int ret = VUtils::showMessage(QMessageBox::Information, + tr("Custom Shortcuts"), + tr("VNote supports customing some standard shorcuts by " + "editing user's configuration file (vnote.ini). Please " + "reference the shortcuts help documentation for more " + "information."), + tr("Click \"OK\" to custom shortcuts."), + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Ok, + this); + + if (ret == QMessageBox::Ok) { + QUrl url = QUrl::fromLocalFile(vconfig.getConfigFilePath()); + QDesktopServices::openUrl(url); + } + }); + + fileMenu->addAction(customShortcutAct); + fileMenu->addSeparator(); // Exit. @@ -663,19 +709,25 @@ void VMainWindow::initEditMenu() m_findReplaceAct = newAction(QIcon(":/resources/icons/find_replace.svg"), tr("Find/Replace"), this); m_findReplaceAct->setToolTip(tr("Open Find/Replace dialog to search in current note")); - m_findReplaceAct->setShortcut(QKeySequence::Find); + QString keySeq = vconfig.getShortcutKeySequence("Find"); + qDebug() << "set Find shortcut to" << keySeq; + m_findReplaceAct->setShortcut(QKeySequence(keySeq)); connect(m_findReplaceAct, &QAction::triggered, this, &VMainWindow::openFindDialog); m_findNextAct = new QAction(tr("Find Next"), this); m_findNextAct->setToolTip(tr("Find next occurence")); - m_findNextAct->setShortcut(QKeySequence::FindNext); + keySeq = vconfig.getShortcutKeySequence("FindNext"); + qDebug() << "set FindNext shortcut to" << keySeq; + m_findNextAct->setShortcut(QKeySequence(keySeq)); connect(m_findNextAct, SIGNAL(triggered(bool)), m_findReplaceDialog, SLOT(findNext())); m_findPreviousAct = new QAction(tr("Find Previous"), this); m_findPreviousAct->setToolTip(tr("Find previous occurence")); - m_findPreviousAct->setShortcut(QKeySequence::FindPrevious); + keySeq = vconfig.getShortcutKeySequence("FindPrevious"); + qDebug() << "set FindPrevious shortcut to" << keySeq; + m_findPreviousAct->setShortcut(QKeySequence(keySeq)); connect(m_findPreviousAct, SIGNAL(triggered(bool)), m_findReplaceDialog, SLOT(findPrevious())); @@ -1279,8 +1331,9 @@ void VMainWindow::updateActionStateFromTabStatusChange(const VFile *p_file, discardExitAct->setVisible(p_file && p_editMode); saveExitAct->setVisible(p_file && p_editMode); saveNoteAct->setVisible(p_file && p_editMode); - deleteNoteAct->setEnabled(p_file && p_file->isModifiable()); - noteInfoAct->setEnabled(p_file && p_file->getType() == FileType::Normal); + deleteNoteAct->setVisible(p_file && p_file->isModifiable()); + noteInfoAct->setVisible(p_file && p_file->getType() == FileType::Normal); + m_closeNoteAct->setVisible(p_file); m_insertImageAct->setEnabled(p_file && p_editMode); diff --git a/src/vmainwindow.h b/src/vmainwindow.h index 9bd534dc..f3624dd2 100644 --- a/src/vmainwindow.h +++ b/src/vmainwindow.h @@ -178,6 +178,7 @@ private: QAction *newNoteAct; QAction *noteInfoAct; QAction *deleteNoteAct; + QAction *m_closeNoteAct; QAction *editNoteAct; QAction *saveNoteAct; QAction *saveExitAct; diff --git a/src/vnote.qrc b/src/vnote.qrc index f6f58715..c0075ff6 100644 --- a/src/vnote.qrc +++ b/src/vnote.qrc @@ -115,5 +115,6 @@ resources/icons/italic.svg resources/icons/strikethrough.svg resources/icons/inline_code.svg + resources/icons/close_note_tb.svg