From 81ea712c6b1b0e867e7d047d6a3089a36e5ccdbe Mon Sep 17 00:00:00 2001 From: Chris Cochrun Date: Wed, 19 Apr 2023 05:35:52 -0500 Subject: [PATCH] feature: a much nicer qml rangedslider In order to make start and end time setting far more efficient, I decided to create my own RangeSlider called RangedSlider. This has a moved signal for each handle and a released signal for each handle. This allows us to change the video playback but not set the value until released. Meaning only one access to the database. --- src/qml/presenter/RangedSlider.qml | 87 ++++++++------- src/qml/presenter/VideoEditor.qml | 168 ++++++++++++++--------------- 2 files changed, 131 insertions(+), 124 deletions(-) diff --git a/src/qml/presenter/RangedSlider.qml b/src/qml/presenter/RangedSlider.qml index de3e7a8..02293d7 100644 --- a/src/qml/presenter/RangedSlider.qml +++ b/src/qml/presenter/RangedSlider.qml @@ -14,13 +14,18 @@ Rectangle { property real firstValue property real secondValue + property real firstInitialValue + property real secondInitialValue property real firstVisualPosition property real secondVisualPosition signal firstReleased() signal secondReleased() + signal firstMoved() + signal secondMoved() Kirigami.Theme.colorSet: Kirigami.Theme.Button + Kirigami.Theme.inherit: false color: Kirigami.Theme.backgroundColor border.width: 0 @@ -28,31 +33,32 @@ Rectangle { height: 6 + + Rectangle { + id: range + color: Kirigami.Theme.hoverColor + height: root.height + radius: width / 2 + border.width: 0 + anchors.right: second.right + anchors.left: first.left + } + Rectangle { id: first - x: firstVisualPosition + x: firstInitialValue / (to - from) * (root.width - width) y: -6 implicitWidth: 18 implicitHeight: 18 - color: firstMouse.containsMouse ? Kirigami.Theme.hoverColor : Kirigami.Theme.alternateBackgroundColor - border.width: firstMouse.containsMouse ? 2 : 1 - border.color: firstMouse.containsMouse ? Kirigami.Theme.hoverColor : Kirigami.Theme.backgroundColor + color: firstMouse.containsMouse || firstMouse.drag.active ? Kirigami.Theme.hoverColor : Kirigami.Theme.alternateBackgroundColor + border.width: firstMouse.containsMouse || firstMouse.drag.active ? 2 : 1 + border.color: firstMouse.containsMouse || firstMouse.drag.active ? Kirigami.Theme.hoverColor : Kirigami.Theme.backgroundColor radius: width / 2 Drag.active: firstMouse.drag.active Drag.hotSpot.x: width / 2 Drag.hotSpot.y: height / 2 - /* states: State { */ - /* name: "dragged" */ - /* when: first.Drag.active */ - /* PropertyChanges { */ - /* target: first */ - /* x: x */ - /* width: width */ - /* height: height */ - /* } */ - /* } */ MouseArea { id: firstMouse anchors.fill: parent @@ -60,41 +66,38 @@ Rectangle { drag { target: first axis: Drag.XAxis - maximumX: root.right - minimumX: root.left + maximumX: Math.min((root.width - first.width / 2), second.x) + minimumX: 0 + first.width / 2 } onReleased: { - firstValue = mouseX; + firstValue = (to - from) / (root.width - first.width) * (first.x - first.width / 2) + from; + firstVisualPosition = firstValue; firstReleased(); } + onPositionChanged: { + if (drag.active) { + firstVisualPosition = (to - from) / (root.width - first.width) * (first.x - first.width / 2) + from; + firstMoved() + } + } } } Rectangle { id: second - x: secondVisualPosition + x: secondInitialValue / (to - from) * (root.width - width) y: -6 implicitWidth: 18 implicitHeight: 18 - color: secondMouse.containsMouse ? Kirigami.Theme.hoverColor : Kirigami.Theme.alternateBackgroundColor - border.width: secondMouse.containsMouse ? 2 : 1 - border.color: secondMouse.containsMouse ? Kirigami.Theme.hoverColor : Kirigami.Theme.backgroundColor + color: secondMouse.containsMouse || secondMouse.drag.active ? Kirigami.Theme.hoverColor : Kirigami.Theme.alternateBackgroundColor + border.width: secondMouse.containsMouse || secondMouse.drag.active ? 2 : 1 + border.color: secondMouse.containsMouse || secondMouse.drag.active ? Kirigami.Theme.hoverColor : Kirigami.Theme.backgroundColor radius: width / 2 Drag.active: secondMouse.drag.active Drag.hotSpot.x: width / 2 Drag.hotSpot.y: height / 2 - /* states: State { */ - /* name: "dragged" */ - /* when: second.Drag.active */ - /* PropertyChanges { */ - /* target: second */ - /* x: x */ - /* width: width */ - /* height: height */ - /* } */ - /* } */ MouseArea { id: secondMouse anchors.fill: parent @@ -102,19 +105,25 @@ Rectangle { drag { target: second axis: Drag.XAxis - maximumX: root.width - minimumX: 0 + maximumX: root.width - second.width / 2 + minimumX: Math.max((0 + second.width / 2), first.x) } onReleased: { - secondValue = second.x; + secondValue = (to - from) / (root.width - second.width) * (second.x - second.width / 2) + from; + secondVisualPosition = secondValue; secondReleased(); } + onPositionChanged: { + if (drag.active) { + secondVisualPosition = (to - from) / (root.width - second.width) * (second.x - second.width / 2) + from; + secondMoved() + } + } } } - Rectangle { - id: range - color: Kirigami.Theme.hoverColor - radius: width / 2 - } + /* function setValues(first, second) { */ + /* first.x = first */ + /* second.x = second */ + /* } */ } diff --git a/src/qml/presenter/VideoEditor.qml b/src/qml/presenter/VideoEditor.qml index 1443896..100366b 100644 --- a/src/qml/presenter/VideoEditor.qml +++ b/src/qml/presenter/VideoEditor.qml @@ -80,10 +80,12 @@ Item { Item { Layout.columnSpan: 2 - Layout.preferredWidth: root.width - 50 - Layout.preferredHeight: Layout.preferredWidth / 16 * 9 + Layout.fillWidth: true + Layout.preferredHeight: width / 16 * 9 Layout.alignment: Qt.AlignCenter Layout.topMargin: 10 + Layout.leftMargin: Kirigami.Units.largeSpacing + Layout.rightMargin: Kirigami.Units.largeSpacing MpvObject { id: videoPreview @@ -100,44 +102,36 @@ Item { } } - Rectangle { - id: videoBg - color: Kirigami.Theme.alternateBackgroundColor - + RowLayout { anchors.top: videoPreview.bottom - width: parent.width + width: videoPreview.width height: videoTitleField.height - - RowLayout { - anchors.fill: parent - spacing: 2 - Kirigami.Icon { - source: videoPreview.isPlaying ? "media-pause" : "media-play" - Layout.preferredWidth: 25 - Layout.preferredHeight: 25 - MouseArea { - anchors.fill: parent - onPressed: videoPreview.playPause() - cursorShape: Qt.PointingHandCursor - } - } - Controls.Slider { - id: videoSlider - Layout.fillWidth: true - Layout.preferredHeight: 25 - from: 0 - to: videoPreview.duration - /* value: videoPreview.postion */ - live: true - onMoved: videoPreview.seek(value); - } - - Controls.Label { - id: videoTime - text: new Date(videoPreview.position * 1000).toISOString().slice(11, 19); + spacing: 2 + Kirigami.Icon { + source: videoPreview.isPlaying ? "media-pause" : "media-play" + Layout.preferredWidth: 25 + Layout.preferredHeight: 25 + MouseArea { + anchors.fill: parent + onPressed: videoPreview.playPause() + cursorShape: Qt.PointingHandCursor } } + Controls.Slider { + id: videoSlider + Layout.fillWidth: true + Layout.preferredHeight: 25 + from: 0 + to: videoPreview.duration + /* value: videoPreview.postion */ + live: true + onMoved: videoPreview.seek(value); + } + Controls.Label { + id: videoTime + text: new Date(videoPreview.position * 1000).toISOString().slice(11, 19); + } } } @@ -145,67 +139,79 @@ Item { id: videoRangeBox Layout.columnSpan: 2 Layout.fillWidth: true + Layout.fillHeight: true Layout.alignment: Qt.AlignHCenter - Layout.leftMargin: 25 - Layout.rightMargin: 25 - Layout.topMargin: 40 - visible: editingRange + Layout.leftMargin: Kirigami.Units.largeSpacing + Layout.rightMargin: Kirigami.Units.largeSpacing + Layout.topMargin: Kirigami.Units.largeSpacing * 3 + Layout.bottomMargin: Kirigami.Units.largeSpacing * 3 + /* visible: editingRange */ + + Kirigami.Theme.colorSet: Kirigami.Theme.Complementary + + color: Kirigami.Theme.backgroundColor + border.width: 1 + border.color: Kirigami.Theme.disabledTextColor + radius: 6 ColumnLayout { - anchors.fill: parent - - Controls.RangeSlider { - id: videoLengthSlider - - Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter - Layout.leftMargin: 25 - Layout.rightMargin: 25 - - to: videoPreview.duration - from: 0 - stepSize: 0.1 - snapMode: Controls.RangeSlider.SnapAlways - - first.value: video.startTime - second.value: video.endTime - - first.onMoved: updateStartTime(first.value) - second.onMoved: updateEndTime(second.value) - + anchors { + fill: parent + topMargin: 5 + bottomMargin: 5 + leftMargin: 5 + rightMargin: 5 } + + Controls.Label { + text: "Adjust start and end times:" + Rectangle { + anchors.top: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + implicitHeight: Kirigami.Units.smallSpacing / 3 + color: Kirigami.Theme.disabledTextColor + } + } + Presenter.RangedSlider { + id: videoLengthSlider Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter - Layout.leftMargin: 25 - Layout.rightMargin: 25 + Layout.topMargin: Kirigami.Units.smallSpacing * 3 + /* Layout.leftMargin: 25 */ + /* Layout.rightMargin: 25 */ - onFirstReleased: showPassiveNotification("first") - onSecondReleased: showPassiveNotification("Second") + from: 0 + to: videoPreview.duration + + firstInitialValue: video.startTime + secondInitialValue: video.endTime + + onFirstMoved: videoPreview.seek(firstVisualPosition) + onSecondMoved: videoPreview.seek(secondVisualPosition) + + onFirstReleased: updateStartTime(firstValue) + onSecondReleased: updateEndTime(secondValue) } RowLayout { Layout.preferredWidth: parent.width Layout.alignment: Qt.AlignLeft - Layout.leftMargin: 25 - Layout.rightMargin: 25 - Controls.Label { + Controls.TextField { Layout.alignment: Qt.AlignLeft - text: "Start Time: " + new Date(videoLengthSlider.first.value * 1000).toISOString().slice(11, 19); + text: "Start Time: " + new Date(videoLengthSlider.firstVisualPosition * 1000).toISOString().slice(11, 19); + horizontalAlignment: TextInput.AlignHCenter } - Controls.Label { + Controls.TextField { Layout.alignment: Qt.AlignRight - text: "End Time: " + new Date(videoLengthSlider.second.value * 1000).toISOString().slice(11, 19); + text: "End Time: " + new Date(videoLengthSlider.secondVisualPosition * 1000).toISOString().slice(11, 19); + horizontalAlignment: TextInput.AlignHCenter } } - Controls.ToolButton { - text: "FIX" - onClicked: videoLengthSlider.setValues(video.startTime, video.endTime) - } - } } @@ -236,8 +242,6 @@ Item { interval: 100 onTriggered: { videoPreview.loadFile(video.filePath.toString()); - videoLengthSlider.setValues(video.startTime, video.endTime); - /* showPassiveNotification(video[0]); */ } } @@ -247,7 +251,6 @@ Item { console.log(video.startTime); console.log(video.endTime); mpvLoadingTimer.restart(); - videoLengthSlider.setValues(vid.startTime, vid.endTime); footerLeftString = "File path: " + video.filePath } @@ -258,18 +261,13 @@ Item { } function updateEndTime(value) { - videoPreview.seek(value); - videoProxyModel.updateEndTime(video.id, value); + videoProxyModel.updateEndTime(video.id, Math.min(value, videoPreview.duration)); video.endTime = value; - /* showPassiveNotification(video.endTime); */ } function updateStartTime(value) { - /* changeStartTime(value, false); */ - videoPreview.seek(value); - videoProxyModel.updateStartTime(video.id, value); + videoProxyModel.updateStartTime(video.id, Math.max(value, 0)); video.startTime = value; - /* showPassiveNotification(value); */ } function updateTitle(text) {