MainWindow: support Navigation mode for dock widgets

This commit is contained in:
Le Tan 2021-09-02 20:15:54 +08:00
parent d8635e957c
commit 8973f40020
23 changed files with 799 additions and 433 deletions

View File

@ -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<Node> NotebookMgr::loadNodeByPath(const QString &p_path)
return nullptr;
}
const QStringList &NotebookMgr::getNotebooksFailedToLoad() const
{
return m_notebooksFailedToLoad;
}
void NotebookMgr::clearNotebooksFailedToLoad()
{
m_notebooksFailedToLoad.clear();
saveNotebooksToConfig();
}

View File

@ -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<Node> 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<QSharedPointer<Notebook>> m_notebooks;
ID m_currentNotebookId = 0;
QStringList m_notebooksFailedToLoad;
};
} // ns vnotex

View File

@ -25,7 +25,8 @@
<file>icons/settings_menu.svg</file>
<file>icons/whatsthis.svg</file>
<file>icons/help_menu.svg</file>
<file>icons/import_export_menu.svg</file>
<file>icons/import_menu.svg</file>
<file>icons/export_menu.svg</file>
<file>icons/flash_page_menu.svg</file>
<file>icons/quick_access_menu.svg</file>
<file>icons/native_notebook_default.svg</file>

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1630411117653" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4795" width="512" height="512" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M840.533333 938.666667H183.466667C128 938.666667 85.333333 896 85.333333 840.533333V183.466667C85.333333 128 128 85.333333 183.466667 85.333333h354.133333c12.8 0 21.333333 8.533333 21.333333 21.333334s-8.533333 21.333333-21.333333 21.333333H183.466667C151.466667 128 128 151.466667 128 183.466667v659.2C128 872.533333 151.466667 896 183.466667 896h659.2c29.866667 0 55.466667-23.466667 55.466666-55.466667V490.666667c0-12.8 8.533333-21.333333 21.333334-21.333334s21.333333 8.533333 21.333333 21.333334v349.866666C938.666667 896 896 938.666667 840.533333 938.666667z" p-id="4796" fill="#000000"></path><path d="M405.333333 640h-4.266666c-8.533333-2.133333-17.066667-10.666667-17.066667-19.2-8.533333-153.6 25.6-270.933333 102.4-349.866667 70.4-74.666667 157.866667-91.733333 196.266667-98.133333V106.666667c0-8.533333 4.266667-17.066667 12.8-19.2 8.533333-2.133333 17.066667-2.133333 23.466666 4.266666l213.333334 192c4.266667 4.266667 6.4 10.666667 6.4 14.933334s-2.133333 12.8-6.4 14.933333l-213.333334 192c-6.4 6.4-14.933333 6.4-23.466666 4.266667-8.533333-2.133333-12.8-10.666667-12.8-19.2v-85.333334c-42.666667 2.133333-153.6 25.6-258.133334 224-4.266667 6.4-10.666667 10.666667-19.2 10.666667zM725.333333 155.733333V192c0 12.8-8.533333 21.333333-21.333333 21.333333 0 0-108.8 2.133333-187.733333 85.333334-53.333333 57.6-83.2 136.533333-89.6 241.066666 134.4-202.666667 275.2-177.066667 281.6-177.066666 10.666667 2.133333 17.066667 10.666667 17.066666 21.333333v59.733333l160-145.066666L725.333333 155.733333z" p-id="4797" fill="#000000"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1607931217887" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1738" width="512" height="512" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M344.352 403.072l-160.8 0 243.296 243.264 243.296-243.264-157.376 0c8.832-338.976-299.168-376.736-491.424-198.88 130.528-72.096 329.664-84.192 323.04 198.88l0 0zM937.76 66.24l-524.896 0c26.976 13.92 51.232 31.904 72.512 53.76 27.872 28.576 49.536 55.456 68.128 120.448l395.04 0 0 701.088-827.104 0 0-683.936c0 0-16.768-20.128-52.96-2.496l0 680.448c0 35.904 29.088 64.864 64.928 64.864l804.384 0c35.872 0 64.896-28.96 64.896-64.864l0-804.384c0-35.84-29.024-64.928-64.928-64.928l0 0z" p-id="1739" fill="#000000"></path></svg>

Before

Width:  |  Height:  |  Size: 900 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1630411129695" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5009" width="512" height="512" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M840.533333 938.666667H183.466667C128 938.666667 85.333333 896 85.333333 840.533333V405.333333c0-12.8 8.533333-21.333333 21.333334-21.333333s21.333333 8.533333 21.333333 21.333333v435.2C128 872.533333 151.466667 896 183.466667 896h659.2c29.866667 0 55.466667-23.466667 55.466666-55.466667V183.466667C896 151.466667 872.533333 128 840.533333 128H529.066667c-12.8 0-21.333333-8.533333-21.333334-21.333333s8.533333-21.333333 21.333334-21.333334h311.466666C896 85.333333 938.666667 128 938.666667 183.466667v659.2c0 53.333333-42.666667 96-98.133334 96z" p-id="5010" fill="#000000"></path><path d="M426.666667 640c-6.4 0-12.8-2.133333-14.933334-6.4l-192-213.333333c-6.4-6.4-6.4-14.933333-4.266666-23.466667s10.666667-12.8 19.2-12.8h87.466666c-2.133333-42.666667-25.6-153.6-224-258.133333-10.666667-4.266667-14.933333-12.8-12.8-23.466667 2.133333-8.533333 10.666667-14.933333 21.333334-17.066667 153.6-8.533333 270.933333 25.6 349.866666 102.4 74.666667 70.4 91.733333 157.866667 98.133334 196.266667h64c8.533333 0 17.066667 4.266667 19.2 12.8 4.266667 8.533333 2.133333 17.066667-4.266667 23.466667l-192 213.333333c-2.133333 4.266667-8.533333 6.4-14.933333 6.4z m-145.066667-213.333333l145.066667 160 145.066666-160H533.333333c-12.8 0-21.333333-8.533333-21.333333-21.333334 0 0-2.133333-108.8-85.333333-187.733333C369.066667 164.266667 290.133333 134.4 185.6 128c202.666667 134.4 177.066667 275.2 177.066667 281.6-2.133333 10.666667-10.666667 17.066667-21.333334 17.066667h-59.733333z" p-id="5011" fill="#000000"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -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

View File

@ -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`
放弃当前更改并退出编辑模式。
#### 文本编辑

View File

@ -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;

View File

@ -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;

View File

@ -7,12 +7,13 @@
#include <QDockWidget>
#include <widgets/widgetsfactory.h>
#include <widgets/mainwindow.h>
#include <core/sessionconfig.h>
#include <core/coreconfig.h>
#include <core/widgetconfig.h>
#include <core/configmgr.h>
#include <utils/widgetutils.h>
#include <widgets/mainwindow.h>
#include <widgets/propertydefs.h>
#include <core/vnotex.h>
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,

View File

@ -0,0 +1,505 @@
#include "dockwidgethelper.h"
#include <QDockWidget>
#include <QTabBar>
#include <QBitArray>
#include <QHelpEvent>
#include <QToolTip>
#include <QShortcut>
#include <core/vnotex.h>
#include <core/thememgr.h>
#include <core/configmgr.h>
#include <core/coreconfig.h>
#include <core/widgetconfig.h>
#include <utils/iconutils.h>
#include <utils/widgetutils.h>
#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<QTabBar*>(QString(), Qt::FindDirectChildrenOnly)) {
bool found = false;
for (int i = 0; i < tabBar->count(); ++i) {
if (p_dock == reinterpret_cast<QWidget *>(tabBar->tabData(i).toULongLong())) {
tabBar->setCurrentIndex(i);
found = true;
break;
}
}
if (found) {
break;
}
}
p_dock->setFocus();
}
const QVector<QDockWidget *> &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<QTabBar*>(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<QDockWidget *>(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<DockIndex>(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<QHelpEvent *>(p_event);
auto tabBar = static_cast<QTabBar *>(p_obj);
int idx = tabBar->tabAt(helpEve->pos());
bool done = false;
if (idx > -1) {
auto dock = reinterpret_cast<QDockWidget *>(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<void *> DockWidgetHelper::getVisibleNavigationItems()
{
m_navigationItems.clear();
QBitArray tabifiedDocks(m_docks.size(), false);
Q_FOREACH(QTabBar* tabBar, m_mainWindow->findChildren<QTabBar*>(QString(), Qt::FindDirectChildrenOnly)) {
if (!tabBar->isVisible()) {
continue;
}
const int cnt = tabBar->count();
for (int i = 0; i < cnt; ++i) {
auto dock = reinterpret_cast<QDockWidget *>(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<void *> 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<IconUtils::OverriddenColor> 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<NavigationItemInfo *>(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<NavigationItemInfo *>(p_item);
activateDock(static_cast<DockIndex>(info->m_dockIndex));
}
void DockWidgetHelper::clearNavigation()
{
NavigationMode::clearNavigation();
m_navigationItems.clear();
}

View File

@ -0,0 +1,132 @@
#ifndef DOCKWIDGETHELPER_H
#define DOCKWIDGETHELPER_H
#include <QVector>
#include <QIcon>
#include <QSet>
#include <QPair>
#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<QDockWidget *> &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<void *> 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<IconInfo> m_dockIcons;
QVector<QDockWidget *> m_docks;
// We need to install event filter to the tabbar of tabified dock widgets.
QSet<QTabBar *> m_tabBarsMonitored;
QVector<NavigationItemInfo> m_navigationItems;
};
}
#endif // DOCKWIDGETHELPER_H

View File

@ -18,9 +18,6 @@
#include <QSystemTrayIcon>
#include <QWindowStateChangeEvent>
#include <QTimer>
#include <QBitArray>
#include <QHelpEvent>
#include <QToolTip>
#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 &notebookMgr = VNoteX::getInst().getNotebookMgr();
connect(&notebookMgr, &NotebookMgr::notebookAboutToClose,
this, [this](const Notebook *p_notebook) {
m_viewArea->close(p_notebook, true);
});
connect(notebookMgr, &NotebookMgr::notebookAboutToRemove,
connect(&notebookMgr, &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<IconUtils::OverriddenColor> 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<QTabBar*>(QString(), Qt::FindDirectChildrenOnly)) {
bool found = false;
for (int i = 0; i < tabBar->count(); ++i) {
if (p_dock == reinterpret_cast<QWidget *>(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 &notebookMgr = VNoteX::getInst().getNotebookMgr();
connect(&notebookMgr, &NotebookMgr::notebooksUpdated,
m_notebookExplorer, &NotebookExplorer::loadNotebooks);
connect(notebookMgr, &NotebookMgr::notebookUpdated,
connect(&notebookMgr, &NotebookMgr::notebookUpdated,
m_notebookExplorer, &NotebookExplorer::reloadNotebook);
connect(notebookMgr, &NotebookMgr::currentNotebookChanged,
connect(&notebookMgr, &NotebookMgr::currentNotebookChanged,
m_notebookExplorer, &NotebookExplorer::setCurrentNotebook);
connect(m_notebookExplorer, &NotebookExplorer::notebookActivated,
notebookMgr, &NotebookMgr::setCurrentNotebook);
&notebookMgr, &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<QDockWidget *> &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<QTabBar*>(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<QDockWidget *>(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<QHelpEvent *>(p_event);
auto tabBar = static_cast<QTabBar *>(p_obj);
int idx = tabBar->tabAt(helpEve->pos());
bool done = false;
if (idx > -1) {
auto dock = reinterpret_cast<QDockWidget *>(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 &notebookMgr = VNoteX::getInst().getNotebookMgr();
const auto &notebooks = 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();
}
}

View File

@ -7,6 +7,7 @@
#include <QSet>
#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<QDockWidget *> m_docks;
QVector<QIcon> 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<QTabBar *> m_tabBarsMonitored;
};
} // ns vnotex

View File

@ -54,7 +54,7 @@ namespace vnotex
virtual QVector<void *> 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();

View File

@ -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";

View File

@ -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;
};
}

View File

@ -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);
}

View File

@ -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

View File

@ -11,7 +11,7 @@
#include <QFileDialog>
#include "mainwindow.h"
#include "vnotex.h"
#include <core/vnotex.h>
#include "widgetsfactory.h"
#include <utils/iconutils.h>
#include <utils/widgetutils.h>
@ -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<QToolButton *>(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<QToolButton *>(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);
}

View File

@ -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<QString, QToolBar *> m_toolBars;
};
} // ns vnotex

View File

@ -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 \