import QtQuick 2.13 import QtQuick.Controls 2.15 as Controls import Qt.labs.platform 1.1 as Labs import QtQuick.Layouts 1.15 import org.kde.kirigami 2.13 as Kirigami import "./" as Presenter import org.presenter 1.0 Item { id: root property int songID property var song: songEditorModel 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 { id: fontBox model: Qt.fontFamilies() implicitWidth: root.width / 5 > 300 ? 300 : root.width / 5 editable: true hoverEnabled: true /* flat: true */ onActivated: updateFont(currentText) onAccepted: updateFont(currentText) background: Presenter.TextBackground { control: fontBox } indicator: Kirigami.Icon { anchors {right: parent.right verticalCenter: parent.verticalCenter rightMargin: 2} source: "arrow-down" rotation: fontBox.down ? 180 : 0 color: fontBox.pressed ? Kirigami.Theme.focusColor : Kirigami.Theme.textColor Behavior on rotation { NumberAnimation { easing.type: Easing.OutCubic duration: 300 } } } } Controls.SpinBox { id: fontSizeBox editable: true from: 5 to: 150 height: parent.height hoverEnabled: true onValueModified: updateFontSize(value) background: Presenter.TextBackground { control: fontSizeBox } } Controls.ComboBox { id: hAlignmentBox model: ["Left", "Center", "Right", "Justify"] implicitWidth: 100 hoverEnabled: true /* flat: true */ onActivated: updateHorizontalTextAlignment(currentText.toLowerCase()); background: Presenter.TextBackground { control: hAlignmentBox } indicator: Kirigami.Icon { anchors {right: parent.right verticalCenter: parent.verticalCenter rightMargin: 2} source: "arrow-down" rotation: hAlignmentBox.down ? 180 : 0 color: hAlignmentBox.pressed ? Kirigami.Theme.focusColor : Kirigami.Theme.textColor Behavior on rotation { NumberAnimation { easing.type: Easing.OutCubic duration: 300 } } } contentItem: Text { leftPadding: 0 rightPadding: hAlignmentBox.indicator.width + hAlignmentBox.spacing text: hAlignmentBox.displayText font: hAlignmentBox.font color: hAlignmentBox.pressed ? Kirigami.Theme.focusColor : Kirigami.Theme.textColor; verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } } Controls.ComboBox { id: vAlignmentBox model: ["Top", "Center", "Bottom"] implicitWidth: 100 hoverEnabled: true /* flat: true */ onActivated: updateVerticalTextAlignment(currentText.toLowerCase()); background: Presenter.TextBackground { control: vAlignmentBox } indicator: Kirigami.Icon { anchors {right: parent.right verticalCenter: parent.verticalCenter rightMargin: 2} source: "arrow-down" rotation: vAlignmentBox.down ? 180 : 0 color: vAlignmentBox.pressed ? Kirigami.Theme.focusColor : Kirigami.Theme.textColor Behavior on rotation { NumberAnimation { easing.type: Easing.OutCubic duration: 300 } } } contentItem: Text { leftPadding: 0 rightPadding: vAlignmentBox.indicator.width + vAlignmentBox.spacing text: vAlignmentBox.displayText font: vAlignmentBox.font color: vAlignmentBox.pressed ? Kirigami.Theme.focusColor : Kirigami.Theme.textColor; verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } } Controls.ToolButton { text: "B" hoverEnabled: true visible: false } Controls.ToolButton { text: "I" hoverEnabled: true visible: false } Controls.ToolButton { text: "U" hoverEnabled: true visible: false } Controls.ToolSeparator {} Item { Layout.fillWidth: true } Controls.ToolSeparator {} Controls.ToolButton { id: backgroundButton text: "Background" icon.name: "fileopen" hoverEnabled: true onClicked: backgroundTypePopup.open() } Controls.ToolButton { text: "Effects" icon.name: "image-auto-adjust" hoverEnabled: true onClicked: {} } Controls.Popup { id: backgroundTypePopup 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: Controls.Popup.CloseOnEscape | Controls.Popup.CloseOnPressOutsideParent ColumnLayout { anchors.fill: parent Controls.ToolButton { Layout.fillHeight: true Layout.fillWidth: true text: "Video" icon.name: "emblem-videos-symbolic" onClicked: updateBackground("video") & backgroundTypePopup.close() hoverEnabled: true } Controls.ToolButton { Layout.fillWidth: true Layout.fillHeight: true text: "Image" icon.name: "folder-pictures-symbolic" onClicked: updateBackground("image") & backgroundTypePopup.close() hoverEnabled: true } } } } } Controls.SplitView { id: songSplitView Layout.fillHeight: true Layout.fillWidth: true Layout.columnSpan: 2 handle: Item { implicitWidth: 6 Rectangle { height: parent.height anchors.horizontalCenter: parent.horizontalCenter width: 1 color: parent.Controls.SplitHandle.hovered ? Qt.lighter(Kirigami.Theme.backgroundColor, 1.5) : Qt.darker(Kirigami.Theme.backgroundColor, 1.5) } } ColumnLayout { Controls.SplitView.fillHeight: true Controls.SplitView.preferredWidth: 400 Controls.SplitView.minimumWidth: 300 Controls.Label { id: songTitleLabel Layout.preferredWidth: 300 Layout.fillWidth: true Layout.leftMargin: 20 Layout.rightMargin: 20 leftPadding: 10 text: "Title" Rectangle { anchors.top: parent.bottom anchors.left: parent.left anchors.right: parent.right implicitHeight: Kirigami.Units.smallSpacing / 3 color: Kirigami.Theme.disabledTextColor } } Controls.TextField { id: songTitleField Layout.preferredWidth: 300 Layout.fillWidth: true Layout.leftMargin: 20 Layout.rightMargin: 20 placeholderText: "Song Title..." text: song.title padding: 10 onEditingFinished: updateTitle(text); background: Presenter.TextBackground { control: songTitleField errorCondition: song.title.length === 0 } } Controls.Label { id: songVorderLabel Layout.preferredWidth: 300 Layout.fillWidth: true Layout.leftMargin: 20 Layout.rightMargin: 20 leftPadding: 10 text: "Verse Order format: V1 C1 V2 B1" Rectangle { anchors.top: parent.bottom anchors.left: parent.left anchors.right: parent.right implicitHeight: Kirigami.Units.smallSpacing / 3 color: Kirigami.Theme.disabledTextColor } } Controls.TextField { id: songVorderField /* Layout.preferredWidth: 300 */ Layout.fillWidth: true Layout.leftMargin: 20 Layout.rightMargin: 20 placeholderText: "verse order..." text: song.verseOrder padding: 10 onEditingFinished: updateVerseOrder(text); background: Presenter.TextBackground { control: songVorderField errorCondition: song.verseOrderError } } Controls.Label { id: songLyricsLabel Layout.preferredWidth: 300 Layout.fillWidth: true Layout.leftMargin: 20 Layout.rightMargin: 20 leftPadding: 10 text: "Lyrics" Rectangle { anchors.top: parent.bottom anchors.left: parent.left anchors.right: parent.right implicitHeight: Kirigami.Units.smallSpacing / 3 color: Kirigami.Theme.disabledTextColor } } Controls.ScrollView { id: songLyricsField Layout.preferredHeight: 2000 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: song.lyrics textFormat: TextEdit.PlainText padding: 10 onEditingFinished: { songEditorModel.lyrics = text /* editorTimer.running = false; */ } onPressed: editorTimer.running = true background: Presenter.TextBackground { control: lyricsEditor } } } Controls.Label { id: songAuthorLabel Layout.preferredWidth: 300 Layout.fillWidth: true Layout.leftMargin: 20 Layout.rightMargin: 20 leftPadding: 10 text: "Artist" Rectangle { anchors.top: parent.bottom anchors.left: parent.left anchors.right: parent.right implicitHeight: Kirigami.Units.smallSpacing / 3 color: Kirigami.Theme.disabledTextColor } } Controls.TextField { id: songAuthorField Layout.fillWidth: true Layout.preferredWidth: 300 Layout.leftMargin: 20 Layout.rightMargin: 20 placeholderText: "Author..." text: song.author padding: 10 onEditingFinished: updateAuthor(text) background: Presenter.TextBackground { control: songAuthorField } } Controls.Label { id: songAudioLabel Layout.preferredWidth: 300 Layout.fillWidth: true Layout.leftMargin: 20 Layout.rightMargin: 20 leftPadding: 10 text: "Audio File" Rectangle { anchors.top: parent.bottom anchors.left: parent.left anchors.right: parent.right implicitHeight: Kirigami.Units.smallSpacing / 3 color: Kirigami.Theme.disabledTextColor } } RowLayout { Layout.fillWidth: true Layout.preferredWidth: 300 Layout.leftMargin: 20 Layout.rightMargin: 20 Controls.TextField { id: songAudioField Layout.fillWidth: true placeholderText: "Audio File..." text: song.audio padding: 10 onEditingFinished: showPassiveNotification(text) background: Presenter.TextBackground { control: songAudioField errorCondition: !song.audioExists } Controls.ToolTip { text: song.audioExists ? "The audio that will be played for this song" : "The audio is missing or does not exists" } } Controls.ToolButton { id: audioPickerButton Layout.fillHeight: true text: "Audio" icon.name: "folder-music-symbolic" onClicked: updateAudioFile() hoverEnabled: true /* background: Presenter.TextBackground { */ /* control: audioPickerButton */ /* } */ } } } ColumnLayout { Controls.SplitView.fillHeight: true Controls.SplitView.preferredWidth: 700 Controls.SplitView.minimumWidth: 300 Presenter.SongEditorSlideList { id: songList imageBackground: songEditorModel.backgroundType === "image" ? song.background : "" videoBackground: songEditorModel.backgroundType === "video" ? song.background : "" font: songEditorModel.font fontSize: songEditorModel.fontSize /* hTextAlignment: songEditorModel.horizontalTextAlignment */ /* vTextAlignment: songEditorModel.verticalTextAlignment */ Layout.preferredWidth: 500 Layout.fillWidth: true Layout.fillHeight: true Layout.bottomMargin: 20 Layout.topMargin: 10 Layout.rightMargin: 0 Layout.leftMargin: 10 } } } } Timer { id: editorTimer interval: 1000 repeat: true running: false onTriggered: { if (lyricsEditor.text === songEditorModel.lyrics) { return; } else { Utils.dbg("Changing Lyrics"); /* songEditorModel.lyrics = lyricsEditor.text; */ /* showPassiveNotification("Lyrics changed"); */ songModel.updateLyrics(songID, lyricsEditor.text); clearSlides(); changeSlideText(songID); Utils.dbg("Lyrics changed"); } /* updateLyrics(lyricsEditor.text); */ } } function changeSong(index) { Utils.dbg("Preparing to change song: " + index + 1 + " out of " + songModel.count); editorTimer.running = false; clearSlides(); const updatedSong = songModel.getItem(index); Utils.dbg(updatedSong.vorder + " " + updatedSong.title + " " + updatedSong.audio); songEditorModel.loadSong(index); songEditorModel.checkVerseOrder(); songEditorModel.checkFiles(); songID = updatedSong.id; changeSlideHAlignment(songEditorModel.horizontalTextAlignment); changeSlideVAlignment(songEditorModel.verticalTextAlignment); changeSlideFont(songEditorModel.font, true); changeSlideFontSize(songEditorModel.fontSize, true) changeSlideText(index); Utils.dbg("Changing to song: " + songEditorModel.title + " with ID: " + songID); footerFirstText = "Song: "; footerSecondText = songEditorModel.title; songList.loadVideo(); } Connections { target: songEditorModel function onLyricsChanged() { } } /* function updateLyrics(lyrics) { */ /* songModel.updateLyrics(songID, lyrics); */ /* /\* songLyrics = lyrics; *\/ */ /* clearSlides(); */ /* changeSlideText(songID); */ /* } */ function updateTitle(title) { songModel.updateTitle(songID, title) song.title = title; } function updateAuthor(author) { songModel.updateAuthor(songID, author) } function updateAudio(audio) { songModel.updateAudio(songID, audio) } function updateCcli(ccli) { songModel.updateCcli(songID, ccli) } function updateVerseOrder(vorder) { songModel.updateVerseOrder(songID, vorder) songEditorModel.verseOrder = vorder; songEditorModel.checkVerseOrder(); } function updateAudioFile() { const file = fileHelper.loadFile("Pick Audio", "audio"); Utils.dbg("song: " + songID); Utils.dbg("song-title: " + songEditorModel.title) songEditorModel.audio = file; songModel.updateAudio(songID, file); songEditorModel.checkFiles(); } function updateBackground(backgroundType) { songEditorModel.backgroundType = backgroundType; const file = fileHelper.loadFile("Pick Background", backgroundType); songEditorModel.background = file; songModel.updateBackground(songID, file); songModel.updateBackgroundType(songID, backgroundType); Utils.dbg("changed background"); } function updateHorizontalTextAlignment(textAlignment) { changeSlideHAlignment(textAlignment); songModel.updateHorizontalTextAlignment(songID, textAlignment); } function updateVerticalTextAlignment(textAlignment) { changeSlideVAlignment(textAlignment); songModel.updateVerticalTextAlignment(songID, textAlignment) } function updateFont(font) { showPassiveNotification(font); changeSlideFont(font, false); songModel.updateFont(songID, font); song.font = font; } function updateFontSize(fontSize) { changeSlideFontSize(fontSize, false); songModel.updateFontSize(songID, fontSize); song.fontSize = fontSize; } function changeSlideHAlignment(alignment) { switch (alignment) { case "left" : hAlignmentBox.currentIndex = 0; songList.hTextAlignment = Text.AlignLeft; break; case "center" : hAlignmentBox.currentIndex = 1; songList.hTextAlignment = Text.AlignHCenter; break; case "right" : hAlignmentBox.currentIndex = 2; songList.hTextAlignment = Text.AlignRight; break; case "justify" : hAlignmentBox.currentIndex = 3; songList.hTextAlignment = Text.AlignJustify; break; } } function changeSlideVAlignment(alignment) { switch (alignment) { case "top" : vAlignmentBox.currentIndex = 0; songList.vTextAlignment = Text.AlignTop; break; case "center" : vAlignmentBox.currentIndex = 1; songList.vTextAlignment = Text.AlignVCenter; break; case "bottom" : vAlignmentBox.currentIndex = 2; songList.vTextAlignment = Text.AlignBottom; break; } } function changeSlideFont(font, updateBox) { const fontIndex = fontBox.find(font); if (updateBox) fontBox.currentIndex = fontIndex; songList.font = font; } function changeSlideFontSize(fontSize, updateBox) { if (updateBox) fontSizeBox.value = fontSize; songList.fontSize = fontSize; } function changeSlideText(id) { const verses = songModel.getLyricList(id); verses.forEach(songList.appendVerse); /* songList.loadVideo(); */ } function clearSlides() { songList.clear() } }