Merge remote-tracking branch 'origin/master'

This commit is contained in:
Chris Cochrun 2022-05-24 10:11:27 -05:00
commit f4a41cd4c6
28 changed files with 1248 additions and 349 deletions

View file

@ -13,7 +13,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) 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(KDEInstallDirs)
include(KDECMakeSettings) 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(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(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 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)

View file

@ -7,12 +7,12 @@ This is an attempt at building a church presentation application in Qt/QML. QML
** Features (planned are in parentheses) ** Features (planned are in parentheses)
- Presents songs lyrics with image and video backgrounds - Presents songs lyrics with image and video backgrounds
- Use MPV as a rendering system for videos - Use MPV as a rendering system for videos
- Simple song creation with a powerful text parser - Almost fully finished
- (Present Slides) - (Present Slides)
- (Custom slide builder) - (Custom slide builder)
- (Simple song creation with a powerful text parser)
** MPV ** 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 * Build and Run
First get the source code First get the source code
@ -33,4 +33,4 @@ Then run.
#+END_SRC #+END_SRC
* Contact Me * 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]]

100
TODO.org
View file

@ -1,11 +1,105 @@
#+TITLE: Todo List #+TITLE: Todo List
:PROPERTIES:
:CATEGORY: dev
:END:
* Inbox * 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 Model
- [X] Create a class that we'll make a list of in the 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 ** DONE Parse Lyrics to create a list of strings for slides
SCHEDULED: <2022-03-23 Wed 10:00> 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 {]]

41
flake.lock generated Normal file
View file

@ -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
}

14
flake.nix Normal file
View file

@ -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; };
}
);
}

42
shell.nix Normal file
View file

@ -0,0 +1,42 @@
{ pkgs ? <nixpkgs> { } }:
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
'';
}

View file

@ -7,6 +7,7 @@ target_sources(presenter
serviceitemmodel.cpp serviceitemmodel.h serviceitemmodel.cpp serviceitemmodel.h
serviceitem.cpp serviceitem.h serviceitem.cpp serviceitem.h
videosqlmodel.cpp videosqlmodel.h videosqlmodel.cpp videosqlmodel.h
imagesqlmodel.cpp imagesqlmodel.h
mpv/mpvobject.h mpv/mpvobject.cpp mpv/mpvobject.h mpv/mpvobject.cpp
mpv/qthelper.hpp mpv/mpvhelpers.h mpv/qthelper.hpp mpv/mpvhelpers.h
) )

View file

@ -22,10 +22,13 @@
#include <QtQuick/QQuickWindow> #include <QtQuick/QQuickWindow>
#include <QtQuick/QQuickView> #include <QtQuick/QQuickView>
#include <qapplication.h>
#include <qcoreapplication.h>
#include <qdir.h> #include <qdir.h>
#include <qglobal.h> #include <qglobal.h>
#include <qguiapplication.h> #include <qguiapplication.h>
#include <qqml.h> #include <qqml.h>
#include <qquickstyle.h>
#include <qsqldatabase.h> #include <qsqldatabase.h>
#include <qsqlquery.h> #include <qsqlquery.h>
#include <qstringliteral.h> #include <qstringliteral.h>
@ -34,6 +37,7 @@
#include "serviceitemmodel.h" #include "serviceitemmodel.h"
#include "songsqlmodel.h" #include "songsqlmodel.h"
#include "videosqlmodel.h" #include "videosqlmodel.h"
#include "imagesqlmodel.h"
static void connectToDatabase() { static void connectToDatabase() {
// let's setup our sql database // let's setup our sql database
@ -67,20 +71,25 @@ static void connectToDatabase() {
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv); QApplication app(argc, argv);
KLocalizedString::setApplicationDomain("presenter"); KLocalizedString::setApplicationDomain("librepresenter");
QCoreApplication::setOrganizationName(QStringLiteral("presenter")); QCoreApplication::setOrganizationName(QStringLiteral("librepresenter"));
QCoreApplication::setOrganizationDomain(QStringLiteral("tfcconnection.org")); QCoreApplication::setOrganizationDomain(QStringLiteral("tfcconnection.org"));
QCoreApplication::setApplicationName(QStringLiteral("Church Presenter")); QCoreApplication::setApplicationName(QStringLiteral("Libre Presenter"));
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
QIcon::setFallbackThemeName("breeze"); QIcon::setFallbackThemeName("breeze");
QQuickStyle::setStyle(QStringLiteral("org.kde.breeze")); QQuickStyle::setStyle(QStringLiteral("org.kde.breeze"));
// QApplication::setStyle(QStringLiteral("breeze"));
#else #else
QIcon::setFallbackThemeName("breeze");
QQuickStyle::setStyle(QStringLiteral("org.kde.desktop")); QQuickStyle::setStyle(QStringLiteral("org.kde.desktop"));
QQuickStyle::setFallbackStyle(QStringLiteral("breeze"));
#endif #endif
QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("system-config-display"))); QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("system-config-display")));
qDebug() << QQuickStyle::availableStyles();
qDebug() << QIcon::themeName();
// apparently mpv needs this class set // apparently mpv needs this class set
// let's register mpv as well // let's register mpv as well
@ -90,6 +99,7 @@ int main(int argc, char *argv[])
//register our models //register our models
qmlRegisterType<SongSqlModel>("org.presenter", 1, 0, "SongSqlModel"); qmlRegisterType<SongSqlModel>("org.presenter", 1, 0, "SongSqlModel");
qmlRegisterType<VideoSqlModel>("org.presenter", 1, 0, "VideoSqlModel"); qmlRegisterType<VideoSqlModel>("org.presenter", 1, 0, "VideoSqlModel");
qmlRegisterType<ImageSqlModel>("org.presenter", 1, 0, "ImageSqlModel");
qmlRegisterType<ServiceItemModel>("org.presenter", 1, 0, "ServiceItemModel"); qmlRegisterType<ServiceItemModel>("org.presenter", 1, 0, "ServiceItemModel");
connectToDatabase(); connectToDatabase();

View file

@ -87,7 +87,7 @@ MpvRenderer::~MpvRenderer() {
if (mpv_gl) if (mpv_gl)
mpv_render_context_free(mpv_gl); mpv_render_context_free(mpv_gl);
mpv_terminate_destroy(obj->mpv); // mpv_destroy(obj->mpv);
} }
void MpvRenderer::render() { void MpvRenderer::render() {
@ -242,7 +242,7 @@ MpvObject::MpvObject(QQuickItem *parent)
MpvObject::~MpvObject() MpvObject::~MpvObject()
{ {
// quit();
} }
QQuickFramebufferObject::Renderer *MpvObject::createRenderer() const 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 // See: https://github.com/mpv-player/mpv/blob/master/player/lua.c#L471
switch (event->event_id) { switch (event->event_id) {
case MPV_EVENT_SHUTDOWN: {
mpv_destroy(mpv);
break;
}
case MPV_EVENT_LOG_MESSAGE: { case MPV_EVENT_LOG_MESSAGE: {
mpv_event_log_message *logData = (mpv_event_log_message *)event->data; mpv_event_log_message *logData = (mpv_event_log_message *)event->data;
Q_EMIT logMessage( Q_EMIT logMessage(
@ -487,7 +491,7 @@ void MpvObject::pause()
{ {
// qDebug() << "pause"; // qDebug() << "pause";
if (isPlaying()) { if (isPlaying()) {
// qDebug() << "!isPlaying"; qDebug() << "!isPlaying";
set_paused(true); set_paused(true);
} }
} }

View file

@ -5,7 +5,7 @@ import Qt.labs.platform 1.1 as Labs
import QtQuick.Window 2.13 import QtQuick.Window 2.13
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2
import QtMultimedia 5.15 import QtMultimedia 5.15
import QtAudioEngine 1.15 /* import QtAudioEngine 1.15 */
import org.kde.kirigami 2.13 as Kirigami import org.kde.kirigami 2.13 as Kirigami
import "./presenter" as Presenter import "./presenter" as Presenter
@ -121,6 +121,7 @@ Kirigami.ApplicationWindow {
/* print(Qt.application.state); */ /* print(Qt.application.state); */
screens = Qt.application.screens; screens = Qt.application.screens;
presentationScreen = screens[1] presentationScreen = screens[1]
print(Kirigami.Settings.Style);
for (let i = 0; i < screens.length; i++) { for (let i = 0; i < screens.length; i++) {
/* print(screens[i]); */ /* print(screens[i]); */
/* print(screens[i].name); */ /* print(screens[i].name); */

View file

@ -4,7 +4,7 @@ import QtQuick.Controls 2.15 as Controls
import QtQuick.Window 2.13 import QtQuick.Window 2.13
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2
import QtMultimedia 5.15 import QtMultimedia 5.15
import QtAudioEngine 1.15 /* import QtAudioEngine 1.15 */
import org.kde.kirigami 2.13 as Kirigami import org.kde.kirigami 2.13 as Kirigami
import "./" as Presenter import "./" as Presenter

View file

@ -38,6 +38,7 @@ Item {
// Emitted when clicking to activate underneath mousearea // Emitted when clicking to activate underneath mousearea
signal clicked() signal clicked()
signal rightClicked()
MouseArea { MouseArea {
id: mouseArea id: mouseArea
@ -55,12 +56,15 @@ Item {
property int mouseDownY property int mouseDownY
property Item originalParent property Item originalParent
property int autoScrollThreshold: (listView.contentHeight > listView.height) ? listItem.height * 3 : 0 property int autoScrollThreshold: (listView.contentHeight > listView.height) ? listItem.height * 3 : 0
opacity: mouseArea.pressed || (!Kirigami.Settings.tabletMode && listItem.hovered) ? 1 : 0.6
function arrangeItem() { 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); root.moveRequested(index, newIndex);
} }
} }
@ -127,10 +131,16 @@ Item {
MouseArea { MouseArea {
id: clickArea id: clickArea
anchors.fill: parent anchors.fill: parent
onClicked: root.clicked()
hoverEnabled: true hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onEntered: root.containsMouse = true onEntered: root.containsMouse = true
onExited: root.containsMouse = false onExited: root.containsMouse = false
onClicked: {
if (mouse.button === Qt.RightButton)
root.rightClicked();
else
root.clicked();
}
} }
} }
} }

View file

@ -8,6 +8,9 @@ import "./" as Presenter
Item { Item {
id: root id: root
property string type: "image"
property var image
GridLayout { GridLayout {
id: mainLayout id: mainLayout
anchors.fill: parent anchors.fill: parent
@ -27,7 +30,7 @@ Item {
implicitWidth: 300 implicitWidth: 300
editable: true editable: true
hoverEnabled: true hoverEnabled: true
onCurrentTextChanged: showPassiveNotification(currentText) /* onCurrentTextChanged: showPassiveNotification(currentText) */
} }
Controls.SpinBox { Controls.SpinBox {
editable: true editable: true
@ -40,18 +43,6 @@ Item {
implicitWidth: 100 implicitWidth: 100
hoverEnabled: true hoverEnabled: true
} }
Controls.ToolButton {
text: "B"
hoverEnabled: true
}
Controls.ToolButton {
text: "I"
hoverEnabled: true
}
Controls.ToolButton {
text: "U"
hoverEnabled: true
}
Controls.ToolSeparator {} Controls.ToolSeparator {}
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
Controls.ToolSeparator {} Controls.ToolSeparator {}
@ -63,7 +54,7 @@ Item {
} }
Controls.ToolButton { Controls.ToolButton {
id: backgroundButton id: backgroundButton
text: "Background" text: "Select Image"
icon.name: "fileopen" icon.name: "fileopen"
hoverEnabled: true hoverEnabled: true
onClicked: backgroundType.open() onClicked: backgroundType.open()
@ -83,15 +74,15 @@ Item {
border.color: Kirigami.Theme.activeBackgroundColor border.color: Kirigami.Theme.activeBackgroundColor
border.width: 2 border.width: 2
} }
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent closePolicy: Controls.Popup.CloseOnEscape | Controls.Popup.CloseOnPressOutsideParent
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
Controls.ToolButton { Controls.ToolButton {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
text: "Video" text: "Image"
icon.name: "emblem-videos-symbolic" icon.name: "emblem-images-symbolic"
onClicked: videoFileDialog.open() & backgroundType.close() onClicked: imageFileDialog.open() & backgroundType.close()
} }
Controls.ToolButton { Controls.ToolButton {
Layout.fillWidth: true Layout.fillWidth: true
@ -121,11 +112,11 @@ Item {
ColumnLayout { ColumnLayout {
Controls.SplitView.fillHeight: true Controls.SplitView.fillHeight: true
Controls.SplitView.preferredWidth: 500 Controls.SplitView.preferredWidth: 300
Controls.SplitView.minimumWidth: 500 Controls.SplitView.minimumWidth: 100
Controls.TextField { Controls.TextField {
id: songTitleField id: imageTitleField
Layout.preferredWidth: 300 Layout.preferredWidth: 300
Layout.fillWidth: true Layout.fillWidth: true
@ -133,98 +124,46 @@ Item {
Layout.rightMargin: 20 Layout.rightMargin: 20
placeholderText: "Song Title..." placeholderText: "Song Title..."
text: songTitle text: image.title
padding: 10 padding: 10
onEditingFinished: updateTitle(text); /* 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 { Item {
id: songLyricsField id: empty
Layout.preferredHeight: 3000
Layout.fillWidth: true
Layout.fillHeight: 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 { ColumnLayout {
Controls.SplitView.fillHeight: true Controls.SplitView.fillHeight: true
Controls.SplitView.preferredWidth: 700 Controls.SplitView.preferredWidth: 700
Controls.SplitView.minimumWidth: 300 Controls.SplitView.minimumWidth: 300
spacing: 5
Rectangle { Item {
id: slideBar id: topEmpty
color: Kirigami.Theme.highlightColor Layout.fillHeight: true
Layout.preferredWidth: 500
Layout.preferredHeight: songTitleField.height
Layout.rightMargin: 20
Layout.leftMargin: 20
} }
Presenter.SlideEditor { Image {
id: slideEditor id: imagePreview
Layout.preferredWidth: 500 Layout.preferredWidth: 600
Layout.fillWidth: true Layout.preferredHeight: Layout.preferredWidth / 16 * 9
Layout.preferredHeight: slideEditor.width / 16 * 9 Layout.alignment: Qt.AlignCenter
Layout.bottomMargin: 30 fillMode: Image.PreserveAspectFit
Layout.rightMargin: 20 source: image.filePath
Layout.leftMargin: 20 }
Item {
id: botEmpty
Layout.fillHeight: true
} }
} }
} }
}
} function changeImage(image) {
Timer { root.image = image;
id: editorTimer print(image.filePath.toString());
interval: 1000
repeat: true
running: false
onTriggered: updateLyrics(lyricsEditor.text)
} }
} }

View file

@ -3,8 +3,9 @@ import QtQuick.Dialogs 1.0
import QtQuick.Controls 2.0 as Controls import QtQuick.Controls 2.0 as Controls
import QtQuick.Window 2.13 import QtQuick.Window 2.13
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2
import QtQml.Models 2.12
import QtMultimedia 5.15 import QtMultimedia 5.15
import QtAudioEngine 1.15 /* import QtAudioEngine 1.15 */
import org.kde.kirigami 2.13 as Kirigami import org.kde.kirigami 2.13 as Kirigami
import "./" as Presenter import "./" as Presenter
@ -34,6 +35,7 @@ ColumnLayout {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
onDropped: { onDropped: {
print("DROPPED AT END");
appendItem(dragItemTitle, appendItem(dragItemTitle,
dragItemType, dragItemType,
dragItemBackground, dragItemBackground,
@ -46,13 +48,14 @@ ColumnLayout {
ListView { ListView {
id: serviceItemList id: serviceItemList
anchors.fill: parent anchors.fill: parent
model: serviceItemModel /* model: serviceItemModel */
delegate: Kirigami.DelegateRecycler { /* delegate: Kirigami.DelegateRecycler { */
width: serviceItemList.width /* width: serviceItemList.width */
sourceComponent: itemDelegate /* sourceComponent: itemDelegate */
} /* } */
clip: true clip: true
spacing: 3 spacing: 3
property int dragItemIndex
addDisplaced: Transition { addDisplaced: Transition {
NumberAnimation {properties: "x, y"; duration: 100} NumberAnimation {properties: "x, y"; duration: 100}
@ -73,68 +76,182 @@ ColumnLayout {
NumberAnimation {properties: "x, y"; duration: 100} NumberAnimation {properties: "x, y"; duration: 100}
} }
Component { model: DelegateModel {
id: itemDelegate id: visualModel
Item { model: serviceItemModel
id: serviceItem delegate: DropArea {
id: serviceDrop
implicitWidth: serviceItemList.width implicitWidth: serviceItemList.width
height: 50 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 { Kirigami.BasicListItem {
anchors.fill: parent id: visServiceItem
width: serviceDrop.width
height: serviceDrop.height
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
label: name label: name
subtitle: type subtitle: type
hoverEnabled: true hoverEnabled: false
supportsMouseEvents: false supportsMouseEvents: false
backgroundColor: { backgroundColor: {
if (parent.ListView.isCurrentItem) { if (serviceItemList.currentIndex === index ||
mouseHandler.containsMouse)
Kirigami.Theme.highlightColor; Kirigami.Theme.highlightColor;
/* } else if (serviceDrop.constainsDrag){ */ else
/* Kirigami.Theme.hoverColor; */
} else if (mouseHandler.containsMouse){
Kirigami.Theme.highlightColor;
} else {
Kirigami.Theme.backgroundColor; Kirigami.Theme.backgroundColor;
} }
}
textColor: { textColor: {
if (parent.ListView.isCurrentItem || mouseHandler.containsMouse) if (serviceItemList.currentIndex === index ||
mouseHandler.containsMouse)
activeTextColor; activeTextColor;
else else
Kirigami.Theme.textColor; Kirigami.Theme.textColor;
} }
states: [
State {
when: mouseHandler.drag.active
ParentChange {
target: visServiceItem
parent: serviceItemList
} }
Presenter.DragHandle {
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 id: mouseHandler
anchors.fill: parent anchors.fill: parent
listItem: serviceItem hoverEnabled: true
listView: serviceItemList acceptedButtons: Qt.LeftButton | Qt.RightButton
onMoveRequested: serviceItemModel.move(oldIndex, newIndex) preventStealing: true
onClicked: {
serviceItemList.currentIndex = index; drag {
/* showPassiveNotification(serviceItemList.currentIndex); */ target: visServiceItem
changeSlideBackground(background, backgroundType); axis: Drag.YAxis
changeSlideText(text); /* minimumY: root.y */
changeSlideType(type); /* maximumY: serviceItemList.height - serviceDrop.height */
smoothed: false
}
drag.onActiveChanged: {
if (mouseHandler.drag.active) {
dragItemIndex = index;
} }
} }
DropArea { /* onPositionChanged: { */
id: serviceDrop /* if (!pressed) { */
anchors.fill: parent /* return; */
onDropped: { /* } */
addItem(index, /* mouseArea.arrangeItem(); */
dragItemTitle, /* } */
dragItemType,
dragItemText, onPressed: {
dragItemBackgroundType, serviceItemList.interactive = false;
dragItemBackground,
dragItemIndex);
} }
keys: ["library"]
onClicked: {
if (mouse.button === Qt.RightButton)
rightClickMenu.popup();
else {
serviceItemList.currentIndex = index;
currentServiceItem = index;
changeServiceItem(index);
}
}
onReleased: {
print("should drop");
visServiceItem.Drag.drop();
} }
} }
} }
/* 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)
}
}
function moveRequested(oldIndex, newIndex) {
serviceItemModel.move(oldIndex, newIndex);
}
function dropPlacement(drag) {
print(drag.y);
}
}
}
Kirigami.WheelHandler { Kirigami.WheelHandler {
id: wheelHandler id: wheelHandler
target: serviceItemList target: serviceItemList
@ -150,16 +267,21 @@ ColumnLayout {
} }
} }
function addItem(index, name, type, function addItem(index, name, type,
background, backgroundType, text, itemID) { background, backgroundType, text, itemID) {
const newtext = songsqlmodel.getLyricList(itemID);
print("adding: " + name + " of type " + type);
serviceItemModel.insertItem(index, name, serviceItemModel.insertItem(index, name,
type, text, background, type, background,
backgroundType) backgroundType, newtext);
} }
function appendItem(name, type, background, backgroundType, text, itemID) { function appendItem(name, type, background, backgroundType, text, itemID) {
print("adding: " + name + " of type " + type);
let lyrics; let lyrics;
if (type == "song") { if (type === "song") {
print(itemID);
lyrics = songsqlmodel.getLyricList(itemID); lyrics = songsqlmodel.getLyricList(itemID);
print(lyrics); print(lyrics);
} }

View file

@ -195,9 +195,14 @@ Item {
target: songListItem target: songListItem
x: x x: x
y: y y: y
width: width
height: height
}
ParentChange {
target: videoListItem
parent: rootApp.overlay
} }
} }
} }
MouseArea { MouseArea {
@ -411,6 +416,12 @@ Item {
target: videoListItem target: videoListItem
x: x x: x
y: y y: y
width: width
height: height
}
ParentChange {
target: videoListItem
parent: rootApp.overlay
} }
} }
@ -474,12 +485,40 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
color: Kirigami.Theme.backgroundColor color: Kirigami.Theme.backgroundColor
opacity: 1.0
Controls.Label { Controls.Label {
id: imageLabel
anchors.centerIn: parent anchors.centerIn: parent
text: "Images" 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 { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
@ -497,6 +536,9 @@ Item {
Layout.preferredHeight: parent.height - 200 Layout.preferredHeight: parent.height - 200
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
model: imagesqlmodel
delegate: imageDelegate
clip: true
state: "deselected" state: "deselected"
states: [ states: [
@ -523,6 +565,118 @@ Item {
duration: 300 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 { Rectangle {
@ -666,7 +820,7 @@ Item {
videoLibraryList.currentIndex = videosqlmodel.rowCount(); videoLibraryList.currentIndex = videosqlmodel.rowCount();
print(videosqlmodel.getVideo(videoLibraryList.currentIndex)); print(videosqlmodel.getVideo(videoLibraryList.currentIndex));
const video = videosqlmodel.getVideo(videoLibraryList.currentIndex); const video = videosqlmodel.getVideo(videoLibraryList.currentIndex);
showPassiveNotification("newest video: " + video); showPassiveNotification("newest video: " + video.title);
if (!editMode) if (!editMode)
editMode = true; editMode = true;
editSwitch("video", video); editSwitch("video", video);

View file

@ -12,6 +12,7 @@ Controls.Page {
padding: 0 padding: 0
// properties passed around for the slides // properties passed around for the slides
property int currentServiceItem
property url imageBackground: "" property url imageBackground: ""
property url videoBackground: "" property url videoBackground: ""
property int blurRadius: 0 property int blurRadius: 0
@ -65,6 +66,7 @@ Controls.Page {
id: presentation id: presentation
anchors.fill: parent anchors.fill: parent
} }
Presenter.SongEditor { Presenter.SongEditor {
id: songEditor id: songEditor
visible: false visible: false
@ -76,6 +78,12 @@ Controls.Page {
visible: false visible: false
anchors.fill: parent anchors.fill: parent
} }
Presenter.ImageEditor {
id: imageEditor
visible: false
anchors.fill: parent
}
} }
Presenter.Library { Presenter.Library {
@ -90,35 +98,7 @@ Controls.Page {
Loader { Loader {
id: presentLoader id: presentLoader
active: presenting active: presenting
sourceComponent: presentWindowComp source: "PresentationWindow.qml"
}
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
}
}
} }
SongSqlModel { SongSqlModel {
@ -129,52 +109,41 @@ Controls.Page {
id: videosqlmodel id: videosqlmodel
} }
ImageSqlModel {
id: imagesqlmodel
}
ServiceItemModel { ServiceItemModel {
id: serviceItemModel id: serviceItemModel
} }
function changeSlideType(type) { function changeServiceItem(index) {
presentation.itemType = type; const item = serviceItemModel.getItem(index);
if (slideItem) print("index grabbed: " + index);
slideItem.itemType = type;
}
function changeSlideText(text) { presentation.stopVideo()
presentation.text = text; presentation.itemType = item.type;
if (slideItem) print("Time to start changing");
slideItem.text = text;
}
function changeSlideBackground(background, type) { if (item.backgroundType === "image") {
showPassiveNotification("starting background change..");
showPassiveNotification(background);
showPassiveNotification(type);
if (type == "image") {
presentation.vidbackground = ""; presentation.vidbackground = "";
presentation.imagebackground = background; presentation.imagebackground = item.background;
if (slideItem) {
slideItem.videoSource = "";
slideItem.stopVideo();
slideItem.imageSource = background;
}
} else { } else {
presentation.imagebackground = ""; presentation.imagebackground = "";
presentation.vidbackground = background; presentation.vidbackground = item.background;
presentation.loadVideo() presentation.loadVideo()
if (slideItem) {
slideItem.imageSource = "";
slideItem.videoSource = background;
slideItem.loadVideo()
}
}
} }
function changeSlideNext() { print(item.text.length)
showPassiveNotification("next slide please") if (item.text.length === 0) {
presentation.text = [""];
} }
else
presentation.text = item.text;
presentation.textIndex = 0;
presentation.changeSlide();
function changeSlidePrevious() { print("Slide changed to: " + item.name);
showPassiveNotification("previous slide please")
} }
function editSwitch(item) { function editSwitch(item) {
@ -184,24 +153,30 @@ Controls.Page {
presentation.visible = false; presentation.visible = false;
videoEditor.visible = false; videoEditor.visible = false;
videoEditor.stop(); videoEditor.stop();
imageEditor.visible = false;
songEditor.visible = true; songEditor.visible = true;
songEditor.changeSong(item); songEditor.changeSong(item);
break; break;
case "video" : case "video" :
presentation.visible = false; presentation.visible = false;
songEditor.visible = false; songEditor.visible = false;
imageEditor.visible = false;
videoEditor.visible = true; videoEditor.visible = true;
videoEditor.changeVideo(item); videoEditor.changeVideo(item);
break; break;
case "image" : case "image" :
mainPageArea.pop(Controls.StackView.Immediate); presentation.visible = false;
mainPageArea.push(imageEditorComp, Controls.StackView.Immediate); videoEditor.visible = false;
videoEditor.stop(); videoEditor.stop();
songEditor.visible = false;
imageEditor.visible = true;
imageEditor.changeImage(item);
break; break;
default: default:
videoEditor.visible = false; videoEditor.visible = false;
videoEditor.stop(); videoEditor.stop();
songEditor.visible = false; songEditor.visible = false;
imageEditor.visible = false;
presentation.visible = true; presentation.visible = true;
editMode = false; editMode = false;
} }
@ -209,6 +184,7 @@ Controls.Page {
videoEditor.visible = false; videoEditor.visible = false;
videoEditor.stop(); videoEditor.stop();
songEditor.visible = false; songEditor.visible = false;
imageEditor.visible = false;
presentation.visible = true; presentation.visible = true;
editMode = false; editMode = false;
} }

View file

@ -3,18 +3,21 @@ import QtQuick.Dialogs 1.0
import QtQuick.Controls 2.15 as Controls import QtQuick.Controls 2.15 as Controls
import QtQuick.Window 2.13 import QtQuick.Window 2.13
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2
import QtAudioEngine 1.15 /* import QtAudioEngine 1.15 */
import org.kde.kirigami 2.13 as Kirigami import org.kde.kirigami 2.13 as Kirigami
import "./" as Presenter import "./" as Presenter
Item { Item {
id: root id: root
property string text property var text
property int textIndex: 0
property string itemType property string itemType
property url imagebackground property url imagebackground
property url vidbackground property url vidbackground
Component.onCompleted: nextSlideAction()
GridLayout { GridLayout {
anchors.fill: parent anchors.fill: parent
columns: 3 columns: 3
@ -29,11 +32,18 @@ Item {
anchors.fill: parent anchors.fill: parent
Controls.ToolButton { Controls.ToolButton {
text: "Grid" text: "Solo"
icon.name: "viewimage"
hoverEnabled: true hoverEnabled: true
} }
Controls.ToolButton { 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 hoverEnabled: true
} }
Controls.ToolSeparator {} Controls.ToolSeparator {}
@ -45,13 +55,6 @@ Item {
hoverEnabled: true hoverEnabled: true
onClicked: {} onClicked: {}
} }
Controls.ToolButton {
id: backgroundButton
text: "Background"
icon.name: "fileopen"
hoverEnabled: true
onClicked: backgroundType.open()
}
} }
} }
@ -69,7 +72,7 @@ Item {
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onPressed: changeSlidePrevious() onPressed: previousSlideAction()
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
} }
} }
@ -81,7 +84,6 @@ Item {
Layout.minimumWidth: 300 Layout.minimumWidth: 300
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
textSize: width / 15 textSize: width / 15
text: root.text
itemType: root.itemType itemType: root.itemType
imageSource: imagebackground imageSource: imagebackground
videoSource: vidbackground videoSource: vidbackground
@ -95,7 +97,7 @@ Item {
Layout.alignment: Qt.AlignLeft Layout.alignment: Qt.AlignLeft
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onPressed: changeSlideNext() onPressed: nextSlideAction()
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
} }
} }
@ -112,4 +114,83 @@ Item {
function loadVideo() { function loadVideo() {
previewSlide.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 = "";
}
} }

View file

@ -2,7 +2,7 @@ import QtQuick 2.13
import QtQuick.Controls 2.15 as Controls import QtQuick.Controls 2.15 as Controls
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2
/* import QtMultimedia 5.15 */ /* import QtMultimedia 5.15 */
import QtAudioEngine 1.15 /* import QtAudioEngine 1.15 */
import QtGraphicalEffects 1.15 import QtGraphicalEffects 1.15
import org.kde.kirigami 2.13 as Kirigami import org.kde.kirigami 2.13 as Kirigami
import "./" as Presenter import "./" as Presenter
@ -15,13 +15,15 @@ Item {
property bool editMode: false property bool editMode: false
// These properties are for the slides visuals // These properties are for the slides visuals
property real textSize: 72 property real textSize: 50
property bool dropShadow: false property bool dropShadow: false
property url imageSource: imageBackground property url imageSource: imageBackground
property url videoSource: videoBackground property url videoSource: videoBackground
property string chosenFont: "Quicksand" property string chosenFont: "Quicksand"
property string text: "This is demo text" property var text: "This is demo text"
property color backgroundColor property color backgroundColor
property var hTextAlignment: Text.AlignHCenter
property var vTextAlignment: Text.AlignVCenter
//these properties are for giving video info to parents //these properties are for giving video info to parents
property int mpvPosition: mpv.position property int mpvPosition: mpv.position
@ -32,6 +34,9 @@ Item {
property string itemType property string itemType
property bool preview: false property bool preview: false
implicitWidth: 1920
implicitHeight: 1080
Rectangle { Rectangle {
id: basePrColor id: basePrColor
anchors.fill: parent anchors.fill: parent
@ -45,22 +50,24 @@ Item {
enableAudio: !preview enableAudio: !preview
Component.onCompleted: mpvLoadingTimer.start() Component.onCompleted: mpvLoadingTimer.start()
onFileLoaded: { onFileLoaded: {
showPassiveNotification(videoSource + " has been loaded"); /* showPassiveNotification(videoSource + " has been loaded"); */
if (itemType == "song") if (itemType == "song")
mpv.setProperty("loop", "inf"); mpv.setProperty("loop", "inf");
showPassiveNotification(mpv.getProperty("loop")); else
mpv.setProperty("loop", "no");
/* showPassiveNotification(mpv.getProperty("loop")); */
} }
MouseArea { MouseArea {
id: playArea id: playArea
anchors.fill: parent anchors.fill: parent
enabled: editMode enabled: editMode
onPressed: mpv.loadFile(videoSource.toString()); onPressed: mpv.playPause();
cursorShape: preview ? Qt.ArrowCursor : Qt.BlankCursor cursorShape: preview ? Qt.ArrowCursor : Qt.BlankCursor
} }
Controls.ProgressBar { Controls.ProgressBar {
anchors.centerIn: parent anchors.top: parent.bottom
visible: editMode visible: editMode
width: parent.width - 400 width: parent.width - 400
value: mpv.position value: mpv.position
@ -71,7 +78,36 @@ Item {
Timer { Timer {
id: mpvLoadingTimer id: mpvLoadingTimer
interval: 100 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 { Image {
@ -87,7 +123,7 @@ Item {
FastBlur { FastBlur {
id: imageBlue id: imageBlue
anchors.fill: parent anchors.fill: parent
source: imageSource == "" ? mpv : backgroundImage source: imageSource === "" ? mpv : backgroundImage
radius: blurRadius radius: blurRadius
Controls.Label { Controls.Label {
@ -97,9 +133,10 @@ Item {
/* minimumPointSize: 5 */ /* minimumPointSize: 5 */
fontSizeMode: Text.Fit fontSizeMode: Text.Fit
font.family: chosenFont font.family: chosenFont
horizontalAlignment: hTextAlignment
verticalAlignment: vTextAlignment
style: Text.Raised style: Text.Raised
anchors.centerIn: parent anchors.fill: parent
/* width: parent.width */
clip: true clip: true
layer.enabled: true layer.enabled: true
@ -123,6 +160,12 @@ Item {
} }
function stopVideo() { function stopVideo() {
mpv.stop() mpv.stop();
black.visible = true;
showPassiveNotification("Black is: " + black.visible);
}
function pauseVideo() {
mpv.pause();
} }
} }

View file

@ -1,10 +1,10 @@
import QtQuick 2.13 import QtQuick 2.15
import QtQuick.Dialogs 1.0 import QtQuick.Dialogs 1.0
import QtQuick.Controls 2.15 as Controls import QtQuick.Controls 2.15 as Controls
import QtQuick.Window 2.13 import QtQuick.Window 2.13
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2
import QtMultimedia 5.15 import QtMultimedia 5.15
import QtAudioEngine 1.15 /* import QtAudioEngine 1.15 */
import org.kde.kirigami 2.13 as Kirigami import org.kde.kirigami 2.13 as Kirigami
import "./" as Presenter import "./" as Presenter
@ -13,14 +13,86 @@ Item {
property string imageBackground property string imageBackground
property string videoBackground property string videoBackground
property var hTextAlignment
property var vTextAlignment
property string font
property real fontSize
Presenter.Slide { property ListModel songs: songModel
id: representation
ListView {
id: slideList
anchors.fill: parent anchors.fill: parent
textSize: width / 15 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 editMode: true
imageSource: imageBackground imageSource: root.imageBackground
videoSource: videoBackground videoSource: root.videoBackground
hTextAlignment: root.hTextAlignment
vTextAlignment: root.vTextAlignment
chosenFont: root.font
textSize: root.fontSize
preview: true 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;
}
}
}

View file

@ -1,5 +1,6 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Controls 2.15 as Controls import QtQuick.Controls 2.15 as Controls
import Qt.labs.platform 1.1 as Labs
import QtQuick.Dialogs 1.3 import QtQuick.Dialogs 1.3
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2
import org.kde.kirigami 2.13 as Kirigami import org.kde.kirigami 2.13 as Kirigami
@ -9,14 +10,7 @@ Item {
id: root id: root
property int songIndex property int songIndex
property string songTitle property var song
property string songLyrics
property string songAuthor
property string songCcli
property string songAudio
property string songVorder
property string songBackground
property string songBackgroundType
GridLayout { GridLayout {
id: mainLayout id: mainLayout
@ -33,34 +27,52 @@ Item {
anchors.fill: parent anchors.fill: parent
Controls.ComboBox { Controls.ComboBox {
id: fontBox
model: Qt.fontFamilies() model: Qt.fontFamilies()
implicitWidth: 300 implicitWidth: 300
editable: true editable: true
hoverEnabled: true hoverEnabled: true
onCurrentTextChanged: showPassiveNotification(currentText) flat: true
onActivated: updateFont(currentText)
} }
Controls.SpinBox { Controls.SpinBox {
id: fontSizeBox
editable: true editable: true
from: 5 from: 5
to: 72 to: 72
hoverEnabled: true hoverEnabled: true
onValueModified: updateFontSize(value)
} }
Controls.ComboBox { Controls.ComboBox {
id: hAlignmentBox
model: ["Left", "Center", "Right", "Justify"] model: ["Left", "Center", "Right", "Justify"]
implicitWidth: 100 implicitWidth: 100
hoverEnabled: true 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 { Controls.ToolButton {
text: "B" text: "B"
hoverEnabled: true hoverEnabled: true
visible: false
} }
Controls.ToolButton { Controls.ToolButton {
text: "I" text: "I"
hoverEnabled: true hoverEnabled: true
visible: false
} }
Controls.ToolButton { Controls.ToolButton {
text: "U" text: "U"
hoverEnabled: true hoverEnabled: true
visible: false
} }
Controls.ToolSeparator {} Controls.ToolSeparator {}
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
@ -143,7 +155,7 @@ Item {
Layout.rightMargin: 20 Layout.rightMargin: 20
placeholderText: "Song Title..." placeholderText: "Song Title..."
text: songTitle text: song.title
padding: 10 padding: 10
onEditingFinished: updateTitle(text); onEditingFinished: updateTitle(text);
} }
@ -156,7 +168,7 @@ Item {
Layout.rightMargin: 20 Layout.rightMargin: 20
placeholderText: "verse order..." placeholderText: "verse order..."
text: songVorder text: song.vorder
padding: 10 padding: 10
onEditingFinished: updateVerseOrder(text); onEditingFinished: updateVerseOrder(text);
} }
@ -176,7 +188,7 @@ Item {
width: parent.width width: parent.width
placeholderText: "Put lyrics here..." placeholderText: "Put lyrics here..."
persistentSelection: true persistentSelection: true
text: songLyrics text: song.lyrics
textFormat: TextEdit.PlainText textFormat: TextEdit.PlainText
padding: 10 padding: 10
onEditingFinished: { onEditingFinished: {
@ -195,7 +207,7 @@ Item {
Layout.rightMargin: 20 Layout.rightMargin: 20
placeholderText: "Author..." placeholderText: "Author..."
text: songAuthor text: song.author
padding: 10 padding: 10
onEditingFinished: updateAuthor(text) onEditingFinished: updateAuthor(text)
} }
@ -210,8 +222,9 @@ Item {
id: slideEditor id: slideEditor
Layout.preferredWidth: 500 Layout.preferredWidth: 500
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: slideEditor.width / 16 * 9 Layout.fillHeight: true
Layout.bottomMargin: 30 Layout.bottomMargin: 30
Layout.topMargin: 30
Layout.rightMargin: 20 Layout.rightMargin: 20
Layout.leftMargin: 20 Layout.leftMargin: 20
} }
@ -262,24 +275,25 @@ Item {
} }
function changeSong(index) { function changeSong(index) {
const song = songsqlmodel.getSong(index); const s = songsqlmodel.getSong(index);
song = s;
songIndex = index; songIndex = index;
songTitle = song[0];
songLyrics = song[1]; if (song.backgroundType == "image") {
songAuthor = song[2];
songCcli = song[3];
songAudio = song[4];
songVorder = song[5];
songBackground = song[6];
songBackgroundType = song[7];
if (songBackgroundType == "image") {
slideEditor.videoBackground = ""; slideEditor.videoBackground = "";
slideEditor.imageBackground = songBackground; slideEditor.imageBackground = song.background;
} else { } else {
slideEditor.imageBackground = ""; 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) { function updateLyrics(lyrics) {
@ -312,4 +326,83 @@ Item {
songsqlmodel.updateBackgroundType(songIndex, backgroundType); songsqlmodel.updateBackgroundType(songIndex, backgroundType);
print("changed background"); 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);
}
} }

View file

@ -32,7 +32,7 @@ Item {
implicitWidth: 300 implicitWidth: 300
editable: true editable: true
hoverEnabled: true hoverEnabled: true
onCurrentTextChanged: showPassiveNotification(currentText) /* onCurrentTextChanged: showPassiveNotification(currentText) */
} }
Controls.SpinBox { Controls.SpinBox {
editable: true editable: true
@ -138,7 +138,7 @@ Item {
Layout.rightMargin: 20 Layout.rightMargin: 20
placeholderText: "Song Title..." placeholderText: "Song Title..."
text: video[0] text: video.title
padding: 10 padding: 10
/* onEditingFinished: updateTitle(text); */ /* onEditingFinished: updateTitle(text); */
} }
@ -170,7 +170,7 @@ Item {
Component.onCompleted: mpvLoadingTimer.start() Component.onCompleted: mpvLoadingTimer.start()
onPositionChanged: videoSlider.value = position onPositionChanged: videoSlider.value = position
onFileLoaded: { onFileLoaded: {
showPassiveNotification(video[0] + " has been loaded"); showPassiveNotification(video.title + " has been loaded");
videoPreview.pause(); videoPreview.pause();
} }
} }
@ -220,7 +220,7 @@ Item {
id: mpvLoadingTimer id: mpvLoadingTimer
interval: 100 interval: 100
onTriggered: { onTriggered: {
videoPreview.loadFile(video[1].toString()); videoPreview.loadFile(video.filePath.toString());
/* showPassiveNotification(video[0]); */ /* showPassiveNotification(video[0]); */
} }
} }

View file

@ -17,5 +17,6 @@
<file>qml/presenter/Presentation.qml</file> <file>qml/presenter/Presentation.qml</file>
<file>qml/presenter/Settings.qml</file> <file>qml/presenter/Settings.qml</file>
<file>assets/parallel.jpg</file> <file>assets/parallel.jpg</file>
<file>assets/black.jpg</file>
</qresource> </qresource>
</RCC> </RCC>

View file

@ -1,6 +1,7 @@
#include "serviceitemmodel.h" #include "serviceitemmodel.h"
#include "serviceitem.h" #include "serviceitem.h"
#include <qabstractitemmodel.h> #include <qabstractitemmodel.h>
#include <qglobal.h>
#include <qnamespace.h> #include <qnamespace.h>
#include <qvariant.h> #include <qvariant.h>
#include <qdebug.h> #include <qdebug.h>
@ -115,13 +116,17 @@ Qt::ItemFlags ServiceItemModel::flags(const QModelIndex &index) const {
void ServiceItemModel::addItem(ServiceItem *item) { void ServiceItemModel::addItem(ServiceItem *item) {
const int index = m_items.size(); const int index = m_items.size();
qDebug() << index;
// foreach (item, m_items) {
// qDebug() << item;
// }
beginInsertRows(QModelIndex(), index, index); beginInsertRows(QModelIndex(), index, index);
m_items.append(item); m_items.append(item);
endInsertRows(); endInsertRows();
} }
void ServiceItemModel::insertItem(const int &index, ServiceItem *item) { 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); m_items.insert(index, item);
endInsertRows(); endInsertRows();
qDebug() << "Success"; qDebug() << "Success";
@ -164,7 +169,7 @@ void ServiceItemModel::insertItem(const int &index, const QString &name, const Q
const QStringList &text) { const QStringList &text) {
ServiceItem *item = new ServiceItem(name, type, background, backgroundType, text); ServiceItem *item = new ServiceItem(name, type, background, backgroundType, text);
insertItem(index, item); insertItem(index, item);
qDebug() << name << type << background; qDebug() << name << type << background << text;
} }
void ServiceItemModel::removeItem(int index) { void ServiceItemModel::removeItem(int index) {
@ -174,17 +179,36 @@ void ServiceItemModel::removeItem(int index) {
} }
bool ServiceItemModel::move(int sourceIndex, int destIndex) { bool ServiceItemModel::move(int sourceIndex, int destIndex) {
qDebug() << "starting move"; qDebug() << index(sourceIndex).row();
qDebug() << index(destIndex).row();
// beginResetModel();
QModelIndex parent = index(sourceIndex).parent(); QModelIndex parent = index(sourceIndex).parent();
bool begsuc = beginMoveRows(parent, sourceIndex, sourceIndex, parent, destIndex); if (sourceIndex >= 0 && sourceIndex != destIndex && destIndex >= 0 && destIndex < rowCount() && sourceIndex < rowCount()) {
qDebug() << begsuc; qDebug() << "starting move of: " << "source: " << sourceIndex << "dest: " << destIndex;
if (!begsuc) { bool begsuc = beginMoveRows(QModelIndex(), sourceIndex, sourceIndex, QModelIndex(), destIndex);
qDebug() << "Failed to start moving rows"; if (begsuc)
m_items.move(sourceIndex, destIndex); m_items.move(sourceIndex, destIndex);
return false;
}
// bool success = moveRow(index(sourceIndex).parent(), sourceIndex, index(destIndex).parent(), destIndex);
endMoveRows(); endMoveRows();
}
// endResetModel();
// emit dataChanged(index(sourceIndex), QModelIndex());
// qDebug() << success; // qDebug() << success;
return true; 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<int,QByteArray> rn = roleNames();
// qDebug() << rn;
QHashIterator<int,QByteArray> it(rn);
while (it.hasNext()) {
it.next();
qDebug() << it.key() << ":" << it.value();
data[it.value()] = idx.data(it.key());
}
return data;
}

View file

@ -56,6 +56,7 @@ public:
const QString &backgroundType, const QStringList &text); const QString &backgroundType, const QStringList &text);
Q_INVOKABLE void removeItem(int index); Q_INVOKABLE void removeItem(int index);
Q_INVOKABLE bool move(int sourceIndex, int destIndex); Q_INVOKABLE bool move(int sourceIndex, int destIndex);
Q_INVOKABLE QVariantMap getItem(int index) const;
private: private:
QList<ServiceItem *> m_items; QList<ServiceItem *> m_items;

View file

@ -34,27 +34,39 @@ static void createTable()
" 'vorder' TEXT," " 'vorder' TEXT,"
" 'background' TEXT," " 'background' TEXT,"
" 'backgroundType' TEXT," " 'backgroundType' TEXT,"
" 'horizontalTextAlignment' TEXT,"
" 'verticalTextAlignment' TEXT,"
" 'font' TEXT,"
" 'fontSize' INTEGER,"
" PRIMARY KEY(id))")) { " PRIMARY KEY(id))")) {
qFatal("Failed to query database: %s", qFatal("Failed to query database: %s",
qPrintable(query.lastError().text())); qPrintable(query.lastError().text()));
} }
qDebug() << query.lastQuery(); // qDebug() << query.lastQuery();
qDebug() << "inserting into songs"; // 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', '', '', '', '')"); query.exec(
qDebug() << query.lastQuery(); "INSERT INTO songs (title, lyrics, author, ccli, audio, vorder, "
query.exec("INSERT INTO songs (title, lyrics, author, ccli, audio, vorder, background, backgroundType) VALUES ('River', 'Im going down to the river', 'Jordan Feliz', '13470183', '', '', '', '')"); "background, backgroundType, horizontalTextAlignment, verticalTextAlignment, font, fontSize) VALUES ('10,000 Reasons', '10,000 reasons "
query.exec("INSERT INTO songs (title, lyrics, author, ccli, audio, vorder, background, backgroundType) VALUES ('Marvelous Light', 'Into marvelous " "for my heart to sing', 'Matt Redman', '13470183', '', '', '', '', 'center', 'center', '', '')");
"light Im running', 'Chris Tomlin', '13470183', '', '', '', '')"); // 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"); query.exec("select * from songs");
qDebug() << query.lastQuery(); // qDebug() << query.lastQuery();
} }
SongSqlModel::SongSqlModel(QObject *parent) SongSqlModel::SongSqlModel(QObject *parent)
: QSqlTableModel(parent) : QSqlTableModel(parent)
{ {
qDebug() << "creating table"; // qDebug() << "creating table";
createTable(); createTable();
setTable(songsTableName); setTable(songsTableName);
setEditStrategy(QSqlTableModel::OnManualSubmit); setEditStrategy(QSqlTableModel::OnManualSubmit);
@ -85,6 +97,10 @@ QHash<int, QByteArray> SongSqlModel::roleNames() const
names[Qt::UserRole + 6] = "vorder"; names[Qt::UserRole + 6] = "vorder";
names[Qt::UserRole + 7] = "background"; names[Qt::UserRole + 7] = "background";
names[Qt::UserRole + 8] = "backgroundType"; 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; return names;
} }
@ -113,25 +129,24 @@ void SongSqlModel::deleteSong(const int &row) {
submitAll(); submitAll();
} }
QVariantList SongSqlModel::getSong(const int &row) { QVariantMap SongSqlModel::getSong(const int &row) {
QSqlRecord recordData = record(row); // this whole function returns all data in the song
if (recordData.isEmpty()) { // regardless of it's length. When new things are added
qDebug() << "this is not a song"; // it will still work without refactoring.
QVariantList empty; QVariantMap data;
return empty; const QModelIndex idx = this->index(row,0);
// qDebug() << idx;
if( !idx.isValid() )
return data;
const QHash<int,QByteArray> rn = roleNames();
// qDebug() << rn;
QHashIterator<int,QByteArray> it(rn);
while (it.hasNext()) {
it.next();
qDebug() << it.key() << ":" << it.value();
data[it.value()] = idx.data(it.key());
} }
return data;
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;
} }
QStringList SongSqlModel::getLyricList(const int &row) { QStringList SongSqlModel::getLyricList(const int &row) {
@ -162,8 +177,11 @@ QStringList SongSqlModel::getLyricList(const int &row) {
QString line; QString line;
QMap<QString, QString> verses; QMap<QString, QString> verses;
//TODO make sure to split empty line in verse into two slides
// This first function pulls out each verse into our verses map // This first function pulls out each verse into our verses map
foreach (line, rawLyrics) { foreach (line, rawLyrics) {
qDebug() << line;
if (firstItem) { if (firstItem) {
if (keywords.contains(line)) { if (keywords.contains(line)) {
recordVerse = true; recordVerse = true;
@ -187,6 +205,16 @@ QStringList SongSqlModel::getLyricList(const int &row) {
} }
qDebug() << verses; 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 // this function appends the verse that matches the verse order from the map
foreach (const QString &vstr, vorder) { foreach (const QString &vstr, vorder) {
foreach (line, rawLyrics) { foreach (line, rawLyrics) {
@ -394,3 +422,107 @@ void SongSqlModel::updateBackgroundType(const int &row, const QString &backgroun
submitAll(); submitAll();
emit backgroundTypeChanged(); 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();
}

View file

@ -18,6 +18,10 @@ class SongSqlModel : public QSqlTableModel
Q_PROPERTY(QString vorder READ vorder WRITE setVerseOrder NOTIFY vorderChanged) Q_PROPERTY(QString vorder READ vorder WRITE setVerseOrder NOTIFY vorderChanged)
Q_PROPERTY(QString background READ background WRITE setBackground NOTIFY backgroundChanged) Q_PROPERTY(QString background READ background WRITE setBackground NOTIFY backgroundChanged)
Q_PROPERTY(QString backgroundType READ backgroundType WRITE setBackgroundType NOTIFY backgroundTypeChanged) 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 QML_ELEMENT
public: public:
@ -32,6 +36,10 @@ public:
QString vorder() const; QString vorder() const;
QString background() const; QString background() const;
QString backgroundType() const; QString backgroundType() const;
QString horizontalTextAlignment() const;
QString verticalTextAlignment() const;
QString font() const;
int fontSize() const;
void setTitle(const QString &title); void setTitle(const QString &title);
void setLyrics(const QString &lyrics); void setLyrics(const QString &lyrics);
@ -41,6 +49,10 @@ public:
void setVerseOrder(const QString &vorder); void setVerseOrder(const QString &vorder);
void setBackground(const QString &background); void setBackground(const QString &background);
void setBackgroundType(const QString &backgroundType); 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 updateTitle(const int &row, const QString &title);
Q_INVOKABLE void updateLyrics(const int &row, const QString &lyrics); 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 updateVerseOrder(const int &row, const QString &vorder);
Q_INVOKABLE void updateBackground(const int &row, const QString &background); Q_INVOKABLE void updateBackground(const int &row, const QString &background);
Q_INVOKABLE void updateBackgroundType(const int &row, const QString &backgroundType); 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 newSong();
Q_INVOKABLE void deleteSong(const int &row); 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); Q_INVOKABLE QStringList getLyricList(const int &row);
QVariant data(const QModelIndex &index, int role) const override; QVariant data(const QModelIndex &index, int role) const override;
@ -68,6 +84,10 @@ signals:
void vorderChanged(); void vorderChanged();
void backgroundChanged(); void backgroundChanged();
void backgroundTypeChanged(); void backgroundTypeChanged();
void horizontalTextAlignmentChanged();
void verticalTextAlignmentChanged();
void fontChanged();
void fontSizeChanged();
private: private:
int m_id; int m_id;
@ -79,6 +99,10 @@ private:
QString m_vorder; QString m_vorder;
QString m_background; QString m_background;
QString m_backgroundType; QString m_backgroundType;
QString m_horizontalTextAlignment;
QString m_verticalTextAlignment;
QString m_font;
int m_fontSize;
}; };
#endif //SONGSQLMODEL_H #endif //SONGSQLMODEL_H

View file

@ -156,12 +156,28 @@ void VideoSqlModel::updateFilePath(const int &row, const QUrl &filePath) {
emit filePathChanged(); emit filePathChanged();
} }
QVariantList VideoSqlModel::getVideo(const int &row) { QVariantMap VideoSqlModel::getVideo(const int &row) {
qDebug() << "Row we are getting is " << row; // qDebug() << "Row we are getting is " << row;
QVariantList video; // QVariantList video;
QSqlRecord rec = record(row); // QSqlRecord rec = record(row);
qDebug() << rec.value("title"); // qDebug() << rec.value("title");
video.append(rec.value("title")); // video.append(rec.value("title"));
video.append(rec.value("filePath")); // video.append(rec.value("filePath"));
return video; // return video;
QVariantMap data;
const QModelIndex idx = this->index(row,0);
// qDebug() << idx;
if( !idx.isValid() )
return data;
const QHash<int,QByteArray> rn = roleNames();
// qDebug() << rn;
QHashIterator<int,QByteArray> it(rn);
while (it.hasNext()) {
it.next();
qDebug() << it.key() << ":" << it.value();
data[it.value()] = idx.data(it.key());
}
return data;
} }

View file

@ -31,7 +31,7 @@ public:
Q_INVOKABLE void newVideo(const QUrl &filePath); Q_INVOKABLE void newVideo(const QUrl &filePath);
Q_INVOKABLE void deleteVideo(const int &row); 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; QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
@ -46,4 +46,4 @@ private:
QUrl m_filePath; QUrl m_filePath;
}; };
#endif //SONGSQLMODEL_H #endif //VIDEOSQLMODEL_H