From 9f4d426a8fe953e0c2a62dc35337cdc4175ce30b Mon Sep 17 00:00:00 2001 From: Chris Cochrun Date: Thu, 28 Sep 2023 06:31:29 -0500 Subject: [PATCH] fixing small ui things and making audio picking use portal --- src/qml/main.qml | 2 +- src/qml/presenter/DragHandle.qml | 284 +++++++++++++++-------------- src/qml/presenter/Presentation.qml | 2 + src/qml/presenter/ServiceList.qml | 2 +- src/qml/presenter/SongEditor.qml | 34 +++- src/rust/file_helper.rs | 10 +- src/rust/songs/song_model.rs | 6 +- 7 files changed, 189 insertions(+), 151 deletions(-) diff --git a/src/qml/main.qml b/src/qml/main.qml index 6d75923..9c5fd78 100644 --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -276,7 +276,7 @@ Kirigami.ApplicationWindow { } function load() { - const file = fileHelper.loadFile(); + const file = fileHelper.loadFile("Load Presentation"); const loaded = mainPage.serviceItems.load(file); loaded ? showPassiveNotification("Loaded: " + file) : showPassiveNotification("FAILED!"); diff --git a/src/qml/presenter/DragHandle.qml b/src/qml/presenter/DragHandle.qml index 710f8bb..8028ad6 100644 --- a/src/qml/presenter/DragHandle.qml +++ b/src/qml/presenter/DragHandle.qml @@ -3,144 +3,146 @@ import QtQuick.Layouts 1.15 import org.kde.kirigami 2.13 as Kirigami Item { - id: root - - /** - * listItem: Item - * The id of the delegate that we want to drag around, which *must* - * be a child of the actual ListView's delegate - */ - property Item listItem - - /** - * listView: Listview - * The id of the ListView the delegates belong to. - */ - property ListView listView - property bool containsMouse - - /** - * Emitted when the drag handle wants to move the item in the model - * The following example does the move in the case a ListModel is used - * @code - * onMoveRequested: listModel.move(oldIndex, newIndex, 1) - * @endcode - * @param oldIndex the index the item is currently at - * @param newIndex the index we want to move the item to - */ - signal moveRequested(int oldIndex, int newIndex) - - /** - * Emitted when the drag operation is complete and the item has been - * dropped in the new final position - */ - signal dropped() - - // Emitted when clicking to activate underneath mousearea - signal clicked() - signal rightClicked() - - MouseArea { - id: mouseArea - anchors.fill: parent - drag { - target: listItem - axis: Drag.YAxis - minimumY: 0 - maximumY: listView.height - listItem.height - filterChildren: true - } - /* cursorShape: pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor */ - - property int startY - property int mouseDownY - property Item originalParent - property int autoScrollThreshold: (listView.contentHeight > listView.height) ? listItem.height * 3 : 0 - - function arrangeItem() { - var newIndex = listView.indexAt(1, - listView.contentItem.mapFromItem(listItem, 0, 0).y + - mouseArea.mouseDownY); - - if (Math.abs(listItem.y - mouseArea.startY) > height && newIndex > -1 && - newIndex !== index) { - console.log("old index is: " + index + " and new index is: " + newIndex); - root.moveRequested(index, newIndex); - } - } - - preventStealing: false - onPressed: { - listView.interactive = false; - mouseArea.originalParent = listItem.parent; - listItem.parent = listView; - listItem.y = mouseArea.originalParent.mapToItem(listItem.parent, listItem.x, listItem.y).y; - mouseArea.originalParent.z = 99; - mouseArea.startY = listItem.y; - mouseArea.mouseDownY = mouse.y; - } - - onPositionChanged: { - if (!pressed) { - return; - } - mouseArea.arrangeItem(); - - scrollTimer.interval = 500 * Math.max(0.1, (1-Math.max(mouseArea.autoScrollThreshold - listItem.y, listItem.y - listView.height + mouseArea.autoScrollThreshold + listItem.height) / mouseArea.autoScrollThreshold)); - scrollTimer.running = (listItem.y < mouseArea.autoScrollThreshold || - listItem.y > listView.height - mouseArea.autoScrollThreshold); - } - onReleased: { - listView.interactive = true; - listItem.y = mouseArea.originalParent.mapFromItem(listItem, 0, 0).y; - listItem.parent = mouseArea.originalParent; - dropAnimation.running = true; - scrollTimer.running = false; - root.dropped(); - } - onCanceled: released() - SequentialAnimation { - id: dropAnimation - YAnimator { - target: listItem - from: listItem.y - to: 0 - duration: Kirigami.Units.longDuration - easing.type: Easing.InOutQuad - } - PropertyAction { - target: listItem.parent - property: "z" - value: 0 - } - } - Timer { - id: scrollTimer - interval: 500 - repeat: true - onTriggered: { - if (listItem.y < mouseArea.autoScrollThreshold) { - listView.contentY = Math.max(0, listView.contentY - Kirigami.Units.gridUnit) - } else { - listView.contentY = Math.min(listView.contentHeight - listView.height, listView.contentY + Kirigami.Units.gridUnit) - } - mouseArea.arrangeItem(); - } - } - - MouseArea { - id: clickArea - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.LeftButton | Qt.RightButton - onEntered: root.containsMouse = true - onExited: root.containsMouse = false - onClicked: { - if (mouse.button === Qt.RightButton) - root.rightClicked(); - else - root.clicked(); - } - } - } -} + id: root + + /** + * @brief This property holds the delegate that will be dragged around. + * + * This item *must* be a child of the actual ListView's delegate. + */ + property Item listItem + + /** + * @brief This property holds the ListView that the delegate belong to. + */ + property ListView listView + + /** + * @brief This signal is emitted when the drag handle wants to move the item in the model. + * + * The following example does the move in the case a ListModel is used: + * @code{.qml} + * onMoveRequested: listModel.move(oldIndex, newIndex, 1) + * @endcode + * @param oldIndex the index the item is currently at + * @param newIndex the index we want to move the item to + */ + signal moveRequested(int oldIndex, int newIndex) + + /** + * @brief This signal is emitted when the drag operation is complete and the item has been + * dropped in the new final position. + */ + signal dropped() + + implicitWidth: Kirigami.Units.iconSizes.smallMedium + implicitHeight: implicitWidth + + MouseArea { + id: mouseArea + anchors.fill: parent + drag { + target: listItem + axis: Drag.YAxis + minimumY: 0 + } + cursorShape: pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor + + Kirigami.Icon { + id: internal + source: "handle-sort" + property int startY + property int mouseDownY + property Item originalParent + opacity: mouseArea.pressed || (!Kirigami.Settings.tabletMode && listItem.hovered) ? 1 : 0.6 + property int listItemLastY + property bool draggingUp + color: "white" + + function arrangeItem() { + const newIndex = listView.indexAt(1, listView.contentItem.mapFromItem(mouseArea, 0, internal.mouseDownY).y); + + if (newIndex > -1 && ((internal.draggingUp && newIndex < index) || (!internal.draggingUp && newIndex > index))) { + root.moveRequested(index, newIndex); + } + } + + anchors.fill: parent + } + preventStealing: true + + + onPressed: mouse => { + internal.originalParent = listItem.parent; + listItem.parent = listView; + listItem.y = internal.originalParent.mapToItem(listItem.parent, listItem.x, listItem.y).y; + internal.originalParent.z = 99; + internal.startY = listItem.y; + internal.listItemLastY = listItem.y; + internal.mouseDownY = mouse.y; + // while dragging listItem's height could change + // we want a const maximumY during the dragging time + mouseArea.drag.maximumY = listView.height - listItem.height; + } + + onPositionChanged: mouse => { + if (!pressed || listItem.y === internal.listItemLastY) { + return; + } + + internal.draggingUp = listItem.y < internal.listItemLastY + internal.listItemLastY = listItem.y; + + internal.arrangeItem(); + + // autoscroll when the dragging item reaches the listView's top/bottom boundary + scrollTimer.running = (listView.contentHeight > listView.height) + && ( (listItem.y === 0 && !listView.atYBeginning) || + (listItem.y === mouseArea.drag.maximumY && !listView.atYEnd) ); + } + onReleased: mouse => { + listItem.y = internal.originalParent.mapFromItem(listItem, 0, 0).y; + listItem.parent = internal.originalParent; + dropAnimation.running = true; + scrollTimer.running = false; + root.dropped(); + } + onCanceled: released() + SequentialAnimation { + id: dropAnimation + YAnimator { + target: listItem + from: listItem.y + to: 0 + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad + } + PropertyAction { + target: listItem.parent + property: "z" + value: 0 + } + } + Timer { + id: scrollTimer + interval: 50 + repeat: true + onTriggered: { + if (internal.draggingUp) { + listView.contentY -= Kirigami.Units.gridUnit; + if (listView.atYBeginning) { + listView.positionViewAtBeginning(); + stop(); + } + } else { + listView.contentY += Kirigami.Units.gridUnit; + if (listView.atYEnd) { + listView.positionViewAtEnd(); + stop(); + } + } + internal.arrangeItem(); + } + } + } + } diff --git a/src/qml/presenter/Presentation.qml b/src/qml/presenter/Presentation.qml index 440e7e1..883bef7 100644 --- a/src/qml/presenter/Presentation.qml +++ b/src/qml/presenter/Presentation.qml @@ -107,6 +107,7 @@ FocusScope { implicitHeight: Kirigami.Units.gridUnit * 10 anchors.right: previewSlide.left anchors.verticalCenter: parent.verticalCenter + color: "white" MouseArea { anchors.fill: parent onPressed: previousSlideAction() @@ -141,6 +142,7 @@ FocusScope { implicitHeight: Kirigami.Units.gridUnit * 10 anchors.left: previewSlide.right anchors.verticalCenter: parent.verticalCenter + color: "white" MouseArea { anchors.fill: parent onPressed: nextSlideAction() diff --git a/src/qml/presenter/ServiceList.qml b/src/qml/presenter/ServiceList.qml index 3e1b24d..8695762 100644 --- a/src/qml/presenter/ServiceList.qml +++ b/src/qml/presenter/ServiceList.qml @@ -263,7 +263,7 @@ Item { } } - Kirigami.ListItemDragHandle { + Presenter.DragHandle { id: dragHandle anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter diff --git a/src/qml/presenter/SongEditor.qml b/src/qml/presenter/SongEditor.qml index cab708d..a6e1b32 100644 --- a/src/qml/presenter/SongEditor.qml +++ b/src/qml/presenter/SongEditor.qml @@ -208,6 +208,23 @@ Item { text: song.vorder padding: 10 onEditingFinished: updateVerseOrder(text); + background: Rectangle { + color: songVorderField.enabled ? Kirigami.Theme.backgroundColor : + song.vorder.trim().length === 0 ? + Kirigami.Theme.negativeBackgroundColor : + Kirigami.Theme.backgroundColor + implicitWidth: parent.width + implicitHeight: parent.height + radius: 10 + border.color: { + if (song.vorder.trim().length === 0) + return Kirigami.Theme.negativeTextColor + else if (songVorderField.enabled) + return Kirigami.Theme.highlightColor + else + return Kirigami.Theme.positiveColor + } + } } Controls.Label { @@ -251,6 +268,18 @@ Item { editorTimer.running = false; } onPressed: editorTimer.running = true + background: Rectangle { + color: Kirigami.Theme.backgroundColor + implicitWidth: parent.width + implicitHeight: parent.height + radius: 10 + border.color: { + if (songVorderField.enabled) + return Kirigami.Theme.highlightColor + else + return Kirigami.Theme.positiveColor + } + } } } @@ -325,7 +354,7 @@ Item { Layout.fillHeight: true text: "Audio" icon.name: "folder-music-symbolic" - onClicked: audioFileDialog.open() + onClicked: updateAudioFile() } } } @@ -465,7 +494,8 @@ Item { songProxyModel.songModel.updateVerseOrder(songIndex, vorder) } - function updateAudioFile(file) { + function updateAudioFile() { + const file = fileHelper.loadFile("Pick Audio"); songProxyModel.songModel.updateAudio(songIndex, file); } diff --git a/src/rust/file_helper.rs b/src/rust/file_helper.rs index 8a168d8..903b209 100644 --- a/src/rust/file_helper.rs +++ b/src/rust/file_helper.rs @@ -92,10 +92,12 @@ mod file_helper { } #[qinvokable] - pub fn load_file(self: Pin<&mut Self>) -> QUrl { - let file = FileDialog::new() - .set_title("Load Presentation") - .pick_file(); + pub fn load_file( + self: Pin<&mut Self>, + title: QString, + ) -> QUrl { + let title = title.to_string(); + let file = FileDialog::new().set_title(title).pick_file(); if let Some(file) = file { println!("loading-file: {:?}", file); let mut string = diff --git a/src/rust/songs/song_model.rs b/src/rust/songs/song_model.rs index 39f154d..ac6fadb 100644 --- a/src/rust/songs/song_model.rs +++ b/src/rust/songs/song_model.rs @@ -859,8 +859,10 @@ pub mod song_model { let mut verse_name = ""; debug!(verse = verse); for word in keywords.clone() { - let end_verse = verse.get(1..2).unwrap(); - let beg_verse = verse.get(0..1).unwrap(); + let end_verse = + verse.get(1..2).unwrap_or_default(); + let beg_verse = + verse.get(0..1).unwrap_or_default(); println!( "verse: {:?}, beginning: {:?}, end: {:?}, word: {:?}", verse, beg_verse, end_verse, word