9.1 KiB · text History c2e4eb1
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import "schedule.js" as Schedule
import "themes.js" as Themes

Kirigami.FormLayout {
    id: root
    twinFormLayouts: parentLayout

    property alias formLayout: root

    property string cfg_Pattern
    property string cfg_FadeFrom
    property string cfg_FadeTo
    property int cfg_BokehLights
    property double cfg_Speed
    property int cfg_FrameRate
    property bool cfg_Wordmark
    property bool cfg_ForegroundBokeh
    property bool cfg_Glints
    property bool cfg_Breathing
    property bool cfg_Grain
    property bool cfg_Dither
    property string cfg_ThemeMode
    property string cfg_Theme
    property string cfg_NightTheme
    property string cfg_SunriseTheme
    property string cfg_DayTheme
    property string cfg_SunsetTheme
    property bool cfg_UseLocation
    property double cfg_Latitude
    property double cfg_Longitude
    property string cfg_SunriseTime
    property string cfg_SunsetTime

    // The patterns a fade can go between; Pattern adds the fade itself.
    readonly property var layers: [
        { value: "both", text: "Moire and halftone" },
        { value: "moire", text: "Moire" },
        { value: "halftone", text: "Halftone" },
        { value: "bokeh", text: "Bokeh" },
        { value: "stars", text: "Starfield" },
    ]
    readonly property var patterns: layers.concat([{ value: "alternate", text: "Fade between two…" }])

    readonly property bool fading: cfg_Pattern === "alternate"
    readonly property bool showsBokeh: cfg_Pattern === "bokeh"
        || (fading && (cfg_FadeFrom === "bokeh" || cfg_FadeTo === "bokeh"))

    component PatternBox: QQC2.ComboBox {
        property var choices
        property string value
        signal picked(string value)
        implicitContentWidthPolicy: QQC2.ComboBox.WidestText
        model: choices
        textRole: "text"
        currentIndex: Math.max(0, choices.findIndex(p => p.value === value))
        onActivated: index => picked(choices[index].value)
    }

    component ThemeBox: QQC2.ComboBox {
        property string value
        signal picked(string name)
        model: Themes.names
        currentIndex: Math.max(0, Themes.names.indexOf(value))
        onActivated: index => picked(Themes.names[index])
    }

    component TimeField: QQC2.TextField {
        inputMask: "99:99"
        validator: RegularExpressionValidator { regularExpression: /([01]\d|2[0-3]):[0-5]\d/ }
    }

    component DegreesField: QQC2.TextField {
        property double value
        signal edited(double value)
        text: value.toFixed(4)
        validator: DoubleValidator { bottom: -180; top: 180; decimals: 4 }
        onEditingFinished: edited(Number(text))
    }

    // ------------------------------------------------------------ pattern

    PatternBox {
        Kirigami.FormData.label: "Pattern:"
        choices: root.patterns
        value: root.cfg_Pattern
        onPicked: v => root.cfg_Pattern = v
    }

    PatternBox {
        Kirigami.FormData.label: "From:"
        visible: root.fading
        choices: root.layers
        value: root.cfg_FadeFrom
        onPicked: v => root.cfg_FadeFrom = v
    }

    PatternBox {
        Kirigami.FormData.label: "To:"
        visible: root.fading
        choices: root.layers
        value: root.cfg_FadeTo
        onPicked: v => root.cfg_FadeTo = v
    }

    QQC2.SpinBox {
        Kirigami.FormData.label: "Bokeh lights:"
        visible: root.showsBokeh
        from: 4; to: 64
        value: root.cfg_BokehLights
        onValueModified: root.cfg_BokehLights = value
    }

    RowLayout {
        Kirigami.FormData.label: "Speed:"
        QQC2.Slider {
            id: speed
            from: 0; to: 4; stepSize: 0.25
            value: root.cfg_Speed
            onMoved: root.cfg_Speed = value
        }
        QQC2.Label { text: speed.value === 0 ? "still" : speed.value + "×" }
    }

    QQC2.SpinBox {
        Kirigami.FormData.label: "Frame rate:"
        from: 5; to: 60
        value: root.cfg_FrameRate
        onValueModified: root.cfg_FrameRate = value
        enabled: root.cfg_Speed > 0
    }

    QQC2.CheckBox {
        Kirigami.FormData.label: "Effects:"
        text: "Light glinting across the pattern"
        checked: root.cfg_Glints
        onToggled: root.cfg_Glints = checked
    }
    QQC2.CheckBox {
        text: "Breathing"
        checked: root.cfg_Breathing
        onToggled: root.cfg_Breathing = checked
    }
    QQC2.CheckBox {
        text: "Large lights drifting past in front"
        checked: root.cfg_ForegroundBokeh
        onToggled: root.cfg_ForegroundBokeh = checked
    }
    QQC2.CheckBox {
        text: "Film grain"
        checked: root.cfg_Grain
        onToggled: root.cfg_Grain = checked
    }
    QQC2.CheckBox {
        text: "Dither (smooths banding in gradients)"
        checked: root.cfg_Dither
        onToggled: root.cfg_Dither = checked
    }

    QQC2.CheckBox {
        Kirigami.FormData.label: "Wordmark:"
        text: "Write NEIAM in the corner"
        checked: root.cfg_Wordmark
        onToggled: root.cfg_Wordmark = checked
    }

    // ------------------------------------------------------------- theme

    Item { Kirigami.FormData.isSection: true }

    QQC2.RadioButton {
        Kirigami.FormData.label: "Theme:"
        text: "Always the same"
        checked: root.cfg_ThemeMode !== "sun"
        onToggled: if (checked) root.cfg_ThemeMode = "static"
    }
    QQC2.RadioButton {
        text: "Follow the time of day"
        checked: root.cfg_ThemeMode === "sun"
        onToggled: if (checked) root.cfg_ThemeMode = "sun"
    }

    ThemeBox {
        Kirigami.FormData.label: "Theme:"
        visible: root.cfg_ThemeMode !== "sun"
        value: root.cfg_Theme
        onPicked: name => root.cfg_Theme = name
    }

    ThemeBox {
        Kirigami.FormData.label: "Night:"
        visible: root.cfg_ThemeMode === "sun"
        value: root.cfg_NightTheme
        onPicked: name => root.cfg_NightTheme = name
    }
    ThemeBox {
        Kirigami.FormData.label: "Sunrise:"
        visible: root.cfg_ThemeMode === "sun"
        value: root.cfg_SunriseTheme
        onPicked: name => root.cfg_SunriseTheme = name
    }
    ThemeBox {
        Kirigami.FormData.label: "Day:"
        visible: root.cfg_ThemeMode === "sun"
        value: root.cfg_DayTheme
        onPicked: name => root.cfg_DayTheme = name
    }
    ThemeBox {
        Kirigami.FormData.label: "Sunset:"
        visible: root.cfg_ThemeMode === "sun"
        value: root.cfg_SunsetTheme
        onPicked: name => root.cfg_SunsetTheme = name
    }

    // ---------------------------------------------------------- the sun

    QQC2.CheckBox {
        Kirigami.FormData.label: "Sun:"
        visible: root.cfg_ThemeMode === "sun"
        text: "Follow the real sun at a location"
        checked: root.cfg_UseLocation
        onToggled: root.cfg_UseLocation = checked
    }

    DegreesField {
        Kirigami.FormData.label: "Latitude:"
        visible: root.cfg_ThemeMode === "sun" && root.cfg_UseLocation
        value: root.cfg_Latitude
        onEdited: v => root.cfg_Latitude = Math.max(-90, Math.min(90, v))
    }
    DegreesField {
        Kirigami.FormData.label: "Longitude:"
        visible: root.cfg_ThemeMode === "sun" && root.cfg_UseLocation
        value: root.cfg_Longitude
        onEdited: v => root.cfg_Longitude = v
    }

    TimeField {
        Kirigami.FormData.label: "Sunrise at:"
        visible: root.cfg_ThemeMode === "sun" && !root.cfg_UseLocation
        text: root.cfg_SunriseTime
        onEditingFinished: if (acceptableInput) root.cfg_SunriseTime = text
    }
    TimeField {
        Kirigami.FormData.label: "Sunset at:"
        visible: root.cfg_ThemeMode === "sun" && !root.cfg_UseLocation
        text: root.cfg_SunsetTime
        onEditingFinished: if (acceptableInput) root.cfg_SunsetTime = text
    }

    QQC2.Label {
        id: now
        Kirigami.FormData.label: "Now:"
        visible: root.cfg_ThemeMode === "sun"

        function describe() {
            var p = Schedule.palette({
                ThemeMode: "sun", UseLocation: root.cfg_UseLocation,
                Latitude: root.cfg_Latitude, Longitude: root.cfg_Longitude,
                SunriseTime: root.cfg_SunriseTime, SunsetTime: root.cfg_SunsetTime,
                NightTheme: root.cfg_NightTheme, SunriseTheme: root.cfg_SunriseTheme,
                DayTheme: root.cfg_DayTheme, SunsetTheme: root.cfg_SunsetTheme,
            }, new Date())
            return p.label + (root.cfg_UseLocation
                ? ", sun at " + p.elevation.toFixed(1) + "°" : "")
        }

        text: describe()
        Timer {
            interval: 60000; running: now.visible; repeat: true
            onTriggered: now.text = now.describe()
        }
        Connections {
            target: root
            function onCfg_UseLocationChanged() { now.text = now.describe() }
            function onCfg_LatitudeChanged() { now.text = now.describe() }
            function onCfg_LongitudeChanged() { now.text = now.describe() }
            function onCfg_SunriseTimeChanged() { now.text = now.describe() }
            function onCfg_SunsetTimeChanged() { now.text = now.describe() }
        }
    }
}