NotebookExplorer: support view order (#1700)

This commit is contained in:
Le Tan 2021-02-24 20:46:05 +08:00 committed by GitHub
parent d8371a9b45
commit 517d977266
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 286 additions and 31 deletions

View File

@ -259,6 +259,8 @@ QDir Node::toDir() const
if (isContainer()) {
return QDir(fetchAbsolutePath());
}
Q_ASSERT(false);
return QDir();
}
void Node::load()

View File

@ -22,6 +22,8 @@ void WidgetConfig::init(const QJsonObject &p_app,
}
m_findAndReplaceOptions = static_cast<FindOptions>(READINT(QStringLiteral("find_and_replace_options")));
m_noteExplorerViewOrder = READINT(QStringLiteral("note_explorer_view_order"));
}
QJsonObject WidgetConfig::toJson() const
@ -29,6 +31,7 @@ QJsonObject WidgetConfig::toJson() const
QJsonObject obj;
obj[QStringLiteral("outline_auto_expanded_level")] = m_outlineAutoExpandedLevel;
obj[QStringLiteral("find_and_replace_options")] = static_cast<int>(m_findAndReplaceOptions);
obj[QStringLiteral("note_explorer_view_order")] = m_noteExplorerViewOrder;
return obj;
}
@ -51,3 +54,13 @@ void WidgetConfig::setFindAndReplaceOptions(FindOptions p_options)
{
updateConfig(m_findAndReplaceOptions, p_options, this);
}
int WidgetConfig::getNoteExplorerViewOrder() const
{
return m_noteExplorerViewOrder;
}
void WidgetConfig::setNoteExplorerViewOrder(int p_viewOrder)
{
updateConfig(m_noteExplorerViewOrder, p_viewOrder, this);
}

View File

@ -24,10 +24,15 @@ namespace vnotex
FindOptions getFindAndReplaceOptions() const;
void setFindAndReplaceOptions(FindOptions p_options);
int getNoteExplorerViewOrder() const;
void setNoteExplorerViewOrder(int p_viewOrder);
private:
int m_outlineAutoExpandedLevel = 6;
FindOptions m_findAndReplaceOptions = FindOption::None;
int m_noteExplorerViewOrder = 0;
};
}

View File

@ -20,6 +20,7 @@
<file>icons/help.svg</file>
<file>icons/menu.svg</file>
<file>icons/settings.svg</file>
<file>icons/view.svg</file>
<file>icons/settings_menu.svg</file>
<file>icons/whatsthis.svg</file>
<file>icons/help_menu.svg</file>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<path fill="#000000" d="M256,128c-81.9,0-145.7,48.8-224,128c67.4,67.7,124,128,224,128c99.9,0,173.4-76.4,224-126.6
C428.2,198.6,354.8,128,256,128z M256,347.3c-49.4,0-89.6-41-89.6-91.3c0-50.4,40.2-91.3,89.6-91.3s89.6,41,89.6,91.3
C345.6,306.4,305.4,347.3,256,347.3z"/>
<g>
<path fill="#000000" d="M256,224c0-7.9,2.9-15.1,7.6-20.7c-2.5-0.4-5-0.6-7.6-0.6c-28.8,0-52.3,23.9-52.3,53.3c0,29.4,23.5,53.3,52.3,53.3
s52.3-23.9,52.3-53.3c0-2.3-0.2-4.6-0.4-6.9c-5.5,4.3-12.3,6.9-19.8,6.9C270.3,256,256,241.7,256,224z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1018 B

View File

@ -285,6 +285,8 @@
"//comment" : "Level of the heading in outline that should expand to automatically (1-6)",
"outline_auto_expanded_level" : 6,
"//comment" : "Default find options in FindAndReplace",
"find_and_replace_options" : 16
"find_and_replace_options" : 16,
"//comment" : "View order of the note explorer",
"note_explorer_view_order" : 0
}
}

View File

@ -80,6 +80,7 @@ bool ExportPdfOption::operator==(const ExportPdfOption &p_other) const
QJsonObject ExportOption::toJson() const
{
QJsonObject obj;
obj["target_format"] = static_cast<int>(m_targetFormat);
obj["use_transparent_bg"] = m_useTransparentBg;
obj["output_dir"] = m_outputDir;
obj["recursive"] = m_recursive;
@ -95,6 +96,29 @@ void ExportOption::fromJson(const QJsonObject &p_obj)
return;
}
{
int fmt = p_obj["target_format"].toInt();
switch (fmt) {
case static_cast<int>(ExportFormat::Markdown):
m_targetFormat = ExportFormat::Markdown;
break;
case static_cast<int>(ExportFormat::PDF):
m_targetFormat = ExportFormat::PDF;
break;
case static_cast<int>(ExportFormat::Custom):
m_targetFormat = ExportFormat::Custom;
break;
case static_cast<int>(ExportFormat::HTML):
Q_FALLTHROUGH();
default:
m_targetFormat = ExportFormat::HTML;
break;
}
}
m_useTransparentBg = p_obj["use_transparent_bg"].toBool();
m_outputDir = p_obj["output_dir"].toString();
m_recursive = p_obj["recursive"].toBool();
@ -105,7 +129,8 @@ void ExportOption::fromJson(const QJsonObject &p_obj)
bool ExportOption::operator==(const ExportOption &p_other) const
{
bool ret = m_useTransparentBg == p_other.m_useTransparentBg
bool ret = m_targetFormat == p_other.m_targetFormat
&& m_useTransparentBg == p_other.m_useTransparentBg
&& m_outputDir == p_other.m_outputDir
&& m_recursive == p_other.m_recursive
&& m_exportAttachments == p_other.m_exportAttachments;

View File

@ -478,6 +478,11 @@ bool WebViewExporter::doExportPdf(const ExportPdfOption &p_pdfOption, const QStr
bool WebViewExporter::doExportWkhtmltopdf(const ExportPdfOption &p_pdfOption, const QString &p_outputFile, const QUrl &p_baseUrl)
{
if (p_pdfOption.m_wkhtmltopdfExePath.isEmpty()) {
qWarning() << "invalid wkhtmltopdf executable path";
return false;
}
ExportState state = ExportState::Busy;
connect(m_viewer->adapter(), &MarkdownViewerAdapter::contentReady,

View File

@ -359,6 +359,7 @@ void ExportDialog::startExport()
// On start.
{
clearInformationText();
m_exportedFile.clear();
m_exportOngoing = true;
updateUIOnExport();
@ -407,7 +408,14 @@ void ExportDialog::updateUIOnExport()
int ExportDialog::doExport(ExportOption p_option)
{
// TODO: Check ExportOption.
if (p_option.m_targetFormat == ExportFormat::PDF && p_option.m_pdfOption.m_useWkhtmltopdf) {
// Check wkhtmltopdf executable.
const auto &wkExePath = p_option.m_pdfOption.m_wkhtmltopdfExePath;
if (wkExePath.isEmpty() || !QFileInfo::exists(wkExePath)) {
appendLog(tr("Please specify a valid wkhtmltopdf executable file (%1)").arg(wkExePath));
return 0;
}
}
int exportedFilesCount = 0;

View File

@ -2,6 +2,9 @@
#include <QVBoxLayout>
#include <QFileDialog>
#include <QToolButton>
#include <QMenu>
#include <QActionGroup>
#include "titlebar.h"
#include "dialogs/newnotebookdialog.h"
@ -24,10 +27,12 @@
#include "messageboxhelper.h"
#include <core/configmgr.h>
#include <core/coreconfig.h>
#include <core/widgetconfig.h>
#include <core/events.h>
#include <core/exception.h>
#include <core/fileopenparameters.h>
#include "navigationmodemgr.h"
#include "widgetsfactory.h"
using namespace vnotex;
@ -77,6 +82,15 @@ TitleBar *NotebookExplorer::setupTitleBar(QWidget *p_parent)
p_parent);
titleBar->setWhatsThis(tr("This title bar contains buttons and menu to manage notebooks and notes."));
{
auto viewMenu = WidgetsFactory::createMenu(titleBar);
titleBar->addActionButton(QStringLiteral("view.svg"), tr("View"), viewMenu);
connect(viewMenu, &QMenu::aboutToShow,
this, [this, viewMenu]() {
setupViewMenu(viewMenu);
});
}
titleBar->addMenuAction(QStringLiteral("manage_notebooks.svg"),
tr("&Manage Notebooks"),
titleBar,
@ -300,3 +314,63 @@ const QSharedPointer<Notebook> &NotebookExplorer::currentNotebook() const
{
return m_currentNotebook;
}
void NotebookExplorer::setupViewMenu(QMenu *p_menu)
{
if (!p_menu->isEmpty()) {
return;
}
auto ag = new QActionGroup(p_menu);
auto act = ag->addAction(tr("View By Configuration"));
act->setCheckable(true);
act->setChecked(true);
act->setData(NotebookNodeExplorer::ViewOrder::OrderedByConfiguration);
p_menu->addAction(act);
act = ag->addAction(tr("View By Name"));
act->setCheckable(true);
act->setData(NotebookNodeExplorer::ViewOrder::OrderedByName);
p_menu->addAction(act);
act = ag->addAction(tr("View By Name (Reversed)"));
act->setCheckable(true);
act->setData(NotebookNodeExplorer::ViewOrder::OrderedByNameReversed);
p_menu->addAction(act);
act = ag->addAction(tr("View By Created Time"));
act->setCheckable(true);
act->setData(NotebookNodeExplorer::ViewOrder::OrderedByCreatedTime);
p_menu->addAction(act);
act = ag->addAction(tr("View By Created Time (Reversed)"));
act->setCheckable(true);
act->setData(NotebookNodeExplorer::ViewOrder::OrderedByCreatedTimeReversed);
p_menu->addAction(act);
act = ag->addAction(tr("View By Modified Time"));
act->setCheckable(true);
act->setData(NotebookNodeExplorer::ViewOrder::OrderedByModifiedTime);
p_menu->addAction(act);
act = ag->addAction(tr("View By Modified Time (Reversed)"));
act->setCheckable(true);
act->setData(NotebookNodeExplorer::ViewOrder::OrderedByModifiedTimeReversed);
p_menu->addAction(act);
int viewOrder = ConfigMgr::getInst().getWidgetConfig().getNoteExplorerViewOrder();
for (const auto &act : ag->actions()) {
if (act->data().toInt() == viewOrder) {
act->setChecked(true);
}
}
connect(ag, &QActionGroup::triggered,
this, [this](QAction *p_action) {
int order = p_action->data().toInt();
ConfigMgr::getInst().getWidgetConfig().setNoteExplorerViewOrder(order);
m_nodeExplorer->reload();
});
}

View File

@ -6,6 +6,8 @@
#include "global.h"
class QMenu;
namespace vnotex
{
class Notebook;
@ -62,6 +64,8 @@ namespace vnotex
Node *checkNotebookAndGetCurrentExploredFolderNode() const;
void setupViewMenu(QMenu *p_menu);
NotebookSelector *m_selector = nullptr;
NotebookNodeExplorer *m_nodeExplorer = nullptr;

View File

@ -22,6 +22,8 @@
#include <core/fileopenparameters.h>
#include <core/events.h>
#include <core/configmgr.h>
#include <core/widgetconfig.h>
using namespace vnotex;
@ -333,7 +335,9 @@ void NotebookNodeExplorer::loadRootNode(const Node *p_node) const
loadRecycleBinNode(recycleBinNode.data());
}
for (auto &child : p_node->getChildren()) {
auto children = p_node->getChildren();
sortNodes(children);
for (auto &child : children) {
if (recycleBinNode == child) {
continue;
}
@ -374,7 +378,9 @@ void NotebookNodeExplorer::loadChildren(QTreeWidgetItem *p_item, Node *p_node, i
return;
}
for (auto &child : p_node->getChildren()) {
auto children = p_node->getChildren();
sortNodes(children);
for (auto &child : children) {
auto item = new QTreeWidgetItem(p_item);
loadNode(item, child.data(), p_level);
}
@ -1316,3 +1322,74 @@ void NotebookNodeExplorer::focusNormalNode()
m_masterExplorer->setCurrentItem(m_masterExplorer->topLevelItem(1));
}
}
void NotebookNodeExplorer::sortNodes(QVector<QSharedPointer<Node>> &p_nodes) const
{
int viewOrder = ConfigMgr::getInst().getWidgetConfig().getNoteExplorerViewOrder();
if (viewOrder == ViewOrder::OrderedByConfiguration) {
return;
}
// Put containers first.
int firstFileIndex = p_nodes.size();
for (int i = 0; i < p_nodes.size(); ++i) {
if (!p_nodes[i]->isContainer()) {
firstFileIndex = i;
break;
}
}
// Sort containers.
sortNodes(p_nodes, 0, firstFileIndex, viewOrder);
// Sort non-containers.
sortNodes(p_nodes, firstFileIndex, p_nodes.size(), viewOrder);
}
void NotebookNodeExplorer::sortNodes(QVector<QSharedPointer<Node>> &p_nodes, int p_start, int p_end, int p_viewOrder) const
{
bool reversed = false;
switch (p_viewOrder) {
case ViewOrder::OrderedByNameReversed:
reversed = true;
Q_FALLTHROUGH();
case ViewOrder::OrderedByName:
std::sort(p_nodes.begin() + p_start, p_nodes.begin() + p_end, [reversed](const QSharedPointer<Node> &p_a, const QSharedPointer<Node> p_b) {
if (reversed) {
return p_b->getName() < p_a->getName();
} else {
return p_a->getName() < p_b->getName();
}
});
break;
case ViewOrder::OrderedByCreatedTimeReversed:
reversed = true;
Q_FALLTHROUGH();
case ViewOrder::OrderedByCreatedTime:
std::sort(p_nodes.begin() + p_start, p_nodes.begin() + p_end, [reversed](const QSharedPointer<Node> &p_a, const QSharedPointer<Node> p_b) {
if (reversed) {
return p_b->getCreatedTimeUtc() < p_a->getCreatedTimeUtc();
} else {
return p_a->getCreatedTimeUtc() < p_b->getCreatedTimeUtc();
}
});
break;
case ViewOrder::OrderedByModifiedTimeReversed:
reversed = true;
Q_FALLTHROUGH();
case ViewOrder::OrderedByModifiedTime:
std::sort(p_nodes.begin() + p_start, p_nodes.begin() + p_end, [reversed](const QSharedPointer<Node> &p_a, const QSharedPointer<Node> p_b) {
if (reversed) {
return p_b->getModifiedTimeUtc() < p_a->getModifiedTimeUtc();
} else {
return p_a->getModifiedTimeUtc() < p_b->getModifiedTimeUtc();
}
});
break;
default:
break;
}
}

View File

@ -76,6 +76,18 @@ namespace vnotex
bool m_loaded = false;
};
enum ViewOrder
{
OrderedByConfiguration = 0,
OrderedByName,
OrderedByNameReversed,
OrderedByCreatedTime,
OrderedByCreatedTimeReversed,
OrderedByModifiedTime,
OrderedByModifiedTimeReversed,
ViewOrderMax
};
explicit NotebookNodeExplorer(QWidget *p_parent = nullptr);
void setNotebook(const QSharedPointer<Notebook> &p_notebook);
@ -191,6 +203,11 @@ namespace vnotex
// Skip the recycle bin node if possible.
void focusNormalNode();
void sortNodes(QVector<QSharedPointer<Node>> &p_nodes) const;
// [p_start, p_end).
void sortNodes(QVector<QSharedPointer<Node>> &p_nodes, int p_start, int p_end, int p_viewOrder) const;
static NotebookNodeExplorer::NodeData getItemNodeData(const QTreeWidgetItem *p_item);
static void setItemNodeData(QTreeWidgetItem *p_item, const NodeData &p_data);

View File

@ -45,16 +45,13 @@ void TitleBar::setupUI(const QString &p_title, TitleBar::Actions p_actionFlags)
mainLayout->addStretch();
{
setupActionButtons(p_actionFlags);
m_buttonWidget = new QWidget(this);
mainLayout->addWidget(m_buttonWidget);
auto btnLayout = new QHBoxLayout(m_buttonWidget);
btnLayout->setContentsMargins(0, 0, 0, 0);
for (auto btn : m_actionButtons) {
btnLayout->addWidget(btn);
}
setupActionButtons(p_actionFlags);
setActionButtonsVisible(false);
}
@ -79,32 +76,17 @@ QToolButton *TitleBar::newActionButton(const QString &p_iconName, const QString
void TitleBar::setupActionButtons(TitleBar::Actions p_actionFlags)
{
if (p_actionFlags & Action::Menu) {
m_menu = WidgetsFactory::createMenu(this);
addActionButton("menu.svg", tr("Menu"), m_menu);
}
if (p_actionFlags & Action::Settings) {
auto btn = newActionButton("settings.svg", tr("Settings"), this);
auto btn = addActionButton("settings.svg", tr("Settings"));
connect(btn, &QToolButton::triggered,
this, []() {
// TODO.
});
m_actionButtons.push_back(btn);
}
if (p_actionFlags & Action::Menu) {
auto btn = newActionButton("menu.svg", tr("Menu"), this);
btn->setPopupMode(QToolButton::InstantPopup);
m_actionButtons.push_back(btn);
m_menu = WidgetsFactory::createMenu(this);
btn->setMenu(m_menu);
connect(m_menu, &QMenu::aboutToShow,
this, [this]() {
m_alwaysShowActionButtons = true;
setActionButtonsVisible(true);
});
connect(m_menu, &QMenu::aboutToHide,
this, [this]() {
m_alwaysShowActionButtons = false;
setActionButtonsVisible(false);
});
}
}
@ -160,12 +142,35 @@ QToolButton *TitleBar::addActionButton(const QString &p_iconName, const QString
layout->addWidget(btn);
} else {
int idx = m_actionButtons.size() - 1;
if (idx < 0) {
idx = 0;
}
m_actionButtons.insert(idx, btn);
layout->insertWidget(idx, btn);
}
return btn;
}
QToolButton *TitleBar::addActionButton(const QString &p_iconName, const QString &p_text, QMenu *p_menu)
{
p_menu->setParent(this);
auto btn = addActionButton(p_iconName, p_text);
btn->setPopupMode(QToolButton::InstantPopup);
btn->setMenu(p_menu);
connect(p_menu, &QMenu::aboutToShow,
this, [this]() {
m_alwaysShowActionButtons = true;
setActionButtonsVisible(true);
});
connect(p_menu, &QMenu::aboutToHide,
this, [this]() {
m_alwaysShowActionButtons = false;
setActionButtonsVisible(false);
});
return btn;
}
QHBoxLayout *TitleBar::actionButtonLayout() const
{
return static_cast<QHBoxLayout *>(m_buttonWidget->layout());

View File

@ -28,6 +28,8 @@ namespace vnotex
QToolButton *addActionButton(const QString &p_iconName, const QString &p_text);
QToolButton *addActionButton(const QString &p_iconName, const QString &p_text, QMenu *p_menu);
// Add action to the menu.
QAction *addMenuAction(const QString &p_iconName, const QString &p_text);