mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 22:09:52 +08:00
534 lines
18 KiB
C++
534 lines
18 KiB
C++
#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"
|
|
#include "tagexplorer.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::TagDock:
|
|
return "tag_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.
|
|
QVector<int> tabifiedDockIndex;
|
|
|
|
tabifiedDockIndex.append(m_docks.size());
|
|
setupNavigationDock();
|
|
|
|
tabifiedDockIndex.append(m_docks.size());
|
|
setupHistoryDock();
|
|
|
|
tabifiedDockIndex.append(m_docks.size());
|
|
setupTagDock();
|
|
|
|
tabifiedDockIndex.append(m_docks.size());
|
|
setupSearchDock();
|
|
|
|
tabifiedDockIndex.append(m_docks.size());
|
|
setupSnippetDock();
|
|
|
|
setupOutlineDock();
|
|
|
|
setupLocationListDock();
|
|
|
|
setupShortcuts();
|
|
|
|
for (int i = 1; i < tabifiedDockIndex.size(); ++i) {
|
|
m_mainWindow->tabifyDockWidget(m_docks[tabifiedDockIndex[i - 1]], m_docks[tabifiedDockIndex[i]]);
|
|
}
|
|
}
|
|
|
|
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::RightDockWidgetArea, 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::setupTagDock()
|
|
{
|
|
auto dock = createDockWidget(DockIndex::TagDock, tr("Tags"), m_mainWindow);
|
|
|
|
dock->setObjectName(QStringLiteral("TagDock.vnotex"));
|
|
dock->setAllowedAreas(Qt::AllDockWidgetAreas);
|
|
|
|
dock->setWidget(m_mainWindow->m_tagExplorer);
|
|
dock->setFocusProxy(m_mainWindow->m_tagExplorer);
|
|
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::TagDock],
|
|
coreConfig.getShortcut(CoreConfig::Shortcut::TagDock));
|
|
|
|
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);
|
|
}
|
|
|
|
updateDockWidgetTabBar();
|
|
}
|
|
|
|
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();
|
|
}
|