Compare commits

..

No commits in common. 'develop' and 'main' have entirely different histories.

Binary file not shown.

@ -11,8 +11,6 @@
/** /**
* Initializes the window components and configures the AboutWindow * Initializes the window components and configures the AboutWindow
*/ */
//包含版本检测和pro版本检测我们可以通过修改m_isProVersion值来做到免费的功能
//当版本低于5.10.0时,移除窗口的帮助按钮功能
AboutWindow::AboutWindow(QWidget *parent) AboutWindow::AboutWindow(QWidget *parent)
: QDialog(parent), m_ui(new Ui::AboutWindow), m_isProVersion(false) : QDialog(parent), m_ui(new Ui::AboutWindow), m_isProVersion(false)
{ {

@ -16,7 +16,6 @@ public:
explicit AboutWindow(QWidget *parent = 0); explicit AboutWindow(QWidget *parent = 0);
~AboutWindow(); ~AboutWindow();
void setTheme(Theme::Value theme); void setTheme(Theme::Value theme);
//或许可以通过此函数来获取免费版本
void setProVersion(bool isProVersion); void setProVersion(bool isProVersion);
public slots: public slots:

@ -7,7 +7,7 @@
#include "notelistview.h" #include "notelistview.h"
#include "editorsettingsoptions.h" #include "editorsettingsoptions.h"
#include "fontloader.h" #include "fontloader.h"
//分操作系统设置font
AllNoteButtonTreeDelegateEditor::AllNoteButtonTreeDelegateEditor(QTreeView *view, AllNoteButtonTreeDelegateEditor::AllNoteButtonTreeDelegateEditor(QTreeView *view,
const QStyleOptionViewItem &option, const QStyleOptionViewItem &option,
const QModelIndex &index, const QModelIndex &index,
@ -45,27 +45,20 @@ AllNoteButtonTreeDelegateEditor::AllNoteButtonTreeDelegateEditor(QTreeView *view
{ {
setContentsMargins(0, 0, 0, 0); setContentsMargins(0, 0, 0, 0);
} }
//重写paintEvent函数
void AllNoteButtonTreeDelegateEditor::paintEvent(QPaintEvent *event) void AllNoteButtonTreeDelegateEditor::paintEvent(QPaintEvent *event)
{ {
QPainter painter(this); QPainter painter(this);
//定图标位置
auto iconRect = QRect(rect().x() + 22, rect().y() + (rect().height() - 20) / 2, 18, 20); auto iconRect = QRect(rect().x() + 22, rect().y() + (rect().height() - 20) / 2, 18, 20);
//获取图标路径
auto iconPath = m_index.data(NodeItem::Roles::Icon).toString(); auto iconPath = m_index.data(NodeItem::Roles::Icon).toString();
//获取显示文本
auto displayName = m_index.data(NodeItem::Roles::DisplayText).toString(); auto displayName = m_index.data(NodeItem::Roles::DisplayText).toString();
QRect nameRect(rect()); QRect nameRect(rect());
nameRect.setLeft(iconRect.x() + iconRect.width() + 5); nameRect.setLeft(iconRect.x() + iconRect.width() + 5);
nameRect.setWidth(nameRect.width() - 22 - 40); nameRect.setWidth(nameRect.width() - 22 - 40);
if (m_view->selectionModel()->isSelected(m_index)) { if (m_view->selectionModel()->isSelected(m_index)) {
//根据是否被选中绘制不同颜色
painter.fillRect(rect(), QBrush(m_activeColor)); painter.fillRect(rect(), QBrush(m_activeColor));
painter.setPen(m_titleSelectedColor); painter.setPen(m_titleSelectedColor);
} else { } else {
//拖动绘制
auto listView = dynamic_cast<NoteListView *>(m_listView); auto listView = dynamic_cast<NoteListView *>(m_listView);
if (listView->isDragging()) { if (listView->isDragging()) {
if (m_theme == Theme::Dark) { if (m_theme == Theme::Dark) {
@ -78,7 +71,6 @@ void AllNoteButtonTreeDelegateEditor::paintEvent(QPaintEvent *event)
} }
painter.setPen(m_folderIconColor); painter.setPen(m_folderIconColor);
} }
//又开始分操作系统绘制
#ifdef __APPLE__ #ifdef __APPLE__
int iconPointSizeOffset = 0; int iconPointSizeOffset = 0;
#else #else
@ -94,7 +86,6 @@ void AllNoteButtonTreeDelegateEditor::paintEvent(QPaintEvent *event)
painter.setPen(m_titleColor); painter.setPen(m_titleColor);
} }
//设置子项
painter.setFont(m_titleFont); painter.setFont(m_titleFont);
painter.drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, displayName); painter.drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, displayName);
auto childCountRect = rect(); auto childCountRect = rect();
@ -109,12 +100,9 @@ void AllNoteButtonTreeDelegateEditor::paintEvent(QPaintEvent *event)
painter.setFont(m_numberOfNotesFont); painter.setFont(m_numberOfNotesFont);
painter.drawText(childCountRect, Qt::AlignHCenter | Qt::AlignVCenter, painter.drawText(childCountRect, Qt::AlignHCenter | Qt::AlignVCenter,
QString::number(childCount)); QString::number(childCount));
//调用基类 QWidget 的 paintEvent 函数,以确保小部件的其他默认绘制行为得以执行
QWidget::paintEvent(event); QWidget::paintEvent(event);
} }
//又是用switch来设置枚举类型的值
//要注意的一点是没有使用0、1、2等来列举而是使用key值来列举有利于增加代码规模和程序员阅读
void AllNoteButtonTreeDelegateEditor::setTheme(Theme::Value theme) void AllNoteButtonTreeDelegateEditor::setTheme(Theme::Value theme)
{ {
m_theme = theme; m_theme = theme;

@ -2,17 +2,12 @@
#define ALLNOTEBUTTONTREEDELEGATEEDITOR_H #define ALLNOTEBUTTONTREEDELEGATEEDITOR_H
#include <QWidget> #include <QWidget>
//用来描述书图像的显示选项
#include <QStyleOptionViewItem> #include <QStyleOptionViewItem>
//用于在模型/视图架构中索引项
#include <QModelIndex> #include <QModelIndex>
//用于设置和管理字体
#include <QFont> #include <QFont>
#include "editorsettingsoptions.h" #include "editorsettingsoptions.h"
//用于显示树形视图的组件
class QTreeView; class QTreeView;
//用于显示列表视图的组件
class QListView; class QListView;
class AllNoteButtonTreeDelegateEditor : public QWidget class AllNoteButtonTreeDelegateEditor : public QWidget
@ -42,7 +37,6 @@ private:
Theme::Value m_theme; Theme::Value m_theme;
// QWidget interface // QWidget interface
protected: protected:
//重写QWidget的paintEvent函数用于自定义绘制编辑器小部件的内容
virtual void paintEvent(QPaintEvent *event) override; virtual void paintEvent(QPaintEvent *event) override;
}; };

@ -11,37 +11,26 @@ class CustomDocument : public QTextEdit
public: public:
CustomDocument(QWidget *parent = nullptr); CustomDocument(QWidget *parent = nullptr);
//初始化文档边距 void setDocumentPadding(int left, int top, int right, int bottom);
void setDocumentPadding(int left, int top, int right, int bottom);// 设置文档边距
//主要是处理按键和鼠标同时按下的鼠标的变化
bool eventFilter(QObject *obj, QEvent *event); bool eventFilter(QObject *obj, QEvent *event);
//分为本地文件、文件夹、网页文件和暂存文件
bool openLinkAtCursorPosition(); bool openLinkAtCursorPosition();
//该函数的鲁棒性好处理链接长度时很谨慎防止C++常见的溢出问题
QString getMarkdownUrlAtPosition(const QString &text, int position); QString getMarkdownUrlAtPosition(const QString &text, int position);
//这个函数没有达到预期的要求应该对scheme的类型进行一定的描述提高鲁棒性 bool isValidUrl(const QString &urlString);
bool isValidUrl(const QString &urlString);// 检查URL是否有效
//没有进行检查,直接调用函数库
void openUrl(const QString &urlString); void openUrl(const QString &urlString);
//该函数值得学习 QMap<QString, QString> parseMarkdownUrlsFromText(const QString &text);
//首先是每个可能链接的正则表达式 QUrl getUrlUnderMouse();
//其次是对于引用链接,如何进行发现、描述和储存 void moveBlockUp();
QMap<QString, QString> parseMarkdownUrlsFromText(const QString &text);// 解析文本中的Markdown链接 void moveBlockDown();
// 这个函数值得学习按照逻辑处理url和text安排的很巧妙
QUrl getUrlUnderMouse(); // 获取鼠标下的URL
//块的移动,提供了一种思路,即先删除,后添加,最后移动游标到指定位置
void moveBlockUp(); // 将当前块向上移动
void moveBlockDown(); // 将当前块向下移动
signals: signals:
void resized(); // 信号:当控件大小改变时发出 void resized();
void mouseMoved(); // 信号:当鼠标移动时发出 void mouseMoved();
// QWidget interface // QWidget interface
protected: protected:
void resizeEvent(QResizeEvent *event); // 重写QWidget的resizeEvent void resizeEvent(QResizeEvent *event);
void mouseMoveEvent(QMouseEvent *event); // 重写QWidget的mouseMoveEvent void mouseMoveEvent(QMouseEvent *event);
QStringList _ignoredClickUrlSchemata; // 忽略点击的URL模式列表 QStringList _ignoredClickUrlSchemata;
}; };
#endif // CUSTOMDOCUMENT_H #endif // CUSTOMDOCUMENT_H

@ -1,15 +1,15 @@
#include "customDocument.h" #include "customDocument.h"
#include <QDebug> #include <QDebug>
#include <QGuiApplication>//应用程序 #include <QGuiApplication>
#include <QTextCursor>//文本光标 #include <QTextCursor>
#include <QMessageBox>//消息框 #include <QMessageBox>
CustomDocument::CustomDocument(QWidget *parent) : QTextEdit(parent) CustomDocument::CustomDocument(QWidget *parent) : QTextEdit(parent)
{ {
installEventFilter(this); // 安装事件过滤器,以便可以处理事件 installEventFilter(this);
viewport()->installEventFilter(this); // 安装视口的事件过滤器 viewport()->installEventFilter(this);
setMouseTracking(true); // 开启鼠标追踪,以便可以捕获鼠标移动事件 setMouseTracking(true);
setAttribute(Qt::WidgetAttribute::WA_Hover, true); // 设置悬停属性,以便可以处理鼠标悬停事件 setAttribute(Qt::WidgetAttribute::WA_Hover, true);
} }
/*! /*!
@ -25,16 +25,16 @@ CustomDocument::CustomDocument(QWidget *parent) : QTextEdit(parent)
*/ */
void CustomDocument::setDocumentPadding(int left, int top, int right, int bottom) void CustomDocument::setDocumentPadding(int left, int top, int right, int bottom)
{ {
setViewportMargins(left, top, right, bottom);//设置视口的边距 setViewportMargins(left, top, right, bottom);
} }
void CustomDocument::resizeEvent(QResizeEvent *event)//处理窗口变化 void CustomDocument::resizeEvent(QResizeEvent *event)
{ {
QTextEdit::resizeEvent(event); QTextEdit::resizeEvent(event);
emit resized(); emit resized();
} }
void CustomDocument::mouseMoveEvent(QMouseEvent *event)//处理鼠标移动事件 void CustomDocument::mouseMoveEvent(QMouseEvent *event)
{ {
QTextEdit::mouseMoveEvent(event); QTextEdit::mouseMoveEvent(event);
emit mouseMoved(); emit mouseMoved();
@ -43,9 +43,7 @@ void CustomDocument::mouseMoveEvent(QMouseEvent *event)//
bool CustomDocument::eventFilter(QObject *obj, QEvent *event) bool CustomDocument::eventFilter(QObject *obj, QEvent *event)
{ {
// qDebug() << event->type(); // qDebug() << event->type();
//分情况处理按键和鼠标状况:
//先处理关注的事件,再处理不关注的事件
//先处理不常见的事件——如按键。后处理常见的事件——如鼠标移动或松开
if (event->type() == QEvent::HoverMove) { if (event->type() == QEvent::HoverMove) {
// if hovering and the control key is active, check whether the mouse is over a link // if hovering and the control key is active, check whether the mouse is over a link
if (QGuiApplication::keyboardModifiers() == Qt::ExtraButton24 if (QGuiApplication::keyboardModifiers() == Qt::ExtraButton24
@ -54,7 +52,7 @@ bool CustomDocument::eventFilter(QObject *obj, QEvent *event)
} else { } else {
viewport()->setCursor(Qt::IBeamCursor); viewport()->setCursor(Qt::IBeamCursor);
} }
} else if (event->type() == QEvent::KeyPress) {//检测只按键的事件并做出响应 } else if (event->type() == QEvent::KeyPress) {
auto *keyEvent = static_cast<QKeyEvent *>(event); auto *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Control) { if (keyEvent->key() == Qt::Key_Control) {
@ -71,7 +69,7 @@ bool CustomDocument::eventFilter(QObject *obj, QEvent *event)
return true; return true;
} }
} }
} else if (event->type() == QEvent::MouseButtonRelease) {//检测松鼠标的事件并做出响应 } else if (event->type() == QEvent::MouseButtonRelease) {
auto *mouseEvent = static_cast<QMouseEvent *>(event); auto *mouseEvent = static_cast<QMouseEvent *>(event);
@ -87,7 +85,7 @@ bool CustomDocument::eventFilter(QObject *obj, QEvent *event)
return true; return true;
} }
} else if (event->type() == QEvent::KeyRelease) {//检测松按键的事件并做出响应 } else if (event->type() == QEvent::KeyRelease) {
auto *keyEvent = static_cast<QKeyEvent *>(event); auto *keyEvent = static_cast<QKeyEvent *>(event);
// reset cursor if control key was released // reset cursor if control key was released
@ -105,8 +103,6 @@ bool CustomDocument::eventFilter(QObject *obj, QEvent *event)
* @param position * @param position
* @return url string * @return url string
*/ */
//注释表明函数需要用到text和position变量并且需要返回一个string类型的url
//但是在实际操作过程中作者用QString代替了String类型
QString CustomDocument::getMarkdownUrlAtPosition(const QString &text, int position) QString CustomDocument::getMarkdownUrlAtPosition(const QString &text, int position)
{ {
QString url; QString url;
@ -120,7 +116,7 @@ QString CustomDocument::getMarkdownUrlAtPosition(const QString &text, int positi
const int foundPositionStart = text.indexOf(linkText); const int foundPositionStart = text.indexOf(linkText);
if (foundPositionStart >= 0) {//谨慎处理url的长度引起的溢出问题是健壮性的体现 if (foundPositionStart >= 0) {
// calculate end position of found linkText // calculate end position of found linkText
const int foundPositionEnd = foundPositionStart + linkText.size(); const int foundPositionEnd = foundPositionStart + linkText.size();
@ -140,31 +136,19 @@ QString CustomDocument::getMarkdownUrlAtPosition(const QString &text, int positi
* *
* @return QUrl * @return QUrl
*/ */
//注释表明函数有返回值且返回值是游标下面的url
QUrl CustomDocument::getUrlUnderMouse() QUrl CustomDocument::getUrlUnderMouse()
{ {
// place a temp cursor at the mouse position // place a temp cursor at the mouse position
//获取当前鼠标光标在全局坐标系中的位置
//并将其映射到视图端口的局部坐标系中
// 存储在 pos变量中。
auto pos = viewport()->mapFromGlobal(QCursor::pos()); auto pos = viewport()->mapFromGlobal(QCursor::pos());
//使用 cursorForPosition 方法创建一个 QTextCursor 对象,该对象位于 pos 指定的位置
QTextCursor cursor = cursorForPosition(pos); QTextCursor cursor = cursorForPosition(pos);
//获取当前 QTextCursor 的位置,即光标在文档中的索引位置
const int cursorPosition = cursor.position(); const int cursorPosition = cursor.position();
// select the text of the current block // select the text of the current block
//将 QTextCursor 移动到当前文本块的开始位置
cursor.movePosition(QTextCursor::StartOfBlock); cursor.movePosition(QTextCursor::StartOfBlock);
//计算光标在当前文本块中的索引位置,即光标位置与文本块开始位置的差值
const int indexInBlock = cursorPosition - cursor.position(); const int indexInBlock = cursorPosition - cursor.position();
//将 QTextCursor 移动到当前文本块的结束位置,并保持起始位置不变
//即选择文本块中的所有文本
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
//先放到块的首段,再放到块的尾端,即选中文本块中所有文本,并且可以计算出当前游标的位置,即链接的位置
// get the correct link from the selected text, or an empty URL if none found // get the correct link from the selected text, or an empty URL if none found
//调用 getMarkdownUrlAtPosition 函数,传入选中的文本和光标在文本块中的索引位置,获取对应的 Markdown URL并将其转换为 QUrl 对象返回。如果未找到 URL则返回空的 QUrl 对象
return QUrl(getMarkdownUrlAtPosition(cursor.selectedText(), indexInBlock)); return QUrl(getMarkdownUrlAtPosition(cursor.selectedText(), indexInBlock));
} }
@ -174,51 +158,30 @@ QUrl CustomDocument::getUrlUnderMouse()
bool CustomDocument::openLinkAtCursorPosition() bool CustomDocument::openLinkAtCursorPosition()
{ {
// find out which url in the selected text was clicked // find out which url in the selected text was clicked
//先转化成QString变量
QUrl const url = getUrlUnderMouse(); QUrl const url = getUrlUnderMouse();
QString const urlString = url.toString(); QString const urlString = url.toString();
//检查是否为本地文件类型
const bool isFileUrl = urlString.startsWith(QLatin1String("file://")); const bool isFileUrl = urlString.startsWith(QLatin1String("file://"));
//检查是否为遗留的附件URL?
const bool isLegacyAttachmentUrl = urlString.startsWith(QLatin1String("file://attachments")); const bool isLegacyAttachmentUrl = urlString.startsWith(QLatin1String("file://attachments"));
//检查是否为本地文件夹类型
const bool isLocalFilePath = urlString.startsWith(QLatin1String("/")); const bool isLocalFilePath = urlString.startsWith(QLatin1String("/"));
//定义一个标志变量,指示是否将本地文件路径转换为 URL。这里硬编码为 true
const bool convertLocalFilepathsToURLs = true; const bool convertLocalFilepathsToURLs = true;
//如果 URL 是有效的,并且通过 isValidUrl 函数验证,或者是文件 URL、本地文件路径或遗留附件 URL则进入后续处理逻辑
//其中url.isValid()是基本验证,是 QUrl 类的成员函数
//用于检查 URL 是否符合基本的 URL 格式规范
//它会验证 URL 是否包含有效的 scheme、host 和 path 等部分,但不会对 URL 的具体内容进行深入的验证
//而isValidUrl是自定义的函数可以对url内容进行核验
//例如isValidUrl 可能会检查 URL 是否指向一个有效的资源
//或者是否符合特定的 URL 模式(如特定的域名或路径格式)
//它还可以结合应用程序的上下文来判断 URL 是否有效,比如检查数据库中是否存在对应的记录等
// 但是在下面的实现中,很显然作者并没有实现这样的功能,只是检查了基本格式,可能需要完善
//两个检验程序依次递增,符合提高效率的要求
//如不符合基本URL的格式规范就不用调用自定义的函数进行搜索和审查降低了对算力的要求。
if ((url.isValid() && isValidUrl(urlString)) || isFileUrl || isLocalFilePath if ((url.isValid() && isValidUrl(urlString)) || isFileUrl || isLocalFilePath
|| isLegacyAttachmentUrl) { || isLegacyAttachmentUrl) {
//如果 URL 的 scheme协议包含在 _ignoredClickUrlSchemata 集合中,则忽略该 URL 并返回 false
//可能是自定义内容的一部分,可以实现链接的自定义实现
//提高了用户的自定义能力,值得借鉴
if (_ignoredClickUrlSchemata.contains(url.scheme())) { if (_ignoredClickUrlSchemata.contains(url.scheme())) {
qDebug() << __func__ << "ignored URL scheme:" << urlString; qDebug() << __func__ << "ignored URL scheme:" << urlString;
return false; return false;
} }
// ignore non-existent files // ignore non-existent files
//检查文件URL
if (isFileUrl) { if (isFileUrl) {
//从第8个字符开始提取
QString trimmed = urlString.mid(7); QString trimmed = urlString.mid(7);
if (!QFile::exists(trimmed)) { if (!QFile::exists(trimmed)) {
qDebug() << __func__ << ": File does not exist:" << urlString; qDebug() << __func__ << ": File does not exist:" << urlString;
// show a message box // show a message box
//使用 QMessageBox::warning 显示一个警告信息框,告知用户文件不存在
//信息框的标题是 "File not found"
//内容是 "The file <strong>%1</strong> does not exist."
//其中 %1 被替换为实际的文件路径 trimmed使用<strong> 标签以突出显示
//提示我们可以在输出的deBug中使用html等标签语言增加信息的分辨度以更快的进行筛选错误
QMessageBox::warning( QMessageBox::warning(
nullptr, tr("File not found"), nullptr, tr("File not found"),
tr("The file <strong>%1</strong> does not exist.").arg(trimmed)); tr("The file <strong>%1</strong> does not exist.").arg(trimmed));
@ -233,8 +196,7 @@ bool CustomDocument::openLinkAtCursorPosition()
tr("The file <strong>%1</strong> does not exist.").arg(urlString)); tr("The file <strong>%1</strong> does not exist.").arg(urlString));
return false; return false;
} }
//验证完毕后再统一集中打开链接,有利于对打开链接的种类进行集中调控
//在后期的纠错过程中将打开部分的函数放在一起更有利于代码的纠错和DeBug
if (isLocalFilePath && convertLocalFilepathsToURLs) { if (isLocalFilePath && convertLocalFilepathsToURLs) {
openUrl(QString("file://") + urlString); openUrl(QString("file://") + urlString);
} else { } else {
@ -243,7 +205,7 @@ bool CustomDocument::openLinkAtCursorPosition()
return true; return true;
} }
//查找到不是URL就返回false其余均为true以保证程序的顺利执行
return false; return false;
} }
@ -253,20 +215,8 @@ bool CustomDocument::openLinkAtCursorPosition()
* @param urlString * @param urlString
* @return * @return
*/ */
//在注释中我们可以了解到函数的作用时间差是否为可达的路径
//函数需要一个urString返回某个值
//实际来看并没有对函数的有效性进行充分验证,函数是失败的
bool CustomDocument::isValidUrl(const QString &urlString) bool CustomDocument::isValidUrl(const QString &urlString)
{ {
//关于正则表达式
//使用 QRegularExpression 类来创建一个正则表达式对象,并使用该对象的 match 方法来匹配 urlString
//正则表达式中,^表示匹配的开始
//\w+表示有一个或多个单词字符(字母、数字或下划线)
// 这里应该表示的是http或https或file等之类的我认为应该写的更详细一些因为常用的协议就那么些
// 两个反斜杠
//.+表示匹配一个或多个任意字符
// 这里表示的应该是主机及内部的文件路径,没有固定格式
//总的来说,这里的匹配在我看来很糟糕,没有起到多少自定义的作用
const QRegularExpressionMatch match = QRegularExpression(R"(^\w+:\/\/.+)").match(urlString); const QRegularExpressionMatch match = QRegularExpression(R"(^\w+:\/\/.+)").match(urlString);
return match.hasMatch(); return match.hasMatch();
} }
@ -280,7 +230,6 @@ bool CustomDocument::isValidUrl(const QString &urlString)
* "/path/to/my/file/QOwnNotes.pdf" if the operating system supports that * "/path/to/my/file/QOwnNotes.pdf" if the operating system supports that
* handler * handler
*/ */
//并没有对函数调用做出一定的检查,直接调用函数库打开链接
void CustomDocument::openUrl(const QString &urlString) void CustomDocument::openUrl(const QString &urlString)
{ {
qDebug() << "CustomDocument " << __func__ << " - 'urlString': " << urlString; qDebug() << "CustomDocument " << __func__ << " - 'urlString': " << urlString;
@ -294,7 +243,6 @@ void CustomDocument::openUrl(const QString &urlString)
* @param text * @param text
* @return parsed urls * @return parsed urls
*/ */
//主要作用是对输入的文本内的链接进行提取
QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString &text) QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString &text)
{ {
QMap<QString, QString> urlMap; QMap<QString, QString> urlMap;
@ -303,7 +251,6 @@ QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString &
// match urls like this: <http://mylink> // match urls like this: <http://mylink>
// re = QRegularExpression("(<(.+?:\\/\\/.+?)>)"); // re = QRegularExpression("(<(.+?:\\/\\/.+?)>)");
//匹配链接类型为<http://mylink>
regex = QRegularExpression(QStringLiteral("(<(.+?)>)")); regex = QRegularExpression(QStringLiteral("(<(.+?)>)"));
iterator = regex.globalMatch(text); iterator = regex.globalMatch(text);
while (iterator.hasNext()) { while (iterator.hasNext()) {
@ -312,7 +259,7 @@ QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString &
QString url = match.captured(2); QString url = match.captured(2);
urlMap[linkText] = url; urlMap[linkText] = url;
} }
//匹配链接类型为[链接文本](链接)
regex = QRegularExpression(R"((\[.*?\]\((.+?)\)))"); regex = QRegularExpression(R"((\[.*?\]\((.+?)\)))");
iterator = regex.globalMatch(text); iterator = regex.globalMatch(text);
while (iterator.hasNext()) { while (iterator.hasNext()) {
@ -323,7 +270,6 @@ QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString &
} }
// match urls like this: http://mylink // match urls like this: http://mylink
//匹配裸链接
regex = QRegularExpression(R"(\b\w+?:\/\/[^\s]+[^\s>\)])"); regex = QRegularExpression(R"(\b\w+?:\/\/[^\s]+[^\s>\)])");
iterator = regex.globalMatch(text); iterator = regex.globalMatch(text);
while (iterator.hasNext()) { while (iterator.hasNext()) {
@ -333,7 +279,6 @@ QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString &
} }
// match urls like this: www.github.com // match urls like this: www.github.com
//匹配以www开头的链接
regex = QRegularExpression(R"(\bwww\.[^\s]+\.[^\s]+\b)"); regex = QRegularExpression(R"(\bwww\.[^\s]+\.[^\s]+\b)");
iterator = regex.globalMatch(text); iterator = regex.globalMatch(text);
while (iterator.hasNext()) { while (iterator.hasNext()) {
@ -344,7 +289,6 @@ QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString &
// match reference urls like this: [this url][1] with this later: // match reference urls like this: [this url][1] with this later:
// [1]: http://domain // [1]: http://domain
//匹配引用定义的链接
regex = QRegularExpression(R"((\[.*?\]\[(.+?)\]))"); regex = QRegularExpression(R"((\[.*?\]\[(.+?)\]))");
iterator = regex.globalMatch(text); iterator = regex.globalMatch(text);
while (iterator.hasNext()) { while (iterator.hasNext()) {
@ -365,8 +309,6 @@ QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString &
return urlMap; return urlMap;
} }
//向上移动一个块
//具体操作是,先删除本块内容,再移动到上一块的开始处,插入本块和换行,最后移动光标到指定位置
void CustomDocument::moveBlockUp() void CustomDocument::moveBlockUp()
{ {
QTextCursor cursor = textCursor(); QTextCursor cursor = textCursor();
@ -400,7 +342,6 @@ void CustomDocument::moveBlockUp()
} }
} }
//向下移动一个块,同理
void CustomDocument::moveBlockDown() void CustomDocument::moveBlockDown()
{ {
QTextCursor cursor = textCursor(); QTextCursor cursor = textCursor();

@ -3,10 +3,8 @@
#include <QVariant> #include <QVariant>
#include <qdebug.h> #include <qdebug.h>
// 重载<<运算符,将枚举类型的值输出到标准输出流
EditorSettingsOptions::EditorSettingsOptions(QObject *) { } EditorSettingsOptions::EditorSettingsOptions(QObject *) { }
//选用switch来选择要输出的枚举值
std::ostream &operator<<(std::ostream &os, const FontTypeface::Value &fontTypeface) std::ostream &operator<<(std::ostream &os, const FontTypeface::Value &fontTypeface)
{ {
switch (fontTypeface) { switch (fontTypeface) {
@ -39,7 +37,6 @@ std::ostream &operator<<(std::ostream &os, const Theme::Value &theme)
return os; return os;
} }
//使用str()函数来重载to_string函数
std::string to_string(FontTypeface::Value fontTypeface) std::string to_string(FontTypeface::Value fontTypeface)
{ {
std::ostringstream oss; std::ostringstream oss;
@ -54,13 +51,11 @@ std::string to_string(Theme::Value theme)
return oss.str(); return oss.str();
} }
//函数调用,没看懂什么意思,是要再进行功能扩展吗
void setCSSThemeAndUpdate(QWidget *obj, Theme::Value theme) void setCSSThemeAndUpdate(QWidget *obj, Theme::Value theme)
{ {
//标准输入输出
setCSSClassesAndUpdate(obj, QString::fromStdString(to_string(theme)).toLower().toStdString()); setCSSClassesAndUpdate(obj, QString::fromStdString(to_string(theme)).toLower().toStdString());
} }
//更新
void setCSSClassesAndUpdate(QWidget *obj, std::string classNames) void setCSSClassesAndUpdate(QWidget *obj, std::string classNames)
{ {
if (obj->styleSheet().isEmpty()) { if (obj->styleSheet().isEmpty()) {

@ -2,13 +2,12 @@
#define EDITORSETTINGSOPTIONS_H #define EDITORSETTINGSOPTIONS_H
#include <QObject> #include <QObject>
//声明QtQml/qqml.h库这个库包含Qt QML相关文件可将C++类暴露给QML
#include <QtQml/qqml.h> #include <QtQml/qqml.h>
#include <sstream> #include <sstream>
#include <QString> #include <QString>
#include <QWidget> #include <QWidget>
#include "lqtutils_enum.h" #include "lqtutils_enum.h"
//声明枚举类型
L_DECLARE_ENUM(FontTypeface, SansSerif, Serif, Mono) L_DECLARE_ENUM(FontTypeface, SansSerif, Serif, Mono)
L_DECLARE_ENUM(FontSizeAction, FontSizeIncrease, FontSizeDecrease) L_DECLARE_ENUM(FontSizeAction, FontSizeIncrease, FontSizeDecrease)
L_DECLARE_ENUM(EditorTextWidth, TextWidthIncrease, TextWidthDecrease, TextWidthFullWidth) L_DECLARE_ENUM(EditorTextWidth, TextWidthIncrease, TextWidthDecrease, TextWidthFullWidth)
@ -23,14 +22,11 @@ public:
explicit EditorSettingsOptions(QObject *parent = nullptr); explicit EditorSettingsOptions(QObject *parent = nullptr);
}; };
//重载<<运算符,将枚举类型的值输出到标准输出流
std::ostream &operator<<(std::ostream &os, const Theme::Value &theme); std::ostream &operator<<(std::ostream &os, const Theme::Value &theme);
std::ostream &operator<<(std::ostream &os, const FontTypeface::Value &fontTypeface); std::ostream &operator<<(std::ostream &os, const FontTypeface::Value &fontTypeface);
//为了适配标准输出流,重载字符串转换函数
std::string to_string(FontTypeface::Value fontTypeface); std::string to_string(FontTypeface::Value fontTypeface);
std::string to_string(Theme::Value theme); std::string to_string(Theme::Value theme);
//更新CSS样式和类
void setCSSThemeAndUpdate(QWidget *obj, Theme::Value theme); void setCSSThemeAndUpdate(QWidget *obj, Theme::Value theme);
void setCSSClassesAndUpdate(QWidget *obj, std::string classNames); void setCSSClassesAndUpdate(QWidget *obj, std::string classNames);

@ -1,121 +1,115 @@
#include "foldertreedelegateeditor.h" // 包含FolderTreeDelegateEditor类的声明 #include "foldertreedelegateeditor.h"
#include <QHBoxLayout> // 包含水平布局管理器 #include <QHBoxLayout>
#include <QLabel> // 包含标签控件 #include <QLabel>
#include <QPainter> // 包含绘图器 #include <QPainter>
#include <QDebug> // 包含调试输出 #include <QDebug>
#include <QTreeView> // 包含树视图控件 #include <QTreeView>
#include <QMouseEvent> // 包含鼠标事件 #include <QMouseEvent>
#include "pushbuttontype.h" // 包含自定义按钮类型 #include "pushbuttontype.h"
#include "nodetreemodel.h" // 包含节点树模型 #include "nodetreemodel.h"
#include "nodetreeview.h" // 包含节点树视图 #include "nodetreeview.h"
#include "notelistview.h" // 包含笔记列表视图 #include "notelistview.h"
#include "labeledittype.h" // 包含标签编辑类型 #include "labeledittype.h"
#include "fontloader.h" // 包含字体加载器 #include "fontloader.h"
// FolderTreeDelegateEditor构造函数
FolderTreeDelegateEditor::FolderTreeDelegateEditor(QTreeView *view, FolderTreeDelegateEditor::FolderTreeDelegateEditor(QTreeView *view,
const QStyleOptionViewItem &option, const QStyleOptionViewItem &option,
const QModelIndex &index, QListView *listView, const QModelIndex &index, QListView *listView,
QWidget *parent) QWidget *parent)
: QWidget(parent), // 调用QWidget构造函数 : QWidget(parent),
m_option(option), // 初始化选项 m_option(option),
m_index(index), // 初始化索引 m_index(index),
#ifdef __APPLE__ // 如果是苹果系统 #ifdef __APPLE__
m_displayFont(QFont(QStringLiteral("SF Pro Text")).exactMatch() m_displayFont(QFont(QStringLiteral("SF Pro Text")).exactMatch()
? QStringLiteral("SF Pro Text") // 使用系统字体 ? QStringLiteral("SF Pro Text")
: QStringLiteral("Roboto")), // 否则使用Roboto字体 : QStringLiteral("Roboto")),
#elif _WIN32 // 如果是Windows系统 #elif _WIN32
m_displayFont(QFont(QStringLiteral("Segoe UI")).exactMatch() ? QStringLiteral("Segoe UI") m_displayFont(QFont(QStringLiteral("Segoe UI")).exactMatch() ? QStringLiteral("Segoe UI")
: QStringLiteral("Roboto")), : QStringLiteral("Roboto")),
#else // 其他系统 #else
m_displayFont(QStringLiteral("Roboto")), m_displayFont(QStringLiteral("Roboto")),
#endif #endif
#ifdef __APPLE__ // 如果是苹果系统 #ifdef __APPLE__
m_titleFont(m_displayFont, 13, QFont::DemiBold), // 设置标题字体 m_titleFont(m_displayFont, 13, QFont::DemiBold),
#else #else
m_titleFont(m_displayFont, 10, QFont::DemiBold), // 设置标题字体 m_titleFont(m_displayFont, 10, QFont::DemiBold),
#endif #endif
m_titleColor(26, 26, 26), // 设置标题颜色 m_titleColor(26, 26, 26),
m_titleSelectedColor(255, 255, 255), // 设置选中标题颜色 m_titleSelectedColor(255, 255, 255),
m_activeColor(68, 138, 201), // 设置活动颜色 m_activeColor(68, 138, 201),
m_hoverColor(207, 207, 207), // 设置悬浮颜色 m_hoverColor(207, 207, 207),
m_folderIconColor(68, 138, 201), // 设置文件夹图标颜色 m_folderIconColor(68, 138, 201),
m_view(view), // 初始化视图 m_view(view),
m_listView(listView), // 初始化列表视图 m_listView(listView),
m_theme(Theme::Light) // 初始化主题为浅色 m_theme(Theme::Light)
{ {
setContentsMargins(0, 0, 0, 0); // 设置内容边距 setContentsMargins(0, 0, 0, 0);
auto layout = new QHBoxLayout(this); // 创建水平布局 auto layout = new QHBoxLayout(this);
layout->setContentsMargins(10, 0, 0, 0); // 设置布局边距 layout->setContentsMargins(10, 0, 0, 0);
layout->setSpacing(0); // 设置布局间隔 layout->setSpacing(0);
setLayout(layout); // 设置布局 setLayout(layout);
m_expandIcon = new QLabel(this);
m_expandIcon = new QLabel(this); // 创建展开图标标签 m_expandIcon->setMinimumSize({ 11, 11 });
m_expandIcon->setMinimumSize({ 11, 11 }); // 设置最小尺寸 m_expandIcon->setMaximumSize({ 11, 11 });
m_expandIcon->setMaximumSize({ 11, 11 }); // 设置最大尺寸
// 根据索引数据判断是否可展开,并相应地添加布局间隔
if (m_index.data(NodeItem::Roles::IsExpandable).toBool()) { if (m_index.data(NodeItem::Roles::IsExpandable).toBool()) {
if (!m_view->isExpanded(m_index)) { if (!m_view->isExpanded(m_index)) {
layout->addSpacing(2); layout->addSpacing(2);
} }
} }
#ifdef __APPLE__ // 如果是苹果系统 #ifdef __APPLE__
int iconPointSizeOffset = 0; // 图标点大小偏移量 int iconPointSizeOffset = 0;
#else #else
int iconPointSizeOffset = -4; // 图标点大小偏移量 int iconPointSizeOffset = -4;
#endif #endif
m_expandIcon->setFont(FontLoader::getInstance().loadFont("Font Awesome 6 Free Solid", "", m_expandIcon->setFont(FontLoader::getInstance().loadFont("Font Awesome 6 Free Solid", "",
10 + iconPointSizeOffset)); // 加载字体 10 + iconPointSizeOffset));
m_expandIcon->setScaledContents(true); // 设置缩放内容 m_expandIcon->setScaledContents(true);
layout->addWidget(m_expandIcon); // 将展开图标添加到布局 layout->addWidget(m_expandIcon);
// 如果索引不可展开或已展开,添加布局间隔
if (!m_index.data(NodeItem::Roles::IsExpandable).toBool() if (!m_index.data(NodeItem::Roles::IsExpandable).toBool()
|| (m_index.data(NodeItem::Roles::IsExpandable).toBool() && m_view->isExpanded(m_index))) { || (m_index.data(NodeItem::Roles::IsExpandable).toBool() && m_view->isExpanded(m_index))) {
layout->addSpacing(2); layout->addSpacing(2);
} }
m_folderIcon = new PushButtonType(parent); // 创建文件夹图标按钮 m_folderIcon = new PushButtonType(parent);
m_folderIcon->setMaximumSize({ 19, 20 }); // 设置最大尺寸 m_folderIcon->setMaximumSize({ 19, 20 });
m_folderIcon->setMinimumSize({ 19, 20 }); // 设置最小尺寸 m_folderIcon->setMinimumSize({ 19, 20 });
m_folderIcon->setIconSize(QSize(19, 20)); // 设置图标尺寸 m_folderIcon->setIconSize(QSize(19, 20));
QFont materialSymbols("Material Symbols Outlined", 16 + iconPointSizeOffset); // 加载字体 QFont materialSymbols("Material Symbols Outlined", 16 + iconPointSizeOffset);
m_folderIcon->setFont(materialSymbols); // 设置字体 m_folderIcon->setFont(materialSymbols);
m_folderIcon->setText(u8"\ue2c7"); // 设置文本为文件夹图标 m_folderIcon->setText(u8"\ue2c7"); // folder
layout->addWidget(m_folderIcon); // 将文件夹图标添加到布局 layout->addWidget(m_folderIcon);
layout->addSpacing(5); // 添加布局间隔 layout->addSpacing(5);
m_label = new LabelEditType(this); // 创建标签编辑类型 m_label = new LabelEditType(this);
m_label->setFont(m_titleFont); // 设置字体 m_label->setFont(m_titleFont);
QSizePolicy labelPolicy; // 创建尺寸策略 QSizePolicy labelPolicy;
labelPolicy.setVerticalPolicy(QSizePolicy::Expanding); // 设置垂直策略为扩展 labelPolicy.setVerticalPolicy(QSizePolicy::Expanding);
labelPolicy.setHorizontalPolicy(QSizePolicy::Expanding); // 设置水平策略为扩展 labelPolicy.setHorizontalPolicy(QSizePolicy::Expanding);
m_label->setSizePolicy(labelPolicy); // 设置尺寸策略 m_label->setSizePolicy(labelPolicy);
m_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); // 设置对齐方式 m_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
connect(m_label, &LabelEditType::editingStarted, this, [this] { // 连接编辑开始信号 connect(m_label, &LabelEditType::editingStarted, this, [this] {
auto tree_view = dynamic_cast<NodeTreeView *>(m_view); // 转换视图为NodeTreeView类型 auto tree_view = dynamic_cast<NodeTreeView *>(m_view);
tree_view->setIsEditing(true); // 设置编辑状态为真 tree_view->setIsEditing(true);
}); });
connect(m_label, &LabelEditType::editingFinished, this, connect(m_label, &LabelEditType::editingFinished, this, [this](const QString &label) {
[this](const QString &label) { // 连接编辑结束信号 auto tree_view = dynamic_cast<NodeTreeView *>(m_view);
auto tree_view = dynamic_cast<NodeTreeView *>(m_view); // 转换视图为NodeTreeView类型 tree_view->onRenameFolderFinished(label);
tree_view->onRenameFolderFinished(label); // 重命名文件夹 tree_view->setIsEditing(false);
tree_view->setIsEditing(false); // 设置编辑状态为假
}); });
connect(dynamic_cast<NodeTreeView *>(m_view), &NodeTreeView::renameFolderRequested, connect(dynamic_cast<NodeTreeView *>(m_view), &NodeTreeView::renameFolderRequested, m_label,
m_label, // 连接重命名请求信号 &LabelEditType::openEditor);
&LabelEditType::openEditor); // 打开编辑器 layout->addWidget(m_label);
layout->addWidget(m_label); // 将标签添加到布局 layout->addSpacing(5);
layout->addSpacing(5); // 添加布局间隔 m_contextButton = new PushButtonType(parent);
m_contextButton = new PushButtonType(parent); // 创建上下文按钮 m_contextButton->setMaximumSize({ 33, 25 });
m_contextButton->setMaximumSize({ 33, 25 }); // 设置最大尺寸 m_contextButton->setMinimumSize({ 33, 25 });
m_contextButton->setMinimumSize({ 33, 25 }); // 设置最小尺寸 m_contextButton->setCursor(QCursor(Qt::PointingHandCursor));
m_contextButton->setCursor(QCursor(Qt::PointingHandCursor)); // 设置鼠标指针为手形 m_contextButton->setFocusPolicy(Qt::TabFocus);
m_contextButton->setFocusPolicy(Qt::TabFocus); // 设置焦点策略为Tab焦点 if (m_view->selectionModel()->isSelected(m_index)) {
if (m_view->selectionModel()->isSelected(m_index)) { // 如果索引被选中
m_contextButton->setStyleSheet(QStringLiteral(R"(QPushButton { )" m_contextButton->setStyleSheet(QStringLiteral(R"(QPushButton { )"
R"( border: none; )" R"( border: none; )"
R"( padding: 0px; )" R"( padding: 0px; )"

@ -1,55 +1,52 @@
#ifndef FOLDERTREEDELEGATEEDITOR_H // 头文件保护 #ifndef FOLDERTREEDELEGATEEDITOR_H
#define FOLDERTREEDELEGATEEDITOR_H #define FOLDERTREEDELEGATEEDITOR_H
#include <QWidget> // 包含QWidget类 #include <QWidget>
#include <QStyleOptionViewItem> // 包含样式选项视图项 #include <QStyleOptionViewItem>
#include <QModelIndex> // 包含模型索引 #include <QModelIndex>
#include <QFont> // 包含字体类 #include <QFont>
#include "editorsettingsoptions.h" // 包含编辑器设置选项 #include "editorsettingsoptions.h"
class QTreeView; // 前向声明QTreeView类 class QTreeView;
class QLabel; // 前向声明QLabel类 class QLabel;
class PushButtonType; // 前向声明PushButtonType类 class PushButtonType;
class LabelEditType; // 前向声明LabelEditType类 class LabelEditType;
class QListView; // 前向声明QListView类 class QListView;
class FolderTreeDelegateEditor : public QWidget // 继承自QWidget class FolderTreeDelegateEditor : public QWidget
{ {
Q_OBJECT // 宏用于支持Qt的信号和槽机制 Q_OBJECT
public:
public explicit FolderTreeDelegateEditor(QTreeView *view, const QStyleOptionViewItem &option,
: explicit FolderTreeDelegateEditor(QTreeView *view,
const QStyleOptionViewItem &option, // 构造函数
const QModelIndex &index, QListView *listView, const QModelIndex &index, QListView *listView,
QWidget *parent = nullptr); QWidget *parent = nullptr);
void setTheme(Theme::Value theme); // 设置主题的方法 void setTheme(Theme::Value theme);
private: private:
QStyleOptionViewItem m_option; // 样式选项视图项 QStyleOptionViewItem m_option;
QModelIndex m_index; // 模型索引 QModelIndex m_index;
QString m_displayFont; // 显示字体 QString m_displayFont;
QFont m_titleFont; // 标题字体 QFont m_titleFont;
QColor m_titleColor; // 标题颜色 QColor m_titleColor;
QColor m_titleSelectedColor; // 选中标题颜色 QColor m_titleSelectedColor;
QColor m_activeColor; // 活动颜色 QColor m_activeColor;
QColor m_hoverColor; // 悬浮颜色 QColor m_hoverColor;
QColor m_folderIconColor; // 文件夹图标颜色 QColor m_folderIconColor;
QTreeView *m_view; // 树视图 QTreeView *m_view;
QListView *m_listView; // 列表视图 QListView *m_listView;
LabelEditType *m_label; // 标签编辑类型 LabelEditType *m_label;
PushButtonType *m_folderIcon; // 文件夹图标按钮 PushButtonType *m_folderIcon;
QPixmap m_expanded; // 展开的图标 QPixmap m_expanded;
QPixmap m_notExpanded; // 未展开的图标 QPixmap m_notExpanded;
QLabel *m_expandIcon; // 展开图标标签 QLabel *m_expandIcon;
PushButtonType *m_contextButton; // 上下文按钮 PushButtonType *m_contextButton;
Theme::Value m_theme; // 主题枚举值 Theme::Value m_theme;
void updateDelegate();
void updateDelegate(); // 更新委托的方法
// QWidget interface
// QWidget接口
protected: protected:
virtual void paintEvent(QPaintEvent *event) override; // 重写绘画事件 virtual void paintEvent(QPaintEvent *event) override;
virtual void mouseDoubleClickEvent(QMouseEvent *event) override; // 重写鼠标双击事件 virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
}; };
#endif // FOLDERTREEDELEGATEEDITOR_H #endif // FOLDERTREEDELEGATEEDITOR_H

@ -3,7 +3,6 @@
#include <QBoxLayout> #include <QBoxLayout>
#include <QDebug> #include <QDebug>
// LabelEditType 类的构造函数
LabelEditType::LabelEditType(QWidget *parent) : QLabel(parent) LabelEditType::LabelEditType(QWidget *parent) : QLabel(parent)
{ {
setContentsMargins(0, 0, 0, 0); setContentsMargins(0, 0, 0, 0);
@ -16,7 +15,6 @@ LabelEditType::LabelEditType(QWidget *parent) : QLabel(parent)
connect(m_editor, &QLineEdit::editingFinished, this, &LabelEditType::onFinishedEdit); connect(m_editor, &QLineEdit::editingFinished, this, &LabelEditType::onFinishedEdit);
} }
// 打开编辑器
void LabelEditType::openEditor() void LabelEditType::openEditor()
{ {
m_editor->setText(text()); m_editor->setText(text());
@ -27,7 +25,6 @@ void LabelEditType::openEditor()
emit editingStarted(); emit editingStarted();
} }
// 编辑完成后的处理
void LabelEditType::onFinishedEdit() void LabelEditType::onFinishedEdit()
{ {
m_editor->hide(); m_editor->hide();

@ -3,31 +3,24 @@
#include <QLabel> #include <QLabel>
// 自定义LabelEditType类继承自QLabel
class QLineEdit; class QLineEdit;
class LabelEditType : public QLabel class LabelEditType : public QLabel
{ {
Q_OBJECT Q_OBJECT
public: public:
// 构造函数,接受一个父部件指针
explicit LabelEditType(QWidget *parent = nullptr); explicit LabelEditType(QWidget *parent = nullptr);
public slots: public slots:
// 打开编辑器的槽函数
void openEditor(); void openEditor();
private slots: private slots:
// 编辑完成后的槽函数
void onFinishedEdit(); void onFinishedEdit();
signals: signals:
// 编辑开始信号
void editingStarted(); void editingStarted();
// 编辑完成信号,携带编辑的文本
void editingFinished(const QString &text); void editingFinished(const QString &text);
private: private:
// 编辑器指针
QLineEdit *m_editor; QLineEdit *m_editor;
}; };

@ -10,7 +10,6 @@
#include "tagpool.h" #include "tagpool.h"
#include <QTimer> #include <QTimer>
// 检查当前笔记 ID 是否无效
static bool isInvalidCurrentNotesId(const QSet<int> &currentNotesId) static bool isInvalidCurrentNotesId(const QSet<int> &currentNotesId)
{ {
if (currentNotesId.isEmpty()) { if (currentNotesId.isEmpty()) {
@ -25,7 +24,6 @@ static bool isInvalidCurrentNotesId(const QSet<int> &currentNotesId)
return isInvalid; return isInvalid;
} }
// ListViewLogic 类的构造函数,负责初始化相关组件并设置信号与槽
ListViewLogic::ListViewLogic(NoteListView *noteView, NoteListModel *noteModel, ListViewLogic::ListViewLogic(NoteListView *noteView, NoteListModel *noteModel,
QLineEdit *searchEdit, QToolButton *clearButton, TagPool *tagPool, QLineEdit *searchEdit, QToolButton *clearButton, TagPool *tagPool,
DBManager *dbManager, QObject *parent) DBManager *dbManager, QObject *parent)
@ -127,7 +125,6 @@ ListViewLogic::ListViewLogic(NoteListView *noteView, NoteListModel *noteModel,
&ListViewLogic::onListViewClicked); &ListViewLogic::onListViewClicked);
} }
// 选择指定的笔记
void ListViewLogic::selectNote(const QModelIndex &noteIndex) void ListViewLogic::selectNote(const QModelIndex &noteIndex)
{ {
if (noteIndex.isValid()) { if (noteIndex.isValid()) {
@ -141,7 +138,6 @@ void ListViewLogic::selectNote(const QModelIndex &noteIndex)
} }
} }
// 将笔记移动到列表顶部
void ListViewLogic::moveNoteToTop(const NodeData &note) void ListViewLogic::moveNoteToTop(const NodeData &note)
{ {
QModelIndex noteIndex = m_listModel->getNoteIndex(note.id()); QModelIndex noteIndex = m_listModel->getNoteIndex(note.id());
@ -168,7 +164,6 @@ void ListViewLogic::moveNoteToTop(const NodeData &note)
} }
} }
// 设置笔记的数据
void ListViewLogic::setNoteData(const NodeData &note) void ListViewLogic::setNoteData(const NodeData &note)
{ {
QModelIndex noteIndex = m_listModel->getNoteIndex(note.id()); QModelIndex noteIndex = m_listModel->getNoteIndex(note.id());
@ -193,7 +188,6 @@ void ListViewLogic::setNoteData(const NodeData &note)
} }
} }
// 处理笔记编辑关闭事件
void ListViewLogic::onNoteEditClosed(const NodeData &note, bool selectNext) void ListViewLogic::onNoteEditClosed(const NodeData &note, bool selectNext)
{ {
if (note.isTempNote()) { if (note.isTempNote()) {
@ -215,14 +209,12 @@ void ListViewLogic::onNoteEditClosed(const NodeData &note, bool selectNext)
} }
} }
// 请求删除笔记
void ListViewLogic::deleteNoteRequested(const NodeData &note) void ListViewLogic::deleteNoteRequested(const NodeData &note)
{ {
auto index = m_listModel->getNoteIndex(note.id()); auto index = m_listModel->getNoteIndex(note.id());
deleteNoteRequestedI({ index }); deleteNoteRequestedI({ index });
} }
// 选择当前笔记的上一笔记
void ListViewLogic::selectNoteUp() void ListViewLogic::selectNoteUp()
{ {
auto currentIndex = m_listView->currentIndex(); auto currentIndex = m_listView->currentIndex();
@ -243,7 +235,6 @@ void ListViewLogic::selectNoteUp()
} }
} }
// 选择当前笔记的下一笔记
void ListViewLogic::selectNoteDown() void ListViewLogic::selectNoteDown()
{ {
auto currentIndex = m_listView->currentIndex(); auto currentIndex = m_listView->currentIndex();
@ -268,14 +259,15 @@ void ListViewLogic::selectNoteDown()
/*! /*!
* \brief ListViewLogic::onSearchEditTextChanged * \brief ListViewLogic::onSearchEditTextChanged
* * When text on searchEdit change:
* "新笔记" * If there is a temp note "New Note" while searching, we delete it
* 便 * Saving the last selected note for recovery after searching
* * Clear all the notes from scrollArea and
* * If text is empty, reload all the notes from database
* * Else, load all the notes contain the string in searchEdit from database
* \param keyword * \param keyword
*/ */
void ListViewLogic::onSearchEditTextChanged(const QString &keyword) void ListViewLogic::onSearchEditTextChanged(const QString &keyword)
{ {
if (keyword.isEmpty()) { if (keyword.isEmpty()) {
@ -295,7 +287,6 @@ void ListViewLogic::onSearchEditTextChanged(const QString &keyword)
} }
} }
// 清除搜索
void ListViewLogic::clearSearch(bool createNewNote, int scrollToId) void ListViewLogic::clearSearch(bool createNewNote, int scrollToId)
{ {
m_listViewInfo.needCreateNewNote = createNewNote; m_listViewInfo.needCreateNewNote = createNewNote;
@ -304,7 +295,6 @@ void ListViewLogic::clearSearch(bool createNewNote, int scrollToId)
emit requestClearSearchUI(); emit requestClearSearchUI();
} }
// 加载笔记列表模型
void ListViewLogic::loadNoteListModel(const QVector<NodeData> &noteList, const ListViewInfo &inf) void ListViewLogic::loadNoteListModel(const QVector<NodeData> &noteList, const ListViewInfo &inf)
{ {
auto currentNotesId = m_listViewInfo.currentNotesId; auto currentNotesId = m_listViewInfo.currentNotesId;
@ -373,7 +363,6 @@ void ListViewLogic::loadNoteListModel(const QVector<NodeData> &noteList, const L
selectFirstNote(); selectFirstNote();
} }
// 添加标签请求处理
void ListViewLogic::onAddTagRequest(const QModelIndex &index, int tagId) void ListViewLogic::onAddTagRequest(const QModelIndex &index, int tagId)
{ {
if (index.isValid()) { if (index.isValid()) {
@ -393,14 +382,12 @@ void ListViewLogic::onAddTagRequest(const QModelIndex &index, int tagId)
} }
} }
// 直接请求添加标签
void ListViewLogic::onAddTagRequestD(int noteId, int tagId) void ListViewLogic::onAddTagRequestD(int noteId, int tagId)
{ {
auto index = m_listModel->getNoteIndex(noteId); auto index = m_listModel->getNoteIndex(noteId);
onAddTagRequest(index, tagId); onAddTagRequest(index, tagId);
} }
// 笔记移动到其他区域的处理
void ListViewLogic::onNoteMovedOut(int nodeId, int targetId) void ListViewLogic::onNoteMovedOut(int nodeId, int targetId)
{ {
auto index = m_listModel->getNoteIndex(nodeId); auto index = m_listModel->getNoteIndex(nodeId);
@ -428,7 +415,6 @@ void ListViewLogic::onNoteMovedOut(int nodeId, int targetId)
} }
} }
// 设置最后选择的笔记
void ListViewLogic::setLastSelectedNote() void ListViewLogic::setLastSelectedNote()
{ {
auto indexes = m_listView->selectedIndex(); auto indexes = m_listView->selectedIndex();
@ -441,13 +427,11 @@ void ListViewLogic::setLastSelectedNote()
setLastSavedState(ids, 0); setLastSavedState(ids, 0);
} }
// 请求加载最后选择的笔记
void ListViewLogic::loadLastSelectedNoteRequested() void ListViewLogic::loadLastSelectedNoteRequested()
{ {
requestLoadSavedState(2); requestLoadSavedState(2);
} }
// 处理文件夹中的笔记列表请求
void ListViewLogic::onNotesListInFolderRequested(int parentID, bool isRecursive, bool newNote, void ListViewLogic::onNotesListInFolderRequested(int parentID, bool isRecursive, bool newNote,
int scrollToId) int scrollToId)
{ {
@ -465,7 +449,6 @@ void ListViewLogic::onNotesListInFolderRequested(int parentID, bool isRecursive,
} }
} }
// 处理标签中的笔记列表请求
void ListViewLogic::onNotesListInTagsRequested(const QSet<int> &tagIds, bool newNote, void ListViewLogic::onNotesListInTagsRequested(const QSet<int> &tagIds, bool newNote,
int scrollToId) int scrollToId)
{ {
@ -482,7 +465,6 @@ void ListViewLogic::onNotesListInTagsRequested(const QSet<int> &tagIds, bool new
} }
} }
// 选择多个笔记
void ListViewLogic::selectNotes(const QModelIndexList &indexes) void ListViewLogic::selectNotes(const QModelIndexList &indexes)
{ {
m_listView->clearSelection(); m_listView->clearSelection();
@ -500,7 +482,6 @@ void ListViewLogic::selectNotes(const QModelIndexList &indexes)
onNotePressed(indexes); onNotePressed(indexes);
} }
// 处理移除标签请求
void ListViewLogic::onRemoveTagRequest(const QModelIndex &index, int tagId) void ListViewLogic::onRemoveTagRequest(const QModelIndex &index, int tagId)
{ {
if (index.isValid()) { if (index.isValid()) {
@ -522,13 +503,14 @@ void ListViewLogic::onRemoveTagRequest(const QModelIndex &index, int tagId)
/*! /*!
* \brief MainWindow::onNotePressed * \brief MainWindow::onNotePressed
* * When clicking on a note in the scrollArea:
* * Unhighlight the previous selected note
* * If selecting a note when temporery note exist, delete the temp note
* * Highlight the selected note
* * Load the selected note content into textedit
* \param index * \param index
*/ */
void ListViewLogic::onNotePressed(const QModelIndexList &indexes) void ListViewLogic::onNotePressed(const QModelIndexList &indexes)
{ {
QVector<NodeData> notes; QVector<NodeData> notes;
@ -545,7 +527,6 @@ void ListViewLogic::onNotePressed(const QModelIndexList &indexes)
m_listView->setCurrentRowActive(false); m_listView->setCurrentRowActive(false);
} }
// 删除笔记请求的内部处理
void ListViewLogic::deleteNoteRequestedI(const QModelIndexList &indexes) void ListViewLogic::deleteNoteRequestedI(const QModelIndexList &indexes)
{ {
if (!indexes.empty()) { if (!indexes.empty()) {
@ -601,7 +582,6 @@ void ListViewLogic::deleteNoteRequestedI(const QModelIndexList &indexes)
} }
} }
// 请求恢复笔记的内部处理
void ListViewLogic::restoreNotesRequestedI(const QModelIndexList &indexes) void ListViewLogic::restoreNotesRequestedI(const QModelIndexList &indexes)
{ {
QModelIndexList needRestoredI; QModelIndexList needRestoredI;
@ -637,7 +617,6 @@ void ListViewLogic::restoreNotesRequestedI(const QModelIndexList &indexes)
} }
} }
// 更新列表视图的标签
void ListViewLogic::updateListViewLabel() void ListViewLogic::updateListViewLabel()
{ {
QString l1, l2; QString l1, l2;
@ -671,7 +650,6 @@ void ListViewLogic::updateListViewLabel()
emit listViewLabelChanged(l1, l2); emit listViewLabelChanged(l1, l2);
} }
// 行数变化的处理
void ListViewLogic::onRowCountChanged() void ListViewLogic::onRowCountChanged()
{ {
m_listView->closeAllEditor(); m_listView->closeAllEditor();
@ -689,7 +667,6 @@ void ListViewLogic::onRowCountChanged()
} }
} }
// 双击笔记的处理
void ListViewLogic::onNoteDoubleClicked(const QModelIndex &index) void ListViewLogic::onNoteDoubleClicked(const QModelIndex &index)
{ {
if (!index.isValid() || !m_listViewInfo.isInSearch) { if (!index.isValid() || !m_listViewInfo.isInSearch) {
@ -699,13 +676,11 @@ void ListViewLogic::onNoteDoubleClicked(const QModelIndex &index)
clearSearch(false, id); clearSearch(false, id);
} }
// 请求设置笔记为置顶的处理
void ListViewLogic::onSetPinnedNoteRequested(const QModelIndexList &indexes, bool isPinned) void ListViewLogic::onSetPinnedNoteRequested(const QModelIndexList &indexes, bool isPinned)
{ {
m_listModel->setNotesIsPinned(indexes, isPinned); m_listModel->setNotesIsPinned(indexes, isPinned);
} }
// 列表视图点击的处理
void ListViewLogic::onListViewClicked() void ListViewLogic::onListViewClicked()
{ {
if (m_listModel->rowCount() > 1) { if (m_listModel->rowCount() > 1) {
@ -725,7 +700,6 @@ void ListViewLogic::onListViewClicked()
} }
} }
// 选择第一笔记
void ListViewLogic::selectFirstNote() void ListViewLogic::selectFirstNote()
{ {
if (m_listModel->rowCount() > 0) { if (m_listModel->rowCount() > 0) {
@ -740,7 +714,6 @@ void ListViewLogic::selectFirstNote()
} }
} }
// 设置主题
void ListViewLogic::setTheme(Theme::Value theme) void ListViewLogic::setTheme(Theme::Value theme)
{ {
m_listView->setTheme(theme); m_listView->setTheme(theme);
@ -748,26 +721,22 @@ void ListViewLogic::setTheme(Theme::Value theme)
m_listView->update(); m_listView->update();
} }
// 检查动画是否正在运行
bool ListViewLogic::isAnimationRunning() bool ListViewLogic::isAnimationRunning()
{ {
return m_listDelegate->animationState() == QTimeLine::Running; return m_listDelegate->animationState() == QTimeLine::Running;
} }
// 设置最后保存的状态
void ListViewLogic::setLastSavedState(const QSet<int> &lastSelectedNotes, int needLoadSavedState) void ListViewLogic::setLastSavedState(const QSet<int> &lastSelectedNotes, int needLoadSavedState)
{ {
m_needLoadSavedState = needLoadSavedState; m_needLoadSavedState = needLoadSavedState;
m_lastSelectedNotes = lastSelectedNotes; m_lastSelectedNotes = lastSelectedNotes;
} }
// 请求加载保存的状态
void ListViewLogic::requestLoadSavedState(int needLoadSavedState) void ListViewLogic::requestLoadSavedState(int needLoadSavedState)
{ {
m_needLoadSavedState = needLoadSavedState; m_needLoadSavedState = needLoadSavedState;
} }
// 选择所有笔记
void ListViewLogic::selectAllNotes() void ListViewLogic::selectAllNotes()
{ {
if (m_listModel->rowCount() > 50) { if (m_listModel->rowCount() > 50) {
@ -802,7 +771,6 @@ void ListViewLogic::selectAllNotes()
onNotePressed(m_listView->selectedIndex()); onNotePressed(m_listView->selectedIndex());
} }
// 获取当前视图信息
const ListViewInfo &ListViewLogic::listViewInfo() const const ListViewInfo &ListViewLogic::listViewInfo() const
{ {
return m_listViewInfo; return m_listViewInfo;

@ -16,136 +16,83 @@ class QLineEdit;
class QToolButton; class QToolButton;
class TagPool; class TagPool;
// 负责笔记列表视图逻辑的类
class ListViewLogic : public QObject class ListViewLogic : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
// 构造函数,初始化笔记视图、笔记模型、搜索框等
explicit ListViewLogic(NoteListView *noteView, NoteListModel *noteModel, QLineEdit *searchEdit, explicit ListViewLogic(NoteListView *noteView, NoteListModel *noteModel, QLineEdit *searchEdit,
QToolButton *clearButton, TagPool *tagPool, DBManager *dbManager, QToolButton *clearButton, TagPool *tagPool, DBManager *dbManager,
QObject *parent = nullptr); QObject *parent = nullptr);
// 选择指定索引的笔记
void selectNote(const QModelIndex &noteIndex); void selectNote(const QModelIndex &noteIndex);
// 获取当前列表视图的信息
const ListViewInfo &listViewInfo() const; const ListViewInfo &listViewInfo() const;
// 选择第一个笔记
void selectFirstNote(); void selectFirstNote();
// 设置主题
void setTheme(Theme::Value theme); void setTheme(Theme::Value theme);
// 检查动画是否正在运行
bool isAnimationRunning(); bool isAnimationRunning();
// 设置最后保存的状态
void setLastSavedState(const QSet<int> &lastSelectedNotes, int needLoadSavedState = 2); void setLastSavedState(const QSet<int> &lastSelectedNotes, int needLoadSavedState = 2);
// 请求加载保存的状态
void requestLoadSavedState(int needLoadSavedState); void requestLoadSavedState(int needLoadSavedState);
// 选择所有笔记
void selectAllNotes(); void selectAllNotes();
public slots: public slots:
// 将笔记移动到顶部
void moveNoteToTop(const NodeData &note); void moveNoteToTop(const NodeData &note);
// 设置笔记数据
void setNoteData(const NodeData &note); void setNoteData(const NodeData &note);
// 笔记编辑关闭后的处理
void onNoteEditClosed(const NodeData &note, bool selectNext); void onNoteEditClosed(const NodeData &note, bool selectNext);
// 请求删除笔记
void deleteNoteRequested(const NodeData &note); void deleteNoteRequested(const NodeData &note);
// 选择上一个笔记
void selectNoteUp(); void selectNoteUp();
// 选择下一个笔记
void selectNoteDown(); void selectNoteDown();
// 搜索框文本改变的处理
void onSearchEditTextChanged(const QString &keyword); void onSearchEditTextChanged(const QString &keyword);
// 清除搜索
void clearSearch(bool createNewNote = false, int scrollToId = SpecialNodeID::InvalidNodeId); void clearSearch(bool createNewNote = false, int scrollToId = SpecialNodeID::InvalidNodeId);
// 添加标签请求的处理
void onAddTagRequestD(int noteId, int tagId); void onAddTagRequestD(int noteId, int tagId);
// 笔记移动事件处理
void onNoteMovedOut(int nodeId, int targetId); void onNoteMovedOut(int nodeId, int targetId);
// 设置最后选择的笔记
void setLastSelectedNote(); void setLastSelectedNote();
// 请求加载最后选择的笔记
void loadLastSelectedNoteRequested(); void loadLastSelectedNoteRequested();
// 请求在文件夹中获取笔记列表
void onNotesListInFolderRequested(int parentID, bool isRecursive, bool newNote, int scrollToId); void onNotesListInFolderRequested(int parentID, bool isRecursive, bool newNote, int scrollToId);
// 请求通过标签获取笔记列表
void onNotesListInTagsRequested(const QSet<int> &tagIds, bool newNote, int scrollToId); void onNotesListInTagsRequested(const QSet<int> &tagIds, bool newNote, int scrollToId);
// 选择笔记
void selectNotes(const QModelIndexList &indexes); void selectNotes(const QModelIndexList &indexes);
signals: signals:
// 显示笔记在编辑器中的信号
void showNotesInEditor(const QVector<NodeData> &notesData); void showNotesInEditor(const QVector<NodeData> &notesData);
// 请求添加标签到数据库的信号
void requestAddTagDb(int noteId, int tagId); void requestAddTagDb(int noteId, int tagId);
// 请求从数据库中移除标签的信号
void requestRemoveTagDb(int noteId, int tagId); void requestRemoveTagDb(int noteId, int tagId);
// 请求从数据库中移除笔记的信号
void requestRemoveNoteDb(const NodeData &noteData); void requestRemoveNoteDb(const NodeData &noteData);
// 请求将笔记移动到数据库的信号
void requestMoveNoteDb(int noteId, const NodeData &targetFolder); void requestMoveNoteDb(int noteId, const NodeData &targetFolder);
// 请求高亮搜索的信号
void requestHighlightSearch(); void requestHighlightSearch();
// 关闭笔记编辑器的信号
void closeNoteEditor(); void closeNoteEditor();
// 笔记标签列表更改的信号
void noteTagListChanged(int noteId, const QSet<int> &tagIds); void noteTagListChanged(int noteId, const QSet<int> &tagIds);
// 请求在数据库中搜索的信号
void requestSearchInDb(const QString &keyword, const ListViewInfo &inf); void requestSearchInDb(const QString &keyword, const ListViewInfo &inf);
// 请求清除数据库中的搜索的信号
void requestClearSearchDb(const ListViewInfo &inf); void requestClearSearchDb(const ListViewInfo &inf);
// 请求清除UI中的搜索的信号
void requestClearSearchUI(); void requestClearSearchUI();
// 请求新笔记的信号
void requestNewNote(); void requestNewNote();
// 请求移动笔记的信号
void moveNoteRequested(int id, int target); void moveNoteRequested(int id, int target);
// 列表视图标签改变的信号
void listViewLabelChanged(const QString &label1, const QString &label2); void listViewLabelChanged(const QString &label1, const QString &label2);
// 设置新笔记按钮可见性的信号
void setNewNoteButtonVisible(bool visible); void setNewNoteButtonVisible(bool visible);
// 请求在文件夹中获取笔记列表的信号
void requestNotesListInFolder(int parentID, bool isRecursive, bool newNote, int scrollToId); void requestNotesListInFolder(int parentID, bool isRecursive, bool newNote, int scrollToId);
// 请求通过标签获取笔记列表的信号
void requestNotesListInTags(const QSet<int> &tagIds, bool newNote, int scrollToId); void requestNotesListInTags(const QSet<int> &tagIds, bool newNote, int scrollToId);
private slots: private slots:
// 加载笔记列表模型
void loadNoteListModel(const QVector<NodeData> &noteList, const ListViewInfo &inf); void loadNoteListModel(const QVector<NodeData> &noteList, const ListViewInfo &inf);
// 处理添加标签请求
void onAddTagRequest(const QModelIndex &index, int tagId); void onAddTagRequest(const QModelIndex &index, int tagId);
// 处理移除标签请求
void onRemoveTagRequest(const QModelIndex &index, int tagId); void onRemoveTagRequest(const QModelIndex &index, int tagId);
// 处理笔记按下事件
void onNotePressed(const QModelIndexList &indexes); void onNotePressed(const QModelIndexList &indexes);
// 请求删除笔记的处理
void deleteNoteRequestedI(const QModelIndexList &indexes); void deleteNoteRequestedI(const QModelIndexList &indexes);
// 请求恢复笔记的处理
void restoreNotesRequestedI(const QModelIndexList &indexes); void restoreNotesRequestedI(const QModelIndexList &indexes);
// 更新列表视图标签
void updateListViewLabel(); void updateListViewLabel();
// 行数改变的处理
void onRowCountChanged(); void onRowCountChanged();
// 处理双击笔记事件
void onNoteDoubleClicked(const QModelIndex &index); void onNoteDoubleClicked(const QModelIndex &index);
// 请求设置固定笔记的处理
void onSetPinnedNoteRequested(const QModelIndexList &indexes, bool isPinned); void onSetPinnedNoteRequested(const QModelIndexList &indexes, bool isPinned);
// 处理列表视图点击事件
void onListViewClicked(); void onListViewClicked();
private: private:
NoteListView *m_listView; // 笔记列表视图 NoteListView *m_listView;
NoteListModel *m_listModel; // 笔记列表模型 NoteListModel *m_listModel;
QLineEdit *m_searchEdit; // 搜索框 QLineEdit *m_searchEdit;
QToolButton *m_clearButton; // 清除按钮 QToolButton *m_clearButton;
DBManager *m_dbManager; // 数据库管理器 DBManager *m_dbManager;
NoteListDelegate *m_listDelegate; // 笔记列表委托 NoteListDelegate *m_listDelegate;
TagPool *m_tagPool; // 标签池 TagPool *m_tagPool;
ListViewInfo m_listViewInfo; // 列表视图信息 ListViewInfo m_listViewInfo;
QVector<QModelIndex> m_editorIndexes; // 编辑器索引 QVector<QModelIndex> m_editorIndexes;
int m_needLoadSavedState; // 需要加载的保存状态 int m_needLoadSavedState;
QSet<int> m_lastSelectedNotes; // 最后选择的笔记集合 QSet<int> m_lastSelectedNotes;
}; };
#endif // LISTVIEWLOGIC_H #endif // LISTVIEWLOGIC_H

@ -23,33 +23,32 @@
**/ **/
// Credit: https://github.com/carlonluca/lqtutils // Credit: https://github.com/carlonluca/lqtutils
#ifndef LQTUTILS_ENUM_H #ifndef LQTUTILS_ENUM_H
#define LQTUTILS_ENUM_H #define LQTUTILS_ENUM_H
#include <QObject> #include <QObject>
#include <QQmlEngine> #include <QQmlEngine>
// 宏定义 L_DECLARE_ENUM 用于声明枚举类型及其相关函数
#define L_DECLARE_ENUM(enumName, ...) \ #define L_DECLARE_ENUM(enumName, ...) \
namespace enumName { \ namespace enumName { \
Q_NAMESPACE \ Q_NAMESPACE \
enum Value { __VA_ARGS__ }; \ enum Value { __VA_ARGS__ }; \
Q_ENUM_NS(Value) \ Q_ENUM_NS(Value) \
// 注册 QML 中的枚举类型 inline int qmlRegister##enumName(const char *uri, int major, int minor) \
inline int qmlRegister##enumName(const char *uri, int major, int minor) { \
{ return qmlRegisterUncreatableMetaObject(enumName::staticMetaObject, uri, major, minor, \
return qmlRegisterUncreatableMetaObject(enumName::staticMetaObject, uri, major, minor, #enumName, "Access to enums & flags only"); \
#enumName, "Access to enums & flags only"); } \
} // 注册元类型 inline int qRegisterMetaType() \
inline int qRegisterMetaType() { \
{ return ::qRegisterMetaType<enumName::Value>(#enumName); \
return ::qRegisterMetaType<enumName::Value>(#enumName); } \
} // 注册枚举类型 inline void registerEnum(const char *uri, int major, int minor) \
inline void registerEnum(const char *uri, int major, int minor) { \
{ enumName::qmlRegister##enumName(uri, major, minor); \
enumName::qmlRegister##enumName(uri, major, minor); enumName::qRegisterMetaType(); \
enumName::qRegisterMetaType(); } \
}
} }
#endif // LQTUTILS_ENUM_H #endif // LQTUTILS_ENUM_H

@ -3,11 +3,16 @@
* Just a meantime project to see the ability of qt, the framework that my OS might be based on * Just a meantime project to see the ability of qt, the framework that my OS might be based on
* And for those linux users that believe in the power of notes * And for those linux users that believe in the power of notes
*********************************************************************************************/ *********************************************************************************************/
// 主函数,程序入口
#include "mainwindow.h"
#include "singleinstance.h"
#include <QApplication>
#include <QFontDatabase>
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QApplication app(argc, argv); QApplication app(argc, argv);
// 设置应用程序信息 // Set application information
app.setApplicationName("Notes"); app.setApplicationName("Notes");
app.setApplicationVersion(APP_VERSION); app.setApplicationVersion(APP_VERSION);
@ -19,7 +24,6 @@ int main(int argc, char *argv[])
app.setAttribute(Qt::AA_DisableWindowContextHelpButton); app.setAttribute(Qt::AA_DisableWindowContextHelpButton);
#endif #endif
// 尝试加载字体并报告错误
if (QFontDatabase::addApplicationFont(":/fonts/fontawesome/fa-solid-900.ttf") < 0) if (QFontDatabase::addApplicationFont(":/fonts/fontawesome/fa-solid-900.ttf") < 0)
qWarning() << "FontAwesome Solid cannot be loaded !"; qWarning() << "FontAwesome Solid cannot be loaded !";
@ -32,7 +36,7 @@ int main(int argc, char *argv[])
if (QFontDatabase::addApplicationFont(":/fonts/material/material-symbols-outlined.ttf") < 0) if (QFontDatabase::addApplicationFont(":/fonts/material/material-symbols-outlined.ttf") < 0)
qWarning() << "Material Symbols cannot be loaded !"; qWarning() << "Material Symbols cannot be loaded !";
// 从资源加载字体 // Load fonts from resources
// Roboto // Roboto
QFontDatabase::addApplicationFont(":/fonts/roboto-hinted/Roboto-Bold.ttf"); QFontDatabase::addApplicationFont(":/fonts/roboto-hinted/Roboto-Bold.ttf");
QFontDatabase::addApplicationFont(":/fonts/roboto-hinted/Roboto-Medium.ttf"); QFontDatabase::addApplicationFont(":/fonts/roboto-hinted/Roboto-Medium.ttf");
@ -52,38 +56,38 @@ int main(int argc, char *argv[])
QFontDatabase::addApplicationFont(":/fonts/sourcesanspro/SourceSansPro-SemiBold.ttf"); QFontDatabase::addApplicationFont(":/fonts/sourcesanspro/SourceSansPro-SemiBold.ttf");
QFontDatabase::addApplicationFont(":/fonts/sourcesanspro/SourceSansPro-SemiBoldItalic.ttf"); QFontDatabase::addApplicationFont(":/fonts/sourcesanspro/SourceSansPro-SemiBoldItalic.ttf");
// Trykker 字体 // Trykker
QFontDatabase::addApplicationFont(":/fonts/trykker/Trykker-Regular.ttf"); QFontDatabase::addApplicationFont(":/fonts/trykker/Trykker-Regular.ttf");
// Mate 字体 // Mate
QFontDatabase::addApplicationFont(":/fonts/mate/Mate-Regular.ttf"); QFontDatabase::addApplicationFont(":/fonts/mate/Mate-Regular.ttf");
QFontDatabase::addApplicationFont(":/fonts/mate/Mate-Italic.ttf"); QFontDatabase::addApplicationFont(":/fonts/mate/Mate-Italic.ttf");
// PT Serif 字体 // PT Serif
QFontDatabase::addApplicationFont(":/fonts/ptserif/PTSerif-Regular.ttf"); QFontDatabase::addApplicationFont(":/fonts/ptserif/PTSerif-Regular.ttf");
QFontDatabase::addApplicationFont(":/fonts/ptserif/PTSerif-Italic.ttf"); QFontDatabase::addApplicationFont(":/fonts/ptserif/PTSerif-Italic.ttf");
QFontDatabase::addApplicationFont(":/fonts/ptserif/PTSerif-Bold.ttf"); QFontDatabase::addApplicationFont(":/fonts/ptserif/PTSerif-Bold.ttf");
QFontDatabase::addApplicationFont(":/fonts/ptserif/PTSerif-BoldItalic.ttf"); QFontDatabase::addApplicationFont(":/fonts/ptserif/PTSerif-BoldItalic.ttf");
// iA Mono 字体 // iA Mono
QFontDatabase::addApplicationFont(":/fonts/iamono/iAWriterMonoS-Regular.ttf"); QFontDatabase::addApplicationFont(":/fonts/iamono/iAWriterMonoS-Regular.ttf");
QFontDatabase::addApplicationFont(":/fonts/iamono/iAWriterMonoS-Italic.ttf"); QFontDatabase::addApplicationFont(":/fonts/iamono/iAWriterMonoS-Italic.ttf");
QFontDatabase::addApplicationFont(":/fonts/iamono/iAWriterMonoS-Bold.ttf"); QFontDatabase::addApplicationFont(":/fonts/iamono/iAWriterMonoS-Bold.ttf");
QFontDatabase::addApplicationFont(":/fonts/iamono/iAWriterMonoS-BoldItalic.ttf"); QFontDatabase::addApplicationFont(":/fonts/iamono/iAWriterMonoS-BoldItalic.ttf");
// iA Duo 字体 // iA Duo
QFontDatabase::addApplicationFont(":/fonts/iaduo/iAWriterDuoS-Regular.ttf"); QFontDatabase::addApplicationFont(":/fonts/iaduo/iAWriterDuoS-Regular.ttf");
QFontDatabase::addApplicationFont(":/fonts/iaduo/iAWriterDuoS-Italic.ttf"); QFontDatabase::addApplicationFont(":/fonts/iaduo/iAWriterDuoS-Italic.ttf");
QFontDatabase::addApplicationFont(":/fonts/iaduo/iAWriterDuoS-Bold.ttf"); QFontDatabase::addApplicationFont(":/fonts/iaduo/iAWriterDuoS-Bold.ttf");
QFontDatabase::addApplicationFont(":/fonts/iaduo/iAWriterDuoS-BoldItalic.ttf"); QFontDatabase::addApplicationFont(":/fonts/iaduo/iAWriterDuoS-BoldItalic.ttf");
// iA Quattro 字体 // iA Quattro
QFontDatabase::addApplicationFont(":/fonts/iaquattro/iAWriterQuattroS-Regular.ttf"); QFontDatabase::addApplicationFont(":/fonts/iaquattro/iAWriterQuattroS-Regular.ttf");
QFontDatabase::addApplicationFont(":/fonts/iaquattro/iAWriterQuattroS-Italic.ttf"); QFontDatabase::addApplicationFont(":/fonts/iaquattro/iAWriterQuattroS-Italic.ttf");
QFontDatabase::addApplicationFont(":/fonts/iaquattro/iAWriterQuattroS-Bold.ttf"); QFontDatabase::addApplicationFont(":/fonts/iaquattro/iAWriterQuattroS-Bold.ttf");
QFontDatabase::addApplicationFont(":/fonts/iaquattro/iAWriterQuattroS-BoldItalic.ttf"); QFontDatabase::addApplicationFont(":/fonts/iaquattro/iAWriterQuattroS-BoldItalic.ttf");
// 防止多个应用实例启动 // Prevent many instances of the app to be launched
QString name = "com.awsomeness.notes"; QString name = "com.awsomeness.notes";
SingleInstance instance; SingleInstance instance;
if (instance.hasPrevious(name)) { if (instance.hasPrevious(name)) {
@ -92,11 +96,11 @@ int main(int argc, char *argv[])
instance.listen(name); instance.listen(name);
// 创建并显示应用程序窗口 // Create and Show the app
MainWindow w; MainWindow w;
w.show(); w.show();
// 将Notes窗口置于最前 // Bring the Notes window to the front
QObject::connect(&instance, &SingleInstance::newInstance, &w, QObject::connect(&instance, &SingleInstance::newInstance, &w,
[&]() { (&w)->setMainWindowVisibility(true); }); [&]() { (&w)->setMainWindowVisibility(true); });

File diff suppressed because it is too large Load Diff

@ -6,6 +6,7 @@
#ifndef MAINWINDOW_H #ifndef MAINWINDOW_H
#define MAINWINDOW_H #define MAINWINDOW_H
#include <QMainWindow> #include <QMainWindow>
#include <QtGui> #include <QtGui>
#include <QtCore> #include <QtCore>
@ -51,15 +52,12 @@
#include "nodetreeview.h" #include "nodetreeview.h"
#include "editorsettingsoptions.h" #include "editorsettingsoptions.h"
// 定义订阅状态枚举
L_DECLARE_ENUM(SubscriptionStatus, NoSubscription, Active, ActivationLimitReached, Expired, Invalid, L_DECLARE_ENUM(SubscriptionStatus, NoSubscription, Active, ActivationLimitReached, Expired, Invalid,
EnteredGracePeriod, GracePeriodOver, NoInternetConnection, UnknownError) EnteredGracePeriod, GracePeriodOver, NoInternetConnection, UnknownError)
namespace Ui { namespace Ui {
class MainWindow; class MainWindow;
} }
// 前向声明类
class TreeViewLogic; class TreeViewLogic;
class ListViewLogic; class ListViewLogic;
class NoteEditorLogic; class NoteEditorLogic;
@ -77,8 +75,6 @@ using MainWindowBase = CFramelessWindow;
#else #else
using MainWindowBase = QMainWindow; using MainWindowBase = QMainWindow;
#endif #endif
// 主窗口类
class MainWindow : public MainWindowBase class MainWindow : public MainWindowBase
{ {
Q_OBJECT Q_OBJECT
@ -113,232 +109,129 @@ public:
Q_ENUM(ShadowSide) Q_ENUM(ShadowSide)
Q_ENUM(StretchSide) Q_ENUM(StretchSide)
// 构造函数
explicit MainWindow(QWidget *parent = nullptr); explicit MainWindow(QWidget *parent = nullptr);
// 析构函数
~MainWindow() override; ~MainWindow() override;
// 设置主窗口可见性
void setMainWindowVisibility(bool state); void setMainWindowVisibility(bool state);
public slots: public slots:
// 保存最近选择的文件夹标签
void saveLastSelectedFolderTags(bool isFolder, const QString &folderPath, void saveLastSelectedFolderTags(bool isFolder, const QString &folderPath,
const QSet<int> &tagId); const QSet<int> &tagId);
// 保存展开的文件夹
void saveExpandedFolder(const QStringList &folderPaths); void saveExpandedFolder(const QStringList &folderPaths);
// 保存最近选择的笔记
void saveLastSelectedNote(const QSet<int> &notesId); void saveLastSelectedNote(const QSet<int> &notesId);
// 从样式按钮改变编辑器字体类型
void changeEditorFontTypeFromStyleButtons(FontTypeface::Value fontType, int chosenFontIndex); void changeEditorFontTypeFromStyleButtons(FontTypeface::Value fontType, int chosenFontIndex);
// 从样式按钮改变编辑器字体大小
void changeEditorFontSizeFromStyleButtons(FontSizeAction::Value fontSizeAction); void changeEditorFontSizeFromStyleButtons(FontSizeAction::Value fontSizeAction);
// 从样式按钮改变编辑器文本宽度
void changeEditorTextWidthFromStyleButtons(EditorTextWidth::Value editorTextWidth); void changeEditorTextWidthFromStyleButtons(EditorTextWidth::Value editorTextWidth);
// 重置编辑器设置
void resetEditorSettings(); void resetEditorSettings();
// 设置主题
void setTheme(Theme::Value theme); void setTheme(Theme::Value theme);
// 设置看板可见性
void setKanbanVisibility(bool isVisible); void setKanbanVisibility(bool isVisible);
// 折叠笔记列表
void collapseNoteList(); void collapseNoteList();
// 展开笔记列表
void expandNoteList(); void expandNoteList();
// 折叠文件夹树
void collapseFolderTree(); void collapseFolderTree();
// 展开文件夹树
void expandFolderTree(); void expandFolderTree();
// 设置Markdown启用状态
void setMarkdownEnabled(bool isMarkdownEnabled); void setMarkdownEnabled(bool isMarkdownEnabled);
// 设置窗口置顶
void stayOnTop(bool checked); void stayOnTop(bool checked);
// 将当前笔记移至回收站
void moveCurrentNoteToTrash(); void moveCurrentNoteToTrash();
// 切换编辑器设置
void toggleEditorSettings(); void toggleEditorSettings();
// 从快速视图设置编辑器设置可见性
void setEditorSettingsFromQuickViewVisibility(bool isVisible); void setEditorSettingsFromQuickViewVisibility(bool isVisible);
// 设置编辑器设置滚动条位置
void setEditorSettingsScrollBarPosition(double position); void setEditorSettingsScrollBarPosition(double position);
// 设置激活成功的状态
void setActivationSuccessful(QString licenseKey, bool removeGracePeriodStartedDate = true); void setActivationSuccessful(QString licenseKey, bool removeGracePeriodStartedDate = true);
// 检查专业版
void checkProVersion(); void checkProVersion();
// 获取用户许可密钥
QVariant getUserLicenseKey(); QVariant getUserLicenseKey();
protected: protected:
// 重新绘制事件
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
// 重新调整事件
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
// 关闭事件
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
// 鼠标按下事件
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
// 鼠标移动事件
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
// 移动事件
void moveEvent(QMoveEvent *event) override; void moveEvent(QMoveEvent *event) override;
// 鼠标释放事件
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
// 鼠标双击事件
void mouseDoubleClickEvent(QMouseEvent *event) override; void mouseDoubleClickEvent(QMouseEvent *event) override;
// 离开事件
void leaveEvent(QEvent *) override; void leaveEvent(QEvent *) override;
// 变更事件
void changeEvent(QEvent *event) override; void changeEvent(QEvent *event) override;
// 事件过滤器
bool eventFilter(QObject *object, QEvent *event) override; bool eventFilter(QObject *object, QEvent *event) override;
private: private:
// UI界面指针
Ui::MainWindow *ui; Ui::MainWindow *ui;
// 设置数据库指针
QSettings *m_settingsDatabase; QSettings *m_settingsDatabase;
// 清除按钮
QToolButton *m_clearButton; QToolButton *m_clearButton;
// 搜索按钮
QToolButton *m_searchButton; QToolButton *m_searchButton;
// 绿色最大化按钮
QPushButton *m_greenMaximizeButton; QPushButton *m_greenMaximizeButton;
// 红色关闭按钮
QPushButton *m_redCloseButton; QPushButton *m_redCloseButton;
// 黄色最小化按钮
QPushButton *m_yellowMinimizeButton; QPushButton *m_yellowMinimizeButton;
// 交通灯布局
QHBoxLayout m_trafficLightLayout; QHBoxLayout m_trafficLightLayout;
// 新笔记按钮
QPushButton *m_newNoteButton; QPushButton *m_newNoteButton;
// 点点按钮
QPushButton *m_dotsButton; QPushButton *m_dotsButton;
// 全局设置按钮
QPushButton *m_globalSettingsButton; QPushButton *m_globalSettingsButton;
// 切换树视图按钮
QPushButton *m_toggleTreeViewButton; QPushButton *m_toggleTreeViewButton;
// 切换文本视图按钮
QPushButton *m_switchToTextViewButton; QPushButton *m_switchToTextViewButton;
// 切换看板视图按钮
QPushButton *m_switchToKanbanViewButton; QPushButton *m_switchToKanbanViewButton;
// 文本编辑器
CustomDocument *m_textEdit; CustomDocument *m_textEdit;
// 笔记编辑器逻辑
NoteEditorLogic *m_noteEditorLogic; NoteEditorLogic *m_noteEditorLogic;
// 搜索框
QLineEdit *m_searchEdit; QLineEdit *m_searchEdit;
// 编辑器日期标签
QLabel *m_editorDateLabel; QLabel *m_editorDateLabel;
// 分割器
QSplitter *m_splitter; QSplitter *m_splitter;
// 笔记列表小部件
QWidget *m_noteListWidget; QWidget *m_noteListWidget;
// 文件夹小部件
QWidget *m_foldersWidget; QWidget *m_foldersWidget;
// 系统托盘图标
QSystemTrayIcon *m_trayIcon; QSystemTrayIcon *m_trayIcon;
#if !defined(Q_OS_MAC) #if !defined(Q_OS_MAC)
// 恢复动作
QAction *m_restoreAction; QAction *m_restoreAction;
// 退出动作
QAction *m_quitAction; QAction *m_quitAction;
#endif #endif
// 笔记列表视图
NoteListView *m_listView; NoteListView *m_listView;
// 笔记列表模型
NoteListModel *m_listModel; NoteListModel *m_listModel;
// 列表视图逻辑
ListViewLogic *m_listViewLogic; ListViewLogic *m_listViewLogic;
// 节点树视图
NodeTreeView *m_treeView; NodeTreeView *m_treeView;
// 节点树模型
NodeTreeModel *m_treeModel; NodeTreeModel *m_treeModel;
// 树视图逻辑
TreeViewLogic *m_treeViewLogic; TreeViewLogic *m_treeViewLogic;
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
// 看板快速视图
QQuickView m_kanbanQuickView; QQuickView m_kanbanQuickView;
// 看板小部件
QWidget *m_kanbanWidget; QWidget *m_kanbanWidget;
#endif #endif
// 编辑器设置快速视图
QQuickView m_editorSettingsQuickView; QQuickView m_editorSettingsQuickView;
// 编辑器设置小部件
QWidget *m_editorSettingsWidget; QWidget *m_editorSettingsWidget;
// 标签池
TagPool *m_tagPool; TagPool *m_tagPool;
// 数据库管理器
DBManager *m_dbManager; DBManager *m_dbManager;
// 数据库线程
QThread *m_dbThread; QThread *m_dbThread;
// 分割器样式
SplitterStyle *m_splitterStyle; SplitterStyle *m_splitterStyle;
#if defined(UPDATE_CHECKER) #if defined(UPDATE_CHECKER)
// 更新窗口
UpdaterWindow m_updater; UpdaterWindow m_updater;
#endif #endif
// 关于窗口
AboutWindow m_aboutWindow; AboutWindow m_aboutWindow;
// 拉伸边
StretchSide m_stretchSide; StretchSide m_stretchSide;
// 自启动
Autostart m_autostart; Autostart m_autostart;
// 鼠标按下坐标
int m_mousePressX; int m_mousePressX;
int m_mousePressY; int m_mousePressY;
// 回收站计数器
int m_trashCounter; int m_trashCounter;
// 布局边距
int m_layoutMargin; int m_layoutMargin;
// 阴影宽度
int m_shadowWidth; int m_shadowWidth;
// 节点树宽度
int m_nodeTreeWidth; int m_nodeTreeWidth;
// 小编辑器宽度
int m_smallEditorWidth; int m_smallEditorWidth;
// 大编辑器宽度
int m_largeEditorWidth; int m_largeEditorWidth;
// 能否移动窗口
bool m_canMoveWindow; bool m_canMoveWindow;
// 能否调整窗口大小
bool m_canStretchWindow; bool m_canStretchWindow;
// 临时标志
bool m_isTemp; bool m_isTemp;
// 列表视图滚动条隐藏标志
bool m_isListViewScrollBarHidden; bool m_isListViewScrollBarHidden;
// 操作运行标志
bool m_isOperationRunning; bool m_isOperationRunning;
#if defined(UPDATE_CHECKER) #if defined(UPDATE_CHECKER)
// 不显示更新窗口标志
bool m_dontShowUpdateWindow; bool m_dontShowUpdateWindow;
#endif #endif
// 始终置顶标志
bool m_alwaysStayOnTop; bool m_alwaysStayOnTop;
// 使用本机窗口框架标志
bool m_useNativeWindowFrame; bool m_useNativeWindowFrame;
// 隐藏到托盘标志
bool m_hideToTray; bool m_hideToTray;
// 样式表
QString m_styleSheet; QString m_styleSheet;
// Serif字体列表
QStringList m_listOfSerifFonts; QStringList m_listOfSerifFonts;
// Sans Serif字体列表
QStringList m_listOfSansSerifFonts; QStringList m_listOfSansSerifFonts;
// Mono字体列表
QStringList m_listOfMonoFonts; QStringList m_listOfMonoFonts;
// 选择的Serif字体索引
int m_chosenSerifFontIndex; int m_chosenSerifFontIndex;
// 选择的Sans Serif字体索引
int m_chosenSansSerifFontIndex; int m_chosenSansSerifFontIndex;
// 选择的Mono字体索引
int m_chosenMonoFontIndex; int m_chosenMonoFontIndex;
// 编辑器中等字体大小
int m_editorMediumFontSize; int m_editorMediumFontSize;
// 当前字体大小
int m_currentFontPointSize; int m_currentFontPointSize;
struct m_charsLimitPerFont struct m_charsLimitPerFont
{ {
@ -346,308 +239,165 @@ private:
int serif; int serif;
int sansSerif; int sansSerif;
} m_currentCharsLimitPerFont; } m_currentCharsLimitPerFont;
// 当前字体类型
FontTypeface::Value m_currentFontTypeface; FontTypeface::Value m_currentFontTypeface;
// 当前字体家族
QString m_currentFontFamily; QString m_currentFontFamily;
// 当前选定字体
QFont m_currentSelectedFont; QFont m_currentSelectedFont;
// 显示字体
QString m_displayFont; QString m_displayFont;
// 当前主题
Theme::Value m_currentTheme; Theme::Value m_currentTheme;
// 当前编辑器文本颜色
QColor m_currentEditorTextColor; QColor m_currentEditorTextColor;
// 非编辑器小部件可见性标志
bool m_areNonEditorWidgetsVisible; bool m_areNonEditorWidgetsVisible;
#if !defined(Q_OS_MAC) #if !defined(Q_OS_MAC)
// 文本编辑滚动条定时器
QTimer *m_textEditScrollBarTimer; QTimer *m_textEditScrollBarTimer;
// 文本编辑滚动条定时器持续时间
int m_textEditScrollBarTimerDuration; int m_textEditScrollBarTimerDuration;
#endif #endif
// 帧右上小部件可见性标志
bool m_isFrameRightTopWidgetsVisible; bool m_isFrameRightTopWidgetsVisible;
// 从快速视图可见的编辑器设置标志
bool m_isEditorSettingsFromQuickViewVisible; bool m_isEditorSettingsFromQuickViewVisible;
// 专业版激活标志
bool m_isProVersionActivated; bool m_isProVersionActivated;
// 本地许可数据
QSettings *m_localLicenseData; QSettings *m_localLicenseData;
// 支付详情
QJsonObject m_paymentDetails; QJsonObject m_paymentDetails;
// 订阅状态
SubscriptionStatus::Value m_subscriptionStatus; SubscriptionStatus::Value m_subscriptionStatus;
// 订阅窗口快速视图
QQuickView m_subscriptionWindowQuickView; QQuickView m_subscriptionWindowQuickView;
// 订阅窗口小部件
QWidget *m_subscriptionWindowWidget; QWidget *m_subscriptionWindowWidget;
// 订阅窗口引擎
QQmlApplicationEngine m_subscriptionWindowEngine; QQmlApplicationEngine m_subscriptionWindowEngine;
// 订阅窗口
QWindow *m_subscriptionWindow; QWindow *m_subscriptionWindow;
// 购买数据备用1
QString m_purchaseDataAlt1; QString m_purchaseDataAlt1;
// 购买数据备用2
QString m_purchaseDataAlt2; QString m_purchaseDataAlt2;
// 数据缓冲区
QByteArray *m_dataBuffer; QByteArray *m_dataBuffer;
// 网络访问管理器
QNetworkAccessManager *m_netManager; QNetworkAccessManager *m_netManager;
// 备用请求1
QNetworkRequest m_reqAlt1; QNetworkRequest m_reqAlt1;
// 备用请求2
QNetworkRequest m_reqAlt2; QNetworkRequest m_reqAlt2;
// 第一次尝试的网络购买数据回复
QNetworkReply *m_netPurchaseDataReplyFirstAttempt; QNetworkReply *m_netPurchaseDataReplyFirstAttempt;
// 第二次尝试的网络购买数据回复
QNetworkReply *m_netPurchaseDataReplySecondAttempt; QNetworkReply *m_netPurchaseDataReplySecondAttempt;
// 用户许可密钥
QString m_userLicenseKey; QString m_userLicenseKey;
// 主菜单
QMenu m_mainMenu; QMenu m_mainMenu;
// 购买或管理订阅动作
QAction *m_buyOrManageSubscriptionAction; QAction *m_buyOrManageSubscriptionAction;
// 检查格式是否已应用
bool alreadyAppliedFormat(const QString &formatChars); bool alreadyAppliedFormat(const QString &formatChars);
// 应用格式
void applyFormat(const QString &formatChars); void applyFormat(const QString &formatChars);
// 设置主窗口
void setupMainWindow(); void setupMainWindow();
// 设置字体
void setupFonts(); void setupFonts();
// 设置托盘图标
void setupTrayIcon(); void setupTrayIcon();
// 设置快捷键
void setupKeyboardShortcuts(); void setupKeyboardShortcuts();
// 设置分割器
void setupSplitter(); void setupSplitter();
// 设置按钮
void setupButtons(); void setupButtons();
// 设置信号与槽
void setupSignalsSlots(); void setupSignalsSlots();
#if defined(UPDATE_CHECKER) #if defined(UPDATE_CHECKER)
// 自动检查更新
void autoCheckForUpdates(); void autoCheckForUpdates();
#endif #endif
// 设置搜索框
void setupSearchEdit(); void setupSearchEdit();
// 设置订阅窗口
void setupSubscrirptionWindow(); void setupSubscrirptionWindow();
// 设置编辑器样式表 void setupEditorSettings();
void setupTextEditStyleSheet(int paddingLeft, int paddingRight); void setupTextEditStyleSheet(int paddingLeft, int paddingRight);
// 对齐文本编辑器文本
void alignTextEditText(); void alignTextEditText();
// 设置文本编辑器
void setupTextEdit(); void setupTextEdit();
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
// 设置看板视图
void setupKanbanView(); void setupKanbanView();
#endif #endif
// 设置数据库
void setupDatabases(); void setupDatabases();
// 设置模型视图
void setupModelView(); void setupModelView();
// 设置全局设置菜单
void setupGlobalSettingsMenu(); void setupGlobalSettingsMenu();
// 初始化设置数据库
void initializeSettingsDatabase(); void initializeSettingsDatabase();
// 为滚动区域设置布局
void setLayoutForScrollArea(); void setLayoutForScrollArea();
// 设置按钮和字段的启用状态
void setButtonsAndFieldsEnabled(bool doEnable); void setButtonsAndFieldsEnabled(bool doEnable);
// 重置格式
void resetFormat(const QString &formatChars); void resetFormat(const QString &formatChars);
// 恢复状态
void restoreStates(); void restoreStates();
// 从版本0.9.0迁移
void migrateFromV0_9_0(); void migrateFromV0_9_0();
// 执行导入
void executeImport(const bool replace); void executeImport(const bool replace);
// 从版本0.9.0迁移笔记
void migrateNoteFromV0_9_0(const QString &notePath); void migrateNoteFromV0_9_0(const QString &notePath);
// 从版本0.9.0迁移回收站
void migrateTrashFromV0_9_0(const QString &trashPath); void migrateTrashFromV0_9_0(const QString &trashPath);
// 根据字体类型设置当前字体
void setCurrentFontBasedOnTypeface(FontTypeface::Value selectedFontTypeFace); void setCurrentFontBasedOnTypeface(FontTypeface::Value selectedFontTypeFace);
// 设置右侧小部件可见性
void setVisibilityOfFrameRightWidgets(bool isVisible); void setVisibilityOfFrameRightWidgets(bool isVisible);
// 设置非编辑器小部件可见性
void setVisibilityOfFrameRightNonEditor(bool isVisible); void setVisibilityOfFrameRightNonEditor(bool isVisible);
// 设置窗口按钮可见性
void setWindowButtonsVisible(bool isVisible); void setWindowButtonsVisible(bool isVisible);
// 显示编辑器设置
void showEditorSettings(); void showEditorSettings();
// 更新选定的编辑器设置选项
void updateSelectedOptionsEditorSettings(); void updateSelectedOptionsEditorSettings();
// 添加阴影
void dropShadow(QPainter &painter, ShadowType type, ShadowSide side); void dropShadow(QPainter &painter, ShadowType type, ShadowSide side);
// 使用渐变填充矩形
void fillRectWithGradient(QPainter &painter, QRect rect, QGradient &gradient); void fillRectWithGradient(QPainter &painter, QRect rect, QGradient &gradient);
// 高斯分布
double gaussianDist(double x, const double center, double sigma) const; double gaussianDist(double x, const double center, double sigma) const;
// 调整并定位编辑器设置窗口
void resizeAndPositionEditorSettingsWindow(); void resizeAndPositionEditorSettingsWindow();
// 获取支付详情信号与槽
void getPaymentDetailsSignalsSlots(); void getPaymentDetailsSignalsSlots();
// 验证许可证信号与槽
void verifyLicenseSignalsSlots(); void verifyLicenseSignalsSlots();
// 获取订阅状态
void getSubscriptionStatus(); void getSubscriptionStatus();
// 设置边距
void setMargins(QMargins margins); void setMargins(QMargins margins);
private slots: private slots:
// 初始化数据
void InitData(); void InitData();
// 系统托盘图标激活
void onSystemTrayIconActivated(QSystemTrayIcon::ActivationReason reason); void onSystemTrayIconActivated(QSystemTrayIcon::ActivationReason reason);
// 新笔记按钮点击
void onNewNoteButtonClicked(); void onNewNoteButtonClicked();
// 点点按钮点击
void onDotsButtonClicked(); void onDotsButtonClicked();
// 切换到看板视图按钮点击
void onSwitchToKanbanViewButtonClicked(); void onSwitchToKanbanViewButtonClicked();
// 全局设置按钮点击
void onGlobalSettingsButtonClicked(); void onGlobalSettingsButtonClicked();
// 清除按钮点击
void onClearButtonClicked(); void onClearButtonClicked();
// 绿色最大化按钮按下
void onGreenMaximizeButtonPressed(); void onGreenMaximizeButtonPressed();
// 黄色最小化按钮按下
void onYellowMinimizeButtonPressed(); void onYellowMinimizeButtonPressed();
// 红色关闭按钮按下
void onRedCloseButtonPressed(); void onRedCloseButtonPressed();
// 绿色最大化按钮点击
void onGreenMaximizeButtonClicked(); void onGreenMaximizeButtonClicked();
// 黄色最小化按钮点击
void onYellowMinimizeButtonClicked(); void onYellowMinimizeButtonClicked();
// 红色关闭按钮点击
void onRedCloseButtonClicked(); void onRedCloseButtonClicked();
// 重置块格式
void resetBlockFormat(); void resetBlockFormat();
// 创建新笔记
void createNewNote(); void createNewNote();
// 向下选择笔记
void selectNoteDown(); void selectNoteDown();
// 向上选择笔记
void selectNoteUp(); void selectNoteUp();
// 设置文本焦点
void setFocusOnText(); void setFocusOnText();
// 全屏窗口
void fullscreenWindow(); void fullscreenWindow();
// 制作代码
void makeCode(); void makeCode();
// 制作粗体
void makeBold(); void makeBold();
// 制作斜体
void makeItalic(); void makeItalic();
// 制作删除线
void makeStrikethrough(); void makeStrikethrough();
// 最大化窗口
void maximizeWindow(); void maximizeWindow();
// 最小化窗口
void minimizeWindow(); void minimizeWindow();
// 退出应用程序
void QuitApplication(); void QuitApplication();
#if defined(UPDATE_CHECKER) #if defined(UPDATE_CHECKER)
// 检查更新
void checkForUpdates(); void checkForUpdates();
#endif #endif
// 切换笔记列表
void toggleNoteList(); void toggleNoteList();
// 切换文件夹树
void toggleFolderTree(); void toggleFolderTree();
// 导入笔记文件
void importNotesFile(); void importNotesFile();
// 导出笔记文件
void exportNotesFile(); void exportNotesFile();
// 恢复笔记文件
void restoreNotesFile(); void restoreNotesFile();
// 增加标题
void increaseHeading(); void increaseHeading();
// 减小标题
void decreaseHeading(); void decreaseHeading();
// 设置标题
void setHeading(int level); void setHeading(int level);
// 设置使用本机窗口框架
void setUseNativeWindowFrame(bool useNativeWindowFrame); void setUseNativeWindowFrame(bool useNativeWindowFrame);
// 设置隐藏到托盘
void setHideToTray(bool enabled); void setHideToTray(bool enabled);
// 切换始终置顶
void toggleStayOnTop(); void toggleStayOnTop();
// 搜索框回车按下
void onSearchEditReturnPressed(); void onSearchEditReturnPressed();
// 删除选中笔记
void deleteSelectedNote(); void deleteSelectedNote();
// 清除搜索框
void clearSearch(); void clearSearch();
// 显示错误信息
void showErrorMessage(const QString &title, const QString &content); void showErrorMessage(const QString &title, const QString &content);
// 设置笔记列表加载状态
void setNoteListLoading(); void setNoteListLoading();
// 选择所有笔记
void selectAllNotesInList(); void selectAllNotesInList();
// 更新框架
void updateFrame(); void updateFrame();
// 检查是否是标题栏
bool isTitleBar(int x, int y) const; bool isTitleBar(int x, int y) const;
// 打开订阅窗口
void openSubscriptionWindow(); void openSubscriptionWindow();
signals: signals:
// 请求节点树
void requestNodesTree(); void requestNodesTree();
// 请求打开数据库管理器
void requestOpenDBManager(const QString &path, bool doCreate); void requestOpenDBManager(const QString &path, bool doCreate);
// 请求恢复笔记
void requestRestoreNotes(const QString &filePath); void requestRestoreNotes(const QString &filePath);
// 请求导入笔记
void requestImportNotes(const QString &filePath); void requestImportNotes(const QString &filePath);
// 请求导出笔记
void requestExportNotes(QString fileName); void requestExportNotes(QString fileName);
// 请求从版本0.9.0迁移笔记
void requestMigrateNotesFromV0_9_0(QVector<NodeData> &noteList); void requestMigrateNotesFromV0_9_0(QVector<NodeData> &noteList);
// 请求从版本0.9.0迁移回收站
void requestMigrateTrashFromV0_9_0(QVector<NodeData> &noteList); void requestMigrateTrashFromV0_9_0(QVector<NodeData> &noteList);
// 请求从版本1.5.0迁移笔记
void requestMigrateNotesFromV1_5_0(const QString &path); void requestMigrateNotesFromV1_5_0(const QString &path);
// 请求更改数据库路径
void requestChangeDatabasePath(const QString &newPath); void requestChangeDatabasePath(const QString &newPath);
// 主题变化信号
void themeChanged(QVariant theme); void themeChanged(QVariant theme);
// 平台设置信号
void platformSet(QVariant platform); void platformSet(QVariant platform);
// Qt版本设置信号
void qtVersionSet(QVariant qtVersion); void qtVersionSet(QVariant qtVersion);
// 编辑器设置显示信号
void editorSettingsShowed(QVariant data); void editorSettingsShowed(QVariant data);
// 主窗口大小改变信号
void mainWindowResized(QVariant data); void mainWindowResized(QVariant data);
// 主窗口移动信号
void mainWindowMoved(QVariant data); void mainWindowMoved(QVariant data);
// 设置显示字体信号
void displayFontSet(QVariant data); void displayFontSet(QVariant data);
// 设置改变信号
void settingsChanged(QVariant data); void settingsChanged(QVariant data);
// 字体改变信号
void fontsChanged(QVariant data); void fontsChanged(QVariant data);
// 切换编辑器设置快捷键触发信号
void toggleEditorSettingsKeyboardShorcutFired(); void toggleEditorSettingsKeyboardShorcutFired();
// 编辑器设置滚动条位置改变信号
void editorSettingsScrollBarPositionChanged(QVariant data); void editorSettingsScrollBarPositionChanged(QVariant data);
// 专业版检查信号
void proVersionCheck(QVariant data); void proVersionCheck(QVariant data);
// 尝试获取第二种购买数据
void tryPurchaseDataSecondAlternative(); void tryPurchaseDataSecondAlternative();
// 远程获取支付详情完成信号
void fetchingPaymentDetailsRemotelyFinished(); void fetchingPaymentDetailsRemotelyFinished();
// 获取支付详情完成信号
void gettingPaymentDetailsFinished(); void gettingPaymentDetailsFinished();
// 订阅状态改变信号
void subscriptionStatusChanged(QVariant subscriptionStatus); void subscriptionStatusChanged(QVariant subscriptionStatus);
}; };

@ -1,7 +1,6 @@
#include "nodedata.h" #include "nodedata.h"
#include <QDataStream> #include <QDataStream>
// NodeData 类的构造函数,初始化成员变量
NodeData::NodeData() NodeData::NodeData()
: m_id{ SpecialNodeID::InvalidNodeId }, : m_id{ SpecialNodeID::InvalidNodeId },
m_isModified(false), m_isModified(false),
@ -15,247 +14,206 @@ NodeData::NodeData()
{ {
} }
// 获取节点ID
int NodeData::id() const int NodeData::id() const
{ {
return m_id; return m_id;
} }
// 设置节点ID
void NodeData::setId(int id) void NodeData::setId(int id)
{ {
m_id = id; m_id = id;
} }
// 获取节点的完整标题
QString NodeData::fullTitle() const QString NodeData::fullTitle() const
{ {
return m_fullTitle; return m_fullTitle;
} }
// 设置节点的完整标题
void NodeData::setFullTitle(const QString &fullTitle) void NodeData::setFullTitle(const QString &fullTitle)
{ {
m_fullTitle = fullTitle; m_fullTitle = fullTitle;
} }
// 获取最后修改时间
QDateTime NodeData::lastModificationdateTime() const QDateTime NodeData::lastModificationdateTime() const
{ {
return m_lastModificationDateTime; return m_lastModificationDateTime;
} }
// 设置最后修改时间
void NodeData::setLastModificationDateTime(const QDateTime &lastModificationdateTime) void NodeData::setLastModificationDateTime(const QDateTime &lastModificationdateTime)
{ {
m_lastModificationDateTime = lastModificationdateTime; m_lastModificationDateTime = lastModificationdateTime;
} }
// 获取节点内容
QString NodeData::content() const QString NodeData::content() const
{ {
return m_content; return m_content;
} }
// 设置节点内容
void NodeData::setContent(const QString &content) void NodeData::setContent(const QString &content)
{ {
m_content = content; m_content = content;
} }
// 检查节点是否被修改过
bool NodeData::isModified() const bool NodeData::isModified() const
{ {
return m_isModified; return m_isModified;
} }
// 设置节点的修改状态
void NodeData::setModified(bool isModified) void NodeData::setModified(bool isModified)
{ {
m_isModified = isModified; m_isModified = isModified;
} }
// 检查节点是否被选中
bool NodeData::isSelected() const bool NodeData::isSelected() const
{ {
return m_isSelected; return m_isSelected;
} }
// 设置节点的选中状态
void NodeData::setSelected(bool isSelected) void NodeData::setSelected(bool isSelected)
{ {
m_isSelected = isSelected; m_isSelected = isSelected;
} }
// 获取滚动条位置
int NodeData::scrollBarPosition() const int NodeData::scrollBarPosition() const
{ {
return m_scrollBarPosition; return m_scrollBarPosition;
} }
// 设置滚动条位置
void NodeData::setScrollBarPosition(int scrollBarPosition) void NodeData::setScrollBarPosition(int scrollBarPosition)
{ {
m_scrollBarPosition = scrollBarPosition; m_scrollBarPosition = scrollBarPosition;
} }
// 获取删除时间
QDateTime NodeData::deletionDateTime() const QDateTime NodeData::deletionDateTime() const
{ {
return m_deletionDateTime; return m_deletionDateTime;
} }
// 设置删除时间
void NodeData::setDeletionDateTime(const QDateTime &deletionDateTime) void NodeData::setDeletionDateTime(const QDateTime &deletionDateTime)
{ {
m_deletionDateTime = deletionDateTime; m_deletionDateTime = deletionDateTime;
} }
// 获取节点类型
NodeData::Type NodeData::nodeType() const NodeData::Type NodeData::nodeType() const
{ {
return m_nodeType; return m_nodeType;
} }
// 设置节点类型
void NodeData::setNodeType(NodeData::Type newNodeType) void NodeData::setNodeType(NodeData::Type newNodeType)
{ {
m_nodeType = newNodeType; m_nodeType = newNodeType;
} }
// 获取父节点ID
int NodeData::parentId() const int NodeData::parentId() const
{ {
return m_parentId; return m_parentId;
} }
// 设置父节点ID
void NodeData::setParentId(int newParentId) void NodeData::setParentId(int newParentId)
{ {
m_parentId = newParentId; m_parentId = newParentId;
} }
// 获取相对位置
int NodeData::relativePosition() const int NodeData::relativePosition() const
{ {
return m_relativePosition; return m_relativePosition;
} }
// 设置相对位置
void NodeData::setRelativePosition(int newRelativePosition) void NodeData::setRelativePosition(int newRelativePosition)
{ {
m_relativePosition = newRelativePosition; m_relativePosition = newRelativePosition;
} }
// 获取绝对路径
const QString &NodeData::absolutePath() const const QString &NodeData::absolutePath() const
{ {
return m_absolutePath; return m_absolutePath;
} }
// 设置绝对路径
void NodeData::setAbsolutePath(const QString &newAbsolutePath) void NodeData::setAbsolutePath(const QString &newAbsolutePath)
{ {
m_absolutePath = newAbsolutePath; m_absolutePath = newAbsolutePath;
} }
// 获取标签ID集
const QSet<int> &NodeData::tagIds() const const QSet<int> &NodeData::tagIds() const
{ {
return m_tagIds; return m_tagIds;
} }
// 设置标签ID集
void NodeData::setTagIds(const QSet<int> &newTagIds) void NodeData::setTagIds(const QSet<int> &newTagIds)
{ {
m_tagIds = newTagIds; m_tagIds = newTagIds;
} }
// 检查是否为临时节点
bool NodeData::isTempNote() const bool NodeData::isTempNote() const
{ {
return m_isTempNote; return m_isTempNote;
} }
// 设置临时节点状态
void NodeData::setIsTempNote(bool newIsTempNote) void NodeData::setIsTempNote(bool newIsTempNote)
{ {
m_isTempNote = newIsTempNote; m_isTempNote = newIsTempNote;
} }
// 获取父节点名称
const QString &NodeData::parentName() const const QString &NodeData::parentName() const
{ {
return m_parentName; return m_parentName;
} }
// 设置父节点名称
void NodeData::setParentName(const QString &newParentName) void NodeData::setParentName(const QString &newParentName)
{ {
m_parentName = newParentName; m_parentName = newParentName;
} }
// 检查是否为固定节点
bool NodeData::isPinnedNote() const bool NodeData::isPinnedNote() const
{ {
return m_isPinnedNote; return m_isPinnedNote;
} }
// 设置固定节点状态
void NodeData::setIsPinnedNote(bool newIsPinnedNote) void NodeData::setIsPinnedNote(bool newIsPinnedNote)
{ {
m_isPinnedNote = newIsPinnedNote; m_isPinnedNote = newIsPinnedNote;
} }
// 获取标签列表滚动条位置
int NodeData::tagListScrollBarPos() const int NodeData::tagListScrollBarPos() const
{ {
return m_tagListScrollBarPos; return m_tagListScrollBarPos;
} }
// 设置标签列表滚动条位置
void NodeData::setTagListScrollBarPos(int newTagListScrollBarPos) void NodeData::setTagListScrollBarPos(int newTagListScrollBarPos)
{ {
m_tagListScrollBarPos = newTagListScrollBarPos; m_tagListScrollBarPos = newTagListScrollBarPos;
} }
// 获取相对位置AN
int NodeData::relativePosAN() const int NodeData::relativePosAN() const
{ {
return m_relativePosAN; return m_relativePosAN;
} }
// 设置相对位置AN
void NodeData::setRelativePosAN(int newRelativePosAN) void NodeData::setRelativePosAN(int newRelativePosAN)
{ {
m_relativePosAN = newRelativePosAN; m_relativePosAN = newRelativePosAN;
} }
// 获取子节点计数
int NodeData::childNotesCount() const int NodeData::childNotesCount() const
{ {
return m_childNotesCount; return m_childNotesCount;
} }
// 设置子节点计数
void NodeData::setChildNotesCount(int newChildCount) void NodeData::setChildNotesCount(int newChildCount)
{ {
m_childNotesCount = newChildCount; m_childNotesCount = newChildCount;
} }
// 获取创建时间
QDateTime NodeData::creationDateTime() const QDateTime NodeData::creationDateTime() const
{ {
return m_creationDateTime; return m_creationDateTime;
} }
// 设置创建时间
void NodeData::setCreationDateTime(const QDateTime &creationDateTime) void NodeData::setCreationDateTime(const QDateTime &creationDateTime)
{ {
m_creationDateTime = creationDateTime; m_creationDateTime = creationDateTime;
} }
// 重载流提取运算符,读取 NodeData 对象
QDataStream &operator>>(QDataStream &stream, NodeData &nodeData) QDataStream &operator>>(QDataStream &stream, NodeData &nodeData)
{ {
int id; int id;
@ -272,7 +230,6 @@ QDataStream &operator>>(QDataStream &stream, NodeData &nodeData)
return stream; return stream;
} }
// 重载流提取运算符,读取 NodeData 指针对象
QDataStream &operator>>(QDataStream &stream, NodeData *&nodeData) QDataStream &operator>>(QDataStream &stream, NodeData *&nodeData)
{ {
nodeData = new NodeData(); nodeData = new NodeData();

@ -14,7 +14,6 @@ enum Value {
}; };
} }
// NodeData类用于表示节点数据
class NodeData class NodeData
{ {
public: public:
@ -22,104 +21,64 @@ public:
enum Type { Note = 0, Folder }; enum Type { Note = 0, Folder };
// 获取节点ID
int id() const; int id() const;
// 设置节点ID
void setId(int id); void setId(int id);
// 获取完整标题
QString fullTitle() const; QString fullTitle() const;
// 设置完整标题
void setFullTitle(const QString &fullTitle); void setFullTitle(const QString &fullTitle);
// 获取最后修改时间
QDateTime lastModificationdateTime() const; QDateTime lastModificationdateTime() const;
// 设置最后修改时间
void setLastModificationDateTime(const QDateTime &lastModificationdateTime); void setLastModificationDateTime(const QDateTime &lastModificationdateTime);
// 获取创建时间
QDateTime creationDateTime() const; QDateTime creationDateTime() const;
// 设置创建时间
void setCreationDateTime(const QDateTime &creationDateTime); void setCreationDateTime(const QDateTime &creationDateTime);
// 获取内容
QString content() const; QString content() const;
// 设置内容
void setContent(const QString &content); void setContent(const QString &content);
// 判断是否被修改
bool isModified() const; bool isModified() const;
// 设置修改状态
void setModified(bool isModified); void setModified(bool isModified);
// 判断是否被选中
bool isSelected() const; bool isSelected() const;
// 设置选中状态
void setSelected(bool isSelected); void setSelected(bool isSelected);
// 获取滚动条位置
int scrollBarPosition() const; int scrollBarPosition() const;
// 设置滚动条位置
void setScrollBarPosition(int scrollBarPosition); void setScrollBarPosition(int scrollBarPosition);
// 获取删除时间
QDateTime deletionDateTime() const; QDateTime deletionDateTime() const;
// 设置删除时间
void setDeletionDateTime(const QDateTime &deletionDateTime); void setDeletionDateTime(const QDateTime &deletionDateTime);
// 获取节点类型
NodeData::Type nodeType() const; NodeData::Type nodeType() const;
// 设置节点类型
void setNodeType(NodeData::Type newNodeType); void setNodeType(NodeData::Type newNodeType);
// 获取父节点ID
int parentId() const; int parentId() const;
// 设置父节点ID
void setParentId(int newParentId); void setParentId(int newParentId);
// 获取相对位置
int relativePosition() const; int relativePosition() const;
// 设置相对位置
void setRelativePosition(int newRelativePosition); void setRelativePosition(int newRelativePosition);
// 获取绝对路径
const QString &absolutePath() const; const QString &absolutePath() const;
// 设置绝对路径
void setAbsolutePath(const QString &newAbsolutePath); void setAbsolutePath(const QString &newAbsolutePath);
// 获取标签ID集合
const QSet<int> &tagIds() const; const QSet<int> &tagIds() const;
// 设置标签ID集合
void setTagIds(const QSet<int> &newTagIds); void setTagIds(const QSet<int> &newTagIds);
// 判断是否为临时笔记
bool isTempNote() const; bool isTempNote() const;
// 设置临时笔记状态
void setIsTempNote(bool newIsTempNote); void setIsTempNote(bool newIsTempNote);
// 获取父节点名称
const QString &parentName() const; const QString &parentName() const;
// 设置父节点名称
void setParentName(const QString &newParentName); void setParentName(const QString &newParentName);
// 判断是否为固定笔记
bool isPinnedNote() const; bool isPinnedNote() const;
// 设置固定笔记状态
void setIsPinnedNote(bool newIsPinnedNote); void setIsPinnedNote(bool newIsPinnedNote);
// 获取标签列表滚动条位置
int tagListScrollBarPos() const; int tagListScrollBarPos() const;
// 设置标签列表滚动条位置
void setTagListScrollBarPos(int newTagListScrollBarPos); void setTagListScrollBarPos(int newTagListScrollBarPos);
// 获取相对位置AN
int relativePosAN() const; int relativePosAN() const;
// 设置相对位置AN
void setRelativePosAN(int newRelativePosAN); void setRelativePosAN(int newRelativePosAN);
// 获取子笔记数量
int childNotesCount() const; int childNotesCount() const;
// 设置子笔记数量
void setChildNotesCount(int newChildCount); void setChildNotesCount(int newChildCount);
private: private:
@ -145,12 +104,9 @@ private:
int m_childNotesCount; int m_childNotesCount;
}; };
// 用于QMetaType的声明
Q_DECLARE_METATYPE(NodeData) Q_DECLARE_METATYPE(NodeData)
// 重载输入流运算符以读取NodeData对象
QDataStream &operator>>(QDataStream &stream, NodeData &nodeData); QDataStream &operator>>(QDataStream &stream, NodeData &nodeData);
// 重载输入流运算符以读取NodeData指针对象
QDataStream &operator>>(QDataStream &stream, NodeData *&nodeData); QDataStream &operator>>(QDataStream &stream, NodeData *&nodeData);
#endif // NODEDATA_H #endif // NODEDATA_H

@ -1,10 +1,8 @@
#include "nodepath.h" #include "nodepath.h"
#include "nodedata.h" #include "nodedata.h"
// NodePath类的构造函数初始化路径
NodePath::NodePath(const QString &path) : m_path(path) { } NodePath::NodePath(const QString &path) : m_path(path) { }
// 分离路径为QStringList
QStringList NodePath::separate() const QStringList NodePath::separate() const
{ {
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
@ -14,13 +12,11 @@ QStringList NodePath::separate() const
#endif #endif
} }
// 获取当前路径
QString NodePath::path() const QString NodePath::path() const
{ {
return m_path; return m_path;
} }
// 获取父路径
NodePath NodePath::parentPath() const NodePath NodePath::parentPath() const
{ {
auto s = separate(); auto s = separate();
@ -28,13 +24,11 @@ NodePath NodePath::parentPath() const
return s.join(PATH_SEPARATOR); return s.join(PATH_SEPARATOR);
} }
// 获取所有笔记文件夹的路径
QString NodePath::getAllNoteFolderPath() QString NodePath::getAllNoteFolderPath()
{ {
return PATH_SEPARATOR + QString::number(SpecialNodeID::RootFolder); return PATH_SEPARATOR + QString::number(SpecialNodeID::RootFolder);
} }
// 获取回收站文件夹的路径
QString NodePath::getTrashFolderPath() QString NodePath::getTrashFolderPath()
{ {
return PATH_SEPARATOR + QString::number(SpecialNodeID::RootFolder) + PATH_SEPARATOR return PATH_SEPARATOR + QString::number(SpecialNodeID::RootFolder) + PATH_SEPARATOR

@ -9,30 +9,19 @@
#define TAG_MIME "application/x-tagnode" #define TAG_MIME "application/x-tagnode"
#define NOTE_MIME "application/x-notenode" #define NOTE_MIME "application/x-notenode"
// NodePath 类用于处理节点路径
class NodePath class NodePath
{ {
public: public:
// 构造函数,初始化节点路径
NodePath(const QString &path); NodePath(const QString &path);
// 将路径分割为字符串列表
QStringList separate() const; QStringList separate() const;
// 获取当前路径
QString path() const; QString path() const;
// 获取父路径
NodePath parentPath() const; NodePath parentPath() const;
// 获取所有笔记文件夹的路径
static QString getAllNoteFolderPath(); static QString getAllNoteFolderPath();
// 获取回收站文件夹的路径
static QString getTrashFolderPath(); static QString getTrashFolderPath();
private: private:
QString m_path; // 存储节点路径 QString m_path;
}; };
#endif // NODEPATH_H #endif // NODEPATH_H

@ -15,9 +15,8 @@
#include <QFontMetrics> #include <QFontMetrics>
#include "fontloader.h" #include "fontloader.h"
NodeTreeDelegate::NodeTreeDelegate(QTreeView *view, QObject *parent, QListView *listView)// 构造函数初始化NodeTreeDelegate NodeTreeDelegate::NodeTreeDelegate(QTreeView *view, QObject *parent, QListView *listView)
: QStyledItemDelegate{ parent }, : QStyledItemDelegate{ parent },
// 根据平台选择合适的字体
#ifdef __APPLE__ #ifdef __APPLE__
m_displayFont(QFont(QStringLiteral("SF Pro Text")).exactMatch() m_displayFont(QFont(QStringLiteral("SF Pro Text")).exactMatch()
? QStringLiteral("SF Pro Text") ? QStringLiteral("SF Pro Text")
@ -28,7 +27,6 @@ NodeTreeDelegate::NodeTreeDelegate(QTreeView *view, QObject *parent, QListView *
#else #else
m_displayFont(QStringLiteral("Roboto")), m_displayFont(QStringLiteral("Roboto")),
#endif #endif
// 根据平台设置字体大小和样式
#ifdef __APPLE__ #ifdef __APPLE__
m_titleFont(m_displayFont, 13, QFont::DemiBold), m_titleFont(m_displayFont, 13, QFont::DemiBold),
m_titleSelectedFont(m_displayFont, 13), m_titleSelectedFont(m_displayFont, 13),
@ -41,7 +39,7 @@ NodeTreeDelegate::NodeTreeDelegate(QTreeView *view, QObject *parent, QListView *
m_dateFont(m_displayFont, 10), m_dateFont(m_displayFont, 10),
m_separatorFont(m_displayFont, 9, QFont::Normal), m_separatorFont(m_displayFont, 9, QFont::Normal),
m_numberOfNotesFont(m_displayFont, 9, QFont::DemiBold), m_numberOfNotesFont(m_displayFont, 9, QFont::DemiBold),
#endif// 初始化颜色设置 #endif
m_titleColor(26, 26, 26), m_titleColor(26, 26, 26),
m_titleSelectedColor(255, 255, 255), m_titleSelectedColor(255, 255, 255),
m_dateColor(132, 132, 132), m_dateColor(132, 132, 132),
@ -59,15 +57,15 @@ NodeTreeDelegate::NodeTreeDelegate(QTreeView *view, QObject *parent, QListView *
m_view(view), m_view(view),
m_listView(listView), m_listView(listView),
m_theme(Theme::Light) m_theme(Theme::Light)
{// 构造函数体为空 {
} }
// 设置主题
void NodeTreeDelegate::setTheme(Theme::Value theme) void NodeTreeDelegate::setTheme(Theme::Value theme)
{ {
emit themeChanged(theme); // 发出主题变化信号 emit themeChanged(theme);
m_theme = theme; // 更新当前主题 m_theme = theme;
switch (theme) { switch (theme) {
case Theme::Light: {// 设置浅色主题的颜色 case Theme::Light: {
m_titleColor = QColor(26, 26, 26); m_titleColor = QColor(26, 26, 26);
m_dateColor = QColor(26, 26, 26); m_dateColor = QColor(26, 26, 26);
m_defaultColor = QColor(247, 247, 247); m_defaultColor = QColor(247, 247, 247);
@ -78,7 +76,7 @@ void NodeTreeDelegate::setTheme(Theme::Value theme)
m_numberOfNotesColor = QColor(26, 26, 26, 127); m_numberOfNotesColor = QColor(26, 26, 26, 127);
break; break;
} }
case Theme::Dark: {// 设置深色主题的颜色 case Theme::Dark: {
m_titleColor = QColor(212, 212, 212); m_titleColor = QColor(212, 212, 212);
m_dateColor = QColor(212, 212, 212); m_dateColor = QColor(212, 212, 212);
m_defaultColor = QColor(25, 25, 25); m_defaultColor = QColor(25, 25, 25);
@ -89,7 +87,7 @@ void NodeTreeDelegate::setTheme(Theme::Value theme)
m_numberOfNotesColor = QColor(212, 212, 212, 127); m_numberOfNotesColor = QColor(212, 212, 212, 127);
break; break;
} }
case Theme::Sepia: {// 设置棕褐色主题的颜色 case Theme::Sepia: {
m_titleColor = QColor(26, 26, 26); m_titleColor = QColor(26, 26, 26);
m_dateColor = QColor(26, 26, 26); m_dateColor = QColor(26, 26, 26);
m_defaultColor = QColor(251, 240, 217); m_defaultColor = QColor(251, 240, 217);
@ -102,28 +100,27 @@ void NodeTreeDelegate::setTheme(Theme::Value theme)
} }
} }
} }
// 绘制函数
void NodeTreeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, void NodeTreeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const const QModelIndex &index) const
{ {
painter->setRenderHint(QPainter::Antialiasing);// 开启抗锯齿 painter->setRenderHint(QPainter::Antialiasing);
auto itemType = static_cast<NodeItem::Type>(index.data(NodeItem::Roles::ItemType).toInt()); auto itemType = static_cast<NodeItem::Type>(index.data(NodeItem::Roles::ItemType).toInt());
// 根据平台设置图标字体大小偏移
#ifdef __APPLE__ #ifdef __APPLE__
int iconPointSizeOffset = 0; int iconPointSizeOffset = 0;
#else #else
int iconPointSizeOffset = -4; int iconPointSizeOffset = -4;
#endif #endif
painter->fillRect(option.rect, m_currentBackgroundColor);// 填充背景色 painter->fillRect(option.rect, m_currentBackgroundColor);
switch (itemType) { switch (itemType) {
// 绘制逻辑根据不同的项类型进行分支处理
case NodeItem::Type::RootItem: { case NodeItem::Type::RootItem: {
break; break;
} }
case NodeItem::Type::AllNoteButton: case NodeItem::Type::AllNoteButton:
case NodeItem::Type::TrashButton: { case NodeItem::Type::TrashButton: {
paintBackgroundSelectable(painter, option, index);// 绘制可选择背景 paintBackgroundSelectable(painter, option, index);
auto iconRect = QRect(option.rect.x() + 22, auto iconRect = QRect(option.rect.x() + 22,
option.rect.y() + (option.rect.height() - 20) / 2, 18, 20); option.rect.y() + (option.rect.height() - 20) / 2, 18, 20);
QFont previousPainterFont = painter->font(); QFont previousPainterFont = painter->font();
@ -279,7 +276,7 @@ void NodeTreeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
break; break;
} }
case NodeItem::Type::TagItem: { case NodeItem::Type::TagItem: {
paintBackgroundSelectable(painter, option, index);// 绘制可选择的背景 paintBackgroundSelectable(painter, option, index);
auto iconRect = QRect(option.rect.x() + 22, auto iconRect = QRect(option.rect.x() + 22,
option.rect.y() + (option.rect.height() - 14) / 2, 16, 16); option.rect.y() + (option.rect.height() - 14) / 2, 16, 16);
auto tagColor = index.data(NodeItem::Roles::TagColor).toString(); auto tagColor = index.data(NodeItem::Roles::TagColor).toString();
@ -318,45 +315,45 @@ void NodeTreeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
} }
} }
} }
// 绘制可选择的背景
void NodeTreeDelegate::paintBackgroundSelectable(QPainter *painter, void NodeTreeDelegate::paintBackgroundSelectable(QPainter *painter,
const QStyleOptionViewItem &option, const QStyleOptionViewItem &option,
const QModelIndex &index) const const QModelIndex &index) const
{ {
if ((option.state & QStyle::State_Selected) == QStyle::State_Selected) { if ((option.state & QStyle::State_Selected) == QStyle::State_Selected) {
painter->fillRect(option.rect, QBrush(m_ActiveColor));// 如果选中,填充活动颜色 painter->fillRect(option.rect, QBrush(m_ActiveColor));
} else if ((option.state & QStyle::State_MouseOver) == QStyle::State_MouseOver) { } else if ((option.state & QStyle::State_MouseOver) == QStyle::State_MouseOver) {
auto treeView = dynamic_cast<NodeTreeView *>(m_view); auto treeView = dynamic_cast<NodeTreeView *>(m_view);
auto itemType = static_cast<NodeItem::Type>(index.data(NodeItem::Roles::ItemType).toInt()); auto itemType = static_cast<NodeItem::Type>(index.data(NodeItem::Roles::ItemType).toInt());
if (itemType == NodeItem::Type::TrashButton) { if (itemType == NodeItem::Type::TrashButton) {
return;// 垃圾桶按钮不绘制悬停背景 return;
} }
if (!treeView->isDragging()) { if (!treeView->isDragging()) {
painter->fillRect(option.rect, QBrush(m_hoverColor));// 如果悬停,填充悬停颜色 painter->fillRect(option.rect, QBrush(m_hoverColor));
} }
} }
} }
// 计算项的大小提示
QSize NodeTreeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const QSize NodeTreeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{ {
QSize result = QStyledItemDelegate::sizeHint(option, index); QSize result = QStyledItemDelegate::sizeHint(option, index);
auto itemType = static_cast<NodeItem::Type>(index.data(NodeItem::Roles::ItemType).toInt()); auto itemType = static_cast<NodeItem::Type>(index.data(NodeItem::Roles::ItemType).toInt());
if (itemType == NodeItem::Type::FolderSeparator) { if (itemType == NodeItem::Type::FolderSeparator) {
result.setHeight(NoteTreeConstant::folderLabelHeight);// 文件夹分隔符的高度 result.setHeight(NoteTreeConstant::folderLabelHeight);
} else if (itemType == NodeItem::Type::TagSeparator) { } else if (itemType == NodeItem::Type::TagSeparator) {
result.setHeight(NoteTreeConstant::tagLabelHeight);// 标签分隔符的高度 result.setHeight(NoteTreeConstant::tagLabelHeight);
} else if (itemType == NodeItem::Type::FolderItem || itemType == NodeItem::Type::TrashButton } else if (itemType == NodeItem::Type::FolderItem || itemType == NodeItem::Type::TrashButton
|| itemType == NodeItem::Type::AllNoteButton) { || itemType == NodeItem::Type::AllNoteButton) {
result.setHeight(NoteTreeConstant::folderItemHeight);// 文件夹项、垃圾桶按钮、所有笔记按钮的高度 result.setHeight(NoteTreeConstant::folderItemHeight);
} else if (itemType == NodeItem::Type::TagItem) { } else if (itemType == NodeItem::Type::TagItem) {
result.setHeight(NoteTreeConstant::tagItemHeight);// 标签项的高度 result.setHeight(NoteTreeConstant::tagItemHeight);
} else { } else {
result.setHeight(30); result.setHeight(30);
} }
return result; return result;
} }
// 创建编辑器
QWidget *NodeTreeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, QWidget *NodeTreeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const const QModelIndex &index) const
{ {
@ -448,7 +445,7 @@ QWidget *NodeTreeDelegate::createEditor(QWidget *parent, const QStyleOptionViewI
} }
return nullptr; return nullptr;
} }
// 更新编辑器的几何形状
void NodeTreeDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, void NodeTreeDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &index) const const QModelIndex &index) const
{ {

@ -7,80 +7,56 @@
class QTreeView; class QTreeView;
class QListView; class QListView;
class NodeTreeDelegate : public QStyledItemDelegate// NodeTreeDelegate 类继承自 QStyledItemDelegate class NodeTreeDelegate : public QStyledItemDelegate
{ {
Q_OBJECT// Qt 元对象宏,用于信号和槽机制 Q_OBJECT
public:// 构造函数,初始化 NodeTreeDelegate public:
explicit NodeTreeDelegate(QTreeView *view, QObject *parent = nullptr, explicit NodeTreeDelegate(QTreeView *view, QObject *parent = nullptr,
QListView *listView = nullptr); QListView *listView = nullptr);
void setTheme(Theme::Value theme);// 设置主题 void setTheme(Theme::Value theme);
signals: signals:
void addFolderRequested();// 信号:请求添加文件夹 void addFolderRequested();
void addTagRequested();// 信号:请求添加标签 void addTagRequested();
void themeChanged(Theme::Value theme);// 信号:主题变化 void themeChanged(Theme::Value theme);
// QAbstractItemDelegate interface // QAbstractItemDelegate interface
// 实现 QAbstractItemDelegate 接口
public: public:
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option,// 绘制项 virtual void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override; const QModelIndex &index) const override;
virtual QSize sizeHint(const QStyleOptionViewItem &option,// 计算项的大小提示 virtual QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const override; const QModelIndex &index) const override;
virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;// 创建编辑器 const QModelIndex &index) const override;
virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;// 更新编辑器的几何形状 const QModelIndex &index) const override;
private: private:
void paintBackgroundSelectable(QPainter *painter, const QStyleOptionViewItem &option, void paintBackgroundSelectable(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const;// 绘制可选择的背景 const QModelIndex &index) const;
private: private:
// 显示字体名称
QString m_displayFont; QString m_displayFont;
// 标题字体
QFont m_titleFont; QFont m_titleFont;
// 选中标题字体
QFont m_titleSelectedFont; QFont m_titleSelectedFont;
// 日期字体
QFont m_dateFont; QFont m_dateFont;
// 分隔符字体
QFont m_separatorFont; QFont m_separatorFont;
// 笔记数量字体
QFont m_numberOfNotesFont; QFont m_numberOfNotesFont;
// 标题颜色
QColor m_titleColor; QColor m_titleColor;
// 选中标题颜色
QColor m_titleSelectedColor; QColor m_titleSelectedColor;
// 日期颜色
QColor m_dateColor; QColor m_dateColor;
// 活动颜色
QColor m_ActiveColor; QColor m_ActiveColor;
// 非活动颜色
QColor m_notActiveColor; QColor m_notActiveColor;
// 悬停颜色
QColor m_hoverColor; QColor m_hoverColor;
// 应用程序非活动颜色
QColor m_applicationInactiveColor; QColor m_applicationInactiveColor;
// 分隔符颜色
QColor m_separatorColor; QColor m_separatorColor;
// 默认颜色
QColor m_defaultColor; QColor m_defaultColor;
// 分隔符文本颜色
QColor m_separatorTextColor; QColor m_separatorTextColor;
// 当前背景颜色
QColor m_currentBackgroundColor; QColor m_currentBackgroundColor;
// 笔记数量颜色
QColor m_numberOfNotesColor; QColor m_numberOfNotesColor;
// 选中笔记数量颜色
QColor m_numberOfNotesSelectedColor; QColor m_numberOfNotesSelectedColor;
// 文件夹图标颜色
QColor m_folderIconColor; QColor m_folderIconColor;
// 树视图指针
QTreeView *m_view; QTreeView *m_view;
// 列表视图指针
QListView *m_listView; QListView *m_listView;
// 主题值
Theme::Value m_theme; Theme::Value m_theme;
}; };

@ -10,126 +10,121 @@
#include "nodedata.h" #include "nodedata.h"
#include "dbmanager.h" #include "dbmanager.h"
#include "nodepath.h" #include "nodepath.h"
// NodeItem 命名空间,定义角色和类型枚举
namespace NodeItem { namespace NodeItem {
enum Roles { enum Roles {
ItemType = Qt::UserRole, // 项类型 ItemType = Qt::UserRole,
DisplayText = Qt::DisplayRole, // 显示文本 DisplayText = Qt::DisplayRole,
Icon = Qt::DecorationRole, // 图标 Icon = Qt::DecorationRole,
TagColor = Qt::UserRole + 1, // 标签颜色 TagColor = Qt::UserRole + 1,
IsExpandable, // 是否可展开 IsExpandable,
AbsPath, // 绝对路径 AbsPath,
RelPos, // 相对位置 RelPos,
ChildCount, // 子项数量 ChildCount,
NodeId // 节点ID NodeId
}; };
enum Type { enum Type {
AllNoteButton = 1, // 所有笔记按钮 AllNoteButton = 1,
TrashButton, // 垃圾桶按钮 // We store this enum inside QVariant,
FolderSeparator, // 文件夹分隔符 // and an invalid QVariant conversion return 0
TagSeparator, // 标签分隔符 TrashButton,
FolderItem, // 文件夹项 FolderSeparator,
NoteItem, // 笔记项 TagSeparator,
TagItem, // 标签项 FolderItem,
RootItem // 根项 NoteItem,
TagItem,
RootItem
}; };
} // namespace NodeItem } // namespace NodeItem
class NodeTreeItem// NodeTreeItem 类,表示树中的一个节点 class NodeTreeItem
{ {
public: public:
explicit NodeTreeItem(const QHash<NodeItem::Roles, QVariant> &data, explicit NodeTreeItem(const QHash<NodeItem::Roles, QVariant> &data,
NodeTreeItem *parentItem = nullptr); // 构造函数 NodeTreeItem *parentItem = nullptr);
~NodeTreeItem(); // 析构函数 ~NodeTreeItem();
void appendChild(NodeTreeItem *child); // 添加子节点 void appendChild(NodeTreeItem *child);
void insertChild(int row, NodeTreeItem *child); // 在指定位置插入子节点 void insertChild(int row, NodeTreeItem *child);
NodeTreeItem *child(int row); // 获取指定位置的子节点 NodeTreeItem *child(int row);
void removeChild(int row); // 移除指定位置的子节点 void removeChild(int row);
NodeTreeItem *takeChildAt(int row); // 移除并返回指定位置的子节点 NodeTreeItem *takeChildAt(int row);
int childCount() const; // 获取子节点数量 int childCount() const;
int columnCount() const; // 获取列数 int columnCount() const;
int recursiveNodeCount() const; // 递归获取节点数量 int recursiveNodeCount() const;
void recursiveUpdateFolderPath(const QString &oldP, const QString &newP); // 递归更新文件夹路径 void recursiveUpdateFolderPath(const QString &oldP, const QString &newP);
QVariant data(NodeItem::Roles role) const; // 获取指定角色的数据 QVariant data(NodeItem::Roles role) const;
void setData(NodeItem::Roles role, const QVariant &d); // 设置指定角色的数据 void setData(NodeItem::Roles role, const QVariant &d);
int row() const; // 获取当前节点的行号 int row() const;
NodeTreeItem *parentItem(); // 获取父节点 NodeTreeItem *parentItem();
void setParentItem(NodeTreeItem *parentItem); // 设置父节点 void setParentItem(NodeTreeItem *parentItem);
void moveChild(int from, int to); // 移动子节点 void moveChild(int from, int to);
void recursiveSort(); // 递归排序 void recursiveSort();
private: private:
QVector<NodeTreeItem *> m_childItems; // 子节点列表 QVector<NodeTreeItem *> m_childItems;
QHash<NodeItem::Roles, QVariant> m_itemData; // 节点数据 QHash<NodeItem::Roles, QVariant> m_itemData;
NodeTreeItem *m_parentItem; // 父节点 NodeTreeItem *m_parentItem;
}; };
class NodeTreeModel : public QAbstractItemModel// NodeTreeModel 类,继承自 QAbstractItemModel class NodeTreeModel : public QAbstractItemModel
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit NodeTreeModel(QObject *parent = nullptr); // 构造函数 explicit NodeTreeModel(QObject *parent = nullptr);
~NodeTreeModel(); // 析构函数 ~NodeTreeModel();
void void appendChildNodeToParent(const QModelIndex &parentIndex,
appendChildNodeToParent(const QModelIndex &parentIndex, const QHash<NodeItem::Roles, QVariant> &data);
const QHash<NodeItem::Roles, QVariant> &data); // 向父节点添加子节点 QModelIndex rootIndex() const;
QModelIndex rootIndex() const; // 获取根节点索引 QModelIndex folderIndexFromIdPath(const NodePath &idPath);
QModelIndex folderIndexFromIdPath(const NodePath &idPath); // 根据ID路径获取文件夹索引 QModelIndex tagIndexFromId(int id);
QModelIndex tagIndexFromId(int id); // 根据ID获取标签索引 QString getNewFolderPlaceholderName(const QModelIndex &parentIndex);
QString getNewFolderPlaceholderName(const QModelIndex &parentIndex); // 获取新文件夹占位符名称 QString getNewTagPlaceholderName();
QString getNewTagPlaceholderName(); // 获取新标签占位符名称 QVector<QModelIndex> getSeparatorIndex();
QVector<QModelIndex> getSeparatorIndex(); // 获取分隔符索引 QModelIndex getDefaultNotesIndex();
QModelIndex getDefaultNotesIndex(); // 获取默认笔记索引 QModelIndex getAllNotesButtonIndex();
QModelIndex getAllNotesButtonIndex(); // 获取所有笔记按钮索引 QModelIndex getTrashButtonIndex();
QModelIndex getTrashButtonIndex(); // 获取垃圾桶按钮索引 void deleteRow(const QModelIndex &rowIndex, const QModelIndex &parentIndex);
void deleteRow(const QModelIndex &rowIndex, const QModelIndex &parentIndex); // 删除行
public slots: public slots:
void setTreeData(const NodeTagTreeData &treeData); // 设置树数据 void setTreeData(const NodeTagTreeData &treeData);
// QAbstractItemModel interface接口实现 // QAbstractItemModel interface
public: public:
virtual QModelIndex index(int row, int column, virtual QModelIndex index(int row, int column, const QModelIndex &parent) const override;
const QModelIndex &parent) const override; // 获取索引 virtual QModelIndex parent(const QModelIndex &index) const override;
virtual QModelIndex parent(const QModelIndex &index) const override; // 获取父索引 virtual int rowCount(const QModelIndex &parent) const override;
virtual int rowCount(const QModelIndex &parent) const override; // 获取行数 virtual int columnCount(const QModelIndex &parent) const override;
virtual int columnCount(const QModelIndex &parent) const override; // 获取列数 virtual QVariant data(const QModelIndex &index, int role) const override;
virtual QVariant data(const QModelIndex &index, int role) const override; // 获取数据 virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
virtual Qt::ItemFlags flags(const QModelIndex &index) const override; // 获取项目标志 virtual bool setData(const QModelIndex &index, const QVariant &value, int role) override;
virtual bool setData(const QModelIndex &index, const QVariant &value, virtual Qt::DropActions supportedDropActions() const override;
int role) override; // 设置数据 virtual Qt::DropActions supportedDragActions() const override;
virtual Qt::DropActions supportedDropActions() const override; // 支持的拖放动作 virtual QStringList mimeTypes() const override;
virtual Qt::DropActions supportedDragActions() const override; // 支持的拖动动作 virtual QMimeData *mimeData(const QModelIndexList &indexes) const override;
virtual QStringList mimeTypes() const override; // 支持的MIME类型
virtual QMimeData *mimeData(const QModelIndexList &indexes) const override; // 获取MIME数据
virtual bool dropMimeData(const QMimeData *mime, Qt::DropAction action, int row, int column, virtual bool dropMimeData(const QMimeData *mime, Qt::DropAction action, int row, int column,
const QModelIndex &parent) override; // 处理拖放数据 const QModelIndex &parent) override;
signals: signals:
void topLevelItemLayoutChanged(); // 顶层项目布局变化信号 void topLevelItemLayoutChanged();
void requestExpand(const QString &indexPath); // 请求展开信号 void requestExpand(const QString &indexPath);
void requestMoveNode(int nodeId, int targetId); // 请求移动节点信号 void requestMoveNode(int nodeId, int targetId);
void requestUpdateNodeRelativePosition(int nodeId, void requestUpdateNodeRelativePosition(int nodeId, int relativePosition);
int relativePosition); // 请求更新节点相对位置信号 void requestUpdateTagRelativePosition(int nodeId, int relativePosition);
void requestUpdateTagRelativePosition(int nodeId, void requestUpdateAbsPath(const QString &oldPath, const QString &newPath);
int relativePosition); // 请求更新标签相对位置信号 void dropFolderSuccessful(const QString &paths);
void requestUpdateAbsPath(const QString &oldPath, void dropTagsSuccessful(const QSet<int> &ids);
const QString &newPath); // 请求更新绝对路径信号 void requestMoveFolderToTrash(const QModelIndex &index);
void dropFolderSuccessful(const QString &paths); // 文件夹拖放成功信号
void dropTagsSuccessful(const QSet<int> &ids); // 标签拖放成功信号
void requestMoveFolderToTrash(const QModelIndex &index); // 请求移动文件夹到垃圾桶信号
private: private:
NodeTreeItem *rootItem; // 根节点 NodeTreeItem *rootItem;
void loadNodeTree(const QVector<NodeData> &nodeData, NodeTreeItem *rootNode); // 加载节点树 void loadNodeTree(const QVector<NodeData> &nodeData, NodeTreeItem *rootNode);
void appendAllNotesAndTrashButton(NodeTreeItem *rootNode); // 添加所有笔记和垃圾桶按钮 void appendAllNotesAndTrashButton(NodeTreeItem *rootNode);
void appendFolderSeparator(NodeTreeItem *rootNode); // 添加文件夹分隔符 void appendFolderSeparator(NodeTreeItem *rootNode);
void appendTagsSeparator(NodeTreeItem *rootNode); // 添加标签分隔符 void appendTagsSeparator(NodeTreeItem *rootNode);
void loadTagList(const QVector<TagData> &tagData, NodeTreeItem *rootNode); // 加载标签列表 void loadTagList(const QVector<TagData> &tagData, NodeTreeItem *rootNode);
void updateChildRelativePosition(NodeTreeItem *parent, void updateChildRelativePosition(NodeTreeItem *parent, const NodeItem::Type type);
const NodeItem::Type type); // 更新子节点相对位置
}; };
#endif // NODETREEMODEL_H #endif // NODETREEMODEL_H

@ -10,60 +10,59 @@
#include <QScrollBar> #include <QScrollBar>
#include "nodetreeview_p.h" #include "nodetreeview_p.h"
NodeTreeView::NodeTreeView(QWidget *parent)// 构造函数,初始化 NodeTreeView NodeTreeView::NodeTreeView(QWidget *parent)
: QTreeView(parent), : QTreeView(parent),
m_isContextMenuOpened{ false }, m_isContextMenuOpened{ false },
m_isEditing{ false }, m_isEditing{ false },
m_ignoreThisCurrentLoad{ false }, m_ignoreThisCurrentLoad{ false },
m_isLastSelectedFolder{ false } m_isLastSelectedFolder{ false }
{ {
setHeaderHidden(true);// 隐藏表头 setHeaderHidden(true);
QFile file(":/styles/nodetreeview.css"); QFile file(":/styles/nodetreeview.css");
file.open(QFile::ReadOnly); file.open(QFile::ReadOnly);
setStyleSheet(file.readAll());// 设置样式表 setStyleSheet(file.readAll());
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) || defined(Q_OS_WIN) || defined(Q_OS_WINDOWS) #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) || defined(Q_OS_WIN) || defined(Q_OS_WINDOWS)
QFile scollBarStyleFile(QStringLiteral(":/styles/components/custom-scrollbar.css")); QFile scollBarStyleFile(QStringLiteral(":/styles/components/custom-scrollbar.css"));
scollBarStyleFile.open(QFile::ReadOnly); scollBarStyleFile.open(QFile::ReadOnly);
QString scrollbarStyleSheet = QString::fromLatin1(scollBarStyleFile.readAll()); QString scrollbarStyleSheet = QString::fromLatin1(scollBarStyleFile.readAll());
verticalScrollBar()->setStyleSheet(scrollbarStyleSheet);// 设置滚动条样式 verticalScrollBar()->setStyleSheet(scrollbarStyleSheet);
#endif #endif
setRootIsDecorated(false); // 不显示根节点的装饰 setRootIsDecorated(false);
setMouseTracking(true); // 启用鼠标跟踪 setMouseTracking(true);
setSelectionMode(QAbstractItemView::MultiSelection); // 设置选择模式为多选 setSelectionMode(QAbstractItemView::MultiSelection);
setContextMenuPolicy(Qt::CustomContextMenu); // 设置上下文菜单策略为自定义 setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &QWidget::customContextMenuRequested, this, connect(this, &QWidget::customContextMenuRequested, this, &NodeTreeView::onCustomContextMenu);
&NodeTreeView::onCustomContextMenu); // 连接自定义上下文菜单信号 contextMenu = new QMenu(this);
contextMenu = new QMenu(this); // 创建上下文菜单 renameFolderAction = new QAction(tr("Rename Folder"), this);
renameFolderAction = new QAction(tr("Rename Folder"), this);// 创建和连接重命名文件夹动作
connect(renameFolderAction, &QAction::triggered, this, [this] { connect(renameFolderAction, &QAction::triggered, this, [this] {
setIsEditing(true); setIsEditing(true);
emit renameFolderRequested(); emit renameFolderRequested();
}); });
deleteFolderAction = new QAction(tr("Delete Folder"), this);// 创建和连接删除文件夹动作 deleteFolderAction = new QAction(tr("Delete Folder"), this);
connect(deleteFolderAction, &QAction::triggered, this, &NodeTreeView::onDeleteNodeAction); connect(deleteFolderAction, &QAction::triggered, this, &NodeTreeView::onDeleteNodeAction);
addSubfolderAction = new QAction(tr("Add Subfolder"), this);// 创建和连接添加子文件夹动作 addSubfolderAction = new QAction(tr("Add Subfolder"), this);
connect(addSubfolderAction, &QAction::triggered, this, &NodeTreeView::addFolderRequested); connect(addSubfolderAction, &QAction::triggered, this, &NodeTreeView::addFolderRequested);
renameTagAction = new QAction(tr("Rename Tag"), this);// 创建和连接重命名标签动作 renameTagAction = new QAction(tr("Rename Tag"), this);
connect(renameTagAction, &QAction::triggered, this, [this] { connect(renameTagAction, &QAction::triggered, this, [this] {
setIsEditing(true); setIsEditing(true);
emit renameTagRequested(); emit renameTagRequested();
}); });
changeTagColorAction = new QAction(tr("Change Tag Color"), this); // 创建和连接更改标签颜色动作 changeTagColorAction = new QAction(tr("Change Tag Color"), this);
connect(changeTagColorAction, &QAction::triggered, this, &NodeTreeView::onChangeTagColorAction); connect(changeTagColorAction, &QAction::triggered, this, &NodeTreeView::onChangeTagColorAction);
deleteTagAction = new QAction(tr("Delete Tag"), this);// 创建和连接删除标签动作 deleteTagAction = new QAction(tr("Delete Tag"), this);
connect(deleteTagAction, &QAction::triggered, this, &NodeTreeView::onDeleteNodeAction); connect(deleteTagAction, &QAction::triggered, this, &NodeTreeView::onDeleteNodeAction);
clearSelectionAction = new QAction(tr("Clear Selection"), this);// 创建和连接清除选择动作 clearSelectionAction = new QAction(tr("Clear Selection"), this);
connect(clearSelectionAction, &QAction::triggered, this, [this] { connect(clearSelectionAction, &QAction::triggered, this, [this] {
closeCurrentEditor(); closeCurrentEditor();
clearSelection(); clearSelection();
setCurrentIndexC(dynamic_cast<NodeTreeModel *>(model())->getAllNotesButtonIndex()); setCurrentIndexC(dynamic_cast<NodeTreeModel *>(model())->getAllNotesButtonIndex());
}); });
contextMenuTimer.setInterval(100);// 设置上下文菜单定时器 contextMenuTimer.setInterval(100);
contextMenuTimer.setSingleShot(true); contextMenuTimer.setSingleShot(true);
connect(&contextMenuTimer, &QTimer::timeout, this, [this] { connect(&contextMenuTimer, &QTimer::timeout, this, [this] {
if (!m_isEditing) { if (!m_isEditing) {
@ -71,19 +70,19 @@ NodeTreeView::NodeTreeView(QWidget *parent)//
} }
}); });
connect(contextMenu, &QMenu::aboutToHide, this, [this] { // 连接上下文菜单隐藏信号 connect(contextMenu, &QMenu::aboutToHide, this, [this] {
m_isContextMenuOpened = false; m_isContextMenuOpened = false;
// this signal is emitted before QAction::triggered // this signal is emitted before QAction::triggered
contextMenuTimer.start(); contextMenuTimer.start();
}); });
connect(this, &NodeTreeView::expanded, this, &NodeTreeView::onExpanded);// 连接展开和收起信号 connect(this, &NodeTreeView::expanded, this, &NodeTreeView::onExpanded);
connect(this, &NodeTreeView::collapsed, this, &NodeTreeView::onCollapsed); connect(this, &NodeTreeView::collapsed, this, &NodeTreeView::onCollapsed);
setDragEnabled(true); // 启用拖动 setDragEnabled(true);
setAcceptDrops(true); // 启用接受拖放 setAcceptDrops(true);
setSelectionMode(QAbstractItemView::SingleSelection); // 设置选择模式为单选 setSelectionMode(QAbstractItemView::SingleSelection);
} }
// 删除节点动作
void NodeTreeView::onDeleteNodeAction() void NodeTreeView::onDeleteNodeAction()
{ {
auto itemType = static_cast<NodeItem::Type>( auto itemType = static_cast<NodeItem::Type>(
@ -99,29 +98,29 @@ void NodeTreeView::onDeleteNodeAction()
emit deleteTagRequested(index); emit deleteTagRequested(index);
} }
} }
// 节点展开时的处理
void NodeTreeView::onExpanded(const QModelIndex &index) void NodeTreeView::onExpanded(const QModelIndex &index)
{ {
m_expanded.push_back(index.data(NodeItem::Roles::AbsPath).toString()); m_expanded.push_back(index.data(NodeItem::Roles::AbsPath).toString());
emit saveExpand(QStringList::fromVector(m_expanded)); emit saveExpand(QStringList::fromVector(m_expanded));
} }
// 节点收起时的处理
void NodeTreeView::onCollapsed(const QModelIndex &index) void NodeTreeView::onCollapsed(const QModelIndex &index)
{ {
m_expanded.removeAll(index.data(NodeItem::Roles::AbsPath).toString()); m_expanded.removeAll(index.data(NodeItem::Roles::AbsPath).toString());
emit saveExpand(QStringList::fromVector(m_expanded)); emit saveExpand(QStringList::fromVector(m_expanded));
} }
// 获取当前编辑的索引
const QModelIndex &NodeTreeView::currentEditingIndex() const const QModelIndex &NodeTreeView::currentEditingIndex() const
{ {
return m_currentEditingIndex; return m_currentEditingIndex;
} }
// 设置忽略当前加载
void NodeTreeView::setIgnoreThisCurrentLoad(bool newIgnoreThisCurrentLoad) void NodeTreeView::setIgnoreThisCurrentLoad(bool newIgnoreThisCurrentLoad)
{ {
m_ignoreThisCurrentLoad = newIgnoreThisCurrentLoad; m_ignoreThisCurrentLoad = newIgnoreThisCurrentLoad;
} }
// 文件夹拖放成功时的处理
void NodeTreeView::onFolderDropSuccessful(const QString &path) void NodeTreeView::onFolderDropSuccessful(const QString &path)
{ {
auto m_model = dynamic_cast<NodeTreeModel *>(model()); auto m_model = dynamic_cast<NodeTreeModel *>(model());
@ -132,7 +131,7 @@ void NodeTreeView::onFolderDropSuccessful(const QString &path)
setCurrentIndexC(m_model->getAllNotesButtonIndex()); setCurrentIndexC(m_model->getAllNotesButtonIndex());
} }
} }
// 标签拖放成功时的处理
void NodeTreeView::onTagsDropSuccessful(const QSet<int> &ids) void NodeTreeView::onTagsDropSuccessful(const QSet<int> &ids)
{ {
auto m_model = dynamic_cast<NodeTreeModel *>(model()); auto m_model = dynamic_cast<NodeTreeModel *>(model());
@ -152,18 +151,18 @@ void NodeTreeView::onTagsDropSuccessful(const QSet<int> &ids)
setCurrentIndexC(m_model->getAllNotesButtonIndex()); setCurrentIndexC(m_model->getAllNotesButtonIndex());
} }
} }
// 获取主题
Theme::Value NodeTreeView::theme() const Theme::Value NodeTreeView::theme() const
{ {
return m_theme; return m_theme;
} }
bool NodeTreeView::isDragging() const// 是否正在拖动 bool NodeTreeView::isDragging() const
{ {
return state() == DraggingState; return state() == DraggingState;
} }
void NodeTreeView::reExpandC()// 重新展开节点 void NodeTreeView::reExpandC()
{ {
auto needExpand = std::move(m_expanded); auto needExpand = std::move(m_expanded);
m_expanded.clear(); m_expanded.clear();
@ -177,14 +176,14 @@ void NodeTreeView::reExpandC()//
} }
} }
void NodeTreeView::reExpandC(const QStringList &expanded)// 重新展开节点(带参数) void NodeTreeView::reExpandC(const QStringList &expanded)
{ {
m_expanded.clear(); m_expanded.clear();
m_expanded = expanded.toVector(); m_expanded = expanded.toVector();
reExpandC(); reExpandC();
} }
void NodeTreeView::onChangeTagColorAction()// 更改标签颜色动作 void NodeTreeView::onChangeTagColorAction()
{ {
auto itemType = static_cast<NodeItem::Type>( auto itemType = static_cast<NodeItem::Type>(
m_currentEditingIndex.data(NodeItem::Roles::ItemType).toInt()); m_currentEditingIndex.data(NodeItem::Roles::ItemType).toInt());
@ -194,13 +193,13 @@ void NodeTreeView::onChangeTagColorAction()//
} }
} }
void NodeTreeView::onRequestExpand(const QString &folderPath)// 请求展开节点 void NodeTreeView::onRequestExpand(const QString &folderPath)
{ {
auto m_model = dynamic_cast<NodeTreeModel *>(model()); auto m_model = dynamic_cast<NodeTreeModel *>(model());
expand(m_model->folderIndexFromIdPath(folderPath)); expand(m_model->folderIndexFromIdPath(folderPath));
} }
void NodeTreeView::onUpdateAbsPath(const QString &oldPath, const QString &newPath)// 更新绝对路径 void NodeTreeView::onUpdateAbsPath(const QString &oldPath, const QString &newPath)
{ {
std::transform(m_expanded.begin(), m_expanded.end(), m_expanded.begin(), [&](QString s) { std::transform(m_expanded.begin(), m_expanded.end(), m_expanded.begin(), [&](QString s) {
s.replace(s.indexOf(oldPath), oldPath.size(), newPath); s.replace(s.indexOf(oldPath), oldPath.size(), newPath);
@ -208,7 +207,7 @@ void NodeTreeView::onUpdateAbsPath(const QString &oldPath, const QString &newPat
}); });
} }
void NodeTreeView::updateEditingIndex(QPoint pos)// 更新编辑索引 void NodeTreeView::updateEditingIndex(QPoint pos)
{ {
auto index = indexAt(pos); auto index = indexAt(pos);
if (indexAt(pos) != m_currentEditingIndex && !m_isContextMenuOpened && !m_isEditing) { if (indexAt(pos) != m_currentEditingIndex && !m_isContextMenuOpened && !m_isEditing) {
@ -225,13 +224,13 @@ void NodeTreeView::updateEditingIndex(QPoint pos)//
} }
} }
void NodeTreeView::closeCurrentEditor()// 关闭当前编辑器 void NodeTreeView::closeCurrentEditor()
{ {
closePersistentEditor(m_currentEditingIndex); closePersistentEditor(m_currentEditingIndex);
m_currentEditingIndex = QModelIndex(); m_currentEditingIndex = QModelIndex();
} }
void NodeTreeView::selectionChanged(const QItemSelection &selected,// 选择发生变化时的处理 void NodeTreeView::selectionChanged(const QItemSelection &selected,
const QItemSelection &deselected) const QItemSelection &deselected)
{ {
QTreeView::selectionChanged(selected, deselected); QTreeView::selectionChanged(selected, deselected);
@ -290,7 +289,7 @@ void NodeTreeView::selectionChanged(const QItemSelection &selected,// ѡ
} }
} }
void NodeTreeView::currentChanged(const QModelIndex &current, const QModelIndex &previous)// 当前索引发生变化时的处理 void NodeTreeView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
{ {
QTreeView::currentChanged(current, previous); QTreeView::currentChanged(current, previous);
auto itemType = static_cast<NodeItem::Type>(current.data(NodeItem::Roles::ItemType).toInt()); auto itemType = static_cast<NodeItem::Type>(current.data(NodeItem::Roles::ItemType).toInt());
@ -303,7 +302,7 @@ void NodeTreeView::currentChanged(const QModelIndex &current, const QModelIndex
} }
} }
void NodeTreeView::dragEnterEvent(QDragEnterEvent *event)// 拖动进入事件 void NodeTreeView::dragEnterEvent(QDragEnterEvent *event)
{ {
if (event->mimeData()->hasFormat(NOTE_MIME)) { if (event->mimeData()->hasFormat(NOTE_MIME)) {
event->acceptProposedAction(); event->acceptProposedAction();
@ -312,7 +311,7 @@ void NodeTreeView::dragEnterEvent(QDragEnterEvent *event)//
} }
} }
void NodeTreeView::dragMoveEvent(QDragMoveEvent *event)// 拖动移动事件 void NodeTreeView::dragMoveEvent(QDragMoveEvent *event)
{ {
if (event->mimeData()->hasFormat(NOTE_MIME)) { if (event->mimeData()->hasFormat(NOTE_MIME)) {
auto index = indexAt(event->pos()); auto index = indexAt(event->pos());
@ -350,13 +349,13 @@ void NodeTreeView::dragMoveEvent(QDragMoveEvent *event)//
} }
} }
void NodeTreeView::reset()// 重置视图 void NodeTreeView::reset()
{ {
closeCurrentEditor(); closeCurrentEditor();
reExpandC(); reExpandC();
} }
void NodeTreeView::dropEvent(QDropEvent *event)// 拖放事件 void NodeTreeView::dropEvent(QDropEvent *event)
{ {
if (event->mimeData()->hasFormat(NOTE_MIME)) { if (event->mimeData()->hasFormat(NOTE_MIME)) {
auto dropIndex = indexAt(event->pos()); auto dropIndex = indexAt(event->pos());
@ -386,12 +385,12 @@ void NodeTreeView::dropEvent(QDropEvent *event)//
} }
} }
void NodeTreeView::setIsEditing(bool newIsEditing)// 设置是否正在编辑 void NodeTreeView::setIsEditing(bool newIsEditing)
{ {
m_isEditing = newIsEditing; m_isEditing = newIsEditing;
} }
void NodeTreeView::onRenameFolderFinished(const QString &newName)// 重命名文件夹完成 void NodeTreeView::onRenameFolderFinished(const QString &newName)
{ {
if (m_currentEditingIndex.isValid()) { if (m_currentEditingIndex.isValid()) {
auto itemType = static_cast<NodeItem::Type>( auto itemType = static_cast<NodeItem::Type>(
@ -408,7 +407,7 @@ void NodeTreeView::onRenameFolderFinished(const QString &newName)//
}*/ }*/
} }
void NodeTreeView::onRenameTagFinished(const QString &newName)// 重命名标签完成 void NodeTreeView::onRenameTagFinished(const QString &newName)
{ {
if (m_currentEditingIndex.isValid()) { if (m_currentEditingIndex.isValid()) {
auto itemType = static_cast<NodeItem::Type>( auto itemType = static_cast<NodeItem::Type>(
@ -425,7 +424,7 @@ void NodeTreeView::onRenameTagFinished(const QString &newName)//
}*/ }*/
} }
void NodeTreeView::setCurrentIndexC(const QModelIndex &index)// 设置当前索引 void NodeTreeView::setCurrentIndexC(const QModelIndex &index)
{ {
setCurrentIndex(index); setCurrentIndex(index);
clearSelection(); clearSelection();
@ -433,18 +432,18 @@ void NodeTreeView::setCurrentIndexC(const QModelIndex &index)//
selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent); selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent);
} }
void NodeTreeView::setCurrentIndexNC(const QModelIndex &index)// 设置当前索引(不改变选择模式) void NodeTreeView::setCurrentIndexNC(const QModelIndex &index)
{ {
setCurrentIndex(index); setCurrentIndex(index);
} }
void NodeTreeView::setTheme(Theme::Value theme)// 设置主题 void NodeTreeView::setTheme(Theme::Value theme)
{ {
setCSSThemeAndUpdate(this, theme); setCSSThemeAndUpdate(this, theme);
m_theme = theme; m_theme = theme;
} }
void NodeTreeView::onCustomContextMenu(QPoint point)// 自定义上下文菜单请求 void NodeTreeView::onCustomContextMenu(QPoint point)
{ {
QModelIndex index = indexAt(point); QModelIndex index = indexAt(point);
if (index.isValid()) { if (index.isValid()) {
@ -472,7 +471,7 @@ void NodeTreeView::onCustomContextMenu(QPoint point)//
} }
} }
void NodeTreeView::setTreeSeparator(const QVector<QModelIndex> &newTreeSeparator,// 设置树分隔符 void NodeTreeView::setTreeSeparator(const QVector<QModelIndex> &newTreeSeparator,
const QModelIndex &defaultNotesIndex) const QModelIndex &defaultNotesIndex)
{ {
for (const auto &sep : qAsConst(m_treeSeparator)) { for (const auto &sep : qAsConst(m_treeSeparator)) {
@ -485,7 +484,7 @@ void NodeTreeView::setTreeSeparator(const QVector<QModelIndex> &newTreeSeparator
m_defaultNotesIndex = defaultNotesIndex; m_defaultNotesIndex = defaultNotesIndex;
} }
void NodeTreeView::mouseMoveEvent(QMouseEvent *event)// 鼠标移动事件 void NodeTreeView::mouseMoveEvent(QMouseEvent *event)
{ {
Q_D(NodeTreeView); Q_D(NodeTreeView);
QPoint topLeft; QPoint topLeft;
@ -512,7 +511,7 @@ void NodeTreeView::mouseMoveEvent(QMouseEvent *event)//
} }
} }
void NodeTreeView::mousePressEvent(QMouseEvent *event)// 鼠标按下事件 void NodeTreeView::mousePressEvent(QMouseEvent *event)
{ {
Q_D(NodeTreeView); Q_D(NodeTreeView);
d->delayedAutoScroll.stop(); d->delayedAutoScroll.stop();
@ -578,7 +577,7 @@ void NodeTreeView::mousePressEvent(QMouseEvent *event)//
updateEditingIndex(event->pos()); updateEditingIndex(event->pos());
} }
void NodeTreeView::leaveEvent(QEvent *event)// 离开事件 void NodeTreeView::leaveEvent(QEvent *event)
{ {
if (!m_isContextMenuOpened && !m_isEditing) { if (!m_isContextMenuOpened && !m_isEditing) {
closeCurrentEditor(); closeCurrentEditor();
@ -586,7 +585,7 @@ void NodeTreeView::leaveEvent(QEvent *event)//
} }
} }
void NodeTreeView::mouseReleaseEvent(QMouseEvent *event)// 鼠标释放 void NodeTreeView::mouseReleaseEvent(QMouseEvent *event)
{ {
Q_UNUSED(event); Q_UNUSED(event);
Q_D(NodeTreeView); Q_D(NodeTreeView);
@ -613,7 +612,7 @@ void NodeTreeView::mouseReleaseEvent(QMouseEvent *event)//
d->pressedIndex = QPersistentModelIndex(); d->pressedIndex = QPersistentModelIndex();
} }
void NodeTreeView::mouseDoubleClickEvent(QMouseEvent *event)// 鼠标双击 void NodeTreeView::mouseDoubleClickEvent(QMouseEvent *event)
{ {
Q_UNUSED(event); Q_UNUSED(event);
Q_D(NodeTreeView); Q_D(NodeTreeView);

@ -14,7 +14,7 @@
#include <QCursor> #include <QCursor>
#define FIRST_LINE_MAX 80 #define FIRST_LINE_MAX 80
// 构造函数,根据 Qt 版本不同,参数有所不同
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
NoteEditorLogic::NoteEditorLogic(CustomDocument *textEdit, QLabel *editorDateLabel, NoteEditorLogic::NoteEditorLogic(CustomDocument *textEdit, QLabel *editorDateLabel,
@ -40,24 +40,20 @@ NoteEditorLogic::NoteEditorLogic(CustomDocument *textEdit, QLabel *editorDateLab
m_spacerColor{ 191, 191, 191 }, m_spacerColor{ 191, 191, 191 },
m_currentAdaptableEditorPadding{ 0 }, m_currentAdaptableEditorPadding{ 0 },
m_currentMinimumEditorPadding{ 0 } m_currentMinimumEditorPadding{ 0 }
{// 连接文本编辑器的文本变化信号 {
connect(m_textEdit, &QTextEdit::textChanged, this, &NoteEditorLogic::onTextEditTextChanged); connect(m_textEdit, &QTextEdit::textChanged, this, &NoteEditorLogic::onTextEditTextChanged);
// 连接创建或更新笔记请求信号
connect(this, &NoteEditorLogic::requestCreateUpdateNote, m_dbManager, connect(this, &NoteEditorLogic::requestCreateUpdateNote, m_dbManager,
&DBManager::onCreateUpdateRequestedNoteContent, Qt::QueuedConnection); &DBManager::onCreateUpdateRequestedNoteContent, Qt::QueuedConnection);
// auto save timer 自动保存定时器 // auto save timer
m_autoSaveTimer.setSingleShot(true); m_autoSaveTimer.setSingleShot(true);
m_autoSaveTimer.setInterval(50); m_autoSaveTimer.setInterval(50);
connect(&m_autoSaveTimer, &QTimer::timeout, this, [this]() { saveNoteToDB(); }); connect(&m_autoSaveTimer, &QTimer::timeout, this, [this]() { saveNoteToDB(); });
// 初始化标签列表模型和委托
m_tagListModel = new TagListModel{ this }; m_tagListModel = new TagListModel{ this };
m_tagListModel->setTagPool(tagPool); m_tagListModel->setTagPool(tagPool);
m_tagListView->setModel(m_tagListModel); m_tagListView->setModel(m_tagListModel);
m_tagListDelegate = new TagListDelegate{ this }; m_tagListDelegate = new TagListDelegate{ this };
m_tagListView->setItemDelegate(m_tagListDelegate); m_tagListView->setItemDelegate(m_tagListDelegate);
// 连接标签池数据更新信号
connect(tagPool, &TagPool::dataUpdated, this, [this](int) { showTagListForCurrentNote(); }); connect(tagPool, &TagPool::dataUpdated, this, [this](int) { showTagListForCurrentNote(); });
// 连接滚动条位置变化信号
connect(m_textEdit->verticalScrollBar(), &QScrollBar::valueChanged, this, [this](int value) { connect(m_textEdit->verticalScrollBar(), &QScrollBar::valueChanged, this, [this](int value) {
if (m_currentNotes.size() == 1 && m_currentNotes[0].id() != SpecialNodeID::InvalidNodeId) { if (m_currentNotes.size() == 1 && m_currentNotes[0].id() != SpecialNodeID::InvalidNodeId) {
m_currentNotes[0].setScrollBarPosition(value); m_currentNotes[0].setScrollBarPosition(value);
@ -67,7 +63,6 @@ NoteEditorLogic::NoteEditorLogic(CustomDocument *textEdit, QLabel *editorDateLab
} }
}); });
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
// 连接显示看板视图信号
connect(this, &NoteEditorLogic::showKanbanView, this, [this]() { connect(this, &NoteEditorLogic::showKanbanView, this, [this]() {
if (m_kanbanWidget != nullptr) { if (m_kanbanWidget != nullptr) {
emit setVisibilityOfFrameRightNonEditor(false); emit setVisibilityOfFrameRightNonEditor(false);
@ -81,7 +76,6 @@ NoteEditorLogic::NoteEditorLogic(CustomDocument *textEdit, QLabel *editorDateLab
emit kanbanShown(); emit kanbanShown();
} }
}); });
// 连接隐藏看板视图信号
connect(this, &NoteEditorLogic::hideKanbanView, this, [this]() { connect(this, &NoteEditorLogic::hideKanbanView, this, [this]() {
if (m_kanbanWidget != nullptr) { if (m_kanbanWidget != nullptr) {
emit setVisibilityOfFrameRightNonEditor(true); emit setVisibilityOfFrameRightNonEditor(true);
@ -93,17 +87,17 @@ NoteEditorLogic::NoteEditorLogic(CustomDocument *textEdit, QLabel *editorDateLab
}); });
#endif #endif
} }
// 获取是否启用 Markdown
bool NoteEditorLogic::markdownEnabled() const bool NoteEditorLogic::markdownEnabled() const
{ {
return m_highlighter->document() != nullptr; return m_highlighter->document() != nullptr;
} }
// 设置是否启用 Markdown
void NoteEditorLogic::setMarkdownEnabled(bool enabled) void NoteEditorLogic::setMarkdownEnabled(bool enabled)
{ {
m_highlighter->setDocument(enabled ? m_textEdit->document() : nullptr); m_highlighter->setDocument(enabled ? m_textEdit->document() : nullptr);
} }
// 在编辑器中显示笔记
void NoteEditorLogic::showNotesInEditor(const QVector<NodeData> &notes) void NoteEditorLogic::showNotesInEditor(const QVector<NodeData> &notes)
{ {
auto currentId = currentEditingNoteId(); auto currentId = currentEditingNoteId();
@ -206,7 +200,7 @@ void NoteEditorLogic::showNotesInEditor(const QVector<NodeData> &notes)
highlightSearch(); highlightSearch();
} }
} }
// 文本编辑器内容变化时的处理
void NoteEditorLogic::onTextEditTextChanged() void NoteEditorLogic::onTextEditTextChanged()
{ {
if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId) { if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId) {
@ -240,7 +234,7 @@ void NoteEditorLogic::onTextEditTextChanged()
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
// 重新排列文本编辑器中的任务
void NoteEditorLogic::rearrangeTasksInTextEditor(int startLinePosition, int endLinePosition, void NoteEditorLogic::rearrangeTasksInTextEditor(int startLinePosition, int endLinePosition,
int newLinePosition) int newLinePosition)
{ {
@ -275,7 +269,7 @@ void NoteEditorLogic::rearrangeTasksInTextEditor(int startLinePosition, int endL
checkForTasksInEditor(); checkForTasksInEditor();
} }
// 重新排列文本编辑器中的列
void NoteEditorLogic::rearrangeColumnsInTextEditor(int startLinePosition, int endLinePosition, void NoteEditorLogic::rearrangeColumnsInTextEditor(int startLinePosition, int endLinePosition,
int newLinePosition) int newLinePosition)
{ {
@ -311,7 +305,7 @@ void NoteEditorLogic::rearrangeColumnsInTextEditor(int startLinePosition, int en
checkForTasksInEditor(); checkForTasksInEditor();
} }
// 获取某行的任务数据
QMap<QString, int> NoteEditorLogic::getTaskDataInLine(const QString &line) QMap<QString, int> NoteEditorLogic::getTaskDataInLine(const QString &line)
{ {
QStringList taskExpressions = { "- [ ]", "- [x]", "* [ ]", "* [x]", "- [X]", "* [X]" }; QStringList taskExpressions = { "- [ ]", "- [x]", "* [ ]", "* [x]", "- [X]", "* [X]" };
@ -331,7 +325,7 @@ QMap<QString, int> NoteEditorLogic::getTaskDataInLine(const QString &line)
return taskMatchLineData; return taskMatchLineData;
} }
// 检查某行的任务
void NoteEditorLogic::checkTaskInLine(int lineNumber) void NoteEditorLogic::checkTaskInLine(int lineNumber)
{ {
QTextDocument *document = m_textEdit->document(); QTextDocument *document = m_textEdit->document();
@ -349,7 +343,7 @@ void NoteEditorLogic::checkTaskInLine(int lineNumber)
cursor.insertText("x"); cursor.insertText("x");
} }
} }
// 取消检查某行的任务
void NoteEditorLogic::uncheckTaskInLine(int lineNumber) void NoteEditorLogic::uncheckTaskInLine(int lineNumber)
{ {
QTextDocument *document = m_textEdit->document(); QTextDocument *document = m_textEdit->document();
@ -367,7 +361,7 @@ void NoteEditorLogic::uncheckTaskInLine(int lineNumber)
cursor.insertText(" "); cursor.insertText(" ");
} }
} }
// 替换某行之间的文本
void NoteEditorLogic::replaceTextBetweenLines(int startLinePosition, int endLinePosition, void NoteEditorLogic::replaceTextBetweenLines(int startLinePosition, int endLinePosition,
QString &newText) QString &newText)
{ {
@ -379,7 +373,7 @@ void NoteEditorLogic::replaceTextBetweenLines(int startLinePosition, int endLine
cursor.removeSelectedText(); cursor.removeSelectedText();
cursor.insertText(newText); cursor.insertText(newText);
} }
// 更新任务文本
void NoteEditorLogic::updateTaskText(int startLinePosition, int endLinePosition, void NoteEditorLogic::updateTaskText(int startLinePosition, int endLinePosition,
const QString &newText) const QString &newText)
{ {
@ -425,7 +419,7 @@ void NoteEditorLogic::updateTaskText(int startLinePosition, int endLinePosition,
checkForTasksInEditor(); checkForTasksInEditor();
} }
} }
// 添加新任务
void NoteEditorLogic::addNewTask(int startLinePosition, const QString newTaskText) void NoteEditorLogic::addNewTask(int startLinePosition, const QString newTaskText)
{ {
QString newText = "\n- [ ] " + newTaskText; QString newText = "\n- [ ] " + newTaskText;
@ -441,7 +435,7 @@ void NoteEditorLogic::addNewTask(int startLinePosition, const QString newTaskTex
checkForTasksInEditor(); checkForTasksInEditor();
} }
// 移除某行之间的文本
void NoteEditorLogic::removeTextBetweenLines(int startLinePosition, int endLinePosition) void NoteEditorLogic::removeTextBetweenLines(int startLinePosition, int endLinePosition)
{ {
if (startLinePosition < 0 || endLinePosition < startLinePosition) { if (startLinePosition < 0 || endLinePosition < startLinePosition) {
@ -459,13 +453,13 @@ void NoteEditorLogic::removeTextBetweenLines(int startLinePosition, int endLineP
} }
cursor.removeSelectedText(); cursor.removeSelectedText();
} }
// 移除任务
void NoteEditorLogic::removeTask(int startLinePosition, int endLinePosition) void NoteEditorLogic::removeTask(int startLinePosition, int endLinePosition)
{ {
removeTextBetweenLines(startLinePosition, endLinePosition); removeTextBetweenLines(startLinePosition, endLinePosition);
checkForTasksInEditor(); checkForTasksInEditor();
} }
// 添加新列
void NoteEditorLogic::addNewColumn(int startLinePosition, const QString &columnTitle) void NoteEditorLogic::addNewColumn(int startLinePosition, const QString &columnTitle)
{ {
if (startLinePosition < 0) if (startLinePosition < 0)
@ -489,7 +483,7 @@ void NoteEditorLogic::addNewColumn(int startLinePosition, const QString &columnT
checkForTasksInEditor(); checkForTasksInEditor();
} }
// 移除列
void NoteEditorLogic::removeColumn(int startLinePosition, int endLinePosition) void NoteEditorLogic::removeColumn(int startLinePosition, int endLinePosition)
{ {
removeTextBetweenLines(startLinePosition, endLinePosition); removeTextBetweenLines(startLinePosition, endLinePosition);
@ -506,7 +500,7 @@ void NoteEditorLogic::removeColumn(int startLinePosition, int endLinePosition)
checkForTasksInEditor(); checkForTasksInEditor();
} }
// 更新列标题
void NoteEditorLogic::updateColumnTitle(int lineNumber, const QString &newText) void NoteEditorLogic::updateColumnTitle(int lineNumber, const QString &newText)
{ {
QTextDocument *document = m_textEdit->document(); QTextDocument *document = m_textEdit->document();
@ -537,7 +531,7 @@ void NoteEditorLogic::updateColumnTitle(int lineNumber, const QString &newText)
} }
} }
} }
// 向文本编辑器添加无标题列
void NoteEditorLogic::addUntitledColumnToTextEditor(int startLinePosition) void NoteEditorLogic::addUntitledColumnToTextEditor(int startLinePosition)
{ {
QString columnTitle = "# Untitled\n\n"; QString columnTitle = "# Untitled\n\n";
@ -550,7 +544,7 @@ void NoteEditorLogic::addUntitledColumnToTextEditor(int startLinePosition)
cursor.insertText(columnTitle); cursor.insertText(columnTitle);
} }
} }
// 向 JSON 数据添加新列
void NoteEditorLogic::appendNewColumn(QJsonArray &data, QJsonObject &currentColumn, void NoteEditorLogic::appendNewColumn(QJsonArray &data, QJsonObject &currentColumn,
QString &currentTitle, QJsonArray &tasks) QString &currentTitle, QJsonArray &tasks)
{ {
@ -577,7 +571,7 @@ void NoteEditorLogic::appendNewColumn(QJsonArray &data, QJsonObject &currentColu
// {"checked":false,"text":"todo 2", "taskStartine": 4, "taskEndLine": 4}}] // {"checked":false,"text":"todo 2", "taskStartine": 4, "taskEndLine": 4}}]
// }, // },
// ]) // ])
bool NoteEditorLogic::checkForTasksInEditor()// 检查编辑器中的任务 bool NoteEditorLogic::checkForTasksInEditor()
{ {
QStringList lines = m_textEdit->toPlainText().split("\n"); QStringList lines = m_textEdit->toPlainText().split("\n");
QJsonArray data; QJsonArray data;
@ -663,13 +657,13 @@ bool NoteEditorLogic::checkForTasksInEditor()//
return false; return false;
} }
#endif #endif
// 将字符串转换为 QDateTime
QDateTime NoteEditorLogic::getQDateTime(const QString &date) QDateTime NoteEditorLogic::getQDateTime(const QString &date)
{ {
QDateTime dateTime = QDateTime::fromString(date, Qt::ISODate); QDateTime dateTime = QDateTime::fromString(date, Qt::ISODate);
return dateTime; return dateTime;
} }
// 显示当前笔记的标签列表
void NoteEditorLogic::showTagListForCurrentNote() void NoteEditorLogic::showTagListForCurrentNote()
{ {
if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId) { if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId) {
@ -684,7 +678,7 @@ void NoteEditorLogic::showTagListForCurrentNote()
m_tagListView->setVisible(false); m_tagListView->setVisible(false);
} }
bool NoteEditorLogic::isInEditMode() const// 获取是否处于编辑模式 bool NoteEditorLogic::isInEditMode() const
{ {
if (m_currentNotes.size() == 1) { if (m_currentNotes.size() == 1) {
return true; return true;
@ -692,26 +686,26 @@ bool NoteEditorLogic::isInEditMode() const//
return false; return false;
} }
int NoteEditorLogic::currentMinimumEditorPadding() const// 获取当前最小编辑器填充 int NoteEditorLogic::currentMinimumEditorPadding() const
{ {
return m_currentMinimumEditorPadding; return m_currentMinimumEditorPadding;
} }
// 设置当前最小编辑器填充
void NoteEditorLogic::setCurrentMinimumEditorPadding(int newCurrentMinimumEditorPadding) void NoteEditorLogic::setCurrentMinimumEditorPadding(int newCurrentMinimumEditorPadding)
{ {
m_currentMinimumEditorPadding = newCurrentMinimumEditorPadding; m_currentMinimumEditorPadding = newCurrentMinimumEditorPadding;
} }
// 获取当前可适应的编辑器填充
int NoteEditorLogic::currentAdaptableEditorPadding() const int NoteEditorLogic::currentAdaptableEditorPadding() const
{ {
return m_currentAdaptableEditorPadding; return m_currentAdaptableEditorPadding;
} }
// 设置当前可适应的编辑器填充
void NoteEditorLogic::setCurrentAdaptableEditorPadding(int newCurrentAdaptableEditorPadding) void NoteEditorLogic::setCurrentAdaptableEditorPadding(int newCurrentAdaptableEditorPadding)
{ {
m_currentAdaptableEditorPadding = newCurrentAdaptableEditorPadding; m_currentAdaptableEditorPadding = newCurrentAdaptableEditorPadding;
} }
// 获取当前编辑的笔记 ID
int NoteEditorLogic::currentEditingNoteId() const int NoteEditorLogic::currentEditingNoteId() const
{ {
if (isInEditMode()) { if (isInEditMode()) {
@ -719,7 +713,7 @@ int NoteEditorLogic::currentEditingNoteId() const
} }
return SpecialNodeID::InvalidNodeId; return SpecialNodeID::InvalidNodeId;
} }
// 将笔记保存到数据库
void NoteEditorLogic::saveNoteToDB() void NoteEditorLogic::saveNoteToDB()
{ {
if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId && m_isContentModified if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId && m_isContentModified
@ -728,7 +722,7 @@ void NoteEditorLogic::saveNoteToDB()
m_isContentModified = false; m_isContentModified = false;
} }
} }
// 关闭编辑器
void NoteEditorLogic::closeEditor() void NoteEditorLogic::closeEditor()
{ {
if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId) { if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId) {
@ -743,7 +737,7 @@ void NoteEditorLogic::closeEditor()
m_textEdit->blockSignals(false); m_textEdit->blockSignals(false);
m_tagListModel->setModelData({}); m_tagListModel->setModelData({});
} }
// 笔记标签列表发生变化时的处理
void NoteEditorLogic::onNoteTagListChanged(int noteId, const QSet<int> &tagIds) void NoteEditorLogic::onNoteTagListChanged(int noteId, const QSet<int> &tagIds)
{ {
if (currentEditingNoteId() == noteId) { if (currentEditingNoteId() == noteId) {
@ -751,7 +745,7 @@ void NoteEditorLogic::onNoteTagListChanged(int noteId, const QSet<int> &tagIds)
showTagListForCurrentNote(); showTagListForCurrentNote();
} }
} }
// 删除当前笔记
void NoteEditorLogic::deleteCurrentNote() void NoteEditorLogic::deleteCurrentNote()
{ {
if (isTempNote()) { if (isTempNote()) {
@ -783,7 +777,7 @@ void NoteEditorLogic::deleteCurrentNote()
* \param str * \param str
* \return * \return
*/ */
QString NoteEditorLogic::getFirstLine(const QString &str)// 获取文本的首行 QString NoteEditorLogic::getFirstLine(const QString &str)
{ {
QString text = str.trimmed(); QString text = str.trimmed();
if (text.isEmpty()) { if (text.isEmpty()) {
@ -793,7 +787,7 @@ QString NoteEditorLogic::getFirstLine(const QString &str)//
return ts.readLine(FIRST_LINE_MAX); return ts.readLine(FIRST_LINE_MAX);
} }
QString NoteEditorLogic::getSecondLine(const QString &str)// 获取文本的第二行 QString NoteEditorLogic::getSecondLine(const QString &str)
{ {
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
auto sl = str.split("\n", QString::SkipEmptyParts); auto sl = str.split("\n", QString::SkipEmptyParts);
@ -815,7 +809,7 @@ QString NoteEditorLogic::getSecondLine(const QString &str)//
QTextStream ts(&text); QTextStream ts(&text);
return ts.readLine(FIRST_LINE_MAX); return ts.readLine(FIRST_LINE_MAX);
} }
// 设置主题
void NoteEditorLogic::setTheme(Theme::Value theme, QColor textColor, qreal fontSize) void NoteEditorLogic::setTheme(Theme::Value theme, QColor textColor, qreal fontSize)
{ {
m_tagListDelegate->setTheme(theme); m_tagListDelegate->setTheme(theme);
@ -845,7 +839,7 @@ void NoteEditorLogic::setTheme(Theme::Value theme, QColor textColor, qreal fontS
m_textEdit->verticalScrollBar()->setValue(verticalScrollBarValueToRestore); m_textEdit->verticalScrollBar()->setValue(verticalScrollBarValueToRestore);
} }
} }
// 获取笔记编辑日期
QString NoteEditorLogic::getNoteDateEditor(const QString &dateEdited) QString NoteEditorLogic::getNoteDateEditor(const QString &dateEdited)
{ {
QDateTime dateTimeEdited(getQDateTime(dateEdited)); QDateTime dateTimeEdited(getQDateTime(dateEdited));
@ -853,7 +847,7 @@ QString NoteEditorLogic::getNoteDateEditor(const QString &dateEdited)
return usLocale.toString(dateTimeEdited, QStringLiteral("MMMM d, yyyy, h:mm A")); return usLocale.toString(dateTimeEdited, QStringLiteral("MMMM d, yyyy, h:mm A"));
} }
// 高亮搜索结果
void NoteEditorLogic::highlightSearch() const void NoteEditorLogic::highlightSearch() const
{ {
QString searchString = m_searchEdit->text(); QString searchString = m_searchEdit->text();
@ -875,7 +869,7 @@ void NoteEditorLogic::highlightSearch() const
m_textEdit->setExtraSelections(extraSelections); m_textEdit->setExtraSelections(extraSelections);
} }
} }
// 获取是否是临时笔记
bool NoteEditorLogic::isTempNote() const bool NoteEditorLogic::isTempNote() const
{ {
if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId && m_currentNotes[0].isTempNote()) { if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId && m_currentNotes[0].isTempNote()) {

@ -26,10 +26,10 @@ class TagListModel;
class TagPool; class TagPool;
class TagListDelegate; class TagListDelegate;
class QListWidget; class QListWidget;
class NoteEditorLogic : public QObject// NoteEditorLogic 类,负责笔记编辑逻辑 class NoteEditorLogic : public QObject
{ {
Q_OBJECT Q_OBJECT
public:// NoteEditorLogic 类,负责笔记编辑逻辑 public:
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
explicit NoteEditorLogic(CustomDocument *textEdit, QLabel *editorDateLabel, explicit NoteEditorLogic(CustomDocument *textEdit, QLabel *editorDateLabel,
QLineEdit *searchEdit, QWidget *kanbanWidget, TagListView *tagListView, QLineEdit *searchEdit, QWidget *kanbanWidget, TagListView *tagListView,
@ -41,150 +41,96 @@ public:// NoteEditorLogic
DBManager *dbManager, QObject *parent = nullptr); DBManager *dbManager, QObject *parent = nullptr);
#endif #endif
// 获取是否启用 Markdown
bool markdownEnabled() const; bool markdownEnabled() const;
// 设置是否启用 Markdown
void setMarkdownEnabled(bool enabled); void setMarkdownEnabled(bool enabled);
// 获取笔记编辑日期
static QString getNoteDateEditor(const QString &dateEdited); static QString getNoteDateEditor(const QString &dateEdited);
// 高亮搜索结果
void highlightSearch() const; void highlightSearch() const;
// 获取是否是临时笔记
bool isTempNote() const; bool isTempNote() const;
// 将笔记保存到数据库
void saveNoteToDB(); void saveNoteToDB();
// 获取当前编辑的笔记 ID
int currentEditingNoteId() const; int currentEditingNoteId() const;
// 删除当前笔记
void deleteCurrentNote(); void deleteCurrentNote();
// 获取文本的首行
static QString getFirstLine(const QString &str); static QString getFirstLine(const QString &str);
// 获取文本的第二行
static QString getSecondLine(const QString &str); static QString getSecondLine(const QString &str);
// 设置主题
void setTheme(Theme::Value theme, QColor textColor, qreal fontSize); void setTheme(Theme::Value theme, QColor textColor, qreal fontSize);
// 获取当前可适应的编辑器填充
int currentAdaptableEditorPadding() const; int currentAdaptableEditorPadding() const;
// 设置当前可适应的编辑器填充
void setCurrentAdaptableEditorPadding(int newCurrentAdaptableEditorPadding); void setCurrentAdaptableEditorPadding(int newCurrentAdaptableEditorPadding);
// 获取当前最小编辑器填充
int currentMinimumEditorPadding() const; int currentMinimumEditorPadding() const;
// 设置当前最小编辑器填充
void setCurrentMinimumEditorPadding(int newCurrentMinimumEditorPadding); void setCurrentMinimumEditorPadding(int newCurrentMinimumEditorPadding);
public slots: public slots:
// 在编辑器中显示笔记
void showNotesInEditor(const QVector<NodeData> &notes); void showNotesInEditor(const QVector<NodeData> &notes);
// 文本编辑器内容变化时的处理
void onTextEditTextChanged(); void onTextEditTextChanged();
// 关闭编辑器
void closeEditor(); void closeEditor();
// 笔记标签列表发生变化时的处理
void onNoteTagListChanged(int noteId, const QSet<int> &tagIds); void onNoteTagListChanged(int noteId, const QSet<int> &tagIds);
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
// 检查编辑器中的任务
bool checkForTasksInEditor(); bool checkForTasksInEditor();
// 重新排列文本编辑器中的任务
void rearrangeTasksInTextEditor(int startLinePosition, int endLinePosition, void rearrangeTasksInTextEditor(int startLinePosition, int endLinePosition,
int newLinePosition); int newLinePosition);
// 重新排列文本编辑器中的列
void rearrangeColumnsInTextEditor(int startLinePosition, int endLinePosition, void rearrangeColumnsInTextEditor(int startLinePosition, int endLinePosition,
int newLinePosition); int newLinePosition);
// 检查某行的任务
void checkTaskInLine(int lineNumber); void checkTaskInLine(int lineNumber);
// 取消检查某行的任务
void uncheckTaskInLine(int lineNumber); void uncheckTaskInLine(int lineNumber);
// 更新任务文本
void updateTaskText(int startLinePosition, int endLinePosition, const QString &newText); void updateTaskText(int startLinePosition, int endLinePosition, const QString &newText);
// 添加新任务
void addNewTask(int startLinePosition, const QString newTaskText); void addNewTask(int startLinePosition, const QString newTaskText);
// 移除任务
void removeTask(int startLinePosition, int endLinePosition); void removeTask(int startLinePosition, int endLinePosition);
// 添加新列
void addNewColumn(int startLinePosition, const QString &columnTitle); void addNewColumn(int startLinePosition, const QString &columnTitle);
// 移除列
void removeColumn(int startLinePosition, int endLinePosition); void removeColumn(int startLinePosition, int endLinePosition);
// 更新列标题
void updateColumnTitle(int lineNumber, const QString &newText); void updateColumnTitle(int lineNumber, const QString &newText);
#endif #endif
signals: signals:
// 请求创建或更新笔记
void requestCreateUpdateNote(const NodeData &note); void requestCreateUpdateNote(const NodeData &note);
// 笔记编辑关闭
void noteEditClosed(const NodeData &note, bool selectNext); void noteEditClosed(const NodeData &note, bool selectNext);
// 设置右侧框架部件的可见性
void setVisibilityOfFrameRightWidgets(bool); void setVisibilityOfFrameRightWidgets(bool);
// 设置右侧非编辑器部件的可见性
void setVisibilityOfFrameRightNonEditor(bool); void setVisibilityOfFrameRightNonEditor(bool);
// 将笔记移动到列表视图顶部
void moveNoteToListViewTop(const NodeData &note); void moveNoteToListViewTop(const NodeData &note);
// 更新列表中的笔记数据
void updateNoteDataInList(const NodeData &note); void updateNoteDataInList(const NodeData &note);
// 请求删除笔记
void deleteNoteRequested(const NodeData &note); void deleteNoteRequested(const NodeData &note);
// 显示看板视图
void showKanbanView(); void showKanbanView();
// 隐藏看板视图
void hideKanbanView(); void hideKanbanView();
// 文本显示完成
void textShown(); void textShown();
// 看板显示完成
void kanbanShown(); void kanbanShown();
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
// 编辑器中找到任务
void tasksFoundInEditor(QVariant data); void tasksFoundInEditor(QVariant data);
// 清空看板模型
void clearKanbanModel(); void clearKanbanModel();
// 重置看板设置
void resetKanbanSettings(); void resetKanbanSettings();
// 检查是否选择了多个笔记
void checkMultipleNotesSelected(QVariant isMultipleNotesSelected); void checkMultipleNotesSelected(QVariant isMultipleNotesSelected);
#endif #endif
private: private:
// 将字符串转换为 QDateTime
static QDateTime getQDateTime(const QString &date); static QDateTime getQDateTime(const QString &date);
// 显示当前笔记的标签列表
void showTagListForCurrentNote(); void showTagListForCurrentNote();
// 获取是否处于编辑模式
bool isInEditMode() const; bool isInEditMode() const;
// 移动文本到新的行位置
QString moveTextToNewLinePosition(const QString &inputText, int startLinePosition, QString moveTextToNewLinePosition(const QString &inputText, int startLinePosition,
int endLinePosition, int newLinePosition, int endLinePosition, int newLinePosition,
bool isColumns = false); bool isColumns = false);
// 获取某行的任务数据
QMap<QString, int> getTaskDataInLine(const QString &line); QMap<QString, int> getTaskDataInLine(const QString &line);
// 替换某行之间的文本
void replaceTextBetweenLines(int startLinePosition, int endLinePosition, QString &newText); void replaceTextBetweenLines(int startLinePosition, int endLinePosition, QString &newText);
// 移除某行之间的文本
void removeTextBetweenLines(int startLinePosition, int endLinePosition); void removeTextBetweenLines(int startLinePosition, int endLinePosition);
// 添加新列到 JSON 数据
void appendNewColumn(QJsonArray &data, QJsonObject &currentColumn, QString &currentTitle, void appendNewColumn(QJsonArray &data, QJsonObject &currentColumn, QString &currentTitle,
QJsonArray &tasks); QJsonArray &tasks);
// 向文本编辑器添加无标题列
void addUntitledColumnToTextEditor(int startLinePosition); void addUntitledColumnToTextEditor(int startLinePosition);
private: private:
CustomDocument *m_textEdit; // 文本编辑器 CustomDocument *m_textEdit;
CustomMarkdownHighlighter *m_highlighter; // Markdown 高亮器 CustomMarkdownHighlighter *m_highlighter;
QLabel *m_editorDateLabel; // 编辑器日期标签 QLabel *m_editorDateLabel;
QLineEdit *m_searchEdit; // 搜索框 QLineEdit *m_searchEdit;
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
QWidget *m_kanbanWidget; // 看板部件 QWidget *m_kanbanWidget;
#endif #endif
TagListView *m_tagListView; // 标签列表视图 TagListView *m_tagListView;
DBManager *m_dbManager; // 数据库管理器 DBManager *m_dbManager;
QVector<NodeData> m_currentNotes; // 当前笔记数据 QVector<NodeData> m_currentNotes;
bool m_isContentModified; // 内容是否被修改 bool m_isContentModified;
QTimer m_autoSaveTimer; // 自动保存定时器 QTimer m_autoSaveTimer;
TagListDelegate *m_tagListDelegate; // 标签列表委托 TagListDelegate *m_tagListDelegate;
TagListModel *m_tagListModel; // 标签列表模型 TagListModel *m_tagListModel;
QColor m_spacerColor; // 分隔符颜色 QColor m_spacerColor;
int m_currentAdaptableEditorPadding; // 当前可适应的编辑器填充 int m_currentAdaptableEditorPadding;
int m_currentMinimumEditorPadding; // 当前最小编辑器填充 int m_currentMinimumEditorPadding;
}; };
#endif // NOTEEDITORLOGIC_H #endif // NOTEEDITORLOGIC_H

@ -1,22 +1,22 @@
#include "notelistdelegateeditor.h" // 包含NoteListDelegateEditor类的声明 #include "notelistdelegateeditor.h"
#include <QPainter> // 包含QPainter类用于绘图 #include <QPainter>
#include <QEvent> // 包含QEvent类用于处理事件 #include <QEvent>
#include <QDebug> // 包含QDebug类用于调试输出 #include <QDebug>
#include <QApplication> // 包含QApplication类访问应用程序对象 #include <QApplication>
#include <QtMath> // 包含数学相关函数 #include <QtMath>
#include <QPainterPath> // 包含QPainterPath类用于绘制路径 #include <QPainterPath>
#include <QScrollBar> // 包含QScrollBar类用于滚动条 #include <QScrollBar>
#include <QDragEnterEvent> // 包含拖拽进入事件 #include <QDragEnterEvent>
#include <QMimeData> // 包含MIME数据类 #include <QMimeData>
#include "notelistmodel.h" // 包含笔记列表模型 #include "notelistmodel.h"
#include "noteeditorlogic.h" // 包含笔记编辑逻辑 #include "noteeditorlogic.h"
#include "tagpool.h" // 包含标签池 #include "tagpool.h"
#include "nodepath.h" // 包含节点路径 #include "nodepath.h"
#include "notelistdelegate.h" // 包含笔记列表委托 #include "notelistdelegate.h"
#include "taglistview.h" // 包含标签列表视图 #include "taglistview.h"
#include "taglistmodel.h" // 包含标签列表模型 #include "taglistmodel.h"
#include "taglistdelegate.h" // 包含标签列表委托 #include "taglistdelegate.h"
#include "fontloader.h" // 包含字体加载器 #include "fontloader.h"
NoteListDelegateEditor::NoteListDelegateEditor(const NoteListDelegate *delegate, NoteListView *view, NoteListDelegateEditor::NoteListDelegateEditor(const NoteListDelegate *delegate, NoteListView *view,
const QStyleOptionViewItem &option, const QStyleOptionViewItem &option,
@ -144,67 +144,48 @@ NoteListDelegateEditor::~NoteListDelegateEditor()
void NoteListDelegateEditor::paintBackground(QPainter *painter, const QStyleOptionViewItem &option, void NoteListDelegateEditor::paintBackground(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const const QModelIndex &index) const
{ {
// 获取当前控件的尺寸
auto bufferSize = rect().size(); auto bufferSize = rect().size();
// 创建一个与控件尺寸相同的QPixmap对象初始填充为透明
QPixmap buffer{ bufferSize }; QPixmap buffer{ bufferSize };
buffer.fill(Qt::transparent); buffer.fill(Qt::transparent);
// 创建一个用于绘制的QPainter对象
QPainter bufferPainter{ &buffer }; QPainter bufferPainter{ &buffer };
// 设置抗锯齿和平滑转换的渲染提示
bufferPainter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); bufferPainter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
// 获取绘制区域的矩形
QRect bufferRect = buffer.rect(); QRect bufferRect = buffer.rect();
// 尝试将视图模型转换为NoteListModel类型
auto model = dynamic_cast<NoteListModel *>(m_view->model()); auto model = dynamic_cast<NoteListModel *>(m_view->model());
// 如果模型存在,并且存在被固定的笔记,并且当前索引是第一个被固定的或未被固定的笔记
if (model && model->hasPinnedNote() if (model && model->hasPinnedNote()
&& &(model->isFirstPinnedNote(index) || model->isFirstUnpinnedNote(index))) { && (model->isFirstPinnedNote(index) || model->isFirstUnpinnedNote(index))) {
// 如果存在被固定的笔记,并且未被折叠,并且当前索引是第一个未被固定的笔记
int fifthYOffset = 0; int fifthYOffset = 0;
if (model && model->hasPinnedNote() && !m_view->isPinnedNotesCollapsed() if (model && model->hasPinnedNote() && !m_view->isPinnedNotesCollapsed()
&& model->isFirstUnpinnedNote(index)) { && model->isFirstUnpinnedNote(index)) {
fifthYOffset = NoteListConstant::lastPinnedToUnpinnedHeader; fifthYOffset = NoteListConstant::lastPinnedToUnpinnedHeader;
} }
// 调整bufferRect的Y坐标以添加额外的偏移量
bufferRect.setY(bufferRect.y() + 25 + fifthYOffset); bufferRect.setY(bufferRect.y() + 25 + fifthYOffset);
} }
// 获取笔记是否被固定的数据
auto isPinned = index.data(NoteListModel::NoteIsPinned).toBool(); auto isPinned = index.data(NoteListModel::NoteIsPinned).toBool();
// 如果当前索引被选中
if (m_view->selectionModel()->isSelected(index)) { if (m_view->selectionModel()->isSelected(index)) {
// 根据应用程序的状态设置背景颜色
if (qApp->applicationState() == Qt::ApplicationActive) { if (qApp->applicationState() == Qt::ApplicationActive) {
// 如果控件处于活动状态,设置为活动颜色
if (m_isActive) { if (m_isActive) {
bufferPainter.fillRect(bufferRect, QBrush(m_ActiveColor)); bufferPainter.fillRect(bufferRect, QBrush(m_ActiveColor));
m_tagListView->setBackground(m_ActiveColor); m_tagListView->setBackground(m_ActiveColor);
} else { } else {
// 否则设置为非活动颜色
bufferPainter.fillRect(bufferRect, QBrush(m_notActiveColor)); bufferPainter.fillRect(bufferRect, QBrush(m_notActiveColor));
m_tagListView->setBackground(m_notActiveColor); m_tagListView->setBackground(m_notActiveColor);
} }
} else if (qApp->applicationState() == Qt::ApplicationInactive) { } else if (qApp->applicationState() == Qt::ApplicationInactive) {
// 如果应用程序处于非活动状态,设置为非活动颜色
bufferPainter.fillRect(bufferRect, QBrush(m_applicationInactiveColor)); bufferPainter.fillRect(bufferRect, QBrush(m_applicationInactiveColor));
m_tagListView->setBackground(m_applicationInactiveColor); m_tagListView->setBackground(m_applicationInactiveColor);
} }
} else if (underMouseC()) { // 如果鼠标在控件下 } else if (underMouseC()) {
// 如果视图正在拖拽
if (m_view->isDragging()) { if (m_view->isDragging()) {
// 如果笔记被固定,绘制特定颜色的矩形
if (isPinned) { if (isPinned) {
auto rect = bufferRect; auto rect = bufferRect;
rect.setTop(rect.bottom() - 5); rect.setTop(rect.bottom() - 5);
bufferPainter.fillRect(rect, QBrush("#d6d5d5")); bufferPainter.fillRect(rect, QBrush("#d6d5d5"));
} }
} else { } else {
// 否则,绘制悬浮颜色的矩形
bufferPainter.fillRect(bufferRect, QBrush(m_hoverColor)); bufferPainter.fillRect(bufferRect, QBrush(m_hoverColor));
m_tagListView->setBackground(m_hoverColor); m_tagListView->setBackground(m_hoverColor);
} }
} else { } else {
// 如果被固定的笔记被折叠,并且当前笔记未被固定
if (m_view->isPinnedNotesCollapsed() && !isPinned) { if (m_view->isPinnedNotesCollapsed() && !isPinned) {
bufferPainter.fillRect(bufferRect, QBrush(m_defaultColor)); bufferPainter.fillRect(bufferRect, QBrush(m_defaultColor));
m_tagListView->setBackground(m_defaultColor); m_tagListView->setBackground(m_defaultColor);
@ -213,8 +194,6 @@ void NoteListDelegateEditor::paintBackground(QPainter *painter, const QStyleOpti
m_tagListView->setBackground(m_defaultColor); m_tagListView->setBackground(m_defaultColor);
} }
} }
// 如果视图正在拖拽,并且当前笔记未被固定,并且拖拽没有在被固定的笔记区域内发生
if (m_view->isDragging() && !isPinned && !m_view->isDraggingInsidePinned()) { if (m_view->isDragging() && !isPinned && !m_view->isDraggingInsidePinned()) {
if (model && model->isFirstUnpinnedNote(index) if (model && model->isFirstUnpinnedNote(index)
&& (index.row() == (model->rowCount() - 1))) { && (index.row() == (model->rowCount() - 1))) {
@ -263,9 +242,7 @@ void NoteListDelegateEditor::paintBackground(QPainter *painter, const QStyleOpti
paintSeparator(&bufferPainter, option, index); paintSeparator(&bufferPainter, option, index);
} }
// 获取当前行的高度
auto rowHeight = rect().height(); auto rowHeight = rect().height();
// 将绘制好的背景绘制到painter上位置是当前控件的矩形区域
painter->drawPixmap(rect(), buffer, painter->drawPixmap(rect(), buffer,
QRect{ 0, bufferSize.height() - rowHeight, rect().width(), rowHeight }); QRect{ 0, bufferSize.height() - rowHeight, rect().width(), rowHeight });
} }

@ -1,134 +1,111 @@
#ifndef NOTELISTMODEL_H // 头文件保护,防止重复包含 #ifndef NOTELISTDELEGATEEDITOR_H
#define NOTELISTMODEL_H #define NOTELISTDELEGATEEDITOR_H
#include <QAbstractListModel> // 包含QAbstractListModel类 #include <QWidget>
#include "nodedata.h" // 包含NodeData类用于存储笔记数据 #include "notelistview.h"
#include "dbmanager.h" // 包含数据库管理器 #include <QTimeLine>
#include "editorsettingsoptions.h"
class NoteListModel : public QAbstractListModel // 继承自QAbstractListModel class NoteListDelegate;
class TagListModel;
class TagListView;
class TagListDelegate;
class NoteListModel;
struct NoteListConstant
{ {
Q_OBJECT // 宏用于支持Qt的信号和槽机制 static constexpr int leftOffsetX = 20;
static constexpr int topOffsetY = 10; // space on top of title
public : static constexpr int titleDateSpace = 2; // space between title and date
// 定义笔记模型的角色枚举 static constexpr int dateDescSpace = 5; // space between date and description
enum NoteRoles { static constexpr int descFolderSpace = 14; // space between description and folder name
NoteID = Qt::UserRole + 1, // 笔记ID角色 static constexpr int lastElSepSpace = 12; // space between the last element and the separator
NoteFullTitle, // 笔记完整标题角色 static constexpr int nextNoteOffset =
NoteCreationDateTime, // 笔记创建日期时间角色 0; // space between the separator and the next note underneath it
NoteLastModificationDateTime, // 笔记最后修改日期时间角色 static constexpr int pinnedHeaderToNoteSpace =
NoteDeletionDateTime, // 笔记删除日期时间角色 0; // space between Pinned label to the pinned list
NoteContent, // 笔记内容角色 static constexpr int unpinnedHeaderToNoteSpace =
NoteScrollbarPos, // 笔记滚动条位置角色 0; // space between Notes label and the normal notes list
NoteTagsList, // 笔记标签列表角色 static constexpr int lastPinnedToUnpinnedHeader =
NoteIsTemp, // 笔记是否为临时笔记角色 10; // space between the last pinned note to Notes label
NoteParentName, // 笔记父级名称角色
NoteTagListScrollbarPos, // 笔记标签列表滚动条位置角色
NoteIsPinned // 笔记是否被钉住角色
}; };
// 构造函数,可以指定一个父对象 class NoteListDelegateEditor : public QWidget
explicit NoteListModel(QObject *parent = nullptr); {
// 析构函数 Q_OBJECT
~NoteListModel(); public:
explicit NoteListDelegateEditor(const NoteListDelegate *delegate, NoteListView *view,
// 添加笔记,并返回新笔记的模型索引 const QStyleOptionViewItem &option, const QModelIndex &index,
QModelIndex addNote(const NodeData &note); TagPool *tagPool, QWidget *parent = nullptr);
// 在指定行插入笔记,并返回新笔记的模型索引 ~NoteListDelegateEditor();
QModelIndex insertNote(const NodeData &note, int row);
// 获取指定模型索引的笔记数据
const NodeData &getNote(const QModelIndex &index) const;
// 获取指定ID的笔记的模型索引
QModelIndex getNoteIndex(int id) const;
// 设置笔记列表和列表视图信息
void setListNote(const QVector<NodeData> &notes, const ListViewInfo &inf);
// 移除指定模型索引列表的笔记
void removeNotes(const QModelIndexList &noteIndexes);
// 移动行
bool moveRow(const QModelIndex &sourceParent, int sourceRow,
const QModelIndex &destinationParent, int destinationChild);
// 清空笔记列表
void clearNotes();
// 获取指定模型索引的数据
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
// 设置指定模型索引的数据
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
// 获取指定模型索引的项标志
Qt::ItemFlags flags(const QModelIndex &index) const override;
// 获取行数
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
// 排序
void sort(int column, Qt::SortOrder order) override;
// 设置指定模型索引的笔记数据
void setNoteData(const QModelIndex &index, const NodeData &note);
// 获取支持的拖放操作 void setRowRightOffset(int rowRightOffset);
virtual Qt::DropActions supportedDropActions() const override; void setActive(bool isActive);
// 获取支持的拖拽操作 void recalculateSize();
virtual Qt::DropActions supportedDragActions() const override; void setScrollBarPos(int pos);
// 获取支持的MIME类型 int getScrollBarPos();
virtual QStringList mimeTypes() const override; bool underMouseC() const;
// 获取指定模型索引列表的MIME数据 QPixmap renderToPixmap();
virtual QMimeData *mimeData(const QModelIndexList &indexes) const override;
// 处理MIME数据的拖放
virtual bool dropMimeData(const QMimeData *mime, Qt::DropAction action, int row, int column,
const QModelIndex &parent) override;
// 检查指定模型索引的笔记是否具有标签 public slots:
bool noteIsHaveTag(const QModelIndex &index) const; void setTheme(Theme::Value theme);
// 检查指定模型索引的笔记是否为第一个被钉住的笔记 signals:
bool isFirstPinnedNote(const QModelIndex &index) const; void updateSizeHint(int id, const QSize &sz, const QModelIndex &index);
// 检查指定模型索引的笔记是否为第一个未被钉住的笔记 void nearDestroyed(int id, const QModelIndex &index);
bool isFirstUnpinnedNote(const QModelIndex &index) const;
// 获取第一个被钉住的笔记的模型索引
QModelIndex getFirstPinnedNote() const;
// 检查是否有被钉住的笔记
bool hasPinnedNote() const;
// 设置指定模型索引列表的笔记的钉住状态
void setNotesIsPinned(const QModelIndexList &indexes, bool isPinned);
private: private:
QVector<NodeData> m_noteList; // 笔记列表 void paintBackground(QPainter *painter, const QStyleOptionViewItem &option,
QVector<NodeData> m_pinnedList; // 被钉住的笔记列表 const QModelIndex &index) const;
ListViewInfo m_listViewInfo; // 列表视图信息 void paintLabels(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
void paintSeparator(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
QString parseDateTime(const QDateTime &dateTime) const;
// 更新被钉住的笔记的相对位置 const NoteListDelegate *m_delegate;
void updatePinnedRelativePosition(); QStyleOptionViewItem m_option;
// 检查是否在所有笔记中 int m_id;
bool isInAllNote() const; NoteListView *m_view;
// 获取指定行的笔记数据的引用
NodeData &getRef(int row);
// 获取指定行的笔记数据的常量引用
const NodeData &getRef(int row) const;
signals: TagPool *m_tagPool;
// 行数发生变化时发出的信号 QString m_displayFont;
void rowCountChanged(); QFont m_titleFont;
// 请求更新笔记的钉住状态时发出的信号 QFont m_titleSelectedFont;
void requestUpdatePinned(int noteId, bool isPinned); QFont m_dateFont;
// 请求更新笔记的相对位置时发出的信号 QFont m_headerFont;
void requestUpdatePinnedRelPos(int noteId, int pos); QColor m_titleColor;
// 请求更新笔记的相对位置(在所有笔记中)时发出的信号 QColor m_dateColor;
void requestUpdatePinnedRelPosAN(int noteId, int pos); QColor m_contentColor;
// 请求移除笔记时发出的信号 QColor m_ActiveColor;
void requestRemoveNotes(QModelIndexList index); QColor m_notActiveColor;
// 行插入时发出的信号 QColor m_hoverColor;
void rowsInsertedC(const QModelIndexList &rows); QColor m_applicationInactiveColor;
// 行即将被移动时发出的信号 QColor m_separatorColor;
void rowsAboutToBeMovedC(const QModelIndexList &source); QColor m_defaultColor;
// 行移动后发出的信号 int m_rowHeight;
void rowsMovedC(const QModelIndexList &dest); int m_rowRightOffset;
// 请求关闭笔记编辑器时发出的信号 bool m_isActive;
void requestCloseNoteEditor(const QModelIndexList &indexes); QImage m_folderIcon;
// 请求打开笔记编辑器时发出的信号 Theme::Value m_theme;
void requestOpenNoteEditor(const QModelIndexList &indexes); bool m_containsMouse;
// 选择笔记时发出的信号 QModelIndex m_animatedIndex;
void selectNotes(const QModelIndexList &indexes);
// 重写QAbstractItemModel的接口 TagListView *m_tagListView;
public: TagListModel *m_tagListModel;
// 移除行 TagListDelegate *m_tagListDelegate;
bool removeRows(int row, int count, const QModelIndex &parent) override; // QWidget interface
protected:
virtual void paintEvent(QPaintEvent *event) override;
virtual void resizeEvent(QResizeEvent *event) override;
virtual void dragEnterEvent(QDragEnterEvent *event) override;
virtual void dragLeaveEvent(QDragLeaveEvent *event) override;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
virtual void enterEvent(QEnterEvent *event) override;
#else
virtual void enterEvent(QEvent *event) override;
#endif
virtual void leaveEvent(QEvent *event) override;
virtual void dropEvent(QDropEvent *event) override;
}; };
#endif // NOTELISTMODEL_H #endif // NOTELISTDELEGATEEDITOR_H

@ -1,232 +1,170 @@
#include "notelistmodel.h" #include "notelistmodel.h"
#include <QDebug> // 包含调试输出头文件 #include <QDebug>
#include "nodepath.h" // 包含节点路径头文件 #include "nodepath.h"
#include <QTimer> // 包含定时器头文件 #include <QTimer>
#include <QMimeData> // 包含MIME数据头文件 #include <QMimeData>
NoteListModel::NoteListModel(QObject *parent) : QAbstractListModel(parent) { } NoteListModel::NoteListModel(QObject *parent) : QAbstractListModel(parent) { }
NoteListModel::~NoteListModel() { } NoteListModel::~NoteListModel() { }
// 添加笔记
QModelIndex NoteListModel::addNote(const NodeData &note) QModelIndex NoteListModel::addNote(const NodeData &note)
{ {
// 如果笔记不是被钉住的
if (!note.isPinnedNote()) { if (!note.isPinnedNote()) {
// 获取当前行数
const int rowCnt = rowCount(); const int rowCnt = rowCount();
// 开始插入行
beginInsertRows(QModelIndex(), rowCnt, rowCnt); beginInsertRows(QModelIndex(), rowCnt, rowCnt);
// 将笔记添加到笔记列表
m_noteList << note; m_noteList << note;
// 结束插入行
endInsertRows(); endInsertRows();
// 发出行插入信号
emit rowsInsertedC({ createIndex(rowCnt, 0) }); emit rowsInsertedC({ createIndex(rowCnt, 0) });
// 发出行数变化信号
emit rowCountChanged(); emit rowCountChanged();
// 返回新笔记的模型索引
return createIndex(rowCnt, 0); return createIndex(rowCnt, 0);
} else { } else {
// 获取被钉住的笔记列表的大小
const int rowCnt = m_pinnedList.size(); const int rowCnt = m_pinnedList.size();
// 开始插入行
beginInsertRows(QModelIndex(), rowCnt, rowCnt); beginInsertRows(QModelIndex(), rowCnt, rowCnt);
// 将笔记添加到被钉住的笔记列表
m_pinnedList << note; m_pinnedList << note;
// 结束插入行
endInsertRows(); endInsertRows();
// 发出行插入信号
emit rowsInsertedC({ createIndex(rowCnt, 0) }); emit rowsInsertedC({ createIndex(rowCnt, 0) });
// 发出行数变化信号
emit rowCountChanged(); emit rowCountChanged();
// 返回新笔记的模型索引
return createIndex(rowCnt, 0); return createIndex(rowCnt, 0);
} }
} }
// 在指定行插入笔记
QModelIndex NoteListModel::insertNote(const NodeData &note, int row) QModelIndex NoteListModel::insertNote(const NodeData &note, int row)
{ // 如果笔记是被钉住的 {
if (note.isPinnedNote()) { if (note.isPinnedNote()) {
// 如果指定行大于被钉住的笔记列表的大小,则设置为列表的末尾
if (row > m_pinnedList.size()) { if (row > m_pinnedList.size()) {
row = m_pinnedList.size(); row = m_pinnedList.size();
} else if (row < 0) {// 如果指定行小于0则设置为列表的开头 } else if (row < 0) {
row = 0; row = 0;
} }
// 开始在被钉住的笔记列表的指定行插入一行
beginInsertRows(QModelIndex(), row, row); beginInsertRows(QModelIndex(), row, row);
// 将笔记插入到被钉住的笔记列表的指定行
m_pinnedList.insert(row, note); m_pinnedList.insert(row, note);
// 结束插入行
endInsertRows(); endInsertRows();
// 发出新笔记插入信号
emit rowsInsertedC({ createIndex(row, 0) }); emit rowsInsertedC({ createIndex(row, 0) });
// 发出行数变化信号
emit rowCountChanged(); emit rowCountChanged();
// 返回新笔记的模型索引
return createIndex(row, 0); return createIndex(row, 0);
} else { } else {
// 如果指定行小于被钉住的笔记列表的大小,则设置为被钉住的笔记列表的末尾
if (row < m_pinnedList.size()) { if (row < m_pinnedList.size()) {
row = m_pinnedList.size(); row = m_pinnedList.size();
} else if (row > (m_pinnedList.size() + m_noteList.size())) { } else if (row > (m_pinnedList.size() + m_noteList.size())) {
// 如果指定行大于笔记列表的总大小,则设置为笔记列表的末尾
row = m_pinnedList.size() + m_noteList.size(); row = m_pinnedList.size() + m_noteList.size();
} }
// 开始在笔记列表的指定行插入一行
beginInsertRows(QModelIndex(), row, row); beginInsertRows(QModelIndex(), row, row);
// 将笔记插入到笔记列表的指定行
m_noteList.insert(row - m_pinnedList.size(), note); m_noteList.insert(row - m_pinnedList.size(), note);
// 结束插入行
endInsertRows(); endInsertRows();
// 发出新笔记插入信号
emit rowsInsertedC({ createIndex(row, 0) }); emit rowsInsertedC({ createIndex(row, 0) });
// 发出行数变化信号
emit rowCountChanged(); emit rowCountChanged();
// 返回新笔记的模型索引
return createIndex(row, 0); return createIndex(row, 0);
} }
} }
// 获取指定模型索引的笔记数据
const NodeData &NoteListModel::getNote(const QModelIndex &index) const const NodeData &NoteListModel::getNote(const QModelIndex &index) const
{ {
// 获取模型索引的行号
auto row = index.row(); auto row = index.row();
// 如果行号小于被钉住的笔记列表的大小,则从被钉住的笔记列表中获取笔记数据
if (row < m_pinnedList.size()) { if (row < m_pinnedList.size()) {
return m_pinnedList.at(row); return m_pinnedList.at(row);
} else {// 否则从笔记列表中获取笔记数据 } else {
row = row - m_pinnedList.size(); row = row - m_pinnedList.size();
return m_noteList.at(row); return m_noteList.at(row);
} }
} }
// 获取指定ID的笔记的模型索引
QModelIndex NoteListModel::getNoteIndex(int id) const QModelIndex NoteListModel::getNoteIndex(int id) const
{ // 在被钉住的笔记列表中查找ID匹配的笔记 {
for (int i = 0; i < m_pinnedList.size(); ++i) { for (int i = 0; i < m_pinnedList.size(); ++i) {
if (m_pinnedList[i].id() == id) { if (m_pinnedList[i].id() == id) {
return createIndex(i, 0); return createIndex(i, 0);
} }
} }
// 在笔记列表中查找ID匹配的笔记
for (int i = 0; i < m_noteList.size(); ++i) { for (int i = 0; i < m_noteList.size(); ++i) {
if (m_noteList[i].id() == id) { if (m_noteList[i].id() == id) {
return createIndex(i + m_pinnedList.size(), 0); return createIndex(i + m_pinnedList.size(), 0);
} }
}// 如果未找到匹配的笔记,则返回无效的模型索引 }
return QModelIndex{}; return QModelIndex{};
} }
// 设置笔记列表和列表视图信息
void NoteListModel::setListNote(const QVector<NodeData> &notes, const ListViewInfo &inf) void NoteListModel::setListNote(const QVector<NodeData> &notes, const ListViewInfo &inf)
{ {
// 开始重置模型
beginResetModel(); beginResetModel();
// 清空被钉住的笔记列表
m_pinnedList.clear(); m_pinnedList.clear();
// 清空笔记列表
m_noteList.clear(); m_noteList.clear();
// 设置列表视图信息
m_listViewInfo = inf; m_listViewInfo = inf;
// 如果不在标签中且不在回收站文件夹中
if ((!m_listViewInfo.isInTag) if ((!m_listViewInfo.isInTag)
&& (m_listViewInfo.parentFolderId != SpecialNodeID::TrashFolder)) { && (m_listViewInfo.parentFolderId != SpecialNodeID::TrashFolder)) {
// 遍历笔记列表
for (const auto &note : qAsConst(notes)) { for (const auto &note : qAsConst(notes)) {
// 如果笔记是被钉住的,则添加到被钉住的笔记列表
if (note.isPinnedNote()) { if (note.isPinnedNote()) {
m_pinnedList.append(note); m_pinnedList.append(note);
} else {// 否则添加到笔记列表 } else {
m_noteList.append(note); m_noteList.append(note);
} }
} }
} else { // 否则直接将笔记列表赋值给笔记列表 } else {
m_noteList = notes; m_noteList = notes;
} }
// 按升序排序
sort(0, Qt::AscendingOrder); sort(0, Qt::AscendingOrder);
// 结束重置模型
endResetModel(); endResetModel();
// 发出行数变化信号
emit rowCountChanged(); emit rowCountChanged();
} }
// 移除指定模型索引列表的笔记
void NoteListModel::removeNotes(const QModelIndexList &noteIndexes) void NoteListModel::removeNotes(const QModelIndexList &noteIndexes)
{ {
emit requestRemoveNotes(noteIndexes);// 发出请求移除笔记信号 emit requestRemoveNotes(noteIndexes);
} }
// 移动行
bool NoteListModel::moveRow(const QModelIndex &sourceParent, int sourceRow, bool NoteListModel::moveRow(const QModelIndex &sourceParent, int sourceRow,
const QModelIndex &destinationParent, int destinationChild) const QModelIndex &destinationParent, int destinationChild)
{// 如果源行号或目标行号超出范围则返回false {
if (sourceRow < 0 || sourceRow >= rowCount() || destinationChild < 0 if (sourceRow < 0 || sourceRow >= rowCount() || destinationChild < 0
|| destinationChild >= rowCount()) { || destinationChild >= rowCount()) {
return false; return false;
}// 如果源行号和目标行号都在被钉住的笔记列表范围内 }
if (sourceRow < m_pinnedList.size() && destinationChild < m_pinnedList.size()) { if (sourceRow < m_pinnedList.size() && destinationChild < m_pinnedList.size()) {
if (beginMoveRows(sourceParent, sourceRow, sourceRow, destinationParent, if (beginMoveRows(sourceParent, sourceRow, sourceRow, destinationParent,
destinationChild)) {// 在被钉住的笔记列表中移动笔记 destinationChild)) {
m_pinnedList.move(sourceRow, destinationChild); m_pinnedList.move(sourceRow, destinationChild);
// 结束移动行
endMoveRows(); endMoveRows();
// 发出行即将被移动信号
emit rowsAboutToBeMovedC({ createIndex(sourceRow, 0) }); emit rowsAboutToBeMovedC({ createIndex(sourceRow, 0) });
// 发出行移动后信号
emit rowsMovedC({ createIndex(destinationChild, 0) }); emit rowsMovedC({ createIndex(destinationChild, 0) });
// 发出行数变化信号 emit rowCountChanged();
return true; return true;
} }
} }
// 如果源行号和目标行号都在笔记列表范围内
if (sourceRow >= m_pinnedList.size() && destinationChild >= m_pinnedList.size()) { if (sourceRow >= m_pinnedList.size() && destinationChild >= m_pinnedList.size()) {
// 调整源行号和目标行号以适应笔记列表的范围
sourceRow = sourceRow - m_pinnedList.size(); sourceRow = sourceRow - m_pinnedList.size();
destinationChild = destinationChild - m_pinnedList.size(); destinationChild = destinationChild - m_pinnedList.size();
// 开始移动行
if (beginMoveRows(sourceParent, sourceRow, sourceRow, destinationParent, if (beginMoveRows(sourceParent, sourceRow, sourceRow, destinationParent,
destinationChild)) { destinationChild)) {
// 在笔记列表中移动笔记
m_noteList.move(sourceRow, destinationChild); m_noteList.move(sourceRow, destinationChild);
// 结束移动行 endMoveRows();
// 发出行即将被移动信号
emit rowsAboutToBeMovedC({ createIndex(sourceRow, 0) }); emit rowsAboutToBeMovedC({ createIndex(sourceRow, 0) });
// 发出行移动后信号
emit rowsMovedC({ createIndex(destinationChild + 1, 0) }); emit rowsMovedC({ createIndex(destinationChild + 1, 0) });
// 发出行数变化信号
emit rowCountChanged(); emit rowCountChanged();
return true; return true;
} }
} }
// 如果源行号和目标行号不在同一范围内则返回false
return false; return false;
} }
// 清空笔记列表
void NoteListModel::clearNotes() void NoteListModel::clearNotes()
{ {
// 开始重置模型
beginResetModel(); beginResetModel();
// 清空被钉住的笔记列表
m_pinnedList.clear(); m_pinnedList.clear();
// 清空笔记列表
m_noteList.clear(); m_noteList.clear();
// 结束重置模型
endResetModel(); endResetModel();
// 发出行数变化信号
emit rowCountChanged(); emit rowCountChanged();
} }
// 获取指定模型索引的数据的实现
QVariant NoteListModel::data(const QModelIndex &index, int role) const QVariant NoteListModel::data(const QModelIndex &index, int role) const
{ {
// 如果模型索引的行号超出范围则返回空的QVariant
if (index.row() < 0 || index.row() >= (m_noteList.count() + m_pinnedList.count())) { if (index.row() < 0 || index.row() >= (m_noteList.count() + m_pinnedList.count())) {
return QVariant(); return QVariant();
} }
// 如果角色超出范围则返回空的QVariant
if (role < NoteID || role > NoteIsPinned) { if (role < NoteID || role > NoteIsPinned) {
return QVariant(); return QVariant();
} }
// 获取指定行的笔记数据
const NodeData &note = getRef(index.row()); const NodeData &note = getRef(index.row());
// 根据角色返回相应的数据
if (role == NoteID) { if (role == NoteID) {
return note.id(); return note.id();
} else if (role == NoteFullTitle) { } else if (role == NoteFullTitle) {
@ -260,16 +198,14 @@ QVariant NoteListModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
// 设置指定模型索引的数据的实现
bool NoteListModel::setData(const QModelIndex &index, const QVariant &value, int role) bool NoteListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{ {
// 如果模型索引的行号超出范围则返回false
if (index.row() < 0 || index.row() >= (m_noteList.count() + m_pinnedList.count())) { if (index.row() < 0 || index.row() >= (m_noteList.count() + m_pinnedList.count())) {
return false; return false;
} }
// 获取指定行的笔记数据的引用
NodeData &note = getRef(index.row()); NodeData &note = getRef(index.row());
// 根据角色设置相应的数据
if (role == NoteID) { if (role == NoteID) {
note.setId(value.toInt()); note.setId(value.toInt());
} else if (role == NoteFullTitle) { } else if (role == NoteFullTitle) {
@ -295,44 +231,37 @@ bool NoteListModel::setData(const QModelIndex &index, const QVariant &value, int
} else { } else {
return false; return false;
} }
// 发出数据变化信号
emit dataChanged(this->index(index.row()), this->index(index.row()), QVector<int>(1, role)); emit dataChanged(this->index(index.row()), this->index(index.row()), QVector<int>(1, role));
return true; return true;
} }
// 获取指定模型索引的项标志
Qt::ItemFlags NoteListModel::flags(const QModelIndex &index) const Qt::ItemFlags NoteListModel::flags(const QModelIndex &index) const
{ {
// 如果模型索引无效,则返回启用和可放置的项标志
if (!index.isValid()) { if (!index.isValid()) {
return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled;
} }
// 返回基类的项标志,并添加可编辑、可拖拽和可放置的项标志
return QAbstractListModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled return QAbstractListModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled; | Qt::ItemIsDropEnabled;
} }
// 获取行数
int NoteListModel::rowCount(const QModelIndex &parent) const int NoteListModel::rowCount(const QModelIndex &parent) const
{ {
// 忽略parent参数
Q_UNUSED(parent) Q_UNUSED(parent)
// 返回笔记列表和被钉住的笔记列表的总大小
return m_noteList.size() + m_pinnedList.size(); return m_noteList.size() + m_pinnedList.size();
} }
// 排序
void NoteListModel::sort(int column, Qt::SortOrder order) void NoteListModel::sort(int column, Qt::SortOrder order)
{ {
// 忽略column和order参数
Q_UNUSED(column) Q_UNUSED(column)
Q_UNUSED(order) Q_UNUSED(order)
// 如果在回收站文件夹中
if (m_listViewInfo.parentFolderId == SpecialNodeID::TrashFolder) { if (m_listViewInfo.parentFolderId == SpecialNodeID::TrashFolder) {
// 按删除日期时间降序排序笔记列表
std::stable_sort(m_noteList.begin(), m_noteList.end(), std::stable_sort(m_noteList.begin(), m_noteList.end(),
[](const NodeData &lhs, const NodeData &rhs) { [](const NodeData &lhs, const NodeData &rhs) {
return lhs.deletionDateTime() > rhs.deletionDateTime(); return lhs.deletionDateTime() > rhs.deletionDateTime();
}); });
} else {// 否则按相对位置升序排序被钉住的笔记列表 } else {
std::stable_sort(m_pinnedList.begin(), m_pinnedList.end(), std::stable_sort(m_pinnedList.begin(), m_pinnedList.end(),
[this](const NodeData &lhs, const NodeData &rhs) { [this](const NodeData &lhs, const NodeData &rhs) {
if (isInAllNote()) { if (isInAllNote()) {
@ -341,217 +270,176 @@ void NoteListModel::sort(int column, Qt::SortOrder order)
return lhs.relativePosition() < rhs.relativePosition(); return lhs.relativePosition() < rhs.relativePosition();
} }
}); });
// 按最后修改日期时间降序排序笔记列表
std::stable_sort(m_noteList.begin(), m_noteList.end(), std::stable_sort(m_noteList.begin(), m_noteList.end(),
[](const NodeData &lhs, const NodeData &rhs) { [](const NodeData &lhs, const NodeData &rhs) {
return lhs.lastModificationdateTime() > rhs.lastModificationdateTime(); return lhs.lastModificationdateTime() > rhs.lastModificationdateTime();
}); });
} }
// 发出数据变化信号
emit dataChanged(index(0), index(rowCount() - 1)); emit dataChanged(index(0), index(rowCount() - 1));
} }
// 设置指定模型索引的笔记数据
void NoteListModel::setNoteData(const QModelIndex &index, const NodeData &note) void NoteListModel::setNoteData(const QModelIndex &index, const NodeData &note)
{ {
// 如果模型索引无效,则返回
if (!index.isValid()) { if (!index.isValid()) {
return; return;
} }
auto row = index.row();// 获取模型索引的行号 auto row = index.row();
// 如果行号小于被钉住的笔记列表的大小,则在被钉住的笔记列表中设置笔记数据
if (row < m_pinnedList.size()) { if (row < m_pinnedList.size()) {
m_pinnedList[row] = note; m_pinnedList[row] = note;
} else { // 否则在笔记列表中设置笔记数据 } else {
row = row - m_pinnedList.size(); row = row - m_pinnedList.size();
m_noteList[row] = note; m_noteList[row] = note;
} }
// 发出数据变化信号
emit dataChanged(this->index(index.row()), this->index(index.row())); emit dataChanged(this->index(index.row()), this->index(index.row()));
} }
// 更新被钉住的笔记的相对位置的实现
void NoteListModel::updatePinnedRelativePosition() void NoteListModel::updatePinnedRelativePosition()
{ {
// 遍历被钉住的笔记列表
for (int i = 0; i < m_pinnedList.size(); ++i) { for (int i = 0; i < m_pinnedList.size(); ++i) {
// 如果不在所有笔记中
if (!isInAllNote()) { if (!isInAllNote()) {
// 发出请求更新笔记的相对位置信号
emit requestUpdatePinnedRelPos(m_pinnedList[i].id(), i); emit requestUpdatePinnedRelPos(m_pinnedList[i].id(), i);
} else { // 否则发出请求更新笔记的相对位置(在所有笔记中)信号 } else {
emit requestUpdatePinnedRelPosAN(m_pinnedList[i].id(), i); emit requestUpdatePinnedRelPosAN(m_pinnedList[i].id(), i);
} }
} }
} }
// 检查是否在所有笔记中
bool NoteListModel::isInAllNote() const bool NoteListModel::isInAllNote() const
{ {
return (!m_listViewInfo.isInTag)// 如果不在标签中且在根文件夹中则返回true return (!m_listViewInfo.isInTag)
&& (m_listViewInfo.parentFolderId == SpecialNodeID::RootFolder); && (m_listViewInfo.parentFolderId == SpecialNodeID::RootFolder);
} }
// 获取指定行的笔记数据的引用
NodeData &NoteListModel::getRef(int row) NodeData &NoteListModel::getRef(int row)
{ {
// 如果行号小于被钉住的笔记列表的大小,则从被钉住的笔记列表中获取笔记数据的引用
if (row < m_pinnedList.size()) if (row < m_pinnedList.size())
return m_pinnedList[row]; return m_pinnedList[row];
// 否则从笔记列表中获取笔记数据的引用
return m_noteList[row - m_pinnedList.size()]; return m_noteList[row - m_pinnedList.size()];
} }
// 获取指定行的笔记数据的常量引用
const NodeData &NoteListModel::getRef(int row) const const NodeData &NoteListModel::getRef(int row) const
{ {
// 如果行号小于被钉住的笔记列表的大小,则从被钉住的笔记列表中获取笔记数据的常量引用
if (row < m_pinnedList.size()) if (row < m_pinnedList.size())
return m_pinnedList[row]; return m_pinnedList[row];
// 否则从笔记列表中获取笔记数据的常量引用
return m_noteList[row - m_pinnedList.size()]; return m_noteList[row - m_pinnedList.size()];
} }
// 移除行
bool NoteListModel::removeRows(int row, int count, const QModelIndex &parent) bool NoteListModel::removeRows(int row, int count, const QModelIndex &parent)
{ {
if (row < 0 || (row + count) > (m_pinnedList.size() + m_noteList.size())) {// 如果行号或行数超出范围则返回false if (row < 0 || (row + count) > (m_pinnedList.size() + m_noteList.size())) {
return false; return false;
} }
beginRemoveRows(parent, row, row + count - 1);// 开始移除行 beginRemoveRows(parent, row, row + count - 1);
for (int r = row; r < row + count; ++r) {// 遍历要移除的行 for (int r = row; r < row + count; ++r) {
// 如果行号小于被钉住的笔记列表的大小,则从被钉住的笔记列表中移除笔记
if (r < m_pinnedList.size()) { if (r < m_pinnedList.size()) {
m_pinnedList.takeAt(r); m_pinnedList.takeAt(r);
} else {// 否则从笔记列表中移除笔记 } else {
auto rr = r - m_pinnedList.size(); auto rr = r - m_pinnedList.size();
m_noteList.takeAt(rr); m_noteList.takeAt(rr);
} }
}// 结束移除行 }
endRemoveRows(); endRemoveRows();
emit rowCountChanged();// 发出行数变化信号 emit rowCountChanged();
return true; return true;
} }
// 获取支持的拖放操作
Qt::DropActions NoteListModel::supportedDropActions() const Qt::DropActions NoteListModel::supportedDropActions() const
{ {
return Qt::MoveAction;// 返回移动操作 return Qt::MoveAction;
} }
// 获取支持的拖拽操作
Qt::DropActions NoteListModel::supportedDragActions() const Qt::DropActions NoteListModel::supportedDragActions() const
{ {
return Qt::MoveAction;// 返回移动操作 return Qt::MoveAction;
} }
// 获取支持的MIME类型
QStringList NoteListModel::mimeTypes() const QStringList NoteListModel::mimeTypes() const
{ {
return QStringList() << NOTE_MIME;// 返回包含笔记MIME类型的列表 return QStringList() << NOTE_MIME;
} }
// 获取指定模型索引列表的MIME数据
QMimeData *NoteListModel::mimeData(const QModelIndexList &indexes) const QMimeData *NoteListModel::mimeData(const QModelIndexList &indexes) const
{ {
// 如果模型索引列表为空则返回nullptr
if (indexes.isEmpty()) { if (indexes.isEmpty()) {
return nullptr; return nullptr;
} }
QStringList d;// 创建一个字符串列表来存储笔记ID QStringList d;
for (const auto &index : indexes) {// 遍历模型索引列表 for (const auto &index : indexes) {
// 获取笔记ID并添加到字符串列表中
auto id = index.data(NoteListModel::NoteID).toInt(); auto id = index.data(NoteListModel::NoteID).toInt();
d.append(QString::number(id)); d.append(QString::number(id));
} }
if (d.isEmpty()) { if (d.isEmpty()) {
return nullptr;// 如果字符串列表为空则返回nullptr return nullptr;
} }
QMimeData *mimeData = new QMimeData;// 创建一个新的QMimeData对象 QMimeData *mimeData = new QMimeData;
mimeData->setData(NOTE_MIME, d.join(QStringLiteral(PATH_SEPARATOR)).toUtf8());// 将笔记ID列表设置为MIME数据 mimeData->setData(NOTE_MIME, d.join(QStringLiteral(PATH_SEPARATOR)).toUtf8());
return mimeData; return mimeData;
} }
// 处理MIME数据的拖放
bool NoteListModel::dropMimeData(const QMimeData *mime, Qt::DropAction action, int row, int column, bool NoteListModel::dropMimeData(const QMimeData *mime, Qt::DropAction action, int row, int column,
const QModelIndex &parent) const QModelIndex &parent)
{// 忽略column参数 {
Q_UNUSED(column); Q_UNUSED(column);
// 如果MIME数据不包含笔记MIME类型或操作不是移动操作则返回false
if (!(mime->hasFormat(NOTE_MIME) && action == Qt::MoveAction)) { if (!(mime->hasFormat(NOTE_MIME) && action == Qt::MoveAction)) {
return false; return false;
} }
// 如果行号为-1则根据parent的有效性确定行号
if (row == -1) { if (row == -1) {
// 如果parent有效则行号为parent的行号 // valid index: drop onto item
if (parent.isValid()) { if (parent.isValid()) {
row = parent.row(); row = parent.row();
} else { // 否则行号为模型的行数 } else {
// invalid index: append at bottom, after last toplevel // invalid index: append at bottom, after last toplevel
row = rowCount(parent); row = rowCount(parent);
} }
} }
// 根据行号确定是否移动到被钉住的笔记列表
bool toPinned = false; bool toPinned = false;
if (row >= m_pinnedList.size()) { if (row >= m_pinnedList.size()) {
toPinned = false; toPinned = false;
} else { } else {
toPinned = true; toPinned = true;
}// 获取笔记ID列表 }
auto idl = QString::fromUtf8(mime->data(NOTE_MIME)).split(QStringLiteral(PATH_SEPARATOR)); auto idl = QString::fromUtf8(mime->data(NOTE_MIME)).split(QStringLiteral(PATH_SEPARATOR));
// 创建一个QSet来存储移动的笔记ID
QSet<int> movedIds; QSet<int> movedIds;
// 创建一个QModelIndexList来存储移动的笔记的模型索引
QModelIndexList idxe; QModelIndexList idxe;
// 遍历笔记ID列表
for (const auto &id_s : qAsConst(idl)) { for (const auto &id_s : qAsConst(idl)) {
// 获取笔记ID并添加到模型索引列表中
auto nodeId = id_s.toInt(); auto nodeId = id_s.toInt();
idxe.append(getNoteIndex(nodeId)); idxe.append(getNoteIndex(nodeId));
} }
// 发出行即将被移动信号
emit rowsAboutToBeMovedC(idxe); emit rowsAboutToBeMovedC(idxe);
// 开始重置模型
beginResetModel(); beginResetModel();
// 如果移动到被钉住的笔记列表
if (toPinned) { if (toPinned) {
// 遍历移动的笔记的模型索引列表
for (const auto &index : qAsConst(idxe)) { for (const auto &index : qAsConst(idxe)) {
// 获取笔记数据的引用
auto &note = getRef(index.row()); auto &note = getRef(index.row());
// 如果笔记不是被钉住的,则设置为被钉住的
if (!note.isPinnedNote()) { if (!note.isPinnedNote()) {
note.setIsPinnedNote(true); note.setIsPinnedNote(true);
// 发出请求更新笔记的钉住状态信号
emit requestUpdatePinned(note.id(), true); emit requestUpdatePinned(note.id(), true);
// 将笔记从笔记列表移动到被钉住的笔记列表的开头
m_pinnedList.prepend(m_noteList.takeAt(index.row() - m_pinnedList.size())); m_pinnedList.prepend(m_noteList.takeAt(index.row() - m_pinnedList.size()));
} }
} }
// 遍历笔记ID列表
for (const auto &id_s : qAsConst(idl)) { for (const auto &id_s : qAsConst(idl)) {
// 获取笔记ID
auto nodeId = id_s.toInt(); auto nodeId = id_s.toInt();
// 遍历被钉住的笔记列表
for (int i = 0; i < m_pinnedList.size(); ++i) { for (int i = 0; i < m_pinnedList.size(); ++i) {
// 如果找到匹配的笔记,则移动到指定行
if (m_pinnedList[i].id() == nodeId) { if (m_pinnedList[i].id() == nodeId) {
m_pinnedList.move(i, row); m_pinnedList.move(i, row);
break; break;
} }
} }
movedIds.insert(nodeId);// 将笔记ID添加到移动的笔记ID集合中 movedIds.insert(nodeId);
} }
} else { // 否则移动到笔记列表 } else {
// 遍历移动的笔记的模型索引列表
for (const auto &index : qAsConst(idxe)) { for (const auto &index : qAsConst(idxe)) {
// 获取笔记数据的引用
auto &note = getRef(index.row()); auto &note = getRef(index.row());
// 将笔记ID添加到移动的笔记ID集合中
movedIds.insert(note.id()); movedIds.insert(note.id());
// 如果笔记是被钉住的,则设置为不是被钉住的
if (!note.isPinnedNote()) { if (!note.isPinnedNote()) {
continue; continue;
} }
note.setIsPinnedNote(false); note.setIsPinnedNote(false);
// 发出请求更新笔记的钉住状态信号
emit requestUpdatePinned(note.id(), false); emit requestUpdatePinned(note.id(), false);
// 根据当前视图确定插入位置
int destinationChild = 0; int destinationChild = 0;
if (m_listViewInfo.parentFolderId == SpecialNodeID::TrashFolder) { if (m_listViewInfo.parentFolderId == SpecialNodeID::TrashFolder) {
// 如果在回收站文件夹中,则按删除日期时间排序
auto lastMod = index.data(NoteDeletionDateTime).toDateTime(); auto lastMod = index.data(NoteDeletionDateTime).toDateTime();
for (destinationChild = 0; destinationChild < m_noteList.size(); for (destinationChild = 0; destinationChild < m_noteList.size();
++destinationChild) { ++destinationChild) {
@ -559,7 +447,7 @@ bool NoteListModel::dropMimeData(const QMimeData *mime, Qt::DropAction action, i
break; break;
} }
} }
} else {// 否则按最后修改日期时间排序 } else {
auto lastMod = index.data(NoteLastModificationDateTime).toDateTime(); auto lastMod = index.data(NoteLastModificationDateTime).toDateTime();
for (destinationChild = 0; destinationChild < m_noteList.size(); for (destinationChild = 0; destinationChild < m_noteList.size();
++destinationChild) { ++destinationChild) {
@ -567,141 +455,99 @@ bool NoteListModel::dropMimeData(const QMimeData *mime, Qt::DropAction action, i
break; break;
} }
} }
}// 将笔记从被钉住的笔记列表移动到笔记列表的指定位置 }
m_noteList.insert(destinationChild, m_pinnedList.takeAt(index.row())); m_noteList.insert(destinationChild, m_pinnedList.takeAt(index.row()));
} }
} }
// 结束重置模型
endResetModel(); endResetModel();
// 创建一个QModelIndexList来存储移动后的笔记的模型索引
QModelIndexList destinations; QModelIndexList destinations;
// 遍历移动的笔记ID集合
for (const auto &id : movedIds) { for (const auto &id : movedIds) {
// 获取笔记的模型索引并添加到模型索引列表中
auto index = getNoteIndex(id); auto index = getNoteIndex(id);
if (!index.isValid()) { if (!index.isValid()) {
continue; continue;
} }
destinations.append(index); destinations.append(index);
} }
// 发出选择笔记信号
emit selectNotes(destinations); emit selectNotes(destinations);
// 发出行移动后信号
emit rowsMovedC(destinations); emit rowsMovedC(destinations);
// 更新被钉住的笔记的相对位置
updatePinnedRelativePosition(); updatePinnedRelativePosition();
return true; return true;
} }
// 获取第一个未被钉住的笔记的模型索引
QModelIndex NoteListModel::getFirstUnpinnedNote() const QModelIndex NoteListModel::getFirstUnpinnedNote() const
{// 如果笔记列表不为空,则返回第一个未被钉住的笔记的模型索引 {
if (!m_noteList.isEmpty()) { if (!m_noteList.isEmpty()) {
return createIndex(m_pinnedList.size(), 0); return createIndex(m_pinnedList.size(), 0);
} else {// 否则返回无效的模型索引 } else {
return QModelIndex(); return QModelIndex();
} }
} }
// 检查是否有被钉住的笔记
bool NoteListModel::hasPinnedNote() const bool NoteListModel::hasPinnedNote() const
{ {
// 如果不在标签中且不在回收站文件夹中
if ((!m_listViewInfo.isInTag) if ((!m_listViewInfo.isInTag)
&& (m_listViewInfo.parentFolderId == SpecialNodeID::TrashFolder)) { && (m_listViewInfo.parentFolderId == SpecialNodeID::TrashFolder)) {
// 回收站文件夹中没有被钉住的笔记返回false
// Trash don't have pinned note // Trash don't have pinned note
return false; return false;
}// 如果被钉住的笔记列表不为空则返回true }
return !m_pinnedList.isEmpty(); return !m_pinnedList.isEmpty();
} }
// 设置指定模型索引列表的笔记的钉住状态的实现
void NoteListModel::setNotesIsPinned(const QModelIndexList &indexes, bool isPinned) void NoteListModel::setNotesIsPinned(const QModelIndexList &indexes, bool isPinned)
{ {
// 发出请求关闭笔记编辑器信号
emit requestCloseNoteEditor(indexes); emit requestCloseNoteEditor(indexes);
// 创建一个QSet来存储需要移动的笔记ID
QSet<int> needMovingIds; QSet<int> needMovingIds;
// 创建一个QModelIndexList来存储需要移动的笔记的模型索引
QModelIndexList needMovingIndexes; QModelIndexList needMovingIndexes;
// 遍历模型索引列表
for (const auto &index : indexes) { for (const auto &index : indexes) {
// 如果模型索引有效
if (index.isValid()) { if (index.isValid()) {
// 获取笔记数据的引用
NodeData &note = getRef(index.row()); NodeData &note = getRef(index.row());
// 如果笔记的钉住状态需要改变
if (note.isPinnedNote() != isPinned) { if (note.isPinnedNote() != isPinned) {
// 将笔记ID添加到需要移动的笔记ID集合中
needMovingIds.insert(note.id()); needMovingIds.insert(note.id());
// 将模型索引添加到需要移动的笔记的模型索引列表中
needMovingIndexes.append(index); needMovingIndexes.append(index);
// 设置笔记的钉住状态
note.setIsPinnedNote(isPinned); note.setIsPinnedNote(isPinned);
// 发出请求更新笔记的钉住状态信号
emit requestUpdatePinned(note.id(), isPinned); emit requestUpdatePinned(note.id(), isPinned);
} }
} }
} }
// 如果设置为被钉住的
if (isPinned) { if (isPinned) {
// 发出行即将被移动信号
emit rowsAboutToBeMovedC(needMovingIndexes); emit rowsAboutToBeMovedC(needMovingIndexes);
// 开始重置模型
beginResetModel(); beginResetModel();
// 遍历需要移动的笔记ID集合
for (const auto &id : qAsConst(needMovingIds)) { for (const auto &id : qAsConst(needMovingIds)) {
// 获取笔记的模型索引
auto index = getNoteIndex(id); auto index = getNoteIndex(id);
if (!index.isValid()) { if (!index.isValid()) {
continue; continue;
} }
// 获取源行号
int sourceRow = index.row(); int sourceRow = index.row();
// 如果源行号超出范围,则继续下一个
if (sourceRow < 0 || sourceRow >= rowCount()) { if (sourceRow < 0 || sourceRow >= rowCount()) {
continue; continue;
} }
// 将笔记从笔记列表移动到被钉住的笔记列表的开头
m_pinnedList.prepend(m_noteList.takeAt(sourceRow - m_pinnedList.size())); m_pinnedList.prepend(m_noteList.takeAt(sourceRow - m_pinnedList.size()));
} }
// 结束重置模型
endResetModel(); endResetModel();
// 创建一个QModelIndexList来存储移动后的笔记的模型索引
QModelIndexList destinations; QModelIndexList destinations;
// 遍历需要移动的笔记ID集合 for (const auto &id : needMovingIds) {
for (const auto &id : qAsConst(needMovingIds)) {
// 获取笔记的模型索引并添加到模型索引列表中
auto index = getNoteIndex(id); auto index = getNoteIndex(id);
if (!index.isValid()) { if (!index.isValid()) {
continue; continue;
} }
destinations.append(index); destinations.append(index);
} }
// 发出选择笔记信号
emit selectNotes(destinations); emit selectNotes(destinations);
// 发出行移动后信号
emit rowsMovedC(destinations); emit rowsMovedC(destinations);
// 发出行数变化信号
emit rowCountChanged(); emit rowCountChanged();
// 更新被钉住的笔记的相对位置
updatePinnedRelativePosition(); updatePinnedRelativePosition();
} else { // 否则设置为不是被钉住的 } else {
// 发出行即将被移动信号
emit rowsAboutToBeMovedC(needMovingIndexes); emit rowsAboutToBeMovedC(needMovingIndexes);
// 开始重置模型
beginResetModel(); beginResetModel();
// 遍历需要移动的笔记ID集合
for (const auto &id : qAsConst(needMovingIds)) { for (const auto &id : qAsConst(needMovingIds)) {
// 获取笔记的模型索引
auto index = getNoteIndex(id); auto index = getNoteIndex(id);
if (!index.isValid()) { if (!index.isValid()) {
continue; continue;
} }
int destinationChild = 0; int destinationChild = 0;
if (m_listViewInfo.parentFolderId == SpecialNodeID::TrashFolder) { if (m_listViewInfo.parentFolderId == SpecialNodeID::TrashFolder) {
// 如果在回收站文件夹中,则按删除日期时间排序
auto lastMod = index.data(NoteDeletionDateTime).toDateTime(); auto lastMod = index.data(NoteDeletionDateTime).toDateTime();
for (destinationChild = 0; destinationChild < m_noteList.size(); for (destinationChild = 0; destinationChild < m_noteList.size();
++destinationChild) { ++destinationChild) {
@ -710,7 +556,7 @@ void NoteListModel::setNotesIsPinned(const QModelIndexList &indexes, bool isPinn
break; break;
} }
} }
} else {// 否则按最后修改日期时间排序 } else {
auto lastMod = index.data(NoteLastModificationDateTime).toDateTime(); auto lastMod = index.data(NoteLastModificationDateTime).toDateTime();
for (destinationChild = 0; destinationChild < m_noteList.size(); for (destinationChild = 0; destinationChild < m_noteList.size();
++destinationChild) { ++destinationChild) {
@ -719,89 +565,72 @@ void NoteListModel::setNotesIsPinned(const QModelIndexList &indexes, bool isPinn
break; break;
} }
} }
}// 将笔记从被钉住的笔记列表移动到笔记列表的指定位置 }
m_noteList.insert(destinationChild, m_pinnedList.takeAt(index.row())); m_noteList.insert(destinationChild, m_pinnedList.takeAt(index.row()));
} }
// 结束重置模型
endResetModel(); endResetModel();
// 创建一个QModelIndexList来存储移动后的笔记的模型索引
QModelIndexList destinations; QModelIndexList destinations;
// 遍历需要移动的笔记ID集合
for (const auto &id : needMovingIds) { for (const auto &id : needMovingIds) {
// 获取笔记的模型索引并添加到模型索引列表中
auto index = getNoteIndex(id); auto index = getNoteIndex(id);
if (!index.isValid()) { if (!index.isValid()) {
continue; continue;
} }
destinations.append(index); destinations.append(index);
} }
// 发出选择笔记信号
emit selectNotes(destinations); emit selectNotes(destinations);
// 发出行移动后信号
emit rowsMovedC(destinations); emit rowsMovedC(destinations);
// 发出行数变化信号
emit rowCountChanged(); emit rowCountChanged();
// 更新被钉住的笔记的相对位置
updatePinnedRelativePosition(); updatePinnedRelativePosition();
} }
} }
// 检查指定模型索引的笔记是否具有标签
bool NoteListModel::noteIsHaveTag(const QModelIndex &index) const bool NoteListModel::noteIsHaveTag(const QModelIndex &index) const
{// 如果模型索引无效则返回false {
if (index.row() < 0 || index.row() >= (m_noteList.count() + m_pinnedList.count())) { if (index.row() < 0 || index.row() >= (m_noteList.count() + m_pinnedList.count())) {
return false; return false;
} }
// 获取模型索引的行号
auto row = index.row(); auto row = index.row();
// 创建一个NodeData对象来存储笔记数据
NodeData note; NodeData note;
// 如果行号小于被钉住的笔记列表的大小,则从被钉住的笔记列表中获取笔记数据
if (row < m_pinnedList.size()) { if (row < m_pinnedList.size()) {
note = m_pinnedList[row]; note = m_pinnedList[row];
} else { // 否则从笔记列表中获取笔记数据 } else {
row = row - m_pinnedList.size(); row = row - m_pinnedList.size();
note = m_noteList[row]; note = m_noteList[row];
} }
return !note.tagIds().empty();// 如果笔记的标签列表不为空则返回true return !note.tagIds().empty();
} }
// 检查指定模型索引的笔记是否为第一个被钉住的笔记
bool NoteListModel::isFirstPinnedNote(const QModelIndex &index) const bool NoteListModel::isFirstPinnedNote(const QModelIndex &index) const
{ {
// 如果模型索引无效则返回false
if (!index.isValid()) { if (!index.isValid()) {
return false; return false;
} }
// 如果模型索引的行号大于0则返回false
if (index.row() > 0) { if (index.row() > 0) {
return false; return false;
} }
// 获取指定行的笔记数据的常量引用
const NodeData &note = getRef(index.row()); const NodeData &note = getRef(index.row());
// 如果模型索引的行号为0且笔记是被钉住的则返回true
if (index.row() == 0 && note.isPinnedNote()) { if (index.row() == 0 && note.isPinnedNote()) {
return true; return true;
} }
return false; return false;
} }
// 检查指定模型索引的笔记是否为第一个未被钉住的笔记
bool NoteListModel::isFirstUnpinnedNote(const QModelIndex &index) const bool NoteListModel::isFirstUnpinnedNote(const QModelIndex &index) const
{// 如果模型索引无效则返回false {
if (!index.isValid()) { if (!index.isValid()) {
return false; return false;
} }
// 获取指定行的笔记数据的常量引用
const NodeData &note = getRef(index.row()); const NodeData &note = getRef(index.row());
if ((index.row() - m_pinnedList.size()) == 0 && !note.isPinnedNote()) {// 如果模型索引的行号减去被钉住的笔记列表的大小为0且笔记不是被钉住的则返回true if ((index.row() - m_pinnedList.size()) == 0 && !note.isPinnedNote()) {
return true; return true;
} }
return false; return false;
} }
// 获取第一个被钉住的笔记的模型索引
QModelIndex NoteListModel::getFirstPinnedNote() const QModelIndex NoteListModel::getFirstPinnedNote() const
{ {
if (m_pinnedList.isEmpty()) {// 如果被钉住的笔记列表不为空,则返回第一个被钉住的笔记的模型索引 if (m_pinnedList.isEmpty()) {
return QModelIndex(); return QModelIndex();
}// 否则返回无效的模型索引 }
return createIndex(0, 0); return createIndex(0, 0);
} }

@ -9,115 +9,80 @@ class NoteListModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
public: public:
// 定义一个枚举类型NoteRoles用于指定模型的角色
enum NoteRoles { enum NoteRoles {
NoteID = Qt::UserRole + 1, // 笔记ID角色 NoteID = Qt::UserRole + 1,
NoteFullTitle, // 笔记完整标题角色 NoteFullTitle,
NoteCreationDateTime, // 笔记创建日期时间角色 NoteCreationDateTime,
NoteLastModificationDateTime, // 笔记最后修改日期时间角色 NoteLastModificationDateTime,
NoteDeletionDateTime, // 笔记删除日期时间角色 NoteDeletionDateTime,
NoteContent, // 笔记内容角色 NoteContent,
NoteScrollbarPos, // 笔记滚动条位置角色 NoteScrollbarPos,
NoteTagsList, // 笔记标签列表角色 NoteTagsList,
NoteIsTemp, // 笔记是否为临时笔记角色 NoteIsTemp,
NoteParentName, // 笔记父级名称角色 NoteParentName,
NoteTagListScrollbarPos, // 笔记标签列表滚动条位置角色 NoteTagListScrollbarPos,
NoteIsPinned, // 笔记是否被钉住角色 NoteIsPinned,
}; };
explicit NoteListModel(QObject *parent = nullptr);// 构造函数,可以指定一个父对象 explicit NoteListModel(QObject *parent = nullptr);
~NoteListModel();// 析构函数 ~NoteListModel();
QModelIndex addNote(const NodeData &note);// 添加笔记,并返回新笔记的模型索引 QModelIndex addNote(const NodeData &note);
QModelIndex insertNote(const NodeData &note, int row);// 在指定行插入笔记,并返回新笔记的模型索引 QModelIndex insertNote(const NodeData &note, int row);
const NodeData &getNote(const QModelIndex &index) const;// 获取指定模型索引的笔记数据 const NodeData &getNote(const QModelIndex &index) const;
QModelIndex getNoteIndex(int id) const; // 获取指定ID的笔记的模型索引 QModelIndex getNoteIndex(int id) const;
void setListNote(const QVector<NodeData> &notes, const ListViewInfo &inf);// 设置笔记列表和列表视图信息 void setListNote(const QVector<NodeData> &notes, const ListViewInfo &inf);
void removeNotes(const QModelIndexList &noteIndexes); // 移除指定模型索引列表的笔记 void removeNotes(const QModelIndexList &noteIndexes);
// 移动行
bool moveRow(const QModelIndex &sourceParent, int sourceRow, bool moveRow(const QModelIndex &sourceParent, int sourceRow,
const QModelIndex &destinationParent, int destinationChild); const QModelIndex &destinationParent, int destinationChild);
// 清空笔记列表
void clearNotes(); void clearNotes();
// 获取指定模型索引的数据
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
// 设置指定模型索引的数据
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
// 获取指定模型索引的项标志
Qt::ItemFlags flags(const QModelIndex &index) const override; Qt::ItemFlags flags(const QModelIndex &index) const override;
// 获取行数
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
// 排序
void sort(int column, Qt::SortOrder order) override; void sort(int column, Qt::SortOrder order) override;
// 设置指定模型索引的笔记数据
void setNoteData(const QModelIndex &index, const NodeData &note); void setNoteData(const QModelIndex &index, const NodeData &note);
// 获取支持的拖放操作
virtual Qt::DropActions supportedDropActions() const override; virtual Qt::DropActions supportedDropActions() const override;
// 获取支持的拖拽操作
virtual Qt::DropActions supportedDragActions() const override; virtual Qt::DropActions supportedDragActions() const override;
// 获取支持的MIME类型
virtual QStringList mimeTypes() const override; virtual QStringList mimeTypes() const override;
// 获取指定模型索引列表的MIME数据
virtual QMimeData *mimeData(const QModelIndexList &indexes) const override; virtual QMimeData *mimeData(const QModelIndexList &indexes) const override;
// 处理MIME数据的拖放
virtual bool dropMimeData(const QMimeData *mime, Qt::DropAction action, int row, int column, virtual bool dropMimeData(const QMimeData *mime, Qt::DropAction action, int row, int column,
const QModelIndex &parent) override; const QModelIndex &parent) override;
// 检查指定模型索引的笔记是否具有标签
bool noteIsHaveTag(const QModelIndex &index) const; bool noteIsHaveTag(const QModelIndex &index) const;
// 检查指定模型索引的笔记是否为第一个被钉住的笔记
bool isFirstPinnedNote(const QModelIndex &index) const; bool isFirstPinnedNote(const QModelIndex &index) const;
// 检查指定模型索引的笔记是否为第一个未被钉住的笔记
bool isFirstUnpinnedNote(const QModelIndex &index) const; bool isFirstUnpinnedNote(const QModelIndex &index) const;
// 获取第一个被钉住的笔记的模型索引
QModelIndex getFirstPinnedNote() const; QModelIndex getFirstPinnedNote() const;
// 检查是否有被钉住的笔记 QModelIndex getFirstUnpinnedNote() const;
bool hasPinnedNote() const; bool hasPinnedNote() const;
// 设置指定模型索引列表的笔记的钉住状态
void setNotesIsPinned(const QModelIndexList &indexes, bool isPinned); void setNotesIsPinned(const QModelIndexList &indexes, bool isPinned);
private: private:
QVector<NodeData> m_noteList; // 笔记列表 QVector<NodeData> m_noteList;
QVector<NodeData> m_pinnedList; // 被钉住的笔记列表 QVector<NodeData> m_pinnedList;
ListViewInfo m_listViewInfo; // 列表视图信息 ListViewInfo m_listViewInfo;
// 更新被钉住的笔记的相对位置
void updatePinnedRelativePosition(); void updatePinnedRelativePosition();
// 检查是否在所有笔记中
bool isInAllNote() const; bool isInAllNote() const;
// 获取指定行的笔记数据的引用
NodeData &getRef(int row); NodeData &getRef(int row);
// 获取指定行的笔记数据的常量引用
const NodeData &getRef(int row) const; const NodeData &getRef(int row) const;
signals: signals:
// 行数发生变化时发出的信号
void rowCountChanged(); void rowCountChanged();
// 请求更新笔记的钉住状态时发出的信号
void requestUpdatePinned(int noteId, bool isPinned); void requestUpdatePinned(int noteId, bool isPinned);
// 请求更新笔记的相对位置时发出的信号
void requestUpdatePinnedRelPos(int noteId, int pos); void requestUpdatePinnedRelPos(int noteId, int pos);
// 请求更新笔记的相对位置(在所有笔记中)时发出的信号
void requestUpdatePinnedRelPosAN(int noteId, int pos); void requestUpdatePinnedRelPosAN(int noteId, int pos);
// 请求移除笔记时发出的信号
void requestRemoveNotes(QModelIndexList index); void requestRemoveNotes(QModelIndexList index);
// 行插入时发出的信号
void rowsInsertedC(const QModelIndexList &rows); void rowsInsertedC(const QModelIndexList &rows);
// 行即将被移动时发出的信号
void rowsAboutToBeMovedC(const QModelIndexList &source); void rowsAboutToBeMovedC(const QModelIndexList &source);
// 行移动后发出的信号
void rowsMovedC(const QModelIndexList &dest); void rowsMovedC(const QModelIndexList &dest);
// 请求关闭笔记编辑器时发出的信号
void requestCloseNoteEditor(const QModelIndexList &indexes); void requestCloseNoteEditor(const QModelIndexList &indexes);
// 请求打开笔记编辑器时发出的信号
void requestOpenNoteEditor(const QModelIndexList &indexes); void requestOpenNoteEditor(const QModelIndexList &indexes);
// 选择笔记时发出的信号
void selectNotes(const QModelIndexList &indexes); void selectNotes(const QModelIndexList &indexes);
// QAbstractItemModel interface // QAbstractItemModel interface
public: public:
// 移除行
bool removeRows(int row, int count, const QModelIndex &parent) override; bool removeRows(int row, int count, const QModelIndex &parent) override;
}; };

@ -1,47 +1,42 @@
#include "pushbuttontype.h"// 包含PushButtonType类的头文件 #include "pushbuttontype.h"
#include <QEvent>// 包含QEvent类的头文件QEvent是Qt中用于表示事件的类 #include <QEvent>
PushButtonType::PushButtonType(QWidget *parent)// PushButtonType类的构造函数 PushButtonType::PushButtonType(QWidget *parent) : QPushButton(parent) { }
: QPushButton(parent){}
// 调用QPushButton的构造函数并传入父窗口指针
bool PushButtonType::event(QEvent *event)// 重载event函数 bool PushButtonType::event(QEvent *event)
{ {
if (event->type() == QEvent::MouseButtonPress) {// 如果事件类型是鼠标按下 if (event->type() == QEvent::MouseButtonPress) {
setIcon(pressedIcon);// 设置按钮图标为按下状态下的图标 setIcon(pressedIcon);
} }
if (event->type() == QEvent::MouseButtonRelease) {// 如果事件类型是鼠标释放 if (event->type() == QEvent::MouseButtonRelease) {
if (underMouse()) { // 如果鼠标在按钮范围内 if (underMouse()) {
setIcon(hoveredIcon);// 设置按钮图标为鼠标悬停状态下的图标 setIcon(hoveredIcon);
} else { } else {
setIcon(normalIcon); // 设置按钮图标为正常状态下的图标 setIcon(normalIcon);
} }
} }
// 如果事件类型是鼠标进入
if (event->type() == QEvent::Enter) { if (event->type() == QEvent::Enter) {
setIcon(hoveredIcon); // 设置按钮图标为鼠标悬停状态下的图标 setIcon(hoveredIcon);
} }
// 如果事件类型是鼠标离开
// 设置按钮图标为正常状态下的图标
if (event->type() == QEvent::Leave) { if (event->type() == QEvent::Leave) {
setIcon(normalIcon); setIcon(normalIcon);
} }
// 调用QPushButton的event函数继续处理其他事件
return QPushButton::event(event); return QPushButton::event(event);
} }
// 设置按钮在按下状态下的图标
void PushButtonType::setPressedIcon(const QIcon &newPressedIcon) void PushButtonType::setPressedIcon(const QIcon &newPressedIcon)
{ {
pressedIcon = newPressedIcon;// 更新按下状态下的图标 pressedIcon = newPressedIcon;
} }
// 设置按钮在鼠标悬停状态下的图标
void PushButtonType::setHoveredIcon(const QIcon &newHoveredIcon) void PushButtonType::setHoveredIcon(const QIcon &newHoveredIcon)
{ {
hoveredIcon = newHoveredIcon;// 更新鼠标悬停状态下的图标 hoveredIcon = newHoveredIcon;
} }
// 设置按钮在正常状态下的图标
void PushButtonType::setNormalIcon(const QIcon &newNormalIcon) void PushButtonType::setNormalIcon(const QIcon &newNormalIcon)
{ {
normalIcon = newNormalIcon; // 更新正常状态下的图标 normalIcon = newNormalIcon;
setIcon(newNormalIcon); // 并将按钮图标设置为正常状态下的图标 setIcon(newNormalIcon);
} }

@ -1,25 +1,25 @@
#ifndef PUSHBUTTONTYPE_H #ifndef PUSHBUTTONTYPE_H
#define PUSHBUTTONTYPE_H #define PUSHBUTTONTYPE_H
#include <QPushButton> // 包含QPushButton类的头文件QPushButton是Qt中用于创建按钮的类 #include <QPushButton>
class PushButtonType : public QPushButton// 定义PushButtonType类继承自QPushButton class PushButtonType : public QPushButton
{ {
Q_OBJECT// 宏,用于支持信号和槽机制 Q_OBJECT
public: public:
explicit PushButtonType(QWidget *parent = nullptr);// 构造函数,允许传入父窗口指针,默认为空 explicit PushButtonType(QWidget *parent = nullptr);
void setNormalIcon(const QIcon &newNormalIcon); // 设置按钮在正常状态下的图标 void setNormalIcon(const QIcon &newNormalIcon);
void setHoveredIcon(const QIcon &newHoveredIcon);// 设置按钮在鼠标悬停状态下的图标 void setHoveredIcon(const QIcon &newHoveredIcon);
void setPressedIcon(const QIcon &newPressedIcon);// 设置按钮在鼠标按下状态下的图标 void setPressedIcon(const QIcon &newPressedIcon);
protected: protected:
bool event(QEvent *event) override;// 重载event函数用于处理各种事件 bool event(QEvent *event) override;
private: private:
QIcon normalIcon;// 私有成员变量,存储按钮在正常状态下的图标 QIcon normalIcon;
QIcon hoveredIcon;// 私有成员变量,存储按钮在鼠标悬停状态下的图标 QIcon hoveredIcon;
QIcon pressedIcon;// 私有成员变量,存储按钮在鼠标按下状态下的图标 QIcon pressedIcon;
}; };
#endif // PUSHBUTTONTYPE_H #endif // PUSHBUTTONTYPE_H

@ -1,22 +1,20 @@
#include "singleinstance.h"// 包含SingleInstance类的头文件 #include "singleinstance.h"
SingleInstance::SingleInstance(QObject *parent) : QObject(parent)// SingleInstance类的构造函数 SingleInstance::SingleInstance(QObject *parent) : QObject(parent)
{ {
// 连接QLocalServer的newConnection信号到一个lambda表达式槽
// 当有新连接时发出newInstance信号
connect(&m_server, &QLocalServer::newConnection, [this]() { emit newInstance(); }); connect(&m_server, &QLocalServer::newConnection, [this]() { emit newInstance(); });
} }
void SingleInstance::listen(const QString &name)// 监听指定名称的本地服务器 void SingleInstance::listen(const QString &name)
{ {
m_server.removeServer(name);// 移除已存在的同名服务器 m_server.removeServer(name);
m_server.listen(name);// 开始监听指定名称的本地服务器 m_server.listen(name);
} }
bool SingleInstance::hasPrevious(const QString &name)// 检查是否有其他实例正在运行 bool SingleInstance::hasPrevious(const QString &name)
{ {
QLocalSocket socket; // 创建一个本地套接字对象 QLocalSocket socket;
socket.connectToServer(name, QLocalSocket::ReadOnly);// 尝试连接到指定名称的服务器 socket.connectToServer(name, QLocalSocket::ReadOnly);
return socket.waitForConnected();// 等待连接结果 return socket.waitForConnected();
} }

@ -1,25 +1,25 @@
#ifndef SINGLEINSTANCE_H #ifndef SINGLEINSTANCE_H
#define SINGLEINSTANCE_H #define SINGLEINSTANCE_H
#include <QObject> // 包含QObject类的头文件QObject是所有Qt对象的基类 #include <QObject>
#include <QLocalServer> // 包含QLocalServer类的头文件用于创建本地服务器 #include <QLocalServer>
#include <QLocalSocket> // 包含QLocalSocket类的头文件用于创建本地套接字 #include <QLocalSocket>
class SingleInstance : public QObject// 定义SingleInstance类继承自QObject class SingleInstance : public QObject
{ {
Q_OBJECT// 宏,用于支持信号和槽机制 Q_OBJECT
public: public:
explicit SingleInstance(QObject *parent = 0);// 构造函数,允许传入父对象指针,默认为空 explicit SingleInstance(QObject *parent = 0);
void listen(const QString &name);// 监听指定名称的本地服务器 void listen(const QString &name);
bool hasPrevious(const QString &name);// 检查是否有其他实例正在运行 bool hasPrevious(const QString &name);
signals: signals:
void newInstance(); // 当有新实例连接时发出的信号 void newInstance();
private: private:
QLocalSocket *m_socket;// 私有成员变量,用于存储本地套接字指针 QLocalSocket *m_socket;
QLocalServer m_server;// 私有成员变量,用于存储本地服务器对象 QLocalServer m_server;
}; };
#endif // SINGLEINSTANCE_H #endif // SINGLEINSTANCE_H

@ -1,56 +1,56 @@
#include "tagdata.h" // 包含TagData类的头文件 #include "tagdata.h"
TagData::TagData()// TagData类的构造函数 TagData::TagData()
: m_id{ SpecialTagID::InvalidTagId }, m_relativePosition{ -1 }, m_childNotesCount{ 0 } : m_id{ SpecialTagID::InvalidTagId }, m_relativePosition{ -1 }, m_childNotesCount{ 0 }
{ // 初始化标签ID为无效值相对位置为-1子笔记数量为0 {
} }
int TagData::id() const int TagData::id() const
{ {
return m_id;// 返回标签ID return m_id;
} }
void TagData::setId(int newId) void TagData::setId(int newId)
{ {
m_id = newId;// 设置新的标签ID m_id = newId;
} }
const QString &TagData::name() const const QString &TagData::name() const
{ {
return m_name; // 返回标签名称 return m_name;
} }
void TagData::setName(const QString &newName) void TagData::setName(const QString &newName)
{ {
m_name = newName; // 设置新的标签名称 m_name = newName;
} }
const QString &TagData::color() const const QString &TagData::color() const
{ {
return m_color;// 返回标签颜色 return m_color;
} }
void TagData::setColor(const QString &newColor) void TagData::setColor(const QString &newColor)
{ {
m_color = newColor;// 设置新的标签颜色 m_color = newColor;
} }
int TagData::relativePosition() const int TagData::relativePosition() const
{ {
return m_relativePosition;// 返回标签的相对位置 return m_relativePosition;
} }
void TagData::setRelativePosition(int newRelativePosition) void TagData::setRelativePosition(int newRelativePosition)
{ {
m_relativePosition = newRelativePosition; // 设置新的标签相对位置 m_relativePosition = newRelativePosition;
} }
int TagData::childNotesCount() const int TagData::childNotesCount() const
{ {
return m_childNotesCount;// 返回标签下的子笔记数量 return m_childNotesCount;
} }
void TagData::setChildNotesCount(int newChildCount) void TagData::setChildNotesCount(int newChildCount)
{ {
m_childNotesCount = newChildCount; // 设置新的子笔记数量 m_childNotesCount = newChildCount;
} }

@ -1,43 +1,43 @@
#ifndef TAGDATA_H #ifndef TAGDATA_H
#define TAGDATA_H #define TAGDATA_H
#include <QString> // 包含QString类的头文件QString是Qt中用于处理字符串的类 #include <QString>
#include <QMetaClassInfo> // 包含QMetaClassInfo类的头文件用于元信息支持 #include <QMetaClassInfo>
namespace SpecialTagID {// 定义SpecialTagID命名空间用于特定的标签ID值 namespace SpecialTagID {
enum Value { enum Value {
InvalidTagId = -1, // 无效的标签ID值 InvalidTagId = -1,
}; };
} }
class TagData// 定义TagData类用于表示标签的数据 class TagData
{ {
public: public:
TagData();// 构造函数 TagData();
int id() const;// 获取标签ID int id() const;
void setId(int newId); // 设置标签ID void setId(int newId);
const QString &name() const;// 获取标签名称 const QString &name() const;
void setName(const QString &newName); // 设置标签名称 void setName(const QString &newName);
const QString &color() const;// 获取标签颜色 const QString &color() const;
void setColor(const QString &newColor); // 设置标签颜色 void setColor(const QString &newColor);
int relativePosition() const; // 获取标签的相对位置 int relativePosition() const;
void setRelativePosition(int newRelativePosition); // 设置标签的相对位置 void setRelativePosition(int newRelativePosition);
int childNotesCount() const; // 获取标签下的子笔记数量 int childNotesCount() const;
void setChildNotesCount(int newChildCount); // 设置标签下的子笔记数量 void setChildNotesCount(int newChildCount);
private: private:
int m_id; // 私有成员变量存储标签ID int m_id;
QString m_name; // 私有成员变量,存储标签名称 QString m_name;
QString m_color; // 私有成员变量,存储标签颜色 QString m_color;
int m_relativePosition; // 私有成员变量,存储标签的相对位置 int m_relativePosition;
int m_childNotesCount; // 私有成员变量,存储标签下的子笔记数量 int m_childNotesCount;
}; };
Q_DECLARE_METATYPE(TagData)// 声明TagData为元类型以便在信号和槽中使用 Q_DECLARE_METATYPE(TagData)
#endif // TAGDATA_H #endif // TAGDATA_H

@ -1,97 +1,92 @@
#include "taglistdelegate.h" // 引入自定义的委托头文件 #include "taglistdelegate.h"
#include "taglistmodel.h" // 引入自定义的模型头文件 #include "taglistmodel.h"
#include <QPainter> // 引入QPainter类用于绘制界面 #include <QPainter>
#include <QPainterPath> // 引入QPainterPath类用于绘制路径 #include <QPainterPath>
#include "fontloader.h" // 自定义的字体加载器 #include "fontloader.h"
// 构造函数,初始化委托的显示字体和标题字体
TagListDelegate::TagListDelegate(QObject *parent) TagListDelegate::TagListDelegate(QObject *parent)
: QStyledItemDelegate(parent), // 调用基类构造函数 : QStyledItemDelegate(parent),
#ifdef __APPLE__ // 如果是苹果系统 #ifdef __APPLE__
m_displayFont(QFont(QStringLiteral("SF Pro Text")).exactMatch() // 检查系统字体是否精确匹配 m_displayFont(QFont(QStringLiteral("SF Pro Text")).exactMatch()
? QStringLiteral("SF Pro Text") // 如果匹配,使用系统字体 ? QStringLiteral("SF Pro Text")
: QStringLiteral("Roboto")), // 否则使用Roboto字体 : QStringLiteral("Roboto")),
#elif _WIN32 // 如果是Windows系统 #elif _WIN32
m_displayFont(QFont(QStringLiteral("Segoe UI")).exactMatch() ? QStringLiteral("Segoe UI") m_displayFont(QFont(QStringLiteral("Segoe UI")).exactMatch() ? QStringLiteral("Segoe UI")
: QStringLiteral("Roboto")), : QStringLiteral("Roboto")),
#else // 其他系统 #else
m_displayFont(QStringLiteral("Roboto")), m_displayFont(QStringLiteral("Roboto")),
#endif #endif
#ifdef __APPLE__ // 如果是苹果系统 #ifdef __APPLE__
m_titleFont(m_displayFont, 13, QFont::DemiBold), // 设置标题字体为SF Pro Text大小13半粗 m_titleFont(m_displayFont, 13, QFont::DemiBold),
#else // 其他系统 #else
m_titleFont(m_displayFont, 10, QFont::DemiBold), // 设置标题字体为Roboto大小10半粗 m_titleFont(m_displayFont, 10, QFont::DemiBold),
#endif #endif
m_titleColor(26, 26, 26), // 设置标题颜色为深灰色 m_titleColor(26, 26, 26),
m_theme(Theme::Light) // 初始化主题为浅色 m_theme(Theme::Light)
{ {
} }
// 重写paint函数用于自定义绘制项
void TagListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, void TagListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const const QModelIndex &index) const
{ {
painter->setRenderHint(QPainter::Antialiasing); // 设置抗锯齿,使绘制更平滑 painter->setRenderHint(QPainter::Antialiasing);
auto name = index.data(TagListModel::NameRole).toString(); // 获取模型中的名称数据 auto name = index.data(TagListModel::NameRole).toString();
auto color = index.data(TagListModel::ColorRole).toString(); // 获取模型中的颜色数据 auto color = index.data(TagListModel::ColorRole).toString();
auto rect = option.rect; // 获取绘制区域 auto rect = option.rect;
rect.setHeight(20); // 设置绘制区域的高度 rect.setHeight(20);
QPainterPath path; // 创建路径对象 QPainterPath path;
path.addRoundedRect(rect, 10, 10); // 添加圆角矩形路径 path.addRoundedRect(rect, 10, 10);
if (m_theme == Theme::Dark) { // 如果主题是深色 if (m_theme == Theme::Dark) {
painter->fillPath(path, QColor(76, 85, 97)); // 填充深灰色背景 painter->fillPath(path, QColor(76, 85, 97));
} else { // 否则 } else {
painter->fillPath(path, QColor(218, 235, 248)); // 填充浅灰色背景 painter->fillPath(path, QColor(218, 235, 248));
} }
auto iconRect = auto iconRect = QRect(rect.x() + 5, rect.y() + (rect.height() - 12) / 2, 12, 12);
QRect(rect.x() + 5, rect.y() + (rect.height() - 12) / 2, 12, 12); // 设置图标区域 painter->setPen(QColor(color));
painter->setPen(QColor(color)); // 设置画笔颜色为模型中的颜色 #ifdef __APPLE__
#ifdef __APPLE__ // 如果是苹果系统 int iconPointSizeOffset = 0;
int iconPointSizeOffset = 0; // 图标点大小偏移量为0 #else
#else // 其他系统 int iconPointSizeOffset = -4;
int iconPointSizeOffset = -4; // 图标点大小偏移量为-4
#endif #endif
painter->setFont(FontLoader::getInstance().loadFont("Font Awesome 6 Free Solid", "", // 加载字体 painter->setFont(FontLoader::getInstance().loadFont("Font Awesome 6 Free Solid", "",
12 + iconPointSizeOffset)); 12 + iconPointSizeOffset));
painter->drawText(iconRect, u8"\uf111"); // 绘制图标(一个圆圈) painter->drawText(iconRect, u8"\uf111"); // fa-circle
painter->setBrush(m_titleColor); // 设置画刷颜色为标题颜色 painter->setBrush(m_titleColor);
painter->setPen(m_titleColor); // 设置画笔颜色为标题颜色 painter->setPen(m_titleColor);
QRect nameRect(rect); // 创建名称区域 QRect nameRect(rect);
nameRect.setLeft(iconRect.x() + iconRect.width() + 5); // 设置名称区域的左边距 nameRect.setLeft(iconRect.x() + iconRect.width() + 5);
painter->setFont(m_titleFont); // 设置字体为标题字体 painter->setFont(m_titleFont);
painter->drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, name); // 绘制名称文本 painter->drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, name);
} }
// 重写sizeHint函数用于自定义项的大小
QSize TagListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const QSize TagListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{ {
Q_UNUSED(option); // 声明option参数未使用 Q_UNUSED(option);
QSize size; // 创建大小对象 QSize size;
size.setHeight(20); // 设置高度为20 size.setHeight(20);
auto name = index.data(TagListModel::NameRole).toString(); // 获取模型中的名称数据 auto name = index.data(TagListModel::NameRole).toString();
QFontMetrics fmName(m_titleFont); // 创建字体度量对象 QFontMetrics fmName(m_titleFont);
QRect fmRectName = fmName.boundingRect(name); // 获取名称文本的边界矩形 QRect fmRectName = fmName.boundingRect(name);
size.setWidth(5 + 12 + 5 + fmRectName.width() + 7); // 计算宽度 size.setWidth(5 + 12 + 5 + fmRectName.width() + 7);
return size; // 返回大小 return size;
} }
// 设置主题,并根据主题改变标题颜色
void TagListDelegate::setTheme(Theme::Value theme) void TagListDelegate::setTheme(Theme::Value theme)
{ {
m_theme = theme; // 设置主题 m_theme = theme;
switch (m_theme) { // 根据主题进行条件编译 switch (m_theme) {
case Theme::Light: { case Theme::Light: {
m_titleColor = QColor(26, 26, 26); // 浅色主题下,标题颜色为深灰色 m_titleColor = QColor(26, 26, 26);
break; break;
} }
case Theme::Dark: { case Theme::Dark: {
m_titleColor = QColor(212, 212, 212); // 深色主题下,标题颜色为浅灰色 m_titleColor = QColor(212, 212, 212);
break; break;
} }
case Theme::Sepia: { case Theme::Sepia: {
m_titleColor = QColor(26, 26, 26); // 棕褐色主题下,标题颜色为深灰色 m_titleColor = QColor(26, 26, 26);
break; break;
} }
} }

@ -1,31 +1,30 @@
#ifndef TAGLISTDELEGATE_H // 头文件保护,防止重复包含 #ifndef TAGLISTDELEGATE_H
#define TAGLISTDELEGATE_H #define TAGLISTDELEGATE_H
#include <QStyledItemDelegate> // 引入QStyledItemDelegate类 #include <QStyledItemDelegate>
#include "editorsettingsoptions.h" // 自定义的编辑器设置选项头文件 #include "editorsettingsoptions.h"
class TagListDelegate class TagListDelegate : public QStyledItemDelegate
: public QStyledItemDelegate // 声明TagListDelegate类继承自QStyledItemDelegate
{ {
Q_OBJECT // 宏用于支持Qt的信号和槽机制 Q_OBJECT
public : explicit TagListDelegate(QObject *parent = nullptr); // 构造函数声明 public:
explicit TagListDelegate(QObject *parent = nullptr);
// QAbstractItemDelegate接口 // QAbstractItemDelegate interface
public: public:
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, virtual void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override; // 重写paint函数用于绘制项 const QModelIndex &index) const override;
virtual QSize virtual QSize sizeHint(const QStyleOptionViewItem &option,
sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
const QModelIndex &index) const override; // 重写sizeHint函数用于获取项的大小 void setTheme(Theme::Value theme);
void setTheme(Theme::Value theme); // 设置主题的函数声明
private: private:
QString m_displayFont; // 显示字体的字符串 QString m_displayFont;
QFont m_titleFont; // 标题字体 QFont m_titleFont;
QFont m_titleSelectedFont; // 选中状态下的标题字体(在提供的代码中未使用) QFont m_titleSelectedFont;
QFont m_dateFont; // 日期字体(在提供的代码中未使用) QFont m_dateFont;
QColor m_titleColor; // 标题颜色 QColor m_titleColor;
Theme::Value m_theme; // 主题枚举值 Theme::Value m_theme;
}; };
#endif // TAGLISTDELEGATE_H #endif // TAGLISTDELEGATE_H

@ -1,87 +1,79 @@
#include "taglistmodel.h" #include "taglistmodel.h"
#include "tagpool.h"//获取、检查和更新标签数据 #include "tagpool.h"
#include <QDebug>//用于调试输出 #include <QDebug>
#include "nodepath.h" #include "nodepath.h"
// TagListModel类的构造函数
TagListModel::TagListModel(QObject *parent) : QAbstractListModel(parent), m_tagPool{ nullptr } { } TagListModel::TagListModel(QObject *parent) : QAbstractListModel(parent), m_tagPool{ nullptr } { }
// 初始化父类QAbstractListModel并将m_tagPool初始化为nullptr
// 设置标签池的函数
void TagListModel::setTagPool(TagPool *tagPool) void TagListModel::setTagPool(TagPool *tagPool)
{ {
beginResetModel(); // 开始重置模型,通知视图模型即将改变 beginResetModel();
m_tagPool = tagPool; // 设置标签池指针 m_tagPool = tagPool;
connect(tagPool, &TagPool::dataReset, this, connect(tagPool, &TagPool::dataReset, this, [this] {
[this] { // 连接标签池的dataReset信号到一个lambda表达式槽 beginResetModel();
beginResetModel(); // 标签池数据重置时,开始重置模型 updateTagData();
updateTagData(); // 更新标签数据 endResetModel();
endResetModel(); // 结束重置模型,通知视图模型已改变
}); });
endResetModel(); // 结束重置模型 endResetModel();
} }
// 设置模型数据的函数
void TagListModel::setModelData(const QSet<int> &data) void TagListModel::setModelData(const QSet<int> &data)
{ {
if (!m_tagPool) { // 如果标签池未初始化 if (!m_tagPool) {
qDebug() << __FUNCTION__ << "Tag pool is not init yet"; // 输出调试信息 qDebug() << __FUNCTION__ << "Tag pool is not init yet";
return; return;
} }
beginResetModel(); // 开始重置模型 beginResetModel();
m_ids = data; // 设置标签ID集合 m_ids = data;
updateTagData(); // 更新标签数据 updateTagData();
endResetModel(); // 结束重置模型 endResetModel();
} }
// 添加标签的函数
void TagListModel::addTag(int tagId) void TagListModel::addTag(int tagId)
{ {
if (!m_tagPool) { // 如果标签池未初始化 if (!m_tagPool) {
qDebug() << __FUNCTION__ << "Tag pool is not init yet"; // 输出调试信息 qDebug() << __FUNCTION__ << "Tag pool is not init yet";
return; return;
} }
if (m_tagPool->contains(tagId)) { // 如果标签池包含该标签ID if (m_tagPool->contains(tagId)) {
beginInsertRows(QModelIndex(), rowCount(), rowCount()); // 开始插入行,通知视图即将插入新行 beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_data.append(m_tagPool->getTag(tagId)); // 将标签添加到数据向量中 m_data.append(m_tagPool->getTag(tagId));
endInsertRows(); // 结束插入行,通知视图已插入新行 endInsertRows();
} else { } else {
qDebug() << __FUNCTION__ << "Tag is not in pool:" << tagId; // 输出调试信息 qDebug() << __FUNCTION__ << "Tag is not in pool:" << tagId;
} }
} }
// 重载rowCount函数返回模型的行数
int TagListModel::rowCount(const QModelIndex &parent) const int TagListModel::rowCount(const QModelIndex &parent) const
{ {
Q_UNUSED(parent); // 声明parent参数未使用避免编译器警告 Q_UNUSED(parent);
return m_data.count(); // 返回标签数据向量的大小 return m_data.count();
} }
QVariant TagListModel::data(const QModelIndex &index, int role) const// 返回指定索引和角色的数据 QVariant TagListModel::data(const QModelIndex &index, int role) const
{ {
if (!index.isValid()) { // 如果索引无效 if (!index.isValid()) {
return QVariant(); // 返回空的QVariant对象 return QVariant();
} }
const TagData &tag = m_data[index.row()]; // 获取对应索引行的标签数据 const TagData &tag = m_data[index.row()];
if (role == IdRole) { // 如果角色是IdRole if (role == IdRole) {
return tag.id(); // 返回标签的ID return tag.id();
} else if (role == NameRole) { // 如果角色是NameRole } else if (role == NameRole) {
return tag.name(); // 返回标签的名称 return tag.name();
} else if (role == ColorRole) { // 如果角色是ColorRole } else if (role == ColorRole) {
return tag.color(); // 返回标签的颜色 return tag.color();
} }
return QVariant(); // 如果角色不匹配返回空的QVariant对象 return QVariant();
} }
void TagListModel::updateTagData()
void TagListModel::updateTagData()// 更新标签数据的函数
{ {
m_data.clear(); // 清空标签数据向量 m_data.clear();
for (const auto &id : qAsConst(m_ids)) { // 遍历标签ID集合 for (const auto &id : qAsConst(m_ids)) {
if (m_tagPool->contains(id)) { // 如果标签池包含该标签ID if (m_tagPool->contains(id)) {
m_data.append(m_tagPool->getTag(id)); // 将标签添加到数据向量中 m_data.append(m_tagPool->getTag(id));
} else { } else {
qDebug() << __FUNCTION__ << "Tag is not in pool:" << id; // 输出调试信息 qDebug() << __FUNCTION__ << "Tag is not in pool:" << id;
} }
} }
} }

@ -1,47 +1,28 @@
#ifndef TAGLISTMODEL_H #ifndef TAGLISTMODEL_H
#define TAGLISTMODEL_H #define TAGLISTMODEL_H
#include <QAbstractListModel> // 包含QAbstractListModel类的头文件QAbstractListModel是Qt中用于实现自定义列表模型的基类 #include <QAbstractListModel>
#include <QSet> // 包含QSet类的头文件QSet是一个集合容器用于存储不重复的元素 #include <QSet>
#include "tagdata.h" #include "tagdata.h"
class TagPool; class TagPool;
// 定义TagListModel类继承自QAbstractListModel
class TagListModel : public QAbstractListModel class TagListModel : public QAbstractListModel
{ {
Q_OBJECT // 宏,用于支持信号和槽机制 Q_OBJECT
public: public:
// 定义角色枚举,用于指定数据模型中的不同角色 enum TagListRoles { IdRole = Qt::UserRole + 1, NameRole, ColorRole };
enum TagListRoles {
IdRole = Qt::UserRole + 1,
NameRole,
ColorRole
};
// 构造函数,允许传入父对象指针,默认为空
TagListModel(QObject *parent = nullptr); TagListModel(QObject *parent = nullptr);
// 设置标签池的函数接受一个TagPool指针
void setTagPool(TagPool *tagPool); void setTagPool(TagPool *tagPool);
// 设置模型数据的函数接受一个QSet<int>类型的集合
void setModelData(const QSet<int> &data); void setModelData(const QSet<int> &data);
// 添加标签的函数接受一个标签ID
void addTag(int tagId); void addTag(int tagId);
// 重载rowCount函数返回模型的行数
int rowCount(const QModelIndex &parent = QModelIndex()) const; int rowCount(const QModelIndex &parent = QModelIndex()) const;
// 重载data函数返回指定索引和角色的数据
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
private: private:
TagPool *m_tagPool; // 私有成员变量,用于存储标签池指针 TagPool *m_tagPool;
QVector<TagData> m_data; // 私有成员变量,用于存储标签数据的向量 QVector<TagData> m_data;
QSet<int> m_ids; // 私有成员变量用于存储标签ID的集合 QSet<int> m_ids;
// 更新标签数据的函数
void updateTagData(); void updateTagData();
}; };

@ -1,93 +1,86 @@
#include "taglistview.h" #include "taglistview.h"
#include <QFile> // 包含QFile类的头文件用于文件操作 #include <QFile>
#include <QDebug> // 包含QDebug类的头文件用于调试输出 #include <QDebug>
#include <QMouseEvent> // 包含QMouseEvent类的头文件用于处理鼠标事件 #include <QMouseEvent>
// TagListView类的构造函数
TagListView::TagListView(QWidget *parent) : QListView(parent) TagListView::TagListView(QWidget *parent) : QListView(parent)
{ {
setFlow(QListView::LeftToRight); // 设置列表项的排列方向为从左到右 setFlow(QListView::LeftToRight);
setSpacing(3); // 设置列表项之间的间距为3像素 setSpacing(3);
setWrapping(true); // 设置列表项可以自动换行 setWrapping(true);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // 设置水平滚动条始终不显示 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QFile file(":/styles/taglistview.css"); // 创建QFile对象用于读取CSS样式文件 QFile file(":/styles/taglistview.css");
file.open(QFile::ReadOnly); // 打开文件,设置为只读模式 file.open(QFile::ReadOnly);
setStyleSheet(file.readAll()); // 读取文件内容,并将其设置为视图的样式表 setStyleSheet(file.readAll());
setTheme(Theme::Light); // 设置视图的主题为浅色主题 setTheme(Theme::Light);
} }
// 设置主题的函数
void TagListView::setTheme(Theme::Value theme) void TagListView::setTheme(Theme::Value theme)
{ {
setCSSThemeAndUpdate(this, theme); // 调用setCSSThemeAndUpdate函数更新视图的CSS样式和主题 setCSSThemeAndUpdate(this, theme);
} }
// 设置背景颜色的函数
void TagListView::setBackground(const QColor color) void TagListView::setBackground(const QColor color)
{ {
if (m_backgroundColor != color) { // 如果传入的颜色与当前背景颜色不同 if (m_backgroundColor != color) {
m_backgroundColor = color; // 更新背景颜色 m_backgroundColor = color;
QString ss = QStringLiteral( // 定义一个字符串用于存储CSS样式 QString ss = QStringLiteral(
R"(QListView { background: %1; } )" // 设置QListView的背景颜色 R"(QListView { background: %1; } )"
R"(QScrollBar::handle:vertical:hover { background: rgb(170, 170, 171); } )" // 设置垂直滚动条手柄在鼠标悬停时的背景颜色 R"(QScrollBar::handle:vertical:hover { background: rgb(170, 170, 171); } )"
R"(QScrollBar::handle:vertical:pressed { background: rgb(149, 149, 149); } )" // 设置垂直滚动条手柄在按下时的背景颜色 R"(QScrollBar::handle:vertical:pressed { background: rgb(149, 149, 149); } )"
R"(QScrollBar::handle:vertical { border-radius: 4px; background: rgb(188, 188, 188); min-height: 20px; } )" // 设置垂直滚动条手柄的样式 R"(QScrollBar::handle:vertical { border-radius: 4px; background: rgb(188, 188, 188); min-height: 20px; } )"
R"(QScrollBar::vertical {border-radius: 4px; width: 8px; color: rgba(255, 255, 255,0);} )" // 设置垂直滚动条的样式 R"(QScrollBar::vertical {border-radius: 4px; width: 8px; color: rgba(255, 255, 255,0);} )"
R"(QScrollBar {margin: 0; background: transparent;} )" // 设置滚动条的样式 R"(QScrollBar {margin: 0; background: transparent;} )"
R"(QScrollBar:hover { background-color: rgb(217, 217, 217);})" // 设置滚动条在鼠标悬停时的背景颜色 R"(QScrollBar:hover { background-color: rgb(217, 217, 217);})"
R"(QScrollBar::add-line:vertical { width:0px; height: 0px; subcontrol-position: bottom; subcontrol-origin: margin; } )" // 设置垂直滚动条增加按钮的样式 R"(QScrollBar::add-line:vertical { width:0px; height: 0px; subcontrol-position: bottom; subcontrol-origin: margin; } )"
R"(QScrollBar::sub-line:vertical { width:0px; height: 0px; subcontrol-position: top; subcontrol-origin: margin; })"); // 设置垂直滚动条减少按钮的样式 R"(QScrollBar::sub-line:vertical { width:0px; height: 0px; subcontrol-position: top; subcontrol-origin: margin; })");
setStyleSheet( setStyleSheet(ss.arg(m_backgroundColor.name()));
ss.arg(m_backgroundColor
.name())); // 将背景颜色插入到样式字符串中,并将其设置为视图的样式表
} }
} }
// 重载reset函数
void TagListView::reset() void TagListView::reset()
{ {
QListView::reset(); // 调用基类的reset函数 QListView::reset();
auto sz = sizeHint(); // 获取视图的尺寸提示 auto sz = sizeHint();
if (!model() || model()->rowCount() == 0) { // 如果视图没有模型或者模型的行数为0 if (!model() || model()->rowCount() == 0) {
sz.setHeight(0); // 将尺寸提示的高度设置为0 sz.setHeight(0);
} else { } else {
auto firstIndex = model()->index(0, 0); // 获取模型的第一个索引 auto firstIndex = model()->index(0, 0);
auto lastIndex = model()->index(model()->rowCount() - 1, 0); // 获取模型的最后一个索引 auto lastIndex = model()->index(model()->rowCount() - 1, 0);
auto fr = visualRect(firstIndex); // 获取第一个索引的可视矩形 auto fr = visualRect(firstIndex);
fr.setBottom(visualRect(lastIndex) fr.setBottom(visualRect(lastIndex).bottom());
.bottom()); // 将可视矩形的底部设置为最后一个索引的可视矩形的底部 if (fr.height() < 80) {
if (fr.height() < 80) { // 如果可视矩形的高度小于80像素 sz.setHeight(fr.height() + 10);
sz.setHeight(fr.height() + 10); // 将尺寸提示的高度设置为可视矩形的高度加上10像素
} else { } else {
sz.setHeight(80); // 否则将尺寸提示的高度设置为80像素 sz.setHeight(80);
} }
} }
setFixedHeight(sz.height()); // 将视图的高度设置为尺寸提示的高度 setFixedHeight(sz.height());
} }
void TagListView::resizeEvent(QResizeEvent *event) void TagListView::resizeEvent(QResizeEvent *event)
{ {
QListView::resizeEvent(event); // 调用基类的resizeEvent函数 QListView::resizeEvent(event);
setWrapping(true); // 设置列表项可以自动换行 setWrapping(true);
} }
void TagListView::mousePressEvent(QMouseEvent *event) void TagListView::mousePressEvent(QMouseEvent *event)
{ {
event->ignore();// 忽略鼠标按下事件 event->ignore();
} }
void TagListView::mouseReleaseEvent(QMouseEvent *event) void TagListView::mouseReleaseEvent(QMouseEvent *event)
{ {
event->ignore();// 忽略鼠标释放事件 event->ignore();
} }
void TagListView::mouseDoubleClickEvent(QMouseEvent *event) void TagListView::mouseDoubleClickEvent(QMouseEvent *event)
{ {
event->ignore();// 忽略鼠标双击事件 event->ignore();
} }
void TagListView::mouseMoveEvent(QMouseEvent *event) void TagListView::mouseMoveEvent(QMouseEvent *event)
{ {
event->ignore();// 忽略鼠标移动事件 event->ignore();
} }

@ -1,29 +1,28 @@
#pragma once #pragma once
#include <QListView>// 包含QListView的定义QListView是Qt框架中的一个列表视图控件 #include <QListView>
#include "editorsettingsoptions.h" #include "editorsettingsoptions.h"
class TagListView : public QListView// 定义TagListView类继承自QListView class TagListView : public QListView
{ {
Q_OBJECT// 宏,用于支持信号和槽机制 Q_OBJECT
public: public:
explicit TagListView(QWidget *parent = nullptr); // 构造函数,允许传入父窗口指针,默认为空 explicit TagListView(QWidget *parent = nullptr);
void setTheme(Theme::Value theme); // 设置主题的函数接受Theme枚举类型中的一个值 void setTheme(Theme::Value theme);
void setBackground(const QColor color);// 设置背景颜色的方法接受一个QColor对象 void setBackground(const QColor color);
signals: signals:
// QAbstractItemView interface QAbstractItemView接口的信号声明 // QAbstractItemView interface
public slots: public slots:
virtual void reset() override;// 重载reset函数用于重置视图状态 virtual void reset() override;
// QWidget interface 下面的函数重载自QWidget接口 // QWidget interface
protected: protected:
virtual void resizeEvent(QResizeEvent *event) override;// 重载resizeEvent函数处理窗口大小变化事件 virtual void resizeEvent(QResizeEvent *event) override;
virtual void mousePressEvent(QMouseEvent *event) override;// 重载mousePressEvent函数处理鼠标按下事件 virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;// 重载mouseReleaseEvent函数处理鼠标释放事件 virtual void mouseReleaseEvent(QMouseEvent *event) override;
virtual void mouseDoubleClickEvent( virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
QMouseEvent *event) override; // 重载mouseDoubleClickEvent函数处理鼠标双击事件 virtual void mouseMoveEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;// 重载mouseMoveEvent函数处理鼠标移动事件
private: private:
QColor m_backgroundColor;// 私有成员变量,用于存储背景颜色 QColor m_backgroundColor;
}; };

@ -1,7 +1,6 @@
#include "tagpool.h" #include "tagpool.h"
#include "dbmanager.h" #include "dbmanager.h"
// TagPool类的构造函数用于初始化数据库管理器并连接信号与槽
TagPool::TagPool(DBManager *dbManager, QObject *parent) : QObject(parent), m_dbManager{ dbManager } TagPool::TagPool(DBManager *dbManager, QObject *parent) : QObject(parent), m_dbManager{ dbManager }
{ {
connect( connect(
@ -23,53 +22,45 @@ TagPool::TagPool(DBManager *dbManager, QObject *parent) : QObject(parent), m_dbM
Qt::QueuedConnection); Qt::QueuedConnection);
} }
// 设置标签池的新数据
void TagPool::setTagPool(const QMap<int, TagData> &newPool) void TagPool::setTagPool(const QMap<int, TagData> &newPool)
{ {
m_pool = newPool; m_pool = newPool;
emit dataReset(); emit dataReset();
} }
// 删除指定ID的标签
void TagPool::onTagDeleted(int id) void TagPool::onTagDeleted(int id)
{ {
m_pool.remove(id); m_pool.remove(id);
emit tagDeleted(id); emit tagDeleted(id);
} }
// 添加新的标签
void TagPool::onTagAdded(const TagData &tag) void TagPool::onTagAdded(const TagData &tag)
{ {
m_pool[tag.id()] = tag; m_pool[tag.id()] = tag;
} }
// 重命名指定ID的标签
void TagPool::onTagRenamed(int id, const QString &newName) void TagPool::onTagRenamed(int id, const QString &newName)
{ {
m_pool[id].setName(newName); m_pool[id].setName(newName);
emit dataUpdated(id); emit dataUpdated(id);
} }
// 修改指定ID的标签颜色
void TagPool::onTagColorChanged(int id, const QString &newColor) void TagPool::onTagColorChanged(int id, const QString &newColor)
{ {
m_pool[id].setColor(newColor); m_pool[id].setColor(newColor);
emit dataUpdated(id); emit dataUpdated(id);
} }
// 获取指定ID的标签数据
TagData TagPool::getTag(int id) const TagData TagPool::getTag(int id) const
{ {
return m_pool[id]; return m_pool[id];
} }
// 检查标签池中是否包含指定ID的标签
bool TagPool::contains(int id) const bool TagPool::contains(int id) const
{ {
return m_pool.contains(id); return m_pool.contains(id);
} }
// 获取标签池中所有标签的ID列表
QList<int> TagPool::tagIds() const QList<int> TagPool::tagIds() const
{ {
return m_pool.keys(); return m_pool.keys();

@ -7,52 +7,31 @@
class DBManager; class DBManager;
// 标签池类,管理标签数据
class TagPool : public QObject class TagPool : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
// 构造函数,初始化标签池和数据库管理器
explicit TagPool(DBManager *dbManager, QObject *parent = nullptr); explicit TagPool(DBManager *dbManager, QObject *parent = nullptr);
// 获取指定ID的标签数据
TagData getTag(int id) const; TagData getTag(int id) const;
// 检查是否包含指定ID的标签
bool contains(int id) const; bool contains(int id) const;
// 获取所有标签的ID列表
QList<int> tagIds() const; QList<int> tagIds() const;
signals: signals:
// 数据重置信号
void dataReset(); void dataReset();
// 数据更新信号携带更新的标签ID
void dataUpdated(int tagId); void dataUpdated(int tagId);
// 标签删除信号携带删除的标签ID
void tagDeleted(int tagId); void tagDeleted(int tagId);
private slots: private slots:
// 标签删除的槽函数
void onTagDeleted(int id); void onTagDeleted(int id);
// 标签添加的槽函数
void onTagAdded(const TagData &tag); void onTagAdded(const TagData &tag);
// 标签重命名的槽函数
void onTagRenamed(int id, const QString &newName); void onTagRenamed(int id, const QString &newName);
// 标签颜色改变的槽函数
void onTagColorChanged(int id, const QString &newColor); void onTagColorChanged(int id, const QString &newColor);
private: private:
QMap<int, TagData> m_pool; // 标签数据池 QMap<int, TagData> m_pool;
DBManager *m_dbManager; // 数据库管理器指针 DBManager *m_dbManager;
// 设置标签池
void setTagPool(const QMap<int, TagData> &newPool); void setTagPool(const QMap<int, TagData> &newPool);
}; };
#endif // TAGPOOL_H void onTagRenamed(int id, const QString &newName); #endif // TAGPOOL_H
// 标签颜色改变的槽函数
void onTagColorChanged(int id, const QString &newColor);
private:
QMap<int, TagData> m_pool; // 标签数据池
DBManager *m_dbManager; // 数据库管理器指针
// 设置标签

@ -1,25 +1,23 @@
#include "tagtreedelegateeditor.h" #include "tagtreedelegateeditor.h"
#include <QHBoxLayout> // 包含水平布局类 #include <QHBoxLayout>
#include <QLabel> // 包含标签类 #include <QLabel>
#include <QPainter> // 包含用于绘制的类 #include <QPainter>
#include <QDebug> // 包含用于调试输出的类 #include <QDebug>
#include <QTreeView> // 包含树形视图类 #include <QTreeView>
#include <QMouseEvent> // 包含鼠标事件类 #include <QMouseEvent>
#include "pushbuttontype.h" // 包含自定义按钮类型 #include "pushbuttontype.h"
#include "nodetreemodel.h" // 包含节点树模型类 #include "nodetreemodel.h"
#include "nodetreeview.h" // 包含节点树视图类 #include "nodetreeview.h"
#include "labeledittype.h" // 包含自定义标签编辑类型 #include "labeledittype.h"
#include "notelistview.h" // 包含笔记列表视图类 #include "notelistview.h"
#include "fontloader.h" // 包含字体加载器类 #include "fontloader.h"
// TagTreeDelegateEditor类的构造函数
TagTreeDelegateEditor::TagTreeDelegateEditor(QTreeView *view, const QStyleOptionViewItem &option, TagTreeDelegateEditor::TagTreeDelegateEditor(QTreeView *view, const QStyleOptionViewItem &option,
const QModelIndex &index, QListView *listView, const QModelIndex &index, QListView *listView,
QWidget *parent) QWidget *parent)
: QWidget(parent), // 调用父类构造函数 : QWidget(parent),
m_option(option), // 保存样式选项 m_option(option),
m_index(index), // 保存模型索引 m_index(index),
// 根据操作系统设置默认字体
#ifdef __APPLE__ #ifdef __APPLE__
m_displayFont(QFont(QStringLiteral("SF Pro Text")).exactMatch() m_displayFont(QFont(QStringLiteral("SF Pro Text")).exactMatch()
? QStringLiteral("SF Pro Text") ? QStringLiteral("SF Pro Text")
@ -30,62 +28,50 @@ TagTreeDelegateEditor::TagTreeDelegateEditor(QTreeView *view, const QStyleOption
#else #else
m_displayFont(QStringLiteral("Roboto")), m_displayFont(QStringLiteral("Roboto")),
#endif #endif
// 根据操作系统设置标题字体
#ifdef __APPLE__ #ifdef __APPLE__
m_titleFont(m_displayFont, 13, QFont::DemiBold), m_titleFont(m_displayFont, 13, QFont::DemiBold),
#else #else
m_titleFont(m_displayFont, 10, QFont::DemiBold), m_titleFont(m_displayFont, 10, QFont::DemiBold),
#endif #endif
m_titleColor(26, 26, 26), // 设置标题颜色 m_titleColor(26, 26, 26),
m_titleSelectedColor(255, 255, 255), // 设置标题选中颜色 m_titleSelectedColor(255, 255, 255),
m_activeColor(68, 138, 201), // 设置活动颜色 m_activeColor(68, 138, 201),
m_hoverColor(207, 207, 207), // 设置悬停颜色 m_hoverColor(207, 207, 207),
m_view(view), // 保存树形视图指针 m_view(view),
m_listView(listView) // 保存列表视图指针 m_listView(listView)
{ {
// 设置布局边距
setContentsMargins(0, 0, 0, 0); setContentsMargins(0, 0, 0, 0);
auto layout = new QHBoxLayout(this); // 创建水平布局 auto layout = new QHBoxLayout(this);
layout->setContentsMargins(22, 0, 0, 0); // 设置布局边距 layout->setContentsMargins(22, 0, 0, 0);
layout->setSpacing(5); // 设置布局间距 layout->setSpacing(5);
setLayout(layout); // 设置布局 setLayout(layout);
layout->addSpacing(27); // 添加间距 layout->addSpacing(27);
// 创建标签编辑器
m_label = new LabelEditType(this); m_label = new LabelEditType(this);
m_label->setFont(m_titleFont); // 设置标签字体 m_label->setFont(m_titleFont);
// 设置标签大小策略
QSizePolicy labelPolicy; QSizePolicy labelPolicy;
labelPolicy.setVerticalPolicy(QSizePolicy::Expanding); labelPolicy.setVerticalPolicy(QSizePolicy::Expanding);
labelPolicy.setHorizontalPolicy(QSizePolicy::Expanding); labelPolicy.setHorizontalPolicy(QSizePolicy::Expanding);
m_label->setSizePolicy(labelPolicy); m_label->setSizePolicy(labelPolicy);
m_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); // 设置标签对齐方式 m_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
// 连接信号和槽
connect(m_label, &LabelEditType::editingStarted, this, [this] { connect(m_label, &LabelEditType::editingStarted, this, [this] {
auto tree_view = dynamic_cast<NodeTreeView *>(m_view); auto tree_view = dynamic_cast<NodeTreeView *>(m_view);
tree_view->setIsEditing(true); // 设置树形视图为编辑状态 tree_view->setIsEditing(true);
}); });
connect(m_label, &LabelEditType::editingFinished, this, [this](const QString &label) { connect(m_label, &LabelEditType::editingFinished, this, [this](const QString &label) {
auto tree_view = dynamic_cast<NodeTreeView *>(m_view); auto tree_view = dynamic_cast<NodeTreeView *>(m_view);
tree_view->onRenameTagFinished(label); // 完成重命名操作 tree_view->onRenameTagFinished(label);
tree_view->setIsEditing(false); // 设置树形视图为非编辑状态 tree_view->setIsEditing(false);
}); });
connect(dynamic_cast<NodeTreeView *>(m_view), &NodeTreeView::renameTagRequested, m_label, connect(dynamic_cast<NodeTreeView *>(m_view), &NodeTreeView::renameTagRequested, m_label,
&LabelEditType::openEditor); // 连接重命名请求信号 &LabelEditType::openEditor);
layout->addWidget(m_label); // 将标签添加到布局中 layout->addWidget(m_label);
// 创建上下文按钮
m_contextButton = new PushButtonType(parent); m_contextButton = new PushButtonType(parent);
m_contextButton->setMaximumSize({ 33, 25 }); // 设置按钮最大尺寸 m_contextButton->setMaximumSize({ 33, 25 });
m_contextButton->setMinimumSize({ 33, 25 }); // 设置按钮最小尺寸 m_contextButton->setMinimumSize({ 33, 25 });
m_contextButton->setCursor(QCursor(Qt::PointingHandCursor)); // 设置鼠标样式 m_contextButton->setCursor(QCursor(Qt::PointingHandCursor));
m_contextButton->setFocusPolicy(Qt::TabFocus); // 设置焦点策略 m_contextButton->setFocusPolicy(Qt::TabFocus);
m_contextButton->setIconSize(QSize(16, 16)); // 设置图标尺寸 m_contextButton->setIconSize(QSize(16, 16));
// 根据选中状态设置按钮样式
if (m_view->selectionModel()->
// ... 继续上面的代码
// 根据选中状态设置按钮样式
if (m_view->selectionModel()->isSelected(m_index)) { if (m_view->selectionModel()->isSelected(m_index)) {
m_contextButton->setStyleSheet(QStringLiteral(R"(QPushButton { )" m_contextButton->setStyleSheet(QStringLiteral(R"(QPushButton { )"
R"( border: none; )" R"( border: none; )"
@ -110,7 +96,6 @@ if (m_view->selectionModel()->isSelected(m_index)) {
R"(})")); R"(})"));
} }
// 根据操作系统设置字体大小偏移
#ifdef __APPLE__ #ifdef __APPLE__
int pointSizeOffset = 0; int pointSizeOffset = 0;
#else #else
@ -118,33 +103,27 @@ int pointSizeOffset = -4;
#endif #endif
m_contextButton->setFont(FontLoader::getInstance().loadFont("Font Awesome 6 Free Solid", "", m_contextButton->setFont(FontLoader::getInstance().loadFont("Font Awesome 6 Free Solid", "",
14 + pointSizeOffset)); 14 + pointSizeOffset));
m_contextButton->setText(u8"\uf141"); // 设置按钮图标为三条横线 m_contextButton->setText(u8"\uf141"); // fa-ellipsis-h
// 连接按钮点击信号到树形视图的上下文菜单槽
connect(m_contextButton, &QPushButton::clicked, m_view, [this](bool) { connect(m_contextButton, &QPushButton::clicked, m_view, [this](bool) {
auto tree_view = dynamic_cast<NodeTreeView *>(m_view); auto tree_view = dynamic_cast<NodeTreeView *>(m_view);
if (!m_view->selectionModel()->selectedIndexes().contains(m_index)) { if (!m_view->selectionModel()->selectedIndexes().contains(m_index)) {
tree_view->setCurrentIndexC(m_index); // 设置当前索引 tree_view->setCurrentIndexC(m_index);
} }
tree_view->onCustomContextMenu(tree_view->visualRect(m_index).topLeft() tree_view->onCustomContextMenu(tree_view->visualRect(m_index).topLeft()
+ m_contextButton->geometry().bottomLeft()); // 显示上下文菜单 + m_contextButton->geometry().bottomLeft());
}); });
layout->addWidget(m_contextButton, 0, Qt::AlignRight); // 将按钮添加到布局中 layout->addWidget(m_contextButton, 0, Qt::AlignRight);
layout->addSpacing(5); // 添加间距 layout->addSpacing(5);
// 连接树形视图展开信号到更新代理槽
connect(m_view, &QTreeView::expanded, this, [this](const QModelIndex &) { update(); }); connect(m_view, &QTreeView::expanded, this, [this](const QModelIndex &) { update(); });
} }
// 更新代理的方法
void TagTreeDelegateEditor::updateDelegate() void TagTreeDelegateEditor::updateDelegate()
{ {
// 获取显示名称并使用字体度量进行省略
auto displayName = m_index.data(NodeItem::Roles::DisplayText).toString(); auto displayName = m_index.data(NodeItem::Roles::DisplayText).toString();
QFontMetrics fm(m_titleFont); QFontMetrics fm(m_titleFont);
displayName = fm.elidedText(displayName, Qt::ElideRight, m_label->contentsRect().width()); displayName = fm.elidedText(displayName, Qt::ElideRight, m_label->contentsRect().width());
// 根据选中状态设置标签样式
if (m_view->selectionModel()->selectedIndexes().contains(m_index)) { if (m_view->selectionModel()->selectedIndexes().contains(m_index)) {
m_label->setStyleSheet(QStringLiteral("QLabel{color: rgb(%1, %2, %3);}") m_label->setStyleSheet(QStringLiteral("QLabel{color: rgb(%1, %2, %3);}")
.arg(QString::number(m_titleSelectedColor.red()), .arg(QString::number(m_titleSelectedColor.red()),
@ -156,20 +135,16 @@ void TagTreeDelegateEditor::updateDelegate()
QString::number(m_titleColor.green()), QString::number(m_titleColor.green()),
QString::number(m_titleColor.blue()))); QString::number(m_titleColor.blue())));
} }
m_label->setText(displayName); // 设置标签文本 m_label->setText(displayName);
} }
// 重写绘制事件
void TagTreeDelegateEditor::paintEvent(QPaintEvent *event) void TagTreeDelegateEditor::paintEvent(QPaintEvent *event)
{ {
updateDelegate(); // 更新代理 updateDelegate();
QPainter painter(this); // 创建画家对象 QPainter painter(this);
// 根据选中状态绘制背景
if (m_view->selectionModel()->selectedIndexes().contains(m_index)) { if (m_view->selectionModel()->selectedIndexes().contains(m_index)) {
painter.fillRect(rect(), QBrush(m_activeColor)); painter.fillRect(rect(), QBrush(m_activeColor));
} else { } else {
// 根据拖拽状态和主题绘制背景
auto listView = dynamic_cast<NoteListView *>(m_listView); auto listView = dynamic_cast<NoteListView *>(m_listView);
if (listView->isDragging()) { if (listView->isDragging()) {
if (m_theme == Theme::Dark) { if (m_theme == Theme::Dark) {
@ -182,50 +157,50 @@ void TagTreeDelegateEditor::paintEvent(QPaintEvent *event)
} }
} }
// 绘制标签颜色图标
auto iconRect = QRect(rect().x() + 22, rect().y() + (rect().height() - 14) / 2, 16, 16);
auto tagColor = m_index.data(NodeItem::Roles::TagColor).toString();
// 继续上面的代码
// 绘制标签颜色图标
auto iconRect = QRect(rect().x() + 22, rect().y() + (rect().height() - 14) / 2, 16, 16); auto iconRect = QRect(rect().x() + 22, rect().y() + (rect().height() - 14) / 2, 16, 16);
auto tagColor = m_index.data(NodeItem::Roles::TagColor).toString(); auto tagColor = m_index.data(NodeItem::Roles::TagColor).toString();
if (!tagColor.isEmpty()) { painter.setPen(QColor(tagColor));
QColor color(tagColor); #ifdef __APPLE__
painter.setPen(Qt::NoPen); int iconPointSizeOffset = 0;
painter.setBrush(color); #else
painter.drawEllipse(iconRect); int iconPointSizeOffset = -4;
} #endif
painter.setFont(FontLoader::getInstance().loadFont("Font Awesome 6 Free Solid", "",
// 绘制标签文本 16 + iconPointSizeOffset));
auto textRect = QRect(iconRect.right() + 12, rect().y(), rect().width() - iconRect.right() - 12, painter.drawText(iconRect, u8"\uf111"); // fa-circle
rect().height()); QWidget::paintEvent(event);
QFontMetrics metrics(m_titleFont);
auto text = metrics.elidedText(m_index.data(NodeItem::Roles::DisplayText).toString(),
Qt::ElideRight, textRect.width());
painter.setFont(m_titleFont);
painter.setPen(m_titleColor);
painter.drawText(textRect, Qt::AlignVCenter, text);
// 绘制完成后,确保事件被处理
event->accept();
} }
// 重写鼠标双击事件
void TagTreeDelegateEditor::mouseDoubleClickEvent(QMouseEvent *event) void TagTreeDelegateEditor::mouseDoubleClickEvent(QMouseEvent *event)
{ {
// 检查事件是否是鼠标左键双击 auto iconRect = QRect(rect().x() + 10, rect().y() + (rect().height() - 14) / 2, 14, 14);
if (event->button() == Qt::LeftButton) { if (iconRect.contains(event->pos())) {
// 这里可以添加双击事件的处理逻辑,例如编辑项 dynamic_cast<NodeTreeView *>(m_view)->onChangeTagColorAction();
// ... } else if (m_label->geometry().contains(event->pos())) {
m_label->openEditor();
} else {
QWidget::mouseDoubleClickEvent(event);
} }
// 确保事件被处理
event->accept();
} }
// 其他可能的类成员和方法声明 void TagTreeDelegateEditor::setTheme(Theme::Value theme)
// ... {
m_theme = theme;
#endif // TAGTREEDELEGATEEDITORH switch (theme) {
case Theme::Light: {
m_hoverColor = QColor(247, 247, 247);
m_titleColor = QColor(26, 26, 26);
break;
}
case Theme::Dark: {
m_hoverColor = QColor(25, 25, 25);
m_titleColor = QColor(212, 212, 212);
break;
}
case Theme::Sepia: {
m_hoverColor = QColor(251, 240, 217);
m_titleColor = QColor(26, 26, 26);
break;
}
}
}

@ -1,54 +1,46 @@
#ifndef TAGTREEDELEGATEEDITOR_H #ifndef TAGTREEDELEGATEEDITOR_H
#define TAGTREEDELEGATEEDITOR_H #define TAGTREEDELEGATEEDITOR_H
#include <QWidget> // 包含QWidget类所有UI对象的基类 #include <QWidget>
#include <QStyleOptionViewItem> // 包含用于自定义绘图项的选项 #include <QStyleOptionViewItem>
#include <QModelIndex> // 包含用于操作模型索引的类 #include <QModelIndex>
#include <QFont> // 包含用于操作字体的类 #include <QFont>
#include "editorsettingsoptions.h" // 包含自定义的编辑器设置选项 #include "editorsettingsoptions.h"
class QTreeView; // 前向声明QTreeView类用于树形视图 class QTreeView;
class QLabel; // 前向声明QLabel类用于显示文本 class QLabel;
class PushButtonType; // 前向声明自定义按钮类型 class PushButtonType;
class LabelEditType; // 前向声明自定义标签编辑类型 class LabelEditType;
class QListView; // 前向声明QListView类用于列表视图 class QListView;
class TagTreeDelegateEditor : public QWidget // 定义一个自定义代理编辑器类继承自QWidget class TagTreeDelegateEditor : public QWidget
{ {
Q_OBJECT // 宏,用于声明元对象系统支持 Q_OBJECT
public: public:
// 构造函数,接收树形视图、样式选项、模型索引、列表视图和父窗口指针
explicit TagTreeDelegateEditor(QTreeView *view, const QStyleOptionViewItem &option, explicit TagTreeDelegateEditor(QTreeView *view, const QStyleOptionViewItem &option,
const QModelIndex &index, QListView *listView, const QModelIndex &index, QListView *listView,
QWidget *parent = nullptr); QWidget *parent = nullptr);
// 设置主题的函数
void setTheme(Theme::Value theme); void setTheme(Theme::Value theme);
private: private:
QStyleOptionViewItem m_option; // 保存样式选项 QStyleOptionViewItem m_option;
QModelIndex m_index; // 保存模型索引 QModelIndex m_index;
QString m_displayFont; // 保存显示字体 QString m_displayFont;
QFont m_titleFont; // 保存标题字体 QFont m_titleFont;
QColor m_titleColor; // 保存标题颜色 QColor m_titleColor;
QColor m_titleSelectedColor; // 保存标题选中颜色 QColor m_titleSelectedColor;
QColor m_activeColor; // 保存活动颜色 QColor m_activeColor;
QColor m_hoverColor; // 保存悬停颜色 QColor m_hoverColor;
QTreeView *m_view; // 保存树形视图指针 QTreeView *m_view;
QListView *m_listView; // 保存列表视图指针 QListView *m_listView;
LabelEditType *m_label; // 保存标签编辑类型指针 LabelEditType *m_label;
PushButtonType *m_contextButton; // 保存上下文按钮类型指针 PushButtonType *m_contextButton;
Theme::Value m_theme; // 保存当前主题 Theme::Value m_theme;
// 更新代理的函数
void updateDelegate(); void updateDelegate();
// QWidget接口重写 // QWidget interface
protected: protected:
// 重写绘制事件处理函数
virtual void paintEvent(QPaintEvent *event) override; virtual void paintEvent(QPaintEvent *event) override;
// 重写鼠标双击事件处理函数
virtual void mouseDoubleClickEvent(QMouseEvent *event) override; virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
}; };

@ -1,4 +1,4 @@
// <20><><EFBFBD><EFBFBD>ͷ<EFBFBD>ļ<EFBFBD> //包含头文件
#include "trashbuttondelegateeditor.h" #include "trashbuttondelegateeditor.h"
#include <QPainter> #include <QPainter>
#include <QDebug> #include <QDebug>
@ -8,8 +8,8 @@
#include "notelistview.h" #include "notelistview.h"
#include "fontloader.h" #include "fontloader.h"
// TrashButtonDelegateEditor <EFBFBD><EFBFBD>Ĺ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> // TrashButtonDelegateEditor 类的构造函数
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽѡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD>͸<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 参数包括视图、样式选项、索引、列表视图和父窗口
TrashButtonDelegateEditor::TrashButtonDelegateEditor(QTreeView *view, TrashButtonDelegateEditor::TrashButtonDelegateEditor(QTreeView *view,
const QStyleOptionViewItem &option, const QStyleOptionViewItem &option,
const QModelIndex &index, QListView *listView, const QModelIndex &index, QListView *listView,
@ -18,24 +18,24 @@ TrashButtonDelegateEditor::TrashButtonDelegateEditor(QTreeView *view,
m_option(option), m_option(option),
m_index(index), m_index(index),
#ifdef __APPLE__ #ifdef __APPLE__
// <20><>ƻ<EFBFBD><C6BB>ϵͳ<CFB5><CDB3>ʹ<EFBFBD><CAB9> SF Pro Text <20><><EFBFBD><EFBFBD><E5A3AC><EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9> Roboto // 在苹果系统上使用 SF Pro Text 字体,如果没有则使用 Roboto
m_displayFont(QFont(QStringLiteral("SF Pro Text")).exactMatch() m_displayFont(QFont(QStringLiteral("SF Pro Text")).exactMatch()
? QStringLiteral("SF Pro Text") ? QStringLiteral("SF Pro Text")
: QStringLiteral("Roboto")), : QStringLiteral("Roboto")),
#elif _WIN32 #elif _WIN32
// <20><> Windows ϵͳ<CFB5><CDB3>ʹ<EFBFBD><CAB9> Segoe UI <20><><EFBFBD><EFBFBD><E5A3AC><EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9> Roboto // 在 Windows 系统上使用 Segoe UI 字体,如果没有则使用 Roboto
m_displayFont(QFont(QStringLiteral("Segoe UI")).exactMatch() ? QStringLiteral("Segoe UI") m_displayFont(QFont(QStringLiteral("Segoe UI")).exactMatch() ? QStringLiteral("Segoe UI")
: QStringLiteral("Roboto")), : QStringLiteral("Roboto")),
#else #else
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϵͳ<CFB5><CDB3>ʹ<EFBFBD><CAB9> Roboto <20><><EFBFBD><EFBFBD> // 在其他系统上使用 Roboto 字体
m_displayFont(QStringLiteral("Roboto")), m_displayFont(QStringLiteral("Roboto")),
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
// <20><>ƻ<EFBFBD><C6BB>ϵͳ<CFB5><CDB3><EFBFBD><EFBFBD><EFBFBD>ñ<EFBFBD><C3B1><EFBFBD>ͱʼ<CDB1><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>С // 在苹果系统上设置标题和笔记数量的字体大小
m_titleFont(m_displayFont, 13, QFont::DemiBold), m_titleFont(m_displayFont, 13, QFont::DemiBold),
m_numberOfNotesFont(m_displayFont, 12, QFont::DemiBold), m_numberOfNotesFont(m_displayFont, 12, QFont::DemiBold),
#else #else
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϵͳ<CFB5><CDB3><EFBFBD><EFBFBD><EFBFBD>ñ<EFBFBD><C3B1><EFBFBD>ͱʼ<CDB1><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>С // 在其他系统上设置标题和笔记数量的字体大小
m_titleFont(m_displayFont, 10, QFont::DemiBold), m_titleFont(m_displayFont, 10, QFont::DemiBold),
m_numberOfNotesFont(m_displayFont, 9, QFont::DemiBold), m_numberOfNotesFont(m_displayFont, 9, QFont::DemiBold),
#endif #endif
@ -49,23 +49,22 @@ TrashButtonDelegateEditor::TrashButtonDelegateEditor(QTreeView *view,
m_view(view), m_view(view),
m_listView(listView) m_listView(listView)
{ {
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݱ߾<EFBFBD>Ϊ0 // 设置内容边距为0
setContentsMargins(0, 0, 0, 0); setContentsMargins(0, 0, 0, 0);
} }
// <20><><EFBFBD><EFBFBD><EFBFBD>¼<EFBFBD><C2BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 绘制事件处理函数
// 绘制事件处理
void TrashButtonDelegateEditor::paintEvent(QPaintEvent *event) void TrashButtonDelegateEditor::paintEvent(QPaintEvent *event)
{ {
QPainter painter(this); QPainter painter(this);
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 绘制图标区域
auto iconRect = QRect(rect().x() + 22, rect().y() + 2 + (rect().height() - 20) / 2, 18, 20); auto iconRect = QRect(rect().x() + 22, rect().y() + 2 + (rect().height() - 20) / 2, 18, 20);
auto iconPath = m_index.data(NodeItem::Roles::Icon).toString(); auto iconPath = m_index.data(NodeItem::Roles::Icon).toString();
auto displayName = m_index.data(NodeItem::Roles::DisplayText).toString(); auto displayName = m_index.data(NodeItem::Roles::DisplayText).toString();
QRect nameRect(rect()); QRect nameRect(rect());
nameRect.setLeft(iconRect.x() + iconRect.width() + 5); nameRect.setLeft(iconRect.x() + iconRect.width() + 5);
nameRect.setWidth(nameRect.width() - 22 - 40); nameRect.setWidth(nameRect.width() - 22 - 40);
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD>ѡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ñ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD><EFBFBD><EFBFBD>ı<EFBFBD><EFBFBD><EFBFBD>ɫ // 根据是否选中设置背景颜色和文本颜色
if (m_view->selectionModel()->isSelected(m_index)) { if (m_view->selectionModel()->isSelected(m_index)) {
painter.fillRect(rect(), QBrush(m_activeColor)); painter.fillRect(rect(), QBrush(m_activeColor));
painter.setPen(m_titleSelectedColor); painter.setPen(m_titleSelectedColor);
@ -87,12 +86,12 @@ void TrashButtonDelegateEditor::paintEvent(QPaintEvent *event)
#else #else
int iconPointSizeOffset = -4; int iconPointSizeOffset = -4;
#endif #endif
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 设置图标字体
painter.setFont(FontLoader::getInstance().loadFont("Font Awesome 6 Free Solid", "", painter.setFont(FontLoader::getInstance().loadFont("Font Awesome 6 Free Solid", "",
16 + iconPointSizeOffset)); 16 + iconPointSizeOffset));
painter.drawText(iconRect, iconPath); // fa-trash painter.drawText(iconRect, iconPath); // fa-trash
// <EFBFBD><EFBFBD><EFBFBD>Ʊ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ı<EFBFBD> // 绘制标题文本
if (m_view->selectionModel()->isSelected(m_index)) { if (m_view->selectionModel()->isSelected(m_index)) {
painter.setPen(m_titleSelectedColor); painter.setPen(m_titleSelectedColor);
} else { } else {
@ -101,12 +100,12 @@ void TrashButtonDelegateEditor::paintEvent(QPaintEvent *event)
painter.setFont(m_titleFont); painter.setFont(m_titleFont);
painter.drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, displayName); painter.drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, displayName);
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 绘制子项数量区域
auto childCountRect = rect(); auto childCountRect = rect();
childCountRect.setLeft(nameRect.right() + 22); childCountRect.setLeft(nameRect.right() + 22);
childCountRect.setWidth(childCountRect.width() - 5); childCountRect.setWidth(childCountRect.width() - 5);
auto childCount = m_index.data(NodeItem::Roles::ChildCount).toInt(); auto childCount = m_index.data(NodeItem::Roles::ChildCount).toInt();
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD>ѡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ı<EFBFBD><EFBFBD><EFBFBD>ɫ // 根据是否选中设置子项数量文本颜色
if (m_view->selectionModel()->isSelected(m_index)) { if (m_view->selectionModel()->isSelected(m_index)) {
painter.setPen(m_numberOfNotesSelectedColor); painter.setPen(m_numberOfNotesSelectedColor);
} else { } else {
@ -118,8 +117,7 @@ void TrashButtonDelegateEditor::paintEvent(QPaintEvent *event)
QWidget::paintEvent(event); QWidget::paintEvent(event);
} }
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E2BAAF> // 设置主题函数
// 设置主题
void TrashButtonDelegateEditor::setTheme(Theme::Value theme) void TrashButtonDelegateEditor::setTheme(Theme::Value theme)
{ {
m_theme = theme; m_theme = theme;

@ -10,39 +10,39 @@
class QTreeView; class QTreeView;
class QListView; class QListView;
// TrashButtonDelegateEditor <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // TrashButtonDelegateEditor 类声明
class TrashButtonDelegateEditor : public QWidget class TrashButtonDelegateEditor : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 构造函数
explicit TrashButtonDelegateEditor(QTreeView *view, const QStyleOptionViewItem &option, explicit TrashButtonDelegateEditor(QTreeView *view, const QStyleOptionViewItem &option,
const QModelIndex &index, QListView *listView, const QModelIndex &index, QListView *listView,
QWidget *parent = nullptr); QWidget *parent = nullptr);
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 设置主题函数
void setTheme(Theme::Value theme); void setTheme(Theme::Value theme);
private: private:
QStyleOptionViewItem m_option; // 存储视图项的样式选项 QStyleOptionViewItem m_option;
QModelIndex m_index; // 存储模型索引 QModelIndex m_index;
QString m_displayFont; // 显示字体 QString m_displayFont;
QFont m_titleFont; // 标题字体 QFont m_titleFont;
QFont m_numberOfNotesFont; // 备注字体 QFont m_numberOfNotesFont;
QColor m_titleColor; // 标题颜色 QColor m_titleColor;
QColor m_titleSelectedColor; // 标题选中颜色 QColor m_titleSelectedColor;
QColor m_activeColor; // 激活状态颜色 QColor m_activeColor;
QColor m_hoverColor; // 悬停状态颜色 QColor m_hoverColor;
QColor m_folderIconColor; // 文件夹图标颜色 QColor m_folderIconColor;
QColor m_numberOfNotesColor; // 备注颜色 QColor m_numberOfNotesColor;
QColor m_numberOfNotesSelectedColor; // 备注选中颜色 QColor m_numberOfNotesSelectedColor;
QTreeView *m_view; // 视图指针 QTreeView *m_view;
QListView *m_listView; // 列表视图指针 QListView *m_listView;
Theme::Value m_theme; // 主题值 Theme::Value m_theme;
// QWidget interface // QWidget interface
// QWidget <20>ӿ<EFBFBD> // QWidget 接口
protected: protected:
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>¼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 绘制事件处理函数
virtual void paintEvent(QPaintEvent *event) override; // 绘制事件 virtual void paintEvent(QPaintEvent *event) override;
}; };
#endif // TRASHBUTTONDELEGATEEDITOR_H #endif // TRASHBUTTONDELEGATEEDITOR_H

@ -1,4 +1,4 @@
/// <20><><EFBFBD><EFBFBD>ͷ<EFBFBD>ļ<EFBFBD> // 包含头文件
#include "treeviewlogic.h" #include "treeviewlogic.h"
#include "nodetreeview.h" #include "nodetreeview.h"
#include "nodetreemodel.h" #include "nodetreemodel.h"
@ -12,8 +12,7 @@
#include <QApplication> #include <QApplication>
#include "customapplicationstyle.h" #include "customapplicationstyle.h"
// TreeViewLogic <20><>Ĺ<EFBFBD><C4B9><EFBFBD><ECBAAF> // TreeViewLogic 类的构造函数
// 管理树视图的逻辑,包括节点的增删改查和更新
TreeViewLogic::TreeViewLogic(NodeTreeView *treeView, NodeTreeModel *treeModel, DBManager *dbManager, TreeViewLogic::TreeViewLogic(NodeTreeView *treeView, NodeTreeModel *treeModel, DBManager *dbManager,
NoteListView *listView, QObject *parent) NoteListView *listView, QObject *parent)
: QObject(parent), : QObject(parent),
@ -27,16 +26,16 @@ TreeViewLogic::TreeViewLogic(NodeTreeView *treeView, NodeTreeModel *treeModel, D
m_lastSelectTags{}, m_lastSelectTags{},
m_expandedFolder{} m_expandedFolder{}
{ {
// <EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 初始化树视图代理
m_treeDelegate = new NodeTreeDelegate(m_treeView, m_treeView, m_listView); m_treeDelegate = new NodeTreeDelegate(m_treeView, m_treeView, m_listView);
m_treeView->setItemDelegate(m_treeDelegate); m_treeView->setItemDelegate(m_treeDelegate);
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ź<EFBFBD> // 连接数据库管理器信号
connect(m_dbManager, &DBManager::nodesTagTreeReceived, this, &TreeViewLogic::loadTreeModel, connect(m_dbManager, &DBManager::nodesTagTreeReceived, this, &TreeViewLogic::loadTreeModel,
Qt::QueuedConnection); Qt::QueuedConnection);
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><EFBFBD><EFBFBD>ź<EFBFBD> // 连接树模型信号
connect(m_treeModel, &NodeTreeModel::topLevelItemLayoutChanged, this, connect(m_treeModel, &NodeTreeModel::topLevelItemLayoutChanged, this,
&TreeViewLogic::updateTreeViewSeparator); &TreeViewLogic::updateTreeViewSeparator);
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD>ź<EFBFBD> // 连接树视图信号
connect(m_treeView, &NodeTreeView::addFolderRequested, this, connect(m_treeView, &NodeTreeView::addFolderRequested, this,
[this] { onAddFolderRequested(false); }); [this] { onAddFolderRequested(false); });
connect(m_treeDelegate, &NodeTreeDelegate::addFolderRequested, this, connect(m_treeDelegate, &NodeTreeDelegate::addFolderRequested, this,
@ -85,25 +84,23 @@ TreeViewLogic::TreeViewLogic(NodeTreeView *treeView, NodeTreeModel *treeModel, D
&TreeViewLogic::onChildNoteCountChangedFolder); &TreeViewLogic::onChildNoteCountChangedFolder);
connect(m_dbManager, &DBManager::childNotesCountUpdatedTag, this, connect(m_dbManager, &DBManager::childNotesCountUpdatedTag, this,
&TreeViewLogic::onChildNotesCountChangedTag); &TreeViewLogic::onChildNotesCountChangedTag);
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ // 设置应用样式
m_style = new CustomApplicationStyle(); m_style = new CustomApplicationStyle();
qApp->setStyle(m_style); qApp->setStyle(m_style);
} }
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD>ָ<EFBFBD><D6B8><EFBFBD> // 更新树视图分隔符
// 更新树视图的分隔符
void TreeViewLogic::updateTreeViewSeparator() void TreeViewLogic::updateTreeViewSeparator()
{ {
m_treeView->setTreeSeparator(m_treeModel->getSeparatorIndex(), m_treeView->setTreeSeparator(m_treeModel->getSeparatorIndex(),
m_treeModel->getDefaultNotesIndex()); m_treeModel->getDefaultNotesIndex());
} }
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><C4A3> // 加载树模型
// 加载树模型的数据
void TreeViewLogic::loadTreeModel(const NodeTagTreeData &treeData) void TreeViewLogic::loadTreeModel(const NodeTagTreeData &treeData)
{ {
m_treeModel->setTreeData(treeData); m_treeModel->setTreeData(treeData);
// <EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD><EFBFBD>ڵ<EFBFBD><EFBFBD><EFBFBD>ӽڵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 获取根节点的子节点数量
{ {
NodeData node; NodeData node;
QMetaObject::invokeMethod(m_dbManager, "getChildNotesCountFolder", QMetaObject::invokeMethod(m_dbManager, "getChildNotesCountFolder",
@ -114,7 +111,7 @@ void TreeViewLogic::loadTreeModel(const NodeTagTreeData &treeData)
m_treeModel->setData(index, node.childNotesCount(), NodeItem::Roles::ChildCount); m_treeModel->setData(index, node.childNotesCount(), NodeItem::Roles::ChildCount);
} }
} }
// <EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڵ<EFBFBD><EFBFBD><EFBFBD>ӽڵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 获取垃圾箱节点的子节点数量
{ {
NodeData node; NodeData node;
QMetaObject::invokeMethod(m_dbManager, "getChildNotesCountFolder", QMetaObject::invokeMethod(m_dbManager, "getChildNotesCountFolder",
@ -125,7 +122,7 @@ void TreeViewLogic::loadTreeModel(const NodeTagTreeData &treeData)
m_treeModel->setData(index, node.childNotesCount(), NodeItem::Roles::ChildCount); m_treeModel->setData(index, node.childNotesCount(), NodeItem::Roles::ChildCount);
} }
} }
// <EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD>״̬ // 加载保存的状态
if (m_needLoadSavedState) { if (m_needLoadSavedState) {
m_needLoadSavedState = false; m_needLoadSavedState = false;
m_treeView->reExpandC(m_expandedFolder); m_treeView->reExpandC(m_expandedFolder);
@ -157,8 +154,7 @@ void TreeViewLogic::loadTreeModel(const NodeTagTreeData &treeData)
updateTreeViewSeparator(); updateTreeViewSeparator();
} }
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 处理添加文件夹请求
// 添加文件夹的请求处理
void TreeViewLogic::onAddFolderRequested(bool fromPlusButton) void TreeViewLogic::onAddFolderRequested(bool fromPlusButton)
{ {
QModelIndex currentIndex; QModelIndex currentIndex;
@ -179,7 +175,7 @@ void TreeViewLogic::onAddFolderRequested(bool fromPlusButton)
static_cast<NodeItem::Type>(currentIndex.data(NodeItem::Roles::ItemType).toInt()); static_cast<NodeItem::Type>(currentIndex.data(NodeItem::Roles::ItemType).toInt());
if (type == NodeItem::FolderItem) { if (type == NodeItem::FolderItem) {
parentId = currentIndex.data(NodeItem::Roles::NodeId).toInt(); parentId = currentIndex.data(NodeItem::Roles::NodeId).toInt();
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĭ<EFBFBD>ϱʼ<EFBFBD><EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>´<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD> // 不允许在默认笔记文件夹下创建子文件夹
if (parentId == SpecialNodeID::DefaultNotesFolder) { if (parentId == SpecialNodeID::DefaultNotesFolder) {
parentId = SpecialNodeID::RootFolder; parentId = SpecialNodeID::RootFolder;
} }
@ -252,14 +248,13 @@ void TreeViewLogic::onAddFolderRequested(bool fromPlusButton)
} }
} }
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӱ<EFBFBD>ǩ<EFBFBD><C7A9><EFBFBD><EFBFBD> // 处理添加标签请求
// 处理添加标签请求
void TreeViewLogic::onAddTagRequested() void TreeViewLogic::onAddTagRequested()
{ {
int newlyCreatedTagId; int newlyCreatedTagId;
TagData newTag; TagData newTag;
newTag.setName(m_treeModel->getNewTagPlaceholderName()); newTag.setName(m_treeModel->getNewTagPlaceholderName());
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 随机颜色生成器
const double lower_bound = 0; const double lower_bound = 0;
const double upper_bound = 1; const double upper_bound = 1;
static std::uniform_real_distribution<double> unif(lower_bound, upper_bound); static std::uniform_real_distribution<double> unif(lower_bound, upper_bound);
@ -283,8 +278,7 @@ void TreeViewLogic::onAddTagRequested()
m_treeModel->appendChildNodeToParent(m_treeModel->rootIndex(), hs); m_treeModel->appendChildNodeToParent(m_treeModel->rootIndex(), hs);
} }
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD><EFBFBD><EFBFBD> // 处理重命名节点请求
// 处理从树视图请求重命名节点
void TreeViewLogic::onRenameNodeRequestedFromTreeView(const QModelIndex &index, void TreeViewLogic::onRenameNodeRequestedFromTreeView(const QModelIndex &index,
const QString &newName) const QString &newName)
{ {
@ -293,8 +287,7 @@ void TreeViewLogic::onRenameNodeRequestedFromTreeView(const QModelIndex &index,
emit requestRenameNodeInDB(id, newName); emit requestRenameNodeInDB(id, newName);
} }
// <20><><EFBFBD><EFBFBD>ɾ<EFBFBD><C9BE><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 处理删除文件夹请求
// 处理删除文件夹的请求
void TreeViewLogic::onDeleteFolderRequested(const QModelIndex &index) void TreeViewLogic::onDeleteFolderRequested(const QModelIndex &index)
{ {
auto btn = QMessageBox::question(nullptr, "Are you sure you want to delete this folder", auto btn = QMessageBox::question(nullptr, "Are you sure you want to delete this folder",
@ -326,8 +319,7 @@ void TreeViewLogic::onDeleteFolderRequested(const QModelIndex &index)
} }
} }
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǩ<EFBFBD><C7A9><EFBFBD><EFBFBD> // 处理重命名标签请求
// 处理从树视图请求重命名标签
void TreeViewLogic::onRenameTagRequestedFromTreeView(const QModelIndex &index, void TreeViewLogic::onRenameTagRequestedFromTreeView(const QModelIndex &index,
const QString &newName) const QString &newName)
{ {
@ -336,8 +328,7 @@ void TreeViewLogic::onRenameTagRequestedFromTreeView(const QModelIndex &index,
emit requestRenameTagInDB(id, newName); emit requestRenameTagInDB(id, newName);
} }
// <20><><EFBFBD><EFBFBD><EFBFBD>޸ı<DEB8>ǩ<EFBFBD><C7A9>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD> // 处理修改标签颜色请求
// 处理颜色请求的标签
void TreeViewLogic::onChangeTagColorRequested(const QModelIndex &index) void TreeViewLogic::onChangeTagColorRequested(const QModelIndex &index)
{ {
if (index.isValid()) { if (index.isValid()) {
@ -351,8 +342,7 @@ void TreeViewLogic::onChangeTagColorRequested(const QModelIndex &index)
} }
} }
// <20><><EFBFBD><EFBFBD>ɾ<EFBFBD><C9BE><EFBFBD><EFBFBD>ǩ<EFBFBD><C7A9><EFBFBD><EFBFBD> // 处理删除标签请求
// 处理删除标签的请求
void TreeViewLogic::onDeleteTagRequested(const QModelIndex &index) void TreeViewLogic::onDeleteTagRequested(const QModelIndex &index)
{ {
auto id = index.data(NodeItem::Roles::NodeId).toInt(); auto id = index.data(NodeItem::Roles::NodeId).toInt();
@ -361,8 +351,7 @@ void TreeViewLogic::onDeleteTagRequested(const QModelIndex &index)
m_treeView->setCurrentIndexC(m_treeModel->getAllNotesButtonIndex()); m_treeView->setCurrentIndexC(m_treeModel->getAllNotesButtonIndex());
} }
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǩ<EFBFBD>ӽڵ<D3BD><DAB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 处理标签子节点数量变化
// 更新标签的子项数量
void TreeViewLogic::onChildNotesCountChangedTag(int tagId, int notesCount) void TreeViewLogic::onChildNotesCountChangedTag(int tagId, int notesCount)
{ {
auto index = m_treeModel->tagIndexFromId(tagId); auto index = m_treeModel->tagIndexFromId(tagId);
@ -371,8 +360,7 @@ void TreeViewLogic::onChildNotesCountChangedTag(int tagId, int notesCount)
} }
} }
// <20><><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ӽڵ<D3BD><DAB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 处理文件夹子节点数量变化
// 更新文件夹的子项数量
void TreeViewLogic::onChildNoteCountChangedFolder(int folderId, const QString &absPath, void TreeViewLogic::onChildNoteCountChangedFolder(int folderId, const QString &absPath,
int notesCount) int notesCount)
{ {
@ -389,8 +377,7 @@ void TreeViewLogic::onChildNoteCountChangedFolder(int folderId, const QString &a
} }
} }
// <20><><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD> // 打开文件夹
// 打开指定文件夹
void TreeViewLogic::openFolder(int id) void TreeViewLogic::openFolder(int id)
{ {
NodeData target; NodeData target;
@ -414,8 +401,7 @@ void TreeViewLogic::openFolder(int id)
} }
} }
// <20><><EFBFBD><EFBFBD><EFBFBD>ƶ<EFBFBD><C6B6>ڵ<EFBFBD><DAB5><EFBFBD><EFBFBD><EFBFBD> // 处理移动节点请求
// 处理移动节点的请求
void TreeViewLogic::onMoveNodeRequested(int nodeId, int targetId) void TreeViewLogic::onMoveNodeRequested(int nodeId, int targetId)
{ {
NodeData target; NodeData target;
@ -428,8 +414,7 @@ void TreeViewLogic::onMoveNodeRequested(int nodeId, int targetId)
emit requestMoveNodeInDB(nodeId, target); emit requestMoveNodeInDB(nodeId, target);
} }
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 设置主题
// 设置主题
void TreeViewLogic::setTheme(Theme::Value theme) void TreeViewLogic::setTheme(Theme::Value theme)
{ {
m_treeView->setTheme(theme); m_treeView->setTheme(theme);
@ -437,8 +422,7 @@ void TreeViewLogic::setTheme(Theme::Value theme)
m_style->setTheme(theme); m_style->setTheme(theme);
} }
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>󱣴<EFBFBD><F3B1A3B4>״̬ // 设置最后保存的状态
// 设置最后保存的状态
void TreeViewLogic::setLastSavedState(bool isLastSelectFolder, const QString &lastSelectFolder, void TreeViewLogic::setLastSavedState(bool isLastSelectFolder, const QString &lastSelectFolder,
const QSet<int> &lastSelectTag, const QSet<int> &lastSelectTag,
const QStringList &expandedFolder) const QStringList &expandedFolder)

@ -12,86 +12,76 @@ class DBManager;
class CustomApplicationStyle; class CustomApplicationStyle;
class NoteListView; class NoteListView;
// TreeViewLogic 类负责管理树视图逻辑 // TreeViewLogic 类负责管理树视图逻辑
class TreeViewLogic : public QObject class TreeViewLogic : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
// 构造函数 // 构造函数
explicit TreeViewLogic(NodeTreeView* treeView, NodeTreeModel* treeModel, DBManager* dbManager, explicit TreeViewLogic(NodeTreeView* treeView, NodeTreeModel* treeModel, DBManager* dbManager,
NoteListView* listView, QObject* parent = nullptr); NoteListView* listView, QObject* parent = nullptr);
// 打开文件夹 // 打开文件夹
void openFolder(int id); void openFolder(int id);
// 请求移动节点 // 处理移动节点请求
void onMoveNodeRequested(int nodeId, int targetId); void onMoveNodeRequested(int nodeId, int targetId);
// 设置主题 // 设置主题
void setTheme(Theme::Value theme); void setTheme(Theme::Value theme);
// 设置最后保存的状态 // 设置最后保存的状态
void setLastSavedState(bool isLastSelectFolder, const QString& lastSelectFolder, void setLastSavedState(bool isLastSelectFolder, const QString& lastSelectFolder,
const QSet<int>& lastSelectTag, const QStringList& expandedFolder); const QSet<int>& lastSelectTag, const QStringList& expandedFolder);
private slots: private slots:
// 更新树视图分隔符 // 更新树视图分隔符
void updateTreeViewSeparator(); void updateTreeViewSeparator();
// 加载树模型 // 加载树模型
void loadTreeModel(const NodeTagTreeData& treeData); void loadTreeModel(const NodeTagTreeData& treeData);
// 请求添加标签 // 处理添加标签请求
void onAddTagRequested(); void onAddTagRequested();
// 请求重命名树视图中的节点 // 处理重命名节点请求
void onRenameNodeRequestedFromTreeView(const QModelIndex& index, const QString& newName); void onRenameNodeRequestedFromTreeView(const QModelIndex& index, const QString& newName);
// 请求删除文件夹 // 处理删除文件夹请求
void onDeleteFolderRequested(const QModelIndex& index); void onDeleteFolderRequested(const QModelIndex& index);
// 请求重命名树视图中的标签 // 处理重命名标签请求
void onRenameTagRequestedFromTreeView(const QModelIndex& index, const QString& newName); void onRenameTagRequestedFromTreeView(const QModelIndex& index, const QString& newName);
// 请求更改标签颜色 // 处理修改标签颜色请求
void onChangeTagColorRequested(const QModelIndex& index); void onChangeTagColorRequested(const QModelIndex& index);
// 请求删除标签 // 处理删除标签请求
void onDeleteTagRequested(const QModelIndex& index); void onDeleteTagRequested(const QModelIndex& index);
// 标签的子笔记数量变化 // 处理标签子节点数量变化
void onChildNotesCountChangedTag(int tagId, int notesCount); void onChildNotesCountChangedTag(int tagId, int notesCount);
// 文件夹的子笔记数量变化 // 处理文件夹子节点数量变化
void onChildNoteCountChangedFolder(int folderId, const QString& absPath, int notesCount); void onChildNoteCountChangedFolder(int folderId, const QString& absPath, int notesCount);
signals: signals:
// 请求在数据库中重命名节点 // 请求重命名节点
void requestRenameNodeInDB(int id, const QString& newName); void requestRenameNodeInDB(int id, const QString& newName);
// 请求在数据库中重命名标签 // 请求重命名标签
void requestRenameTagInDB(int id, const QString& newName); void requestRenameTagInDB(int id, const QString& newName);
// 请求在数据库中更改标签颜色 // 请求改标签颜色
void requestChangeTagColorInDB(int id, const QString& newColor); void requestChangeTagColorInDB(int id, const QString& newColor);
// 请求在数据库中移动节点 // 请求移动节点
void requestMoveNodeInDB(int id, const NodeData& target); void requestMoveNodeInDB(int id, const NodeData& target);
// 向标签添加笔记 // 添加笔记到标签
void addNoteToTag(int noteId, int tagId); void addNoteToTag(int noteId, int tagId);
// 通知节点移动 // 节点移动
void noteMoved(int nodeId, int targetId); void noteMoved(int nodeId, int targetId);
private: private:
// 请求添加文件夹 // 处理添加文件夹请求
void onAddFolderRequested(bool fromPlusButton); void onAddFolderRequested(bool fromPlusButton);
private: private:
// 节点树视图 // 成员变量
NodeTreeView* m_treeView; NodeTreeView* m_treeView;
// 节点树模型
NodeTreeModel* m_treeModel; NodeTreeModel* m_treeModel;
// 笔记列表视图
NoteListView* m_listView; NoteListView* m_listView;
// 节点树代理
NodeTreeDelegate* m_treeDelegate; NodeTreeDelegate* m_treeDelegate;
// 数据库管理器
DBManager* m_dbManager; DBManager* m_dbManager;
// 自定义应用样式
CustomApplicationStyle* m_style; CustomApplicationStyle* m_style;
// 是否需要加载保存的状态
bool m_needLoadSavedState; bool m_needLoadSavedState;
// 是否是最后选择的文件夹
bool m_isLastSelectFolder; bool m_isLastSelectFolder;
// 最后选择的文件夹路径
QString m_lastSelectFolder; QString m_lastSelectFolder;
// 最后选择的标签集合
QSet<int> m_lastSelectTags; QSet<int> m_lastSelectTags;
// 展开的文件夹列表
QStringList m_expandedFolder; QStringList m_expandedFolder;
}; };

@ -33,13 +33,13 @@ static QProcess XDGOPEN_PROCESS;
#endif #endif
/** /**
* * Indicates from where we should download the update definitions file
*/ */
static const QString static const QString
UPDATES_URL("https://raw.githubusercontent.com/nuttyartist/notes/master/UPDATES.json"); UPDATES_URL("https://raw.githubusercontent.com/nuttyartist/notes/master/UPDATES.json");
/** /**
* QSimpleUpdater * Initializes the window components and configures the QSimpleUpdater
*/ */
UpdaterWindow::UpdaterWindow(QWidget *parent) UpdaterWindow::UpdaterWindow(QWidget *parent)
: QDialog(parent), : QDialog(parent),
@ -53,21 +53,21 @@ UpdaterWindow::UpdaterWindow(QWidget *parent)
m_updater(QSimpleUpdater::getInstance()), m_updater(QSimpleUpdater::getInstance()),
m_manager(new QNetworkAccessManager(this)) m_manager(new QNetworkAccessManager(this))
{ {
/* 加载样式表 */ /* Load the stylesheet */
QFile file(":/styles/updater-window.css"); QFile file(":/styles/updater-window.css");
if (file.open(QIODevice::ReadOnly)) { if (file.open(QIODevice::ReadOnly)) {
setStyleSheet(QString::fromUtf8(file.readAll()).toLatin1()); setStyleSheet(QString::fromUtf8(file.readAll()).toLatin1());
file.close(); file.close();
} }
/* 初始化 UI */ /* Initialize the UI */
m_ui->setupUi(this); m_ui->setupUi(this);
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
setWindowFlag(Qt::WindowContextHelpButtonHint, false); setWindowFlag(Qt::WindowContextHelpButtonHint, false);
#endif #endif
setWindowTitle(qApp->applicationName() + " " + tr("Updater")); setWindowTitle(qApp->applicationName() + " " + tr("Updater"));
/* 更改字体 */ /* Change fonts */
#ifdef __APPLE__ #ifdef __APPLE__
QFont fontToUse = QFont(QStringLiteral("SF Pro Text")).exactMatch() QFont fontToUse = QFont(QStringLiteral("SF Pro Text")).exactMatch()
? QStringLiteral("SF Pro Text") ? QStringLiteral("SF Pro Text")
@ -86,50 +86,47 @@ UpdaterWindow::UpdaterWindow(QWidget *parent)
widgetChild->setFont(fontToUse); widgetChild->setFont(fontToUse);
} }
/* 连接 UI 信号/槽 */ /* Connect UI signals/slots */
connect(m_ui->closeButton, &QPushButton::clicked, this, &UpdaterWindow::close); connect(m_ui->closeButton, &QPushButton::clicked, this, &UpdaterWindow::close);
connect(m_ui->updateButton, &QPushButton::clicked, this, connect(m_ui->updateButton, &QPushButton::clicked, this,
&UpdaterWindow::onDownloadButtonClicked); &UpdaterWindow::onDownloadButtonClicked);
connect(m_updater, &QSimpleUpdater::checkingFinished, this, &UpdaterWindow::onCheckFinished); connect(m_updater, &QSimpleUpdater::checkingFinished, this, &UpdaterWindow::onCheckFinished);
connect(m_ui->checkBox, &QCheckBox::toggled, this, &UpdaterWindow::dontShowUpdateWindowChanged); connect(m_ui->checkBox, &QCheckBox::toggled, this, &UpdaterWindow::dontShowUpdateWindowChanged);
/* 启动 UI 循环 */ /* Start the UI loops */
updateTitleLabel(); updateTitleLabel();
/* 移除原生窗口装饰 */ /* Remove native window decorations */
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
setWindowFlags(windowFlags() | Qt::CustomizeWindowHint | Qt::FramelessWindowHint); setWindowFlags(windowFlags() | Qt::CustomizeWindowHint | Qt::FramelessWindowHint);
#else #else
setWindowFlags(windowFlags() | Qt::CustomizeWindowHint); setWindowFlags(windowFlags() | Qt::CustomizeWindowHint);
#endif #endif
/* 当 xdg-open 完成时响应(仅限 Linux */ /* React when xdg-open finishes (Linux only) */
#ifdef UseXdgOpen #ifdef UseXdgOpen
connect(&XDGOPEN_PROCESS, SIGNAL(finished(int)), this, SLOT(onXdgOpenFinished(int))); connect(&XDGOPEN_PROCESS, SIGNAL(finished(int)), this, SLOT(onXdgOpenFinished(int)));
#endif #endif
/* 设置窗口标志 */ /* Set window flags */
setWindowModality(Qt::ApplicationModal); setWindowModality(Qt::ApplicationModal);
} }
/** /**
* * Deletes the user interface
*/ */
UpdaterWindow::~UpdaterWindow() UpdaterWindow::~UpdaterWindow()
{ {
/* 确保 xdg-open 进程已关闭 */ /* Ensure that xdg-open process is closed */
#ifdef UseXdgOpen #ifdef UseXdgOpen
if (XDGOPEN_PROCESS.isOpen()) if (XDGOPEN_PROCESS.isOpen())
XDGOPEN_PROCESS.close(); XDGOPEN_PROCESS.close();
#endif #endif
/* 删除 UI 控件 */ /* Delete UI controls */
delete m_ui; delete m_ui;
} }
/**
*
*/
void UpdaterWindow::setShowWindowDisable(const bool dontShowWindow) void UpdaterWindow::setShowWindowDisable(const bool dontShowWindow)
{ {
m_dontShowUpdateWindow = dontShowWindow; m_dontShowUpdateWindow = dontShowWindow;
@ -137,28 +134,29 @@ void UpdaterWindow::setShowWindowDisable(const bool dontShowWindow)
} }
/** /**
* QSimpleUpdater * Instructs the QSimpleUpdater to download and interpret the updater
* definitions file
*/ */
void UpdaterWindow::checkForUpdates(bool force) void UpdaterWindow::checkForUpdates(bool force)
{ {
/* 更改静默标志 */ /* Change the silent flag */
if (!m_updater->getUpdateAvailable(UPDATES_URL)) { if (!m_updater->getUpdateAvailable(UPDATES_URL)) {
m_checkingForUpdates = true; m_checkingForUpdates = true;
m_forced = force; m_forced = force;
/* 设置模块属性 */ /* Set module properties */
m_updater->setNotifyOnFinish(UPDATES_URL, false); m_updater->setNotifyOnFinish(UPDATES_URL, false);
m_updater->setNotifyOnUpdate(UPDATES_URL, false); m_updater->setNotifyOnUpdate(UPDATES_URL, false);
m_updater->setDownloaderEnabled(UPDATES_URL, false); m_updater->setDownloaderEnabled(UPDATES_URL, false);
m_updater->setUseCustomInstallProcedures(UPDATES_URL, true); m_updater->setUseCustomInstallProcedures(UPDATES_URL, true);
m_updater->setModuleVersion(UPDATES_URL, qApp->applicationVersion()); m_updater->setModuleVersion(UPDATES_URL, qApp->applicationVersion());
/* 检查更新 */ /* Check for updates */
m_updater->checkForUpdates(UPDATES_URL); m_updater->checkForUpdates(UPDATES_URL);
} }
/* 如果 force 标志被设置,则显示窗口 */ /* Show window if force flag is set */
if (force) { if (force) {
if (!m_updater->getUpdateAvailable(UPDATES_URL)) { if (!m_updater->getUpdateAvailable(UPDATES_URL)) {
m_ui->updateButton->setEnabled(false); m_ui->updateButton->setEnabled(false);
@ -171,62 +169,65 @@ void UpdaterWindow::checkForUpdates(bool force)
} }
/** /**
* UI QSimpleUpdater * Resets the state, text and information displayed the the UI controls
* to indicate what is happening right now in the QSimpleUpdater
*/ */
void UpdaterWindow::resetControls() void UpdaterWindow::resetControls()
{ {
/* 重置按钮状态 */ /* Reset the button states */
m_ui->updateButton->setEnabled(false); m_ui->updateButton->setEnabled(false);
/* 设置已安装版本标签 */ /* Set installed version label */
m_ui->installedVersion->setText(qApp->applicationVersion()); m_ui->installedVersion->setText(qApp->applicationVersion());
/* 设置可用版本标签 */ /* Set available version label */
m_ui->availableVersion->setText(m_updater->getLatestVersion(UPDATES_URL)); m_ui->availableVersion->setText(m_updater->getLatestVersion(UPDATES_URL));
/* 设置标题标签 */ /* Set title label */
if (m_updater->getUpdateAvailable(UPDATES_URL)) { if (m_updater->getUpdateAvailable(UPDATES_URL)) {
m_ui->title->setText(tr("A Newer Version is Available!")); m_ui->title->setText(tr("A Newer Version is Available!"));
} else { } else {
m_ui->title->setText(tr("You're up-to-date!")); m_ui->title->setText(tr("You're up-to-date!"));
} }
/* 重置进度控件 */ /* Reset the progress controls */
m_ui->progressControls->hide(); m_ui->progressControls->hide();
m_ui->progressBar->setValue(0); m_ui->progressBar->setValue(0);
m_ui->downloadLabel->setText(tr("Downloading Updates") + "..."); m_ui->downloadLabel->setText(tr("Downloading Updates") + "...");
m_ui->timeLabel->setText(tr("Time remaining") + ": " + tr("unknown")); m_ui->timeLabel->setText(tr("Time remaining") + ": " + tr("unknown"));
/* 设置更新日志文本 */ /* Set changelog text */
QString changelogText = m_updater->getChangelog(UPDATES_URL); QString changelogText = m_updater->getChangelog(UPDATES_URL);
m_ui->changelog->setText(changelogText); m_ui->changelog->setText(changelogText);
if (m_ui->changelog->toPlainText().isEmpty()) { if (m_ui->changelog->toPlainText().isEmpty()) {
m_ui->changelog->setText("<p>No changelog found...</p>"); m_ui->changelog->setText("<p>No changelog found...</p>");
} else { } else {
m_ui->changelog->setText(changelogText.append( m_ui->changelog->setText(changelogText.append(
"\n")); // 不知道为什么当前更新日志框在底部消失了,所以添加一个新行以查看文本。 "\n")); // Don't know why currently changelog box is disappearing at the bottom, so
// I add a new line to see the text.
} }
/* 启用/禁用更新按钮 */ /* Enable/disable update button */
bool available = m_updater->getUpdateAvailable(UPDATES_URL); bool available = m_updater->getUpdateAvailable(UPDATES_URL);
bool validOpenUrl = !m_updater->getOpenUrl(UPDATES_URL).isEmpty(); bool validOpenUrl = !m_updater->getOpenUrl(UPDATES_URL).isEmpty();
bool validDownUrl = !m_updater->getDownloadUrl(UPDATES_URL).isEmpty(); bool validDownUrl = !m_updater->getDownloadUrl(UPDATES_URL).isEmpty();
m_ui->updateButton->setEnabled(available && (validOpenUrl || validDownUrl)); m_ui->updateButton->setEnabled(available && (validOpenUrl || validDownUrl));
/* 调整窗口大小 */ /* Resize window */
bool showAgain = isVisible(); bool showAgain = isVisible();
int height = minimumSizeHint().height(); int height = minimumSizeHint().height();
int width = qMax(minimumSizeHint().width(), int(height * 1.2)); int width = qMax(minimumSizeHint().width(), int(height * 1.2));
resize(QSize(width, height)); resize(QSize(width, height));
/* 重新显示窗口(如果需要) */ /* Re-show the window(if required)*/
if (showAgain) { if (showAgain) {
showNormal(); showNormal();
} }
} }
/** /**
* QSimpleUpdater * Changes the number of dots of the title label while the QSimpleUpdater
* is downloading and interpreting the update definitions file
*/ */
void UpdaterWindow::updateTitleLabel() void UpdaterWindow::updateTitleLabel()
{ {
@ -244,7 +245,8 @@ void UpdaterWindow::updateTitleLabel()
} }
/** /**
* UI QSimpleUpdater * Updates the text displayed the the UI controls to reflect the information
* obtained by the QSimpleUpdater and shows the dialog
*/ */
void UpdaterWindow::onUpdateAvailable() void UpdaterWindow::onUpdateAvailable()
{ {
@ -255,7 +257,7 @@ void UpdaterWindow::onUpdateAvailable()
} }
/** /**
* * Initializes the download of the update and disables the 'update' button
*/ */
void UpdaterWindow::onDownloadButtonClicked() void UpdaterWindow::onDownloadButtonClicked()
{ {
@ -266,23 +268,25 @@ void UpdaterWindow::onDownloadButtonClicked()
} }
/** /**
* / * Initializes the download of the update and configures the connections
* to automatically update the user interface when we receive new data
* from the download/update server
*/ */
void UpdaterWindow::startDownload(const QUrl &url) void UpdaterWindow::startDownload(const QUrl &url)
{ {
/* URL 无效,尝试打开网页浏览器 */ /* URL is invalid, try opening web browser */
if (url.isEmpty()) { if (url.isEmpty()) {
QDesktopServices::openUrl(QUrl(m_updater->getOpenUrl(UPDATES_URL))); QDesktopServices::openUrl(QUrl(m_updater->getOpenUrl(UPDATES_URL)));
return; return;
} }
/* 取消之前的下载(如果有) */ /* Cancel previous download (if any)*/
if (m_reply) { if (m_reply) {
m_reply->abort(); m_reply->abort();
m_reply->deleteLater(); m_reply->deleteLater();
} }
/* 开始下载 */ /* Start download */
m_startTime = QDateTime::currentDateTime().toSecsSinceEpoch(); m_startTime = QDateTime::currentDateTime().toSecsSinceEpoch();
QNetworkRequest netReq(url); QNetworkRequest netReq(url);
@ -290,7 +294,7 @@ void UpdaterWindow::startDownload(const QUrl &url)
QNetworkRequest::NoLessSafeRedirectPolicy); QNetworkRequest::NoLessSafeRedirectPolicy);
m_reply = m_manager->get(netReq); m_reply = m_manager->get(netReq);
/* 设置文件名 */ /* Set file name */
m_fileName = m_updater->getDownloadUrl(UPDATES_URL).split("/").last(); m_fileName = m_updater->getDownloadUrl(UPDATES_URL).split("/").last();
if (m_fileName.isEmpty()) { if (m_fileName.isEmpty()) {
m_fileName = QString("%1_Update_%2.bin") m_fileName = QString("%1_Update_%2.bin")
@ -298,37 +302,37 @@ void UpdaterWindow::startDownload(const QUrl &url)
m_updater->getLatestVersion(UPDATES_URL)); m_updater->getLatestVersion(UPDATES_URL));
} }
/* 准备下载目录 */ /* Prepare download directory */
if (!m_downloadDir.exists()) if (!m_downloadDir.exists())
m_downloadDir.mkpath("."); m_downloadDir.mkpath(".");
/* 删除之前的下载(如果有) */ /* Remove previous downloads(if any)*/
QFile::remove(m_downloadDir.filePath(m_fileName)); QFile::remove(m_downloadDir.filePath(m_fileName));
/* 显示 UI 控件 */ /* Show UI controls */
m_ui->progressControls->show(); m_ui->progressControls->show();
showNormal(); showNormal();
/* 当下载进度变化或下载完成时更新 UI */ /* Update UI when download progress changes or download finishes */
connect(m_reply, &QNetworkReply::downloadProgress, this, &UpdaterWindow::updateProgress); connect(m_reply, &QNetworkReply::downloadProgress, this, &UpdaterWindow::updateProgress);
connect(m_reply, &QNetworkReply::finished, this, &UpdaterWindow::onDownloadFinished); connect(m_reply, &QNetworkReply::finished, this, &UpdaterWindow::onDownloadFinished);
} }
/** /**
* * Opens the downloaded file
*/ */
void UpdaterWindow::openDownload(const QString &file) void UpdaterWindow::openDownload(const QString &file)
{ {
/* 文件为空,中止 */ /* File is empty, abort */
if (file.isEmpty()) { if (file.isEmpty()) {
return; return;
} }
/* 更改标签 */ /* Change labels */
m_ui->downloadLabel->setText(tr("Download finished!")); m_ui->downloadLabel->setText(tr("Download finished!"));
m_ui->timeLabel->setText(tr("Opening downloaded file") + "..."); m_ui->timeLabel->setText(tr("Opening downloaded file") + "...");
/* 尝试打开下载的文件Windows 和 Mac */ /* Try to open the downloaded file (Windows & Mac) */
#ifndef UseXdgOpen #ifndef UseXdgOpen
bool openUrl = QDesktopServices::openUrl(QUrl::fromLocalFile(file)); bool openUrl = QDesktopServices::openUrl(QUrl::fromLocalFile(file));
if (!openUrl) { if (!openUrl) {
@ -342,25 +346,27 @@ void UpdaterWindow::openDownload(const QString &file)
} }
#endif #endif
/* 在 Linux 上,使用 xdg-open 来确认文件是否被正确处理 */ /* On Linux, use xdg-open to know if the file was handled correctly */
#ifdef UseXdgOpen #ifdef UseXdgOpen
XDGOPEN_PROCESS.start("xdg-open", QStringList() << file); XDGOPEN_PROCESS.start("xdg-open", QStringList() << file);
#endif #endif
} }
/** /**
* QSimpleUpdater * Called when the \a QSimpleUpdater notifies us that it downloaded and
* interpreted the update definitions file.
* *
* / notes * This function decides whenever to show the dialog or just notify the user
* that he/she is running the latest version of notes
*/ */
void UpdaterWindow::onCheckFinished(const QString &url) void UpdaterWindow::onCheckFinished(const QString &url)
{ {
Q_UNUSED(url) Q_UNUSED(url)
/* 不允许标题标签自动更改 */ /* Do not allow the title label to change automatically */
m_checkingForUpdates = false; m_checkingForUpdates = false;
/* 如果有可用更新,显示窗口 */ /* There is an update available, show the window */
if (m_updater->getUpdateAvailable(url) && (UPDATES_URL == url)) { if (m_updater->getUpdateAvailable(url) && (UPDATES_URL == url)) {
onUpdateAvailable(); onUpdateAvailable();
} else if (m_forced) { } else if (m_forced) {
@ -371,8 +377,9 @@ void UpdaterWindow::onCheckFinished(const QString &url)
} }
/** /**
* xdg-open exitCode * Called when \c xdg-open finishes with the given \a exitCode.
* exitCode 0 * If \a exitCode is not 0, then we shall try to open the folder in which
* the update file was saved
*/ */
void UpdaterWindow::onXdgOpenFinished(const int exitCode) void UpdaterWindow::onXdgOpenFinished(const int exitCode)
{ {
@ -389,11 +396,13 @@ void UpdaterWindow::onXdgOpenFinished(const int exitCode)
} }
/** /**
* * Notifies the user that there's been an error opening the downloaded
* file directly and instructs the operating system to open the folder
* in which the \a file is located
*/ */
void UpdaterWindow::openDownloadFolder(const QString &file) void UpdaterWindow::openDownloadFolder(const QString &file)
{ {
/* 通知用户问题 */ /* Notify the user of the problem */
QString extension = file.split(".").last(); QString extension = file.split(".").last();
QMessageBox::information(this, tr("Open Error"), QMessageBox::information(this, tr("Open Error"),
tr("It seems that your OS does not have an " tr("It seems that your OS does not have an "
@ -402,29 +411,30 @@ void UpdaterWindow::openDownloadFolder(const QString &file)
.arg(extension), .arg(extension),
QMessageBox::Ok); QMessageBox::Ok);
/* 获取下载文件的完整路径列表 */ /* Get the full path list of the downloaded file */
QString native_path = QDir::toNativeSeparators(QDir(file).absolutePath()); QString native_path = QDir::toNativeSeparators(QDir(file).absolutePath());
QStringList directories = native_path.split(QDir::separator()); QStringList directories = native_path.split(QDir::separator());
/* 从列表中删除文件名以获取更新文件的文件夹 */ /* Remove file name from list to get the folder of the update file */
directories.removeLast(); directories.removeLast();
QString path = directories.join(QDir::separator()); QString path = directories.join(QDir::separator());
/* 获取有效的 URL 并打开它 */ /* Get valid URL and open it */
QUrl url = QUrl::fromLocalFile(QDir(path).absolutePath()); QUrl url = QUrl::fromLocalFile(QDir(path).absolutePath());
QDesktopServices::openUrl(url); QDesktopServices::openUrl(url);
} }
/** /**
* KB MB * Calculates the appropriate size units(bytes, KB or MB)for the received
* /UI * data and the total download size. Then, this function proceeds to update the
* dialog controls/UI.
*/ */
void UpdaterWindow::calculateSizes(qint64 received, qint64 total) void UpdaterWindow::calculateSizes(qint64 received, qint64 total)
{ {
QString totalSize; QString totalSize;
QString receivedSize; QString receivedSize;
/* 获取总大小字符串 */ /* Get total size string */
if (total < 1024) { if (total < 1024) {
totalSize = tr("%1 bytes").arg(total); totalSize = tr("%1 bytes").arg(total);
} else if (total < (1024 * 1024)) { } else if (total < (1024 * 1024)) {
@ -433,7 +443,7 @@ void UpdaterWindow::calculateSizes(qint64 received, qint64 total)
totalSize = tr("%1 MB").arg(round(total / (1024 * 1024))); totalSize = tr("%1 MB").arg(round(total / (1024 * 1024)));
} }
/* 获取接收大小字符串 */ /* Get received size string */
if (received < 1024) { if (received < 1024) {
receivedSize = tr("%1 bytes").arg(received); receivedSize = tr("%1 bytes").arg(received);
} else if (received < (1024 * 1024)) { } else if (received < (1024 * 1024)) {
@ -442,13 +452,14 @@ void UpdaterWindow::calculateSizes(qint64 received, qint64 total)
receivedSize = tr("%1 MB").arg(received / (1024 * 1024)); receivedSize = tr("%1 MB").arg(received / (1024 * 1024));
} }
/* 更新标签文本 */ /* Update the label text */
m_ui->downloadLabel->setText(tr("Downloading updates") + " (" + receivedSize + " " + tr("of") m_ui->downloadLabel->setText(tr("Downloading updates") + " (" + receivedSize + " " + tr("of")
+ " " + totalSize + ")"); + " " + totalSize + ")");
} }
/** /**
* 使 received total * Uses the \a received and \a total parameters to get the download progress
* and update the progressbar value on the dialog.
*/ */
void UpdaterWindow::updateProgress(qint64 received, qint64 total) void UpdaterWindow::updateProgress(qint64 received, qint64 total)
{ {
@ -469,9 +480,12 @@ void UpdaterWindow::updateProgress(qint64 received, qint64 total)
} }
/** /**
* 使 * Uses two time samples(from the current time and a previous sample)to
* calculate how many bytes have been downloaded.
* *
* * Then, this function proceeds to calculate the appropriate units of time
*(hours, minutes or seconds)and constructs a user-friendly string, which
* is displayed in the dialog.
*/ */
void UpdaterWindow::calculateTimeRemaining(qint64 received, qint64 total) void UpdaterWindow::calculateTimeRemaining(qint64 received, qint64 total)
{ {
@ -505,4 +519,83 @@ void UpdaterWindow::calculateTimeRemaining(qint64 received, qint64 total)
if (seconds > 1) { if (seconds > 1) {
timeString = tr("%1 seconds").arg(seconds); timeString = tr("%1 seconds").arg(seconds);
} else { } else {
timeString timeString = tr("1 second");
}
}
m_ui->timeLabel->setText(tr("Time remaining") + ": " + timeString);
}
}
void UpdaterWindow::onDownloadFinished()
{
QString redirectedUrl =
m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toString();
if (redirectedUrl.isEmpty()) {
const QString filePath = m_downloadDir.filePath(m_fileName);
QFile file(filePath);
if (file.open(QIODevice::WriteOnly | QIODevice::Append)) {
file.write(m_reply->readAll());
file.close();
qApp->processEvents();
}
openDownload(filePath);
} else {
startDownload(redirectedUrl);
}
}
/**
* Allows the user to move the window and registers the position in which
* the user originally clicked to move the window
*/
void UpdaterWindow::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
if (event->x() < width() - 5 && event->x() > 5 && event->pos().y() < height() - 5
&& event->pos().y() > 5) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
m_canMoveWindow = !window()->windowHandle()->startSystemMove();
#else
m_canMoveWindow = true;
#endif
m_mousePressX = event->pos().x();
m_mousePressY = event->pos().y();
}
}
event->accept();
}
/**
* Changes the cursor icon to a hand(to hint the user that he/she is dragging
* the window)and moves the window to the desired position of the given
* \a event
*/
void UpdaterWindow::mouseMoveEvent(QMouseEvent *event)
{
if (m_canMoveWindow) {
int dx = event->globalX() - m_mousePressX;
int dy = event->globalY() - m_mousePressY;
move(dx, dy);
}
}
/**
* Disallows the user to move the window and resets the window cursor
*/
void UpdaterWindow::mouseReleaseEvent(QMouseEvent *event)
{
m_canMoveWindow = false;
unsetCursor();
event->accept();
}
/**
* Rounds the given \a input to two decimal places
*/
qreal UpdaterWindow::round(qreal input)
{
return qreal(roundf(float(input * 100)) / 100);
}

@ -19,87 +19,64 @@ class QNetworkReply;
class QSimpleUpdater; class QSimpleUpdater;
class QNetworkAccessManager; class QNetworkAccessManager;
// UpdaterWindow 类用于管理更新窗口的显示和更新逻辑
class UpdaterWindow : public QDialog class UpdaterWindow : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
// 构造函数,初始化更新窗口
explicit UpdaterWindow(QWidget *parent = 0); explicit UpdaterWindow(QWidget *parent = 0);
// 析构函数,释放资源
~UpdaterWindow(); ~UpdaterWindow();
// 设置是否显示更新窗口
void setShowWindowDisable(const bool dontShowWindow); void setShowWindowDisable(const bool dontShowWindow);
public slots: public slots:
// 检查更新force 参数表示是否强制检查
void checkForUpdates(bool force); void checkForUpdates(bool force);
signals: signals:
// 信号,当更新窗口显示状态改变时发出
void dontShowUpdateWindowChanged(bool state); void dontShowUpdateWindowChanged(bool state);
private slots: private slots:
// 重置控件状态
void resetControls(); void resetControls();
// 更新标题标签
void updateTitleLabel(); void updateTitleLabel();
// 当有可用更新时调用
void onUpdateAvailable(); void onUpdateAvailable();
// 当下载按钮被点击时调用
void onDownloadButtonClicked(); void onDownloadButtonClicked();
// 开始下载url 参数表示下载链接
void startDownload(const QUrl &url); void startDownload(const QUrl &url);
// 打开下载的文件file 参数表示文件路径
void openDownload(const QString &file); void openDownload(const QString &file);
// 当检查更新完成时调用url 参数表示检查的链接
void onCheckFinished(const QString &url); void onCheckFinished(const QString &url);
// 当 xdg-open 命令完成时调用exitCode 参数表示退出码
void onXdgOpenFinished(const int exitCode); void onXdgOpenFinished(const int exitCode);
// 打开下载文件夹file 参数表示文件路径
void openDownloadFolder(const QString &file); void openDownloadFolder(const QString &file);
// 计算下载大小received 参数表示已接收的字节数total 参数表示总字节数
void calculateSizes(qint64 received, qint64 total); void calculateSizes(qint64 received, qint64 total);
// 更新下载进度received 参数表示已接收的字节数total 参数表示总字节数
void updateProgress(qint64 received, qint64 total); void updateProgress(qint64 received, qint64 total);
// 计算剩余下载时间received 参数表示已接收的字节数total 参数表示总字节数
void calculateTimeRemaining(qint64 received, qint64 total); void calculateTimeRemaining(qint64 received, qint64 total);
// 当下载完成时调用
void onDownloadFinished(); void onDownloadFinished();
protected: protected:
// 处理鼠标移动事件
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
// 处理鼠标按下事件
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
// 处理鼠标释放事件
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
private: private:
// 四舍五入函数input 参数表示输入值
qreal round(qreal input); qreal round(qreal input);
private: private:
QString m_fileName; // 文件名 QString m_fileName;
const QDir m_downloadDir; // 下载目录 const QDir m_downloadDir;
Ui::UpdaterWindow *m_ui; // 用户界面指针 Ui::UpdaterWindow *m_ui;
QPoint m_dragPosition; // 窗口拖动位置 QPoint m_dragPosition;
int m_mousePressX; // 鼠标按下时的 X 坐标 int m_mousePressX;
int m_mousePressY; // 鼠标按下时的 Y 坐标 int m_mousePressY;
bool m_canMoveWindow; // 是否可以移动窗口 bool m_canMoveWindow;
bool m_checkingForUpdates; // 是否正在检查更新 bool m_checkingForUpdates;
bool m_dontShowUpdateWindow; // 是否不显示更新窗口 bool m_dontShowUpdateWindow;
bool m_forced; // 是否强制检查更新 bool m_forced;
uint m_startTime; // 下载开始时间 uint m_startTime;
QNetworkReply *m_reply; // 网络回复指针 QNetworkReply *m_reply;
QSimpleUpdater *m_updater; // 更新器指针 QSimpleUpdater *m_updater;
QNetworkAccessManager *m_manager; // 网络访问管理器指针 QNetworkAccessManager *m_manager;
}; };
#endif #endif
Loading…
Cancel
Save