Making some more functional ui

This commit is contained in:
Chris Cochrun 2022-02-18 16:53:27 -06:00
parent ef1cb70d10
commit 2496f6708a
15 changed files with 218 additions and 236 deletions

View file

@ -4,8 +4,8 @@ target_sources(presenter
PRIVATE
main.cpp resources.qrc
songlistmodel.cpp songlistmodel.h
mpvobject.h mpvobject.cpp
qthelper.hpp mpvhelpers.h
mpv/mpvobject.h mpv/mpvobject.cpp
mpv/qthelper.hpp mpv/mpvhelpers.h
)
target_link_libraries(presenter

View file

@ -20,7 +20,7 @@
#include <QtQuick/QQuickView>
#include "songlistmodel.h"
#include "mpvobject.h"
#include "mpv/mpvobject.h"
int main(int argc, char *argv[])
{
@ -59,147 +59,3 @@ int main(int argc, char *argv[])
return app.exec();
}
// namespace
// {
// void on_mpv_events(void *ctx)
// {
// Q_UNUSED(ctx)
// }
// void on_mpv_redraw(void *ctx)
// {
// MpvObject::on_update(ctx);
// }
// static void *get_proc_address_mpv(void *ctx, const char *name)
// {
// Q_UNUSED(ctx)
// QOpenGLContext *glctx = QOpenGLContext::currentContext();
// if (!glctx) return nullptr;
// return reinterpret_cast<void *>(glctx->getProcAddress(QByteArray(name)));
// }
// }
// class MpvRenderer : public QQuickFramebufferObject::Renderer
// {
// MpvObject *obj;
// public:
// MpvRenderer(MpvObject *new_obj)
// : obj{new_obj}
// {
// mpv_set_wakeup_callback(obj->mpv, on_mpv_events, nullptr);
// }
// virtual ~MpvRenderer()
// {}
// // This function is called when a new FBO is needed.
// // This happens on the initial frame.
// QOpenGLFramebufferObject * createFramebufferObject(const QSize &size)
// {
// // init mpv_gl:
// if (!obj->mpv_gl)
// {
// mpv_opengl_init_params gl_init_params{get_proc_address_mpv, nullptr, nullptr};
// mpv_render_param params[]{
// {MPV_RENDER_PARAM_API_TYPE, const_cast<char *>(MPV_RENDER_API_TYPE_OPENGL)},
// {MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_init_params},
// {MPV_RENDER_PARAM_INVALID, nullptr}
// };
// if (mpv_render_context_create(&obj->mpv_gl, obj->mpv, params) < 0)
// throw std::runtime_error("failed to initialize mpv GL context");
// mpv_render_context_set_update_callback(obj->mpv_gl, on_mpv_redraw, obj);
// }
// return QQuickFramebufferObject::Renderer::createFramebufferObject(size);
// }
// void render()
// {
// obj->window()->resetOpenGLState();
// QOpenGLFramebufferObject *fbo = framebufferObject();
// mpv_opengl_fbo mpfbo{.fbo = static_cast<int>(fbo->handle()), .w = fbo->width(), .h = fbo->height(), .internal_format = 0};
// int flip_y{0};
// mpv_render_param params[] = {
// // Specify the default framebuffer (0) as target. This will
// // render onto the entire screen. If you want to show the video
// // in a smaller rectangle or apply fancy transformations, you'll
// // need to render into a separate FBO and draw it manually.
// {MPV_RENDER_PARAM_OPENGL_FBO, &mpfbo},
// // Flip rendering (needed due to flipped GL coordinate system).
// {MPV_RENDER_PARAM_FLIP_Y, &flip_y},
// {MPV_RENDER_PARAM_INVALID, nullptr}
// };
// // See render_gl.h on what OpenGL environment mpv expects, and
// // other API details.
// mpv_render_context_render(obj->mpv_gl, params);
// obj->window()->resetOpenGLState();
// }
// };
// MpvObject::MpvObject(QQuickItem * parent)
// : QQuickFramebufferObject(parent), mpv{mpv_create()}, mpv_gl(nullptr)
// {
// if (!mpv)
// throw std::runtime_error("could not create mpv context");
// mpv_set_option_string(mpv, "terminal", "yes");
// mpv_set_option_string(mpv, "msg-level", "all=v");
// if (mpv_initialize(mpv) < 0)
// throw std::runtime_error("could not initialize mpv context");
// // Request hw decoding, just for testing.
// mpv::qt::set_option_variant(mpv, "hwdec", "auto");
// connect(this, &MpvObject::onUpdate, this, &MpvObject::doUpdate,
// Qt::QueuedConnection);
// }
// MpvObject::~MpvObject()
// {
// if (mpv_gl) // only initialized if something got drawn
// {
// mpv_render_context_free(mpv_gl);
// }
// mpv_terminate_destroy(mpv);
// }
// void MpvObject::on_update(void *ctx)
// {
// MpvObject *self = (MpvObject *)ctx;
// emit self->onUpdate();
// }
// // connected to onUpdate(); signal makes sure it runs on the GUI thread
// void MpvObject::doUpdate()
// {
// update();
// }
// void MpvObject::command(const QVariant& params)
// {
// mpv::qt::command_variant(mpv, params);
// }
// void MpvObject::setProperty(const QString& name, const QVariant& value)
// {
// mpv::qt::set_property_variant(mpv, name, value);
// }
// QQuickFramebufferObject::Renderer *MpvObject::createRenderer() const
// {
// window()->setPersistentOpenGLContext(true);
// window()->setPersistentSceneGraph(true);
// return new MpvRenderer(const_cast<MpvObject *>(this));
// }

View file

@ -0,0 +1,120 @@
import QtQuick 2.13
import QtQuick.Layouts 1.2
import org.kde.kirigami 2.13 as Kirigami
Item {
id: root
/**
* listItem: Item
* The id of the delegate that we want to drag around, which *must*
* be a child of the actual ListView's delegate
*/
property Item listItem
/**
* listView: Listview
* The id of the ListView the delegates belong to.
*/
property ListView listView
/**
* Emitted when the drag handle wants to move the item in the model
* The following example does the move in the case a ListModel is used
* @code
* onMoveRequested: listModel.move(oldIndex, newIndex, 1)
* @endcode
* @param oldIndex the index the item is currently at
* @param newIndex the index we want to move the item to
*/
signal moveRequested(int oldIndex, int newIndex)
/**
* Emitted when the drag operation is complete and the item has been
* dropped in the new final position
*/
signal dropped()
MouseArea {
id: mouseArea
anchors.fill: parent
drag {
target: listItem
axis: Drag.YAxis
minimumY: 0
maximumY: listView.height - listItem.height
}
/* cursorShape: pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor */
property int startY
property int mouseDownY
property Item originalParent
property int autoScrollThreshold: (listView.contentHeight > listView.height) ? listItem.height * 3 : 0
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);
if (Math.abs(listItem.y - mouseArea.startY) > height && newIndex > -1 && newIndex !== index) {
root.moveRequested(index, newIndex);
}
}
preventStealing: true
onPressed: {
mouseArea.originalParent = listItem.parent;
listItem.parent = listView;
listItem.y = mouseArea.originalParent.mapToItem(listItem.parent, listItem.x, listItem.y).y;
mouseArea.originalParent.z = 99;
mouseArea.startY = listItem.y;
mouseArea.mouseDownY = mouse.y;
}
onPositionChanged: {
if (!pressed) {
return;
}
mouseArea.arrangeItem();
scrollTimer.interval = 500 * Math.max(0.1, (1-Math.max(mouseArea.autoScrollThreshold - listItem.y, listItem.y - listView.height + mouseArea.autoScrollThreshold + listItem.height) / mouseArea.autoScrollThreshold));
scrollTimer.running = (listItem.y < mouseArea.autoScrollThreshold ||
listItem.y > listView.height - mouseArea.autoScrollThreshold);
}
onReleased: {
listItem.y = mouseArea.originalParent.mapFromItem(listItem, 0, 0).y;
listItem.parent = mouseArea.originalParent;
dropAnimation.running = true;
scrollTimer.running = false;
root.dropped();
}
onCanceled: released()
SequentialAnimation {
id: dropAnimation
YAnimator {
target: listItem
from: listItem.y
to: 0
duration: Kirigami.Units.longDuration
easing.type: Easing.InOutQuad
}
PropertyAction {
target: listItem.parent
property: "z"
value: 0
}
}
Timer {
id: scrollTimer
interval: 500
repeat: true
onTriggered: {
if (listItem.y < mouseArea.autoScrollThreshold) {
listView.contentY = Math.max(0, listView.contentY - Kirigami.Units.gridUnit)
} else {
listView.contentY = Math.min(listView.contentHeight - listView.height, listView.contentY + Kirigami.Units.gridUnit)
}
mouseArea.arrangeItem();
}
}
}
}

View file

@ -6,10 +6,13 @@ import QtQuick.Layouts 1.2
import QtMultimedia 5.15
import QtAudioEngine 1.15
import org.kde.kirigami 2.13 as Kirigami
import "./" as Presenter
ColumnLayout {
id: root
property var selectedItem: serviceItemList.selected
Rectangle {
id: headerBackground
color: Kirigami.Theme.backgroundColor
@ -31,20 +34,58 @@ ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
model: listModel
delegate: itemDelegate
delegate: Kirigami.DelegateRecycler {
width: serviceItemList.width
sourceComponent: itemDelegate
}
clip: true
spacing: 2
addDisplaced: Transition {
NumberAnimation {properties: "x, y"; duration: 100}
}
moveDisplaced: Transition {
NumberAnimation { properties: "x, y"; duration: 100 }
}
remove: Transition {
NumberAnimation { properties: "x, y"; duration: 100 }
NumberAnimation { properties: "opacity"; duration: 100 }
}
removeDisplaced: Transition {
NumberAnimation { properties: "x, y"; duration: 100 }
}
displaced: Transition {
NumberAnimation {properties: "x, y"; duration: 100}
}
Component {
id: itemDelegate
Kirigami.BasicListItem {
id: serviceItem
width: serviceItemList.width
height:50
label: itemName
subtitle: type
hoverEnabled: true
backgroundColor: serviceDrop.containsDrag ? Kirigami.Theme.highlightColor : Kirigami.Theme.viewBackgroundColor
onClicked: serviceItemList.currentIndex = index
Presenter.DragHandle {
listItem: serviceItem
listView: serviceItemList
onMoveRequested: listModel.move(oldIndex, newIndex, 1)
anchors.fill: parent
}
DropArea {
id: serviceDrop
Layout.fillHeight: true
Layout.fillWidth: true
onEntered: showPassiveNotification(drag.source + " has entered")
onDropped: listModel.append(drag.source)
}
}
}
@ -70,71 +111,8 @@ ColumnLayout {
itemName: "Marvelous Light"
type: "song"
}
ListElement {
itemName: "10 reason to use church presenter"
type: "video"
}
ListElement {
itemName: "10,000 Reason"
type: "song"
}
ListElement {
itemName: "Marvelous Light"
type: "song"
}
ListElement {
itemName: "10 reason to use church presenter"
type: "video"
}
ListElement {
itemName: "10,000 Reason"
type: "song"
}
ListElement {
itemName: "Marvelous Light"
type: "song"
}
ListElement {
itemName: "10 reason to use church presenter"
type: "video"
}
ListElement {
itemName: "10,000 Reason"
type: "song"
}
ListElement {
itemName: "Marvelous Light"
type: "song"
}
ListElement {
itemName: "10 reason to use church presenter"
type: "video"
}
ListElement {
itemName: "10,000 Reason"
type: "song"
}
ListElement {
itemName: "Marvelous Light"
type: "song"
}
ListElement {
itemName: "10 reason to use church presenter"
type: "video"
}
ListElement {
itemName: "10,000 Reason"
type: "song"
}
ListElement {
itemName: "Marvelous Light"
type: "song"
}
ListElement {
itemName: "10 reason to use church presenter"
type: "video"
}
}
}
}
}

View file

@ -78,6 +78,7 @@ Item {
Component {
id: itemDelegate
Kirigami.BasicListItem {
id: songListItem
width: ListView.view.width
height:40
label: title
@ -91,6 +92,34 @@ Item {
songAuthor = author
showPassiveNotification(songLyrics, 3000)
}
Drag.active: dragHandler.drag.active
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
MouseArea {
id: dragHandler
anchors.fill: parent
drag {
target: songListItem
onActiveChanged: {
if (dragHandler.drag.active) {
draggedLibraryItem = songLibraryList.currentItem
showPassiveNotification(index)
}
}
}
}
states: State {
name: "dragged"
when: songListItem.Drag.active
PropertyChanges {
target: songListItem
x: x
y: y
}
}
}
}

View file

@ -18,6 +18,7 @@ Controls.Page {
property string songLyrics: ""
property string songAuthor: ""
property int blurRadius: 0
property var draggedLibraryItem
Item {
id: mainItem
@ -113,7 +114,7 @@ Controls.Page {
nameFilters: ["Image files (*.jpg *.jpeg *.png *.JPG *.JPEG *.PNG)"]
onAccepted: {
videoBackground = ""
imageBackground = imageFileDialog.fileUrls
imageBackground = imageFileDialog.fileUrls[0]
}
onRejected: {
print("Canceled")

View file

@ -12,7 +12,7 @@ Item {
id: root
anchors.fill: parent
property real textSize: 50
property real textSize: 26
property bool editMode: false
property bool dropShadow: false
property url imageSource: imageBackground
@ -83,7 +83,7 @@ Item {
source: imageSource
fillMode: Image.PreserveAspectCrop
clip: true
visible: false
visible: true
}
@ -100,19 +100,18 @@ Item {
font.family: chosenFont
style: Text.Raised
anchors.centerIn: parent
clip: true
layer.enabled: true
layer.effect: DropShadow {
horizontalOffset: 5
verticalOffset: 5
radius: 11.0
samples: 24
color: "#80000000"
}
}
DropShadow {
id: textDropShadow
source: lyrics
anchors.fill: lyrics
horizontalOffset: 3
verticalOffset: 3
radius: 8.0
samples: 17
color: "#80000000"
visible: true
}
}
}
}

View file

@ -109,9 +109,8 @@ Item {
id: slideBar
color: Kirigami.Theme.highlightColor
Layout.preferredWidth: 400
Layout.preferredWidth: 700
Layout.preferredHeight: songTitleField.height
Layout.fillWidth: true
Layout.rightMargin: 20
}
@ -140,12 +139,11 @@ Item {
Presenter.SlideEditor {
id: slideEditor
Layout.preferredHeight: 800
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: 700
Layout.preferredHeight: 394
Layout.bottomMargin: 30
Layout.rightMargin: 20
Layout.rowSpan: 15
Layout.rowSpan: 2
}
Controls.TextField {

View file

@ -14,6 +14,7 @@
<file>qml/presenter/SongEditor.qml</file>
<file>qml/presenter/Slide.qml</file>
<file>qml/presenter/SlideEditor.qml</file>
<file>qml/presenter/DragHandle.qml</file>
<file>assets/parallel.jpg</file>
</qresource>
</RCC>

View file

@ -10,7 +10,7 @@ SongListModel::SongListModel(QObject *parent)
<< Data("Marvelous Light", "Into marvelous light I'm running", "Chris Tomlin", "13470183", "")
<< Data("10,000 Reasons", "10,000 reasons for my heart to sing", "Matt Redman", "13470183", "")
<< Data("Marvelous Light", "Into marvelous light I'm running", "Chris Tomlin", "13470183", "")
<< Data("10,000 Reasons", "10,000 reasons for my heart to sing", "Matt Redman", "13470183", "")
<< Data("River", "I'm going down to the river", "Jordan Feliz", "13470183", "")
<< Data("Marvelous Light", "Into marvelous light I'm running", "Chris Tomlin", "13470183", "")
<< Data("10,000 Reasons", "10,000 reasons for my heart to sing", "Matt Redman", "13470183", "")
<< Data("Marvelous Light", "Into marvelous light I'm running", "Chris Tomlin", "13470183", "")