162 lines
5.3 KiB
QML
162 lines
5.3 KiB
QML
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
|
|
// A folding outline of a document's sections. Each row renders the section
|
|
// headline as distinct structural objects (keyword, priority, title, tags,
|
|
// timestamps) followed by the section body, always visible inline — the
|
|
// document is the control surface.
|
|
TreeView {
|
|
id: tree
|
|
clip: true
|
|
|
|
readonly property color textColor: "#d8dee9"
|
|
readonly property color dimColor: "#7a8290"
|
|
readonly property int rowPad: 6
|
|
|
|
// Palette for TODO keywords by state.
|
|
function keywordColor(kw) {
|
|
switch (kw) {
|
|
case "TODO": return "#bf616a"
|
|
case "NEXT": return "#d08770"
|
|
case "WAITING": return "#ebcb8b"
|
|
case "DONE": return "#a3be8c"
|
|
case "CANCELLED": return "#7a8290"
|
|
default: return "#88c0d0"
|
|
}
|
|
}
|
|
|
|
// Headline text scales down slightly with depth.
|
|
function levelFontSize(level) {
|
|
return Math.max(13, 19 - level)
|
|
}
|
|
|
|
selectionModel: ItemSelectionModel {}
|
|
|
|
delegate: TreeViewDelegate {
|
|
id: del
|
|
indentation: 18
|
|
|
|
readonly property string sectionBody: (model.body ?? "").trim()
|
|
readonly property int bodyIndent: 12
|
|
|
|
implicitWidth: tree.width
|
|
|
|
background: Rectangle {
|
|
color: del.current ? "#2f3540" : "transparent"
|
|
radius: 4
|
|
}
|
|
|
|
contentItem: ColumnLayout {
|
|
spacing: 4
|
|
|
|
RowLayout {
|
|
id: headlineRow
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: tree.rowPad
|
|
spacing: 8
|
|
|
|
// TODO keyword pill.
|
|
Rectangle {
|
|
visible: (model.keyword ?? "") !== ""
|
|
radius: 3
|
|
color: "transparent"
|
|
border.color: tree.keywordColor(model.keyword ?? "")
|
|
border.width: 1
|
|
implicitWidth: kwLabel.implicitWidth + 10
|
|
implicitHeight: kwLabel.implicitHeight + 4
|
|
Label {
|
|
id: kwLabel
|
|
anchors.centerIn: parent
|
|
text: model.keyword ?? ""
|
|
color: tree.keywordColor(model.keyword ?? "")
|
|
font.pixelSize: 11
|
|
font.bold: true
|
|
}
|
|
}
|
|
|
|
// Priority cookie.
|
|
Label {
|
|
visible: (model.priority ?? "") !== ""
|
|
text: "[#" + (model.priority ?? "") + "]"
|
|
color: "#b48ead"
|
|
font.pixelSize: 12
|
|
font.bold: true
|
|
}
|
|
|
|
// Title.
|
|
Label {
|
|
text: (model.title ?? "")
|
|
color: (model.isDone ?? false) ? tree.dimColor : tree.textColor
|
|
font.pixelSize: tree.levelFontSize(model.level ?? 1)
|
|
font.strikeout: (model.isDone ?? false)
|
|
elide: Text.ElideRight
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
// Scheduled / deadline stamps.
|
|
Label {
|
|
visible: (model.scheduled ?? "") !== ""
|
|
text: "⏱ " + (model.scheduled ?? "")
|
|
color: "#81a1c1"
|
|
font.pixelSize: 11
|
|
}
|
|
Label {
|
|
visible: (model.deadline ?? "") !== ""
|
|
text: "⚑ " + (model.deadline ?? "")
|
|
color: "#bf616a"
|
|
font.pixelSize: 11
|
|
}
|
|
|
|
// Tags.
|
|
Repeater {
|
|
model: del.model.tags ?? []
|
|
delegate: Rectangle {
|
|
required property string modelData
|
|
radius: 3
|
|
color: "#3b4252"
|
|
implicitWidth: tagLabel.implicitWidth + 8
|
|
implicitHeight: tagLabel.implicitHeight + 3
|
|
Label {
|
|
id: tagLabel
|
|
anchors.centerIn: parent
|
|
text: parent.modelData
|
|
color: "#8fbcbb"
|
|
font.pixelSize: 10
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Body row with left rule.
|
|
RowLayout {
|
|
visible: del.sectionBody !== ""
|
|
Layout.fillWidth: true
|
|
Layout.bottomMargin: tree.rowPad
|
|
spacing: 8
|
|
|
|
// Left rule marking the body region.
|
|
Rectangle {
|
|
width: 2
|
|
Layout.fillHeight: true
|
|
Layout.leftMargin: 2
|
|
color: "#3b4252"
|
|
}
|
|
|
|
// Section body text.
|
|
Text {
|
|
id: bodyLabel
|
|
Layout.fillWidth: true
|
|
Layout.leftMargin: del.bodyIndent - 10
|
|
text: del.sectionBody
|
|
color: tree.dimColor
|
|
font.pixelSize: 12
|
|
lineHeight: 1.15
|
|
wrapMode: Text.WordWrap
|
|
textFormat: Text.PlainText
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|