aboutsummaryrefslogtreecommitdiffstats
path: root/theme/Main.qml
blob: 63cb6eab42386de33d20564f38c456426673a1b9 (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
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
//
// UDT SDDM greeter. SDDM creates one window per screen, each sized to its own
// screen and carrying a one-row ScreenModel, so this root item is exactly one
// screen. The wallpaper fills it; the login card draws only on the screen
// named by config uiScreen, else on the primary screen. Input wakes the card
// after it fades.

import QtQuick
import "components"

Item {
    id: root

    // SDDM creates one window per screen and sizes it to that screen. The
    // model holds exactly one row and geometry(index) ignores index, returning
    // this window's own screen at origin (0,0).
    readonly property var windowGeometry: screenModel.geometry(0)
    width: windowGeometry.width
    height: windowGeometry.height

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

    // The screen the card belongs on: config uiScreen if non-empty, else the
    // primary screen. `primaryScreen` is a per-window context property SDDM
    // sets true only on the window showing the primary screen.
    readonly property string wantedScreen: config.stringValue("uiScreen")
    property string thisScreenName: ""
    readonly property bool cardScreen: wantedScreen === ""
        ? primaryScreen
        : (thisScreenName === wantedScreen)

    // The wallpaper fills this window's single screen. The frosted card
    // samples this image.
    Image {
        id: wallpaperImage
        anchors.fill: parent
        source: root.wallpaperUrl
        fillMode: Image.PreserveAspectCrop
        sourceSize.width: width
        sourceSize.height: 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
    }

    // Non-visual: reads the model's single row to learn this screen's name.
    Repeater {
        model: screenModel
        delegate: Item {
            visible: false
            Component.onCompleted: root.thisScreenName = name
        }
    }

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

    // While faded the card is disabled, which clears its focus; take focus on
    // the root so the first key press wakes the card instead of vanishing.
    onUiVisibleChanged: {
        if (!uiVisible)
            root.forceActiveFocus()
    }

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

    function wake() {
        if (!root.uiVisible) {
            root.uiVisible = true
            card.restoreFocus()
        }
        idle.restart()
    }

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

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

    LoginCard {
        id: card
        anchors.centerIn: parent
        anchors.horizontalCenterOffset: card.shakeOffset
        visible: root.cardScreen
        enabled: root.uiVisible
        opacity: root.uiVisible ? 1 : 0
        Behavior on opacity { NumberAnimation { duration: 260; easing.type: Easing.OutCubic } }
        backdropSource: wallpaperImage
        onWake: root.wake()
    }
}