mirror of
https://git.pumpin.online/funkpower/menologion.git
synced 2026-10-22 18:57:14 +00:00
A Quickshell widget for Hyprland, a waybar module and an eww panel, all reading the same JSON from the typikon CLI so none of them touches the network and all three agree. The Quickshell piece is three separable parts: a Typikon singleton that wraps the CLI and re-reads only when the date rolls over, a Theme singleton that takes colours from JSON with a default for every key, and the widget itself. 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 particular law -- and the words for them differ too. The church date shows beside the civil one whenever they differ, which on the old calendar is always. Forefeasts and afterfeasts get their own line instead of crowding out the commemoration that makes the day.
212 lines
7.2 KiB
QML
212 lines
7.2 KiB
QML
import QtQuick
|
|
import QtQuick.Layouts
|
|
import qs
|
|
import qs.services
|
|
|
|
Panel {
|
|
id: root
|
|
title: "Church calendar"
|
|
note: Typikon.available
|
|
? (Typikon.season || 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.churchDate !== ""
|
|
&& Typikon.churchDate !== Qt.formatDate(new Date(), "d MMMM"))
|
|
bits.push(Typikon.churchDate);
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|