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.
This commit is contained in:
Chris Cochrun 2023-04-19 05:35:52 -05:00
parent 8b231de32b
commit 81ea712c6b
2 changed files with 131 additions and 124 deletions

View file

@ -14,13 +14,18 @@ Rectangle {
property real firstValue property real firstValue
property real secondValue property real secondValue
property real firstInitialValue
property real secondInitialValue
property real firstVisualPosition property real firstVisualPosition
property real secondVisualPosition property real secondVisualPosition
signal firstReleased() signal firstReleased()
signal secondReleased() signal secondReleased()
signal firstMoved()
signal secondMoved()
Kirigami.Theme.colorSet: Kirigami.Theme.Button Kirigami.Theme.colorSet: Kirigami.Theme.Button
Kirigami.Theme.inherit: false
color: Kirigami.Theme.backgroundColor color: Kirigami.Theme.backgroundColor
border.width: 0 border.width: 0
@ -28,31 +33,32 @@ Rectangle {
height: 6 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 { Rectangle {
id: first id: first
x: firstVisualPosition x: firstInitialValue / (to - from) * (root.width - width)
y: -6 y: -6
implicitWidth: 18 implicitWidth: 18
implicitHeight: 18 implicitHeight: 18
color: firstMouse.containsMouse ? Kirigami.Theme.hoverColor : Kirigami.Theme.alternateBackgroundColor color: firstMouse.containsMouse || firstMouse.drag.active ? Kirigami.Theme.hoverColor : Kirigami.Theme.alternateBackgroundColor
border.width: firstMouse.containsMouse ? 2 : 1 border.width: firstMouse.containsMouse || firstMouse.drag.active ? 2 : 1
border.color: firstMouse.containsMouse ? Kirigami.Theme.hoverColor : Kirigami.Theme.backgroundColor border.color: firstMouse.containsMouse || firstMouse.drag.active ? Kirigami.Theme.hoverColor : Kirigami.Theme.backgroundColor
radius: width / 2 radius: width / 2
Drag.active: firstMouse.drag.active Drag.active: firstMouse.drag.active
Drag.hotSpot.x: width / 2 Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2 Drag.hotSpot.y: height / 2
/* states: State { */
/* name: "dragged" */
/* when: first.Drag.active */
/* PropertyChanges { */
/* target: first */
/* x: x */
/* width: width */
/* height: height */
/* } */
/* } */
MouseArea { MouseArea {
id: firstMouse id: firstMouse
anchors.fill: parent anchors.fill: parent
@ -60,41 +66,38 @@ Rectangle {
drag { drag {
target: first target: first
axis: Drag.XAxis axis: Drag.XAxis
maximumX: root.right maximumX: Math.min((root.width - first.width / 2), second.x)
minimumX: root.left minimumX: 0 + first.width / 2
} }
onReleased: { onReleased: {
firstValue = mouseX; firstValue = (to - from) / (root.width - first.width) * (first.x - first.width / 2) + from;
firstVisualPosition = firstValue;
firstReleased(); firstReleased();
} }
onPositionChanged: {
if (drag.active) {
firstVisualPosition = (to - from) / (root.width - first.width) * (first.x - first.width / 2) + from;
firstMoved()
}
}
} }
} }
Rectangle { Rectangle {
id: second id: second
x: secondVisualPosition x: secondInitialValue / (to - from) * (root.width - width)
y: -6 y: -6
implicitWidth: 18 implicitWidth: 18
implicitHeight: 18 implicitHeight: 18
color: secondMouse.containsMouse ? Kirigami.Theme.hoverColor : Kirigami.Theme.alternateBackgroundColor color: secondMouse.containsMouse || secondMouse.drag.active ? Kirigami.Theme.hoverColor : Kirigami.Theme.alternateBackgroundColor
border.width: secondMouse.containsMouse ? 2 : 1 border.width: secondMouse.containsMouse || secondMouse.drag.active ? 2 : 1
border.color: secondMouse.containsMouse ? Kirigami.Theme.hoverColor : Kirigami.Theme.backgroundColor border.color: secondMouse.containsMouse || secondMouse.drag.active ? Kirigami.Theme.hoverColor : Kirigami.Theme.backgroundColor
radius: width / 2 radius: width / 2
Drag.active: secondMouse.drag.active Drag.active: secondMouse.drag.active
Drag.hotSpot.x: width / 2 Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2 Drag.hotSpot.y: height / 2
/* states: State { */
/* name: "dragged" */
/* when: second.Drag.active */
/* PropertyChanges { */
/* target: second */
/* x: x */
/* width: width */
/* height: height */
/* } */
/* } */
MouseArea { MouseArea {
id: secondMouse id: secondMouse
anchors.fill: parent anchors.fill: parent
@ -102,19 +105,25 @@ Rectangle {
drag { drag {
target: second target: second
axis: Drag.XAxis axis: Drag.XAxis
maximumX: root.width maximumX: root.width - second.width / 2
minimumX: 0 minimumX: Math.max((0 + second.width / 2), first.x)
} }
onReleased: { onReleased: {
secondValue = second.x; secondValue = (to - from) / (root.width - second.width) * (second.x - second.width / 2) + from;
secondVisualPosition = secondValue;
secondReleased(); secondReleased();
} }
onPositionChanged: {
if (drag.active) {
secondVisualPosition = (to - from) / (root.width - second.width) * (second.x - second.width / 2) + from;
secondMoved()
}
}
} }
} }
Rectangle { /* function setValues(first, second) { */
id: range /* first.x = first */
color: Kirigami.Theme.hoverColor /* second.x = second */
radius: width / 2 /* } */
}
} }

View file

@ -80,10 +80,12 @@ Item {
Item { Item {
Layout.columnSpan: 2 Layout.columnSpan: 2
Layout.preferredWidth: root.width - 50 Layout.fillWidth: true
Layout.preferredHeight: Layout.preferredWidth / 16 * 9 Layout.preferredHeight: width / 16 * 9
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
Layout.topMargin: 10 Layout.topMargin: 10
Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.rightMargin: Kirigami.Units.largeSpacing
MpvObject { MpvObject {
id: videoPreview id: videoPreview
@ -100,16 +102,10 @@ Item {
} }
} }
Rectangle {
id: videoBg
color: Kirigami.Theme.alternateBackgroundColor
anchors.top: videoPreview.bottom
width: parent.width
height: videoTitleField.height
RowLayout { RowLayout {
anchors.fill: parent anchors.top: videoPreview.bottom
width: videoPreview.width
height: videoTitleField.height
spacing: 2 spacing: 2
Kirigami.Icon { Kirigami.Icon {
source: videoPreview.isPlaying ? "media-pause" : "media-play" source: videoPreview.isPlaying ? "media-pause" : "media-play"
@ -137,75 +133,85 @@ Item {
text: new Date(videoPreview.position * 1000).toISOString().slice(11, 19); text: new Date(videoPreview.position * 1000).toISOString().slice(11, 19);
} }
} }
}
} }
Rectangle { Rectangle {
id: videoRangeBox id: videoRangeBox
Layout.columnSpan: 2 Layout.columnSpan: 2
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.leftMargin: 25 Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.rightMargin: 25 Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.topMargin: 40 Layout.topMargin: Kirigami.Units.largeSpacing * 3
visible: editingRange 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 { ColumnLayout {
anchors.fill: parent anchors {
fill: parent
Controls.RangeSlider { topMargin: 5
id: videoLengthSlider bottomMargin: 5
leftMargin: 5
Layout.fillWidth: true rightMargin: 5
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)
} }
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 { Presenter.RangedSlider {
id: videoLengthSlider
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.leftMargin: 25 Layout.topMargin: Kirigami.Units.smallSpacing * 3
Layout.rightMargin: 25 /* Layout.leftMargin: 25 */
/* Layout.rightMargin: 25 */
onFirstReleased: showPassiveNotification("first") from: 0
onSecondReleased: showPassiveNotification("Second") to: videoPreview.duration
firstInitialValue: video.startTime
secondInitialValue: video.endTime
onFirstMoved: videoPreview.seek(firstVisualPosition)
onSecondMoved: videoPreview.seek(secondVisualPosition)
onFirstReleased: updateStartTime(firstValue)
onSecondReleased: updateEndTime(secondValue)
} }
RowLayout { RowLayout {
Layout.preferredWidth: parent.width Layout.preferredWidth: parent.width
Layout.alignment: Qt.AlignLeft Layout.alignment: Qt.AlignLeft
Layout.leftMargin: 25
Layout.rightMargin: 25
Controls.Label { Controls.TextField {
Layout.alignment: Qt.AlignLeft 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 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 interval: 100
onTriggered: { onTriggered: {
videoPreview.loadFile(video.filePath.toString()); 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.startTime);
console.log(video.endTime); console.log(video.endTime);
mpvLoadingTimer.restart(); mpvLoadingTimer.restart();
videoLengthSlider.setValues(vid.startTime, vid.endTime);
footerLeftString = "File path: " + video.filePath footerLeftString = "File path: " + video.filePath
} }
@ -258,18 +261,13 @@ Item {
} }
function updateEndTime(value) { function updateEndTime(value) {
videoPreview.seek(value); videoProxyModel.updateEndTime(video.id, Math.min(value, videoPreview.duration));
videoProxyModel.updateEndTime(video.id, value);
video.endTime = value; video.endTime = value;
/* showPassiveNotification(video.endTime); */
} }
function updateStartTime(value) { function updateStartTime(value) {
/* changeStartTime(value, false); */ videoProxyModel.updateStartTime(video.id, Math.max(value, 0));
videoPreview.seek(value);
videoProxyModel.updateStartTime(video.id, value);
video.startTime = value; video.startTime = value;
/* showPassiveNotification(value); */
} }
function updateTitle(text) { function updateTitle(text) {