1
0
mirror of https://gitlab.com/kelteseth/ScreenPlay.git synced 2024-11-25 12:13:00 +01:00

WIP: Replace Sidebar with bottom drawer with timeline

This commit is contained in:
Elias Steurer 2024-03-28 12:21:11 +01:00
parent c1a81588d9
commit f9cafc891b
18 changed files with 511 additions and 454 deletions

View File

@ -61,7 +61,7 @@ set(VCPKG_BIN_PATH "${VCPKG_INSTALLED_PATH}/bin")
# Godot Editor
set(GODOT_VERSION_MAJOR "4")
set(GODOT_VERSION_MINOR "2")
set(GODOT_VERSION_PATCH "")
set(GODOT_VERSION_PATCH "1")
set(GODOT_RELEASE_TYPE "stable")
# Use an if statement to check if GODOT_VERSION_PATCH is empty or not

View File

@ -1,21 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<body>
<script>
let body = document.getElementsByTagName('body')[0];
body.setAttribute('style', 'margin: 0px;padding: 0px;overflow: hidden;background:black;');
let html = document.getElementsByTagName('html')[0];
html.setAttribute('style', 'margin: 0px;padding: 0px;overflow: hidden;background:black;');
var videoElem = document.createElement('video');
videoElem.setAttribute('width', '100%');
videoElem.setAttribute('height', '100%');
videoElem.setAttribute('id', 'video');
videoElem.muted = true;
videoElem.autoplay = true;
videoElem.loop = true;
document.body.appendChild(videoElem);
</script>
</body>
</html>

View File

@ -13,6 +13,7 @@
#include "monitorlistmodel.h"
#include "profilelistmodel.h"
#include "projectsettingslistmodel.h"
#include "screenplayprofiles.h"
#include "screenplaywallpaper.h"
#include "screenplaywidget.h"
#include "settings.h"
@ -21,6 +22,21 @@
namespace ScreenPlay {
struct Wallpaper {
QString name;
QTime startTime;
QTime endTime;
QString absolutePath;
QString previewImage;
float playbackRate;
float volume;
QString file;
QJsonObject properties;
ContentTypes::InstalledType type;
Video::FillMode fillMode;
QVector<int> monitors;
};
class ScreenPlayManager : public QObject {
Q_OBJECT
QML_ELEMENT
@ -139,11 +155,14 @@ public slots:
private:
bool loadProfiles();
bool loadWidgetConfig(const QJsonObject& widget);
bool loadWallpaperConfig(const QJsonObject& wallpaper);
bool checkIsAnotherScreenPlayInstanceRunning();
bool removeWallpaper(const QString& appID);
bool removeWidget(const QString& appID);
private:
ScreenPlayProfiles m_screenPlayProfiles;
std::shared_ptr<GlobalVariables> m_globalVariables;
std::shared_ptr<MonitorListModel> m_monitorListModel;
std::shared_ptr<Settings> m_settings;
@ -161,6 +180,6 @@ private:
QTimer m_saveLimiter;
const quint16 m_webSocketPort = 16395;
bool loadTimelineWallpaperConfig(const QJsonObject& wallpaperObj);
};
}

View File

@ -17,7 +17,7 @@ TabButton {
ToolButton {
icon.source: control.icon.source
icon.color: control.checked ? Material.accentColor : Material.secondaryTextColor
icon.color: control.checked ? Material.accentColor : "transparent"
hoverEnabled: false
icon.width: 16
icon.height: 16
@ -49,6 +49,7 @@ TabButton {
opacity: 0.6
width: parent.width * 0.2
icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_open_in_new.svg"
icon.color: "transparent"
icon.width: 16
icon.height: 16
onClicked: Qt.openUrlExternally(control.openLink)

View File

@ -14,7 +14,7 @@ Rectangle {
Text {
id: text
color: "white"
text: root.index
//text: root.index
anchors{
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom

View File

@ -4,11 +4,11 @@ import QtQuick.Layouts
Control {
id: root
height: 220
height: 160
implicitWidth: 800
topPadding: 40
leftPadding: 40
rightPadding: 40
topPadding: 20
leftPadding: 20
rightPadding: 20
contentItem: Item {
id: timeLine

View File

@ -335,7 +335,7 @@ Item {
modalSource: root.modalSource
anchors.centerIn: Overlay.overlay
onAccepted: {
root.sidebar.clear()
root.sidebar.close()
if (!App.installedListModel.deinstallItemAt(
contextMenu.absoluteStoragePath)) {
console.error("Unable to uninstall item",

View File

@ -9,11 +9,14 @@ import ScreenPlayApp
import ScreenPlayUtil
import "../Monitors"
import "../Components"
Item {
Drawer {
id: root
property real navHeight
height: 500
modal: false
edge: Qt.BottomEdge
property bool hasPreviewGif: false
property var type: ContentTypes.InstalledType.QMLWallpaper
property string contentFolderName
@ -29,15 +32,13 @@ Item {
// This is used for removing wallpaper. We need to clear
// the preview image/gif so we can release the file for deletion.
function clear() {
root.close()
imagePreview.source = ""
animatedImagePreview.source = ""
txtHeadline.text = ""
root.state = "inactive"
sidebarWrapper.state = "inactive"
}
width: 400
state: "inactive"
property bool hasPreviewGif: false
onContentFolderNameChanged: {
const item = App.installedListModel.get(root.contentFolderName)
txtHeadline.text = item.m_title
@ -64,97 +65,109 @@ Item {
function onSetSidebarItem(folderName, type) {
// Toggle sidebar if clicked on the same content twice
if (root.contentFolderName === folderName
&& root.state !== "inactive") {
root.state = "inactive"
if (root.contentFolderName === folderName && root.opened) {
root.close()
return
}
root.contentFolderName = folderName
root.type = type
if (App.util.isWallpaper(root.type)) {
if (type === ContentTypes.InstalledType.VideoWallpaper)
root.state = "activeWallpaper"
sidebarWrapper.state = "wallpaper"
else
root.state = "activeScene"
sidebarWrapper.state = "scene"
btnLaunchContent.text = qsTr("Set Wallpaper")
} else {
root.state = "activeWidget"
sidebarWrapper.state = "widget"
btnLaunchContent.text = qsTr("Set Widget")
}
root.open()
}
target: App.util
}
MouseHoverBlocker {}
Rectangle {
anchors.fill: parent
color: Material.theme === Material.Light ? "white" : Material.background
opacity: 0.95
layer.enabled: true
layer.effect: ElevationEffect {
elevation: 4
}
}
Item {
RowLayout {
id: sidebarWrapper
state: "inactive"
spacing: 20
anchors.fill: parent
Item {
id: navBackground
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: 10
spacing: 5
height: navHeight
ColumnLayout {
spacing: 5
Layout.fillWidth: true
Layout.preferredHeight: 160
anchors {
top: parent.top
right: parent.right
left: parent.left
Text {
Layout.leftMargin: 20
text: qsTr("Select the duration your wallpaper should be visible")
font.family: App.settings.font
verticalAlignment: Text.AlignVCenter
font.pointSize: 11
color: Material.secondaryTextColor
}
Rectangle {
anchors.fill: parent
gradient: Gradient {
GradientStop {
position: 0
color: "transparent"
Timeline {
Layout.fillWidth: true
Layout.fillHeight: true
}
GradientStop {
position: 0.1
color: "#AAffffff"
}
GradientStop {
position: 1
color: "#ffffff"
}
RowLayout {
spacing: 10
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.horizontalStretchFactor: 3
spacing: 10
Text {
text: qsTr("Select a Monitor to display the content")
font.family: App.settings.font
verticalAlignment: Text.AlignVCenter
font.pointSize: 11
color: Material.secondaryTextColor
}
MonitorSelection {
id: monitorSelection
objectName: "monitorSelection"
height: 200
Layout.fillWidth: true
availableWidth: width
availableHeight: height
fontSize: 11
}
}
Item {
id: sidebarBackground
anchors {
top: navBackground.bottom
right: parent.right
bottom: parent.bottom
left: parent.left
Layout.fillWidth: true
Layout.fillHeight: true
Layout.horizontalStretchFactor: 1
}
}
}
ColumnLayout {
Layout.preferredWidth: 260
Layout.fillHeight: true
spacing: 10
Rectangle {
id: imageWrapper
height: 237
color: "#2b2b2b"
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
Layout.preferredHeight: 180
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
// Do NOT enable async image loading!
// Otherwhise it will still hold the file
@ -220,62 +233,12 @@ Item {
left: parent.left
}
}
MouseArea {
id: button
height: 50
width: 50
anchors.top: parent.top
anchors.left: parent.left
cursorShape: Qt.PointingHandCursor
onClicked: root.state = "inactive"
Image {
id: imgBack
source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_arrow_right.svg"
sourceSize: Qt.size(15, 15)
fillMode: Image.PreserveAspectFit
anchors.centerIn: parent
}
}
}
ColumnLayout {
spacing: 20
anchors {
top: imageWrapper.bottom
right: parent.right
left: parent.left
margins: 30
}
ColumnLayout {
Layout.fillWidth: true
Text {
id: txtHeadlineMonitor
height: 20
text: qsTr("Select a Monitor to display the content")
font.family: App.settings.font
verticalAlignment: Text.AlignVCenter
font.pointSize: 11
color: Material.secondaryTextColor
}
MonitorSelection {
id: monitorSelection
objectName: "monitorSelection"
height: 180
Layout.fillWidth: true
availableWidth: width
availableHeight: height
fontSize: 11
}
}
Layout.alignment: Qt.AlignTop
LabelSlider {
id: sliderVolume
@ -365,14 +328,13 @@ Item {
const absoluteStoragePath = item.m_absoluteStoragePath
const previewImage = item.m_preview
if (App.util.isWallpaper(root.type)) {
if (type === ContentTypes.InstalledType.GodotWallpaper){
if(!App.globalVariables.isBasicVersion()) {
root.state = "inactive"
if (type === ContentTypes.InstalledType.GodotWallpaper) {
if (App.globalVariables.isBasicVersion()) {
sidebarWrapper.state = "inactive"
return
}
}
let activeMonitors = monitorSelection.getActiveMonitors(
)
// TODO Alert user to choose a monitor
@ -404,7 +366,7 @@ Item {
1, {}, true)
}
})
root.state = "inactive"
root.close()
return
}
@ -420,28 +382,30 @@ Item {
absoluteStoragePath,
previewImage, {},
true)
root.state = "inactive"
root.close()
monitorSelection.reset()
}
anchors {
bottom: parent.bottom
bottomMargin: 20
horizontalCenter: parent.horizontalCenter
}
}
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
}
}
ToolButton {
id: button
Layout.alignment: Qt.AlignTop
icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_close.svg"
icon.width: 15
icon.height: 15
onClicked: {
root.close()
sidebarWrapper.state = "inactive"
}
}
states: [
State {
name: "inactive"
PropertyChanges {
target: root
anchors.rightMargin: -root.width
}
PropertyChanges {
target: imagePreview
opacity: 0
@ -484,7 +448,7 @@ Item {
}
},
State {
name: "activeWallpaper"
name: "wallpaper"
PropertyChanges {
target: imagePreview
@ -515,7 +479,7 @@ Item {
}
},
State {
name: "activeScene"
name: "scene"
PropertyChanges {
target: imagePreview
@ -557,26 +521,12 @@ Item {
property: "anchors.topMargin"
duration: 400
}
NumberAnimation {
target: root
properties: "anchors.rightMargin"
duration: 250
easing.type: Easing.OutQuart
}
},
Transition {
to: "activeWidget"
to: "widget"
from: "*"
SequentialAnimation {
NumberAnimation {
target: root
properties: "anchors.rightMargin"
duration: 250
easing.type: Easing.OutQuart
}
ParallelAnimation {
NumberAnimation {
targets: [animatedImagePreview, imagePreview]
@ -593,17 +543,10 @@ Item {
}
},
Transition {
to: "activeWallpaper"
to: "wallpaper"
from: "*"
SequentialAnimation {
NumberAnimation {
target: root
properties: "anchors.rightMargin"
duration: 250
easing.type: Easing.OutQuart
}
ParallelAnimation {
NumberAnimation {
targets: [animatedImagePreview, imagePreview]
@ -620,16 +563,9 @@ Item {
}
},
Transition {
to: "activeScene"
to: "scene"
SequentialAnimation {
NumberAnimation {
target: root
properties: "anchors.rightMargin"
duration: 250
easing.type: Easing.OutQuart
}
ParallelAnimation {
NumberAnimation {
targets: [animatedImagePreview, imagePreview]
@ -646,4 +582,5 @@ Item {
}
}
]
}
}

View File

@ -40,7 +40,7 @@ Item {
"modalSource": content
});
nav.setNavigation(name);
sidebar.state = "inactive";
sidebar.close()
}
Navigation.ExitPopup {
@ -147,9 +147,9 @@ Item {
Connections {
function onSetSidebarActive(active) {
if (active)
sidebar.state = "active";
sidebar.open()
else
sidebar.state = "inactive";
sidebar.close()
}
function onSetNavigationItem(pos) {
@ -166,13 +166,7 @@ Item {
Installed.Sidebar {
id: sidebar
objectName: "installedSidebar"
navHeight: nav.height
anchors {
top: parent.top
right: parent.right
bottom: parent.bottom
}
width: content.width
}
Navigation.Navigation {

View File

@ -69,6 +69,7 @@ Rectangle {
}
function resize() {
print("resize")
var absoluteDesktopSize = App.monitorListModel.absoluteDesktopSize();
var isWidthGreaterThanHeight = false;
var windowsDelta = 0;
@ -161,4 +162,23 @@ Rectangle {
snapMode: ScrollBar.SnapOnRelease
}
}
ToolButton {
onClicked: App.monitorListModel.reset()
icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/font-awsome/rotate-right-solid.svg"
icon.width: 14
icon.height: 14
opacity: hovered ? 1 : .4
hoverEnabled: true
Behavior on opacity {
NumberAnimation {
duration: 250
}
}
anchors {
top: parent.top
right: parent.right
}
}
}

View File

@ -22,7 +22,7 @@ GlobalVariables::GlobalVariables(QObject* parent)
} else {
setVersion(GlobalVariables::Version::OpenSourceStandalone);
}
setVersion(GlobalVariables::Version::OpenSourceUltraSteam);
setVersion(GlobalVariables::Version::OpenSourceStandalone);
setLocalSettingsPath(QUrl { QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) });
}

View File

@ -565,13 +565,51 @@ bool ScreenPlayManager::loadProfiles()
}
}
for (QJsonValueRef wallpaper : wallpaper.toObject().value("wallpaper").toArray()) {
// for (QJsonValueRef wallpaper : wallpaper.toObject().value("wallpaper").toArray()) {
// QJsonObject wallpaperObj = wallpaper.toObject();
// if (!loadWallpaperConfig(wallpaperObj))
// containsInvalidData = true;
// }
for (QJsonValueRef timelineWallpaper : wallpaper.toObject().value("timelineWallpaper").toArray()) {
QJsonObject wallpaperObj = wallpaper.toObject();
if (!loadTimelineWallpaperConfig(wallpaperObj))
containsInvalidData = true;
}
for (const QJsonValueRef widget : wallpaper.toObject().value("widgets").toArray()) {
QJsonObject widgetObj = widget.toObject();
if (!loadWidgetConfig(widgetObj))
containsInvalidData = true;
}
}
// The can happen if the user unpluggs a wallpaper but it still exists
// in the settings.json. For this we save all profiles with now active
// content.
if (containsInvalidData)
saveProfiles();
return true;
}
bool ScreenPlayManager::loadTimelineWallpaperConfig(const QJsonObject& wallpaperObj)
{
const QString name = wallpaperObj.value("name").toString();
const QString timeFormat = "hh:mm::ss";
const QTime startTime = QTime::fromString(wallpaperObj.value("startTime").toString(), timeFormat);
const QTime endTime = QTime::fromString(wallpaperObj.value("endTime").toString(), timeFormat);
if (!loadWallpaperConfig(wallpaperObj))
return false;
return true;
}
bool ScreenPlayManager::loadWallpaperConfig(const QJsonObject& wallpaperObj)
{
if (wallpaperObj.empty())
continue;
return false;
QJsonArray monitorsArray = wallpaper.toObject().value("monitors").toArray();
QJsonArray monitorsArray = wallpaperObj.value("monitors").toArray();
QVector<int> monitors;
for (const QJsonValueRef monitorNumber : monitorsArray) {
@ -608,15 +646,15 @@ bool ScreenPlayManager::loadProfiles()
if (!success) {
qWarning() << "Unable to start Wallpaper! " << type << fillMode << monitors << absolutePath;
containsInvalidData = true;
}
return false;
}
return true;
}
for (const QJsonValueRef widget : wallpaper.toObject().value("widgets").toArray()) {
QJsonObject widgetObj = widget.toObject();
bool ScreenPlayManager::loadWidgetConfig(const QJsonObject& widgetObj)
{
if (widgetObj.empty())
continue;
return false;
const QString absolutePath = widgetObj.value("absolutePath").toString();
const QString previewImage = widgetObj.value("previewImage").toString();
@ -631,17 +669,8 @@ bool ScreenPlayManager::loadProfiles()
if (!success) {
qWarning() << "Unable to start Widget! " << type << position << absolutePath;
containsInvalidData = true;
return false;
}
}
}
// The can happen if the user unpluggs a wallpaper but it still exists
// in the settings.json. For this we save all profiles with now active
// content.
if (containsInvalidData)
saveProfiles();
return true;
}
}

View File

@ -0,0 +1,68 @@
{
"profiles": [
{
"appdrawer": [],
"name": "default",
"wallpaper": [
{
"name": "default",
"absolutePath": "C:/Program Files (x86)/Steam/steamapps/workshop/content/672870/05012024134225",
"file": "2_15488489005711.mp4",
"fillMode": "Contain",
"isLooping": true,
"monitors": [
0
],
"playbackRate": 1,
"previewImage": "previewThumbnail.jpg",
"type": "VideoWallpaper",
"volume": 0.6299999952316284
}
],
"timelineWallpaper": [
{
"name": "test",
"startTime": "00:00:00",
"endTime": "12:00:00",
"wallpaper": [
{
"absolutePath": "C:/Program Files (x86)/Steam/steamapps/workshop/content/672870/05012024134225",
"file": "2_15488489005711.mp4",
"fillMode": "Contain",
"isLooping": true,
"monitors": [
0
],
"playbackRate": 1,
"previewImage": "previewThumbnail.jpg",
"type": "VideoWallpaper",
"volume": 0.6299999952316284
}
]
},
{
"name": "test",
"startTime": "12:01",
"endTime": "24:00",
"wallpaper": [
{
"absolutePath": "C:/Program Files (x86)/Steam/steamapps/workshop/content/672870/05012024134225",
"file": "2_15488489005711.mp4",
"fillMode": "Contain",
"isLooping": true,
"monitors": [
0
],
"playbackRate": 1,
"previewImage": "previewThumbnail.jpg",
"type": "VideoWallpaper",
"volume": 0.6299999952316284
}
]
}
],
"widgets": []
}
],
"version": "1.0.0"
}

View File

@ -16,6 +16,11 @@ Rectangle {
height: 50
color: Material.theme === Material.Light ? "white" : Material.background
// Block input to underlying installed item
MouseArea {
anchors.fill: parent
}
Item {
id: wrapper

View File

@ -34,8 +34,8 @@ Item {
Rotation {
id: rt
origin.x: width * 0.5
origin.y: height * 0.5
origin.x: screenPlayItem.width * 0.5
origin.y: screenPlayItem.height * 0.5
angle: 0
axis {
@ -50,15 +50,15 @@ Item {
Scale {
id: sc
origin.x: width * 0.5
origin.y: height * 0.5
origin.x: screenPlayItem.width * 0.5
origin.y: screenPlayItem.height * 0.5
}
]
Timer {
id: timerAnim
interval: 40 * itemIndex * Math.random()
interval: 40 * screenPlayItem.itemIndex * Math.random()
running: true
repeat: false
onTriggered: showAnim.start()
@ -151,7 +151,7 @@ Item {
id: screenPlayItemImage
anchors.fill: parent
sourceImage: Qt.resolvedUrl(screenPlayItem.absoluteStoragePath + "/" + screenPreview)
sourceImage: Qt.resolvedUrl(screenPlayItem.absoluteStoragePath + "/" + screenPlayItem.screenPreview)
}
Image {
@ -193,17 +193,17 @@ Item {
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
onEntered: {
if (!hasMenuOpen)
if (!screenPlayItem.hasMenuOpen)
screenPlayItem.state = "hover";
}
onExited: {
if (!hasMenuOpen)
if (!screenPlayItem.hasMenuOpen)
screenPlayItem.state = "visible";
}
onClicked: {
onClicked: function(mouse) {
checkBox.toggle();
if (mouse.button === Qt.LeftButton)
itemClicked(screenId, type, checkBox.checkState === Qt.Checked);
itemClicked(screenPlayItem.folderName, type, checkBox.checkState === Qt.Checked);
}
}
}
@ -213,9 +213,9 @@ Item {
onCheckStateChanged: {
if (checkState == Qt.Checked)
isSelected = true;
screenPlayItem.isSelected = true;
else
isSelected = false;
screenPlayItem.isSelected = false;
}
anchors {

View File

@ -179,6 +179,7 @@ Drawer {
Layout.fillWidth: true
icon.source: "qrc:/qml/ScreenPlayWorkshop/assets/icons/icon_thumb_up.svg"
icon.color: "transparent"
ToolTip.visible: hovered
ToolTip.text: qsTr("Click here if you like the content")
onClicked: {
@ -193,6 +194,7 @@ Drawer {
Layout.fillWidth: true
icon.source: "qrc:/qml/ScreenPlayWorkshop/assets/icons/icon_thumb_down.svg"
icon.color: "transparent"
ToolTip.visible: hovered
ToolTip.text: qsTr("Click here if you do not like the content")
onClicked: {
@ -322,6 +324,7 @@ Drawer {
font.pointSize: 10
icon.source: "qrc:/qml/ScreenPlayWorkshop/assets/icons/icon_open_in_new.svg"
icon.color: "transparent"
height: 25
text: qsTr("Open In Steam")
onClicked: Qt.openUrlExternally("steam://url/CommunityFilePage/" + root.publishedFileID)

View File

@ -168,6 +168,7 @@ Item {
ToolButton {
onClicked: Qt.openUrlExternally("steam://url/CommunityFilePage/" + banner.bannerPublishedFileID)
icon.source: "qrc:/qml/ScreenPlayWorkshop/assets/icons/icon_open_in_new.svg"
icon.color: "transparent"
}
}
}
@ -339,6 +340,7 @@ Item {
ToolButton {
property bool hasContent: tiSearch.text.length > 0
icon.source: hasContent ? "qrc:/qml/ScreenPlayWorkshop/assets/icons/icon_close.svg" : "qrc:/qml/ScreenPlayWorkshop/assets/icons/icon_search.svg"
icon.color: "transparent"
onClicked: {
if (hasContent) {
root.state = "searching";

View File

@ -36,7 +36,7 @@ VCPKG_BASE_PACKAGES = [
]
PYTHON_EXECUTABLE = "python" if sys.platform == "win32" else "python3"
FFMPEG_VERSION = "6.1"
GODOT_VERSION = "4.2"
GODOT_VERSION = "4.2.1"
GODOT_RELEASE_TYPE = "stable"
GODOT_DOWNLOAD_SERVER = "https://github.com/godotengine/godot-builds/releases/download"
if sys.platform == "win32":