summaryrefslogtreecommitdiff
path: root/Events.qml
blob: 2128f39b80413f3574d05d4cfc0174cc9471c21d (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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// SPDX-License-Identifier: Unlicense

import QtQuick 2.12
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.6

import 'util.js' as Util

ListView {
    id: control

    required property var tags // tag definitions
    property bool editing: false

    signal selected(var event)

    clip: true
    focus: true
    highlightMoveDuration: 0
    highlightResizeDuration: 0

    onCurrentIndexChanged: editing = false

    Keys.onPressed: {
        switch (event.key) {
        case Qt.Key_Home:
            currentIndex = 0
            break
        case Qt.Key_End:
            currentIndex = count - 1
            break
        case Qt.Key_Enter:
        case Qt.Key_Return:
            if (editing) {
                currentItem.store()
                editing = false
            } else {
                if (currentItem.fields.length > 0)
                    editing = true
            }
            break
        case Qt.Key_Escape:
            editing = false
            break
        case Qt.Key_Delete:
            editing = false
            model.remove(currentIndex)
            break
        case Qt.Key_Tab:
        case Qt.Key_Backtab:
            // swallow tabs so we don’t lose focus when editing
            break
        default:
            return
        }
        event.accepted = true
    }

    ScrollBar.vertical: ScrollBar { anchors.right: parent.right }

    delegate: ItemDelegate {
        id: event

        required property var model
        required property int index
        required property int time
        required property string tag

        property alias fields: inputs.model // field definitions
        property bool editing: control.editing && ListView.isCurrentItem

        width: control.width
        highlighted: ListView.isCurrentItem

        clip: true
        padding: 2

        background: Rectangle {
            anchors.fill: parent
            color: highlighted ? Util.alphize(border.color, 0.1) :
                (index % 2 === 0 ? palette.base : palette.alternateBase)
            border {
                color: editing ? palette.highlight : palette.dark
                width: highlighted ? 1 : 0
            }
            radius: border.width
        }

        // Store current inputs in model.
        function store() {
            var values = {}
            for (var i = 0; i < inputs.model.length; i++)
                values[inputs.model[i].name] = inputs.items[i].value
            model.values = values
        }

        // Try passing key to each field input in order. If none can
        // handle it, pass it to control.
        Keys.forwardTo: Array.prototype.concat(control, editing ? inputs.items : [])

        onClicked: {
            control.currentIndex = index
            control.forceActiveFocus()
        }
        onDoubleClicked: control.selected(event)

        contentItem: ColumnLayout {
            anchors { left: parent.left; right: parent.right; margins: 5 }

            // Event time, tag and summary.
            RowLayout {
                Label {
                    text: new Date(model.time).toISOString().substr(12, 9)
                    font.pixelSize: 10
                    Layout.alignment: Qt.AlignBaseline
                }
                Label {
                    text: tag
                    font.weight: Font.DemiBold
                    Layout.alignment: Qt.AlignBaseline
                }
                Label {
                    text: {
                        var str = ''
                        for (var i = 0; i < inputs.count; i++) {
                            const field = inputs.model[i]
                            const value = model.values[field.name]
                            if (value && field.type !== 'TextArea')
                                str += (field.type === 'Bool' ? field.name : value) + ' '
                        }
                        return str
                    }
                    elide: Text.ElideRight
                    textFormat: Text.PlainText
                    Layout.fillWidth: true
                    Layout.alignment: Qt.AlignBaseline
                }
            }

            // Inputs for event‐specific fields.
            GridLayout {
                flow: GridLayout.TopToBottom
                rows: inputs.count

                columnSpacing: 10
                visible: editing

                // Labels.
                Repeater {
                    model: inputs.model
                    delegate: Label {
                        text: Util.addShortcut(modelData.name, modelData.key)
                        Layout.alignment: Qt.AlignRight
                    }
                }

                // Inputs.
                Repeater {
                    id: inputs

                    readonly property var items: Array.from({ length: count }, (_, i) => itemAt(i).item)

                    // If field definitions are missing for this event’s tag, use
                    // Text for all field types unless where the value is bool.
                    model: tags[tag] ? tags[tag].fields :
                        Object.entries(event.model.values).map(value => ({
                            'name': value[0],
                            'type': typeof(value[1]) === 'boolean' ? 'Bool' : 'Text',
                        }))

                    delegate: Loader {
                        source: 'qrc:/Fields/' + modelData.type + '.qml'

                        Layout.fillHeight: true
                        Layout.fillWidth: true

                        // Set input value to what is in the model each time the control is expanded.
                        onVisibleChanged: {
                            if (item && visible)
                                item.set(event.model.values[modelData.name])
                        }

                        Binding {
                            target: item; property: 'model'
                            value: modelData
                        }
                    }
                }
            }
        }
    }
}