Files

138 lines
5.5 KiB
QML
Raw Permalink Normal View History

import QtQuick
import QtQuick.Controls
import Flora
Item {
id: root
property string markdown: ""
property string vaultRoot: ""
property string fileDir: ""
property string emptyText: qsTr("Пустой файл — начните писать в режиме редактирования")
// Если задан — markdown считается старой версией и подсвечиваются отличия от него
// (блоки del — только в markdown, add — только в compareMarkdown).
// Сравнение считается в рабочем потоке, чтобы UI не висел на больших файлах.
property string compareMarkdown: ""
property int _diffReq: -1
implicitHeight: column.implicitHeight + 24
signal linkActivated(string href)
signal diffApplied()
// Для обновления. Async-ветка идёт через дебаунс: updateDiff() в диалоге меняет
// markdown и compareMarkdown по очереди, без дебаунса уходили бы два запроса
// (первый заведомо протухает и отбрасывается по _diffReq).
Timer { id: asyncDebounce; interval: 80; onTriggered: root._requestDiff() }
function _requestDiff() {
if (compareMarkdown.length === 0)
return // пока таймер ждал, сравнение уже сняли
_diffReq = mdParser.parseBlocksWithDiffAsync(markdown, compareMarkdown, vaultRoot, fileDir)
}
function update() {
if (compareMarkdown.length > 0) {
asyncDebounce.restart()
return
}
_diffReq = -1 // гасим возможный async в полёте: его ответ станет чужим
// Триггерим пересборку блоков
blocksModel.clear()
const blocks = mdParser.parseBlocks(markdown, vaultRoot, fileDir)
for (let i = 0; i < blocks.length; i++) {
blocksModel.append(blocks[i])
}
// Позиция скролла в начало
// contentItem.contentY = 0
}
// База изменилась (переиндексация, правка свойств, вкл/выкл SQL) —
// ~sql-блоки надо пересчитать. Дебаунс против шторма при reindex.
Timer { id: dbDebounce; interval: 400; onTriggered: root.update() }
Connections {
target: vaultDatabase
function onChanged() { dbDebounce.restart() }
function onQueryEnabledChanged() { dbDebounce.restart() }
function onMaxRowsChanged() { dbDebounce.restart() }
}
Connections {
target: mdParser
function onBlocksReady(reqId, blocks) {
if (reqId !== root._diffReq)
return // устарел — уже запрошен более свежий
blocksModel.clear()
for (let i = 0; i < blocks.length; i++) {
blocksModel.append(blocks[i])
}
root.diffApplied()
}
}
onMarkdownChanged: update()
onCompareMarkdownChanged: update()
onVaultRootChanged: update()
onFileDirChanged: update()
Component.onCompleted: update()
ListModel { id: blocksModel }
Column {
id: column
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 12
spacing: 8
Repeater {
model: blocksModel
delegate: Item {
required property var model
required property int index
width: parent.width
height: childrenRect.height
property string blockType: model.type || ""
property string blockHtml: model.html || ""
property string blockCode: model.codeText || ""
property string blockLang: model.language || "text"
property string blockCodeId: model.codeId || ""
property string blockHighlighted: model.highlightedText || ""
// HTML block
Text {
visible: blockType === "html" && blockHtml
width: parent.width
text: blockHtml
textFormat: Text.RichText
wrapMode: Text.Wrap
color: Theme.text
font.family: "Menlo, Consolas, 'DejaVu Sans Mono', monospace"
font.pixelSize: 14
onLinkActivated: (href) => root.linkActivated(href)
}
// Code block - separate component
CodeBlock {
visible: blockType === "code"
width: parent.width
code: blockCode
language: blockLang
codeId: blockCodeId
highlightedText: blockHighlighted
onCopyRequested: (id) => {
if (mdParser.copyCodeBlock(id))
root.linkActivated("flora://copycode/" + id)
}
}
}
}
Label {
width: parent.width
text: markdown.trim().length === 0 ? emptyText : ""
color: Theme.textFaint
wrapMode: Text.Wrap
font.italic: true
visible: markdown.trim().length === 0 && blocksModel.count === 0
}
}
}