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:
parent
8b231de32b
commit
81ea712c6b
2 changed files with 131 additions and 124 deletions
|
@ -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
|
/* } */
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,44 +102,36 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
RowLayout {
|
||||||
id: videoBg
|
|
||||||
color: Kirigami.Theme.alternateBackgroundColor
|
|
||||||
|
|
||||||
anchors.top: videoPreview.bottom
|
anchors.top: videoPreview.bottom
|
||||||
width: parent.width
|
width: videoPreview.width
|
||||||
height: videoTitleField.height
|
height: videoTitleField.height
|
||||||
|
spacing: 2
|
||||||
RowLayout {
|
Kirigami.Icon {
|
||||||
anchors.fill: parent
|
source: videoPreview.isPlaying ? "media-pause" : "media-play"
|
||||||
spacing: 2
|
Layout.preferredWidth: 25
|
||||||
Kirigami.Icon {
|
Layout.preferredHeight: 25
|
||||||
source: videoPreview.isPlaying ? "media-pause" : "media-play"
|
MouseArea {
|
||||||
Layout.preferredWidth: 25
|
anchors.fill: parent
|
||||||
Layout.preferredHeight: 25
|
onPressed: videoPreview.playPause()
|
||||||
MouseArea {
|
cursorShape: Qt.PointingHandCursor
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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
|
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) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue