Files
menologion/quickshell/modules/Horologion.qml
T

209 lines
7.1 KiB
QML
Raw Normal View History

2026-09-18 09:17:14 -04:00
import QtQuick
import QtQuick.Layouts
import qs
import qs.services
Panel {
id: root
title: "Church calendar"
// The church date, always: on the old calendar it is the answer people
// came for, and on the new it is a harmless echo of the title bar.
note: Typikon.available ? Typikon.churchDate : ""
2026-09-18 09:17:14 -04:00
// The glyph carries what kind of fast it is, so obligation has to be shown
// some other way or it disappears with the text: the weekday takes the
// colour. Under the Typikon nothing binds and everything is yellow; under
// a particular law that does bind, the days that bind go red. A panel that
// blurs the two states something untrue.
function tint(d) {
if (!d) return Theme.fgDim;
if (d.fast === "none") return Theme.fgDim;
return d.obligation === "obligatory" ? Theme.red : Theme.yellow;
}
// The legend is built from the week in view rather than hardcoded, because
// the levels a tradition uses are its own — six under the Typikon, three
// under Ruthenian law — and the words for them differ too.
readonly property var legend: {
const seen = [], out = [];
for (const d of Typikon.week) {
if (!d.emoji || seen.indexOf(d.fast) !== -1) continue;
seen.push(d.fast);
out.push({ emoji: d.emoji, label: d.fastLabel });
}
return out;
}
ColumnLayout {
spacing: 7
Text {
visible: !Typikon.available
Layout.fillWidth: true
text: Typikon.error !== "" ? Typikon.error
: "unavailable — is `typikon` on PATH?"
color: Theme.fgDim
wrapMode: Text.WordWrap
font.family: Config.font
font.pixelSize: Config.fontSize - 1
}
// Today, given room because it is the day being asked about.
RowLayout {
Layout.fillWidth: true
visible: Typikon.available
spacing: 11
Text {
text: Typikon.emoji
font.family: Config.font
font.pixelSize: 28
}
ColumnLayout {
Layout.fillWidth: true
spacing: 1
Text {
Layout.fillWidth: true
text: Typikon.title
color: Typikon.feast !== "" ? Theme.accent : Theme.fg
wrapMode: Text.WordWrap
maximumLineCount: 2
elide: Text.ElideRight
font.family: Config.font
font.pixelSize: Config.fontSize
font.bold: true
}
Text {
Layout.fillWidth: true
text: {
const bits = [Typikon.weekday];
if (Typikon.season !== "") bits.push(Typikon.season);
if (Typikon.obligatory) bits.push("obligatory");
return bits.join(" · ");
}
color: root.tint(Typikon.today)
elide: Text.ElideRight
font.family: Config.font
font.pixelSize: Config.fontSize - 3
font.bold: Typikon.obligatory
}
// Forefeast, afterfeast, leave-taking. These repeat for days at
// a time, so they get their own quiet line rather than crowding
// out the commemoration that makes the day itself.
Text {
Layout.fillWidth: true
visible: Typikon.cycle !== ""
text: Typikon.cycle
color: Qt.alpha(Theme.fgDim, 0.8)
elide: Text.ElideRight
font.family: Config.font
font.pixelSize: Config.fontSize - 3
font.italic: true
}
}
}
// The rest of today's commemorations, under it.
ColumnLayout {
Layout.fillWidth: true
Layout.leftMargin: 39
visible: Typikon.available
spacing: 0
Repeater {
model: Typikon.commemorations.filter(c => c !== Typikon.title).slice(0, 3)
delegate: Text {
required property var modelData
Layout.fillWidth: true
text: "· " + modelData
color: Theme.fgDim
elide: Text.ElideRight
font.family: Config.font
font.pixelSize: Config.fontSize - 3
}
}
}
Rectangle {
Layout.fillWidth: true
visible: Typikon.week.length > 1
implicitHeight: 1
color: Qt.alpha(Theme.overlay, 0.6)
}
// The rest of the week: glyph, weekday, and what is kept that day.
Repeater {
model: Typikon.week.length > 1 ? Typikon.week.slice(1) : []
delegate: RowLayout {
required property var modelData
Layout.fillWidth: true
spacing: 9
Text {
text: modelData.emoji
font.family: Config.font
font.pixelSize: Config.fontSize + 3
}
Text {
Layout.preferredWidth: 32
text: modelData.weekday.substring(0, 3)
color: root.tint(modelData)
font.family: Config.font
font.pixelSize: Config.fontSize - 2
font.bold: modelData.obligation === "obligatory"
}
Text {
Layout.fillWidth: true
text: modelData.title
color: modelData.feast ? Theme.accent : Theme.fgDim
elide: Text.ElideRight
font.family: Config.font
font.pixelSize: Config.fontSize - 3
}
}
}
Rectangle {
Layout.fillWidth: true
visible: Typikon.available
implicitHeight: 1
color: Qt.alpha(Theme.overlay, 0.6)
}
RowLayout {
Layout.fillWidth: true
visible: Typikon.available
spacing: 10
Flow {
Layout.fillWidth: true
spacing: 9
Repeater {
model: root.legend
delegate: Text {
required property var modelData
text: modelData.emoji + " " + modelData.label
color: Qt.alpha(Theme.fgDim, 0.75)
font.family: Config.font
font.pixelSize: Config.fontSize - 4
}
}
}
Text {
text: Typikon.pascha
color: Theme.fgDim
font.family: Config.font
font.pixelSize: Config.fontSize - 4
}
}
}
}