diff --git a/README.md b/README.md index e373e0a1..aa2557ee 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A pleasant note-taking platform. -For more information, please visit [**VNote's Home Page**](https://vnotex.github.io/vnote). +For more information, please visit [**VNote's Home Page**](https://vnotex.github.io/vnote) or [Home Page on Gitee](https://tamlok.gitee.io/vnote). ![VNote](pics/vnote.png) diff --git a/README_zh_CN.md b/README_zh_CN.md index b451b100..66f4cfbc 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -5,7 +5,7 @@ 一个舒适的笔记平台! -更多信息,请访问 [VNote 主页](https://vnotex.github.io/vnote) 。 +更多信息,请访问 [VNote 主页](https://tamlok.gitee.io/vnote) 或者 [Gitee 托管主页](https://tamlok.gitee.io/vnote) 。 ![VNote](pics/vnote.png) diff --git a/libs/vtextedit b/libs/vtextedit index 83f131ed..34ad7467 160000 --- a/libs/vtextedit +++ b/libs/vtextedit @@ -1 +1 @@ -Subproject commit 83f131edfa70ffce125f4f7ac4f9f75bf5f03078 +Subproject commit 34ad7467eb42b5d1d228228d875a7675814f222b diff --git a/src/core/coreconfig.h b/src/core/coreconfig.h index 75461d38..6ada3757 100644 --- a/src/core/coreconfig.h +++ b/src/core/coreconfig.h @@ -53,6 +53,10 @@ namespace vnotex ActivatePreviousTab, FocusContentArea, OpenWithDefaultProgram, + OneSplitLeft, + OneSplitDown, + OneSplitUp, + OneSplitRight, MaxShortcut }; Q_ENUM(Shortcut) diff --git a/src/core/mainconfig.cpp b/src/core/mainconfig.cpp index c9151277..0a004336 100644 --- a/src/core/mainconfig.cpp +++ b/src/core/mainconfig.cpp @@ -117,5 +117,5 @@ QString MainConfig::getVersion(const QJsonObject &p_jobj) void MainConfig::doVersionSpecificOverride() { // In a new version, we may want to change one value by force. - m_coreConfig->m_shortcuts[CoreConfig::Shortcut::SearchDock].clear(); + m_coreConfig->m_shortcuts[CoreConfig::Shortcut::LocationListDock] = "Ctrl+G, C"; } diff --git a/src/data/core/vnotex.json b/src/data/core/vnotex.json index ccbaef69..70cc8907 100644 --- a/src/data/core/vnotex.json +++ b/src/data/core/vnotex.json @@ -19,7 +19,7 @@ "OutlineDock" : "Ctrl+G, U", "SearchDock" : "", "SnippetDock" : "Ctrl+G, S", - "LocationListDock" : "Ctrl+G, L", + "LocationListDock" : "Ctrl+G, C", "Search" : "Ctrl+Alt+F", "NavigationMode" : "Ctrl+G, W", "LocateNode" : "Ctrl+G, D", @@ -46,7 +46,11 @@ "ActivateNextTab" : "Ctrl+G, N", "ActivatePreviousTab" : "Ctrl+G, P", "FocusContentArea" : "Ctrl+G, Y", - "OpenWithDefaultProgram" : "F9" + "OpenWithDefaultProgram" : "F9", + "OneSplitLeft" : "Ctrl+G, H", + "OneSplitDown" : "Ctrl+G, J", + "OneSplitUp" : "Ctrl+G, K", + "OneSplitRight" : "Ctrl+G, L" }, "toolbar_icon_size" : 16, "note_management" : { diff --git a/src/data/extra/docs/en/about_vnotex.txt b/src/data/extra/docs/en/about_vnotex.txt index 6ccba5ad..28f71978 100644 --- a/src/data/extra/docs/en/about_vnotex.txt +++ b/src/data/extra/docs/en/about_vnotex.txt @@ -1,5 +1,5 @@

-VNoteX is designed to be a pleasant note-taking platform, refactored from VNote, which is an open source note-taking application for Markdown since 2016. VNote shares most of the code base with VNoteX since version 3 and continue to be open source. +VNoteX is designed to be a pleasant note-taking platform, refactored from VNote, which is an open source note-taking application for Markdown since 2016. VNote shares most of the code base with VNoteX since version 3 and continues to be open source.

Source code of VNote could be found at GitHub.

diff --git a/src/data/extra/docs/en/shortcuts.md b/src/data/extra/docs/en/shortcuts.md index cde7d096..a9341201 100644 --- a/src/data/extra/docs/en/shortcuts.md +++ b/src/data/extra/docs/en/shortcuts.md @@ -1,6 +1,7 @@ # Shortcuts 1. All the keys without special notice are **case insensitive**; -2. On macOS, `Ctrl` corresponds to `Command` except in Vi mode. +2. On macOS, `Ctrl` corresponds to `Command` except in Vi mode; +3. For a complete shortcuts list, please view the `vnotex.json` configuration file. ## General - `Ctrl+G E` diff --git a/src/data/extra/docs/zh_CN/shortcuts.md b/src/data/extra/docs/zh_CN/shortcuts.md index aa0c70c8..2c4c6689 100644 --- a/src/data/extra/docs/zh_CN/shortcuts.md +++ b/src/data/extra/docs/zh_CN/shortcuts.md @@ -1,6 +1,7 @@ # 快捷键 1. 以下按键除特别说明外,都不区分大小写; -2. 在 macOS 下,`Ctrl`对应于`Command`,在 Vi 模式下除外。 +2. 在 macOS 下,`Ctrl`对应于`Command`,在 Vi 模式下除外; +3. 可以通过查看配置文件 `vnotex.json` 来获取一个完整的快捷键列表。 ## 通用 - `Ctrl+G E` diff --git a/src/data/extra/themes/moonlight/interface.qss b/src/data/extra/themes/moonlight/interface.qss index c8e032da..6a8bb25d 100644 --- a/src/data/extra/themes/moonlight/interface.qss +++ b/src/data/extra/themes/moonlight/interface.qss @@ -1126,6 +1126,10 @@ vnotex--ViewSplit QTabBar::tab:selected { background-color: @widgets#viewsplit#tabbar#tab#selected#bg; } +vnotex--ViewSplit QTabBar[ViewSplitFlash="true"]::tab:selected { + background-color: @widgets#viewsplit#flash#bg; +} + vte--VTextEdit { border: none; } diff --git a/src/data/extra/themes/moonlight/palette.json b/src/data/extra/themes/moonlight/palette.json index 8f5fc181..9cadfe0c 100644 --- a/src/data/extra/themes/moonlight/palette.json +++ b/src/data/extra/themes/moonlight/palette.json @@ -252,6 +252,9 @@ "bg" : "@base#content#bg" } } + }, + "flash" : { + "bg" : "@base#master#alt" } }, "qmainwindow" : { diff --git a/src/data/extra/themes/native/interface.qss b/src/data/extra/themes/native/interface.qss index 380a7eb8..cb47fda9 100644 --- a/src/data/extra/themes/native/interface.qss +++ b/src/data/extra/themes/native/interface.qss @@ -123,3 +123,7 @@ vnotex--MainWindow QLabel#MainWindowTipsLabel { font-size: 18pt; font-weight: bold; } + +vnotex--ViewSplit QTabBar[ViewSplitFlash="true"]::tab:selected { + background-color: @widgets#viewsplit#flash#bg; +} diff --git a/src/data/extra/themes/native/palette.json b/src/data/extra/themes/native/palette.json index ea0f4075..e9f2bc96 100644 --- a/src/data/extra/themes/native/palette.json +++ b/src/data/extra/themes/native/palette.json @@ -103,6 +103,9 @@ "active" : { "fg" : "@base#icon#fg" } + }, + "flash" : { + "bg" : "@base#lighter#fg" } }, "qmainwindow" : { diff --git a/src/data/extra/themes/pure/interface.qss b/src/data/extra/themes/pure/interface.qss index c8e032da..6a8bb25d 100644 --- a/src/data/extra/themes/pure/interface.qss +++ b/src/data/extra/themes/pure/interface.qss @@ -1126,6 +1126,10 @@ vnotex--ViewSplit QTabBar::tab:selected { background-color: @widgets#viewsplit#tabbar#tab#selected#bg; } +vnotex--ViewSplit QTabBar[ViewSplitFlash="true"]::tab:selected { + background-color: @widgets#viewsplit#flash#bg; +} + vte--VTextEdit { border: none; } diff --git a/src/data/extra/themes/pure/palette.json b/src/data/extra/themes/pure/palette.json index 23fa17dd..89363361 100644 --- a/src/data/extra/themes/pure/palette.json +++ b/src/data/extra/themes/pure/palette.json @@ -248,6 +248,9 @@ "bg" : "@base#content#bg" } } + }, + "flash" : { + "bg" : "@base#master#alt" } }, "qmainwindow" : { diff --git a/src/widgets/notebooknodeexplorer.cpp b/src/widgets/notebooknodeexplorer.cpp index 122ca0fa..40da1d0c 100644 --- a/src/widgets/notebooknodeexplorer.cpp +++ b/src/widgets/notebooknodeexplorer.cpp @@ -581,6 +581,11 @@ void NotebookNodeExplorer::updateNode(Node *p_node) item->setExpanded(expanded); } else { + if (m_notebook->isRecycleBinNode(p_node) && !m_recycleBinNodeVisible) { + // No need to update. + return; + } + saveNotebookTreeState(false); generateNodeTree(); diff --git a/src/widgets/propertydefs.cpp b/src/widgets/propertydefs.cpp index cc9f3d5f..71ca41f3 100644 --- a/src/widgets/propertydefs.cpp +++ b/src/widgets/propertydefs.cpp @@ -12,6 +12,8 @@ const char *PropertyDefs::c_dialogCentralWidget = "DialogCentralWidget"; const char *PropertyDefs::c_viewSplitCornerWidget = "ViewSplitCornerWidget"; +const char *PropertyDefs::c_viewSplitFlash = "ViewSplitFlash"; + const char *PropertyDefs::c_state = "State"; const char *PropertyDefs::c_viewWindowToolBar = "ViewWindowToolBar"; diff --git a/src/widgets/propertydefs.h b/src/widgets/propertydefs.h index 7e2df928..33f6ffaf 100644 --- a/src/widgets/propertydefs.h +++ b/src/widgets/propertydefs.h @@ -19,6 +19,8 @@ namespace vnotex static const char *c_viewSplitCornerWidget; + static const char *c_viewSplitFlash; + static const char *c_viewWindowToolBar; static const char *c_consoleTextEdit; diff --git a/src/widgets/toolbarhelper.cpp b/src/widgets/toolbarhelper.cpp index 534c3a36..fa79f798 100644 --- a/src/widgets/toolbarhelper.cpp +++ b/src/widgets/toolbarhelper.cpp @@ -83,9 +83,9 @@ QToolBar *ToolBarHelper::setupFileToolBar(MainWindow *p_win, QToolBar *p_toolBar emit VNoteX::getInst().importNotebookRequested(); }); - // Import notebook of VNote 2.0. + // Import notebook of VNote 2. btnMenu->addAction(generateIcon("import_notebook_of_vnote2.svg"), - MainWindow::tr("Import Legacy Notebook Of VNote 2.0"), + MainWindow::tr("Import Legacy Notebook Of VNote 2"), btnMenu, []() { emit VNoteX::getInst().importLegacyNotebookRequested(); diff --git a/src/widgets/viewarea.cpp b/src/widgets/viewarea.cpp index 6abe3a37..914fe002 100644 --- a/src/widgets/viewarea.cpp +++ b/src/widgets/viewarea.cpp @@ -12,13 +12,15 @@ #include #include #include +#include #include "viewwindow.h" #include "mainwindow.h" -#include "events.h" +#include "propertydefs.h" #include #include #include +#include #include #include #include @@ -314,7 +316,18 @@ void ViewArea::addFirstViewSplit() void ViewArea::postFirstViewSplit() { Q_ASSERT(!m_splits.isEmpty()); - setCurrentViewSplit(m_splits.first(), false); + auto currentSplit = m_splits.first(); + // Check if any split has focus. If there is any, then set it as current split. + auto focusWidget = QApplication::focusWidget(); + if (focusWidget) { + for (const auto &split : m_splits) { + if (split == focusWidget || split->isAncestorOf(focusWidget)) { + currentSplit = split; + break; + } + } + } + setCurrentViewSplit(currentSplit, false); emit viewSplitsCountChanged(); checkCurrentViewWindowChange(); @@ -372,7 +385,6 @@ void ViewArea::removeViewSplit(ViewSplit *p_split, bool p_removeWorkspace) unwrapSplitter(splitter); } } else { - Q_ASSERT(m_splits.isEmpty()); m_mainLayout->removeWidget(p_split); if (!m_splits.isEmpty()) { newCurrentSplit = m_splits.first(); @@ -427,6 +439,9 @@ void ViewArea::setCurrentViewSplit(ViewSplit *p_split, bool p_focus) { Q_ASSERT(!p_split || m_splits.contains(p_split)); if (p_split == m_currentSplit) { + if (p_split && p_focus) { + p_split->focus(); + } return; } @@ -829,6 +844,50 @@ void ViewArea::setupShortcuts() }); } } + + // OneSplitLeft. + { + auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::OneSplitLeft), this); + if (shortcut) { + connect(shortcut, &QShortcut::activated, + this, [this]() { + focusSplitByDirection(Direction::Left); + }); + } + } + + // OneSplitDown. + { + auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::OneSplitDown), this); + if (shortcut) { + connect(shortcut, &QShortcut::activated, + this, [this]() { + focusSplitByDirection(Direction::Down); + }); + } + } + + // OneSplitUp. + { + auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::OneSplitUp), this); + if (shortcut) { + connect(shortcut, &QShortcut::activated, + this, [this]() { + focusSplitByDirection(Direction::Up); + }); + } + } + + // OneSplitRight. + { + auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::OneSplitRight), this); + if (shortcut) { + connect(shortcut, &QShortcut::activated, + this, [this]() { + focusSplitByDirection(Direction::Right); + }); + } + } } bool ViewArea::close(Node *p_node, bool p_force) @@ -1232,3 +1291,95 @@ void ViewArea::openViewWindowFromSession(const ViewWindowSession &p_session) emit VNoteX::getInst().openFileRequested(p_session.m_bufferPath, paras); } + +void ViewArea::focusSplitByDirection(Direction p_direction) +{ + if (!m_currentSplit) { + return; + } + + QWidget *widget = m_currentSplit; + auto targetSplitType = SplitType::Vertical; + if (p_direction == Direction::Up || p_direction == Direction::Down) { + targetSplitType = SplitType::Horizontal; + } + int splitIdx = 0; + + QSplitter *targetSplitter = nullptr; + while (true) { + auto splitter = tryGetParentSplitter(widget); + if (!splitter) { + return; + } + + if (checkSplitType(splitter) == targetSplitType) { + targetSplitter = splitter; + splitIdx = splitter->indexOf(widget); + break; + } else { + widget = splitter; + } + } + + Q_ASSERT(targetSplitter); + switch (p_direction) { + case Direction::Left: + --splitIdx; + break; + + case Direction::Right: + ++splitIdx; + break; + + case Direction::Up: + --splitIdx; + break; + + case Direction::Down: + ++splitIdx; + break; + } + + if (splitIdx < 0 || splitIdx >= targetSplitter->count()) { + return; + } + + auto targetWidget = targetSplitter->widget(splitIdx); + // Find first split from targetWidget. + while (true) { + auto splitter = dynamic_cast(targetWidget); + if (splitter) { + if (splitter->count() == 0) { + // Should not be an empty splitter. + Q_ASSERT(false); + return; + } + targetWidget = splitter->widget(0); + } else { + auto viewSplit = dynamic_cast(targetWidget); + Q_ASSERT(viewSplit); + setCurrentViewSplit(viewSplit, true); + flashViewSplit(viewSplit); + break; + } + } +} + +ViewArea::SplitType ViewArea::checkSplitType(const QSplitter *p_splitter) const +{ + return p_splitter->orientation() == Qt::Horizontal ? SplitType::Vertical : SplitType::Horizontal; +} + +void ViewArea::flashViewSplit(ViewSplit *p_split) +{ + auto tabBar = p_split->tabBar(); + if (!tabBar) { + return; + } + + // Directly set the property of ViewSplit won't work. + WidgetUtils::setPropertyDynamically(tabBar, PropertyDefs::c_viewSplitFlash, true); + QTimer::singleShot(1000, tabBar, [tabBar]() { + WidgetUtils::setPropertyDynamically(tabBar, PropertyDefs::c_viewSplitFlash, false); + }); +} diff --git a/src/widgets/viewarea.h b/src/widgets/viewarea.h index 803bcfbe..837e95bf 100644 --- a/src/widgets/viewarea.h +++ b/src/widgets/viewarea.h @@ -144,6 +144,14 @@ namespace vnotex Horizontal }; + enum class Direction + { + Left, + Down, + Up, + Right + }; + void setupUI(); // Find given @p_buffer among all view splits. @@ -218,6 +226,12 @@ namespace vnotex void openViewWindowFromSession(const ViewWindowSession &p_session); + void focusSplitByDirection(Direction p_direction); + + SplitType checkSplitType(const QSplitter *p_splitter) const; + + void flashViewSplit(ViewSplit *p_split); + QLayout *m_mainLayout = nullptr; QWidget *m_sceneWidget = nullptr;