Files
menologion/quickshell/modules/Menologion.qml
T
funkpower be81c682cb horologion was the wrong book
The Horologion is the Book of Hours: the fixed portions of the services
themselves. This shows the day's commemorations, its fast and its season,
which is a Menologion. Naming it for a book whose contents it does not carry
was a real error and worth the churn to correct.

typikon already uses the word internally, for menologion.py and the data file
it loads. That is not a collision: typikon provides a menologion, this
displays one.

Renamed throughout, not just the repo: the two scripts, the QML component and
its three qmldir modules, MENOLOGION_COLORS, ~/.config/menologion/colors.json,
the eww widgets and scss variables, the waybar class. Verified standalone and
embedded in a foreign shell, and the smoke suite still passes.

Also drops the em dashes from the prose.
2026-09-18 10:52:56 -04:00

210 lines
7.2 KiB
QML

import QtQuick
import QtQuick.Layouts
import ".."
import "../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 : ""
// 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 {
// Labelled, because a bare ISO date in a corner is a riddle.
text: Typikon.pascha !== "" ? "Pascha " + Typikon.pascha : ""
color: Theme.fgDim
font.family: Config.font
font.pixelSize: Config.fontSize - 4
}
}
}
}