From e298fd7124b180c45e4d7a52d771f6e7533a9aab Mon Sep 17 00:00:00 2001 From: zhangtingan Date: Mon, 17 Aug 2026 13:43:51 +0800 Subject: [PATCH] fix: persist and restore mark color highlights across sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增隐藏配置项 advance.editor.markcolor 用于按文件路径持久化标记颜色信息; 2. 在 StartManager 中新增 initMarkColor/saveMarkColor/recordMarkColor/findMarkColor,仿书签范式实现标记颜色的启动读取、关窗/会话保存、变更缓存与按路径查找; 3. 在会话保存(autoBackupFile)与关闭标签页(closeTab)处记录当前标记颜色,在关窗与周期备份处落盘; 4. 在 TextEdit 中新增 getMarkColorInfo/setMarkColorList,复用既有 MarkReplaceInfo/convertMarkToReplace/manualUpdateAllMark 模型,用当前文档光标重建选中区间后恢复内存标记并刷新显示; 5. 在 EditWrapper::handleFileLoadFinished 文件内容加载完成后按路径恢复标记颜色,解决位置型数据需待内容就绪的时序问题; ===================================== 1. added hidden config option advance.editor.markcolor to persist mark color info keyed by file path; 2. added initMarkColor/saveMarkColor/recordMarkColor/findMarkColor in StartManager, mirroring the bookmark pattern for startup load, close/session save, change cache and path-based lookup; 3. record current mark color on session save (autoBackupFile) and on tab close (closeTab), persist on window close and periodic backup; 4. added getMarkColorInfo/setMarkColorList in TextEdit, reusing the existing MarkReplaceInfo/convertMarkToReplace/manualUpdateAllMark model, rebuilding selections with the live document cursor before restoring in-memory marks and refreshing display; 5. restore mark color in EditWrapper::handleFileLoadFinished after content is loaded, fixing the timing requirement that position-based data needs the content ready; Log: 修复文本编辑器标记颜色无法保存的问题,新增标记颜色的持久化与重开恢复机制,标记颜色在保存关闭后重开文件可正确恢复 Bug: https://pms.uniontech.com/bug-view-328067.html --- src/editor/dtextedit.cpp | 31 +++++++++ src/editor/dtextedit.h | 12 ++++ src/editor/editwrapper.cpp | 6 ++ src/resources/settings.json | 6 ++ src/startmanager.cpp | 122 ++++++++++++++++++++++++++++++++++++ src/startmanager.h | 10 +++ src/widgets/window.cpp | 5 ++ 7 files changed, 192 insertions(+) diff --git a/src/editor/dtextedit.cpp b/src/editor/dtextedit.cpp index 1893f77cd..3965d6808 100644 --- a/src/editor/dtextedit.cpp +++ b/src/editor/dtextedit.cpp @@ -4768,6 +4768,37 @@ void TextEdit::setBookMarkList(QList bookMarkList) m_listBookmark = bookMarkList; } +QList TextEdit::getMarkColorInfo() +{ + // 将当前标记操作转换为含绝对位置的替换信息,作为持久化载体 + return convertMarkToReplace(m_markOperations); +} + +void TextEdit::setMarkColorList(const QList &markInfo) +{ + // 持久化数据仅含绝对位置,需用当前文档构造有效光标后再交由既有逻辑恢复 + QList> markList; + const int docLen = document()->characterCount(); + for (const auto &info : markInfo) { + MarkOperation markOpt; + markOpt.type = info.opt.type; + markOpt.color = info.opt.color; + markOpt.matchText = info.opt.matchText; + + // 用当前文档构造有效光标,避免持久化数据中光标无文档导致恢复失败 + markOpt.cursor = QTextCursor(document()); + // 限制位置在当前文档范围内,文件被外部修改后不致越界 + int start = qBound(0, static_cast(info.start), docLen); + int end = qBound(0, static_cast(info.end), docLen); + markOpt.cursor.setPosition(start); + markOpt.cursor.setPosition(end, QTextCursor::KeepAnchor); + + markList.append(qMakePair(markOpt, info.time)); + } + + manualUpdateAllMark(markList); +} + void TextEdit::updateSaveIndex() { m_lastSaveIndex = m_pUndoStack->index(); diff --git a/src/editor/dtextedit.h b/src/editor/dtextedit.h index de2206a5c..32e513fcc 100644 --- a/src/editor/dtextedit.h +++ b/src/editor/dtextedit.h @@ -455,6 +455,18 @@ class TextEdit : public DPlainTextEdit */ void setBookMarkList(QList bookMarkList); + /** + * @brief getMarkColorInfo 得到当前标记颜色信息(含绝对位置) + * @return 标记颜色信息列表 + */ + QList getMarkColorInfo(); + + /** + * @brief setMarkColorList 从持久化数据恢复标记颜色,重建内存标记并刷新显示 + * @param markInfo 标记颜色信息列表(含绝对位置,光标无需有效) + */ + void setMarkColorList(const QList &markInfo); + /** * 更新上次保存时的撤销回收栈的索引值 */ diff --git a/src/editor/editwrapper.cpp b/src/editor/editwrapper.cpp index b8acff1d3..317331341 100644 --- a/src/editor/editwrapper.cpp +++ b/src/editor/editwrapper.cpp @@ -5,6 +5,7 @@ #include "editwrapper.h" #include "leftareaoftextedit.h" #include "drecentmanager.h" +#include "../startmanager.h" #include @@ -939,6 +940,11 @@ void EditWrapper::handleFileLoadFinished(const QByteArray &encode, const QByteAr m_pTextEdit->setTextFinished(); + // 文件内容加载完成后,恢复该文件的标记颜色(位置型数据需待内容就绪) + QString markLocalPath = m_pTextEdit->getTruePath(); + QList markColorInfo = StartManager::instance()->findMarkColor(markLocalPath); + m_pTextEdit->setMarkColorList(markColorInfo); + QStringList temFileList = Settings::instance()->settings->option("advance.editor.browsing_history_temfile")->value().toStringList(); for (int var = 0; var < temFileList.count(); ++var) { diff --git a/src/resources/settings.json b/src/resources/settings.json index d84ccfd45..1d1231300 100644 --- a/src/resources/settings.json +++ b/src/resources/settings.json @@ -2133,6 +2133,12 @@ "hide": true, "reset": false, "default": "" + }, + { + "key": "markcolor", + "hide": true, + "reset": false, + "default": "" } ] }, diff --git a/src/startmanager.cpp b/src/startmanager.cpp index 630c89e55..7f89a18ca 100644 --- a/src/startmanager.cpp +++ b/src/startmanager.cpp @@ -14,6 +14,7 @@ #include #include #include +#include //#include DWIDGET_USE_NAMESPACE @@ -26,6 +27,8 @@ enum BackupInterval { // 用于配置文件书签的标识 static const QString s_bookMarkKey = "advance.editor.bookmark"; +// 用于配置文件标记颜色的标识 +static const QString s_markColorKey = "advance.editor.markcolor"; StartManager *StartManager::m_instance = nullptr; @@ -61,6 +64,8 @@ StartManager::StartManager(QObject *parent) m_qlistTemFile = Settings::instance()->settings->option("advance.editor.browsing_history_temfile")->value().toStringList(); // 初始化书签信息记录表 initBookmark(); + // 初始化标记颜色信息记录表 + initMarkColor(); //按时间自动备份(5分钟) m_pTimer = new QTimer; @@ -190,6 +195,10 @@ void StartManager::autoBackupFile() m_bookmarkTable.remove(localPath); } + //记录标记颜色 + QList markColorList = wrapper->textEditor()->getMarkColorInfo(); + recordMarkColor(localPath, markColorList); + //记录活动页 if (m_windows.value(var)->isActiveWindow()) { if (wrapper == m_windows.value(var)->currentWrapper()) { @@ -223,6 +232,8 @@ void StartManager::autoBackupFile() Settings::instance()->settings->option("advance.editor.browsing_history_temfile")->setValue(m_qlistTemFile); // 备份书签信息 saveBookmark(); + // 备份标记颜色信息 + saveMarkColor(); } int StartManager::recoverFile(Window *window) @@ -650,6 +661,8 @@ void StartManager::slotCloseWindow() if (m_windows.isEmpty()) { // 保存书签信息 saveBookmark(); + // 保存标记颜色信息 + saveMarkColor(); QDir path = QDir::currentPath(); if (!path.exists()) { @@ -832,6 +845,115 @@ QList StartManager::findBookmark(const QString &localPath) return m_bookmarkTable.value(localPath); } +/** + * @brief 主动更新记录文件 \a localPath 的标记颜色信息,空列表会清除该文件的记录。 + * @param localPath 文件路径 + * @param markColor 标记颜色信息(含绝对位置) + */ +void StartManager::recordMarkColor(const QString &localPath, const QList &markColor) +{ + if (markColor.isEmpty()) { + // 无标记颜色,移除历史记录 + m_markColorTable.remove(localPath); + } else { + m_markColorTable.insert(localPath, markColor); + } +} + +/** + * @return 返回文件 \a localPath 的标记颜色记录 + */ +QList StartManager::findMarkColor(const QString &localPath) +{ + return m_markColorTable.value(localPath); +} + +/** + * @brief 从配置文件中获取全局的标记颜色信息,包括已关闭的所有文件标记颜色, + * 会遍历每条记录并判断文件是否存在,若文件被删除或移动,则不再记录对应信息。 + */ +void StartManager::initMarkColor() +{ + // 遍历标记颜色信息列表 + QStringList markColorInfoList = Settings::instance()->settings->value(s_markColorKey).toStringList(); + for (const QString &markColorInfo : markColorInfoList) { + QJsonParseError readError; + QJsonDocument doc = QJsonDocument::fromJson(markColorInfo.toUtf8(), &readError); + if (QJsonParseError::NoError == readError.error + && !doc.isNull()) { + QJsonObject obj = doc.object(); + QString filePath = obj.value("localPath").toString(); + + // 判断文件是否仍存在,若不存在,则不保留标记颜色信息 + if (!filePath.isEmpty() + && QFileInfo::exists(filePath)) { + QList markList; + QJsonArray marksArray = obj.value("marks").toArray(); + for (const QJsonValue &markValue : marksArray) { + QJsonObject markObj = markValue.toObject(); + TextEdit::MarkReplaceInfo info; + info.opt.type = static_cast(markObj.value("type").toInt()); + info.opt.color = markObj.value("color").toString(); + info.opt.matchText = markObj.value("matchText").toString(); + info.start = markObj.value("start").toInt(); + info.end = markObj.value("end").toInt(); + info.time = static_cast(markObj.value("time").toDouble()); + markList.append(info); + } + + if (!markList.isEmpty()) { + // 文件存在且标记颜色非空,缓存标记颜色信息 + m_markColorTable.insert(filePath, markList); + } + } + } + } +} + +/** + * @brief 将当前记录的所有文件的标记颜色信息转换为 Json 数据列表记录到配置信息中, + * 被删除或移动文件的标记颜色将被销毁。 + */ +void StartManager::saveMarkColor() +{ + QStringList recordInfo; + // 遍历记录 + for (auto itr = m_markColorTable.begin(); itr != m_markColorTable.end();) { + if (!QFileInfo::exists(itr.key()) + || itr.value().isEmpty()) { + // 文件不存在或无标记颜色则销毁记录 + itr = m_markColorTable.erase(itr); + } else { + QJsonObject obj; + obj.insert("localPath", itr.key()); + + // 遍历标记颜色信息并组合 + QJsonArray marksArray; + const auto &markList = itr.value(); + for (const TextEdit::MarkReplaceInfo &info : markList) { + QJsonObject markObj; + markObj.insert("type", static_cast(info.opt.type)); + markObj.insert("color", info.opt.color); + markObj.insert("matchText", info.opt.matchText); + markObj.insert("start", info.start); + markObj.insert("end", info.end); + // time 为 qint64,超过 int 范围时用 double 保证精度 + markObj.insert("time", static_cast(info.time)); + marksArray.append(markObj); + } + obj.insert("marks", marksArray); + + QJsonDocument doc(obj); + recordInfo.append(doc.toJson(QJsonDocument::Compact)); + + ++itr; + } + } + + // 将标记颜色信息保存至配置文件 + Settings::instance()->settings->option(s_markColorKey)->setValue(recordInfo); +} + void StartManager::initBlockShutdown() { if (m_reply.value().isValid()) { diff --git a/src/startmanager.h b/src/startmanager.h index 7b5f22da7..df304a4bb 100644 --- a/src/startmanager.h +++ b/src/startmanager.h @@ -69,6 +69,11 @@ class StartManager : public QObject // 查找文件对应的书签记录 QList findBookmark(const QString &localPath); + // 主动更新记录标记颜色信息 + void recordMarkColor(const QString &localPath, const QList &markColor); + // 查找文件对应的标记颜色记录 + QList findMarkColor(const QString &localPath); + // 延迟释放堆内存 void delayMallocTrim(); @@ -105,6 +110,10 @@ public slots: void initBookmark(); // 保存书签信息 void saveBookmark(); + // 初始化标记颜色信息 + void initMarkColor(); + // 保存标记颜色信息 + void saveMarkColor(); private: static StartManager *m_instance; @@ -119,6 +128,7 @@ public slots: QScopedPointer m_pEntry; QStringList m_qlistTemFile; ///< 备份信息列表 QHash> m_bookmarkTable; ///< 书签标记信息表 + QHash> m_markColorTable; ///< 标记颜色信息表 QTimer *m_pTimer; QBasicTimer m_DelayTimer; ///< 延迟备份定时器 QString m_blankFileDir; ///< 新建文件目录 diff --git a/src/widgets/window.cpp b/src/widgets/window.cpp index 73d1a5000..52dd3e3a4 100644 --- a/src/widgets/window.cpp +++ b/src/widgets/window.cpp @@ -815,6 +815,11 @@ bool Window::closeTab(const QString &filePath) StartManager::instance()->recordBookmark(localPath, bookmarkInfo); } + // 关闭标签页前,记录全局的标记颜色信息(空列表会清除历史记录) + QList markColorInfo = wrapper->textEditor()->getMarkColorInfo(); + QString markLocalPath = wrapper->textEditor()->getTruePath(); + StartManager::instance()->recordMarkColor(markLocalPath, markColorInfo); + if (isDraftFile) { if (isModified) { DDialog *dialog = createDialog(tr("Do you want to save this file?"), "");