diff --git a/src/core/notebookmgr.cpp b/src/core/notebookmgr.cpp index 1c97d6f0..24b25948 100644 --- a/src/core/notebookmgr.cpp +++ b/src/core/notebookmgr.cpp @@ -228,6 +228,7 @@ void NotebookMgr::readNotebooksFromConfig() qCritical("failed to read notebook (%s) from config (%s)", item.m_rootFolderPath.toStdString().c_str(), p_e.what()); + m_notebooksFailedToLoad.push_back(item.m_rootFolderPath); } } @@ -386,3 +387,14 @@ QSharedPointer NotebookMgr::loadNodeByPath(const QString &p_path) return nullptr; } + +const QStringList &NotebookMgr::getNotebooksFailedToLoad() const +{ + return m_notebooksFailedToLoad; +} + +void NotebookMgr::clearNotebooksFailedToLoad() +{ + m_notebooksFailedToLoad.clear(); + saveNotebooksToConfig(); +} diff --git a/src/core/notebookmgr.h b/src/core/notebookmgr.h index c7b68fb5..bbb2f202 100644 --- a/src/core/notebookmgr.h +++ b/src/core/notebookmgr.h @@ -10,6 +10,7 @@ #include "sessionconfig.h" #include "global.h" #include "notebook/notebook.h" +#include "noncopyable.h" namespace vnotex { @@ -23,7 +24,7 @@ namespace vnotex class NotebookParameters; class Node; - class NotebookMgr : public QObject + class NotebookMgr : public QObject, private Noncopyable { Q_OBJECT public: @@ -71,6 +72,10 @@ namespace vnotex // Try to load @p_path as a node if it is within one notebook. QSharedPointer loadNodeByPath(const QString &p_path); + const QStringList &getNotebooksFailedToLoad() const; + + void clearNotebooksFailedToLoad(); + public slots: void setCurrentNotebook(ID p_notebookId); @@ -116,6 +121,8 @@ namespace vnotex QVector> m_notebooks; ID m_currentNotebookId = 0; + + QStringList m_notebooksFailedToLoad; }; } // ns vnotex diff --git a/src/data/core/core.qrc b/src/data/core/core.qrc index 285fa0e3..49002d88 100644 --- a/src/data/core/core.qrc +++ b/src/data/core/core.qrc @@ -25,7 +25,8 @@ icons/settings_menu.svg icons/whatsthis.svg icons/help_menu.svg - icons/import_export_menu.svg + icons/import_menu.svg + icons/export_menu.svg icons/flash_page_menu.svg icons/quick_access_menu.svg icons/native_notebook_default.svg diff --git a/src/data/core/icons/export_menu.svg b/src/data/core/icons/export_menu.svg new file mode 100644 index 00000000..218a052a --- /dev/null +++ b/src/data/core/icons/export_menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/data/core/icons/import_export_menu.svg b/src/data/core/icons/import_export_menu.svg deleted file mode 100644 index cfdda573..00000000 --- a/src/data/core/icons/import_export_menu.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/data/core/icons/import_menu.svg b/src/data/core/icons/import_menu.svg new file mode 100644 index 00000000..e88d9c26 --- /dev/null +++ b/src/data/core/icons/import_menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/data/extra/docs/en/shortcuts.md b/src/data/extra/docs/en/shortcuts.md index 9315a4d4..770f2f63 100644 --- a/src/data/extra/docs/en/shortcuts.md +++ b/src/data/extra/docs/en/shortcuts.md @@ -2,7 +2,7 @@ 1. All the keys without special notice are **case insensitive**; 2. On macOS, `Ctrl` corresponds to `Command` except in Vi mode; 3. The key sequence `Ctrl+G, I` means that first press both `Ctrl` and `G` simultaneously, release them, then press `I` and release; -4. For a complete shortcuts list, please view the `vnotex.json` configuration file. +4. For a **complete shortcuts list**, please view the `vnotex.json` configuration file. ## General - `Ctrl+G E` @@ -23,15 +23,11 @@ Recover last closed file. Open Flash Page. - `Ctrl+Alt+I` Open Quick Access. -- `Ctrl+G 1` -Focus the Navigation dock. -- `Ctrl+G 2` -Focus the Outline dock. -- `Ctrl+G X` +- `Ctrl+G X` Close current tab. -- `Ctrl+G D` +- `Ctrl+G D` Locate to the folder of current note. -- `Ctrl+G O` +- `Ctrl+G O` Open the Outline popup. ## Text Editor @@ -93,7 +89,7 @@ Shares the same shortcuts with Text Editor. - `Ctrl+T` Edit current note or save changes and exit edit mode. -- `Ctrl+G Q` +- `Ctrl+G Q` Discard current changes and exit edit mode. #### Text Editing diff --git a/src/data/extra/docs/zh_CN/shortcuts.md b/src/data/extra/docs/zh_CN/shortcuts.md index d712ea49..a7bffcbc 100644 --- a/src/data/extra/docs/zh_CN/shortcuts.md +++ b/src/data/extra/docs/zh_CN/shortcuts.md @@ -2,7 +2,7 @@ 1. 以下按键除特别说明外,都不区分大小写; 2. 在 macOS 下,`Ctrl` 对应于 `Command`,在 Vi 模式下除外; 3. 按键序列 `Ctrl+G, I` 表示先同时按下 `Ctrl` 和 `G`,释放,然后按下 `I` 并释放; -4. 可以通过查看配置文件 `vnotex.json` 来获取一个完整的快捷键列表。 +4. 可以通过查看配置文件 `vnotex.json` 来获取一个**完整的快捷键列表**。 ## 通用 - `Ctrl+G E` @@ -23,15 +23,11 @@ VNote 的很多部件均支持`Ctrl+J`和`Ctrl+K`导航。 打开灵犀页。 - `Ctrl+Alt+I` 打开快速访问。 -- `Ctrl+G 1` -跳转到导航停靠窗口。 -- `Ctrl+G 2` -跳转到大纲停靠窗口。 -- `Ctrl+G X` +- `Ctrl+G X` 关闭当前标签页。 -- `Ctrl+G D` +- `Ctrl+G D` 定位到当前笔记所在文件夹。 -- `Ctrl+G O` +- `Ctrl+G O` 打开大纲弹出窗口。 ## 文本编辑器 @@ -93,7 +89,7 @@ VNote 的很多部件均支持`Ctrl+J`和`Ctrl+K`导航。 - `Ctrl+T` 编辑当前笔记或者保存更改并退出编辑模式。 -- `Ctrl+G Q` +- `Ctrl+G Q` 放弃当前更改并退出编辑模式。 #### 文本编辑 diff --git a/src/data/extra/themes/moonlight/interface.qss b/src/data/extra/themes/moonlight/interface.qss index 4b873f81..f104f88d 100644 --- a/src/data/extra/themes/moonlight/interface.qss +++ b/src/data/extra/themes/moonlight/interface.qss @@ -457,8 +457,8 @@ QLineEdit:disabled { color: @widgets#qlineedit#disabled#fg; } -/* QPlainTextEdit */ -QPlainTextEdit[ConsoleTextEdit="true"] { +/* QPlainTextEdit and QTextEdit */ +QPlainTextEdit, QTextEdit { color: @widgets#qlineedit#fg; background-color: @widgets#qlineedit#bg; selection-color: @widgets#qlineedit#selection#fg; diff --git a/src/data/extra/themes/pure/interface.qss b/src/data/extra/themes/pure/interface.qss index 4b873f81..f104f88d 100644 --- a/src/data/extra/themes/pure/interface.qss +++ b/src/data/extra/themes/pure/interface.qss @@ -457,8 +457,8 @@ QLineEdit:disabled { color: @widgets#qlineedit#disabled#fg; } -/* QPlainTextEdit */ -QPlainTextEdit[ConsoleTextEdit="true"] { +/* QPlainTextEdit and QTextEdit */ +QPlainTextEdit, QTextEdit { color: @widgets#qlineedit#fg; background-color: @widgets#qlineedit#bg; selection-color: @widgets#qlineedit#selection#fg; diff --git a/src/widgets/dialogs/settings/appearancepage.cpp b/src/widgets/dialogs/settings/appearancepage.cpp index 2eb28c7d..b359e7c5 100644 --- a/src/widgets/dialogs/settings/appearancepage.cpp +++ b/src/widgets/dialogs/settings/appearancepage.cpp @@ -7,12 +7,13 @@ #include #include +#include #include #include #include #include #include -#include +#include #include using namespace vnotex; @@ -59,7 +60,7 @@ void AppearancePage::setupUI() auto layout = new QVBoxLayout(); for (int i = 0; i < docks.size(); ++i) { - m_keepDocksExpandingContentArea[i].first = WidgetsFactory::createCheckBox(docks[i]->property(MainWindow::c_propertyDockTitle).toString(), this); + m_keepDocksExpandingContentArea[i].first = WidgetsFactory::createCheckBox(docks[i]->property(PropertyDefs::c_dockWidgetTitle).toString(), this); m_keepDocksExpandingContentArea[i].second = docks[i]->objectName(); layout->addWidget(m_keepDocksExpandingContentArea[i].first); connect(m_keepDocksExpandingContentArea[i].first, &QCheckBox::stateChanged, diff --git a/src/widgets/dockwidgethelper.cpp b/src/widgets/dockwidgethelper.cpp new file mode 100644 index 00000000..c969fd28 --- /dev/null +++ b/src/widgets/dockwidgethelper.cpp @@ -0,0 +1,505 @@ +#include "dockwidgethelper.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "mainwindow.h" +#include "propertydefs.h" +#include "notebookexplorer.h" +#include "outlineviewer.h" +#include "locationlist.h" +#include "searchpanel.h" +#include "snippetpanel.h" +#include "historypanel.h" + +using namespace vnotex; + +DockWidgetHelper::NavigationItemInfo::NavigationItemInfo(QTabBar *p_tabBar, int p_tabIndex, int p_dockIndex) + : m_tabBar(p_tabBar), + m_tabIndex(p_tabIndex), + m_dockIndex(p_dockIndex) +{ +} + +DockWidgetHelper::NavigationItemInfo::NavigationItemInfo(int p_dockIndex) + : m_dockIndex(p_dockIndex) +{ +} + +DockWidgetHelper::DockWidgetHelper(MainWindow *p_mainWindow) + : QObject(p_mainWindow), + NavigationMode(NavigationMode::Type::DoubleKeys, p_mainWindow), + m_mainWindow(p_mainWindow) +{ +} + +static int rotationAngle(Qt::DockWidgetArea p_area) +{ + switch (p_area) { + case Qt::LeftDockWidgetArea: + return 90; + + case Qt::RightDockWidgetArea: + return 270; + + default: + return -1; + } +} + +QString DockWidgetHelper::iconFileName(DockIndex p_dockIndex) +{ + switch (p_dockIndex) { + case DockIndex::NavigationDock: + return "navigation_dock.svg"; + case DockIndex::OutlineDock: + return "outline_dock.svg"; + case DockIndex::HistoryDock: + return "history_dock.svg"; + case DockIndex::SearchDock: + return "search_dock.svg"; + case DockIndex::SnippetDock: + return "snippet_dock.svg"; + case DockIndex::LocationListDock: + return "location_list_dock.svg"; + default: + return QString(); + } +} + +void DockWidgetHelper::setupDocks() +{ + m_mainWindow->setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::West); + m_mainWindow->setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East); + m_mainWindow->setTabPosition(Qt::TopDockWidgetArea, QTabWidget::North); + m_mainWindow->setTabPosition(Qt::BottomDockWidgetArea, QTabWidget::North); + m_mainWindow->setDockNestingEnabled(true); + + m_dockIcons.resize(DockIndex::MaxDock); + + // The order of m_docks should be identical with enum DockIndex. + setupNavigationDock(); + + setupOutlineDock(); + + setupHistoryDock(); + + setupSearchDock(); + + setupSnippetDock(); + + for (int i = 1; i < m_docks.size(); ++i) { + m_mainWindow->tabifyDockWidget(m_docks[i - 1], m_docks[i]); + } + + // Following are non-tabfieid docks. + setupLocationListDock(); + + setupShortcuts(); +} + +void DockWidgetHelper::setupNavigationDock() +{ + auto dock = createDockWidget(DockIndex::NavigationDock, tr("Navigation"), m_mainWindow); + + dock->setObjectName(QStringLiteral("NavigationDock.vnotex")); + dock->setAllowedAreas(Qt::AllDockWidgetAreas); + + dock->setWidget(m_mainWindow->m_notebookExplorer); + dock->setFocusProxy(m_mainWindow->m_notebookExplorer); + m_mainWindow->addDockWidget(Qt::LeftDockWidgetArea, dock); +} + +void DockWidgetHelper::setupOutlineDock() +{ + auto dock = createDockWidget(DockIndex::OutlineDock, tr("Outline"), m_mainWindow); + + dock->setObjectName(QStringLiteral("OutlineDock.vnotex")); + dock->setAllowedAreas(Qt::AllDockWidgetAreas); + + dock->setWidget(m_mainWindow->m_outlineViewer); + dock->setFocusProxy(m_mainWindow->m_outlineViewer); + m_mainWindow->addDockWidget(Qt::LeftDockWidgetArea, dock); +} + +void DockWidgetHelper::setupSearchDock() +{ + auto dock = createDockWidget(DockIndex::SearchDock, tr("Search"), m_mainWindow); + + dock->setObjectName(QStringLiteral("SearchDock.vnotex")); + dock->setAllowedAreas(Qt::AllDockWidgetAreas); + + dock->setWidget(m_mainWindow->m_searchPanel); + dock->setFocusProxy(m_mainWindow->m_searchPanel); + m_mainWindow->addDockWidget(Qt::LeftDockWidgetArea, dock); +} + +void DockWidgetHelper::setupSnippetDock() +{ + auto dock = createDockWidget(DockIndex::SnippetDock, tr("Snippets"), m_mainWindow); + + dock->setObjectName(QStringLiteral("SnippetDock.vnotex")); + dock->setAllowedAreas(Qt::AllDockWidgetAreas); + + dock->setWidget(m_mainWindow->m_snippetPanel); + dock->setFocusProxy(m_mainWindow->m_snippetPanel); + m_mainWindow->addDockWidget(Qt::LeftDockWidgetArea, dock); +} + +void DockWidgetHelper::setupHistoryDock() +{ + auto dock = createDockWidget(DockIndex::HistoryDock, tr("History"), m_mainWindow); + + dock->setObjectName(QStringLiteral("HistoryDock.vnotex")); + dock->setAllowedAreas(Qt::AllDockWidgetAreas); + + dock->setWidget(m_mainWindow->m_historyPanel); + dock->setFocusProxy(m_mainWindow->m_historyPanel); + m_mainWindow->addDockWidget(Qt::LeftDockWidgetArea, dock); +} + +void DockWidgetHelper::setupLocationListDock() +{ + auto dock = createDockWidget(DockIndex::LocationListDock, tr("Location List"), m_mainWindow); + + dock->setObjectName(QStringLiteral("LocationListDock.vnotex")); + dock->setAllowedAreas(Qt::AllDockWidgetAreas); + + dock->setWidget(m_mainWindow->m_locationList); + dock->setFocusProxy(m_mainWindow->m_locationList); + m_mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dock); + dock->hide(); +} + +QDockWidget *DockWidgetHelper::createDockWidget(DockIndex p_dockIndex, const QString &p_title, QWidget *p_parent) +{ + auto dock = new QDockWidget(p_title, p_parent); + dock->setToolTip(p_title); + dock->setProperty(PropertyDefs::c_dockWidgetIndex, p_dockIndex); + dock->setProperty(PropertyDefs::c_dockWidgetTitle, p_title); + m_docks.push_back(dock); + return dock; +} + +void DockWidgetHelper::activateDock(DockIndex p_dockIndex) +{ + Q_ASSERT(p_dockIndex < DockIndex::MaxDock); + activateDock(getDock(p_dockIndex)); +} + +void DockWidgetHelper::activateDock(QDockWidget *p_dock) +{ + p_dock->show(); + Q_FOREACH(QTabBar* tabBar, m_mainWindow->findChildren(QString(), Qt::FindDirectChildrenOnly)) { + bool found = false; + for (int i = 0; i < tabBar->count(); ++i) { + if (p_dock == reinterpret_cast(tabBar->tabData(i).toULongLong())) { + tabBar->setCurrentIndex(i); + found = true; + break; + } + } + + if (found) { + break; + } + } + p_dock->setFocus(); +} + +const QVector &DockWidgetHelper::getDocks() const +{ + return m_docks; +} + +QDockWidget *DockWidgetHelper::getDock(DockIndex p_dockIndex) const +{ + Q_ASSERT(p_dockIndex < DockIndex::MaxDock); + return m_docks[p_dockIndex]; +} + +void DockWidgetHelper::setupShortcuts() +{ + const auto &coreConfig = ConfigMgr::getInst().getCoreConfig(); + + setupDockActivateShortcut(m_docks[DockIndex::NavigationDock], + coreConfig.getShortcut(CoreConfig::Shortcut::NavigationDock)); + + setupDockActivateShortcut(m_docks[DockIndex::OutlineDock], + coreConfig.getShortcut(CoreConfig::Shortcut::OutlineDock)); + + setupDockActivateShortcut(m_docks[DockIndex::HistoryDock], + coreConfig.getShortcut(CoreConfig::Shortcut::HistoryDock)); + + setupDockActivateShortcut(m_docks[DockIndex::SearchDock], + coreConfig.getShortcut(CoreConfig::Shortcut::SearchDock)); + // Extra shortcut for SearchDock. + setupDockActivateShortcut(m_docks[DockIndex::SearchDock], + coreConfig.getShortcut(CoreConfig::Shortcut::Search)); + + setupDockActivateShortcut(m_docks[DockIndex::LocationListDock], + coreConfig.getShortcut(CoreConfig::Shortcut::LocationListDock)); + + setupDockActivateShortcut(m_docks[DockIndex::SnippetDock], + coreConfig.getShortcut(CoreConfig::Shortcut::SnippetDock)); +} + +void DockWidgetHelper::setupDockActivateShortcut(QDockWidget *p_dock, const QString &p_keys) +{ + auto shortcut = WidgetUtils::createShortcut(p_keys, m_mainWindow); + if (shortcut) { + p_dock->setToolTip(QString("%1\t%2").arg(p_dock->windowTitle(), + QKeySequence(p_keys).toString(QKeySequence::NativeText))); + connect(shortcut, &QShortcut::activated, + this, [this, p_dock]() { + activateDock(p_dock); + }); + } +} + +void DockWidgetHelper::postSetup() +{ + updateDockWidgetTabBar(); + + for (const auto dock : m_docks) { + connect(dock, &QDockWidget::dockLocationChanged, + this, &DockWidgetHelper::updateDockWidgetTabBar); + connect(dock, &QDockWidget::topLevelChanged, + this, &DockWidgetHelper::updateDockWidgetTabBar); + } +} + +void DockWidgetHelper::updateDockWidgetTabBar() +{ + QBitArray tabifiedDocks(m_docks.size(), false); + Q_FOREACH(QTabBar* tabBar, m_mainWindow->findChildren(QString(), Qt::FindDirectChildrenOnly)) { + if (!m_tabBarsMonitored.contains(tabBar)) { + m_tabBarsMonitored.insert(tabBar); + tabBar->installEventFilter(this); + } + + tabBar->setDrawBase(false); + + const int sz = ConfigMgr::getInst().getCoreConfig().getDocksTabBarIconSize(); + tabBar->setIconSize(QSize(sz, sz)); + + auto tabShape = tabBar->shape(); + bool iconOnly = tabShape == QTabBar::RoundedWest || tabShape == QTabBar::RoundedEast + || tabShape == QTabBar::TriangularWest || tabShape == QTabBar::TriangularEast; + const int cnt = tabBar->count(); + if (cnt == 1) { + iconOnly = false; + } + for (int i = 0; i < cnt; ++i) { + auto dock = reinterpret_cast(tabBar->tabData(i).toULongLong()); + if (!dock) { + continue; + } + int dockIdx = dock->property(PropertyDefs::c_dockWidgetIndex).toInt(); + tabifiedDocks.setBit(dockIdx); + if (iconOnly) { + dock->setWindowTitle(QString()); + } else if (dock->windowTitle().isEmpty()) { + dock->setWindowTitle(dock->property(PropertyDefs::c_dockWidgetTitle).toString()); + } + tabBar->setTabIcon(i, getDockIcon(static_cast(dockIdx))); + } + } + + // Non-tabified docks. + for (int i = 0; i < m_docks.size(); ++i) { + if (!tabifiedDocks[i] && m_docks[i]->windowTitle().isEmpty()) { + m_docks[i]->setWindowTitle(m_docks[i]->property(PropertyDefs::c_dockWidgetTitle).toString()); + } + } + + emit m_mainWindow->layoutChanged(); +} + +bool DockWidgetHelper::eventFilter(QObject *p_obj, QEvent *p_event) +{ + if (p_event->type() == QEvent::ToolTip) { + // The QTabBar of the tabified dock widgets does not show tooltip due to Qt's internal implementation. + auto helpEve = static_cast(p_event); + auto tabBar = static_cast(p_obj); + int idx = tabBar->tabAt(helpEve->pos()); + bool done = false; + if (idx > -1) { + auto dock = reinterpret_cast(tabBar->tabData(idx).toULongLong()); + if (dock) { + done = true; + QToolTip::showText(helpEve->globalPos(), dock->property(PropertyDefs::c_dockWidgetTitle).toString()); + } + } + + if (!done) { + QToolTip::hideText(); + p_event->ignore(); + } + + return true; + } + + return QObject::eventFilter(p_obj, p_event); +} + +QStringList DockWidgetHelper::getVisibleDocks() const +{ + QStringList visibleDocks; + for (const auto dock : m_docks) { + if (dock->isVisible()) { + visibleDocks.push_back(dock->objectName()); + } + } + return visibleDocks; +} + +QStringList DockWidgetHelper::hideDocks() +{ + const auto &keepDocks = ConfigMgr::getInst().getWidgetConfig().getMainWindowKeepDocksExpandingContentArea(); + QStringList visibleDocks; + for (const auto dock : m_docks) { + const auto objName = dock->objectName(); + if (dock->isVisible()) { + visibleDocks.push_back(objName); + } + + if (dock->isFloating() || keepDocks.contains(objName)) { + continue; + } + + dock->setVisible(false); + } + + return visibleDocks; +} + +void DockWidgetHelper::restoreDocks(const QStringList &p_visibleDocks) +{ + const auto &keepDocks = ConfigMgr::getInst().getWidgetConfig().getMainWindowKeepDocksExpandingContentArea(); + bool hasVisible = false; + for (const auto dock : m_docks) { + const auto objName = dock->objectName(); + if (dock->isFloating() || keepDocks.contains(objName)) { + continue; + } + + const bool visible = p_visibleDocks.contains(objName); + hasVisible = hasVisible || visible; + + dock->setVisible(visible); + } + + if (!hasVisible) { + // At least make one visible. + getDock(DockIndex::NavigationDock)->setVisible(true); + } +} + +bool DockWidgetHelper::isAnyDockVisible() const +{ + const auto &keepDocks = ConfigMgr::getInst().getWidgetConfig().getMainWindowKeepDocksExpandingContentArea(); + for (const auto dock : m_docks) { + if (!dock->isFloating() && dock->isVisible() && !keepDocks.contains(dock->objectName())) { + return true; + } + } + return false; +} + +QVector DockWidgetHelper::getVisibleNavigationItems() +{ + m_navigationItems.clear(); + + QBitArray tabifiedDocks(m_docks.size(), false); + Q_FOREACH(QTabBar* tabBar, m_mainWindow->findChildren(QString(), Qt::FindDirectChildrenOnly)) { + if (!tabBar->isVisible()) { + continue; + } + + const int cnt = tabBar->count(); + for (int i = 0; i < cnt; ++i) { + auto dock = reinterpret_cast(tabBar->tabData(i).toULongLong()); + if (!dock) { + continue; + } + int dockIdx = dock->property(PropertyDefs::c_dockWidgetIndex).toInt(); + tabifiedDocks.setBit(dockIdx); + + m_navigationItems.push_back(NavigationItemInfo(tabBar, i, dockIdx)); + } + } + + // Non-tabified docks. + for (int i = 0; i < m_docks.size(); ++i) { + if (!tabifiedDocks[i] && m_docks[i]->isVisible()) { + m_navigationItems.push_back(NavigationItemInfo(i)); + } + } + + QVector items; + for (auto &item : m_navigationItems) { + items.push_back(&item); + } + return items; +} + +const QIcon &DockWidgetHelper::getDockIcon(DockIndex p_dockIndex) +{ + static const auto fg = VNoteX::getInst().getThemeMgr().paletteColor("widgets#mainwindow#dockwidget_tabbar#icon#fg"); + static const auto selectedFg = VNoteX::getInst().getThemeMgr().paletteColor("widgets#mainwindow#dockwidget_tabbar#icon#selected#fg"); + + const auto area = m_mainWindow->dockWidgetArea(m_docks[p_dockIndex]); + const int newAngle = rotationAngle(area); + if (m_dockIcons[p_dockIndex].m_rotationAngle != newAngle && area != Qt::NoDockWidgetArea) { + QVector colors; + colors.push_back(IconUtils::OverriddenColor(fg, QIcon::Normal)); + // FIXME: the Selected Mode is not used by the selected tab of a QTabBar. + colors.push_back(IconUtils::OverriddenColor(selectedFg, QIcon::Selected)); + + auto iconFile = VNoteX::getInst().getThemeMgr().getIconFile(iconFileName(p_dockIndex)); + m_dockIcons[p_dockIndex].m_icon = IconUtils::fetchIcon(iconFile, colors, newAngle); + m_dockIcons[p_dockIndex].m_rotationAngle = newAngle; + } + + return m_dockIcons[p_dockIndex].m_icon; +} + +void DockWidgetHelper::placeNavigationLabel(int p_idx, void *p_item, QLabel *p_label) +{ + Q_UNUSED(p_idx); + auto info = static_cast(p_item); + if (info->m_tabBar) { + auto pos = info->m_tabBar->tabRect(info->m_tabIndex).topLeft(); + pos = info->m_tabBar->mapToGlobal(pos); + p_label->move(m_mainWindow->mapFromGlobal(pos)); + } else { + p_label->setParent(m_docks[info->m_dockIndex]); + p_label->move(0, 0); + } +} + +void DockWidgetHelper::handleTargetHit(void *p_item) +{ + auto info = static_cast(p_item); + activateDock(static_cast(info->m_dockIndex)); +} + +void DockWidgetHelper::clearNavigation() +{ + NavigationMode::clearNavigation(); + + m_navigationItems.clear(); +} diff --git a/src/widgets/dockwidgethelper.h b/src/widgets/dockwidgethelper.h new file mode 100644 index 00000000..abfffba2 --- /dev/null +++ b/src/widgets/dockwidgethelper.h @@ -0,0 +1,132 @@ +#ifndef DOCKWIDGETHELPER_H +#define DOCKWIDGETHELPER_H + +#include +#include +#include +#include + +#include "navigationmode.h" + +class QDockWidget; +class QTabBar; + +namespace vnotex +{ + class MainWindow; + + // Dock widget helper for MainWindow. + class DockWidgetHelper : public QObject, public NavigationMode + { + Q_OBJECT + public: + // Index in m_docks. + enum DockIndex + { + NavigationDock = 0, + OutlineDock, + HistoryDock, + SearchDock, + SnippetDock, + LocationListDock, + MaxDock + }; + Q_ENUM(DockIndex) + + explicit DockWidgetHelper(MainWindow *p_mainWindow); + + void setupDocks(); + + void postSetup(); + + void activateDock(DockIndex p_dockIndex); + + QDockWidget *getDock(DockIndex p_dockIndex) const; + + const QVector &getDocks() const; + + void updateDockWidgetTabBar(); + + QStringList getVisibleDocks() const; + + QStringList hideDocks(); + + void restoreDocks(const QStringList &p_visibleDocks); + + // If there is any dock that does not belong to keep docks visible. + bool isAnyDockVisible() const; + + // NavigationMode. + protected: + QVector getVisibleNavigationItems() Q_DECL_OVERRIDE; + + void placeNavigationLabel(int p_idx, void *p_item, QLabel *p_label) Q_DECL_OVERRIDE; + + void handleTargetHit(void *p_item) Q_DECL_OVERRIDE; + + void clearNavigation() Q_DECL_OVERRIDE; + + protected: + bool eventFilter(QObject *p_obj, QEvent *p_event) Q_DECL_OVERRIDE; + + private: + struct NavigationItemInfo + { + NavigationItemInfo() = default; + + NavigationItemInfo(QTabBar *p_tabBar, int p_tabIndex, int p_dockIndex); + + NavigationItemInfo(int p_dockIndex); + + QTabBar *m_tabBar = nullptr; + + int m_tabIndex = -1; + + int m_dockIndex = -1; + }; + + struct IconInfo + { + QIcon m_icon; + + int m_rotationAngle = INT_MIN; + }; + + void setupNavigationDock(); + + void setupOutlineDock(); + + void setupSearchDock(); + + void setupSnippetDock(); + + void setupHistoryDock(); + + void setupLocationListDock(); + + QDockWidget *createDockWidget(DockIndex p_dockIndex, const QString &p_title, QWidget *p_parent); + + void setupShortcuts(); + + void activateDock(QDockWidget *p_dock); + + void setupDockActivateShortcut(QDockWidget *p_dock, const QString &p_keys); + + const QIcon &getDockIcon(DockIndex p_dockIndex); + + static QString iconFileName(DockIndex p_dockIndex); + + MainWindow *m_mainWindow = nullptr; + + QVector m_dockIcons; + + QVector m_docks; + + // We need to install event filter to the tabbar of tabified dock widgets. + QSet m_tabBarsMonitored; + + QVector m_navigationItems; + }; +} + +#endif // DOCKWIDGETHELPER_H diff --git a/src/widgets/mainwindow.cpp b/src/widgets/mainwindow.cpp index 54ed9104..73d5efbb 100644 --- a/src/widgets/mainwindow.cpp +++ b/src/widgets/mainwindow.cpp @@ -18,9 +18,6 @@ #include #include #include -#include -#include -#include #include "toolbox.h" #include "notebookexplorer.h" @@ -57,11 +54,11 @@ using namespace vnotex; -const char *MainWindow::c_propertyDockIndex = "DockIndex"; -const char *MainWindow::c_propertyDockTitle = "DockTitle"; - MainWindow::MainWindow(QWidget *p_parent) - : QMainWindow(p_parent) + : QMainWindow(p_parent), + m_toolBarHelper(this), + m_statusBarHelper(this), + m_dockWidgetHelper(this) { VNoteX::getInst().setMainWindow(this); @@ -73,14 +70,7 @@ MainWindow::MainWindow(QWidget *p_parent) loadStateAndGeometry(); - { - updateDockWidgetTabBar(); - - for (auto dock : m_docks) { - connect(dock, &QDockWidget::visibilityChanged, - this, &MainWindow::updateDockWidgetTabBar); - } - } + m_dockWidgetHelper.postSetup(); // The signal is particularly useful if your application has to do some last-second cleanup. // Note that no user interaction is possible in this state. @@ -113,6 +103,8 @@ void MainWindow::kickOffOnStart(const QStringList &p_paths) emit layoutChanged(); + checkNotebooksFailedToLoad(); + demoWidget(); openFiles(p_paths); @@ -148,12 +140,12 @@ void MainWindow::setupUI() setupTipsArea(); setupSystemTray(); - activateDock(m_docks[DockIndex::NavigationDock]); + m_dockWidgetHelper.activateDock(DockWidgetHelper::NavigationDock); } void MainWindow::setupStatusBar() { - m_statusBarHelper.setupStatusBar(this); + m_statusBarHelper.setupStatusBar(); connect(&VNoteX::getInst(), &VNoteX::statusMessageRequested, statusBar(), &QStatusBar::showMessage); } @@ -213,12 +205,12 @@ void MainWindow::setupCentralWidget() }); { - auto notebookMgr = &VNoteX::getInst().getNotebookMgr(); - connect(notebookMgr, &NotebookMgr::notebookAboutToClose, + auto ¬ebookMgr = VNoteX::getInst().getNotebookMgr(); + connect(¬ebookMgr, &NotebookMgr::notebookAboutToClose, this, [this](const Notebook *p_notebook) { m_viewArea->close(p_notebook, true); }); - connect(notebookMgr, &NotebookMgr::notebookAboutToRemove, + connect(¬ebookMgr, &NotebookMgr::notebookAboutToRemove, this, [this](const Notebook *p_notebook) { m_viewArea->close(p_notebook, true); }); @@ -229,120 +221,21 @@ void MainWindow::setupCentralWidget() void MainWindow::setupDocks() { - setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::West); - setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East); - setTabPosition(Qt::TopDockWidgetArea, QTabWidget::North); - setTabPosition(Qt::BottomDockWidgetArea, QTabWidget::North); - setDockNestingEnabled(true); - - // Init docks icon. - { - m_dockIcons.resize(DockIndex::MaxDock); - - const auto &themeMgr = VNoteX::getInst().getThemeMgr(); - const auto fg = themeMgr.paletteColor("widgets#mainwindow#dockwidget_tabbar#icon#fg"); - const auto selectedFg = themeMgr.paletteColor("widgets#mainwindow#dockwidget_tabbar#icon#selected#fg"); - - QVector colors; - colors.push_back(IconUtils::OverriddenColor(fg, QIcon::Normal)); - // FIXME: the Selected Mode is not used by the selected tab of a QTabBar. - colors.push_back(IconUtils::OverriddenColor(selectedFg, QIcon::Selected)); - - auto iconFile = themeMgr.getIconFile("navigation_dock.svg"); - m_dockIcons[DockIndex::NavigationDock] = IconUtils::fetchIcon(iconFile, colors, 90); - - iconFile = themeMgr.getIconFile("outline_dock.svg"); - m_dockIcons[DockIndex::OutlineDock] = IconUtils::fetchIcon(iconFile, colors, 90); - - iconFile = themeMgr.getIconFile("history_dock.svg"); - m_dockIcons[DockIndex::HistoryDock] = IconUtils::fetchIcon(iconFile, colors, 90); - - iconFile = themeMgr.getIconFile("search_dock.svg"); - m_dockIcons[DockIndex::SearchDock] = IconUtils::fetchIcon(iconFile, colors, 90); - - iconFile = themeMgr.getIconFile("snippet_dock.svg"); - m_dockIcons[DockIndex::SnippetDock] = IconUtils::fetchIcon(iconFile, colors, 90); - - iconFile = themeMgr.getIconFile("location_list_dock.svg"); - m_dockIcons[DockIndex::LocationListDock] = IconUtils::fetchIcon(iconFile, colors, 90); - } - - // The order of m_docks should be identical with enum DockIndex. - setupNavigationDock(); - - setupOutlineDock(); - - setupHistoryDock(); - - setupSearchDock(); - - setupSnippetDock(); - - for (int i = 1; i < m_docks.size(); ++i) { - tabifyDockWidget(m_docks[i - 1], m_docks[i]); - } - - // Following are non-tabfieid docks. - setupLocationListDock(); -} - -void MainWindow::activateDock(QDockWidget *p_dock) -{ - p_dock->show(); - Q_FOREACH(QTabBar* tabBar, this->findChildren(QString(), Qt::FindDirectChildrenOnly)) { - bool found = false; - for (int i = 0; i < tabBar->count(); ++i) { - if (p_dock == reinterpret_cast(tabBar->tabData(i).toULongLong())) { - tabBar->setCurrentIndex(i); - found = true; - break; - } - } - - if (found) { - break; - } - } - p_dock->setFocus(); -} - -void MainWindow::setupNavigationDock() -{ - auto dock = createDockWidget(DockIndex::NavigationDock, tr("Navigation"), this); - - dock->setObjectName(QStringLiteral("NavigationDock.vnotex")); - dock->setAllowedAreas(Qt::AllDockWidgetAreas); - setupNotebookExplorer(this); - dock->setWidget(m_notebookExplorer); - dock->setFocusProxy(m_notebookExplorer); - addDockWidget(Qt::LeftDockWidgetArea, dock); -} - -void MainWindow::setupOutlineDock() -{ - auto dock = createDockWidget(DockIndex::OutlineDock, tr("Outline"), this); - - dock->setObjectName(QStringLiteral("OutlineDock.vnotex")); - dock->setAllowedAreas(Qt::AllDockWidgetAreas); setupOutlineViewer(); - dock->setWidget(m_outlineViewer); - dock->setFocusProxy(m_outlineViewer); - addDockWidget(Qt::LeftDockWidgetArea, dock); -} -void MainWindow::setupSearchDock() -{ - auto dock = createDockWidget(DockIndex::SearchDock, tr("Search"), this); - - dock->setObjectName(QStringLiteral("SearchDock.vnotex")); - dock->setAllowedAreas(Qt::AllDockWidgetAreas); + setupHistoryPanel(); setupSearchPanel(); - dock->setWidget(m_searchPanel); - dock->setFocusProxy(m_searchPanel); - addDockWidget(Qt::LeftDockWidgetArea, dock); + + setupSnippetPanel(); + + setupLocationList(); + + m_dockWidgetHelper.setupDocks(); + + NavigationModeMgr::getInst().registerNavigationTarget(&m_dockWidgetHelper); } void MainWindow::setupSearchPanel() @@ -355,19 +248,6 @@ void MainWindow::setupSearchPanel() m_searchPanel->setObjectName("SearchPanel.vnotex"); } -void MainWindow::setupSnippetDock() -{ - auto dock = createDockWidget(DockIndex::SnippetDock, tr("Snippets"), this); - - dock->setObjectName(QStringLiteral("SnippetDock.vnotex")); - dock->setAllowedAreas(Qt::AllDockWidgetAreas); - - setupSnippetPanel(); - dock->setWidget(m_snippetPanel); - dock->setFocusProxy(m_snippetPanel); - addDockWidget(Qt::LeftDockWidgetArea, dock); -} - void MainWindow::setupSnippetPanel() { m_snippetPanel = new SnippetPanel(this); @@ -382,39 +262,12 @@ void MainWindow::setupSnippetPanel() }); } -void MainWindow::setupHistoryDock() -{ - auto dock = createDockWidget(DockIndex::HistoryDock, tr("History"), this); - - dock->setObjectName(QStringLiteral("HistoryDock.vnotex")); - dock->setAllowedAreas(Qt::AllDockWidgetAreas); - - setupHistoryPanel(); - dock->setWidget(m_historyPanel); - dock->setFocusProxy(m_historyPanel); - addDockWidget(Qt::LeftDockWidgetArea, dock); -} - void MainWindow::setupHistoryPanel() { m_historyPanel = new HistoryPanel(this); m_historyPanel->setObjectName("HistoryPanel.vnotex"); } -void MainWindow::setupLocationListDock() -{ - auto dock = createDockWidget(DockIndex::LocationListDock, tr("Location List"), this); - - dock->setObjectName(QStringLiteral("LocationListDock.vnotex")); - dock->setAllowedAreas(Qt::AllDockWidgetAreas); - - setupLocationList(); - dock->setWidget(m_locationList); - dock->setFocusProxy(m_locationList); - addDockWidget(Qt::BottomDockWidgetArea, dock); - dock->hide(); -} - void MainWindow::setupLocationList() { m_locationList = new LocationList(this); @@ -444,19 +297,19 @@ void MainWindow::setupNotebookExplorer(QWidget *p_parent) m_notebookExplorer, &NotebookExplorer::importLegacyNotebook); connect(&VNoteX::getInst(), &VNoteX::locateNodeRequested, this, [this](Node *p_node) { - activateDock(m_docks[DockIndex::NavigationDock]); + m_dockWidgetHelper.activateDock(DockWidgetHelper::NavigationDock); m_notebookExplorer->locateNode(p_node); }); - auto notebookMgr = &VNoteX::getInst().getNotebookMgr(); - connect(notebookMgr, &NotebookMgr::notebooksUpdated, + auto ¬ebookMgr = VNoteX::getInst().getNotebookMgr(); + connect(¬ebookMgr, &NotebookMgr::notebooksUpdated, m_notebookExplorer, &NotebookExplorer::loadNotebooks); - connect(notebookMgr, &NotebookMgr::notebookUpdated, + connect(¬ebookMgr, &NotebookMgr::notebookUpdated, m_notebookExplorer, &NotebookExplorer::reloadNotebook); - connect(notebookMgr, &NotebookMgr::currentNotebookChanged, + connect(¬ebookMgr, &NotebookMgr::currentNotebookChanged, m_notebookExplorer, &NotebookExplorer::setCurrentNotebook); connect(m_notebookExplorer, &NotebookExplorer::notebookActivated, - notebookMgr, &NotebookMgr::setCurrentNotebook); + ¬ebookMgr, &NotebookMgr::setCurrentNotebook); } void MainWindow::closeEvent(QCloseEvent *p_event) @@ -550,11 +403,7 @@ void MainWindow::loadStateAndGeometry(bool p_stateOnly) m_visibleDocksBeforeExpand = sg.m_visibleDocksBeforeExpand; if (m_visibleDocksBeforeExpand.isEmpty()) { // Init (or init again if there is no visible dock). - for (int i = 0; i < m_docks.size(); ++i) { - if (m_docks[i]->isVisible()) { - m_visibleDocksBeforeExpand.push_back(m_docks[i]->objectName()); - } - } + m_visibleDocksBeforeExpand = m_dockWidgetHelper.getVisibleDocks(); } } } @@ -573,54 +422,18 @@ void MainWindow::resetStateAndGeometry() void MainWindow::setContentAreaExpanded(bool p_expanded) { - const auto &keepDocks = ConfigMgr::getInst().getWidgetConfig().getMainWindowKeepDocksExpandingContentArea(); - if (p_expanded) { // Store the state and hide. - m_visibleDocksBeforeExpand.clear(); - for (int i = 0; i < m_docks.size(); ++i) { - const auto objName = m_docks[i]->objectName(); - if (m_docks[i]->isVisible()) { - m_visibleDocksBeforeExpand.push_back(objName); - } - - if (m_docks[i]->isFloating() || keepDocks.contains(objName)) { - continue; - } - - m_docks[i]->setVisible(false); - } + m_visibleDocksBeforeExpand = m_dockWidgetHelper.hideDocks(); } else { // Restore the state. - bool hasVisible = false; - for (int i = 0; i < m_docks.size(); ++i) { - const auto objName = m_docks[i]->objectName(); - if (m_docks[i]->isFloating() || keepDocks.contains(objName)) { - continue; - } - - const bool visible = m_visibleDocksBeforeExpand.contains(objName); - hasVisible = hasVisible || visible; - - m_docks[i]->setVisible(visible); - } - - if (!hasVisible) { - // At least make one visible. - m_docks[DockIndex::NavigationDock]->setVisible(true); - } + m_dockWidgetHelper.restoreDocks(m_visibleDocksBeforeExpand); } } bool MainWindow::isContentAreaExpanded() const { - const auto &keepDocks = ConfigMgr::getInst().getWidgetConfig().getMainWindowKeepDocksExpandingContentArea(); - for (auto dock : m_docks) { - if (!dock->isFloating() && dock->isVisible() && !keepDocks.contains(dock->objectName())) { - return false; - } - } - return true; + return !m_dockWidgetHelper.isAnyDockVisible(); } void MainWindow::demoWidget() @@ -656,7 +469,7 @@ void MainWindow::setupOutlineViewer() const QVector &MainWindow::getDocks() const { - return m_docks; + return m_dockWidgetHelper.getDocks(); } void MainWindow::focusViewArea() @@ -680,7 +493,7 @@ void MainWindow::setupToolBar() auto toolBar = new TitleToolBar(tr("Global"), this); toolBar->setIconSize(iconSize); - m_toolBarHelper.setupToolBars(this, toolBar); + m_toolBarHelper.setupToolBars(toolBar); toolBar->addTitleBarIcons(ToolBarHelper::generateIcon(QStringLiteral("minimize.svg")), ToolBarHelper::generateIcon(QStringLiteral("maximize.svg")), ToolBarHelper::generateIcon(QStringLiteral("maximize_restore.svg")), @@ -688,7 +501,7 @@ void MainWindow::setupToolBar() } else { auto toolBar = new QToolBar(tr("Global"), this); toolBar->setIconSize(iconSize); - m_toolBarHelper.setupToolBars(this, toolBar); + m_toolBarHelper.setupToolBars(toolBar); } // Disable the context menu above tool bar. @@ -703,41 +516,6 @@ void MainWindow::closeOnQuit() void MainWindow::setupShortcuts() { - const auto &coreConfig = ConfigMgr::getInst().getCoreConfig(); - - setupDockActivateShortcut(m_docks[DockIndex::NavigationDock], - coreConfig.getShortcut(CoreConfig::Shortcut::NavigationDock)); - - setupDockActivateShortcut(m_docks[DockIndex::OutlineDock], - coreConfig.getShortcut(CoreConfig::Shortcut::OutlineDock)); - - setupDockActivateShortcut(m_docks[DockIndex::HistoryDock], - coreConfig.getShortcut(CoreConfig::Shortcut::HistoryDock)); - - setupDockActivateShortcut(m_docks[DockIndex::SearchDock], - coreConfig.getShortcut(CoreConfig::Shortcut::SearchDock)); - // Extra shortcut for SearchDock. - setupDockActivateShortcut(m_docks[DockIndex::SearchDock], - coreConfig.getShortcut(CoreConfig::Shortcut::Search)); - - setupDockActivateShortcut(m_docks[DockIndex::LocationListDock], - coreConfig.getShortcut(CoreConfig::Shortcut::LocationListDock)); - - setupDockActivateShortcut(m_docks[DockIndex::SnippetDock], - coreConfig.getShortcut(CoreConfig::Shortcut::SnippetDock)); -} - -void MainWindow::setupDockActivateShortcut(QDockWidget *p_dock, const QString &p_keys) -{ - auto shortcut = WidgetUtils::createShortcut(p_keys, this); - if (shortcut) { - p_dock->setToolTip(QString("%1\t%2").arg(p_dock->windowTitle(), - QKeySequence(p_keys).toString(QKeySequence::NativeText))); - connect(shortcut, &QShortcut::activated, - this, [this, p_dock]() { - activateDock(p_dock); - }); - } } void MainWindow::setStayOnTop(bool p_enabled) @@ -806,50 +584,7 @@ void MainWindow::quitApp() void MainWindow::updateDockWidgetTabBar() { - QBitArray tabifiedDocks(m_docks.size(), false); - Q_FOREACH(QTabBar* tabBar, this->findChildren(QString(), Qt::FindDirectChildrenOnly)) { - if (!m_tabBarsMonitored.contains(tabBar)) { - m_tabBarsMonitored.insert(tabBar); - tabBar->installEventFilter(this); - } - - tabBar->setDrawBase(false); - - const int sz = ConfigMgr::getInst().getCoreConfig().getDocksTabBarIconSize(); - tabBar->setIconSize(QSize(sz, sz)); - - auto tabShape = tabBar->shape(); - bool iconOnly = tabShape == QTabBar::RoundedWest || tabShape == QTabBar::RoundedEast - || tabShape == QTabBar::TriangularWest || tabShape == QTabBar::TriangularEast; - const int cnt = tabBar->count(); - if (cnt == 1) { - iconOnly = false; - } - for (int i = 0; i < cnt; ++i) { - auto dock = reinterpret_cast(tabBar->tabData(i).toULongLong()); - if (!dock) { - continue; - } - int dockIdx = dock->property(c_propertyDockIndex).toInt(); - tabifiedDocks.setBit(dockIdx); - if (iconOnly) { - dock->setWindowTitle(QString()); - tabBar->setTabIcon(i, m_dockIcons[dockIdx]); - } else if (dock->windowTitle().isEmpty()) { - dock->setWindowTitle(dock->property(c_propertyDockTitle).toString()); - tabBar->setTabIcon(i, QIcon()); - } - } - } - - // Non-tabified docks. - for (int i = 0; i < m_docks.size(); ++i) { - if (!tabifiedDocks[i] && m_docks[i]->windowTitle().isEmpty()) { - m_docks[i]->setWindowTitle(m_docks[i]->property(c_propertyDockTitle).toString()); - } - } - - emit layoutChanged(); + m_dockWidgetHelper.updateDockWidgetTabBar(); } void MainWindow::exportNotes() @@ -926,15 +661,15 @@ LocationList *MainWindow::getLocationList() const void MainWindow::setLocationListVisible(bool p_visible) { if (p_visible) { - activateDock(m_docks[DockIndex::LocationListDock]); + m_dockWidgetHelper.activateDock(DockWidgetHelper::LocationListDock); } else { - m_docks[DockIndex::LocationListDock]->hide(); + m_dockWidgetHelper.getDock(DockWidgetHelper::LocationListDock)->hide(); } } void MainWindow::toggleLocationListVisible() { - bool visible = m_docks[DockIndex::LocationListDock]->isVisible(); + bool visible = m_dockWidgetHelper.getDock(DockWidgetHelper::LocationListDock)->isVisible(); setLocationListVisible(!visible); } @@ -945,16 +680,6 @@ void MainWindow::setupSpellCheck() QStringList() << configMgr.getUserDictsFolder() << configMgr.getAppDictsFolder()); } -QDockWidget *MainWindow::createDockWidget(DockIndex p_dockIndex, const QString &p_title, QWidget *p_parent) -{ - auto dock = new QDockWidget(p_title, p_parent); - dock->setToolTip(p_title); - dock->setProperty(c_propertyDockIndex, p_dockIndex); - dock->setProperty(c_propertyDockTitle, p_title); - m_docks.push_back(dock); - return dock; -} - void MainWindow::checkForUpdates() { Updater::checkForUpdates(this, [this](bool p_hasUpdate, const QString &p_version, const QString &p_errMsg) { @@ -966,29 +691,22 @@ void MainWindow::checkForUpdates() }); } -bool MainWindow::eventFilter(QObject *p_obj, QEvent *p_event) +void MainWindow::checkNotebooksFailedToLoad() { - if (p_event->type() == QEvent::ToolTip) { - // The QTabBar of the tabified dock widgets does not show tooltip due to Qt's internal implementation. - auto helpEve = static_cast(p_event); - auto tabBar = static_cast(p_obj); - int idx = tabBar->tabAt(helpEve->pos()); - bool done = false; - if (idx > -1) { - auto dock = reinterpret_cast(tabBar->tabData(idx).toULongLong()); - if (dock) { - done = true; - QToolTip::showText(helpEve->globalPos(), dock->property(c_propertyDockTitle).toString()); - } - } - - if (!done) { - QToolTip::hideText(); - p_event->ignore(); - } - - return true; + auto ¬ebookMgr = VNoteX::getInst().getNotebookMgr(); + const auto ¬ebooks = notebookMgr.getNotebooksFailedToLoad(); + if (notebooks.isEmpty()) { + return; } - return QMainWindow::eventFilter(p_obj, p_event); + int ret = MessageBoxHelper::questionYesNo(MessageBoxHelper::Warning, + tr("Failed to load %n notebook(s).", "", notebooks.size()), + tr("These notebooks may be moved or deleted. It is recommended to remove " + "them from configuration and open them with the correct root folder path later.\n" + "Remove them from the configuration?"), + notebooks.join(QLatin1Char('\n')), + this); + if (ret == QMessageBox::Yes) { + notebookMgr.clearNotebooksFailedToLoad(); + } } diff --git a/src/widgets/mainwindow.h b/src/widgets/mainwindow.h index b18bfa76..da20e7c2 100644 --- a/src/widgets/mainwindow.h +++ b/src/widgets/mainwindow.h @@ -7,6 +7,7 @@ #include #include "toolbarhelper.h" +#include "dockwidgethelper.h" #include "statusbarhelper.h" class QDockWidget; @@ -32,6 +33,8 @@ namespace vnotex { Q_OBJECT public: + friend class DockWidgetHelper; + explicit MainWindow(QWidget *p_parent = nullptr); ~MainWindow(); @@ -69,10 +72,6 @@ namespace vnotex void updateDockWidgetTabBar(); - static const char *c_propertyDockIndex; - - static const char *c_propertyDockTitle; - signals: void mainWindowStarted(); @@ -89,8 +88,6 @@ namespace vnotex void changeEvent(QEvent *p_event) Q_DECL_OVERRIDE; - bool eventFilter(QObject *p_obj, QEvent *p_event) Q_DECL_OVERRIDE; - private slots: void closeOnQuit(); @@ -99,42 +96,18 @@ namespace vnotex void showTips(const QString &p_message, int p_timeoutMilliseconds); private: - // Index in m_docks. - enum DockIndex - { - NavigationDock = 0, - OutlineDock, - HistoryDock, - SearchDock, - SnippetDock, - LocationListDock, - MaxDock - }; - void setupUI(); void setupCentralWidget(); void setupOutlineViewer(); - void setupNavigationDock(); - - void setupOutlineDock(); - - void setupSearchDock(); - void setupSearchPanel(); - void setupLocationListDock(); - void setupLocationList(); - void setupSnippetDock(); - void setupSnippetPanel(); - void setupHistoryDock(); - void setupHistoryPanel(); void setupNotebookExplorer(QWidget *p_parent = nullptr); @@ -156,8 +129,6 @@ namespace vnotex QString getViewAreaTitle() const; - void activateDock(QDockWidget *p_dock); - void setupToolBar(); void setupShortcuts(); @@ -166,18 +137,18 @@ namespace vnotex void setTipsAreaVisible(bool p_visible); - void setupDockActivateShortcut(QDockWidget *p_dock, const QString &p_keys); - void setupSpellCheck(); - QDockWidget *createDockWidget(DockIndex p_dockIndex, const QString &p_title, QWidget *p_parent); - void checkForUpdates(); + void checkNotebooksFailedToLoad(); + ToolBarHelper m_toolBarHelper; StatusBarHelper m_statusBarHelper; + DockWidgetHelper m_dockWidgetHelper; + ToolBox *m_navigationToolBox = nullptr; NotebookExplorer *m_notebookExplorer = nullptr; @@ -196,10 +167,6 @@ namespace vnotex HistoryPanel *m_historyPanel = nullptr; - QVector m_docks; - - QVector m_dockIcons; - bool m_layoutReset = false; // -1: do not request to quit; @@ -215,9 +182,6 @@ namespace vnotex QTimer *m_tipsTimer = nullptr; QStringList m_visibleDocksBeforeExpand; - - // We need to install event filter to the tabbar of tabified dock widgets. - QSet m_tabBarsMonitored; }; } // ns vnotex diff --git a/src/widgets/navigationmode.h b/src/widgets/navigationmode.h index 805042c5..b5b6e071 100644 --- a/src/widgets/navigationmode.h +++ b/src/widgets/navigationmode.h @@ -54,7 +54,7 @@ namespace vnotex virtual QVector getVisibleNavigationItems(); - // @p_idx: -1 for SingleKey and the major stage of StagedDoubleKeys. + // @p_idx: will be -1 for SingleKey case and the major stage of StagedDoubleKeys case. virtual void placeNavigationLabel(int p_idx, void * p_item, QLabel *p_label) = 0; virtual void showNavigationWithDoubleKeys(); diff --git a/src/widgets/propertydefs.cpp b/src/widgets/propertydefs.cpp index 71ca41f3..1c96ba2a 100644 --- a/src/widgets/propertydefs.cpp +++ b/src/widgets/propertydefs.cpp @@ -21,3 +21,7 @@ const char *PropertyDefs::c_viewWindowToolBar = "ViewWindowToolBar"; const char *PropertyDefs::c_consoleTextEdit = "ConsoleTextEdit"; const char *PropertyDefs::c_embeddedLineEdit = "EmbeddedLineEdit"; + +const char *PropertyDefs::c_dockWidgetIndex = "DockIndex"; + +const char *PropertyDefs::c_dockWidgetTitle = "DockTitle"; diff --git a/src/widgets/propertydefs.h b/src/widgets/propertydefs.h index 33f6ffaf..0e5d2e38 100644 --- a/src/widgets/propertydefs.h +++ b/src/widgets/propertydefs.h @@ -29,6 +29,10 @@ namespace vnotex // Values: info/warning/error. static const char *c_state; + + static const char *c_dockWidgetIndex; + + static const char *c_dockWidgetTitle; }; } diff --git a/src/widgets/statusbarhelper.cpp b/src/widgets/statusbarhelper.cpp index fc10a4bc..7e639f0f 100644 --- a/src/widgets/statusbarhelper.cpp +++ b/src/widgets/statusbarhelper.cpp @@ -7,8 +7,13 @@ using namespace vnotex; -void StatusBarHelper::setupStatusBar(MainWindow *p_win) +StatusBarHelper::StatusBarHelper(MainWindow *p_mainWindow) + : m_mainWindow(p_mainWindow) { - m_statusBar = new QStatusBar(p_win); - p_win->setStatusBar(m_statusBar); +} + +void StatusBarHelper::setupStatusBar() +{ + m_statusBar = new QStatusBar(m_mainWindow); + m_mainWindow->setStatusBar(m_statusBar); } diff --git a/src/widgets/statusbarhelper.h b/src/widgets/statusbarhelper.h index 7bf65a34..083fd122 100644 --- a/src/widgets/statusbarhelper.h +++ b/src/widgets/statusbarhelper.h @@ -11,13 +11,13 @@ namespace vnotex class StatusBarHelper { public: - StatusBarHelper() - { - } + explicit StatusBarHelper(MainWindow *p_mainWindow); - void setupStatusBar(MainWindow *p_win); + void setupStatusBar(); private: + MainWindow *m_mainWindow = nullptr; + QStatusBar *m_statusBar; }; } // ns vnotex diff --git a/src/widgets/toolbarhelper.cpp b/src/widgets/toolbarhelper.cpp index 20154926..2af23ab2 100644 --- a/src/widgets/toolbarhelper.cpp +++ b/src/widgets/toolbarhelper.cpp @@ -11,7 +11,7 @@ #include #include "mainwindow.h" -#include "vnotex.h" +#include #include "widgetsfactory.h" #include #include @@ -29,6 +29,11 @@ using namespace vnotex; +ToolBarHelper::ToolBarHelper(MainWindow *p_mainWindow) + : m_mainWindow(p_mainWindow) +{ +} + static QToolBar *createToolBar(MainWindow *p_win, const QString &p_title, const QString &p_name) { auto tb = p_win->addToolBar(p_title); @@ -147,12 +152,13 @@ QToolBar *ToolBarHelper::setupFileToolBar(MainWindow *p_win, QToolBar *p_toolBar tb->addWidget(newBtn); } - // Import and export. + // Import. { - auto act = tb->addAction(generateIcon("import_export_menu.svg"), MainWindow::tr("Import/Export")); + auto act = tb->addAction(generateIcon("import_menu.svg"), MainWindow::tr("Import")); auto btn = dynamic_cast(tb->widgetForAction(act)); Q_ASSERT(btn); + btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); btn->setPopupMode(QToolButton::InstantPopup); btn->setProperty(PropertyDefs::c_toolButtonWithoutMenuIndicator, true); @@ -172,16 +178,24 @@ QToolBar *ToolBarHelper::setupFileToolBar(MainWindow *p_win, QToolBar *p_toolBar []() { emit VNoteX::getInst().importFolderRequested(); }); + } - newMenu->addSeparator(); + // Export. + { + auto exportAct = tb->addAction(generateIcon("export_menu.svg"), + MainWindow::tr("Export (Convert Format)"), + []() { + emit VNoteX::getInst().exportRequested(); + }); - auto exportAct = newMenu->addAction(MainWindow::tr("Export (Convert Format)"), - newMenu, - []() { - emit VNoteX::getInst().exportRequested(); - }); WidgetUtils::addActionShortcut(exportAct, coreConfig.getShortcut(CoreConfig::Shortcut::Export)); + + // To hide the shortcut text shown in button. + auto toolBtn = dynamic_cast(tb->widgetForAction(exportAct)); + Q_ASSERT(toolBtn); + toolBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toolBtn->setText(MainWindow::tr("Export")); } return tb; @@ -579,31 +593,31 @@ QIcon ToolBarHelper::generateDangerousIcon(const QString &p_iconName) return IconUtils::fetchIcon(iconFile, colors); } -void ToolBarHelper::setupToolBars(MainWindow *p_win) +void ToolBarHelper::setupToolBars() { m_toolBars.clear(); - auto fileTab = setupFileToolBar(p_win, nullptr); + auto fileTab = setupFileToolBar(m_mainWindow, nullptr); m_toolBars.insert(fileTab->objectName(), fileTab); - auto quickAccessTb = setupQuickAccessToolBar(p_win, nullptr); + auto quickAccessTb = setupQuickAccessToolBar(m_mainWindow, nullptr); m_toolBars.insert(quickAccessTb->objectName(), quickAccessTb); - auto settingsToolBar = setupSettingsToolBar(p_win, nullptr); + auto settingsToolBar = setupSettingsToolBar(m_mainWindow, nullptr); m_toolBars.insert(settingsToolBar->objectName(), settingsToolBar); } -void ToolBarHelper::setupToolBars(MainWindow *p_win, QToolBar *p_toolBar) +void ToolBarHelper::setupToolBars(QToolBar *p_toolBar) { m_toolBars.clear(); p_toolBar->setObjectName(QStringLiteral("UnifiedToolBar")); p_toolBar->setMovable(false); - p_win->addToolBar(p_toolBar); + m_mainWindow->addToolBar(p_toolBar); - setupFileToolBar(p_win, p_toolBar); - setupQuickAccessToolBar(p_win, p_toolBar); - setupSettingsToolBar(p_win, p_toolBar); + setupFileToolBar(m_mainWindow, p_toolBar); + setupQuickAccessToolBar(m_mainWindow, p_toolBar); + setupSettingsToolBar(m_mainWindow, p_toolBar); m_toolBars.insert(p_toolBar->objectName(), p_toolBar); } diff --git a/src/widgets/toolbarhelper.h b/src/widgets/toolbarhelper.h index c49744e2..291ca04f 100644 --- a/src/widgets/toolbarhelper.h +++ b/src/widgets/toolbarhelper.h @@ -14,11 +14,13 @@ namespace vnotex class ToolBarHelper { public: + explicit ToolBarHelper(MainWindow *p_mainWindow); + // Setup all tool bars of main window. - void setupToolBars(MainWindow *p_win); + void setupToolBars(); // Setup tool bars of main window into one unified tool bar. - void setupToolBars(MainWindow *p_win, QToolBar *p_toolBar); + void setupToolBars(QToolBar *p_toolBar); static QIcon generateIcon(const QString &p_iconName); @@ -33,6 +35,8 @@ namespace vnotex static QToolBar *setupSettingsToolBar(MainWindow *p_win, QToolBar *p_toolBar); + MainWindow *m_mainWindow = nullptr; + QHash m_toolBars; }; } // ns vnotex diff --git a/src/widgets/widgets.pri b/src/widgets/widgets.pri index 114e02d8..5dfa076b 100644 --- a/src/widgets/widgets.pri +++ b/src/widgets/widgets.pri @@ -35,6 +35,7 @@ SOURCES += \ $$PWD/dialogs/sortdialog.cpp \ $$PWD/dialogs/tableinsertdialog.cpp \ $$PWD/dialogs/updater.cpp \ + $$PWD/dockwidgethelper.cpp \ $$PWD/dragdropareaindicator.cpp \ $$PWD/editors/editormarkdownvieweradapter.cpp \ $$PWD/editors/graphhelper.cpp \ @@ -151,6 +152,7 @@ HEADERS += \ $$PWD/dialogs/sortdialog.h \ $$PWD/dialogs/tableinsertdialog.h \ $$PWD/dialogs/updater.h \ + $$PWD/dockwidgethelper.h \ $$PWD/dragdropareaindicator.h \ $$PWD/editors/editormarkdownvieweradapter.h \ $$PWD/editors/graphhelper.h \