// 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 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 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 } // Pass the key to parent first to check if Video handles it. // If not, try passing it to each field input when editing. 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 } } } } } } }