diff --git a/CMakeLists.txt b/CMakeLists.txt index e8a2ecf..ec65f22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH}) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(KDEInstallDirs) include(KDECMakeSettings) @@ -27,6 +27,10 @@ kde_enable_exceptions() find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Quick Test Gui QuickControls2 Widgets Sql X11Extras) find_package(KF5 ${KF_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 I18n CoreAddons) +find_package(Libmpv) +set_package_properties(Libmpv PROPERTIES TYPE REQUIRED) + + set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/README.org b/README.org index 7af305b..ba7a4fc 100644 --- a/README.org +++ b/README.org @@ -7,12 +7,12 @@ This is an attempt at building a church presentation application in Qt/QML. QML ** Features (planned are in parentheses) - Presents songs lyrics with image and video backgrounds - Use MPV as a rendering system for videos +- Simple song creation with a powerful text parser - Almost fully finished - (Present Slides) - (Custom slide builder) -- (Simple song creation with a powerful text parser) ** MPV -You will need MPV installed in order to use this. All videos run through it. This, however, enables us to make videos work very well and with a lot of control and since it uses ffmpeg underneath, nearly any codec regardless of underlying system. This prevents the need for the user to go and install other codecs to work with Windows or MacOS. It also means a much easier control system and the potential to stream web content as well. +You will need MPV installed in order to use this. All videos run through it. This, however, enables us to make videos work very well and with a lot of control and since it uses ffmpeg underneath, nearly any codec regardless of underlying system. This prevents the need for the user to install other codecs to work with Windows or MacOS. It also means a much easier control system and the potential to stream web content as well without downloading first. * Build and Run First get the source code @@ -33,4 +33,4 @@ Then run. #+END_SRC * Contact Me -If, for whatever reason, you need to contact me and get something ironed out, please do so at [[mailto:chris@tfcconnection.org][chris@tfcconnection.org]] +If, for whatever reason, you need to contact me and get something ironed out, please do so at [[mailto:chris@tfcconnection.org][chris@cochrun.xyz]] diff --git a/TODO.org b/TODO.org index c10f55a..3a80e3e 100644 --- a/TODO.org +++ b/TODO.org @@ -1,11 +1,105 @@ #+TITLE: Todo List +:PROPERTIES: +:CATEGORY: dev +:END: + * Inbox -** TODO Need to make ListModel capable of bringing in a string list [2/4] [50%] +** TODO Need to make =getLyricList= give back the verses with empty lines as separate slides :core: +[[file:~/dev/church-presenter/src/songsqlmodel.cpp:://TODO make sure to split empty line in verse into two slides]] + +** TODO Make toolbar functional for =songeditor= [3/4] [75%] :core: +[[file:~/dev/church-presenter/src/qml/presenter/SongEditor.qml::Controls.ToolBar {]] + +- [X] alignment +- [X] font - Need to finish the UI portion of it +- [X] fontsize - Need to finish the UI portion of it +- [ ] effects? + For effects, I'm not 100% sure how to do this in an easy to build out way. Should I just do them the same as the other attributes or have effects be individually stored? Which effects to use? + + I'm thinking shadows for sure for readability on slides. Also, maybe I should have an effect of like glow? But maybe I'll come back to this after more of the core system is finished. + +** TODO bug in changing slides with the arrows :core: +[[file:~/dev/church-presenter/src/qml/presenter/Presentation.qml::function changeSlide() {]] + +slides are inconsistent in changing from one slide to the next or previous. Both functions need looked at. + +Maybe my best solution would be to architect a model or class for both the presentation controller and the presentation window to follow and do all the heavy lifting in there. + +** TODO Check for edge cases in inputing wrong vorder and lyrics :core: +[[file:~/dev/church-presenter/TODO.org::*Fix broken append when importing River song][Fix broken append when importing River song]] + +** TODO Images stored in sql need to have aspect saved and applied dynamically here :core: +[[file:~/dev/church-presenter/src/qml/presenter/Slide.qml::fillMode: Image.PreserveAspectCrop]] + +** TODO Build out a slide preview system so we can see each slide in the song or image slideshow :ui: +[[file:~/dev/church-presenter/src/qml/presenter/SongEditor.qml::Presenter.SlideEditor {]] + +- [X] Initial ListView with text coming from =getLyricList= +- [ ] Depending on this [[*Need to make getLyricList give back the verses with empty lines as separate slides][Need to make getLyricList give back the verses with empty lines as separate slides]] +- [ ] Need to perhaps address the MPV crashing problem for a smoother experience. + Essentially, mpv objects cause a seg fault when we remove them from the qml graph scene and are somehow re-referencing them. Using =reuseItems=, I can prevent the seg fault but then we are storing a lot of things in memory and will definitely cause slowdowns on older hardware. + +** TODO Fix possible bug in arrangingItems in draghandler [1/3] [33%] :bug: +[[file:~/dev/church-presenter/src/qml/presenter/DragHandle.qml::function arrangeItem() {]] + +- [X] Basic fixed drag n drop +- [ ] Allow for a less buggy interaction +- [ ] Need to check for edge cases + +** PROJ [#A] Make Presentation Window follow the presenter component :core: +[[file:~/dev/church-presenter/src/qml/presenter/MainWindow.qml::Presenter.Slide {]] + +** TODO Finish toolbar in presentation display :ui: +[[file:~/dev/church-presenter/src/qml/presenter/Presentation.qml::Controls.ToolBar {]] + +** TODO Find a way to maths the textsize :slide: +[[file:~/dev/church-presenter/src/qml/presenter/Slide.qml::property real textSize: 50]] + +This may not be as needed. Apparently the text shrinks to fit it's space. + +** TODO Create a nextslide function to be used after the end of the list of slides :slide: +[[file:~/dev/church-presenter/src/qml/presenter/Presentation.qml::function nextSlide() {]] + +- [ ] Check to make sure this works in all conditions but I believe it works ok. + +** TODO Make sure the video gets changed in a proper manner to not have left over video showing from previous items :video:slide: +[[file:~/dev/church-presenter/src/qml/presenter/Presentation.qml::currentServiceItem++;]] + +- [X] Build a basic system that changes to black first and then switches to the video +- [ ] Build out a loading system that will load the next video if it needs to and then the switch can be instant. + + The second option is the best, but requires a lot more work. I have the first already working so I'll come back to this once I have more of an idea of how to do it. + +** DONE images and videos need a better get system +[[file:~/dev/church-presenter/src/videosqlmodel.cpp::QVariantList VideoSqlModel::getVideo(const int &row) {]] + +** DONE Fix broken append when importing River song +[[file:~/dev/church-presenter/src/qml/presenter/LeftDock.qml::function appendItem(name, type, background, backgroundType, text, itemID) {]] + +This was due to the song not having a vorder. Need to protect from edge cases of the user inputing the formatted text that doesn't fit what's expected in code. + +** DONE implement previousSlide and previousAction +[[file:~/dev/church-presenter/src/qml/presenter/Presentation.qml::function nextSlide() {]] + +** DONE Need to make ListModel capable of bringing in a string list [2/2] [100%] - [X] Create a Model - [X] Create a class that we'll make a list of in the model -- [ ] Implement move functions -- [ ] Implement insert functions + +** DONE Make an image sql model +[[file:~/dev/church-presenter/src/videosqlmodel.h::ifndef VIDEOSQLMODEL_H]] ** DONE Parse Lyrics to create a list of strings for slides SCHEDULED: <2022-03-23 Wed 10:00> +** DONE BUG in dropping and then selecting song will duplicate entries :dev: +SCHEDULED: <2022-04-05 Tue> +[[file:~/dev/church-presenter/src/qml/presenter/LeftDock.qml::Layout.fillHeight: true]] + +or at least turns the entry above it into the same as itself while retaining it's title? + +** DONE Make nextSlideText a nextAction function to incorporate other types of items +[[file:~/dev/church-presenter/src/qml/presenter/Presentation.qml::function nextSlideText() {]] + +** DONE Fix file dialog using basic QT theme +[[file:~/dev/church-presenter/src/qml/presenter/SongEditor.qml::FileDialog {]] + diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..ed9d9eb --- /dev/null +++ b/flake.lock @@ -0,0 +1,41 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1652776076, + "narHash": "sha256-gzTw/v1vj4dOVbpBSJX4J0DwUR6LIyXo7/SuuTJp1kM=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "04c1b180862888302ddfb2e3ad9eaa63afc60cf8", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1653117584, + "narHash": "sha256-5uUrHeHBIaySBTrRExcCoW8fBBYVSDjDYDU5A6iOl+k=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "f4dfed73ee886b115a99e5b85fdfbeb683290d83", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..d9f2d72 --- /dev/null +++ b/flake.nix @@ -0,0 +1,14 @@ +{ + description = "A Church Presentation Application"; + + inputs.flake-utils.url = "github:numtide/flake-utils"; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem + (system: + let pkgs = nixpkgs.legacyPackages.${system}; in + { + devShell = import ./shell.nix { inherit pkgs; }; + } + ); +} diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..ad75edd --- /dev/null +++ b/shell.nix @@ -0,0 +1,42 @@ +{ pkgs ? { } }: +with pkgs; +mkShell { + name = "presenter-env"; + + nativeBuildInputs = [ + gcc + gnumake + clang + cmake + extra-cmake-modules + pkg-config + libsForQt5.wrapQtAppsHook + # gccStdenv + # stdenv + ]; + + buildInputs = [ + qt5.qtbase + qt5.qttools + qt5.qtquickcontrols2 + qt5.qtx11extras + qt5.qtmultimedia + libsForQt5.kirigami2 + libsForQt5.ki18n + libsForQt5.kcoreaddons + mpv + # libsForQt5.kconfig + # ffmpeg-full + # yt-dlp + ]; + + # This creates the proper qt env so that plugins are found right. + shellHook = '' + setQtEnvironment=$(mktemp --suffix .setQtEnvironment.sh) + echo "shellHook: setQtEnvironment = $setQtEnvironment" + makeWrapper "/bin/sh" "$setQtEnvironment" "''${qtWrapperArgs[@]}" + sed "/^exec/d" -i "$setQtEnvironment" + source "$setQtEnvironment" + fish + ''; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fe6f05d..ab4398d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ target_sources(presenter serviceitemmodel.cpp serviceitemmodel.h serviceitem.cpp serviceitem.h videosqlmodel.cpp videosqlmodel.h + imagesqlmodel.cpp imagesqlmodel.h mpv/mpvobject.h mpv/mpvobject.cpp mpv/qthelper.hpp mpv/mpvhelpers.h ) diff --git a/src/main.cpp b/src/main.cpp index 4641cbb..845fdc3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,10 +22,13 @@ #include #include +#include +#include #include #include #include #include +#include #include #include #include @@ -34,6 +37,7 @@ #include "serviceitemmodel.h" #include "songsqlmodel.h" #include "videosqlmodel.h" +#include "imagesqlmodel.h" static void connectToDatabase() { // let's setup our sql database @@ -67,20 +71,25 @@ static void connectToDatabase() { int main(int argc, char *argv[]) { QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QGuiApplication app(argc, argv); - KLocalizedString::setApplicationDomain("presenter"); - QCoreApplication::setOrganizationName(QStringLiteral("presenter")); + QApplication app(argc, argv); + KLocalizedString::setApplicationDomain("librepresenter"); + QCoreApplication::setOrganizationName(QStringLiteral("librepresenter")); QCoreApplication::setOrganizationDomain(QStringLiteral("tfcconnection.org")); - QCoreApplication::setApplicationName(QStringLiteral("Church Presenter")); + QCoreApplication::setApplicationName(QStringLiteral("Libre Presenter")); #ifdef Q_OS_WINDOWS QIcon::setFallbackThemeName("breeze"); QQuickStyle::setStyle(QStringLiteral("org.kde.breeze")); + // QApplication::setStyle(QStringLiteral("breeze")); #else + QIcon::setFallbackThemeName("breeze"); QQuickStyle::setStyle(QStringLiteral("org.kde.desktop")); + QQuickStyle::setFallbackStyle(QStringLiteral("breeze")); #endif QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("system-config-display"))); + qDebug() << QQuickStyle::availableStyles(); + qDebug() << QIcon::themeName(); // apparently mpv needs this class set // let's register mpv as well @@ -90,6 +99,7 @@ int main(int argc, char *argv[]) //register our models qmlRegisterType("org.presenter", 1, 0, "SongSqlModel"); qmlRegisterType("org.presenter", 1, 0, "VideoSqlModel"); + qmlRegisterType("org.presenter", 1, 0, "ImageSqlModel"); qmlRegisterType("org.presenter", 1, 0, "ServiceItemModel"); connectToDatabase(); diff --git a/src/mpv/mpvobject.cpp b/src/mpv/mpvobject.cpp index 17aa838..39fc683 100644 --- a/src/mpv/mpvobject.cpp +++ b/src/mpv/mpvobject.cpp @@ -87,7 +87,7 @@ MpvRenderer::~MpvRenderer() { if (mpv_gl) mpv_render_context_free(mpv_gl); - mpv_terminate_destroy(obj->mpv); + // mpv_destroy(obj->mpv); } void MpvRenderer::render() { @@ -242,7 +242,7 @@ MpvObject::MpvObject(QQuickItem *parent) MpvObject::~MpvObject() { - + // quit(); } QQuickFramebufferObject::Renderer *MpvObject::createRenderer() const @@ -340,6 +340,10 @@ void MpvObject::handle_mpv_event(mpv_event *event) // See: https://github.com/mpv-player/mpv/blob/master/player/lua.c#L471 switch (event->event_id) { + case MPV_EVENT_SHUTDOWN: { + mpv_destroy(mpv); + break; + } case MPV_EVENT_LOG_MESSAGE: { mpv_event_log_message *logData = (mpv_event_log_message *)event->data; Q_EMIT logMessage( @@ -487,7 +491,7 @@ void MpvObject::pause() { // qDebug() << "pause"; if (isPlaying()) { - // qDebug() << "!isPlaying"; + qDebug() << "!isPlaying"; set_paused(true); } } diff --git a/src/qml/main.qml b/src/qml/main.qml index 128d754..b98f97c 100644 --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -5,7 +5,7 @@ import Qt.labs.platform 1.1 as Labs import QtQuick.Window 2.13 import QtQuick.Layouts 1.2 import QtMultimedia 5.15 -import QtAudioEngine 1.15 +/* import QtAudioEngine 1.15 */ import org.kde.kirigami 2.13 as Kirigami import "./presenter" as Presenter @@ -121,6 +121,7 @@ Kirigami.ApplicationWindow { /* print(Qt.application.state); */ screens = Qt.application.screens; presentationScreen = screens[1] + print(Kirigami.Settings.Style); for (let i = 0; i < screens.length; i++) { /* print(screens[i]); */ /* print(screens[i].name); */ diff --git a/src/qml/presenter/Actions.qml b/src/qml/presenter/Actions.qml index 2552ade..2f99693 100644 --- a/src/qml/presenter/Actions.qml +++ b/src/qml/presenter/Actions.qml @@ -4,7 +4,7 @@ import QtQuick.Controls 2.15 as Controls import QtQuick.Window 2.13 import QtQuick.Layouts 1.2 import QtMultimedia 5.15 -import QtAudioEngine 1.15 +/* import QtAudioEngine 1.15 */ import org.kde.kirigami 2.13 as Kirigami import "./" as Presenter diff --git a/src/qml/presenter/DragHandle.qml b/src/qml/presenter/DragHandle.qml index ad3aa0c..b15c469 100644 --- a/src/qml/presenter/DragHandle.qml +++ b/src/qml/presenter/DragHandle.qml @@ -38,6 +38,7 @@ Item { // Emitted when clicking to activate underneath mousearea signal clicked() + signal rightClicked() MouseArea { id: mouseArea @@ -55,12 +56,15 @@ Item { property int mouseDownY property Item originalParent property int autoScrollThreshold: (listView.contentHeight > listView.height) ? listItem.height * 3 : 0 - opacity: mouseArea.pressed || (!Kirigami.Settings.tabletMode && listItem.hovered) ? 1 : 0.6 function arrangeItem() { - var newIndex = listView.indexAt(1, listView.contentItem.mapFromItem(listItem, 0, 0).y + mouseArea.mouseDownY); + 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) { + if (Math.abs(listItem.y - mouseArea.startY) > height && newIndex > -1 && + newIndex !== index) { + print("old index is: " + index + " and new index is: " + newIndex); root.moveRequested(index, newIndex); } } @@ -127,10 +131,16 @@ Item { MouseArea { id: clickArea anchors.fill: parent - onClicked: root.clicked() 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(); + } } } } diff --git a/src/qml/presenter/ImageEditor.qml b/src/qml/presenter/ImageEditor.qml index 4dea612..530f7b4 100644 --- a/src/qml/presenter/ImageEditor.qml +++ b/src/qml/presenter/ImageEditor.qml @@ -8,6 +8,9 @@ import "./" as Presenter Item { id: root + property string type: "image" + property var image + GridLayout { id: mainLayout anchors.fill: parent @@ -27,7 +30,7 @@ Item { implicitWidth: 300 editable: true hoverEnabled: true - onCurrentTextChanged: showPassiveNotification(currentText) + /* onCurrentTextChanged: showPassiveNotification(currentText) */ } Controls.SpinBox { editable: true @@ -40,18 +43,6 @@ Item { 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 {} @@ -63,7 +54,7 @@ Item { } Controls.ToolButton { id: backgroundButton - text: "Background" + text: "Select Image" icon.name: "fileopen" hoverEnabled: true onClicked: backgroundType.open() @@ -83,15 +74,15 @@ Item { border.color: Kirigami.Theme.activeBackgroundColor border.width: 2 } - closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + 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: videoFileDialog.open() & backgroundType.close() + text: "Image" + icon.name: "emblem-images-symbolic" + onClicked: imageFileDialog.open() & backgroundType.close() } Controls.ToolButton { Layout.fillWidth: true @@ -121,11 +112,11 @@ Item { ColumnLayout { Controls.SplitView.fillHeight: true - Controls.SplitView.preferredWidth: 500 - Controls.SplitView.minimumWidth: 500 + Controls.SplitView.preferredWidth: 300 + Controls.SplitView.minimumWidth: 100 Controls.TextField { - id: songTitleField + id: imageTitleField Layout.preferredWidth: 300 Layout.fillWidth: true @@ -133,98 +124,46 @@ Item { Layout.rightMargin: 20 placeholderText: "Song Title..." - text: songTitle + text: image.title 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); + /* onEditingFinished: updateTitle(text); */ } - Controls.ScrollView { - id: songLyricsField - - Layout.preferredHeight: 3000 - Layout.fillWidth: true + Item { + id: empty 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 + spacing: 5 - Rectangle { - id: slideBar - color: Kirigami.Theme.highlightColor - - Layout.preferredWidth: 500 - Layout.preferredHeight: songTitleField.height - Layout.rightMargin: 20 - Layout.leftMargin: 20 + Item { + id: topEmpty + Layout.fillHeight: true } - 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 + Image { + id: imagePreview + Layout.preferredWidth: 600 + Layout.preferredHeight: Layout.preferredWidth / 16 * 9 + Layout.alignment: Qt.AlignCenter + fillMode: Image.PreserveAspectFit + source: image.filePath + } + Item { + id: botEmpty + Layout.fillHeight: true } } + } } + function changeImage(image) { + root.image = image; + print(image.filePath.toString()); } - Timer { - id: editorTimer - interval: 1000 - repeat: true - running: false - onTriggered: updateLyrics(lyricsEditor.text) - } } diff --git a/src/qml/presenter/LeftDock.qml b/src/qml/presenter/LeftDock.qml index 7b06297..00981fe 100644 --- a/src/qml/presenter/LeftDock.qml +++ b/src/qml/presenter/LeftDock.qml @@ -3,8 +3,9 @@ import QtQuick.Dialogs 1.0 import QtQuick.Controls 2.0 as Controls import QtQuick.Window 2.13 import QtQuick.Layouts 1.2 +import QtQml.Models 2.12 import QtMultimedia 5.15 -import QtAudioEngine 1.15 +/* import QtAudioEngine 1.15 */ import org.kde.kirigami 2.13 as Kirigami import "./" as Presenter @@ -34,6 +35,7 @@ ColumnLayout { Layout.fillHeight: true Layout.fillWidth: true onDropped: { + print("DROPPED AT END"); appendItem(dragItemTitle, dragItemType, dragItemBackground, @@ -46,13 +48,14 @@ ColumnLayout { ListView { id: serviceItemList anchors.fill: parent - model: serviceItemModel - delegate: Kirigami.DelegateRecycler { - width: serviceItemList.width - sourceComponent: itemDelegate - } + /* model: serviceItemModel */ + /* delegate: Kirigami.DelegateRecycler { */ + /* width: serviceItemList.width */ + /* sourceComponent: itemDelegate */ + /* } */ clip: true spacing: 3 + property int dragItemIndex addDisplaced: Transition { NumberAnimation {properties: "x, y"; duration: 100} @@ -73,67 +76,181 @@ ColumnLayout { NumberAnimation {properties: "x, y"; duration: 100} } - Component { - id: itemDelegate - Item { - id: serviceItem + model: DelegateModel { + id: visualModel + model: serviceItemModel + delegate: DropArea { + id: serviceDrop implicitWidth: serviceItemList.width height: 50 + onEntered: (drag) => { + /* dropPlacement(drag); */ + const from = (drag.source as visServiceItem) + visualModel.items.move(dragItemIndex, index); + } + onDropped: (drag) => { + print("DROPPED IN ITEM AREA: " + drag.keys); + print(dragItemIndex + " " + index); + const hlIndex = serviceItemList.currentIndex; + if (drag.keys === ["library"]) { + addItem(index, + dragItemTitle, + dragItemType, + dragItemBackground, + dragItemBackgroundType, + dragItemText, + dragItemIndex); + } else if (drag.keys === ["serviceitem"]) { + moveRequested(dragItemIndex, index); + if (hlIndex === dragItemIndex) + serviceItemList.currentIndex = index; + else if (hlIndex === index) + serviceItemList.currentIndex = index + 1; + } + } + keys: ["library","serviceitem"] + Kirigami.BasicListItem { - anchors.fill: parent + id: visServiceItem + width: serviceDrop.width + height: serviceDrop.height + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } label: name subtitle: type - hoverEnabled: true + hoverEnabled: false supportsMouseEvents: false backgroundColor: { - if (parent.ListView.isCurrentItem) { + if (serviceItemList.currentIndex === index || + mouseHandler.containsMouse) Kirigami.Theme.highlightColor; - /* } else if (serviceDrop.constainsDrag){ */ - /* Kirigami.Theme.hoverColor; */ - } else if (mouseHandler.containsMouse){ - Kirigami.Theme.highlightColor; - } else { - Kirigami.Theme.backgroundColor; - } + else + Kirigami.Theme.backgroundColor; } textColor: { - if (parent.ListView.isCurrentItem || mouseHandler.containsMouse) + if (serviceItemList.currentIndex === index || + mouseHandler.containsMouse) activeTextColor; else Kirigami.Theme.textColor; } - } - Presenter.DragHandle { - id: mouseHandler - anchors.fill: parent - listItem: serviceItem - listView: serviceItemList - onMoveRequested: serviceItemModel.move(oldIndex, newIndex) - onClicked: { - serviceItemList.currentIndex = index; - /* showPassiveNotification(serviceItemList.currentIndex); */ - changeSlideBackground(background, backgroundType); - changeSlideText(text); - changeSlideType(type); + + states: [ + State { + when: mouseHandler.drag.active + ParentChange { + target: visServiceItem + parent: serviceItemList + } + + PropertyChanges { + target: visServiceItem + backgroundColor: Kirigami.Theme.backgroundColor + textColor: Kirigami.Theme.textColor + anchors.verticalCenter: undefined + anchors.horizontalCenter: undefined + } + } + ] + + /* Drag.dragType: Drag.Automatic */ + Drag.active: mouseHandler.drag.active + Drag.hotSpot.x: width / 2 + Drag.hotSpot.y: height / 2 + Drag.keys: ["serviceitem"] + + MouseArea { + id: mouseHandler + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton | Qt.RightButton + preventStealing: true + + drag { + target: visServiceItem + axis: Drag.YAxis + /* minimumY: root.y */ + /* maximumY: serviceItemList.height - serviceDrop.height */ + smoothed: false + } + + drag.onActiveChanged: { + if (mouseHandler.drag.active) { + dragItemIndex = index; + } + } + + /* onPositionChanged: { */ + /* if (!pressed) { */ + /* return; */ + /* } */ + /* mouseArea.arrangeItem(); */ + /* } */ + + onPressed: { + serviceItemList.interactive = false; + } + + onClicked: { + if (mouse.button === Qt.RightButton) + rightClickMenu.popup(); + else { + serviceItemList.currentIndex = index; + currentServiceItem = index; + changeServiceItem(index); + } + } + + onReleased: { + print("should drop"); + visServiceItem.Drag.drop(); + } } } - DropArea { - id: serviceDrop - anchors.fill: parent - onDropped: { - addItem(index, - dragItemTitle, - dragItemType, - dragItemText, - dragItemBackgroundType, - dragItemBackground, - dragItemIndex); + /* Presenter.DragHandle { */ + /* id: mouseHandler */ + /* anchors.fill: parent */ + /* listItem: serviceItem */ + /* listView: serviceItemList */ + /* onMoveRequested: { */ + /* print(oldIndex, newIndex); */ + /* serviceItemModel.move(oldIndex, newIndex); */ + /* } */ + /* onDropped: { */ + /* } */ + /* onClicked: { */ + /* serviceItemList.currentIndex = index; */ + /* currentServiceItem = index; */ + /* changeServiceItem(index); */ + /* } */ + /* onRightClicked: rightClickMenu.popup() */ + /* } */ + + + + Controls.Menu { + id: rightClickMenu + x: mouseHandler.mouseX + y: mouseHandler.mouseY + 10 + Kirigami.Action { + text: "delete" + onTriggered: serviceItemModel.removeItem(index) } - keys: ["library"] + } + + function moveRequested(oldIndex, newIndex) { + serviceItemModel.move(oldIndex, newIndex); + } + + function dropPlacement(drag) { + print(drag.y); } } } + Kirigami.WheelHandler { id: wheelHandler @@ -148,18 +265,23 @@ ColumnLayout { active: hovered || pressed } } - } + } + function addItem(index, name, type, background, backgroundType, text, itemID) { + const newtext = songsqlmodel.getLyricList(itemID); + print("adding: " + name + " of type " + type); serviceItemModel.insertItem(index, name, - type, text, background, - backgroundType) + type, background, + backgroundType, newtext); } function appendItem(name, type, background, backgroundType, text, itemID) { + print("adding: " + name + " of type " + type); let lyrics; - if (type == "song") { + if (type === "song") { + print(itemID); lyrics = songsqlmodel.getLyricList(itemID); print(lyrics); } diff --git a/src/qml/presenter/Library.qml b/src/qml/presenter/Library.qml index cb6b7b2..e7914d3 100644 --- a/src/qml/presenter/Library.qml +++ b/src/qml/presenter/Library.qml @@ -195,9 +195,14 @@ Item { target: songListItem x: x y: y + width: width + height: height + } + ParentChange { + target: videoListItem + parent: rootApp.overlay } } - } MouseArea { @@ -411,6 +416,12 @@ Item { target: videoListItem x: x y: y + width: width + height: height + } + ParentChange { + target: videoListItem + parent: rootApp.overlay } } @@ -474,12 +485,40 @@ Item { Layout.fillWidth: true Layout.alignment: Qt.AlignTop color: Kirigami.Theme.backgroundColor + opacity: 1.0 Controls.Label { + id: imageLabel anchors.centerIn: parent text: "Images" } + Controls.Label { + id: imageCount + anchors {left: imageLabel.right + verticalCenter: imageLabel.verticalCenter + leftMargin: 15} + text: imagesqlmodel.rowCount() + font.pixelSize: 15 + color: Kirigami.Theme.disabledTextColor + } + + Kirigami.Icon { + id: imageDrawerArrow + anchors {right: parent.right + verticalCenter: imageCount.verticalCenter + rightMargin: 10} + source: "arrow-down" + rotation: selectedLibrary == "images" ? 0 : 180 + + Behavior on rotation { + NumberAnimation { + easing.type: Easing.OutCubic + duration: 300 + } + } + } + MouseArea { anchors.fill: parent onClicked: { @@ -497,6 +536,9 @@ Item { Layout.preferredHeight: parent.height - 200 Layout.fillWidth: true Layout.alignment: Qt.AlignTop + model: imagesqlmodel + delegate: imageDelegate + clip: true state: "deselected" states: [ @@ -523,6 +565,118 @@ Item { duration: 300 } } + + Component { + id: imageDelegate + Item{ + implicitWidth: ListView.view.width + height: selectedLibrary == "images" ? 50 : 0 + Kirigami.BasicListItem { + id: imageListItem + + property bool rightMenu: false + + implicitWidth: imageLibraryList.width + height: selectedLibrary == "images" ? 50 : 0 + clip: true + label: title + /* subtitle: author */ + supportsMouseEvents: false + backgroundColor: { + if (parent.ListView.isCurrentItem) { + Kirigami.Theme.highlightColor; + } else if (imageDragHandler.containsMouse){ + Kirigami.Theme.highlightColor; + } else { + Kirigami.Theme.backgroundColor; + } + } + textColor: { + if (parent.ListView.isCurrentItem || imageDragHandler.containsMouse) + activeTextColor; + else + Kirigami.Theme.textColor; + } + + Behavior on height { + NumberAnimation { + easing.type: Easing.OutCubic + duration: 300 + } + } + Drag.active: imageDragHandler.drag.active + Drag.hotSpot.x: width / 2 + Drag.hotSpot.y: height / 2 + Drag.keys: [ "library" ] + + states: State { + name: "dragged" + when: imageListItem.Drag.active + PropertyChanges { + target: imageListItem + x: x + y: y + width: width + height: height + } + ParentChange { + target: imageListItem + parent: rootApp.overlay + } + } + + } + + MouseArea { + id: imageDragHandler + anchors.fill: parent + hoverEnabled: true + drag { + target: imageListItem + onActiveChanged: { + if (imageDragHandler.drag.active) { + dragItemTitle = title; + dragItemType = "image"; + dragItemText = ""; + dragItemBackgroundType = "image"; + dragItemBackground = filePath; + } else { + imageListItem.Drag.drop() + } + } + filterChildren: true + threshold: 10 + } + MouseArea { + id: imageClickHandler + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: { + if(mouse.button == Qt.RightButton) + rightClickImageMenu.popup() + else{ + imageLibraryList.currentIndex = index + const image = imagesqlmodel.getImage(imageLibraryList.currentIndex); + if (!editMode) + editMode = true; + editType = "image"; + editSwitch(image); + } + } + + } + } + Controls.Menu { + id: rightClickImageMenu + x: imageClickHandler.mouseX + y: imageClickHandler.mouseY + 10 + Kirigami.Action { + text: "delete" + onTriggered: imagesqlmodel.deleteImage(index) + } + } + } + } } Rectangle { @@ -666,7 +820,7 @@ Item { videoLibraryList.currentIndex = videosqlmodel.rowCount(); print(videosqlmodel.getVideo(videoLibraryList.currentIndex)); const video = videosqlmodel.getVideo(videoLibraryList.currentIndex); - showPassiveNotification("newest video: " + video); + showPassiveNotification("newest video: " + video.title); if (!editMode) editMode = true; editSwitch("video", video); diff --git a/src/qml/presenter/MainWindow.qml b/src/qml/presenter/MainWindow.qml index 938718d..cfa5f3a 100644 --- a/src/qml/presenter/MainWindow.qml +++ b/src/qml/presenter/MainWindow.qml @@ -12,6 +12,7 @@ Controls.Page { padding: 0 // properties passed around for the slides + property int currentServiceItem property url imageBackground: "" property url videoBackground: "" property int blurRadius: 0 @@ -65,17 +66,24 @@ Controls.Page { id: presentation anchors.fill: parent } + Presenter.SongEditor { id: songEditor visible: false anchors.fill: parent } - + Presenter.VideoEditor { id: videoEditor visible: false anchors.fill: parent } + + Presenter.ImageEditor { + id: imageEditor + visible: false + anchors.fill: parent + } } Presenter.Library { @@ -90,35 +98,7 @@ Controls.Page { Loader { id: presentLoader active: presenting - sourceComponent: presentWindowComp - } - - Component { - id: presentWindowComp - Window { - id: presentationWindow - title: "presentation-window" - height: maximumHeight - width: maximumWidth - screen: presentationScreen - flags: Qt.X11BypassWindowManagerHint - onClosing: presenting = false - - Component.onCompleted: { - presentationWindow.showFullScreen(); - print(screen.name); - } - - Presenter.Slide { - id: presentationSlide - anchors.fill: parent - imageSource: imageBackground - videoSource: videoBackground - text: "" - - Component.onCompleted: slideItem = presentationSlide - } - } + source: "PresentationWindow.qml" } SongSqlModel { @@ -129,52 +109,41 @@ Controls.Page { id: videosqlmodel } + ImageSqlModel { + id: imagesqlmodel + } + ServiceItemModel { id: serviceItemModel } - function changeSlideType(type) { - presentation.itemType = type; - if (slideItem) - slideItem.itemType = type; - } + function changeServiceItem(index) { + const item = serviceItemModel.getItem(index); + print("index grabbed: " + index); - function changeSlideText(text) { - presentation.text = text; - if (slideItem) - slideItem.text = text; - } - - function changeSlideBackground(background, type) { - showPassiveNotification("starting background change.."); - showPassiveNotification(background); - showPassiveNotification(type); - if (type == "image") { + presentation.stopVideo() + presentation.itemType = item.type; + print("Time to start changing"); + + if (item.backgroundType === "image") { presentation.vidbackground = ""; - presentation.imagebackground = background; - if (slideItem) { - slideItem.videoSource = ""; - slideItem.stopVideo(); - slideItem.imageSource = background; - } + presentation.imagebackground = item.background; } else { presentation.imagebackground = ""; - presentation.vidbackground = background; + presentation.vidbackground = item.background; presentation.loadVideo() - if (slideItem) { - slideItem.imageSource = ""; - slideItem.videoSource = background; - slideItem.loadVideo() - } } - } - function changeSlideNext() { - showPassiveNotification("next slide please") - } + print(item.text.length) + if (item.text.length === 0) { + presentation.text = [""]; + } + else + presentation.text = item.text; + presentation.textIndex = 0; + presentation.changeSlide(); - function changeSlidePrevious() { - showPassiveNotification("previous slide please") + print("Slide changed to: " + item.name); } function editSwitch(item) { @@ -184,24 +153,30 @@ Controls.Page { presentation.visible = false; videoEditor.visible = false; videoEditor.stop(); + imageEditor.visible = false; songEditor.visible = true; songEditor.changeSong(item); break; case "video" : presentation.visible = false; songEditor.visible = false; + imageEditor.visible = false; videoEditor.visible = true; videoEditor.changeVideo(item); break; case "image" : - mainPageArea.pop(Controls.StackView.Immediate); - mainPageArea.push(imageEditorComp, Controls.StackView.Immediate); + presentation.visible = false; + videoEditor.visible = false; videoEditor.stop(); + songEditor.visible = false; + imageEditor.visible = true; + imageEditor.changeImage(item); break; default: videoEditor.visible = false; videoEditor.stop(); songEditor.visible = false; + imageEditor.visible = false; presentation.visible = true; editMode = false; } @@ -209,6 +184,7 @@ Controls.Page { videoEditor.visible = false; videoEditor.stop(); songEditor.visible = false; + imageEditor.visible = false; presentation.visible = true; editMode = false; } diff --git a/src/qml/presenter/Presentation.qml b/src/qml/presenter/Presentation.qml index f1647bf..4bcc1c5 100644 --- a/src/qml/presenter/Presentation.qml +++ b/src/qml/presenter/Presentation.qml @@ -3,18 +3,21 @@ import QtQuick.Dialogs 1.0 import QtQuick.Controls 2.15 as Controls import QtQuick.Window 2.13 import QtQuick.Layouts 1.2 -import QtAudioEngine 1.15 +/* import QtAudioEngine 1.15 */ import org.kde.kirigami 2.13 as Kirigami import "./" as Presenter Item { id: root - property string text + property var text + property int textIndex: 0 property string itemType property url imagebackground property url vidbackground + Component.onCompleted: nextSlideAction() + GridLayout { anchors.fill: parent columns: 3 @@ -29,11 +32,18 @@ Item { anchors.fill: parent Controls.ToolButton { - text: "Grid" + text: "Solo" + icon.name: "viewimage" hoverEnabled: true } Controls.ToolButton { - text: "Solo" + text: "Grid" + icon.name: "view-app-grid-symbolic" + hoverEnabled: true + } + Controls.ToolButton { + text: "Details" + icon.name: "view-list-details" hoverEnabled: true } Controls.ToolSeparator {} @@ -45,13 +55,6 @@ Item { hoverEnabled: true onClicked: {} } - Controls.ToolButton { - id: backgroundButton - text: "Background" - icon.name: "fileopen" - hoverEnabled: true - onClicked: backgroundType.open() - } } } @@ -69,7 +72,7 @@ Item { Layout.alignment: Qt.AlignRight MouseArea { anchors.fill: parent - onPressed: changeSlidePrevious() + onPressed: previousSlideAction() cursorShape: Qt.PointingHandCursor } } @@ -81,7 +84,6 @@ Item { Layout.minimumWidth: 300 Layout.alignment: Qt.AlignCenter textSize: width / 15 - text: root.text itemType: root.itemType imageSource: imagebackground videoSource: vidbackground @@ -95,7 +97,7 @@ Item { Layout.alignment: Qt.AlignLeft MouseArea { anchors.fill: parent - onPressed: changeSlideNext() + onPressed: nextSlideAction() cursorShape: Qt.PointingHandCursor } } @@ -112,4 +114,83 @@ Item { function loadVideo() { previewSlide.loadVideo(); } + + function stopVideo() { + previewSlide.stopVideo() + } + + function nextSlideAction() { + print(textIndex); + if (itemType === "song") { + if (textIndex === 0) { + previewSlide.text = root.text[textIndex]; + print(root.text[textIndex]); + textIndex++; + } else if (textIndex < root.text.length) { + previewSlide.text = root.text[textIndex]; + print(root.text[textIndex]); + textIndex++; + } else { + print("Next slide time"); + textIndex = 0; + clearText(); + nextSlide(); + } + } else if (itemType === "video") { + clearText(); + nextSlide(); + } + else if (itemType === "image") { + clearText(); + nextSlide(); + } + } + + function nextSlide() { + changeServiceItem(currentServiceItem++); + print(slideItem); + } + + function previousSlideAction() { + print(textIndex); + if (itemType === "song") { + if (textIndex === 0) { + clearText(); + nextSlide(); + } else if (textIndex <= root.text.length) { + previewSlide.text = root.text[textIndex]; + print(root.text[textIndex]); + --textIndex; + } + } else if (itemType === "video") { + clearText(); + previousSlide(); + } + else if (itemType === "image") { + clearText(); + previousSlide(); + } + } + + function previousSlide() { + changeServiceItem(--currentServiceItem); + print(slideItem); + } + + function changeSlide() { + if (itemType === "song") { + previewSlide.text = root.text[textIndex]; + print(root.text[textIndex]); + textIndex++; + } else if (itemType === "video") { + clearText(); + } + else if (itemType === "image") { + clearText(); + } + } + + function clearText() { + previewSlide.text = ""; + } } diff --git a/src/qml/presenter/Slide.qml b/src/qml/presenter/Slide.qml index 099ee58..d2fe6dd 100644 --- a/src/qml/presenter/Slide.qml +++ b/src/qml/presenter/Slide.qml @@ -2,7 +2,7 @@ import QtQuick 2.13 import QtQuick.Controls 2.15 as Controls import QtQuick.Layouts 1.2 /* import QtMultimedia 5.15 */ -import QtAudioEngine 1.15 +/* import QtAudioEngine 1.15 */ import QtGraphicalEffects 1.15 import org.kde.kirigami 2.13 as Kirigami import "./" as Presenter @@ -15,13 +15,15 @@ Item { property bool editMode: false // These properties are for the slides visuals - property real textSize: 72 + property real textSize: 50 property bool dropShadow: false property url imageSource: imageBackground property url videoSource: videoBackground property string chosenFont: "Quicksand" - property string text: "This is demo text" + property var text: "This is demo text" property color backgroundColor + property var hTextAlignment: Text.AlignHCenter + property var vTextAlignment: Text.AlignVCenter //these properties are for giving video info to parents property int mpvPosition: mpv.position @@ -32,6 +34,9 @@ Item { property string itemType property bool preview: false + implicitWidth: 1920 + implicitHeight: 1080 + Rectangle { id: basePrColor anchors.fill: parent @@ -45,22 +50,24 @@ Item { enableAudio: !preview Component.onCompleted: mpvLoadingTimer.start() onFileLoaded: { - showPassiveNotification(videoSource + " has been loaded"); + /* showPassiveNotification(videoSource + " has been loaded"); */ if (itemType == "song") mpv.setProperty("loop", "inf"); - showPassiveNotification(mpv.getProperty("loop")); + else + mpv.setProperty("loop", "no"); + /* showPassiveNotification(mpv.getProperty("loop")); */ } MouseArea { id: playArea anchors.fill: parent enabled: editMode - onPressed: mpv.loadFile(videoSource.toString()); + onPressed: mpv.playPause(); cursorShape: preview ? Qt.ArrowCursor : Qt.BlankCursor } Controls.ProgressBar { - anchors.centerIn: parent + anchors.top: parent.bottom visible: editMode width: parent.width - 400 value: mpv.position @@ -71,7 +78,36 @@ Item { Timer { id: mpvLoadingTimer interval: 100 - onTriggered: mpv.loadFile(videoSource.toString()) + onTriggered: { + /* showPassiveNotification("YIPPEEE!") */ + mpv.loadFile(videoSource.toString()); + if (editMode) { + print("WHY AREN'T YOU PASUING!"); + pauseTimer.restart(); + } + blackTimer.restart(); + } + } + + Timer { + id: pauseTimer + interval: 200 + onTriggered: mpv.pause() + } + + Timer { + id: blackTimer + interval: 400 + onTriggered: { + black.visible = false; + } + } + + Rectangle { + id: black + color: "Black" + anchors.fill: parent + visible: false } Image { @@ -87,7 +123,7 @@ Item { FastBlur { id: imageBlue anchors.fill: parent - source: imageSource == "" ? mpv : backgroundImage + source: imageSource === "" ? mpv : backgroundImage radius: blurRadius Controls.Label { @@ -97,9 +133,10 @@ Item { /* minimumPointSize: 5 */ fontSizeMode: Text.Fit font.family: chosenFont + horizontalAlignment: hTextAlignment + verticalAlignment: vTextAlignment style: Text.Raised - anchors.centerIn: parent - /* width: parent.width */ + anchors.fill: parent clip: true layer.enabled: true @@ -123,6 +160,12 @@ Item { } function stopVideo() { - mpv.stop() + mpv.stop(); + black.visible = true; + showPassiveNotification("Black is: " + black.visible); + } + + function pauseVideo() { + mpv.pause(); } } diff --git a/src/qml/presenter/SlideEditor.qml b/src/qml/presenter/SlideEditor.qml index 028c104..277f61f 100644 --- a/src/qml/presenter/SlideEditor.qml +++ b/src/qml/presenter/SlideEditor.qml @@ -1,10 +1,10 @@ -import QtQuick 2.13 +import QtQuick 2.15 import QtQuick.Dialogs 1.0 import QtQuick.Controls 2.15 as Controls import QtQuick.Window 2.13 import QtQuick.Layouts 1.2 import QtMultimedia 5.15 -import QtAudioEngine 1.15 +/* import QtAudioEngine 1.15 */ import org.kde.kirigami 2.13 as Kirigami import "./" as Presenter @@ -13,14 +13,86 @@ Item { property string imageBackground property string videoBackground + property var hTextAlignment + property var vTextAlignment + property string font + property real fontSize - Presenter.Slide { - id: representation + property ListModel songs: songModel + + ListView { + id: slideList anchors.fill: parent - textSize: width / 15 - editMode: true - imageSource: imageBackground - videoSource: videoBackground - preview: true + model: songModel + clip: true + cacheBuffer: 900 + reuseItems: true + spacing: Kirigami.Units.gridUnit + flickDeceleration: 4000 + /* boundsMovement: Flickable.StopAtBounds */ + synchronousDrag: true + delegate: Presenter.Slide { + id: representation + editMode: true + imageSource: root.imageBackground + videoSource: root.videoBackground + hTextAlignment: root.hTextAlignment + vTextAlignment: root.vTextAlignment + chosenFont: root.font + textSize: root.fontSize + preview: true + text: verse + implicitWidth: slideList.width + implicitHeight: width * 9 / 16 + } + } + + Component.onCompleted: { + } + + ListModel { + id: songModel + } + + function appendVerse(verse) { + print(verse); + songModel.append({"verse": verse}) + } + + /* function loadVideo() { */ + /* representation.loadVideo(); */ + /* } */ + + function updateHAlignment(alignment) { + switch (alignment) { + case "left" : + root.hTextAlignment = Text.AlignLeft; + break; + case "center" : + root.hTextAlignment = Text.AlignHCenter; + break; + case "right" : + root.hTextAlignment = Text.AlignRight; + break; + case "justify" : + root.hTextAlignment = Text.AlignJustify; + break; + } + } + + function updateVAlignment(alignment) { + switch (alignment) { + case "top" : + root.vTextAlignment = Text.AlignTop; + break; + case "center" : + root.vTextAlignment = Text.AlignVCenter; + break; + case "bottom" : + root.vTextAlignment = Text.AlignBottom; + break; + } + } + } diff --git a/src/qml/presenter/SongEditor.qml b/src/qml/presenter/SongEditor.qml index 8248574..cbedf47 100644 --- a/src/qml/presenter/SongEditor.qml +++ b/src/qml/presenter/SongEditor.qml @@ -1,5 +1,6 @@ import QtQuick 2.13 import QtQuick.Controls 2.15 as Controls +import Qt.labs.platform 1.1 as Labs import QtQuick.Dialogs 1.3 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.13 as Kirigami @@ -9,14 +10,7 @@ Item { id: root property int songIndex - property string songTitle - property string songLyrics - property string songAuthor - property string songCcli - property string songAudio - property string songVorder - property string songBackground - property string songBackgroundType + property var song GridLayout { id: mainLayout @@ -33,34 +27,52 @@ Item { anchors.fill: parent Controls.ComboBox { + id: fontBox model: Qt.fontFamilies() implicitWidth: 300 editable: true hoverEnabled: true - onCurrentTextChanged: showPassiveNotification(currentText) + flat: true + onActivated: updateFont(currentText) } Controls.SpinBox { + id: fontSizeBox editable: true from: 5 to: 72 hoverEnabled: true + onValueModified: updateFontSize(value) } Controls.ComboBox { + id: hAlignmentBox model: ["Left", "Center", "Right", "Justify"] implicitWidth: 100 hoverEnabled: true + flat: true + onActivated: updateHorizontalTextAlignment(currentText.toLowerCase()); + } + Controls.ComboBox { + id: vAlignmentBox + model: ["Top", "Center", "Bottom"] + implicitWidth: 100 + hoverEnabled: true + flat: true + onActivated: updateVerticalTextAlignment(currentText.toLowerCase()); } 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 } @@ -143,7 +155,7 @@ Item { Layout.rightMargin: 20 placeholderText: "Song Title..." - text: songTitle + text: song.title padding: 10 onEditingFinished: updateTitle(text); } @@ -156,7 +168,7 @@ Item { Layout.rightMargin: 20 placeholderText: "verse order..." - text: songVorder + text: song.vorder padding: 10 onEditingFinished: updateVerseOrder(text); } @@ -176,7 +188,7 @@ Item { width: parent.width placeholderText: "Put lyrics here..." persistentSelection: true - text: songLyrics + text: song.lyrics textFormat: TextEdit.PlainText padding: 10 onEditingFinished: { @@ -195,7 +207,7 @@ Item { Layout.rightMargin: 20 placeholderText: "Author..." - text: songAuthor + text: song.author padding: 10 onEditingFinished: updateAuthor(text) } @@ -210,8 +222,9 @@ Item { id: slideEditor Layout.preferredWidth: 500 Layout.fillWidth: true - Layout.preferredHeight: slideEditor.width / 16 * 9 + Layout.fillHeight: true Layout.bottomMargin: 30 + Layout.topMargin: 30 Layout.rightMargin: 20 Layout.leftMargin: 20 } @@ -262,24 +275,25 @@ Item { } function changeSong(index) { - const song = songsqlmodel.getSong(index); + const s = songsqlmodel.getSong(index); + song = s; songIndex = index; - songTitle = song[0]; - songLyrics = song[1]; - songAuthor = song[2]; - songCcli = song[3]; - songAudio = song[4]; - songVorder = song[5]; - songBackground = song[6]; - songBackgroundType = song[7]; - if (songBackgroundType == "image") { + + if (song.backgroundType == "image") { slideEditor.videoBackground = ""; - slideEditor.imageBackground = songBackground; + slideEditor.imageBackground = song.background; } else { slideEditor.imageBackground = ""; - slideEditor.videoBackground = songBackground; + slideEditor.videoBackground = song.background; + /* slideEditor.loadVideo(); */ } - print(song); + + changeSlideHAlignment(song.horizontalTextAlignment); + changeSlideVAlignment(song.verticalTextAlignment); + changeSlideFont(song.font, true); + changeSlideFontSize(song.fontSize, true) + changeSlideText(songIndex); + print(s.title); } function updateLyrics(lyrics) { @@ -312,4 +326,83 @@ Item { songsqlmodel.updateBackgroundType(songIndex, backgroundType); print("changed background"); } + + + function updateHorizontalTextAlignment(textAlignment) { + changeSlideHAlignment(textAlignment); + songsqlmodel.updateHorizontalTextAlignment(songIndex, textAlignment); + } + + function updateVerticalTextAlignment(textAlignment) { + changeSlideVAlignment(textAlignment); + songsqlmodel.updateVerticalTextAlignment(songIndex, textAlignment) + } + + function updateFont(font) { + changeSlideFont(font, false); + songsqlmodel.updateFont(songIndex, font); + } + + function updateFontSize(fontSize) { + changeSlideFontSize(fontSize, false); + songsqlmodel.updateFontSize(songIndex, fontSize); + } + + function changeSlideHAlignment(alignment) { + switch (alignment) { + case "left" : + hAlignmentBox.currentIndex = 0; + slideEditor.hTextAlignment = Text.AlignLeft; + break; + case "center" : + hAlignmentBox.currentIndex = 1; + slideEditor.hTextAlignment = Text.AlignHCenter; + break; + case "right" : + hAlignmentBox.currentIndex = 2; + slideEditor.hTextAlignment = Text.AlignRight; + break; + case "justify" : + hAlignmentBox.currentIndex = 3; + slideEditor.hTextAlignment = Text.AlignJustify; + break; + } + } + + function changeSlideVAlignment(alignment) { + switch (alignment) { + case "top" : + vAlignmentBox.currentIndex = 0; + slideEditor.vTextAlignment = Text.AlignTop; + break; + case "center" : + vAlignmentBox.currentIndex = 1; + slideEditor.vTextAlignment = Text.AlignVCenter; + break; + case "bottom" : + vAlignmentBox.currentIndex = 2; + slideEditor.vTextAlignment = Text.AlignBottom; + break; + } + } + + function changeSlideFont(font, updateBox) { + const fontIndex = fontBox.find(font); + if (updateBox) + fontBox.currentIndex = fontIndex; + slideEditor.font = font; + } + + function changeSlideFontSize(fontSize, updateBox) { + if (updateBox) + fontSizeBox.value = fontSize; + slideEditor.fontSize = fontSize; + } + + function changeSlideText(id) { + const verses = songsqlmodel.getLyricList(id); + print("Here are the verses: " + verses); + slideEditor.songs.clear() + verses.forEach(slideEditor.appendVerse); + } } diff --git a/src/qml/presenter/VideoEditor.qml b/src/qml/presenter/VideoEditor.qml index 47b3d63..a3623ba 100644 --- a/src/qml/presenter/VideoEditor.qml +++ b/src/qml/presenter/VideoEditor.qml @@ -32,7 +32,7 @@ Item { implicitWidth: 300 editable: true hoverEnabled: true - onCurrentTextChanged: showPassiveNotification(currentText) + /* onCurrentTextChanged: showPassiveNotification(currentText) */ } Controls.SpinBox { editable: true @@ -138,7 +138,7 @@ Item { Layout.rightMargin: 20 placeholderText: "Song Title..." - text: video[0] + text: video.title padding: 10 /* onEditingFinished: updateTitle(text); */ } @@ -170,7 +170,7 @@ Item { Component.onCompleted: mpvLoadingTimer.start() onPositionChanged: videoSlider.value = position onFileLoaded: { - showPassiveNotification(video[0] + " has been loaded"); + showPassiveNotification(video.title + " has been loaded"); videoPreview.pause(); } } @@ -220,7 +220,7 @@ Item { id: mpvLoadingTimer interval: 100 onTriggered: { - videoPreview.loadFile(video[1].toString()); + videoPreview.loadFile(video.filePath.toString()); /* showPassiveNotification(video[0]); */ } } diff --git a/src/resources.qrc b/src/resources.qrc index 5fc74df..30066e3 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -17,5 +17,6 @@ qml/presenter/Presentation.qml qml/presenter/Settings.qml assets/parallel.jpg + assets/black.jpg diff --git a/src/serviceitemmodel.cpp b/src/serviceitemmodel.cpp index 3d1d196..5118060 100644 --- a/src/serviceitemmodel.cpp +++ b/src/serviceitemmodel.cpp @@ -1,6 +1,7 @@ #include "serviceitemmodel.h" #include "serviceitem.h" #include +#include #include #include #include @@ -115,13 +116,17 @@ Qt::ItemFlags ServiceItemModel::flags(const QModelIndex &index) const { void ServiceItemModel::addItem(ServiceItem *item) { const int index = m_items.size(); + qDebug() << index; + // foreach (item, m_items) { + // qDebug() << item; + // } beginInsertRows(QModelIndex(), index, index); m_items.append(item); endInsertRows(); } void ServiceItemModel::insertItem(const int &index, ServiceItem *item) { - beginInsertRows(this->index(index), index, index); + beginInsertRows(this->index(index).parent(), index, index); m_items.insert(index, item); endInsertRows(); qDebug() << "Success"; @@ -164,7 +169,7 @@ void ServiceItemModel::insertItem(const int &index, const QString &name, const Q const QStringList &text) { ServiceItem *item = new ServiceItem(name, type, background, backgroundType, text); insertItem(index, item); - qDebug() << name << type << background; + qDebug() << name << type << background << text; } void ServiceItemModel::removeItem(int index) { @@ -174,17 +179,36 @@ void ServiceItemModel::removeItem(int index) { } bool ServiceItemModel::move(int sourceIndex, int destIndex) { - qDebug() << "starting move"; + qDebug() << index(sourceIndex).row(); + qDebug() << index(destIndex).row(); + // beginResetModel(); QModelIndex parent = index(sourceIndex).parent(); - bool begsuc = beginMoveRows(parent, sourceIndex, sourceIndex, parent, destIndex); - qDebug() << begsuc; - if (!begsuc) { - qDebug() << "Failed to start moving rows"; - m_items.move(sourceIndex, destIndex); - return false; + if (sourceIndex >= 0 && sourceIndex != destIndex && destIndex >= 0 && destIndex < rowCount() && sourceIndex < rowCount()) { + qDebug() << "starting move of: " << "source: " << sourceIndex << "dest: " << destIndex; + bool begsuc = beginMoveRows(QModelIndex(), sourceIndex, sourceIndex, QModelIndex(), destIndex); + if (begsuc) + m_items.move(sourceIndex, destIndex); + endMoveRows(); } - // bool success = moveRow(index(sourceIndex).parent(), sourceIndex, index(destIndex).parent(), destIndex); - endMoveRows(); + // endResetModel(); + // emit dataChanged(index(sourceIndex), QModelIndex()); // qDebug() << success; return true; } + +QVariantMap ServiceItemModel::getItem(int index) const { + QVariantMap data; + const QModelIndex idx = this->index(index,0); + // qDebug() << idx; + if( !idx.isValid() ) + return data; + const QHash rn = roleNames(); + // qDebug() << rn; + QHashIterator it(rn); + while (it.hasNext()) { + it.next(); + qDebug() << it.key() << ":" << it.value(); + data[it.value()] = idx.data(it.key()); + } + return data; +} diff --git a/src/serviceitemmodel.h b/src/serviceitemmodel.h index 7ec670d..4536b2c 100644 --- a/src/serviceitemmodel.h +++ b/src/serviceitemmodel.h @@ -56,6 +56,7 @@ public: const QString &backgroundType, const QStringList &text); Q_INVOKABLE void removeItem(int index); Q_INVOKABLE bool move(int sourceIndex, int destIndex); + Q_INVOKABLE QVariantMap getItem(int index) const; private: QList m_items; diff --git a/src/songsqlmodel.cpp b/src/songsqlmodel.cpp index 218fa31..fdb1080 100644 --- a/src/songsqlmodel.cpp +++ b/src/songsqlmodel.cpp @@ -34,27 +34,39 @@ static void createTable() " 'vorder' TEXT," " 'background' TEXT," " 'backgroundType' TEXT," + " 'horizontalTextAlignment' TEXT," + " 'verticalTextAlignment' TEXT," + " 'font' TEXT," + " 'fontSize' INTEGER," " PRIMARY KEY(id))")) { qFatal("Failed to query database: %s", qPrintable(query.lastError().text())); } - qDebug() << query.lastQuery(); - qDebug() << "inserting into songs"; + // qDebug() << query.lastQuery(); + // qDebug() << "inserting into songs"; - query.exec("INSERT INTO songs (title, lyrics, author, ccli, audio, vorder, background, backgroundType) VALUES ('10,000 Reasons', '10,000 reasons for my heart to sing', 'Matt Redman', '13470183', '', '', '', '')"); - qDebug() << query.lastQuery(); - query.exec("INSERT INTO songs (title, lyrics, author, ccli, audio, vorder, background, backgroundType) VALUES ('River', 'Im going down to the river', 'Jordan Feliz', '13470183', '', '', '', '')"); - query.exec("INSERT INTO songs (title, lyrics, author, ccli, audio, vorder, background, backgroundType) VALUES ('Marvelous Light', 'Into marvelous " - "light Im running', 'Chris Tomlin', '13470183', '', '', '', '')"); + query.exec( + "INSERT INTO songs (title, lyrics, author, ccli, audio, vorder, " + "background, backgroundType, horizontalTextAlignment, verticalTextAlignment, font, fontSize) VALUES ('10,000 Reasons', '10,000 reasons " + "for my heart to sing', 'Matt Redman', '13470183', '', '', '', '', 'center', 'center', '', '')"); + // qDebug() << query.lastQuery(); + query.exec("INSERT INTO songs (title, lyrics, author, ccli, audio, vorder, " + "background, backgroundType, horizontalTextAlignment, verticalTextAlignment, font, fontSize) VALUES ('River', 'Im going down to " + "the river', 'Jordan Feliz', '13470183', '', '', '', '', 'center', 'center', '', '')"); + query.exec( + "INSERT INTO songs (title, lyrics, author, ccli, audio, vorder, " + "background, backgroundType, horizontalTextAlignment, verticalTextAlignment, font, fontSize) VALUES ('Marvelous Light', 'Into marvelous " + "light Im running', 'Chris Tomlin', '13470183', '', '', '', '', 'center', 'center', '', '')"); + // qDebug() << query.lastQuery(); query.exec("select * from songs"); - qDebug() << query.lastQuery(); + // qDebug() << query.lastQuery(); } SongSqlModel::SongSqlModel(QObject *parent) : QSqlTableModel(parent) { - qDebug() << "creating table"; + // qDebug() << "creating table"; createTable(); setTable(songsTableName); setEditStrategy(QSqlTableModel::OnManualSubmit); @@ -85,6 +97,10 @@ QHash SongSqlModel::roleNames() const names[Qt::UserRole + 6] = "vorder"; names[Qt::UserRole + 7] = "background"; names[Qt::UserRole + 8] = "backgroundType"; + names[Qt::UserRole + 9] = "horizontalTextAlignment"; + names[Qt::UserRole + 10] = "verticalTextAlignment"; + names[Qt::UserRole + 11] = "font"; + names[Qt::UserRole + 12] = "fontSize"; return names; } @@ -113,25 +129,24 @@ void SongSqlModel::deleteSong(const int &row) { submitAll(); } -QVariantList SongSqlModel::getSong(const int &row) { - QSqlRecord recordData = record(row); - if (recordData.isEmpty()) { - qDebug() << "this is not a song"; - QVariantList empty; - return empty; +QVariantMap SongSqlModel::getSong(const int &row) { + // this whole function returns all data in the song + // regardless of it's length. When new things are added + // it will still work without refactoring. + QVariantMap data; + const QModelIndex idx = this->index(row,0); + // qDebug() << idx; + if( !idx.isValid() ) + return data; + const QHash rn = roleNames(); + // qDebug() << rn; + QHashIterator it(rn); + while (it.hasNext()) { + it.next(); + qDebug() << it.key() << ":" << it.value(); + data[it.value()] = idx.data(it.key()); } - - QVariantList song; - song.append(recordData.value("title")); - song.append(recordData.value("lyrics")); - song.append(recordData.value("author")); - song.append(recordData.value("ccli")); - song.append(recordData.value("audio")); - song.append(recordData.value("vorder")); - song.append(recordData.value("background")); - song.append(recordData.value("backgroundType")); - - return song; + return data; } QStringList SongSqlModel::getLyricList(const int &row) { @@ -162,8 +177,11 @@ QStringList SongSqlModel::getLyricList(const int &row) { QString line; QMap verses; + //TODO make sure to split empty line in verse into two slides + // This first function pulls out each verse into our verses map foreach (line, rawLyrics) { + qDebug() << line; if (firstItem) { if (keywords.contains(line)) { recordVerse = true; @@ -187,6 +205,16 @@ QStringList SongSqlModel::getLyricList(const int &row) { } qDebug() << verses; + // let's check to see if there is a verse order, if not return the list given + if (vorder.first().isEmpty()) { + qDebug() << "NO VORDER"; + foreach (verse, verses) { + qDebug() << verse; + lyrics.append(verse); + } + qDebug() << lyrics; + return lyrics; + } // this function appends the verse that matches the verse order from the map foreach (const QString &vstr, vorder) { foreach (line, rawLyrics) { @@ -394,3 +422,107 @@ void SongSqlModel::updateBackgroundType(const int &row, const QString &backgroun submitAll(); emit backgroundTypeChanged(); } + +QString SongSqlModel::horizontalTextAlignment() const { + return m_horizontalTextAlignment; +} + +void SongSqlModel::setHorizontalTextAlignment(const QString &horizontalTextAlignment) { + if (horizontalTextAlignment == m_horizontalTextAlignment) + return; + + m_horizontalTextAlignment = horizontalTextAlignment; + + select(); + emit horizontalTextAlignmentChanged(); +} + +// This function is for updating the lyrics from outside the delegate +void SongSqlModel::updateHorizontalTextAlignment(const int &row, const QString &horizontalTextAlignment) { + qDebug() << "Row is " << row; + QSqlRecord rowdata = record(row); + qDebug() << rowdata; + rowdata.setValue("horizontalTextAlignment", horizontalTextAlignment); + setRecord(row, rowdata); + qDebug() << rowdata; + submitAll(); + emit horizontalTextAlignmentChanged(); +} + +QString SongSqlModel::verticalTextAlignment() const { + return m_verticalTextAlignment; +} + +void SongSqlModel::setVerticalTextAlignment(const QString &verticalTextAlignment) { + if (verticalTextAlignment == m_verticalTextAlignment) + return; + + m_verticalTextAlignment = verticalTextAlignment; + + select(); + emit verticalTextAlignmentChanged(); +} + +// This function is for updating the lyrics from outside the delegate +void SongSqlModel::updateVerticalTextAlignment(const int &row, const QString &verticalTextAlignment) { + qDebug() << "Row is " << row; + QSqlRecord rowdata = record(row); + qDebug() << rowdata; + rowdata.setValue("verticalTextAlignment", verticalTextAlignment); + setRecord(row, rowdata); + qDebug() << rowdata; + submitAll(); + emit verticalTextAlignmentChanged(); +} + +QString SongSqlModel::font() const { + return m_font; +} + +void SongSqlModel::setFont(const QString &font) { + if (font == m_font) + return; + + m_font = font; + + select(); + emit fontChanged(); +} + +// This function is for updating the lyrics from outside the delegate +void SongSqlModel::updateFont(const int &row, const QString &font) { + qDebug() << "Row is " << row; + QSqlRecord rowdata = record(row); + qDebug() << rowdata; + rowdata.setValue("font", font); + setRecord(row, rowdata); + qDebug() << rowdata; + submitAll(); + emit fontChanged(); +} + +int SongSqlModel::fontSize() const { + return m_fontSize; +} + +void SongSqlModel::setFontSize(const int &fontSize) { + if (fontSize == m_fontSize) + return; + + m_fontSize = fontSize; + + select(); + emit fontSizeChanged(); +} + +// This function is for updating the lyrics from outside the delegate +void SongSqlModel::updateFontSize(const int &row, const int &fontSize) { + qDebug() << "Row is " << row; + QSqlRecord rowdata = record(row); + qDebug() << rowdata; + rowdata.setValue("fontSize", fontSize); + setRecord(row, rowdata); + qDebug() << rowdata; + submitAll(); + emit fontSizeChanged(); +} diff --git a/src/songsqlmodel.h b/src/songsqlmodel.h index 09045c1..1d2b60f 100644 --- a/src/songsqlmodel.h +++ b/src/songsqlmodel.h @@ -18,6 +18,10 @@ class SongSqlModel : public QSqlTableModel Q_PROPERTY(QString vorder READ vorder WRITE setVerseOrder NOTIFY vorderChanged) Q_PROPERTY(QString background READ background WRITE setBackground NOTIFY backgroundChanged) Q_PROPERTY(QString backgroundType READ backgroundType WRITE setBackgroundType NOTIFY backgroundTypeChanged) + Q_PROPERTY(QString horizontalTextAlignment READ horizontalTextAlignment WRITE setHorizontalTextAlignment NOTIFY horizontalTextAlignmentChanged) + Q_PROPERTY(QString verticalTextAlignment READ verticalTextAlignment WRITE setVerticalTextAlignment NOTIFY verticalTextAlignmentChanged) + Q_PROPERTY(QString font READ font WRITE setFont NOTIFY fontChanged) + Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged) QML_ELEMENT public: @@ -32,6 +36,10 @@ public: QString vorder() const; QString background() const; QString backgroundType() const; + QString horizontalTextAlignment() const; + QString verticalTextAlignment() const; + QString font() const; + int fontSize() const; void setTitle(const QString &title); void setLyrics(const QString &lyrics); @@ -41,6 +49,10 @@ public: void setVerseOrder(const QString &vorder); void setBackground(const QString &background); void setBackgroundType(const QString &backgroundType); + void setHorizontalTextAlignment(const QString &horizontalTextAlignment); + void setVerticalTextAlignment(const QString &verticalTextAlignment); + void setFont(const QString &font); + void setFontSize(const int &fontSize); Q_INVOKABLE void updateTitle(const int &row, const QString &title); Q_INVOKABLE void updateLyrics(const int &row, const QString &lyrics); @@ -50,10 +62,14 @@ public: Q_INVOKABLE void updateVerseOrder(const int &row, const QString &vorder); Q_INVOKABLE void updateBackground(const int &row, const QString &background); Q_INVOKABLE void updateBackgroundType(const int &row, const QString &backgroundType); + Q_INVOKABLE void updateHorizontalTextAlignment(const int &row, const QString &horizontalTextAlignment); + Q_INVOKABLE void updateVerticalTextAlignment(const int &row, const QString &horizontalTextAlignment); + Q_INVOKABLE void updateFont(const int &row, const QString &font); + Q_INVOKABLE void updateFontSize(const int &row, const int &fontSize); Q_INVOKABLE void newSong(); Q_INVOKABLE void deleteSong(const int &row); - Q_INVOKABLE QVariantList getSong(const int &row); + Q_INVOKABLE QVariantMap getSong(const int &row); Q_INVOKABLE QStringList getLyricList(const int &row); QVariant data(const QModelIndex &index, int role) const override; @@ -68,6 +84,10 @@ signals: void vorderChanged(); void backgroundChanged(); void backgroundTypeChanged(); + void horizontalTextAlignmentChanged(); + void verticalTextAlignmentChanged(); + void fontChanged(); + void fontSizeChanged(); private: int m_id; @@ -79,6 +99,10 @@ private: QString m_vorder; QString m_background; QString m_backgroundType; + QString m_horizontalTextAlignment; + QString m_verticalTextAlignment; + QString m_font; + int m_fontSize; }; #endif //SONGSQLMODEL_H diff --git a/src/videosqlmodel.cpp b/src/videosqlmodel.cpp index a503af1..7d4a99f 100644 --- a/src/videosqlmodel.cpp +++ b/src/videosqlmodel.cpp @@ -156,12 +156,28 @@ void VideoSqlModel::updateFilePath(const int &row, const QUrl &filePath) { emit filePathChanged(); } -QVariantList VideoSqlModel::getVideo(const int &row) { - qDebug() << "Row we are getting is " << row; - QVariantList video; - QSqlRecord rec = record(row); - qDebug() << rec.value("title"); - video.append(rec.value("title")); - video.append(rec.value("filePath")); - return video; +QVariantMap VideoSqlModel::getVideo(const int &row) { + // qDebug() << "Row we are getting is " << row; + // QVariantList video; + // QSqlRecord rec = record(row); + // qDebug() << rec.value("title"); + // video.append(rec.value("title")); + // video.append(rec.value("filePath")); + // return video; + + QVariantMap data; + const QModelIndex idx = this->index(row,0); + // qDebug() << idx; + if( !idx.isValid() ) + return data; + const QHash rn = roleNames(); + // qDebug() << rn; + QHashIterator it(rn); + while (it.hasNext()) { + it.next(); + qDebug() << it.key() << ":" << it.value(); + data[it.value()] = idx.data(it.key()); + } + return data; + } diff --git a/src/videosqlmodel.h b/src/videosqlmodel.h index be269a7..07430d4 100644 --- a/src/videosqlmodel.h +++ b/src/videosqlmodel.h @@ -31,7 +31,7 @@ public: Q_INVOKABLE void newVideo(const QUrl &filePath); Q_INVOKABLE void deleteVideo(const int &row); - Q_INVOKABLE QVariantList getVideo(const int &row); + Q_INVOKABLE QVariantMap getVideo(const int &row); QVariant data(const QModelIndex &index, int role) const override; QHash roleNames() const override; @@ -46,4 +46,4 @@ private: QUrl m_filePath; }; -#endif //SONGSQLMODEL_H +#endif //VIDEOSQLMODEL_H