aboutsummaryrefslogtreecommitdiffstats
path: root/theme/Main.qml
blob: f1b91eeab28f359b3e9679daeda85798c8cb39d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
//
// UDT SDDM greeter. The root item spans every screen. The wallpaper is drawn
// once per screen; the login card is pinned to one screen (config uiScreen,
// else the primary screen); input wakes the card after it fades.

import QtQuick

Item {
    id: root

    // SDDM sizes the greeter to the union of every screen.
    readonly property var union: screenModel.geometry(-1)
    width: union.width
    height: union.height

    // Palette. Values come from theme.conf, overridden live by theme.conf.user.
    readonly property string baseColor: config.stringValue("base")
    readonly property int cardRadius: 10
    readonly property string wallpaperUrl:
        config.stringValue("background") === ""
            ? ""
            : "file://" + config.stringValue("background")

    readonly property string wantedScreen: config.stringValue("uiScreen")
    property var primaryGeometry: screenModel.geometry(screenModel.primary >= 0 ? screenModel.primary : 0)
    property var namedGeometry: primaryGeometry
    readonly property var cardGeometry: wantedScreen === "" ? primaryGeometry : namedGeometry

    // The Image delegate of the screen the card sits on. The frosted card
    // samples it. Null until the model has populated.
    property Item cardScreenImage: null

    Repeater {
        id: screens
        model: screenModel
        delegate: Item {
            x: geometry.x
            y: geometry.y
            width: geometry.width
            height: geometry.height

            Image {
                id: screenImage
                anchors.fill: parent
                source: root.wallpaperUrl
                fillMode: Image.PreserveAspectCrop
                sourceSize.width: parent.width
                sourceSize.height: parent.height
                smooth: true
                asynchronous: true
            }

            // Scrim so the card and text read on any wallpaper.
            Rectangle {
                anchors.fill: parent
                color: root.baseColor
                opacity: 0.45
            }

            readonly property string screenName: name

            Component.onCompleted: root.registerScreen(index, screenName, screenImage, geometry)
        }
    }

    // Filled as the model populates: the primary screen's name, and the
    // wallpaper image plus geometry of whichever screen owns the card.
    property string primaryName: ""

    function registerScreen(index, screenName, image, geom) {
        if (index === screenModel.primary)
            root.primaryName = screenName
        if (screenName === root.cardScreenName()) {
            root.cardScreenImage = image
            root.namedGeometry = geom
        }
    }

    function cardScreenName() {
        return root.wantedScreen !== "" ? root.wantedScreen : root.primaryName
    }

    // Idle: fade the card after fadeoutMs, restore on any input.
    property bool uiVisible: true

    Timer {
        id: idle
        interval: config.intValue("fadeoutMs") > 0 ? config.intValue("fadeoutMs") : 60000
        running: root.uiVisible
        repeat: false
        onTriggered: root.uiVisible = false
    }

    function wake() {
        root.uiVisible = true
        idle.restart()
    }

    MouseArea {
        anchors.fill: parent
        hoverEnabled: true
        onPressed: root.wake()
        onPositionChanged: root.wake()
    }

    Keys.onPressed: event => {
        root.wake()
        event.accepted = false
    }

    // Placeholder card. Task 2 replaces this with components/LoginCard.qml.
    Rectangle {
        id: card
        x: root.cardGeometry.x + (root.cardGeometry.width - width) / 2
        y: root.cardGeometry.y + (root.cardGeometry.height - height) / 2
        width: 380
        height: 220
        radius: root.cardRadius
        color: config.stringValue("mantle")
        opacity: root.uiVisible ? 0.9 : 0
        visible: opacity > 0
        Behavior on opacity { NumberAnimation { duration: 260; easing.type: Easing.OutCubic } }
    }
}