fuzbal/Sidebar.qml

254 lines
7.6 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-License-Identifier: Unlicense
import QtQuick 2.12
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.6
import Qt.labs.platform 1.1
import fuzbal 1
import 'util.js' as Util
Page {
id: control
required property Video video
property bool modified: false
EventList {
id: eventList
onDataChanged: modified = true
onRowsInserted: modified = true
onRowsRemoved: modified = true
}
EventFilter {
id: eventFilter
sourceModel: eventList
}
FileDialog {
id: dialog
title: qsTr('Open video or tags')
nameFilters: [qsTr('all files (*)'), qsTr('fuzbal files (*.events *.json)')]
onAccepted: {
const path = file.toString()
if (path.endsWith('.json')) {
eventList.load({ 'tags': JSON.parse(io.read(file)) })
modified = true
} else {
video.source = path.endsWith('.events') ? path.substr(0, path.length-7) : path
const json = JSON.parse(io.read(video.source+'.events') || '{}')
eventList.load(json)
description.text = json['description'] || ''
modified = false
}
}
}
Keys.forwardTo: [tags, video]
// Save / load buttons.
header: ToolBar {
horizontalPadding: 0
RowLayout {
anchors.fill: parent
spacing: 0
Label {
text: video.loaded ? video.source : qsTr('(no video)')
elide: Text.ElideLeft
leftPadding: 5
Layout.fillWidth: true
}
ToolButton {
action: Action {
icon.name: 'document-save'
shortcut: StandardKey.Save
enabled: video.loaded && control.modified
onTriggered: {
var json = eventList.save()
json['description'] = description.text
json['video'] = video.source
json['version'] = Qt.application.version
io.write(video.source+'.events', JSON.stringify(json))
modified = false
}
}
visible: video.loaded
opacity: enabled ? 1 : 0.25
focusPolicy: Qt.NoFocus
}
ToolButton {
action: Action {
icon.name: 'document-open'
shortcut: StandardKey.Open
onTriggered: dialog.open()
}
focusPolicy: Qt.NoFocus
}
}
}
ColumnLayout {
anchors.fill: parent
// Description box.
Frame {
Layout.fillWidth: true
Layout.maximumHeight: 100
padding: 1
ScrollView {
anchors.fill: parent
contentWidth: parent.availableWidth
padding: 0
visible: description.enabled
background: Frame { }
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
TextArea {
id: description
placeholderText: qsTr('Description')
background: Rectangle { color: palette.base }
leftPadding: padding
selectByMouse: true
wrapMode: Text.Wrap
onTextChanged: modified = true
KeyNavigation.priority: KeyNavigation.BeforeItem
KeyNavigation.tab: events
}
}
}
TextField {
id: filter
Layout.fillWidth: true
placeholderText: qsTr('Filter')
onTextChanged: eventFilter.setFilter(text)
Keys.onEscapePressed: text = ''
}
Events {
id: events
Layout.fillWidth: true
Layout.fillHeight: true
Layout.rightMargin: -control.padding
focus: true
model: eventFilter
tags: eventList.tags
onEditingChanged: video.pause(editing)
onCurrentItemChanged: {
if (currentItem)
video.seek(currentItem.time)
}
Rectangle {
anchors { left: parent.left; right: parent.right; top: parent.top }
implicitHeight: 1
color: palette.mid
}
Rectangle {
anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
implicitHeight: 1
color: palette.mid
}
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
eventFilter.remove(currentIndex)
break
case Qt.Key_Tab:
case Qt.Key_Backtab:
// swallow tabs so we dont lose focus when editing
break
default:
return
}
event.accepted = true
}
}
Flow {
id: tags
Layout.fillWidth: true
enabled: video.loaded && !events.editing
// Try passing key to each field input in order.
Keys.enabled: enabled
Keys.forwardTo: Array.from({ length: buttons.count }, (_, i) => buttons.itemAt(i))
spacing: 5
Label {
text: qsTr('(no tags)')
visible: buttons.count === 0
}
Repeater {
id: buttons
model: eventList.tagsOrder.map(name => eventList.tags[name])
delegate: Button {
readonly property string name: modelData.name || modelData.tag
text: Util.addShortcut(name, modelData.key)
focusPolicy: Qt.NoFocus
implicitWidth: implicitContentWidth + 2*padding
onClicked: {
const index = eventList.insert(name, video.time)
// Reset filter if new event doesnt match.
var row = eventFilter.mapFromSource(eventList.index(index, 0)).row
if (row === -1) {
filter.text = ''
row = index
}
events.currentIndex = row
const event = events.currentItem
if (event.fields.length > 0)
events.editing = true
}
Keys.onPressed: {
if (event.text === modelData.key) {
clicked()
event.accepted = true
}
}
}
}
}
}
}