diff --git a/src/core/coreconfig.cpp b/src/core/coreconfig.cpp index ecba94e1..b7271ca8 100644 --- a/src/core/coreconfig.cpp +++ b/src/core/coreconfig.cpp @@ -48,6 +48,11 @@ void CoreConfig::init(const QJsonObject &p_app, m_toolBarIconSize = 16; } + m_docksTabBarIconSize = READINT(QStringLiteral("docks_tabbar_icon_size")); + if (m_docksTabBarIconSize <= 0) { + m_docksTabBarIconSize = 20; + } + loadNoteManagement(appObj, userObj); m_recoverLastSessionOnStartEnabled = READBOOL(QStringLiteral("recover_last_session_on_start")); @@ -65,6 +70,7 @@ QJsonObject CoreConfig::toJson() const obj[QStringLiteral("locale")] = m_locale; obj[QStringLiteral("shortcuts")] = saveShortcuts(); obj[QStringLiteral("toolbar_icon_size")] = m_toolBarIconSize; + obj[QStringLiteral("docks_tabbar_icon_size")] = m_docksTabBarIconSize; obj[QStringLiteral("recover_last_session_on_start")] = m_recoverLastSessionOnStartEnabled; obj[QStringLiteral("history_max_count")] = m_historyMaxCount; return obj; @@ -154,6 +160,17 @@ void CoreConfig::setToolBarIconSize(int p_size) updateConfig(m_toolBarIconSize, p_size, this); } +int CoreConfig::getDocksTabBarIconSize() const +{ + return m_docksTabBarIconSize; +} + +void CoreConfig::setDocksTabBarIconSize(int p_size) +{ + Q_ASSERT(p_size > 0); + updateConfig(m_docksTabBarIconSize, p_size, this); +} + const QStringList &CoreConfig::getExternalNodeExcludePatterns() const { return m_externalNodeExcludePatterns; diff --git a/src/core/coreconfig.h b/src/core/coreconfig.h index 41e81161..d1d03d2a 100644 --- a/src/core/coreconfig.h +++ b/src/core/coreconfig.h @@ -87,6 +87,9 @@ namespace vnotex int getToolBarIconSize() const; void setToolBarIconSize(int p_size); + int getDocksTabBarIconSize() const; + void setDocksTabBarIconSize(int p_size); + const QStringList &getExternalNodeExcludePatterns() const; static const QStringList &getAvailableLocales(); @@ -117,6 +120,9 @@ namespace vnotex // Icon size of MainWindow tool bar. int m_toolBarIconSize = 16; + // Icon size of MainWindow QDockWidgets tab bar. + int m_docksTabBarIconSize = 20; + QStringList m_externalNodeExcludePatterns; // Whether recover last session on start. diff --git a/src/data/core/core.qrc b/src/data/core/core.qrc index 74756f66..285fa0e3 100644 --- a/src/data/core/core.qrc +++ b/src/data/core/core.qrc @@ -15,7 +15,6 @@ icons/read_editor.svg icons/expand.svg icons/fullscreen.svg - icons/history_explorer.svg icons/tag_explorer.svg icons/help.svg icons/menu.svg @@ -81,6 +80,12 @@ icons/cancel.svg icons/section_number_editor.svg icons/sort.svg + icons/navigation_dock.svg + icons/history_dock.svg + icons/outline_dock.svg + icons/search_dock.svg + icons/snippet_dock.svg + icons/location_list_dock.svg logo/vnote.svg logo/vnote.png logo/256x256/vnote.png diff --git a/src/data/core/icons/history_explorer.svg b/src/data/core/icons/history_dock.svg similarity index 100% rename from src/data/core/icons/history_explorer.svg rename to src/data/core/icons/history_dock.svg diff --git a/src/data/core/icons/location_list_dock.svg b/src/data/core/icons/location_list_dock.svg new file mode 100644 index 00000000..5b303164 --- /dev/null +++ b/src/data/core/icons/location_list_dock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/data/core/icons/navigation_dock.svg b/src/data/core/icons/navigation_dock.svg new file mode 100644 index 00000000..6557edbe --- /dev/null +++ b/src/data/core/icons/navigation_dock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/data/core/icons/outline_dock.svg b/src/data/core/icons/outline_dock.svg new file mode 100644 index 00000000..7bf1d5b2 --- /dev/null +++ b/src/data/core/icons/outline_dock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/data/core/icons/search_dock.svg b/src/data/core/icons/search_dock.svg new file mode 100644 index 00000000..0927bfb2 --- /dev/null +++ b/src/data/core/icons/search_dock.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/data/core/icons/snippet_dock.svg b/src/data/core/icons/snippet_dock.svg new file mode 100644 index 00000000..339038eb --- /dev/null +++ b/src/data/core/icons/snippet_dock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/data/core/vnotex.json b/src/data/core/vnotex.json index 9d56db6d..eca78743 100644 --- a/src/data/core/vnotex.json +++ b/src/data/core/vnotex.json @@ -59,6 +59,7 @@ "OpenLastClosedFile" : "Ctrl+Shift+T" }, "toolbar_icon_size" : 16, + "docks_tabbar_icon_size" : 20, "note_management" : { "external_node" : { "//comment" : "Wildcard patterns of files and folders to exclude as external files", diff --git a/src/data/extra/themes/moonlight/interface.qss b/src/data/extra/themes/moonlight/interface.qss index f5b5641a..a02ec2e0 100644 --- a/src/data/extra/themes/moonlight/interface.qss +++ b/src/data/extra/themes/moonlight/interface.qss @@ -511,6 +511,22 @@ QTabBar::tab:left { min-width: 20px; } +/* Tabified QDockWidget */ +QMainWindow > QTabBar::tab:right { + border-right: 3px solid transparent; + border-bottom: none; + margin: 0px; + padding: -10px 2px 5px 2px; +} + +/* Tabified QDockWidget */ +QMainWindow > QTabBar::tab:left { + border-left: 3px solid transparent; + border-bottom: none; + margin: 0px; + padding: -10px 2px 5px 2px; +} + QTabBar::tab:hover { color: @widgets#qtabbar#tab#hover#fg; background-color: @widgets#qtabbar#tab#hover#bg; diff --git a/src/data/extra/themes/moonlight/palette.json b/src/data/extra/themes/moonlight/palette.json index 26994e32..5cb570c8 100644 --- a/src/data/extra/themes/moonlight/palette.json +++ b/src/data/extra/themes/moonlight/palette.json @@ -164,6 +164,9 @@ }, "danger" : { "fg": "@base#danger#fg" + }, + "selected" : { + "fg" : "@base#master#bg" } } }, @@ -627,6 +630,16 @@ "fg" : "@base#master#fg", "bg" : "@base#master#bg" } + }, + "mainwindow" : { + "dockwidget_tabbar" : { + "icon" : { + "fg" : "@base#icon#fg", + "selected" : { + "fg" : "@base#icon#selected#fg" + } + } + } } } } diff --git a/src/data/extra/themes/native/palette.json b/src/data/extra/themes/native/palette.json index a3f545de..cdd94883 100644 --- a/src/data/extra/themes/native/palette.json +++ b/src/data/extra/themes/native/palette.json @@ -43,6 +43,9 @@ }, "danger" : { "fg": "@base#danger#fg" + }, + "selected" : { + "fg": "@base#info#fg" } }, "master" : { @@ -140,6 +143,16 @@ "fg" : "@base#master#fg", "bg" : "@base#master#bg" } + }, + "mainwindow" : { + "dockwidget_tabbar" : { + "icon" : { + "fg" : "@base#icon#fg", + "selected" : { + "fg" : "@base#icon#selected#fg" + } + } + } } } } diff --git a/src/data/extra/themes/pure/interface.qss b/src/data/extra/themes/pure/interface.qss index f5b5641a..a02ec2e0 100644 --- a/src/data/extra/themes/pure/interface.qss +++ b/src/data/extra/themes/pure/interface.qss @@ -511,6 +511,22 @@ QTabBar::tab:left { min-width: 20px; } +/* Tabified QDockWidget */ +QMainWindow > QTabBar::tab:right { + border-right: 3px solid transparent; + border-bottom: none; + margin: 0px; + padding: -10px 2px 5px 2px; +} + +/* Tabified QDockWidget */ +QMainWindow > QTabBar::tab:left { + border-left: 3px solid transparent; + border-bottom: none; + margin: 0px; + padding: -10px 2px 5px 2px; +} + QTabBar::tab:hover { color: @widgets#qtabbar#tab#hover#fg; background-color: @widgets#qtabbar#tab#hover#bg; diff --git a/src/data/extra/themes/pure/palette.json b/src/data/extra/themes/pure/palette.json index 5b370a7f..006761b1 100644 --- a/src/data/extra/themes/pure/palette.json +++ b/src/data/extra/themes/pure/palette.json @@ -160,6 +160,9 @@ }, "danger" : { "fg": "@base#danger#fg" + }, + "selected" : { + "fg" : "@base#master#bg" } } }, @@ -623,6 +626,16 @@ "fg" : "@base#master#fg", "bg" : "@base#master#bg" } + }, + "mainwindow" : { + "dockwidget_tabbar" : { + "icon" : { + "fg" : "@base#icon#fg", + "selected" : { + "fg" : "@base#icon#selected#fg" + } + } + } } } } diff --git a/src/utils/iconutils.cpp b/src/utils/iconutils.cpp index 82256eba..cc871d7d 100644 --- a/src/utils/iconutils.cpp +++ b/src/utils/iconutils.cpp @@ -15,7 +15,8 @@ QString IconUtils::s_defaultIconForeground; QString IconUtils::s_defaultIconDisabledForeground; QIcon IconUtils::fetchIcon(const QString &p_iconFile, - const QVector &p_overriddenColors) + const QVector &p_overriddenColors, + qreal p_angle) { const auto suffix = QFileInfo(p_iconFile).suffix().toLower().toStdString(); if (p_overriddenColors.isEmpty() || suffix != "svg") { @@ -33,6 +34,9 @@ QIcon IconUtils::fetchIcon(const QString &p_iconFile, auto data = overriddenContent.toLocal8Bit(); QPixmap pixmap; pixmap.loadFromData(data, suffix.c_str()); + if (p_angle > 0) { + pixmap = pixmap.transformed(QTransform().rotate(p_angle)); + } icon.addPixmap(pixmap, color.m_mode, color.m_state); } diff --git a/src/utils/iconutils.h b/src/utils/iconutils.h index ef4e93ed..cf951915 100644 --- a/src/utils/iconutils.h +++ b/src/utils/iconutils.h @@ -37,7 +37,8 @@ namespace vnotex static void setDefaultIconForeground(const QString &p_fg, const QString &p_disabledFg); static QIcon fetchIcon(const QString &p_iconFile, - const QVector &p_overriddenColors); + const QVector &p_overriddenColors, + qreal p_angle = -1); static QIcon fetchIcon(const QString &p_iconFile, const QString &p_overriddenForeground); diff --git a/src/widgets/dialogs/dialog.cpp b/src/widgets/dialogs/dialog.cpp index c3609bfc..a28524f3 100644 --- a/src/widgets/dialogs/dialog.cpp +++ b/src/widgets/dialogs/dialog.cpp @@ -176,3 +176,9 @@ bool Dialog::isCompleted() const { return m_completed; } + +QSize Dialog::sizeHint() const +{ + auto sz = QDialog::sizeHint(); + return sz * 1.2; +} diff --git a/src/widgets/dialogs/dialog.h b/src/widgets/dialogs/dialog.h index 423c2f5b..5f07d0a4 100644 --- a/src/widgets/dialogs/dialog.h +++ b/src/widgets/dialogs/dialog.h @@ -40,6 +40,8 @@ namespace vnotex bool isCompleted() const; + QSize sizeHint() const Q_DECL_OVERRIDE; + protected: virtual void acceptedButtonClicked(); diff --git a/src/widgets/dialogs/importfolderutils.cpp b/src/widgets/dialogs/importfolderutils.cpp index d5b766ed..96056b19 100644 --- a/src/widgets/dialogs/importfolderutils.cpp +++ b/src/widgets/dialogs/importfolderutils.cpp @@ -48,7 +48,13 @@ void ImportFolderUtils::importFolderContentsByLegacyConfig(Notebook *p_notebook, { auto rootDir = p_node->toDir(); - const auto config = LegacyNotebookUtils::getFolderConfig(rootDir.absolutePath()); + QJsonObject config; + try { + config = LegacyNotebookUtils::getFolderConfig(rootDir.absolutePath()); + } catch (Exception &p_e) { + Utils::appendMsg(p_errMsg, ImportFolderUtilsTranslate::tr("Failed to read folder config (%1).").arg(rootDir.absolutePath())); + return; + } // Remove the config file. LegacyNotebookUtils::removeFolderConfigFile(rootDir.absolutePath()); diff --git a/src/widgets/dialogs/importlegacynotebookdialog.cpp b/src/widgets/dialogs/importlegacynotebookdialog.cpp index 1e6d5f4d..0a459bfb 100644 --- a/src/widgets/dialogs/importlegacynotebookdialog.cpp +++ b/src/widgets/dialogs/importlegacynotebookdialog.cpp @@ -19,7 +19,7 @@ using namespace vnotex; ImportLegacyNotebookDialog::ImportLegacyNotebookDialog(QWidget *p_parent) : NewNotebookDialog(p_parent) { - setWindowTitle(tr("Import Legacy Notebook")); + setWindowTitle(tr("Open Legacy Notebook")); m_infoWidget->setMode(NotebookInfoWidget::Mode::CreateFromLegacy); m_infoWidget->getRootFolderPathLineEdit()->setFocus(); @@ -34,8 +34,8 @@ void ImportLegacyNotebookDialog::acceptedButtonClicked() // Warn user about the transformation. int ret = MessageBoxHelper::questionOkCancel(MessageBoxHelper::Warning, - tr("Once imported, the legacy notebook could no longer be recognized by legacy VNote!"), - tr("This operation is irreversible. Please make sure the new VNote already meets all your needs before continue."), + tr("Once opened, the legacy notebook could no longer be recognized by legacy VNote!"), + QString(), tr("Welcome to VNoteX and the new VNote!"), this); if (ret == QMessageBox::Ok && importLegacyNotebook()) { @@ -48,7 +48,7 @@ bool ImportLegacyNotebookDialog::validateRootFolderInput(QString &p_msg) { const auto rootFolderPath = m_infoWidget->getRootFolderPath(); if (!QFileInfo::exists(rootFolderPath) || !PathUtils::isLegalPath(rootFolderPath)) { - Utils::appendMsg(p_msg, tr("Please specify a valid root folder to import.")); + Utils::appendMsg(p_msg, tr("Please specify a valid root folder to open.")); return false; } @@ -76,14 +76,30 @@ bool ImportLegacyNotebookDialog::importLegacyNotebook() const auto rootFolderPath = m_infoWidget->getRootFolderPath(); auto ¬ebookMgr = VNoteX::getInst().getNotebookMgr(); - auto imageFolder = LegacyNotebookUtils::getImageFolderOfNotebook(rootFolderPath); - if (imageFolder.isEmpty()) { - imageFolder = Notebook::c_defaultImageFolder; - } - auto attachmentFolder = LegacyNotebookUtils::getAttachmentFolderOfNotebook(rootFolderPath); - if (attachmentFolder.isEmpty()) { - attachmentFolder = Notebook::c_defaultAttachmentFolder; + + QString imageFolder; + QString attachmentFolder; + QDateTime createdTimeUtc; + + try { + imageFolder = LegacyNotebookUtils::getImageFolderOfNotebook(rootFolderPath); + if (imageFolder.isEmpty()) { + imageFolder = Notebook::c_defaultImageFolder; + } + + attachmentFolder = LegacyNotebookUtils::getAttachmentFolderOfNotebook(rootFolderPath); + if (attachmentFolder.isEmpty()) { + attachmentFolder = Notebook::c_defaultAttachmentFolder; + } + + createdTimeUtc = LegacyNotebookUtils::getCreatedTimeUtcOfFolder(rootFolderPath); + } catch (Exception &p_e) { + QString msg = tr("Failed to read legacy notebook configuration in (%1) (%2).").arg(rootFolderPath, p_e.what()); + qCritical() << msg; + setInformationText(msg, ScrollDialog::InformationLevel::Error); + return false; } + auto paras = NotebookParameters::createNotebookParameters(notebookMgr, m_infoWidget->getType(), m_infoWidget->getName(), @@ -92,7 +108,7 @@ bool ImportLegacyNotebookDialog::importLegacyNotebook() m_infoWidget->getIcon(), imageFolder, attachmentFolder, - LegacyNotebookUtils::getCreatedTimeUtcOfFolder(rootFolderPath), + createdTimeUtc, m_infoWidget->getBackend(), m_infoWidget->getVersionController(), m_infoWidget->getConfigMgr()); @@ -101,7 +117,7 @@ bool ImportLegacyNotebookDialog::importLegacyNotebook() try { nb = notebookMgr.newNotebook(paras); } catch (Exception &p_e) { - QString msg = tr("Failed to create notebook in %1 (%2).").arg(rootFolderPath, p_e.what()); + QString msg = tr("Failed to create notebook in (%1) (%2).").arg(rootFolderPath, p_e.what()); qCritical() << msg; setInformationText(msg, ScrollDialog::InformationLevel::Error); return false; @@ -123,7 +139,12 @@ bool ImportLegacyNotebookDialog::importLegacyNotebook() } auto rootNode = nb->getRootNode(); - ImportFolderUtils::importFolderContentsByLegacyConfig(nb.data(), rootNode.data(), errMsg); + + try { + ImportFolderUtils::importFolderContentsByLegacyConfig(nb.data(), rootNode.data(), errMsg); + } catch (Exception &p_e) { + errMsg = tr("Failed to import folder contents by legacy config in (%1) (%2).").arg(rootFolderPath, p_e.what()); + } emit nb->nodeUpdated(rootNode.data()); diff --git a/src/widgets/dialogs/importnotebookdialog.cpp b/src/widgets/dialogs/importnotebookdialog.cpp index 783f99c9..4d6ceab9 100644 --- a/src/widgets/dialogs/importnotebookdialog.cpp +++ b/src/widgets/dialogs/importnotebookdialog.cpp @@ -34,7 +34,7 @@ void ImportNotebookDialog::setupUI() setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); setButtonEnabled(QDialogButtonBox::Ok, false); - setWindowTitle(tr("Import Notebook")); + setWindowTitle(tr("Open Notebook")); } void ImportNotebookDialog::setupNotebookInfoWidget(QWidget *p_parent) @@ -135,7 +135,7 @@ bool ImportNotebookDialog::createNotebookToImport(QString &p_msg) bool ImportNotebookDialog::importNotebook() { if (!m_notebookToImport) { - QString msg = tr("Failed to import notebook."); + QString msg = tr("Failed to open notebook."); qCritical() << msg; setInformationText(msg, ScrollDialog::InformationLevel::Error); return false; @@ -145,7 +145,7 @@ bool ImportNotebookDialog::importNotebook() auto ¬ebookMgr = VNoteX::getInst().getNotebookMgr(); notebookMgr.importNotebook(m_notebookToImport); } catch (Exception &p_e) { - QString msg = tr("Failed to import notebook (%1).").arg(p_e.what()); + QString msg = tr("Failed to open notebook (%1).").arg(p_e.what()); qCritical() << msg; setInformationText(msg, ScrollDialog::InformationLevel::Error); return false; diff --git a/src/widgets/dialogs/settings/appearancepage.cpp b/src/widgets/dialogs/settings/appearancepage.cpp index 55c57878..2eb28c7d 100644 --- a/src/widgets/dialogs/settings/appearancepage.cpp +++ b/src/widgets/dialogs/settings/appearancepage.cpp @@ -59,7 +59,7 @@ void AppearancePage::setupUI() auto layout = new QVBoxLayout(); for (int i = 0; i < docks.size(); ++i) { - m_keepDocksExpandingContentArea[i].first = WidgetsFactory::createCheckBox(docks[i]->windowTitle(), this); + m_keepDocksExpandingContentArea[i].first = WidgetsFactory::createCheckBox(docks[i]->property(MainWindow::c_propertyDockTitle).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/dialogs/settings/settingsdialog.cpp b/src/widgets/dialogs/settings/settingsdialog.cpp index 50003566..edd12087 100644 --- a/src/widgets/dialogs/settings/settingsdialog.cpp +++ b/src/widgets/dialogs/settings/settingsdialog.cpp @@ -267,14 +267,3 @@ QTreeWidgetItem *SettingsDialog::addSubPage(SettingsPage *p_page, QTreeWidgetIte setupPage(subItem, p_page); return subItem; } - -void SettingsDialog::showEvent(QShowEvent *p_event) -{ - Dialog::showEvent(p_event); - - if (m_firstShown) { - m_firstShown = false; - const auto sz = size(); - resize(sz * 1.2); - } -} diff --git a/src/widgets/dialogs/settings/settingsdialog.h b/src/widgets/dialogs/settings/settingsdialog.h index 681f50f5..0b1d74c5 100644 --- a/src/widgets/dialogs/settings/settingsdialog.h +++ b/src/widgets/dialogs/settings/settingsdialog.h @@ -28,8 +28,6 @@ namespace vnotex void appliedButtonClicked() Q_DECL_OVERRIDE; - void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE; - private: void setupUI(); @@ -63,8 +61,6 @@ namespace vnotex bool m_changesUnsaved = false; bool m_ready = false; - - bool m_firstShown = true; }; } diff --git a/src/widgets/mainwindow.cpp b/src/widgets/mainwindow.cpp index d3b7ce84..7ce9ac8f 100644 --- a/src/widgets/mainwindow.cpp +++ b/src/widgets/mainwindow.cpp @@ -4,8 +4,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -18,6 +18,7 @@ #include #include #include +#include #include "toolbox.h" #include "notebookexplorer.h" @@ -48,9 +49,14 @@ #include "searchinfoprovider.h" #include #include +#include +#include using namespace vnotex; +const char *MainWindow::c_propertyDockIndex = "DockIndex"; +const char *MainWindow::c_propertyDockTitle = "DockTitle"; + MainWindow::MainWindow(QWidget *p_parent) : QMainWindow(p_parent) { @@ -64,6 +70,15 @@ MainWindow::MainWindow(QWidget *p_parent) loadStateAndGeometry(); + { + updateDockWidgetTabBar(); + + for (auto dock : m_docks) { + connect(dock, &QDockWidget::visibilityChanged, + this, &MainWindow::updateDockWidgetTabBar); + } + } + // 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. connect(qApp, &QCoreApplication::aboutToQuit, @@ -213,6 +228,38 @@ void MainWindow::setupDocks() 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(); @@ -230,16 +277,6 @@ void MainWindow::setupDocks() // Following are non-tabfieid docks. setupLocationListDock(); - - for (auto dock : m_docks) { - connect(dock, &QDockWidget::visibilityChanged, - this, [this]() { - updateTabBarStyle(); - emit layoutChanged(); - }); - } - - activateDock(m_docks[DockIndex::NavigationDock]); } void MainWindow::activateDock(QDockWidget *p_dock) @@ -264,8 +301,7 @@ void MainWindow::activateDock(QDockWidget *p_dock) void MainWindow::setupNavigationDock() { - auto dock = new QDockWidget(tr("Navigation"), this); - m_docks.push_back(dock); + auto dock = createDockWidget(DockIndex::NavigationDock, tr("Navigation"), this); dock->setObjectName(QStringLiteral("NavigationDock.vnotex")); dock->setAllowedAreas(Qt::AllDockWidgetAreas); @@ -278,8 +314,7 @@ void MainWindow::setupNavigationDock() void MainWindow::setupOutlineDock() { - auto dock = new QDockWidget(tr("Outline"), this); - m_docks.push_back(dock); + auto dock = createDockWidget(DockIndex::OutlineDock, tr("Outline"), this); dock->setObjectName(QStringLiteral("OutlineDock.vnotex")); dock->setAllowedAreas(Qt::AllDockWidgetAreas); @@ -292,8 +327,7 @@ void MainWindow::setupOutlineDock() void MainWindow::setupSearchDock() { - auto dock = new QDockWidget(tr("Search"), this); - m_docks.push_back(dock); + auto dock = createDockWidget(DockIndex::SearchDock, tr("Search"), this); dock->setObjectName(QStringLiteral("SearchDock.vnotex")); dock->setAllowedAreas(Qt::AllDockWidgetAreas); @@ -316,8 +350,7 @@ void MainWindow::setupSearchPanel() void MainWindow::setupSnippetDock() { - auto dock = new QDockWidget(tr("Snippets"), this); - m_docks.push_back(dock); + auto dock = createDockWidget(DockIndex::SnippetDock, tr("Snippets"), this); dock->setObjectName(QStringLiteral("SnippetDock.vnotex")); dock->setAllowedAreas(Qt::AllDockWidgetAreas); @@ -344,8 +377,7 @@ void MainWindow::setupSnippetPanel() void MainWindow::setupHistoryDock() { - auto dock = new QDockWidget(tr("History"), this); - m_docks.push_back(dock); + auto dock = createDockWidget(DockIndex::HistoryDock, tr("History"), this); dock->setObjectName(QStringLiteral("HistoryDock.vnotex")); dock->setAllowedAreas(Qt::AllDockWidgetAreas); @@ -364,8 +396,7 @@ void MainWindow::setupHistoryPanel() void MainWindow::setupLocationListDock() { - auto dock = new QDockWidget(tr("Location List"), this); - m_docks.push_back(dock); + auto dock = createDockWidget(DockIndex::LocationListDock, tr("Location List"), this); dock->setObjectName(QStringLiteral("LocationListDock.vnotex")); dock->setAllowedAreas(Qt::AllDockWidgetAreas); @@ -766,11 +797,47 @@ void MainWindow::quitApp() close(); } -void MainWindow::updateTabBarStyle() +void MainWindow::updateDockWidgetTabBar() { + QBitArray tabifiedDocks(m_docks.size(), false); Q_FOREACH(QTabBar* tabBar, this->findChildren(QString(), Qt::FindDirectChildrenOnly)) { 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(); } void MainWindow::exportNotes() @@ -860,3 +927,13 @@ void MainWindow::setupSpellCheck() vte::SpellChecker::addDictionaryCustomSearchPaths( 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; +} diff --git a/src/widgets/mainwindow.h b/src/widgets/mainwindow.h index ac68a1ac..4e710cff 100644 --- a/src/widgets/mainwindow.h +++ b/src/widgets/mainwindow.h @@ -66,6 +66,12 @@ namespace vnotex void toggleLocationListVisible(); + void updateDockWidgetTabBar(); + + static const char *c_propertyDockIndex; + + static const char *c_propertyDockTitle; + signals: void mainWindowStarted(); @@ -85,8 +91,6 @@ namespace vnotex private slots: void closeOnQuit(); - void updateTabBarStyle(); - void exportNotes(); void showTips(const QString &p_message, int p_timeoutMilliseconds); @@ -100,7 +104,8 @@ namespace vnotex HistoryDock, SearchDock, SnippetDock, - LocationListDock + LocationListDock, + MaxDock }; void setupUI(); @@ -162,6 +167,8 @@ namespace vnotex void setupSpellCheck(); + QDockWidget *createDockWidget(DockIndex p_dockIndex, const QString &p_title, QWidget *p_parent); + ToolBarHelper m_toolBarHelper; StatusBarHelper m_statusBarHelper; @@ -186,6 +193,8 @@ namespace vnotex QVector m_docks; + QVector m_dockIcons; + bool m_layoutReset = false; // -1: do not request to quit; diff --git a/src/widgets/toolbarhelper.cpp b/src/widgets/toolbarhelper.cpp index 3d1bd887..ddbdd479 100644 --- a/src/widgets/toolbarhelper.cpp +++ b/src/widgets/toolbarhelper.cpp @@ -77,7 +77,7 @@ QToolBar *ToolBarHelper::setupFileToolBar(MainWindow *p_win, QToolBar *p_toolBar // Import notebook. btnMenu->addAction(generateIcon("import_notebook.svg"), - MainWindow::tr("Import Notebook"), + MainWindow::tr("Open Other Notebooks"), btnMenu, []() { emit VNoteX::getInst().importNotebookRequested(); @@ -85,7 +85,7 @@ QToolBar *ToolBarHelper::setupFileToolBar(MainWindow *p_win, QToolBar *p_toolBar // Import notebook of VNote 2. btnMenu->addAction(generateIcon("import_notebook_of_vnote2.svg"), - MainWindow::tr("Import Legacy Notebook Of VNote 2"), + MainWindow::tr("Open Legacy Notebooks Of VNote 2"), btnMenu, []() { emit VNoteX::getInst().importLegacyNotebookRequested(); @@ -174,7 +174,7 @@ QToolBar *ToolBarHelper::setupFileToolBar(MainWindow *p_win, QToolBar *p_toolBar newMenu->addSeparator(); - auto exportAct = newMenu->addAction(MainWindow::tr("Export"), + auto exportAct = newMenu->addAction(MainWindow::tr("Export (Convert Format)"), newMenu, []() { emit VNoteX::getInst().exportRequested(); @@ -337,9 +337,26 @@ QToolBar *ToolBarHelper::setupSettingsToolBar(MainWindow *p_win, QToolBar *p_too { // Windows. + // MainWindow will clear the title of the dock widget for the tab bar, so we need to use + // another action to wrap the no-text action. auto subMenu = menu->addMenu(MainWindow::tr("Windows")); for (auto dock : p_win->getDocks()) { - subMenu->addAction(dock->toggleViewAction()); + // @act is owned by the QDockWidget. + auto act = dock->toggleViewAction(); + auto actWrapper = subMenu->addAction(act->text()); + actWrapper->setCheckable(act->isCheckable()); + actWrapper->setChecked(act->isChecked()); + MainWindow::connect(act, &QAction::toggled, + actWrapper, [actWrapper](bool checked) { + if (actWrapper->isChecked() != checked) { + actWrapper->setChecked(checked); + } + }); + MainWindow::connect(actWrapper, &QAction::triggered, + act, [p_win, act]() { + act->trigger(); + p_win->updateDockWidgetTabBar(); + }); } }