1/*
2 *  SPDX-FileCopyrightText: 2012 Sebastian Gottfried <sebastiangottfried@web.de>
3 *  SPDX-FileCopyrightText: 2015 Sebastian Gottfried <sebastiangottfried@web.de>
4 *
5 *  SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8import QtQuick 2.9
9import QtGraphicalEffects 1.0
10import ktouch 1.0
11
12Item {
13    id: item
14
15    property int keyIndex
16    property KeyboardLayout keyboardLayout
17    property bool isHighlighted: false
18    property bool animateHighlight: true
19    property bool enabled: true
20    property bool pressed: false
21    property real horizontalScaleFactor: 1
22    property real verticalScaleFactor: 1
23
24    property AbstractKey key: item.keyboardLayout.key(item.keyIndex)
25    property AbstractKey referenceKey: keyboardLayout.referenceKey
26
27    function match(data) {
28        var eventText = data
29        var eventKey = -1
30        if (typeof data === "object") {
31            eventText = data.text
32            eventKey = data.key
33        }
34        if (typeof data === "number") {
35            eventText = ""
36            eventKey = data
37        }
38        switch (key.keyType()) {
39        case "key":
40            for (var i = 0; i < key.keyCharCount; i++) {
41                if (key.keyChar(i).value == eventText) {
42                    return true;
43                }
44            }
45            return false
46
47        case "specialKey":
48            switch (key.type) {
49            case SpecialKey.Tab:
50                return eventKey == Qt.Key_Tab
51            case SpecialKey.Capslock:
52                return eventKey == Qt.Key_CapsLock
53            case SpecialKey.Shift:
54                return eventKey == Qt.Key_Shift
55            case SpecialKey.Backspace:
56                return eventKey == Qt.Key_Backspace
57            case SpecialKey.Return:
58                return eventKey == Qt.Key_Return
59            case SpecialKey.Space:
60                return eventKey == Qt.Key_Space || eventText == " "
61            }
62            return false
63        }
64        return false;
65    }
66
67    function getTint(color) {
68        color.a = preferences.fingerOpacity / 100;
69        return color
70    }
71
72    property color tint: key && key.keyType() == "key"?
73        getTint(preferences.fingerColor(key.fingerIndex)):
74        "#00000000"
75
76    x: Math.round(key.left * horizontalScaleFactor)
77    y: Math.round(key.top * verticalScaleFactor)
78    width: Math.round(key.width * horizontalScaleFactor)
79    height: Math.round(key.height * verticalScaleFactor)
80
81    state: enabled? (pressed? "pressed": "normal"): "disabled"
82
83    onIsHighlightedChanged: {
84        if (!animateHighlight) {
85            shadow.state = isHighlighted? "highlighted1": "normal"
86        }
87    }
88
89    Rectangle {
90        id: shadow
91
92        property int marginSize: 0
93
94        anchors.centerIn: parent
95        width: item.width + marginSize
96        height: item.height + marginSize
97        smooth: true
98        radius: body.radius
99        state: "normal"
100
101        states: [
102            State {
103                name: "normal"
104                PropertyChanges {
105                    target: shadow
106                    color: "#000"
107                    marginSize: 0
108                }
109                PropertyChanges {
110                    target: shadowEffect
111                    glowRadius: body.radius
112                }
113            },
114            State {
115                name: "highlighted1"
116                PropertyChanges {
117                    target: shadow
118                    color: "#54A7F0"
119                    marginSize: 4
120                }
121                PropertyChanges {
122                    target: shadowEffect
123                    glowRadius: 1.5 * body.radius
124                }
125            },
126            State {
127                name: "highlighted2"
128                PropertyChanges {
129                    target: shadow
130                    color: "#54A7F0"
131                    marginSize: 0
132                }
133                PropertyChanges {
134                    target: shadowEffect
135                    glowRadius: 1.5 * body.radius
136                }
137            }
138        ]
139
140        Behavior on marginSize {
141            enabled: animateHighlight
142            NumberAnimation {
143                duration: 150
144                easing.type: Easing.InOutQuad
145            }
146        }
147
148        Behavior on color {
149            enabled: animateHighlight
150            ColorAnimation { duration: 150 }
151        }
152
153
154        SequentialAnimation {
155            id: pulseAnimation
156            loops: Animation.Infinite
157            running: isHighlighted && animateHighlight
158            onRunningChanged: {
159                if (!running)
160                    shadow.state = "normal"
161            }
162
163            ScriptAction {
164                script: shadow.state = "highlighted1"
165            }
166            PauseAnimation { duration: 850 }
167            ScriptAction {
168                script: shadow.state = "highlighted2"
169            }
170            PauseAnimation { duration: 150 }
171        }
172    }
173
174    RectangularGlow {
175        id: shadowEffect
176        anchors.fill: shadow
177        color: shadow.color
178        glowRadius: 5
179        cornerRadius: glowRadius + shadow.radius
180
181        Behavior on glowRadius {
182            enabled: animateHighlight
183            NumberAnimation {
184                duration: 150
185                easing.type: Easing.InOutQuad
186            }
187        }
188    }
189
190    Rectangle {
191        id: body
192        anchors.fill: parent
193        radius: Math.max(3, Math.min(referenceKey.height, referenceKey.width) / 10 * Math.min(horizontalScaleFactor, verticalScaleFactor))
194        border.width: 1
195        border.color: "#000"
196        smooth: true
197
198        gradient: Gradient {
199            GradientStop { id: gradientStop0; position: 0.0; }
200            GradientStop { id: gradientStop1; position: 0.5; }
201            GradientStop { id: gradientStop2; position: 1.0; }
202        }
203
204        Rectangle {
205            id: hapticMarker
206            anchors {
207                bottom: parent.bottom
208                horizontalCenter: parent.horizontalCenter
209                bottomMargin: 4
210            }
211            visible: item.key.keyType() == "key" && item.key.hasHapticMarker
212            height: 3
213            width: body.width / 3
214            radius: 1
215            color: topLeftLabel.color
216            border {
217                width: 1
218                color: topLeftLabel.color
219            }
220        }
221    }
222
223    Item {
224        anchors.topMargin: Math.max(referenceKey.width / 20, 3) * verticalScaleFactor
225        anchors.bottomMargin: anchors.topMargin
226        anchors.leftMargin: Math.max(referenceKey.width / 10, 5) * horizontalScaleFactor
227        anchors.rightMargin: anchors.leftMargin
228        anchors.fill: parent
229        KeyLabel {
230            id: topLeftLabel
231            key: item.key
232            position: KeyChar.TopLeft
233        }
234        KeyLabel {
235            id: topRightLabel
236            anchors.right: parent.right
237            key: item.key
238            position: KeyChar.TopRight
239        }
240        KeyLabel {
241            id: bottomLeftLabel
242            anchors.bottom: parent.bottom
243            key: item.key
244            position: KeyChar.BottomLeft
245        }
246        KeyLabel {
247            id: bottomRightLabel
248            anchors.right: parent.right
249            anchors.bottom: parent.bottom
250            key: item.key
251            position: KeyChar.BottomRight
252        }
253    }
254
255    states: [
256        State {
257            name: "normal"
258            PropertyChanges {
259                target: gradientStop0
260                color: Qt.tint("#f0f0f0", item.tint)
261            }
262            PropertyChanges {
263                target: gradientStop1
264                color: Qt.tint("#d5d5d5", item.tint)
265            }
266            PropertyChanges {
267                target: gradientStop2
268                color: Qt.tint("#ccc", item.tint)
269            }
270        },
271        State {
272            name: "pressed"
273            PropertyChanges {
274                target: gradientStop0
275                color: Qt.tint("#666", item.tint)
276            }
277            PropertyChanges {
278                target: gradientStop1
279                color: Qt.tint("#888", item.tint)
280            }
281            PropertyChanges {
282                target: gradientStop2
283                color: Qt.tint("#999", item.tint)
284            }
285        },
286        State {
287            name: "disabled"
288            PropertyChanges {
289                target: gradientStop0
290                color: Qt.tint("#444", item.tint)
291            }
292            PropertyChanges {
293                target: gradientStop1
294                color: Qt.tint("#333", item.tint)
295            }
296            PropertyChanges {
297                target: gradientStop2
298                color: Qt.tint("#222", item.tint)
299            }
300        }
301    ]
302}
303