adding in a video model and editor

This commit is contained in:
Chris Cochrun 2022-03-15 15:08:17 -05:00
parent fab9f86b41
commit c35c0f6550
22 changed files with 972 additions and 160 deletions

View file

@ -0,0 +1,230 @@
import QtQuick 2.13
import QtQuick.Controls 2.15 as Controls
import QtQuick.Dialogs 1.3
import QtQuick.Layouts 1.2
import org.kde.kirigami 2.13 as Kirigami
import "./" as Presenter
Item {
id: root
GridLayout {
id: mainLayout
anchors.fill: parent
columns: 2
rowSpacing: 5
columnSpacing: 0
Controls.ToolBar {
Layout.fillWidth: true
Layout.columnSpan: 2
id: toolbar
RowLayout {
anchors.fill: parent
Controls.ComboBox {
model: Qt.fontFamilies()
implicitWidth: 300
editable: true
hoverEnabled: true
onCurrentTextChanged: showPassiveNotification(currentText)
}
Controls.SpinBox {
editable: true
from: 5
to: 72
hoverEnabled: true
}
Controls.ComboBox {
model: ["IMAGES", "Center", "Right", "Justify"]
implicitWidth: 100
hoverEnabled: true
}
Controls.ToolButton {
text: "B"
hoverEnabled: true
}
Controls.ToolButton {
text: "I"
hoverEnabled: true
}
Controls.ToolButton {
text: "U"
hoverEnabled: true
}
Controls.ToolSeparator {}
Item { Layout.fillWidth: true }
Controls.ToolSeparator {}
Controls.ToolButton {
text: "Effects"
icon.name: "image-auto-adjust"
hoverEnabled: true
onClicked: {}
}
Controls.ToolButton {
id: backgroundButton
text: "Background"
icon.name: "fileopen"
hoverEnabled: true
onClicked: backgroundType.open()
}
Controls.Popup {
id: backgroundType
x: backgroundButton.x
y: backgroundButton.y + backgroundButton.height + 20
modal: true
focus: true
dim: false
background: Rectangle {
Kirigami.Theme.colorSet: Kirigami.Theme.Tooltip
color: Kirigami.Theme.backgroundColor
radius: 10
border.color: Kirigami.Theme.activeBackgroundColor
border.width: 2
}
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
ColumnLayout {
anchors.fill: parent
Controls.ToolButton {
Layout.fillHeight: true
Layout.fillWidth: true
text: "Video"
icon.name: "emblem-videos-symbolic"
onClicked: videoFileDialog.open() & backgroundType.close()
}
Controls.ToolButton {
Layout.fillWidth: true
Layout.fillHeight: true
text: "Image"
icon.name: "folder-pictures-symbolic"
onClicked: imageFileDialog.open() & backgroundType.close()
}
}
}
}
}
Controls.SplitView {
Layout.fillHeight: true
Layout.fillWidth: true
Layout.columnSpan: 2
handle: Item{
implicitWidth: 6
Rectangle {
height: parent.height
anchors.horizontalCenter: parent.horizontalCenter
width: 1
color: Controls.SplitHandle.hovered ? Kirigami.Theme.hoverColor : Kirigami.Theme.backgroundColor
}
}
ColumnLayout {
Controls.SplitView.fillHeight: true
Controls.SplitView.preferredWidth: 500
Controls.SplitView.minimumWidth: 500
Controls.TextField {
id: songTitleField
Layout.preferredWidth: 300
Layout.fillWidth: true
Layout.leftMargin: 20
Layout.rightMargin: 20
placeholderText: "Song Title..."
text: songTitle
padding: 10
onEditingFinished: updateTitle(text);
}
Controls.TextField {
id: songVorderField
Layout.preferredWidth: 300
Layout.fillWidth: true
Layout.leftMargin: 20
Layout.rightMargin: 20
placeholderText: "verse order..."
text: songVorder
padding: 10
onEditingFinished: updateVerseOrder(text);
}
Controls.ScrollView {
id: songLyricsField
Layout.preferredHeight: 3000
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: 20
rightPadding: 20
Controls.TextArea {
id: lyricsEditor
width: parent.width
placeholderText: "Put lyrics here..."
persistentSelection: true
text: songLyrics
textFormat: TextEdit.MarkdownText
padding: 10
onEditingFinished: {
updateLyrics(text);
editorTimer.running = false;
}
onPressed: editorTimer.running = true
}
}
Controls.TextField {
id: songAuthorField
Layout.fillWidth: true
Layout.preferredWidth: 300
Layout.leftMargin: 20
Layout.rightMargin: 20
placeholderText: "Author..."
text: songAuthor
padding: 10
onEditingFinished: updateAuthor(text)
}
}
ColumnLayout {
Controls.SplitView.fillHeight: true
Controls.SplitView.preferredWidth: 700
Controls.SplitView.minimumWidth: 300
Rectangle {
id: slideBar
color: Kirigami.Theme.highlightColor
Layout.preferredWidth: 500
Layout.preferredHeight: songTitleField.height
Layout.rightMargin: 20
Layout.leftMargin: 20
}
Presenter.SlideEditor {
id: slideEditor
Layout.preferredWidth: 500
Layout.fillWidth: true
Layout.preferredHeight: slideEditor.width / 16 * 9
Layout.bottomMargin: 30
Layout.rightMargin: 20
Layout.leftMargin: 20
}
}
}
}
Timer {
id: editorTimer
interval: 1000
repeat: true
running: false
onTriggered: updateLyrics(lyricsEditor.text)
}
}

View file

@ -109,6 +109,7 @@ ColumnLayout {
showPassiveNotification(serviceItemList.currentIndex);
changeSlideBackground(background, backgroundType);
changeSlideText(text);
changeSlideType(type);
}
}

View file

@ -1,14 +1,17 @@
import QtQuick 2.13
import QtQuick.Controls 2.0 as Controls
import QtQuick.Layouts 1.2
import Qt.labs.platform 1.1 as Labs
import org.kde.kirigami 2.13 as Kirigami
import "./" as Presenter
import org.presenter 1.0
import mpv 1.0
Item {
id: root
property string selectedLibrary: "songs"
property bool overlay: false
Kirigami.Theme.colorSet: Kirigami.Theme.View
@ -292,10 +295,37 @@ Item {
opacity: 1.0
Controls.Label {
id: videoLabel
anchors.centerIn: parent
text: "Videos"
}
Controls.Label {
id: videoCount
anchors {left: videoLabel.right
verticalCenter: videoLabel.verticalCenter
leftMargin: 15}
text: videosqlmodel.rowCount()
font.pixelSize: 15
color: Kirigami.Theme.disabledTextColor
}
Kirigami.Icon {
id: videoDrawerArrow
anchors {right: parent.right
verticalCenter: videoCount.verticalCenter
rightMargin: 10}
source: "arrow-down"
rotation: selectedLibrary == "videos" ? 0 : 180
Behavior on rotation {
NumberAnimation {
easing.type: Easing.OutCubic
duration: 300
}
}
}
MouseArea {
anchors.fill: parent
onClicked: {
@ -313,6 +343,9 @@ Item {
Layout.preferredHeight: parent.height - 200
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
model: videosqlmodel
delegate: videoDelegate
clip: true
state: "deselected"
states: [
@ -339,6 +372,109 @@ Item {
duration: 300
}
}
Component {
id: videoDelegate
Item{
implicitWidth: ListView.view.width
height: selectedLibrary == "videos" ? 50 : 0
Kirigami.BasicListItem {
id: videoListItem
property bool rightMenu: false
implicitWidth: videoLibraryList.width
height: selectedLibrary == "videos" ? 50 : 0
clip: true
label: title
/* subtitle: author */
supportsMouseEvents: false
backgroundColor: {
if (parent.ListView.isCurrentItem) {
Kirigami.Theme.highlightColor;
} else if (videoDragHandler.containsMouse){
Kirigami.Theme.highlightColor;
} else {
Kirigami.Theme.backgroundColor;
}
}
textColor: {
if (parent.ListView.isCurrentItem || videoDragHandler.containsMouse)
activeTextColor;
else
Kirigami.Theme.textColor;
}
Behavior on height {
NumberAnimation {
easing.type: Easing.OutCubic
duration: 300
}
}
Drag.active: videoDragHandler.drag.active
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
Drag.keys: [ "library" ]
states: State {
name: "dragged"
when: videoListItem.Drag.active
PropertyChanges {
target: videoListItem
x: x
y: y
}
}
}
MouseArea {
id: videoDragHandler
anchors.fill: parent
hoverEnabled: true
drag {
target: videoListItem
onActiveChanged: {
if (videoDragHandler.drag.active) {
dragVideoTitle = title
showPassiveNotification(dragVideoTitle)
} else {
videoListItem.Drag.drop()
}
}
filterChildren: true
threshold: 10
}
MouseArea {
id: videoClickHandler
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if(mouse.button == Qt.RightButton)
rightClickVideoMenu.popup()
else{
videoLibraryList.currentIndex = index
const video = videosqlmodel.getVideo(videoLibraryList.currentIndex);
/* showPassiveNotification("selected video: " + video); */
if (!editMode)
editMode = true;
editSwitch("video", video);
}
}
}
}
Controls.Menu {
id: rightClickVideoMenu
x: videoClickHandler.mouseX
y: videoClickHandler.mouseY + 10
Kirigami.Action {
text: "delete"
onTriggered: videosqlmodel.deleteVideo(index)
}
}
}
}
}
Rectangle {
@ -513,5 +649,59 @@ Item {
}
}
DropArea {
id: fileDropArea
anchors.fill: parent
onDropped: drop => {
overlay = false;
showPassiveNotification("dropped");
print(drop.urls);
/* thumbnailer.loadFile(drop.urls[0]); */
addVideo(drop.urls[0]);
}
onEntered: overlay = true
onExited: overlay = false
function addVideo(url) {
videosqlmodel.newVideo(url);
selectedLibrary = "videos";
videoLibraryList.currentIndex = videosqlmodel.rowCount();
print(videosqlmodel.getVideo(videoLibraryList.currentIndex));
const video = videosqlmodel.getVideo(videoLibraryList.currentIndex);
showPassiveNotification("newest video: " + video);
if (!editMode)
editMode = true;
editSwitch("video", video);
}
}
Rectangle {
id: fileDropOverlay
color: overlay ? Kirigami.Theme.highlightColor : "#00000000"
anchors.fill: parent
border.width: 8
border.color: overlay ? Kirigami.Theme.hoverColor : "#00000000"
}
MpvObject {
id: thumbnailer
useHwdec: true
enableAudio: false
width: 0
height: 0
Component.onCompleted: print("ready")
onFileLoaded: {
thumbnailer.pause();
print("FILE: " + thumbnailer.mediaTitle);
thumbnailer.screenshotToFile(thumbnailFile(thumbnailer.mediaTitle));
showPassiveNotification("Screenshot Taken to: " + thumbnailFile(thumbnailer.mediaTitle));
thumbnailer.stop();
}
function thumbnailFile(title) {
const thumbnailFolder = Labs.StandardPaths.writableLocation(Labs.StandardPaths.AppDataLocation) + "/thumbnails/";
return Qt.resolvedUrl(thumbnailFolder + title);
}
}
}
}

View file

@ -20,6 +20,8 @@ Controls.Page {
property string songVorder: ""
property int blurRadius: 0
/* property var video */
property string dragSongTitle: ""
property bool editing: true
@ -78,6 +80,20 @@ Controls.Page {
}
}
Component {
id: videoEditorComp
Presenter.VideoEditor {
id: videoEditor
}
}
Component {
id: imageEditorComp
Presenter.ImageEditor {
id: imageEditor
}
}
Loader {
id: presentLoader
active: presenting
@ -152,6 +168,19 @@ Controls.Page {
id: songsqlmodel
}
VideoSqlModel {
id: videosqlmodel
}
function changeSlideType(type) {
/* showPassiveNotification("used to be: " + presentation.text); */
presentation.itemType = type;
/* showPassiveNotification("next"); */
if (slideItem)
slideItem.itemType = type;
/* showPassiveNotification("last"); */
}
function changeSlideText(text) {
/* showPassiveNotification("used to be: " + presentation.text); */
presentation.text = text;
@ -193,11 +222,24 @@ Controls.Page {
showPassiveNotification("previous slide please")
}
function editSwitch(edit) {
if (edit)
mainPageArea.push(songEditorComp, Controls.StackView.Immediate)
else
mainPageArea.pop(Controls.StackView.Immediate)
function editSwitch(editType, item) {
if (editMode) {
switch (editType) {
case "song" :
mainPageArea.push(songEditorComp, Controls.StackView.Immediate);
break;
case "video" :
mainPageArea.push(videoEditorComp, {"video": item}, Controls.StackView.Immediate);
break;
case "image" :
mainPageArea.push(imageEditorComp, Controls.StackView.Immediate);
break;
default:
mainPageArea.pop(Controls.StackView.Immediate);
editMode = false;
}
} else
mainPageArea.pop(Controls.StackView.Immediate);
}
function present(present) {

View file

@ -11,6 +11,7 @@ Item {
id: root
property string text
property string itemType
property url imagebackground
property url vidbackground
@ -80,6 +81,7 @@ Item {
Layout.alignment: Qt.AlignCenter
textSize: width / 15
text: root.text
itemType: root.itemType
imageSource: imagebackground
videoSource: vidbackground
preview: true

View file

@ -45,10 +45,10 @@ Item {
enableAudio: !preview
Component.onCompleted: mpvLoadingTimer.start()
onFileLoaded: {
print(videoSource + " has been loaded");
showPassiveNotification(videoSource + " has been loaded");
if (itemType == "song")
mpv.setProperty("loop", "inf");
print(mpv.getProperty("loop"));
showPassiveNotification(mpv.getProperty("loop"));
}
MouseArea {

View file

@ -0,0 +1,227 @@
import QtQuick 2.13
import QtQuick.Controls 2.15 as Controls
import QtQuick.Dialogs 1.3
import QtQuick.Layouts 1.2
import org.kde.kirigami 2.13 as Kirigami
import "./" as Presenter
import mpv 1.0
Item {
id: root
property var video
property bool audioOn: true
GridLayout {
id: mainLayout
anchors.fill: parent
columns: 2
rowSpacing: 5
columnSpacing: 0
Controls.ToolBar {
Layout.fillWidth: true
Layout.columnSpan: 2
id: toolbar
RowLayout {
anchors.fill: parent
Controls.ComboBox {
model: Qt.fontFamilies()
implicitWidth: 300
editable: true
hoverEnabled: true
onCurrentTextChanged: showPassiveNotification(currentText)
}
Controls.SpinBox {
editable: true
from: 5
to: 72
hoverEnabled: true
}
Controls.ComboBox {
model: ["VIDEOS", "Center", "Right", "Justify"]
implicitWidth: 100
hoverEnabled: true
}
Controls.ToolButton {
text: "B"
hoverEnabled: true
}
Controls.ToolButton {
text: "I"
hoverEnabled: true
}
Controls.ToolButton {
text: "U"
hoverEnabled: true
}
Controls.ToolSeparator {}
Item { Layout.fillWidth: true }
Controls.ToolSeparator {}
Controls.ToolButton {
text: "Effects"
icon.name: "image-auto-adjust"
hoverEnabled: true
onClicked: {}
}
Controls.ToolButton {
id: backgroundButton
text: "Background"
icon.name: "fileopen"
hoverEnabled: true
onClicked: backgroundType.open()
}
Controls.Popup {
id: backgroundType
x: backgroundButton.x
y: backgroundButton.y + backgroundButton.height + 20
modal: true
focus: true
dim: false
background: Rectangle {
Kirigami.Theme.colorSet: Kirigami.Theme.Tooltip
color: Kirigami.Theme.backgroundColor
radius: 10
border.color: Kirigami.Theme.activeBackgroundColor
border.width: 2
}
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
ColumnLayout {
anchors.fill: parent
Controls.ToolButton {
Layout.fillHeight: true
Layout.fillWidth: true
text: "Video"
icon.name: "emblem-videos-symbolic"
onClicked: videoFileDialog.open() & backgroundType.close()
}
Controls.ToolButton {
Layout.fillWidth: true
Layout.fillHeight: true
text: "Image"
icon.name: "folder-pictures-symbolic"
onClicked: imageFileDialog.open() & backgroundType.close()
}
}
}
}
}
Controls.SplitView {
Layout.fillHeight: true
Layout.fillWidth: true
Layout.columnSpan: 2
handle: Item{
implicitWidth: 6
Rectangle {
height: parent.height
anchors.horizontalCenter: parent.horizontalCenter
width: 1
color: Controls.SplitHandle.hovered ? Kirigami.Theme.hoverColor : Kirigami.Theme.backgroundColor
}
}
ColumnLayout {
Controls.SplitView.fillHeight: true
Controls.SplitView.preferredWidth: 300
Controls.SplitView.minimumWidth: 100
Controls.TextField {
id: videoTitleField
Layout.preferredWidth: 300
Layout.fillWidth: true
Layout.leftMargin: 20
Layout.rightMargin: 20
placeholderText: "Song Title..."
text: video[0]
padding: 10
/* onEditingFinished: updateTitle(text); */
}
Item {
id: empty
Layout.fillHeight: true
}
}
ColumnLayout {
Controls.SplitView.fillHeight: true
Controls.SplitView.preferredWidth: 700
Controls.SplitView.minimumWidth: 300
spacing: 5
Item {
id: topEmpty
Layout.fillHeight: true
}
MpvObject {
id: videoPreview
objectName: "mpv"
Layout.preferredWidth: 600
Layout.preferredHeight: Layout.preferredWidth / 16 * 9
Layout.alignment: Qt.AlignCenter
useHwdec: true
enableAudio: audioOn
Component.onCompleted: mpvLoadingTimer.start()
onPositionChanged: videoSlider.value = position
onFileLoaded: {
showPassiveNotification(video.title + " has been loaded");
videoPreview.pause();
/* showPassiveNotification(mpv.getProperty("loop")); */
}
}
Rectangle {
id: videoBg
color: Kirigami.Theme.alternateBackgroundColor
Layout.preferredWidth: videoPreview.Layout.preferredWidth
Layout.preferredHeight: videoTitleField.height
Layout.alignment: Qt.AlignHCenter
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: false
onMoved: videoPreview.seek(value);
}
}
}
Item {
id: botEmpty
Layout.fillHeight: true
}
}
}
}
Timer {
id: mpvLoadingTimer
interval: 100
onTriggered: {
videoPreview.loadFile(video[1].toString());
/* showPassiveNotification(video[0]); */
}
}
}