Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ffe0bba88 | |||
|
b4a1e1bbf4
|
|||
|
2fab4f209f
|
|||
|
6541cedc67
|
|||
|
f3adc14a6e
|
|||
|
7b25255126
|
|||
|
1aaff0cd02
|
|||
|
d60cdef637
|
|||
|
97c9ba30f0
|
|||
|
6e94484fac
|
|||
|
1d87abd5af
|
|||
|
0402b0902d
|
|||
|
ac65bbb3ab
|
|||
|
45f293a349
|
|||
|
fa3c6354cb
|
|||
|
f23aada9a5
|
|||
|
0fe189bfde
|
|||
|
bb90186932
|
|||
|
61231a890a
|
|||
|
8e95f66dc3
|
|||
|
b8d5670530
|
|||
|
700de7a757
|
|||
|
5ef7f54cfe
|
|||
|
63e45eb4d4
|
|||
|
1d5bcb9365
|
|||
|
e6d7d908b1
|
|||
|
471c91e622
|
|||
|
9e8b6a70c8
|
|||
|
294a38f26a
|
|||
|
a776997119
|
|||
|
f69ba41046
|
|||
|
6b7812fa50
|
|||
|
bb58c8a9d2
|
|||
|
4a7960177d
|
|||
|
9fbf90b0c9
|
|||
|
bb4e3c7562
|
|||
|
7f86e1105d
|
|||
|
0f2e237fe6
|
|||
|
5deb869c2c
|
|||
|
a1d3f20439
|
|||
|
4157e66557
|
|||
|
4182e2d5a7
|
|||
|
a56532a047
|
|||
|
ab0ab4b4fb
|
|||
|
abd653ef1c
|
|||
|
9db9ed67d8
|
|||
|
990457042a
|
|||
|
2a5e66d68c
|
|||
|
1826209521
|
|||
|
3d0655edd1
|
|||
|
69743883d7
|
|||
|
100c6da3cf
|
|||
|
b64834084e
|
|||
|
a58fdfadc6
|
|||
|
da24f34bb4
|
|||
|
e9bc016fbb
|
|||
|
6947a65f75
|
@@ -0,0 +1,3 @@
|
||||
[submodule "KomplexHubPlugin/3rdparty/qtkeychain"]
|
||||
path = KomplexHubPlugin/3rdparty/qtkeychain
|
||||
url = https://github.com/frankosterfeld/qtkeychain.git
|
||||
@@ -0,0 +1,91 @@
|
||||
;This file is used to denote what files have generative content
|
||||
;Format: {file, class, model/version, ["message"]}
|
||||
|
||||
;Kero
|
||||
{
|
||||
{
|
||||
images/kero/kero_build_1.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_build_2.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_build_3.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_build_4.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_build_error.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_choice.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_hello.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_login.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_no_results.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_run_1.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_run_2.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_run_3.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_run_4.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_warning.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_success.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_spotlight.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
},
|
||||
{
|
||||
images/kero/kero_star.png,
|
||||
placeholders,
|
||||
chatgpt5
|
||||
}
|
||||
}
|
||||
+22
-7
@@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.21.1)
|
||||
option(LINK_INSIGHT "Link Qt Insight Tracker library" ON)
|
||||
option(BUILD_QDS_COMPONENTS "Build design studio components" ON)
|
||||
|
||||
project(KomplexHub LANGUAGES CXX)
|
||||
project(KomplexHub VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
@@ -20,13 +20,26 @@ set(QML_IMPORT_PATH ${QT_QML_OUTPUT_DIRECTORY}
|
||||
FORCE
|
||||
)
|
||||
|
||||
find_package(Qt6 6.8 REQUIRED COMPONENTS Core Gui Widgets Qml Quick QuickControls2 QuickTimeline ShaderTools Concurrent)
|
||||
find_package(
|
||||
Qt6
|
||||
REQUIRED COMPONENTS
|
||||
Core
|
||||
Gui
|
||||
Widgets
|
||||
Qml
|
||||
Quick
|
||||
QuickControls2
|
||||
QuickTimeline
|
||||
ShaderTools
|
||||
Concurrent
|
||||
)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(${CMAKE_PROJECT_NAME}
|
||||
README.md
|
||||
LICENSE
|
||||
)
|
||||
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES VERSION "1.0")
|
||||
|
||||
qt_add_resources(${CMAKE_PROJECT_NAME} "configuration"
|
||||
PREFIX "/"
|
||||
FILES
|
||||
@@ -85,14 +98,16 @@ qt_add_resources(${CMAKE_PROJECT_NAME} "app_images"
|
||||
"images/kero/kero_run_3.png"
|
||||
"images/kero/kero_run_4.png"
|
||||
"images/kero/kero_success.png"
|
||||
"images/kero/kero_spotlight.png"
|
||||
"images/kero/kero_star.png"
|
||||
"images/kero/kero_warning.png"
|
||||
)
|
||||
)
|
||||
|
||||
target_compile_definitions(
|
||||
target_compile_definitions(
|
||||
${CMAKE_PROJECT_NAME}
|
||||
PUBLIC
|
||||
KOMPLEX_LOCAL_DEV
|
||||
)
|
||||
)
|
||||
|
||||
include(qds)
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Imported 3D assets and components imported from bundles will be created in this folder.
|
||||
@@ -18,6 +18,20 @@ qt6_add_qml_module(KomplexHubContent
|
||||
"pages/UserProfilePage.qml"
|
||||
"pages/WallpaperSettingsPage.qml"
|
||||
"pages/VideoSearchPage.qml"
|
||||
"pages/FeaturedVideosPage.qml"
|
||||
"pages/FeaturedPacksPage.qml"
|
||||
"pages/FeaturedImagesPage.qml"
|
||||
"pages/NewestPacksPage.qml"
|
||||
"views/CompletedView.qml"
|
||||
"views/DownloadView.qml"
|
||||
"views/ErrorView.qml"
|
||||
"views/ImageView.qml"
|
||||
"views/PackView.qml"
|
||||
"views/VideoView.qml"
|
||||
RESOURCES views/CMakeLists.txt
|
||||
RESOURCES pages/CMakeLists.txt
|
||||
)
|
||||
|
||||
add_subdirectory(pages)
|
||||
add_subdirectory(views)
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ Rectangle {
|
||||
|
||||
height: 64
|
||||
width: 100
|
||||
text: "Home"
|
||||
text: qsTr("Home")
|
||||
|
||||
icon.height: 32
|
||||
icon.width: 32
|
||||
@@ -109,7 +109,7 @@ Rectangle {
|
||||
|
||||
height: 64
|
||||
width: 100
|
||||
text: "Images"
|
||||
text: qsTr("Images")
|
||||
|
||||
icon.height: 32
|
||||
icon.width: 32
|
||||
@@ -132,7 +132,7 @@ Rectangle {
|
||||
|
||||
height: 64
|
||||
width: 100
|
||||
text: "Videos"
|
||||
text: qsTr("Videos")
|
||||
|
||||
icon.height: 32
|
||||
icon.width: 32
|
||||
@@ -155,7 +155,7 @@ Rectangle {
|
||||
|
||||
height: 64
|
||||
width: 100
|
||||
text: "Live"
|
||||
text: qsTr("Komplex")
|
||||
|
||||
icon.height: 32
|
||||
icon.width: 32
|
||||
@@ -178,7 +178,7 @@ Rectangle {
|
||||
|
||||
height: 64
|
||||
width: 100
|
||||
text: "Installed"
|
||||
text: qsTr("Installed")
|
||||
|
||||
icon.height: 32
|
||||
icon.width: 32
|
||||
@@ -207,7 +207,7 @@ Rectangle {
|
||||
|
||||
height: 64
|
||||
width: 100
|
||||
text: "Settings"
|
||||
text: qsTr("Settings")
|
||||
|
||||
icon.height: 32
|
||||
icon.width: 32
|
||||
@@ -369,7 +369,7 @@ Rectangle {
|
||||
|
||||
function isSearchPage()
|
||||
{
|
||||
return (
|
||||
return(
|
||||
pageLoader.item instanceof ImageSearchPage ||
|
||||
pageLoader.item instanceof LiveSearchPage ||
|
||||
pageLoader.item instanceof VideoSearchPage
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
qt_add_library(KomplexHubPages STATIC)
|
||||
qt6_add_qml_module(KomplexHubPages
|
||||
URI "KomplexHub.Pages"
|
||||
VERSION 1.0
|
||||
RESOURCE_PREFIX "/qt/qml"
|
||||
QML_FILES
|
||||
"HomePage.qml"
|
||||
"ImageSearchPage.qml"
|
||||
"InstalledWallpaperPage.qml"
|
||||
"LiveSearchPage.qml"
|
||||
"LoginPage.qml"
|
||||
"SettingsPage.qml"
|
||||
"UserProfilePage.qml"
|
||||
"WallpaperSettingsPage.qml"
|
||||
"VideoSearchPage.qml"
|
||||
"FeaturedVideosPage.qml"
|
||||
"FeaturedPacksPage.qml"
|
||||
"FeaturedImagesPage.qml"
|
||||
"NewestPacksPage.qml"
|
||||
)
|
||||
@@ -0,0 +1,154 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHub.Plugin
|
||||
import KomplexHub.Views
|
||||
import KomplexHub.Pages
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: rootItem
|
||||
|
||||
property string query
|
||||
readonly property bool searchable: paginator.searchable
|
||||
property bool popup: false
|
||||
property int resultWidth: 256
|
||||
property int resultHeight: 245
|
||||
|
||||
color: palette.base
|
||||
|
||||
FeaturedImagesModel
|
||||
{
|
||||
id: searchModel
|
||||
//query: rootItem.query
|
||||
resultsPerPage: paginator.resultsPerPage
|
||||
|
||||
Component.onCompleted: () => nextPage()
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
id: resultsLayout
|
||||
visible: opacity > 0.01
|
||||
anchors.fill: parent
|
||||
anchors.margins: Constants.mediumMargin
|
||||
|
||||
KeroHeader
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 180
|
||||
title: qsTr("Featured Images")
|
||||
description: qsTr("A specially curated collection of images, courtesy of Pexels")
|
||||
}
|
||||
|
||||
PaginatorGrid
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: paginator
|
||||
model: searchModel
|
||||
loading: model.state === FeaturedImagesModel.Loading
|
||||
|
||||
delegate: ItemDelegate
|
||||
{
|
||||
id: resultDelegate
|
||||
|
||||
width: resultWidth
|
||||
height: resultHeight
|
||||
|
||||
required property string author
|
||||
required property string description
|
||||
required property string uuid
|
||||
required property string thumbnail
|
||||
required property string authorId
|
||||
required property int index
|
||||
|
||||
SearchResultItem
|
||||
{
|
||||
anchors.fill: parent
|
||||
title: parent.author
|
||||
description: parent.description
|
||||
thumbnail: parent.thumbnail
|
||||
uuid: parent.uuid
|
||||
pexels: true
|
||||
selected: paginator.currentIndex === parent.index
|
||||
|
||||
onTriggered: () => paginator.currentIndex = parent.index
|
||||
|
||||
onViewMoreTriggered: () =>
|
||||
{
|
||||
showImagePopup(parent.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: popupContainer
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
color: palette.base
|
||||
|
||||
ImageView
|
||||
{
|
||||
id: viewMoreImagePopup
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showImagePopup(index)
|
||||
{
|
||||
viewMoreImagePopup.author = searchModel.data(searchModel.index(index,0), FeaturedImagesModel.AuthorRole)
|
||||
viewMoreImagePopup.authorUrl = searchModel.data(searchModel.index(index,0), FeaturedImagesModel.AuthorUrlRole)
|
||||
viewMoreImagePopup.description = searchModel.data(searchModel.index(index,0), FeaturedImagesModel.DescriptionRole)
|
||||
viewMoreImagePopup.uuid = searchModel.data(searchModel.index(index,0), FeaturedImagesModel.UuidRole)
|
||||
viewMoreImagePopup.thumbnail = searchModel.data(searchModel.index(index,0), FeaturedImagesModel.LargeThumbnailRole)
|
||||
viewMoreImagePopup.original = searchModel.data(searchModel.index(index,0), FeaturedImagesModel.OriginalUrlRole)
|
||||
viewMoreImagePopup.portrait = searchModel.data(searchModel.index(index,0), FeaturedImagesModel.PortraitUrlRole)
|
||||
viewMoreImagePopup.landscape = searchModel.data(searchModel.index(index,0), FeaturedImagesModel.LandscapeUrlRole)
|
||||
viewMoreImagePopup.backgroundPortrait = searchModel.data(searchModel.index(index,0), FeaturedImagesModel.BackgroundPortraitRole)
|
||||
viewMoreImagePopup.backgroundLandscape = searchModel.data(searchModel.index(index,0), FeaturedImagesModel.BackgroundLandscapeRole)
|
||||
viewMoreImagePopup.fullScreen = searchModel.data(searchModel.index(index,0), FeaturedImagesModel.ScreenUrlRole)
|
||||
viewMoreImagePopup.portraitSize = searchModel.data(searchModel.index(index,0), FeaturedImagesModel.PortraitSizeRole)
|
||||
viewMoreImagePopup.landscapeSize = searchModel.data(searchModel.index(index,0), FeaturedImagesModel.LandscapeSizeRole)
|
||||
viewMoreImagePopup.fullScreenSize = searchModel.data(searchModel.index(index,0), FeaturedImagesModel.ScreenSizeRole)
|
||||
viewMoreImagePopup.opacity = 1
|
||||
popupContainer.opacity = 1
|
||||
resultsLayout.opacity = 0
|
||||
rootItem.popup = true
|
||||
}
|
||||
|
||||
function closePopup()
|
||||
{
|
||||
viewMoreImagePopup.opacity = 0
|
||||
popupContainer.opacity = 0
|
||||
resultsLayout.opacity = 1
|
||||
rootItem.popup = false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHub.Plugin
|
||||
import KomplexHub.Views
|
||||
import KomplexHub.Pages
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: rootItem
|
||||
|
||||
property string query
|
||||
readonly property bool searchable: false
|
||||
property bool popup: false
|
||||
property int resultWidth: 256
|
||||
property int resultHeight: 245
|
||||
|
||||
color: palette.base
|
||||
|
||||
NewestPacksModel
|
||||
{
|
||||
id: packsModel
|
||||
resultsPerPage: paginator.resultsPerPage
|
||||
|
||||
Component.onCompleted: () => nextPage()
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
id: resultsLayout
|
||||
visible: opacity > 0.01
|
||||
anchors.fill: parent
|
||||
anchors.margins: Constants.mediumMargin
|
||||
|
||||
KeroHeader
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 180
|
||||
title: qsTr("Featured Live Wallpapers")
|
||||
description: qsTr("Live Wallpaper Packs from Komplex or ShaderToy")
|
||||
}
|
||||
|
||||
PaginatorGrid
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: paginator
|
||||
model: packsModel
|
||||
loading: model.state === NewestPacksModel.Loading
|
||||
|
||||
delegate: ItemDelegate
|
||||
{
|
||||
id: resultDelegate
|
||||
|
||||
width: resultWidth
|
||||
height: resultHeight
|
||||
|
||||
required property string name
|
||||
required property string author
|
||||
required property string description
|
||||
required property string uuid
|
||||
required property string thumbnail
|
||||
required property string authorId
|
||||
required property int index
|
||||
|
||||
SearchResultItem
|
||||
{
|
||||
anchors.fill: parent
|
||||
title: parent.name
|
||||
author: parent.author
|
||||
description: parent.description
|
||||
thumbnail: parent.thumbnail
|
||||
uuid: parent.uuid
|
||||
selected: paginator.currentIndex === parent.index
|
||||
|
||||
onTriggered: () => paginator.currentIndex = parent.index
|
||||
|
||||
onViewMoreTriggered: () =>
|
||||
{
|
||||
showWallpaperPopup(parent.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: popupContainer
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
color: palette.base
|
||||
|
||||
PackView
|
||||
{
|
||||
id: viewMoreWallpaperPopup
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function closePopup()
|
||||
{
|
||||
viewMoreWallpaperPopup.opacity = 0
|
||||
popupContainer.opacity = 0
|
||||
resultsLayout.opacity = 1
|
||||
rootItem.popup = false
|
||||
}
|
||||
|
||||
function showWallpaperPopup(index)
|
||||
{
|
||||
viewMoreWallpaperPopup.author = packsModel.data(packsModel.index(index,0), NewestPacksModel.AuthorRole)
|
||||
viewMoreWallpaperPopup.authorId = packsModel.data(packsModel.index(index,0), NewestPacksModel.AuthorIdRole)
|
||||
viewMoreWallpaperPopup.description = packsModel.data(packsModel.index(index,0), NewestPacksModel.DescriptionRole)
|
||||
viewMoreWallpaperPopup.thumbnail = packsModel.data(packsModel.index(index,0), NewestPacksModel.ThumbnailRole)
|
||||
viewMoreWallpaperPopup.uuid = packsModel.data(packsModel.index(index,0), NewestPacksModel.UuidRole)
|
||||
viewMoreWallpaperPopup.opacity = 1
|
||||
popupContainer.opacity = 1
|
||||
resultsLayout.opacity = 0
|
||||
rootItem.popup = true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHub.Plugin
|
||||
import KomplexHub.Views
|
||||
import KomplexHub.Pages
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: rootItem
|
||||
|
||||
readonly property bool searchable: false
|
||||
property bool popup: false
|
||||
property int resultWidth: 256
|
||||
property int resultHeight: 245
|
||||
|
||||
color: palette.base
|
||||
|
||||
FeaturedVideosModel
|
||||
{
|
||||
id: videosModel
|
||||
resultsPerPage: paginator.resultsPerPage
|
||||
windowSize: 80
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
id: resultsLayout
|
||||
visible: opacity > 0.01
|
||||
anchors.fill: parent
|
||||
anchors.margins: Constants.mediumMargin
|
||||
|
||||
KeroHeader
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 180
|
||||
title: qsTr("Most Popular Videos")
|
||||
description: qsTr("Most popular videos, provided courtesy of Pexels")
|
||||
image: "qrc:/images/kero/kero_star.png"
|
||||
}
|
||||
|
||||
PaginatorGrid
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: paginator
|
||||
model: videosModel
|
||||
loading: videosModel.state === FeaturedVideosModel.Loading
|
||||
visible: opacity > 0.01
|
||||
|
||||
delegate: ItemDelegate
|
||||
{
|
||||
id: resultDelegate
|
||||
|
||||
width: resultWidth
|
||||
height: resultHeight
|
||||
|
||||
required property string uuid
|
||||
required property string author
|
||||
required property string authorId
|
||||
required property string authorUrl
|
||||
required property string thumbnail
|
||||
required property int index
|
||||
|
||||
SearchResultItem
|
||||
{
|
||||
anchors.fill: parent
|
||||
author: parent.author
|
||||
description: qsTr("")
|
||||
thumbnail: parent.thumbnail
|
||||
uuid: parent.uuid
|
||||
pexels: true
|
||||
selected: paginator.currentIndex === parent.index
|
||||
|
||||
onTriggered: () => paginator.currentIndex = parent.index
|
||||
|
||||
onViewMoreTriggered: () =>
|
||||
{
|
||||
showVideoPopup(parent.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: popupContainer
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
color: palette.base
|
||||
|
||||
VideoView
|
||||
{
|
||||
id: videoDetailsPopup
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
|
||||
Behavior on opacity
|
||||
{
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity
|
||||
{
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: () =>
|
||||
{
|
||||
videosModel.nextPage()
|
||||
}
|
||||
|
||||
function showVideoPopup(index)
|
||||
{
|
||||
videosModel.setItem(index)
|
||||
videoDetailsPopup.author = searchModel.data(searchModel.index(index,0), VideoSearchModel.AuthorRole)
|
||||
videoDetailsPopup.authorUrl = searchModel.data(searchModel.index(index,0), VideoSearchModel.AuthorUrlRole)
|
||||
videoDetailsPopup.uuid = searchModel.data(searchModel.index(index,0), VideoSearchModel.UuidRole)
|
||||
videoDetailsPopup.thumbnail = searchModel.data(searchModel.index(index,0), VideoSearchModel.ThumbnailRole)
|
||||
videoDetailsPopup.modelIndex = index
|
||||
videoDetailsPopup.model = searchModel.itemModel
|
||||
videoDetailsPopup.opacity = 1
|
||||
resultsLayout.opacity = 0
|
||||
popupContainer.opacity = 1
|
||||
rootItem.popup = true
|
||||
}
|
||||
|
||||
function closePopup()
|
||||
{
|
||||
videoDetailsPopup.opacity = 0
|
||||
popupContainer.opacity = 0
|
||||
resultsLayout.opacity = 1
|
||||
rootItem.popup = false
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHubPlugin
|
||||
import KomplexHub.Plugin
|
||||
import KomplexHubContent
|
||||
|
||||
Item
|
||||
{
|
||||
@@ -19,15 +20,15 @@ Item
|
||||
imagesModel.state === FeaturedImagesModel.Loading ||
|
||||
videosModel.state === FeaturedVideosModel.Loading;
|
||||
|
||||
onWidthChanged:() => {
|
||||
onWidthChanged: () =>
|
||||
{
|
||||
packsModel.resultsPerPage = resultsPerRow
|
||||
imagesModel.resultsPerPage = resultsPerRow
|
||||
videosModel.resultsPerPage = resultsPerRow
|
||||
}
|
||||
|
||||
clip: true
|
||||
|
||||
id: homePageRoot
|
||||
clip: true
|
||||
|
||||
NewestPacksModel
|
||||
{
|
||||
@@ -47,37 +48,81 @@ Item
|
||||
resultsPerPage: 0
|
||||
}
|
||||
|
||||
Flickable
|
||||
{
|
||||
id: pageArea
|
||||
visible: opacity > 0.01
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
contentHeight: content.height
|
||||
contentWidth: content.width
|
||||
|
||||
Column
|
||||
{
|
||||
id: content
|
||||
width: pageArea.width
|
||||
|
||||
VerticalSpacer{ height: Constants.largeMargin; width: Constants.largeMargin }
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.fill: parent
|
||||
color: palette.base
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: childrenRect.height
|
||||
anchors.margins: Constants.largeMargin
|
||||
|
||||
ColumnLayout
|
||||
color: palette.base
|
||||
border.color: palette.midlight
|
||||
|
||||
Column
|
||||
{
|
||||
id: resultsLayout
|
||||
visible: opacity > 0.01
|
||||
anchors.fill: parent
|
||||
anchors.margins: Constants.mediumMargin
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
KeroHeader
|
||||
{
|
||||
height: 180
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: childrenRect.height
|
||||
|
||||
color: palette.window
|
||||
|
||||
Column
|
||||
{
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Constants.largeMargin
|
||||
|
||||
Text
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.preferredHeight: 50
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 36
|
||||
|
||||
color: palette.text
|
||||
font.pixelSize: Constants.h2Font.pixelSize
|
||||
font.bold: true
|
||||
text: qsTr("Newest Wallpaper Packs")
|
||||
text: qsTr("Featured Komplex Wallpapers")
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
HorizontalPaginator
|
||||
{
|
||||
id: packsPaginator
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 245
|
||||
Layout.alignment: Qt.AlignTop
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 245
|
||||
|
||||
id: packsPaginator
|
||||
model: packsModel
|
||||
delegate: ItemDelegate
|
||||
{
|
||||
@@ -109,19 +154,20 @@ Item
|
||||
{
|
||||
showWallpaperPopup(parent.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onThumbnailChanged: () =>
|
||||
onViewMoreTriggered: () =>
|
||||
{
|
||||
console.log(index + " " + thumbnail + " " + parent.thumbnail)
|
||||
}
|
||||
}
|
||||
showFeaturedPacksPage()
|
||||
}
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.preferredHeight: 50
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 36
|
||||
|
||||
color: palette.text
|
||||
font.pixelSize: Constants.h2Font.pixelSize
|
||||
@@ -133,9 +179,9 @@ Item
|
||||
HorizontalPaginator
|
||||
{
|
||||
id: imagesPaginator
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 245
|
||||
Layout.alignment: Qt.AlignTop
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 245
|
||||
|
||||
model: imagesModel
|
||||
delegate: ItemDelegate
|
||||
@@ -153,7 +199,7 @@ Item
|
||||
SearchResultItem
|
||||
{
|
||||
anchors.fill: parent
|
||||
title: parent.author
|
||||
author: parent.author
|
||||
description: parent.description
|
||||
thumbnail: parent.thumbnail
|
||||
uuid: parent.uuid
|
||||
@@ -168,12 +214,124 @@ Item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onViewMoreTriggered: () =>
|
||||
{
|
||||
showFeaturedImagesPage()
|
||||
}
|
||||
}
|
||||
|
||||
VerticalSpacer { height: Constants.largeMargin; width: Constants.largeMargin }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalSpacer { height: Constants.largeMargin; width: Constants.largeMargin }
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: childrenRect.height
|
||||
anchors.margins: Constants.largeMargin
|
||||
|
||||
color: palette.base
|
||||
border.color: palette.midlight
|
||||
|
||||
Column
|
||||
{
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
KeroHeader
|
||||
{
|
||||
height: 180
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
title: qsTr("New & Popular Wallpapers")
|
||||
image: "qrc:/images/kero/kero_star.png"
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Constants.largeMargin
|
||||
|
||||
height: childrenRect.height
|
||||
color: palette.window
|
||||
|
||||
Column
|
||||
{
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Text
|
||||
{
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 36
|
||||
|
||||
color: palette.text
|
||||
font.pixelSize: Constants.h2Font.pixelSize
|
||||
font.bold: true
|
||||
text: qsTr("Newest Wallpaper Packs")
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
HorizontalPaginator
|
||||
{
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 245
|
||||
|
||||
id: packsPaginator2
|
||||
model: packsModel
|
||||
delegate: ItemDelegate
|
||||
{
|
||||
width: resultWidth
|
||||
height: resultHeight
|
||||
|
||||
required property string name
|
||||
required property string author
|
||||
required property string description
|
||||
required property string uuid
|
||||
required property string thumbnail
|
||||
required property string authorId
|
||||
required property int index
|
||||
|
||||
SearchResultItem
|
||||
{
|
||||
anchors.fill: parent
|
||||
title: parent.name
|
||||
author: parent.author
|
||||
description: parent.description
|
||||
thumbnail: parent.thumbnail
|
||||
uuid: parent.uuid
|
||||
|
||||
selected: packsPaginator2.currentIndex === parent.index
|
||||
|
||||
onTriggered: () => packsPaginator2.currentIndex = parent.index
|
||||
|
||||
onViewMoreTriggered: () =>
|
||||
{
|
||||
showWallpaperPopup(parent.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onViewMoreTriggered: () =>
|
||||
{
|
||||
showFeaturedPacksPage()
|
||||
}
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.preferredHeight: 50
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 36
|
||||
|
||||
color: palette.text
|
||||
font.pixelSize: Constants.h2Font.pixelSize
|
||||
@@ -185,9 +343,10 @@ Item
|
||||
HorizontalPaginator
|
||||
{
|
||||
id: videosPaginator
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 245
|
||||
Layout.alignment: Qt.AlignTop
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 245
|
||||
|
||||
model: videosModel
|
||||
delegate: ItemDelegate
|
||||
@@ -205,7 +364,7 @@ Item
|
||||
SearchResultItem
|
||||
{
|
||||
anchors.fill: parent
|
||||
title: parent.author
|
||||
author: parent.author
|
||||
thumbnail: parent.thumbnail
|
||||
uuid: parent.uuid
|
||||
pexels: true
|
||||
@@ -215,10 +374,20 @@ Item
|
||||
onViewMoreTriggered:() => showVideoPopup(parent.index)
|
||||
}
|
||||
}
|
||||
|
||||
onViewMoreTriggered: () =>
|
||||
{
|
||||
showFeaturedVideosPage()
|
||||
}
|
||||
}
|
||||
|
||||
/* spacer */
|
||||
Item { Layout.fillHeight: true }
|
||||
VerticalSpacer{ height: Constants.largeMargin; width: Constants.largeMargin }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalSpacer{ height: Constants.largeMargin; width: Constants.largeMargin }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,7 +422,8 @@ Item
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
|
||||
Behavior on opacity {
|
||||
Behavior on opacity
|
||||
{
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 250
|
||||
@@ -267,9 +437,9 @@ Item
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
//model: videosModel.itemModel
|
||||
|
||||
Behavior on opacity {
|
||||
Behavior on opacity
|
||||
{
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 250
|
||||
@@ -283,9 +453,9 @@ Item
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
//model: videosModel.itemModel
|
||||
|
||||
Behavior on opacity {
|
||||
Behavior on opacity
|
||||
{
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 250
|
||||
@@ -293,7 +463,8 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Behavior on opacity
|
||||
{
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 250
|
||||
@@ -301,8 +472,91 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
Component
|
||||
{
|
||||
id: featuredVideosComponent
|
||||
|
||||
FeaturedVideosPage
|
||||
{
|
||||
id: featuredVideosPage
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
|
||||
Behavior on opacity
|
||||
{
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: () => featuredVideosPage.opacity = 1
|
||||
}
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: featuredPacksComponent
|
||||
|
||||
FeaturedPacksPage
|
||||
{
|
||||
id: featuredPacksPage
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
|
||||
Behavior on opacity
|
||||
{
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: () => featuredPacksPage.opacity = 1
|
||||
}
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: featuredImagesComponent
|
||||
|
||||
FeaturedImagesPage
|
||||
{
|
||||
id: featuredImagesPage
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
|
||||
Behavior on opacity
|
||||
{
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: () => featuredImagesPage.opacity = 1
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: featuredLoaderItem
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
enabled: visible
|
||||
|
||||
Loader
|
||||
{
|
||||
id: featuredLoader
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
states:
|
||||
[
|
||||
State
|
||||
{
|
||||
name: "loading"
|
||||
when: homePageRoot.loading
|
||||
|
||||
@@ -314,7 +568,7 @@ Item
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: resultsLayout
|
||||
target: pageArea
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
@@ -324,11 +578,10 @@ Item
|
||||
opacity: 0
|
||||
}
|
||||
},
|
||||
State {
|
||||
State
|
||||
{
|
||||
name: "idle"
|
||||
when: packsModel.state !== NewestPacksModel.Loading &&
|
||||
imagesModel.state !== FeaturedImagesModel.Loading &&
|
||||
videosModel.state !== FeaturedVideosModel.Loading;
|
||||
when: !popup && !loading
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
@@ -338,13 +591,31 @@ Item
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: resultsLayout
|
||||
target: pageArea
|
||||
opacity: 1
|
||||
}
|
||||
},
|
||||
State
|
||||
{
|
||||
name: "popup"
|
||||
when: popup
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: pageArea
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: loadingAnimation
|
||||
opacity: 0
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: [
|
||||
transitions:
|
||||
[
|
||||
Transition
|
||||
{
|
||||
from: "loading"
|
||||
@@ -357,13 +628,37 @@ Item
|
||||
}
|
||||
NumberAnimation
|
||||
{
|
||||
target: resultsLayout
|
||||
target: pageArea
|
||||
duration: 250
|
||||
property: "opacity";
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
function showFeaturedVideosPage()
|
||||
{
|
||||
pageArea.opacity = 0
|
||||
featuredLoaderItem.opacity = 1
|
||||
featuredLoader.sourceComponent = featuredVideosComponent
|
||||
popup = true
|
||||
}
|
||||
|
||||
function showFeaturedPacksPage()
|
||||
{
|
||||
pageArea.opacity = 0
|
||||
featuredLoaderItem.opacity = 1
|
||||
featuredLoader.sourceComponent = featuredPacksComponent
|
||||
popup = true
|
||||
}
|
||||
|
||||
function showFeaturedImagesPage()
|
||||
{
|
||||
pageArea.opacity = 0
|
||||
featuredLoaderItem.opacity = 1
|
||||
featuredLoader.sourceComponent = featuredImagesComponent
|
||||
popup = true
|
||||
}
|
||||
|
||||
function showImagePopup(index)
|
||||
{
|
||||
viewMoreImagePopup.author = imagesModel.data(imagesModel.index(index,0), FeaturedImagesModel.AuthorRole)
|
||||
@@ -371,7 +666,6 @@ Item
|
||||
viewMoreImagePopup.description = imagesModel.data(imagesModel.index(index,0), FeaturedImagesModel.DescriptionRole)
|
||||
viewMoreImagePopup.uuid = imagesModel.data(imagesModel.index(index,0), FeaturedImagesModel.UuidRole)
|
||||
viewMoreImagePopup.thumbnail = imagesModel.data(imagesModel.index(index,0), FeaturedImagesModel.LargeThumbnailRole)
|
||||
viewMoreImagePopup.original = imagesModel.data(imagesModel.index(index,0), FeaturedImagesModel.OriginalUrlRole)
|
||||
viewMoreImagePopup.portrait = imagesModel.data(imagesModel.index(index,0), FeaturedImagesModel.PortraitUrlRole)
|
||||
viewMoreImagePopup.landscape = imagesModel.data(imagesModel.index(index,0), FeaturedImagesModel.LandscapeUrlRole)
|
||||
viewMoreImagePopup.backgroundPortrait = imagesModel.data(imagesModel.index(index,0), FeaturedImagesModel.BackgroundPortraitRole)
|
||||
@@ -382,7 +676,7 @@ Item
|
||||
viewMoreImagePopup.fullScreenSize = imagesModel.data(imagesModel.index(index,0), FeaturedImagesModel.ScreenSizeRole)
|
||||
viewMoreImagePopup.opacity = 1
|
||||
popupContainer.opacity = 1
|
||||
resultsLayout.opacity = 0
|
||||
pageArea.opacity = 0
|
||||
homePageRoot.popup = true
|
||||
}
|
||||
|
||||
@@ -396,7 +690,7 @@ Item
|
||||
viewMoreVideoPopup.modelIndex = index
|
||||
viewMoreVideoPopup.model = videosModel.itemModel
|
||||
viewMoreVideoPopup.opacity = 1
|
||||
resultsLayout.opacity = 0
|
||||
pageArea.opacity = 0
|
||||
popupContainer.opacity = 1
|
||||
homePageRoot.popup = true
|
||||
}
|
||||
@@ -407,19 +701,28 @@ Item
|
||||
viewMoreWallpaperPopup.authorId = packsModel.data(packsModel.index(index,0), NewestPacksModel.AuthorIdRole)
|
||||
viewMoreWallpaperPopup.description = packsModel.data(packsModel.index(index,0), NewestPacksModel.DescriptionRole)
|
||||
viewMoreWallpaperPopup.thumbnail = packsModel.data(packsModel.index(index,0), NewestPacksModel.ThumbnailRole)
|
||||
viewMoreWallpaperPopup.name = packsModel.data(packsModel.index(index,0), NewestPacksModel.NameRole)
|
||||
viewMoreWallpaperPopup.uuid = packsModel.data(packsModel.index(index,0), NewestPacksModel.UuidRole)
|
||||
viewMoreWallpaperPopup.opacity = 1
|
||||
popupContainer.opacity = 1
|
||||
resultsLayout.opacity = 0
|
||||
pageArea.opacity = 0
|
||||
homePageRoot.popup = true
|
||||
}
|
||||
|
||||
function closePopup()
|
||||
{
|
||||
if(featuredLoader.item && featuredLoader.item.popup)
|
||||
{
|
||||
featuredLoader.item.closePopup()
|
||||
return
|
||||
}
|
||||
|
||||
featuredLoaderItem.opacity = 0
|
||||
viewMoreImagePopup.opacity = 0
|
||||
viewMoreVideoPopup.opacity = 0
|
||||
viewMoreWallpaperPopup.opacity = 0
|
||||
popupContainer.opacity = 0
|
||||
resultsLayout.opacity = 1
|
||||
pageArea.opacity = 1
|
||||
homePageRoot.popup = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ import QtQuick.Layouts
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHubPlugin
|
||||
import KomplexHub.Plugin
|
||||
import KomplexHub.Views
|
||||
import KomplexHub.Pages
|
||||
|
||||
Item
|
||||
{
|
||||
|
||||
@@ -2,10 +2,13 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHubPlugin
|
||||
import KomplexHub.Plugin
|
||||
import KomplexHub.Views
|
||||
import KomplexHub.Pages
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHubPlugin
|
||||
import KomplexHub.Plugin
|
||||
import KomplexHub.Views
|
||||
import KomplexHub.Pages
|
||||
|
||||
Item
|
||||
{
|
||||
@@ -33,8 +36,8 @@ Item
|
||||
{
|
||||
id: resultDelegate
|
||||
|
||||
width: 256
|
||||
height: 245
|
||||
width: resultWidth
|
||||
height: resultHeight
|
||||
|
||||
required property string name
|
||||
required property string author
|
||||
@@ -47,7 +50,8 @@ Item
|
||||
SearchResultItem
|
||||
{
|
||||
anchors.fill: parent
|
||||
title: parent.author
|
||||
title: parent.name
|
||||
author: parent.author
|
||||
description: parent.description
|
||||
thumbnail: parent.thumbnail
|
||||
uuid: parent.uuid
|
||||
@@ -108,6 +112,7 @@ Item
|
||||
viewMoreWallpaperPopup.authorId = searchModel.data(searchModel.index(index,0), PackSearchModel.AuthorIdRole)
|
||||
viewMoreWallpaperPopup.description = searchModel.data(searchModel.index(index,0), PackSearchModel.DescriptionRole)
|
||||
viewMoreWallpaperPopup.thumbnail = searchModel.data(searchModel.index(index,0), PackSearchModel.ThumbnailRole)
|
||||
viewMoreWallpaperPopup.uuid = searchModel.data(searchModel.index(index,0), PackSearchModel.UuidRole)
|
||||
viewMoreWallpaperPopup.opacity = 1
|
||||
popupContainer.opacity = 1
|
||||
paginator.opacity = 0
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHub.Plugin
|
||||
import KomplexHub.Views
|
||||
import KomplexHub.Pages
|
||||
|
||||
Item {
|
||||
id: loginFormRoot
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHub.Plugin
|
||||
import KomplexHub.Views
|
||||
import KomplexHub.Pages
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: rootItem
|
||||
|
||||
property string query
|
||||
readonly property bool searchable: false
|
||||
property bool popup: false
|
||||
property int resultWidth: 256
|
||||
property int resultHeight: 245
|
||||
|
||||
color: palette.base
|
||||
|
||||
NewestPacksModel
|
||||
{
|
||||
id: packsModel
|
||||
resultsPerPage: paginator.resultsPerPage
|
||||
|
||||
Component.onCompleted: () => nextPage()
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
id: resultsLayout
|
||||
visible: opacity > 0.01
|
||||
anchors.fill: parent
|
||||
anchors.margins: Constants.mediumMargin
|
||||
|
||||
KeroHeader
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 180
|
||||
title: qsTr("Newest Live Wallpapers")
|
||||
description: qsTr("Live Wallpaper Packs from Komplex or ShaderToy")
|
||||
image: "qrc:/images/kero/kero_star.png"
|
||||
}
|
||||
|
||||
PaginatorGrid
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: paginator
|
||||
model: packsModel
|
||||
loading: model.state === NewestPacksModel.Loading
|
||||
|
||||
delegate: ItemDelegate
|
||||
{
|
||||
id: resultDelegate
|
||||
|
||||
width: resultWidth
|
||||
height: resultHeight
|
||||
|
||||
required property string name
|
||||
required property string author
|
||||
required property string description
|
||||
required property string uuid
|
||||
required property string thumbnail
|
||||
required property string authorId
|
||||
required property int index
|
||||
|
||||
SearchResultItem
|
||||
{
|
||||
anchors.fill: parent
|
||||
title: parent.name
|
||||
author: parent.author
|
||||
description: parent.description
|
||||
thumbnail: parent.thumbnail
|
||||
uuid: parent.uuid
|
||||
selected: paginator.currentIndex === parent.index
|
||||
|
||||
onTriggered: () => paginator.currentIndex = parent.index
|
||||
|
||||
onViewMoreTriggered: () =>
|
||||
{
|
||||
showWallpaperPopup(parent.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: popupContainer
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
color: palette.base
|
||||
|
||||
PackView
|
||||
{
|
||||
id: viewMoreWallpaperPopup
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function closePopup()
|
||||
{
|
||||
viewMoreWallpaperPopup.opacity = 0
|
||||
popupContainer.opacity = 0
|
||||
resultsLayout.opacity = 1
|
||||
rootItem.popup = false
|
||||
}
|
||||
|
||||
function showWallpaperPopup(index)
|
||||
{
|
||||
viewMoreWallpaperPopup.author = packsModel.data(packsModel.index(index,0), NewestPacksModel.AuthorRole)
|
||||
viewMoreWallpaperPopup.authorId = packsModel.data(packsModel.index(index,0), NewestPacksModel.AuthorIdRole)
|
||||
viewMoreWallpaperPopup.description = packsModel.data(packsModel.index(index,0), NewestPacksModel.DescriptionRole)
|
||||
viewMoreWallpaperPopup.thumbnail = packsModel.data(packsModel.index(index,0), NewestPacksModel.ThumbnailRole)
|
||||
viewMoreWallpaperPopup.opacity = 1
|
||||
popupContainer.opacity = 1
|
||||
resultsLayout.opacity = 0
|
||||
rootItem.popup = true
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHub.Plugin
|
||||
import KomplexHub.Views
|
||||
import KomplexHub.Pages
|
||||
|
||||
Item {
|
||||
readonly property bool searchable: false
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHub.Plugin
|
||||
import KomplexHub.Views
|
||||
import KomplexHub.Pages
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
@@ -5,7 +5,9 @@ import QtQuick.Layouts
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHubPlugin
|
||||
import KomplexHub.Plugin
|
||||
import KomplexHub.Views
|
||||
import KomplexHub.Pages
|
||||
|
||||
Item
|
||||
{
|
||||
|
||||
@@ -31,9 +31,12 @@ import QtQuick.Layouts
|
||||
//import org.kde.plasma.core as PlasmaCore
|
||||
import QtCore
|
||||
import Qt.labs.folderlistmodel 2.15
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHubPlugin
|
||||
import KomplexHub.Plugin
|
||||
import KomplexHub.Views
|
||||
import KomplexHub.Pages
|
||||
|
||||
//import com.github.digitalartifex.komplex 1.0 as Komplex
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
qt_add_library(KomplexHubViews STATIC)
|
||||
qt6_add_qml_module(KomplexHubViews
|
||||
URI "KomplexHub.Views"
|
||||
VERSION 1.0
|
||||
RESOURCE_PREFIX "/qt/qml"
|
||||
QML_FILES
|
||||
"CompletedView.qml"
|
||||
"DownloadView.qml"
|
||||
"ErrorView.qml"
|
||||
"ImageView.qml"
|
||||
"PackView.qml"
|
||||
"VideoView.qml"
|
||||
)
|
||||
@@ -0,0 +1,66 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Kero
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Plugin
|
||||
|
||||
Item
|
||||
{
|
||||
property DownloadManager manager: null
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
anchors.fill: parent
|
||||
|
||||
VerticalSpacer { }
|
||||
|
||||
KeroSuccessAvatar
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredHeight: 512
|
||||
Layout.maximumHeight: 512
|
||||
}
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 36
|
||||
Layout.maximumHeight: 36
|
||||
Layout.margins: Constants.largeMargin
|
||||
|
||||
Item
|
||||
{
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
SquareButton
|
||||
{
|
||||
Layout.preferredWidth: 186
|
||||
Layout.fillHeight: true
|
||||
text: qsTr("Okay")
|
||||
|
||||
onTriggered: () =>
|
||||
{
|
||||
if(manager != null)
|
||||
{
|
||||
manager.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
SquareButton
|
||||
{
|
||||
Layout.preferredWidth: 186
|
||||
Layout.fillHeight: true
|
||||
|
||||
text: qsTr("Set Wallpaper")
|
||||
icon.source: "qrc:/images/icons/icons8-download.svg"
|
||||
}
|
||||
}
|
||||
|
||||
VerticalSpacer {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Plugin
|
||||
import KomplexHub.Kero
|
||||
|
||||
Item
|
||||
{
|
||||
property string thumbnail
|
||||
property DownloadManager manager
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.fill: parent
|
||||
color: palette.base
|
||||
|
||||
BackgroundImage
|
||||
{
|
||||
source: thumbnail
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
anchors.fill: parent
|
||||
anchors.margins: Constants.largeMargin
|
||||
|
||||
Item { Layout.preferredHeight: 64; Layout.fillWidth: true }
|
||||
|
||||
KeroBuildingAnimation
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
VerticalSpacer {}
|
||||
|
||||
Text
|
||||
{
|
||||
text: qsTr("Download Progress")
|
||||
font: Constants.h3Font
|
||||
color : palette.text
|
||||
Layout.preferredHeight: Constants.largeMargin
|
||||
}
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Constants.largeMargin
|
||||
|
||||
ProgressBar
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
value: manager.downloadProgress
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
text: manager.downloadedBytes
|
||||
font: Constants.h4Font
|
||||
color : palette.text
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
text: "/"
|
||||
font: Constants.h4Font
|
||||
color : palette.text
|
||||
Layout.preferredHeight: 32
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
text: manager.downloadSize
|
||||
font: Constants.h4Font
|
||||
color : palette.text
|
||||
Layout.preferredHeight: 32
|
||||
}
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
text: qsTr("Install Progress")
|
||||
font: Constants.h3Font
|
||||
color : palette.text
|
||||
height: 32
|
||||
}
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Constants.largeMargin
|
||||
|
||||
ProgressBar
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
value: manager.compileProgress
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
text: manager.compileStepsCompleted
|
||||
font: Constants.h4Font
|
||||
color : palette.text
|
||||
Layout.preferredHeight: Constants.largeMargin
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
text: "/"
|
||||
font: Constants.h4Font
|
||||
color : palette.text
|
||||
Layout.preferredHeight: Constants.largeMargin
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
text: manager.compileSteps
|
||||
font: Constants.h4Font
|
||||
color : palette.text
|
||||
Layout.preferredHeight: Constants.largeMargin
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.preferredHeight: Constants.largeMargin * 2; Layout.fillWidth: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Kero
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Plugin
|
||||
|
||||
Item
|
||||
{
|
||||
property DownloadManager manager: null
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
anchors.fill: parent
|
||||
|
||||
VerticalSpacer { }
|
||||
|
||||
KeroWarningAvatar
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredHeight: 512
|
||||
Layout.maximumHeight: 512
|
||||
}
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 36
|
||||
Layout.maximumHeight: 36
|
||||
Layout.margins: Constants.largeMargin
|
||||
|
||||
Item
|
||||
{
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
SquareButton
|
||||
{
|
||||
Layout.preferredWidth: 186
|
||||
Layout.fillHeight: true
|
||||
text: qsTr("Okay")
|
||||
|
||||
onTriggered: () =>
|
||||
{
|
||||
if(manager != null)
|
||||
{
|
||||
manager.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalSpacer {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHub.Plugin
|
||||
|
||||
Item
|
||||
{
|
||||
property string uuid
|
||||
property string author
|
||||
property string authorUrl
|
||||
property string description
|
||||
property string thumbnail
|
||||
property string portrait
|
||||
property string landscape
|
||||
property string fullScreen
|
||||
property string portraitSize
|
||||
property string landscapeSize
|
||||
property string fullScreenSize
|
||||
property string backgroundPortrait
|
||||
property string backgroundLandscape
|
||||
|
||||
property int imageHeight
|
||||
property int imageWidth
|
||||
|
||||
id: rootItem
|
||||
|
||||
DownloadManager
|
||||
{
|
||||
id: downloadManager
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: pageView
|
||||
anchors.fill: parent
|
||||
color: palette.base
|
||||
|
||||
BackgroundImage
|
||||
{
|
||||
anchors.fill: parent
|
||||
source: width > height ? backgroundLandscape : backgroundPortrait
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
anchors.fill: parent
|
||||
anchors.margins: Constants.largeMargin
|
||||
spacing: Constants.largeMargin
|
||||
|
||||
ImageFrame
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
Layout.topMargin: Constants.largeMargin
|
||||
Layout.preferredHeight: width / 1.77777777778
|
||||
Layout.maximumWidth: 800
|
||||
|
||||
source: rootItem.thumbnail
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: titleText
|
||||
text: rootItem.author
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h3Font.pixelSize
|
||||
elide: Text.ElideRight
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: descriptionText
|
||||
text: rootItem.description
|
||||
color: palette.text
|
||||
font.pointSize: Constants.h4Font.pixelSize
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
/** Spacer **/
|
||||
Item
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 32
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
text: qsTr("Available Downloads")
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h3Font.pixelSize
|
||||
elide: Text.ElideRight
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignLeft
|
||||
}
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
|
||||
ComboBox
|
||||
{
|
||||
id: downloadSelector
|
||||
model:[
|
||||
"Screen (" + fullScreenSize + ")",
|
||||
"Portrait (" + portraitSize + ")",
|
||||
"Landscape (" + landscapeSize + ")"
|
||||
]
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 36
|
||||
}
|
||||
SquareButton
|
||||
{
|
||||
Layout.preferredHeight: 36
|
||||
Layout.preferredWidth: 128
|
||||
text: qsTr("Download")
|
||||
icon.source: "qrc:/images/icons/icons8-download.svg"
|
||||
|
||||
onTriggered: () => rootItem.download()
|
||||
}
|
||||
}
|
||||
|
||||
/** Spacer **/
|
||||
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
Text {
|
||||
text: qsTr("Images Provided Courtesy of Pexels")
|
||||
elide: Text.ElideLeft
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h6Font.pixelSize
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
}
|
||||
|
||||
Image
|
||||
{
|
||||
transform: Image.PreserveAspectFit
|
||||
source: "qrc:/images/icons/pexels-icon-filled-256.svg"
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 32
|
||||
Layout.preferredWidth: 32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states:
|
||||
[
|
||||
State
|
||||
{
|
||||
name: "downloading"
|
||||
when: downloadManager.state === DownloadManager.Downloading
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: pageView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: downloadView
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: completedView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: errorView
|
||||
opacity: 0
|
||||
}
|
||||
},
|
||||
State
|
||||
{
|
||||
name: "complete"
|
||||
when: downloadManager.state === DownloadManager.Complete
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: pageView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: downloadView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: completedView
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: errorView
|
||||
opacity: 0
|
||||
}
|
||||
},
|
||||
State
|
||||
{
|
||||
name: "error"
|
||||
when: downloadManager.state === DownloadManager.Error
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: pageView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: downloadView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: completedView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: errorView
|
||||
opacity: 1
|
||||
}
|
||||
},
|
||||
State
|
||||
{
|
||||
name: "idle"
|
||||
when: downloadManager.state === DownloadManager.Idle
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: pageView
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: downloadView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: completedView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: errorView
|
||||
opacity: 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
DownloadView
|
||||
{
|
||||
id: downloadView
|
||||
anchors.fill: parent
|
||||
|
||||
manager: downloadManager
|
||||
thumbnail: rootItem.thumbnail
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
}
|
||||
|
||||
CompletedView
|
||||
{
|
||||
id: completedView
|
||||
anchors.fill: parent
|
||||
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
manager: downloadManager
|
||||
}
|
||||
|
||||
ErrorView
|
||||
{
|
||||
id: errorView
|
||||
anchors.fill: parent
|
||||
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
manager: downloadManager
|
||||
}
|
||||
|
||||
function download()
|
||||
{
|
||||
let url = ""
|
||||
|
||||
switch(downloadSelector.currentIndex)
|
||||
{
|
||||
case 1:
|
||||
url = portrait
|
||||
break;
|
||||
case 2:
|
||||
url = landscape
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
url = fullScreen
|
||||
break;
|
||||
}
|
||||
|
||||
downloadManager.downloadImage(author, authorUrl, description, url)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHub.Plugin
|
||||
|
||||
Item
|
||||
{
|
||||
property string uuid
|
||||
property string author
|
||||
property string authorId
|
||||
property string description
|
||||
property string thumbnail
|
||||
property string name
|
||||
|
||||
property int imageHeight
|
||||
property int imageWidth
|
||||
|
||||
id: rootItem
|
||||
|
||||
DownloadManager
|
||||
{
|
||||
id: downloadManager
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: pageView
|
||||
anchors.fill: parent
|
||||
color: palette.base
|
||||
|
||||
BackgroundImage
|
||||
{
|
||||
anchors.fill: parent
|
||||
source: rootItem.thumbnail
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
anchors.fill: parent
|
||||
anchors.margins: Constants.largeMargin
|
||||
spacing: Constants.largeMargin
|
||||
|
||||
ImageFrame
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignHCenter
|
||||
Layout.topMargin: Constants.largeMargin
|
||||
Layout.preferredHeight: width / 1.77777777778
|
||||
Layout.maximumWidth: 800
|
||||
|
||||
source: rootItem.thumbnail
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: titleText
|
||||
text: rootItem.name
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h3Font.pixelSize
|
||||
elide: Text.ElideRight
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignLeft
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: descriptionText
|
||||
text: rootItem.description
|
||||
color: palette.text
|
||||
font.pointSize: Constants.h4Font.pixelSize
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: authorText
|
||||
text: qsTr("By: ") + rootItem.author
|
||||
color: palette.text
|
||||
font.pointSize: Constants.h5Font.pixelSize
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
horizontalAlignment: Qt.AlignRight
|
||||
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
/** Spacer **/
|
||||
Item
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 32
|
||||
}
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
SquareButton
|
||||
{
|
||||
Layout.preferredHeight: 36
|
||||
Layout.preferredWidth: 128
|
||||
text: qsTr("Download")
|
||||
icon.source: "qrc:/images/icons/icons8-download.svg"
|
||||
|
||||
onTriggered: () => rootItem.download()
|
||||
}
|
||||
}
|
||||
|
||||
/** Spacer **/
|
||||
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
Text {
|
||||
text: qsTr("Shaders Provided Courtesy of ShaderToy, Komplex and Their Respective Authors")
|
||||
elide: Text.ElideLeft
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h6Font.pixelSize
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
}
|
||||
|
||||
Image
|
||||
{
|
||||
transform: Image.PreserveAspectFit
|
||||
source: "qrc:/images/icons/pexels-icon-filled-256.svg"
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 32
|
||||
Layout.preferredWidth: 32
|
||||
}
|
||||
}
|
||||
}
|
||||
states:
|
||||
[
|
||||
State
|
||||
{
|
||||
name: "downloading"
|
||||
when: downloadManager.state === DownloadManager.Downloading
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: pageView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: downloadView
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: completedView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: errorView
|
||||
opacity: 0
|
||||
}
|
||||
},
|
||||
State
|
||||
{
|
||||
name: "complete"
|
||||
when: downloadManager.state === DownloadManager.Complete
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: pageView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: downloadView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: completedView
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: errorView
|
||||
opacity: 0
|
||||
}
|
||||
},
|
||||
State
|
||||
{
|
||||
name: "error"
|
||||
when: downloadManager.state === DownloadManager.Error
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: pageView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: downloadView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: completedView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: errorView
|
||||
opacity: 1
|
||||
}
|
||||
},
|
||||
State
|
||||
{
|
||||
name: "idle"
|
||||
when: downloadManager.state === DownloadManager.Idle
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: pageView
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: downloadView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: completedView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: errorView
|
||||
opacity: 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
DownloadView
|
||||
{
|
||||
id: downloadView
|
||||
anchors.fill: parent
|
||||
|
||||
manager: downloadManager
|
||||
thumbnail: rootItem.thumbnail
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
}
|
||||
|
||||
CompletedView
|
||||
{
|
||||
id: completedView
|
||||
anchors.fill: parent
|
||||
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
manager: downloadManager
|
||||
}
|
||||
|
||||
ErrorView
|
||||
{
|
||||
id: errorView
|
||||
anchors.fill: parent
|
||||
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
manager: downloadManager
|
||||
}
|
||||
|
||||
function download()
|
||||
{
|
||||
downloadManager.downloadPack(uuid)
|
||||
}
|
||||
|
||||
Component.onCompleted: () => downloadManager.reset()
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHub.Plugin
|
||||
|
||||
Item
|
||||
{
|
||||
property string uuid
|
||||
property string author
|
||||
property string authorUrl
|
||||
property string thumbnail
|
||||
property alias model: downloadSelector.model
|
||||
property int modelIndex
|
||||
|
||||
property int imageHeight
|
||||
property int imageWidth
|
||||
|
||||
visible: opacity > 0.01
|
||||
|
||||
Connections
|
||||
{
|
||||
target: model ? model : null
|
||||
|
||||
function onRowCountChanged()
|
||||
{
|
||||
downloadSelector.currentIndex = 0
|
||||
}
|
||||
}
|
||||
|
||||
DownloadManager
|
||||
{
|
||||
id: downloadManager
|
||||
}
|
||||
|
||||
id: rootItem
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: pageView
|
||||
anchors.fill: parent
|
||||
color: palette.base
|
||||
|
||||
BackgroundImage
|
||||
{
|
||||
anchors.fill: parent
|
||||
source: rootItem.thumbnail
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
anchors.fill: parent
|
||||
anchors.margins: Constants.largeMargin
|
||||
spacing: Constants.largeMargin
|
||||
|
||||
ImageFrame
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignHCenter
|
||||
Layout.topMargin: Constants.largeMargin
|
||||
Layout.preferredHeight: width / 1.77777777778
|
||||
Layout.maximumWidth: 800
|
||||
|
||||
source: rootItem.thumbnail
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: titleText
|
||||
text: rootItem.author
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h3Font.pixelSize
|
||||
elide: Text.ElideRight
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignLeft
|
||||
}
|
||||
|
||||
/** Spacer **/
|
||||
Item
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 32
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
text: qsTr("Available Downloads")
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h3Font.pixelSize
|
||||
elide: Text.ElideRight
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignLeft
|
||||
}
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
|
||||
ComboBox
|
||||
{
|
||||
id: downloadSelector
|
||||
textRole: "text"
|
||||
valueRole: "url"
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 36
|
||||
}
|
||||
SquareButton
|
||||
{
|
||||
Layout.preferredHeight: 36
|
||||
Layout.preferredWidth: 128
|
||||
text: qsTr("Download")
|
||||
icon.source: "qrc:/images/icons/icons8-download.svg"
|
||||
|
||||
onTriggered: () => rootItem.download()
|
||||
}
|
||||
}
|
||||
|
||||
/** Spacer **/
|
||||
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
Text {
|
||||
text: qsTr("Videos Provided Courtesy of Pexels and Their Respective Authors")
|
||||
elide: Text.ElideLeft
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h6Font.pixelSize
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
}
|
||||
|
||||
Image
|
||||
{
|
||||
transform: Image.PreserveAspectFit
|
||||
source: "qrc:/images/icons/pexels-icon-filled-256.svg"
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 32
|
||||
Layout.preferredWidth: 32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states:
|
||||
[
|
||||
State
|
||||
{
|
||||
name: "downloading"
|
||||
when: downloadManager.state === DownloadManager.Downloading
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: pageView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: downloadView
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: completedView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: errorView
|
||||
opacity: 0
|
||||
}
|
||||
},
|
||||
State
|
||||
{
|
||||
name: "complete"
|
||||
when: downloadManager.state === DownloadManager.Complete
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: pageView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: downloadView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: completedView
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: errorView
|
||||
opacity: 0
|
||||
}
|
||||
},
|
||||
State
|
||||
{
|
||||
name: "error"
|
||||
when: downloadManager.state === DownloadManager.Error
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: pageView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: downloadView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: completedView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: errorView
|
||||
opacity: 1
|
||||
}
|
||||
},
|
||||
State
|
||||
{
|
||||
name: "idle"
|
||||
when: downloadManager.state === DownloadManager.Idle
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: pageView
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: downloadView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: completedView
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
PropertyChanges
|
||||
{
|
||||
target: errorView
|
||||
opacity: 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
DownloadView
|
||||
{
|
||||
id: downloadView
|
||||
anchors.fill: parent
|
||||
|
||||
manager: downloadManager
|
||||
thumbnail: rootItem.thumbnail
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
}
|
||||
|
||||
CompletedView
|
||||
{
|
||||
id: completedView
|
||||
anchors.fill: parent
|
||||
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
manager: downloadManager
|
||||
}
|
||||
|
||||
ErrorView
|
||||
{
|
||||
id: errorView
|
||||
anchors.fill: parent
|
||||
|
||||
opacity: 0
|
||||
visible: opacity > 0.01
|
||||
manager: downloadManager
|
||||
}
|
||||
|
||||
function download()
|
||||
{
|
||||
downloadManager.downloadImage(author, authorUrl, downloadSelector.currentValue)
|
||||
}
|
||||
}
|
||||
@@ -18,5 +18,6 @@ qt6_add_qml_module(KomplexHubModule
|
||||
"WallpaperModelData.qml"
|
||||
)
|
||||
|
||||
set_target_properties(KomplexHubModule PROPERTIES VERSION "1.0")
|
||||
add_subdirectory("Controls")
|
||||
add_subdirectory("Kero")
|
||||
|
||||
@@ -63,7 +63,7 @@ QtObject {
|
||||
readonly property int normalAnimationDuration: 250
|
||||
readonly property int fastAnimationDuration: 125
|
||||
|
||||
property StudioApplication application: StudioApplication {
|
||||
fontPath: Qt.resolvedUrl("../KomplexHubContent/" + relativeFontDirectory)
|
||||
}
|
||||
// property StudioApplication application: StudioApplication {
|
||||
// fontPath: Qt.resolvedUrl("../KomplexHubContent/" + relativeFontDirectory)
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
|
||||
Item
|
||||
{
|
||||
property alias source: image.source
|
||||
property alias imageOpacity: effect.opacity
|
||||
property alias blur: effect.blur
|
||||
property alias saturation: effect.saturation
|
||||
|
||||
Image
|
||||
{
|
||||
anchors.fill: parent
|
||||
id: image
|
||||
source: thumbnail
|
||||
fillMode: Image.PreserveAspectFit
|
||||
visible: false
|
||||
}
|
||||
|
||||
MultiEffect
|
||||
{
|
||||
id: effect
|
||||
anchors.fill: image
|
||||
source: image
|
||||
blurEnabled: true
|
||||
blurMax: 64
|
||||
blur: 1.0
|
||||
opacity: 0.25
|
||||
saturation: -0.8
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
### This file is automatically generated by Qt Design Studio.
|
||||
### Do not change
|
||||
|
||||
qt_add_library(KomplexHubModule_Controls STATIC)
|
||||
qt6_add_qml_module(KomplexHubModule_Controls
|
||||
qt_add_library(KomplexHubControls STATIC)
|
||||
qt6_add_qml_module(KomplexHubControls
|
||||
URI "KomplexHub.Controls"
|
||||
VERSION 1.0
|
||||
RESOURCE_PREFIX "/qt/qml"
|
||||
@@ -14,12 +14,12 @@ qt6_add_qml_module(KomplexHubModule_Controls
|
||||
"Throbber.qml"
|
||||
"SearchResultItem.qml"
|
||||
"SearchResultList.qml"
|
||||
QML_FILES PaginationBox.qml
|
||||
QML_FILES PaginatorGrid.qml
|
||||
QML_FILES HorizontalPaginator.qml
|
||||
QML_FILES ImageView.qml
|
||||
QML_FILES
|
||||
QML_FILES VideoView.qml
|
||||
QML_FILES PackView.qml
|
||||
"PaginationBox.qml"
|
||||
"PaginatorGrid.qml"
|
||||
"HorizontalPaginator.qml"
|
||||
"VerticalSpacer.qml"
|
||||
"ImageFrame.qml"
|
||||
"BackgroundImage.qml"
|
||||
)
|
||||
|
||||
#set_target_properties(KomplexHubControls PROPERTIES VERSION "1.0")
|
||||
|
||||
@@ -5,7 +5,7 @@ import QtQuick.Layouts
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHubPlugin
|
||||
import KomplexHub.Plugin
|
||||
|
||||
Item
|
||||
{
|
||||
@@ -25,7 +25,10 @@ Item
|
||||
|
||||
property Component delegate
|
||||
|
||||
signal viewMoreTriggered()
|
||||
|
||||
id: rootItem
|
||||
clip: true
|
||||
|
||||
Item
|
||||
{
|
||||
@@ -45,8 +48,6 @@ Item
|
||||
height: 245
|
||||
color: palette.accent
|
||||
radius: 5
|
||||
// x: resultsView.currentItem.x
|
||||
// y: resultsView.currentItem.y
|
||||
Behavior on x { SpringAnimation { spring: 3; damping: 0.2 } }
|
||||
Behavior on y { SpringAnimation { spring: 3; damping: 0.2 } }
|
||||
}
|
||||
@@ -57,6 +58,7 @@ Item
|
||||
id: resultsView
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
clip: true
|
||||
|
||||
spacing: Constants.mediumMargin
|
||||
|
||||
@@ -132,7 +134,7 @@ Item
|
||||
Item
|
||||
{
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: 128
|
||||
Layout.preferredWidth: 64
|
||||
Rectangle
|
||||
{
|
||||
id: gradientMap
|
||||
@@ -162,6 +164,8 @@ Item
|
||||
icon.source: "qrc:/images/icons/icons8-forward.svg"
|
||||
icon.height: 24
|
||||
icon.width: 24
|
||||
|
||||
onTriggered: () => viewMoreTriggered()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item
|
||||
{
|
||||
property alias source: viewImage.source
|
||||
id: rootItem
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.fill: parent
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
color: palette.base.darker()
|
||||
border.color: palette.midlight
|
||||
|
||||
Throbber
|
||||
{
|
||||
anchors.fill: parent
|
||||
id: loadingThrobber
|
||||
visible: viewImage.status === Image.Loading
|
||||
}
|
||||
|
||||
Image
|
||||
{
|
||||
id: viewImage
|
||||
anchors.fill: parent
|
||||
transform: Image.PreserveAspectCrop
|
||||
source: rootItem.source
|
||||
visible: !(status === Image.Loading)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHubPlugin
|
||||
|
||||
Item
|
||||
{
|
||||
property string uuid
|
||||
property string author
|
||||
property string authorUrl
|
||||
property string description
|
||||
property string thumbnail
|
||||
property string portrait
|
||||
property string landscape
|
||||
property string original
|
||||
property string fullScreen
|
||||
property string portraitSize
|
||||
property string landscapeSize
|
||||
property string fullScreenSize
|
||||
property string backgroundPortrait
|
||||
property string backgroundLandscape
|
||||
|
||||
property int imageHeight
|
||||
property int imageWidth
|
||||
|
||||
id: rootItem
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.fill: parent
|
||||
color: palette.base
|
||||
|
||||
Image
|
||||
{
|
||||
id: backgroundImage
|
||||
source: width > height ? backgroundLandscape : backgroundPortrait
|
||||
transform: Image.PreserveAspectCrop
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
}
|
||||
|
||||
MultiEffect
|
||||
{
|
||||
anchors.fill: backgroundImage
|
||||
source: backgroundImage
|
||||
blurEnabled: true
|
||||
blurMax: 64
|
||||
blur: 1.0
|
||||
opacity: 0.25
|
||||
saturation: -0.8
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
anchors.fill: parent
|
||||
anchors.margins: Constants.largeMargin
|
||||
spacing: Constants.largeMargin
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignHCenter
|
||||
Layout.topMargin: 32
|
||||
|
||||
Rectangle
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: width / 1.77777777778
|
||||
Layout.maximumWidth: 800
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: palette.base.darker()
|
||||
|
||||
Throbber
|
||||
{
|
||||
anchors.fill: parent
|
||||
id: loadingThrobber
|
||||
visible: viewImage.status === Image.Loading
|
||||
}
|
||||
|
||||
Image
|
||||
{
|
||||
id: viewImage
|
||||
anchors.fill: parent
|
||||
transform: Image.PreserveAspectCrop
|
||||
source: rootItem.thumbnail
|
||||
visible: !(status === Image.Loading)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: titleText
|
||||
text: rootItem.author
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h3Font.pixelSize
|
||||
elide: Text.ElideRight
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignLeft
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: descriptionText
|
||||
text: rootItem.description
|
||||
color: palette.text
|
||||
font.pointSize: Constants.h4Font.pixelSize
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
/** Spacer **/
|
||||
Item
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 32
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
text: qsTr("Available Downloads")
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h3Font.pixelSize
|
||||
elide: Text.ElideRight
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignLeft
|
||||
}
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
|
||||
ComboBox
|
||||
{
|
||||
id: downloadSelector
|
||||
model:[
|
||||
"Screen (" + fullScreenSize + ")",
|
||||
"Portrait (" + portraitSize + ")",
|
||||
"Landscape (" + landscapeSize + ")"
|
||||
]
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 36
|
||||
}
|
||||
SquareButton
|
||||
{
|
||||
Layout.preferredHeight: 36
|
||||
Layout.preferredWidth: 128
|
||||
text: qsTr("Download")
|
||||
icon.source: "qrc:/images/icons/icons8-download.svg"
|
||||
}
|
||||
}
|
||||
|
||||
/** Spacer **/
|
||||
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
Text {
|
||||
text: qsTr("Images Provided Courtesy of Pexels")
|
||||
elide: Text.ElideLeft
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h6Font.pixelSize
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
}
|
||||
|
||||
Image
|
||||
{
|
||||
transform: Image.PreserveAspectFit
|
||||
source: "qrc:/images/icons/pexels-icon-filled-256.svg"
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 32
|
||||
Layout.preferredWidth: 32
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHubPlugin
|
||||
|
||||
Item
|
||||
{
|
||||
property string uuid
|
||||
property string author
|
||||
property string authorId
|
||||
property string description
|
||||
property string thumbnail
|
||||
|
||||
property int imageHeight
|
||||
property int imageWidth
|
||||
|
||||
id: rootItem
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.fill: parent
|
||||
color: palette.base
|
||||
|
||||
Image
|
||||
{
|
||||
id: backgroundImage
|
||||
source: thumbnail
|
||||
transform: Image.PreserveAspectCrop
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
}
|
||||
|
||||
MultiEffect
|
||||
{
|
||||
anchors.fill: backgroundImage
|
||||
source: backgroundImage
|
||||
blurEnabled: true
|
||||
blurMax: 64
|
||||
blur: 1.0
|
||||
opacity: 0.25
|
||||
saturation: -0.8
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
anchors.fill: parent
|
||||
anchors.margins: Constants.largeMargin
|
||||
spacing: Constants.largeMargin
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignHCenter
|
||||
Layout.topMargin: 32
|
||||
|
||||
Rectangle
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: width / 1.77777777778
|
||||
Layout.maximumWidth: 800
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: palette.base.darker()
|
||||
|
||||
Throbber
|
||||
{
|
||||
anchors.fill: parent
|
||||
id: loadingThrobber
|
||||
visible: viewImage.status === Image.Loading
|
||||
}
|
||||
|
||||
Image
|
||||
{
|
||||
id: viewImage
|
||||
anchors.fill: parent
|
||||
transform: Image.PreserveAspectCrop
|
||||
source: rootItem.thumbnail
|
||||
visible: !(status === Image.Loading)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: titleText
|
||||
text: rootItem.author
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h3Font.pixelSize
|
||||
elide: Text.ElideRight
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignLeft
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: descriptionText
|
||||
text: rootItem.description
|
||||
color: palette.text
|
||||
font.pointSize: Constants.h4Font.pixelSize
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
/** Spacer **/
|
||||
Item
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 32
|
||||
}
|
||||
|
||||
// Text
|
||||
// {
|
||||
// text: qsTr("Available Downloads")
|
||||
// color: palette.text
|
||||
// font.bold: true
|
||||
// font.pixelSize: Constants.h3Font.pixelSize
|
||||
// elide: Text.ElideRight
|
||||
|
||||
// Layout.fillWidth: true
|
||||
// Layout.alignment: Qt.AlignTop| Qt.AlignLeft
|
||||
// }
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
SquareButton
|
||||
{
|
||||
Layout.preferredHeight: 36
|
||||
Layout.preferredWidth: 128
|
||||
text: qsTr("Download")
|
||||
icon.source: "qrc:/images/icons/icons8-download.svg"
|
||||
}
|
||||
}
|
||||
|
||||
/** Spacer **/
|
||||
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
Text {
|
||||
text: qsTr("Shaders Provided Courtesy of ShaderToy and Their Respective Authors")
|
||||
elide: Text.ElideLeft
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h6Font.pixelSize
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
}
|
||||
|
||||
Image
|
||||
{
|
||||
transform: Image.PreserveAspectFit
|
||||
source: "qrc:/images/icons/pexels-icon-filled-256.svg"
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 32
|
||||
Layout.preferredWidth: 32
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import QtQuick.Layouts
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHubPlugin
|
||||
import KomplexHub.Plugin
|
||||
|
||||
Item
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ Item {
|
||||
signal triggered
|
||||
signal viewMoreTriggered
|
||||
|
||||
id: mainItem
|
||||
id: rootItem
|
||||
width: 256
|
||||
height: 256
|
||||
clip: true
|
||||
@@ -48,7 +48,7 @@ Item {
|
||||
onReleased: () => {
|
||||
if(!states.selected)
|
||||
{
|
||||
mainItem.triggered()
|
||||
rootItem.triggered()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,47 +57,13 @@ Item {
|
||||
{
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle
|
||||
ImageFrame
|
||||
{
|
||||
Layout.preferredWidth: 250
|
||||
Layout.preferredHeight: 141
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
|
||||
Layout.topMargin: 3
|
||||
|
||||
border.color: palette.alternateBase.lighter(1.75)
|
||||
|
||||
color: palette.base.lighter(1.25)
|
||||
|
||||
Throbber
|
||||
{
|
||||
anchors.fill: parent
|
||||
id: loadingThrobber
|
||||
visible: viewImage.status === Image.Loading
|
||||
}
|
||||
|
||||
Image
|
||||
{
|
||||
property int retries: 0
|
||||
id: viewImage
|
||||
anchors.fill: parent
|
||||
source: mainItem.thumbnail
|
||||
visible: !(status === Image.Loading)
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
|
||||
onStatusChanged: () =>
|
||||
{
|
||||
// if(status === Image.Error)
|
||||
// {
|
||||
// if(retries <= 3)
|
||||
// {
|
||||
// ++retries
|
||||
// let src = source
|
||||
// source = ""
|
||||
// source = src
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
source: rootItem.thumbnail
|
||||
|
||||
Image
|
||||
{
|
||||
@@ -108,18 +74,18 @@ Item {
|
||||
|
||||
width: 24
|
||||
height: 24
|
||||
transform: Image.PreserveAspectFit
|
||||
fillMode: Image.PreserveAspectFit
|
||||
opacity: 0.5
|
||||
source: "qrc:/images/icons/pexels-icon-filled-256.svg"
|
||||
|
||||
visible: mainItem.pexels
|
||||
visible: rootItem.pexels
|
||||
}
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
color: palette.text
|
||||
text: mainItem.title
|
||||
text: rootItem.title
|
||||
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
@@ -131,25 +97,13 @@ Item {
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
Text
|
||||
{
|
||||
color: palette.text
|
||||
font.pixelSize: 12
|
||||
text: "By: " + mainItem.author
|
||||
leftPadding: 12
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignBottom
|
||||
Layout.preferredWidth: 250
|
||||
visible: !mainItem.pexels
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: descriptionText
|
||||
clip: true
|
||||
color: palette.text
|
||||
font.pixelSize: 12
|
||||
text: mainItem.description
|
||||
text: rootItem.description
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
@@ -162,6 +116,19 @@ Item {
|
||||
Layout.bottomMargin: 3
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
color: palette.text
|
||||
font.pixelSize: 12
|
||||
text: "By: " + rootItem.author
|
||||
leftPadding: Constants.mediumMargin
|
||||
rightPadding: Constants.mediumMargin
|
||||
horizontalAlignment: Qt.AlignRight
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignBottom
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
SquareButton
|
||||
{
|
||||
id: viewMoreButton
|
||||
@@ -176,7 +143,7 @@ Item {
|
||||
opacity: 0
|
||||
|
||||
onTriggered: () => {
|
||||
mainItem.viewMoreTriggered()
|
||||
rootItem.viewMoreTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +156,7 @@ Item {
|
||||
id: states
|
||||
property bool clicked: false
|
||||
property bool hovered: false
|
||||
property alias selected: mainItem.selected
|
||||
property alias selected: rootItem.selected
|
||||
property bool idle: true
|
||||
|
||||
onIdleChanged: () =>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Rectangle
|
||||
{
|
||||
Layout.fillHeight: true
|
||||
|
||||
color: "transparent"
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
|
||||
import KomplexHub
|
||||
import KomplexHub.Controls
|
||||
import KomplexHub.Kero
|
||||
import KomplexHubPlugin
|
||||
|
||||
Item
|
||||
{
|
||||
property string uuid
|
||||
property string author
|
||||
property string authorUrl
|
||||
property string thumbnail
|
||||
property alias model: downloadSelector.model
|
||||
property int modelIndex
|
||||
|
||||
property int imageHeight
|
||||
property int imageWidth
|
||||
|
||||
visible: opacity > 0.01
|
||||
onVisibleChanged: () =>
|
||||
{
|
||||
if(!visible)
|
||||
{
|
||||
model = null
|
||||
}
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: model
|
||||
|
||||
function onRowCountChanged()
|
||||
{
|
||||
downloadSelector.currentIndex = 0
|
||||
}
|
||||
}
|
||||
|
||||
id: rootItem
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.fill: parent
|
||||
color: palette.base
|
||||
|
||||
Image
|
||||
{
|
||||
id: backgroundImage
|
||||
source: thumbnail
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
MultiEffect
|
||||
{
|
||||
anchors.fill: backgroundImage
|
||||
source: backgroundImage
|
||||
blurEnabled: true
|
||||
blurMax: 64
|
||||
blur: 1.0
|
||||
opacity: 0.25
|
||||
saturation: -0.8
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
anchors.fill: parent
|
||||
anchors.margins: Constants.largeMargin
|
||||
spacing: Constants.largeMargin
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignHCenter
|
||||
Layout.topMargin: 32
|
||||
|
||||
Rectangle
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: width / 1.77777777778
|
||||
Layout.maximumWidth: 800
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: palette.base.darker()
|
||||
|
||||
Throbber
|
||||
{
|
||||
anchors.fill: parent
|
||||
id: loadingThrobber
|
||||
visible: viewImage.status === Image.Loading
|
||||
}
|
||||
|
||||
Image
|
||||
{
|
||||
id: viewImage
|
||||
anchors.fill: parent
|
||||
source: rootItem.thumbnail
|
||||
visible: !(status === Image.Loading)
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: titleText
|
||||
text: rootItem.author
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h3Font.pixelSize
|
||||
elide: Text.ElideRight
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignLeft
|
||||
}
|
||||
|
||||
/** Spacer **/
|
||||
Item
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 32
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
text: qsTr("Available Downloads")
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h3Font.pixelSize
|
||||
elide: Text.ElideRight
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop| Qt.AlignLeft
|
||||
}
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
|
||||
ComboBox
|
||||
{
|
||||
id: downloadSelector
|
||||
textRole: "text"
|
||||
valueRole: "url"
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 36
|
||||
}
|
||||
SquareButton
|
||||
{
|
||||
Layout.preferredHeight: 36
|
||||
Layout.preferredWidth: 128
|
||||
text: qsTr("Download")
|
||||
icon.source: "qrc:/images/icons/icons8-download.svg"
|
||||
}
|
||||
}
|
||||
|
||||
/** Spacer **/
|
||||
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
Text {
|
||||
text: qsTr("Videos Provided Courtesy of Pexels and Their Respective Authors")
|
||||
elide: Text.ElideLeft
|
||||
color: palette.text
|
||||
font.bold: true
|
||||
font.pixelSize: Constants.h6Font.pixelSize
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
}
|
||||
|
||||
Image
|
||||
{
|
||||
transform: Image.PreserveAspectFit
|
||||
source: "qrc:/images/icons/pexels-icon-filled-256.svg"
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 32
|
||||
Layout.preferredWidth: 32
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
qt_add_library(KomplexHubModule_Kero STATIC)
|
||||
qt6_add_qml_module(KomplexHubModule_Kero
|
||||
qt_add_library(KomplexHubKero STATIC)
|
||||
qt6_add_qml_module(KomplexHubKero
|
||||
URI "KomplexHub.Kero"
|
||||
VERSION 1.0
|
||||
RESOURCE_PREFIX "/qt/qml"
|
||||
@@ -11,4 +11,7 @@ qt6_add_qml_module(KomplexHubModule_Kero
|
||||
"KeroLoadingAnimation.qml"
|
||||
"KeroNoResultsAvatar.qml"
|
||||
"KeroWarningAvatar.qml"
|
||||
"KeroHeader.qml"
|
||||
"KeroSuccessAvatar.qml"
|
||||
)
|
||||
#set_target_properties(KomplexHubKero PROPERTIES VERSION "1.0")
|
||||
@@ -4,9 +4,9 @@ import QtQuick.Layouts
|
||||
import KomplexHub
|
||||
|
||||
Item {
|
||||
property string title: qsTr("Loading")
|
||||
property string description: qsTr("Kero is busy looking for wallpapers..we promise")
|
||||
property color color: palette.window
|
||||
property string title: qsTr("Building")
|
||||
property string description: qsTr("Kero is getting your wallpaper ready")
|
||||
property color color: "transparent"
|
||||
|
||||
id: keroLoadingOverlayRoot
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Layouts
|
||||
import KomplexHub
|
||||
|
||||
Item
|
||||
{
|
||||
property string title: qsTr("Wallpaper Spotlight")
|
||||
property string description: qsTr("Featured Images and Live Wallpaper Packs")
|
||||
property color color: palette.alternateBase
|
||||
property string image: "qrc:/images/kero/kero_spotlight.png"
|
||||
|
||||
id: rootItem
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.fill: parent
|
||||
color: rootItem.color
|
||||
border.color: palette.midlight
|
||||
|
||||
RowLayout
|
||||
{
|
||||
anchors.fill: parent
|
||||
|
||||
Item
|
||||
{
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: height
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
|
||||
color: "transparent"
|
||||
|
||||
Image
|
||||
{
|
||||
anchors.fill: parent
|
||||
|
||||
source: image
|
||||
antialiasing: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
|
||||
Text
|
||||
{
|
||||
color: palette.text
|
||||
text: rootItem.title
|
||||
font: Constants.h1Font
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: errorText
|
||||
text: rootItem.description
|
||||
color: palette.text.darker()
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||
Layout.bottomMargin: Constants.largeMargin
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import KomplexHub
|
||||
|
||||
Item {
|
||||
property string title: qsTr("Success!")
|
||||
property string description: qsTr("Your new wallpaper is ready!")
|
||||
property color color: "transparent"
|
||||
|
||||
id: keroHelloAvatarRoot
|
||||
|
||||
Rectangle {
|
||||
id: keroHelloAvatarBackground
|
||||
anchors.fill: parent
|
||||
color: keroHelloAvatarRoot.color
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumWidth: 64
|
||||
Layout.minimumHeight: 64
|
||||
Layout.maximumWidth: 512
|
||||
Layout.maximumHeight: 512
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
|
||||
color: "transparent"
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
|
||||
source: "qrc:/images/kero/kero_success.png"
|
||||
antialiasing: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Text {
|
||||
color: palette.text
|
||||
text: keroHelloAvatarRoot.title
|
||||
font: Constants.h1Font
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||
}
|
||||
|
||||
Text {
|
||||
id: errorText
|
||||
text: keroHelloAvatarRoot.description
|
||||
color: palette.text.darker()
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||
Layout.bottomMargin: Constants.largeMargin
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import KomplexHub
|
||||
Item {
|
||||
property string title: qsTr("Warning")
|
||||
property string description: qsTr("Kero noticed there might be an issue")
|
||||
property color color: palette.window
|
||||
property color color: "transparent"
|
||||
|
||||
id: keroWarningAvatarRoot
|
||||
|
||||
|
||||
+1
Submodule KomplexHubPlugin/3rdparty/qtkeychain added at 087369387c
@@ -1,12 +1,38 @@
|
||||
find_package(
|
||||
Qt6
|
||||
REQUIRED COMPONENTS
|
||||
Core
|
||||
Gui
|
||||
GuiPrivate
|
||||
Widgets
|
||||
Qml
|
||||
Quick
|
||||
QuickControls2
|
||||
QuickTimeline
|
||||
ShaderTools
|
||||
ShaderToolsPrivate
|
||||
Concurrent
|
||||
)
|
||||
|
||||
find_package(
|
||||
OpenGL REQUIRED COMPONENTS OpenGL
|
||||
)
|
||||
|
||||
find_package(
|
||||
Vulkan REQUIRED COMPONENTS glslang SPIRV-Tools
|
||||
)
|
||||
|
||||
add_library(
|
||||
KomplexHubPlugin
|
||||
STATIC
|
||||
)
|
||||
|
||||
set_target_properties(KomplexHubPlugin PROPERTIES VERSION "1.0")
|
||||
|
||||
qt_add_qml_module(
|
||||
KomplexHubPlugin
|
||||
URI
|
||||
KomplexHubPlugin
|
||||
"KomplexHub.Plugin"
|
||||
VERSION
|
||||
1.0
|
||||
SOURCES
|
||||
@@ -19,23 +45,17 @@ qt_add_qml_module(
|
||||
newestpacksmodel.cpp
|
||||
common/komplex_global.h
|
||||
common/slidingcachecontroller.h
|
||||
common/paginator.h
|
||||
common/coreservices.h
|
||||
common/coreservices.cpp
|
||||
common/logging.h
|
||||
common/fetchresult.h
|
||||
paginators/paginator.h
|
||||
paginators/newestpackspaginator.h
|
||||
paginators/featuredpackspaginator.h
|
||||
paginators/featuredimagespaginator.h
|
||||
paginators/wallpaperpaginator.h
|
||||
paginators/packsearchpaginator.h
|
||||
paginators/imagesearchpaginator.h
|
||||
paginators/newestshaderspaginator.h
|
||||
paginators/newestcubemapspaginator.h
|
||||
paginators/featuredshaderspaginator.h
|
||||
paginators/featuredcubemapspaginator.h
|
||||
paginators/searchshaderspaginator.h
|
||||
paginators/searchcubemapspaginator.h
|
||||
paginators/videosearchpaginator.h
|
||||
featuredpacksmodel.h
|
||||
featuredpacksmodel.cpp
|
||||
@@ -60,7 +80,15 @@ qt_add_qml_module(
|
||||
common/exceptions.h
|
||||
common/shadertoymetadata.h
|
||||
common/shaderpack.h
|
||||
SOURCES packcompiler.h packcompiler.cpp
|
||||
packcompiler.h
|
||||
packcompiler.cpp
|
||||
common/userservices.h
|
||||
common/userservices.cpp
|
||||
common/qwallet.h
|
||||
common/qwallet.cpp
|
||||
usermodel.h
|
||||
usermodel.cpp
|
||||
SOURCES
|
||||
)
|
||||
|
||||
target_compile_definitions(
|
||||
@@ -69,9 +97,17 @@ target_compile_definitions(
|
||||
KOMPLEX_PLUGIN
|
||||
)
|
||||
|
||||
add_subdirectory(3rdparty/qtkeychain)
|
||||
|
||||
target_link_libraries(KomplexHubPlugin PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt6::Core
|
||||
Qt6::Concurrent
|
||||
Qt6::Gui
|
||||
Qt6::GuiPrivate
|
||||
Qt6::ShaderTools
|
||||
Qt6::ShaderToolsPrivate
|
||||
Vulkan::Vulkan
|
||||
qt6keychain
|
||||
)
|
||||
|
||||
if(ENABLE_ASAN)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "coreservices.h"
|
||||
#include "logging.h"
|
||||
#include "userservices.h"
|
||||
|
||||
QNetworkAccessManager *CoreServices::s_networkAccessManager = new QNetworkAccessManager();
|
||||
QNetworkAccessManager *CoreServices::s_networkAccessManager = nullptr;
|
||||
QMutex CoreServices::s_networkAccessMutex;
|
||||
QSharedPointer<QNetworkAccessManager> CoreServices::s_networkAccessPointer;
|
||||
|
||||
@@ -11,7 +12,9 @@ auto CoreServices::networkAccessManager() -> QWeakPointer<QNetworkAccessManager>
|
||||
|
||||
if(!s_networkAccessPointer)
|
||||
{
|
||||
s_networkAccessManager = new QNetworkAccessManager;
|
||||
if(!s_networkAccessManager)
|
||||
{
|
||||
s_networkAccessManager = new QNetworkAccessManager(QCoreApplication::instance());
|
||||
s_networkAccessManager->setAutoDeleteReplies(true);
|
||||
|
||||
QObject::connect(
|
||||
@@ -28,11 +31,12 @@ auto CoreServices::networkAccessManager() -> QWeakPointer<QNetworkAccessManager>
|
||||
);
|
||||
}
|
||||
|
||||
#ifdef KOMPLEX_LOCAL_DEV
|
||||
#ifdef KOMPLEX_LOCAL_DEV
|
||||
reply->ignoreSslErrors();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
s_networkAccessPointer = QSharedPointer<QNetworkAccessManager>(
|
||||
s_networkAccessManager,
|
||||
@@ -45,11 +49,7 @@ auto CoreServices::networkAccessManager() -> QWeakPointer<QNetworkAccessManager>
|
||||
|
||||
auto CoreServices::sessionToken() -> QByteArray
|
||||
{
|
||||
LOG_ERROR_X(true,
|
||||
"CoreServices::sessionToken",
|
||||
"User services not yet implemented",
|
||||
{}
|
||||
);
|
||||
return UserServices::userCredentials().sessionToken;
|
||||
}
|
||||
|
||||
auto CoreServices::screenSize() -> QSize
|
||||
|
||||
@@ -1,34 +1,215 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2026 @DigitalArtifex
|
||||
* https://digitalartifex.dev - https://github.com/DigitalArtifex
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef EXCEPTIONS_H
|
||||
#define EXCEPTIONS_H
|
||||
#include <QString>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include "komplex_global.h"
|
||||
|
||||
struct KOMPLEX_EXPORT Exception : std::exception
|
||||
struct KOMPLEX_EXPORT network_exception : std::ios_base::failure
|
||||
{
|
||||
explicit Exception(const QString &message, quint16 errorCode = 0)
|
||||
: std::exception(), message(message), errorCode(errorCode){}
|
||||
network_exception(const std::string &message, const std::string &details, const std::error_code &code) :
|
||||
details(details),
|
||||
std::ios_base::failure(message, code) { }
|
||||
|
||||
const QString message;
|
||||
const quint16 errorCode;
|
||||
network_exception(const std::string &message, const std::error_code &code) :
|
||||
std::ios_base::failure(message, code) { }
|
||||
|
||||
network_exception(const QString &message, const QString &details, const std::error_code &code) :
|
||||
details(details.toStdString()),
|
||||
std::ios_base::failure(message.toStdString(), code) { }
|
||||
|
||||
network_exception(const QString &message, const int code = errno) :
|
||||
std::ios_base::failure
|
||||
(
|
||||
message.toStdString(),
|
||||
std::error_code
|
||||
(
|
||||
code,
|
||||
std::system_category()
|
||||
)
|
||||
) { }
|
||||
|
||||
const std::string details;
|
||||
};
|
||||
|
||||
struct NetworkException : Exception
|
||||
struct KOMPLEX_EXPORT file_exception : std::filesystem::filesystem_error
|
||||
{
|
||||
NetworkException(const QString &message, qsizetype errorCode = 0) : Exception(message, errorCode) {}
|
||||
};
|
||||
struct FileException : Exception
|
||||
{
|
||||
FileException(const QString &message, qsizetype errorCode = 0) : Exception(message, errorCode) {}
|
||||
};
|
||||
struct SqlException : Exception
|
||||
{
|
||||
SqlException(const QString &message, qsizetype errorCode = 0) : Exception(message, errorCode) {}
|
||||
file_exception(const std::string &message, const std::string &details, const std::error_code &code) :
|
||||
details(details),
|
||||
std::filesystem::filesystem_error(message, code) { }
|
||||
|
||||
file_exception(const std::string &message, const std::error_code &code) :
|
||||
std::filesystem::filesystem_error(message, code) { }
|
||||
|
||||
file_exception(const std::string &message, const int code = errno) :
|
||||
std::filesystem::filesystem_error
|
||||
(
|
||||
message,
|
||||
std::error_code
|
||||
(
|
||||
code,
|
||||
std::system_category()
|
||||
)
|
||||
) { }
|
||||
|
||||
file_exception(const std::string &message, const std::string &details, const int code = errno) :
|
||||
details(details),
|
||||
std::filesystem::filesystem_error
|
||||
(
|
||||
message,
|
||||
std::error_code
|
||||
(
|
||||
code,
|
||||
std::system_category()
|
||||
)
|
||||
) { }
|
||||
|
||||
const std::string details;
|
||||
};
|
||||
|
||||
|
||||
struct KOMPLEX_EXPORT ShaderCompilerException : Exception
|
||||
struct KOMPLEX_EXPORT sql_exception : std::ios_base::failure
|
||||
{
|
||||
ShaderCompilerException(const QString &message, qsizetype errorCode = 0) : Exception(message, errorCode) {}
|
||||
sql_exception(const std::string &message, const std::string &details, const std::error_code &code) :
|
||||
details(details),
|
||||
std::ios_base::failure(message, code) { }
|
||||
|
||||
sql_exception(const std::string &message, const std::error_code &code) :
|
||||
std::ios_base::failure(message, code) { }
|
||||
|
||||
sql_exception(const std::string &message, const int code = errno) :
|
||||
std::ios_base::failure
|
||||
(
|
||||
message,
|
||||
std::error_code
|
||||
(
|
||||
code,
|
||||
std::system_category()
|
||||
)
|
||||
) { }
|
||||
|
||||
const std::string details;
|
||||
};
|
||||
|
||||
struct KOMPLEX_EXPORT wallet_exception : std::ios_base::failure
|
||||
{
|
||||
wallet_exception(const std::string &message, const std::string &details, const std::error_code &code) :
|
||||
details(details),
|
||||
std::ios_base::failure(message, code) { }
|
||||
|
||||
wallet_exception(const std::string &message, const std::error_code &code) :
|
||||
std::ios_base::failure(message, code) { }
|
||||
|
||||
wallet_exception(const std::string &message, const std::string &details, const int code = errno) :
|
||||
details(details),
|
||||
std::ios_base::failure
|
||||
(
|
||||
message,
|
||||
std::error_code
|
||||
(
|
||||
code,
|
||||
std::system_category()
|
||||
)
|
||||
) { }
|
||||
|
||||
wallet_exception(const std::string &message, const int code = errno) :
|
||||
std::ios_base::failure
|
||||
(
|
||||
message,
|
||||
std::error_code
|
||||
(
|
||||
code,
|
||||
std::system_category()
|
||||
)
|
||||
) { }
|
||||
|
||||
const std::string details;
|
||||
};
|
||||
|
||||
struct KOMPLEX_EXPORT process_exception : std::runtime_error
|
||||
{
|
||||
process_exception(const std::string &message, const std::string &details, const std::error_code &code) :
|
||||
details(details),
|
||||
code(code),
|
||||
std::runtime_error(message) { }
|
||||
|
||||
process_exception(const std::string &message, const std::error_code &code) :
|
||||
code(code),
|
||||
std::runtime_error(message) { }
|
||||
|
||||
process_exception(const std::string &message, const int code = errno) :
|
||||
code
|
||||
(
|
||||
std::error_code
|
||||
(
|
||||
code,
|
||||
std::system_category()
|
||||
)
|
||||
),
|
||||
std::runtime_error
|
||||
(
|
||||
message
|
||||
) { }
|
||||
|
||||
process_exception(const std::string &message, const std::string &details, const int code = errno) :
|
||||
details(details),
|
||||
code
|
||||
(
|
||||
std::error_code
|
||||
(
|
||||
code,
|
||||
std::system_category()
|
||||
)
|
||||
),
|
||||
std::runtime_error (message) { }
|
||||
|
||||
const std::string details;
|
||||
const std::error_code code;
|
||||
};
|
||||
|
||||
namespace shader
|
||||
{
|
||||
struct KOMPLEX_EXPORT logic_error : std::logic_error
|
||||
{
|
||||
logic_error(const std::string &message, const std::string &details, const std::error_code &code) :
|
||||
details(details),
|
||||
code(code),
|
||||
std::logic_error(message) { }
|
||||
|
||||
logic_error(const std::string &message, const std::error_code &code) :
|
||||
code(code),
|
||||
std::logic_error(message) { }
|
||||
|
||||
logic_error(const std::string &message, const std::string &details = {}, const int code = errno) :
|
||||
code
|
||||
(
|
||||
std::error_code
|
||||
(
|
||||
code,
|
||||
std::system_category()
|
||||
)
|
||||
),
|
||||
std::logic_error(message) { }
|
||||
|
||||
const std::string details;
|
||||
const std::error_code code;
|
||||
};
|
||||
}
|
||||
#endif // EXCEPTIONS_H
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2026 @DigitalArtifex
|
||||
* https://digitalartifex.dev - https://github.com/DigitalArtifex
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef IMAGECACHE_H
|
||||
#define IMAGECACHE_H
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#ifndef KOMPLEX_GLOBAL_H
|
||||
#define KOMPLEX_GLOBAL_H
|
||||
#include <QtTypes>
|
||||
#include <QString>
|
||||
#include <chrono>
|
||||
|
||||
#ifdef KOMPLEX_PLUGIN
|
||||
@@ -36,35 +37,41 @@
|
||||
#ifdef KOMPLEX_LOCAL_DEV
|
||||
#define KOMPLEX_API_HOST "https://komplexapi.hlab"
|
||||
#else
|
||||
#define KOMPLEX_API_HOST "https://api.komplex.dev"
|
||||
inline const QString KOMPLEX_API_HOST = QStringLiteral("https://api.komplex.dev");
|
||||
#endif
|
||||
|
||||
#define KOMPLEX_API_VERSION "v2"
|
||||
inline const QString KOMPLEX_API_VERSION = QStringLiteral("v2");
|
||||
|
||||
#define KOMPLEX_ENDPOINT_IMAGES_SEARCH "images/search"
|
||||
#define KOMPLEX_ENDPOINT_IMAGES_FEATURED "featured/images"
|
||||
#define KOMPLEX_ENDPOINT_IMAGES_ITEM "images/item"
|
||||
inline const QString KOMPLEX_ENDPOINT_IMAGES_SEARCH = QStringLiteral("images/search");
|
||||
inline const QString KOMPLEX_ENDPOINT_IMAGES_FEATURED = QStringLiteral("featured/images");
|
||||
inline const QString KOMPLEX_ENDPOINT_IMAGES_ITEM = QStringLiteral("images/item");
|
||||
|
||||
#define KOMPLEX_ENDPOINT_VIDEOS_SEARCH "videos/search"
|
||||
#define KOMPLEX_ENDPOINT_VIDEOS_FEATURED "featured/videos"
|
||||
#define KOMPLEX_ENDPOINT_VIDEOS_ITEM "videos/item"
|
||||
inline const QString KOMPLEX_ENDPOINT_VIDEOS_SEARCH = QStringLiteral("videos/search");
|
||||
inline const QString KOMPLEX_ENDPOINT_VIDEOS_FEATURED = QStringLiteral("featured/videos");
|
||||
inline const QString KOMPLEX_ENDPOINT_VIDEOS_ITEM = QStringLiteral("videos/item");
|
||||
|
||||
#define KOMPLEX_ENDPOINT_SHADERS_SEARCH "shaders/search"
|
||||
#define KOMPLEX_ENDPOINT_SHADERS_FEATURED "shaders/featured"
|
||||
#define KOMPLEX_ENDPOINT_SHADERS_ITEM "shaders/item"
|
||||
#define KOMPLEX_ENDPOINT_SHADERS_NEWEST "shaders/newest"
|
||||
inline const QString KOMPLEX_ENDPOINT_SHADERS_SEARCH = QStringLiteral("shaders/search");
|
||||
inline const QString KOMPLEX_ENDPOINT_SHADERS_FEATURED = QStringLiteral("shaders/featured");
|
||||
inline const QString KOMPLEX_ENDPOINT_SHADERS_ITEM = QStringLiteral("shaders/item");
|
||||
inline const QString KOMPLEX_ENDPOINT_SHADERS_NEWEST = QStringLiteral("shaders/newest");
|
||||
|
||||
#define KOMPLEX_ENDPOINT_CUBEMAPS_SEARCH "cubemaps/search"
|
||||
#define KOMPLEX_ENDPOINT_CUBEMAPS_FEATURED "cubemaps/featured"
|
||||
#define KOMPLEX_ENDPOINT_CUBEMAPS_ITEM "cubemaps/item"
|
||||
#define KOMPLEX_ENDPOINT_CUBEMAPS_NEWEST "cubemaps/newest"
|
||||
inline const QString KOMPLEX_ENDPOINT_CUBEMAPS_SEARCH = QStringLiteral("cubemaps/search");
|
||||
inline const QString KOMPLEX_ENDPOINT_CUBEMAPS_FEATURED = QStringLiteral("cubemaps/featured");
|
||||
inline const QString KOMPLEX_ENDPOINT_CUBEMAPS_ITEM = QStringLiteral("cubemaps/item");
|
||||
inline const QString KOMPLEX_ENDPOINT_CUBEMAPS_NEWEST = QStringLiteral("cubemaps/newest");
|
||||
|
||||
#define KOMPLEX_ENDPOINT_PACKS_SEARCH "packs/search"
|
||||
#define KOMPLEX_ENDPOINT_PACKS_FEATURED "packs/featured"
|
||||
#define KOMPLEX_ENDPOINT_PACKS_ITEM "packs/item"
|
||||
#define KOMPLEX_ENDPOINT_PACKS_NEWEST "packs/newest"
|
||||
inline const QString KOMPLEX_ENDPOINT_PACKS_SEARCH = QStringLiteral("packs/search");
|
||||
inline const QString KOMPLEX_ENDPOINT_PACKS_FEATURED = QStringLiteral("packs/featured");
|
||||
inline const QString KOMPLEX_ENDPOINT_PACKS_ITEM = QStringLiteral("packs/item");
|
||||
inline const QString KOMPLEX_ENDPOINT_PACKS_NEWEST = QStringLiteral("packs/newest");
|
||||
|
||||
inline static qsizetype nullsize = std::numeric_limits<qsizetype>::min();
|
||||
inline static std::chrono::milliseconds KOMPLEX_RATE_LIMIT(500);
|
||||
inline const QString KOMPLEX_ENDPOINT_USER_AUTH = QStringLiteral("user/auth");
|
||||
|
||||
inline const qsizetype nullsize = std::numeric_limits<qsizetype>::min();
|
||||
inline const std::chrono::milliseconds KOMPLEX_RATE_LIMIT(500);
|
||||
inline const QString KOMPLEX_KEYCHAIN_SERVICE_NAME = QStringLiteral
|
||||
(
|
||||
"com.digitalartifex.komplex"
|
||||
);
|
||||
|
||||
#endif // KOMPLEX_GLOBAL_H
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
#include "qwallet.h"
|
||||
#include "../3rdparty/qtkeychain/qtkeychain/keychain.h"
|
||||
#include "common/exceptions.h"
|
||||
|
||||
auto QWallet::read(const QString &service, const QString &key) const -> QFuture<QByteArray>
|
||||
{
|
||||
return QtConcurrent::run
|
||||
(
|
||||
[this, service(service), key(key)]() -> QByteArray
|
||||
{
|
||||
QEventLoop loop;
|
||||
QByteArray value;
|
||||
|
||||
auto readCredentialJob = new QKeychain::ReadPasswordJob(service);
|
||||
readCredentialJob->setAutoDelete(true);
|
||||
readCredentialJob->setKey(key);
|
||||
|
||||
QObject::connect
|
||||
(
|
||||
readCredentialJob,
|
||||
&QKeychain::ReadPasswordJob::finished,
|
||||
this,
|
||||
[this, key, &loop, &value](QKeychain::Job *job)
|
||||
{
|
||||
auto j = static_cast<QKeychain::ReadPasswordJob*>(job);
|
||||
if (j->error() == QKeychain::NoError)
|
||||
{
|
||||
value = j->binaryData();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_errorString = qPrintable(j->errorString());
|
||||
throw wallet_exception
|
||||
(
|
||||
QStringLiteral("Wallet read error").toStdString(),
|
||||
errorString().toStdString()
|
||||
);
|
||||
}
|
||||
|
||||
loop.quit();
|
||||
}
|
||||
);
|
||||
|
||||
readCredentialJob->start();
|
||||
loop.exec();
|
||||
|
||||
return value;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto QWallet::write(const QString &service, const QString &key, const QByteArray &value) -> QFuture<void>
|
||||
{
|
||||
return QtConcurrent::run
|
||||
(
|
||||
[this, service(service), key(key), value(value)]() -> void
|
||||
{
|
||||
QEventLoop loop;
|
||||
bool success;
|
||||
|
||||
auto writeCredentialJob = new QKeychain::WritePasswordJob(service);
|
||||
writeCredentialJob->setAutoDelete(true);
|
||||
writeCredentialJob->setKey(key);
|
||||
writeCredentialJob->setBinaryData(value);
|
||||
|
||||
QObject::connect
|
||||
(
|
||||
writeCredentialJob,
|
||||
&QKeychain::WritePasswordJob::finished,
|
||||
this,
|
||||
[this, key, &loop, &success](QKeychain::Job *job)
|
||||
{
|
||||
auto j = static_cast<QKeychain::WritePasswordJob*>(job);
|
||||
if (j->error() != QKeychain::NoError)
|
||||
{
|
||||
setErrorString(qPrintable(j->errorString()));
|
||||
throw wallet_exception
|
||||
(
|
||||
QStringLiteral("Wallet write error").toStdString(),
|
||||
errorString().toStdString()
|
||||
);
|
||||
}
|
||||
|
||||
loop.quit();
|
||||
}
|
||||
);
|
||||
|
||||
writeCredentialJob->start();
|
||||
loop.exec();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto QWallet::remove(const QString &service, const QString &key) -> QFuture<void>
|
||||
{
|
||||
return QtConcurrent::run
|
||||
(
|
||||
[this, service(service), key(key)]() -> void
|
||||
{
|
||||
QEventLoop loop;
|
||||
bool value;
|
||||
|
||||
auto deleteCredentialJob = new QKeychain::DeletePasswordJob(service);
|
||||
deleteCredentialJob->setAutoDelete(true);
|
||||
deleteCredentialJob->setKey(key);
|
||||
|
||||
QObject::connect
|
||||
(
|
||||
deleteCredentialJob,
|
||||
&QKeychain::DeletePasswordJob::finished,
|
||||
this,
|
||||
[this, key, &loop, &value](QKeychain::Job *job)
|
||||
{
|
||||
auto j = static_cast<QKeychain::DeletePasswordJob*>(job);
|
||||
if (j->error() != QKeychain::NoError)
|
||||
{
|
||||
m_errorString = qPrintable(j->errorString());
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
loop.quit();
|
||||
}
|
||||
);
|
||||
|
||||
deleteCredentialJob->start();
|
||||
loop.exec();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
QWallet::QWallet(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QWallet::~QWallet()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
auto QWallet::errorString() const -> const QString &
|
||||
{
|
||||
return m_errorString;
|
||||
}
|
||||
|
||||
auto QWallet::setErrorString(const QString &errorString) const -> void
|
||||
{
|
||||
m_errorString = errorString;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2026 @DigitalArtifex
|
||||
* https://digitalartifex.dev - https://github.com/DigitalArtifex
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef QWALLET_H
|
||||
#define QWALLET_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QFuture>
|
||||
#include <QEventLoop>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
|
||||
#include "komplex_global.h"
|
||||
|
||||
/**
|
||||
* @brief The QWallet class
|
||||
* QWallet is a QFuture wrapper for QtKeychain to make interaction with the keychain easier
|
||||
*/
|
||||
class KOMPLEX_EXPORT QWallet : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QWallet(QObject *parent = nullptr);
|
||||
~QWallet();
|
||||
|
||||
/**
|
||||
* @brief read
|
||||
* Reads the provided key from the system keychain/wallet
|
||||
* @param service
|
||||
* Service name of the application making the request
|
||||
* @param key
|
||||
* Key to read
|
||||
* @return
|
||||
* QFuture for the binary value
|
||||
*/
|
||||
[[nodiscard]]
|
||||
auto read(const QString &service, const QString &key) const -> QFuture<QByteArray>;
|
||||
|
||||
/**
|
||||
* @brief write
|
||||
* Writes the provided value to the system kaychain/wallet
|
||||
* @param service
|
||||
* Service name of the application making the request
|
||||
* @param key
|
||||
* Key to read
|
||||
* @param value
|
||||
* Binary data to store
|
||||
* @return
|
||||
* QFuture
|
||||
*/
|
||||
[[nodiscard]]
|
||||
auto write(const QString &service, const QString &key, const QByteArray &value) -> QFuture<void>;
|
||||
|
||||
/**
|
||||
* @brief write
|
||||
* Removes the provided value from the system kaychain/wallet
|
||||
* @param service
|
||||
* Service name of the application making the request
|
||||
* @param key
|
||||
* Key to read
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]]
|
||||
auto remove(const QString &service, const QString &key) -> QFuture<void>;
|
||||
|
||||
/**
|
||||
* @brief errorString
|
||||
* String describing the last encountered error.
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]]
|
||||
auto errorString() const -> const QString &;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief setErrorString
|
||||
* Sets the error string
|
||||
* @param errorString
|
||||
*/
|
||||
auto setErrorString(const QString &errorString) const -> void;
|
||||
|
||||
mutable QString m_errorString;
|
||||
};
|
||||
|
||||
#endif // QWALLET_H
|
||||
@@ -1,3 +1,21 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2026 @DigitalArtifex
|
||||
* https://digitalartifex.dev - https://github.com/DigitalArtifex
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef SHADERPACK_H
|
||||
#define SHADERPACK_H
|
||||
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2026 @DigitalArtifex
|
||||
* https://digitalartifex.dev - https://github.com/DigitalArtifex
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef SHADERTOYMETADATA_H
|
||||
#define SHADERTOYMETADATA_H
|
||||
#include <QObject>
|
||||
|
||||
@@ -337,7 +337,17 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
while (index >= m_windowOffset + (SLIDING_CACHE_WINDOW_SIZE - SLIDING_CACHE_PREFETCH_THRESHOLD))
|
||||
qsizetype prefetchThreshold = SLIDING_CACHE_PREFETCH_THRESHOLD;
|
||||
|
||||
if(prefetchThreshold >= windowSize())
|
||||
{
|
||||
prefetchThreshold = std::floor
|
||||
(
|
||||
static_cast<qreal>(windowSize()) * 0.8
|
||||
);
|
||||
}
|
||||
|
||||
while (index >= m_windowOffset + (windowSize() - prefetchThreshold))
|
||||
{
|
||||
if(!slideForward())
|
||||
{
|
||||
@@ -407,7 +417,7 @@ private:
|
||||
m_windowOffset = 0;
|
||||
}
|
||||
|
||||
QThread::sleep(KOMPLEX_RATE_LIMIT);
|
||||
// QThread::sleep(KOMPLEX_RATE_LIMIT);
|
||||
|
||||
FetchResult<T> result = m_fetch(
|
||||
m_windowOffset,
|
||||
@@ -490,7 +500,7 @@ private:
|
||||
false
|
||||
);
|
||||
|
||||
QThread::sleep(KOMPLEX_RATE_LIMIT);
|
||||
// QThread::sleep(KOMPLEX_RATE_LIMIT);
|
||||
|
||||
m_windowOffset -= movementSize;
|
||||
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
#include "userservices.h"
|
||||
#include "common/komplex_global.h"
|
||||
#include "coreservices.h"
|
||||
#include <QtConcurrent/qtconcurrentrun.h>
|
||||
#include <exception>
|
||||
#include <qcontainerfwd.h>
|
||||
#include <qstandardpaths.h>
|
||||
#include <qstringview.h>
|
||||
|
||||
UserServices::UserServices(QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_wallet = new QWallet(this);
|
||||
}
|
||||
|
||||
UserServices::~UserServices()
|
||||
{
|
||||
if(m_wallet)
|
||||
{
|
||||
m_wallet->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
auto UserServices::setUserCredentials(const UserCredentials &credentials) -> void
|
||||
{
|
||||
s_userCredentials = credentials;
|
||||
Q_EMIT userCredentialsChanged();
|
||||
}
|
||||
|
||||
auto UserServices::errorString() const -> const QString &
|
||||
{
|
||||
return m_errorString;
|
||||
}
|
||||
|
||||
auto UserServices::setErrorString(const QString &errorString) -> void
|
||||
{
|
||||
if (m_errorString == errorString)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_errorString = errorString;
|
||||
emit errorStringChanged();
|
||||
}
|
||||
|
||||
auto UserServices::savePassword(const QString &username, const QByteArray &password) -> void
|
||||
{
|
||||
QFuture saveAttempt = m_wallet->write
|
||||
(
|
||||
KOMPLEX_KEYCHAIN_SERVICE_NAME,
|
||||
username,
|
||||
password
|
||||
);
|
||||
}
|
||||
|
||||
auto UserServices::readPassword(const QString &username) -> QFuture<QByteArray>
|
||||
{
|
||||
return m_wallet->read(KOMPLEX_KEYCHAIN_SERVICE_NAME, username);
|
||||
}
|
||||
|
||||
auto UserServices::readToken(const QString &username) -> QFuture<UserCredentials>
|
||||
{
|
||||
return QtConcurrent::run
|
||||
(
|
||||
[this, username] () -> UserCredentials
|
||||
{
|
||||
UserCredentials credentials;
|
||||
|
||||
QFuture readAttempt = m_wallet->read
|
||||
(
|
||||
KOMPLEX_KEYCHAIN_SERVICE_NAME,
|
||||
username + QStringLiteral("_token")
|
||||
);
|
||||
|
||||
QString _username = username;
|
||||
|
||||
readAttempt
|
||||
.then
|
||||
(
|
||||
[this, &credentials, _username] (const QByteArray &value) -> void
|
||||
{
|
||||
auto parts = value.split(PART_SEPARATOR);
|
||||
QDateTime expiry;
|
||||
|
||||
if(parts.count() == 1)
|
||||
{
|
||||
setErrorString(QStringLiteral("Token malformed"));
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
if(parts[0].length() != sizeof(qint64))
|
||||
{
|
||||
setErrorString(QStringLiteral("Token expiry malformed"));
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
QByteArray numericData = parts[0];
|
||||
qint64 numericConversion = 0;
|
||||
|
||||
for(int i = 0; i < sizeof(qint64); ++i)
|
||||
{
|
||||
numericConversion |=
|
||||
(
|
||||
static_cast<qint64>
|
||||
(
|
||||
(
|
||||
static_cast<qint8>(numericData[i]) &
|
||||
0xFF
|
||||
)
|
||||
<< (i * sizeof(qint8))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
expiry = QDateTime::fromMSecsSinceEpoch(numericConversion);
|
||||
|
||||
if(parts[1].isEmpty() && parts.count() == 3)
|
||||
{
|
||||
parts[1] = parts[2];
|
||||
}
|
||||
|
||||
credentials = UserCredentials
|
||||
{
|
||||
.username = _username.toUtf8(),
|
||||
.sessionToken = parts[1],
|
||||
.expiry = expiry
|
||||
};
|
||||
}
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this] () -> void
|
||||
{
|
||||
setErrorString(m_wallet->errorString());
|
||||
throw std::exception();
|
||||
}
|
||||
);
|
||||
|
||||
readAttempt.waitForFinished();
|
||||
|
||||
return credentials;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto UserServices::saveToken(const QString &username, const QByteArray &token, const QDateTime &expiry) -> void
|
||||
{
|
||||
QByteArray data;
|
||||
qint64 numericData = expiry.toMSecsSinceEpoch();
|
||||
|
||||
for(int i = 0; i < sizeof(qint64); ++i)
|
||||
{
|
||||
data[i] = 0xFF & static_cast<qint8>(numericData >> (i * sizeof(qint8)));
|
||||
}
|
||||
|
||||
data += PART_SEPARATOR + token;
|
||||
|
||||
QFuture saveAttempt = m_wallet->write
|
||||
(
|
||||
KOMPLEX_KEYCHAIN_SERVICE_NAME,
|
||||
username + QString("_token"),
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
auto UserServices::instance() -> QWeakPointer<UserServices>
|
||||
{
|
||||
if(!s_pointer)
|
||||
{
|
||||
s_instance = new UserServices(QCoreApplication::instance());
|
||||
s_pointer = QSharedPointer<UserServices>
|
||||
(
|
||||
s_instance,
|
||||
&UserServices::deleteLater
|
||||
);
|
||||
}
|
||||
|
||||
return s_pointer.toWeakRef();
|
||||
}
|
||||
|
||||
auto UserServices::userCredentials() -> const UserCredentials &
|
||||
{
|
||||
return s_userCredentials;
|
||||
}
|
||||
|
||||
auto UserServices::login(const QString &username, const QByteArray &password) -> QFuture<UserCredentials>
|
||||
{
|
||||
return QtConcurrent::run
|
||||
(
|
||||
[this, username, password] () -> UserCredentials
|
||||
{
|
||||
QSharedPointer<QNetworkAccessManager> manager = CoreServices::networkAccessManager().toStrongRef();
|
||||
|
||||
if(!manager)
|
||||
{
|
||||
Q_EMIT loginFailed();
|
||||
setErrorString(QStringLiteral("Network manager reference deleted"));
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
saveDefaultUsername(username);
|
||||
|
||||
QNetworkRequest request
|
||||
(
|
||||
QUrl
|
||||
(
|
||||
QStringLiteral("%1/%2/%3").arg
|
||||
(
|
||||
KOMPLEX_API_HOST,
|
||||
KOMPLEX_API_VERSION,
|
||||
KOMPLEX_ENDPOINT_USER_AUTH
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
QJsonObject loginRootObject
|
||||
{
|
||||
{
|
||||
QString("username"),
|
||||
QJsonValue::fromVariant(username.toUtf8())
|
||||
},
|
||||
{
|
||||
QString("password"),
|
||||
QJsonValue::fromVariant(password.toBase64())
|
||||
}
|
||||
};
|
||||
|
||||
QJsonDocument loginDocument(loginRootObject);
|
||||
|
||||
QNetworkReply *reply = manager->post(request, loginDocument.toJson(QJsonDocument::Compact));
|
||||
|
||||
while(!reply->isFinished())
|
||||
{
|
||||
std::chrono::milliseconds(10);
|
||||
}
|
||||
|
||||
if(reply->error() != QNetworkReply::NoError)
|
||||
{
|
||||
setErrorString(reply->errorString());
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
|
||||
QJsonParseError parseError;
|
||||
QJsonDocument document = QJsonDocument::fromJson(data, &parseError);
|
||||
|
||||
if(parseError.error != QJsonParseError::NoError)
|
||||
{
|
||||
setErrorString(parseError.errorString());
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
QJsonObject rootObject = document.object();
|
||||
QJsonObject dataObject = rootObject.value(QStringLiteral("data")).toObject();
|
||||
|
||||
if(!rootObject.value("success").toBool())
|
||||
{
|
||||
setErrorString
|
||||
(
|
||||
QStringLiteral("Login failure (%1): %2").arg
|
||||
(
|
||||
dataObject.value(QStringLiteral("errorCode")).toString(),
|
||||
dataObject.value(QStringLiteral("message")).toString()
|
||||
)
|
||||
);
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
QString expiryData = dataObject.value(QStringLiteral("expires")).toString();
|
||||
bool converted = false;
|
||||
qint64 secsSinceEpoch = expiryData.toLongLong(&converted);
|
||||
|
||||
if(!converted)
|
||||
{
|
||||
setErrorString(QStringLiteral("Malformed expiry data"));
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
UserCredentials credentials
|
||||
{
|
||||
.username = username.toUtf8(),
|
||||
.sessionToken = dataObject.value
|
||||
(
|
||||
QStringLiteral("jwt")
|
||||
).toString().toUtf8(),
|
||||
.expiry = QDateTime::fromSecsSinceEpoch(secsSinceEpoch)
|
||||
};
|
||||
|
||||
savePassword(username, password);
|
||||
saveToken(username, credentials.sessionToken, credentials.expiry);
|
||||
setUserCredentials(credentials);
|
||||
|
||||
Q_EMIT loginComplete();
|
||||
return credentials;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto UserServices::saveDefaultUsername(const QString &username) -> void
|
||||
{
|
||||
QDir configDirectory(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
|
||||
|
||||
if(!configDirectory.exists())
|
||||
{
|
||||
if(!configDirectory.mkpath(configDirectory.absolutePath()))
|
||||
{
|
||||
setErrorString(QStringLiteral("Could not make config directory"));
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
|
||||
QFile usernameFile(configDirectory.absoluteFilePath(QStringLiteral("uname")));
|
||||
|
||||
if(!usernameFile.open(QFile::ReadWrite | QFile::Truncate))
|
||||
{
|
||||
setErrorString(QStringLiteral("Could not open uname file"));
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
QByteArray data = username.toUtf8().toBase64();
|
||||
|
||||
if(usernameFile.write(data) != data.length())
|
||||
{
|
||||
setErrorString(QStringLiteral("Could not write uname file"));
|
||||
}
|
||||
|
||||
usernameFile.close();
|
||||
}
|
||||
|
||||
auto UserServices::defaultUsername() -> QFuture<QString>
|
||||
{
|
||||
return QtConcurrent::run
|
||||
(
|
||||
[this] () -> QString
|
||||
{
|
||||
QDir configDirectory(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
|
||||
|
||||
if(!configDirectory.exists())
|
||||
{
|
||||
if(!configDirectory.mkpath(configDirectory.absolutePath()))
|
||||
{
|
||||
setErrorString(QStringLiteral("Could not make config directory"));
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
|
||||
QFile usernameFile(configDirectory.absoluteFilePath(QStringLiteral("uname")));
|
||||
|
||||
if(!usernameFile.open(QFile::ReadOnly))
|
||||
{
|
||||
setErrorString(QStringLiteral("Could not open uname file"));
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
QByteArray data = QByteArray::fromBase64(usernameFile.readAll());
|
||||
|
||||
usernameFile.close();
|
||||
|
||||
return QString(data);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QObject>
|
||||
#include <QDateTime>
|
||||
#include <QSharedPointer>
|
||||
#include <QFuture>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include "komplex_global.h"
|
||||
#include "qwallet.h"
|
||||
|
||||
struct UserCredentials
|
||||
{
|
||||
QByteArray username;
|
||||
QByteArray sessionToken;
|
||||
QDateTime expiry;
|
||||
|
||||
UserCredentials &operator = (const UserCredentials &other)
|
||||
{
|
||||
username = other.username;
|
||||
sessionToken = other.sessionToken;
|
||||
expiry = other.expiry;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isValid()
|
||||
{
|
||||
return !username.isEmpty() && username.isValidUtf8() &&
|
||||
!sessionToken.isEmpty() && sessionToken.isValidUtf8();
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(UserCredentials)
|
||||
|
||||
class KOMPLEX_EXPORT UserServices : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static auto instance() -> QWeakPointer<UserServices>;
|
||||
static auto userCredentials() -> const UserCredentials &;
|
||||
|
||||
[[nodiscard]]
|
||||
auto login(const QString &username, const QByteArray &password) -> QFuture<UserCredentials>;
|
||||
|
||||
auto errorString() const -> const QString &;
|
||||
|
||||
[[nodiscard]]
|
||||
auto readPassword(const QString &username) -> QFuture<QByteArray>;
|
||||
|
||||
[[nodiscard]]
|
||||
auto readToken(const QString &username) -> QFuture<UserCredentials>;
|
||||
|
||||
[[nodiscard]]
|
||||
auto defaultUsername() -> QFuture<QString>;
|
||||
|
||||
signals:
|
||||
auto loginComplete() -> void;
|
||||
auto loginFailed() -> void;
|
||||
auto userCredentialsChanged() -> void;
|
||||
auto errorStringChanged() -> void;
|
||||
|
||||
private:
|
||||
inline static UserServices *s_instance = nullptr;
|
||||
inline static QSharedPointer<UserServices> s_pointer;
|
||||
|
||||
explicit UserServices(QObject *parent);
|
||||
~UserServices();
|
||||
|
||||
auto setUserCredentials(const UserCredentials &credentials) -> void;
|
||||
auto setErrorString(const QString &errorString) -> void;
|
||||
auto savePassword(const QString &username, const QByteArray &password) -> void;
|
||||
auto saveToken(const QString &username, const QByteArray &token, const QDateTime &expiry) -> void;
|
||||
auto saveDefaultUsername(const QString &username) -> void;
|
||||
|
||||
QWallet *m_wallet = nullptr;
|
||||
QString m_errorString;
|
||||
inline static UserCredentials s_userCredentials;
|
||||
|
||||
inline const static char PART_SEPARATOR = static_cast<char>('\x1f');
|
||||
|
||||
Q_PROPERTY(UserCredentials userCredentials READ userCredentials NOTIFY userCredentialsChanged FINAL)
|
||||
Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged FINAL)
|
||||
};
|
||||
Q_DECLARE_METATYPE(UserServices)
|
||||
@@ -1,3 +1,21 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2026 @DigitalArtifex
|
||||
* https://digitalartifex.dev - https://github.com/DigitalArtifex
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef VIDEOCACHE_H
|
||||
#define VIDEOCACHE_H
|
||||
|
||||
|
||||
@@ -31,6 +31,44 @@ DownloadManager::DownloadManager(QObject *parent)
|
||||
this,
|
||||
&DownloadManager::compileStepsChanged
|
||||
);
|
||||
|
||||
QObject::connect
|
||||
(
|
||||
&m_moveProcess,
|
||||
&QProcess::readyReadStandardOutput,
|
||||
this,
|
||||
[this]()
|
||||
{
|
||||
QByteArray processData = m_moveProcess.readAllStandardOutput();
|
||||
|
||||
if(!processData.isValidUtf8())
|
||||
{
|
||||
qWarning() << QStringLiteral("Process output not valid UTF8 data");
|
||||
return;
|
||||
}
|
||||
|
||||
setCompilerOutput(m_compilerOutput + processData);
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect
|
||||
(
|
||||
&m_moveProcess,
|
||||
&QProcess::readyReadStandardError,
|
||||
this,
|
||||
[this]()
|
||||
{
|
||||
QByteArray processData = m_moveProcess.readAllStandardError();
|
||||
|
||||
if(!processData.isValidUtf8())
|
||||
{
|
||||
qWarning() << QStringLiteral("Process output not valid UTF8 data");
|
||||
return;
|
||||
}
|
||||
|
||||
setCompilerOutput(m_compilerOutput + processData);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,30 +77,39 @@ DownloadManager::DownloadManager(QObject *parent)
|
||||
* [3] Optionally ask user if they would like to apply manipulative shader
|
||||
* [4] Install locally
|
||||
*/
|
||||
auto DownloadManager::downloadImage(const QString &author, const QString &authorId, const QString &description, const QUrl &url) noexcept(false) -> void
|
||||
auto DownloadManager::downloadImage(const QString &author, const QString &authorId, const QString &description, const QUrl &url) -> void
|
||||
{
|
||||
QFuture future = QtConcurrent::run
|
||||
(
|
||||
[this, url, author, authorId, description]()
|
||||
{
|
||||
QNetworkRequest request(url);
|
||||
QString requestUrl = request.url().toString();
|
||||
QString id = QUuid::createUuidV7().toString(QUuid::WithoutBraces);
|
||||
|
||||
QFuture<QUrl> downloadUri = download(request, id);
|
||||
setState(Downloading);
|
||||
|
||||
downloadUri
|
||||
//QFuture<QUrl> downloadUri = download(request, id, Get);
|
||||
if(m_downloadFuture.isRunning())
|
||||
{
|
||||
m_downloadFuture.cancelChain();
|
||||
}
|
||||
|
||||
m_downloadFuture = download(request, id, Get);
|
||||
|
||||
m_downloadFuture
|
||||
.then
|
||||
(
|
||||
[this, author, description, id](QUrl result) -> QUrl
|
||||
{
|
||||
setState(Installing);
|
||||
|
||||
ShaderPack metadata;
|
||||
metadata.setAuthor(author);
|
||||
metadata.setDescription(description);
|
||||
metadata.setName(QStringLiteral("Pexels Image (%1)").arg(id));
|
||||
metadata.setSource(QStringLiteral("./images/%1").arg(result.fileName()));
|
||||
metadata.setId(id);
|
||||
|
||||
QUrl packUri
|
||||
(
|
||||
QStringLiteral("%1/%2").arg
|
||||
QStringLiteral("file://%1/%2").arg
|
||||
(
|
||||
QStandardPaths::writableLocation(QStandardPaths::TempLocation),
|
||||
id
|
||||
@@ -71,7 +118,7 @@ auto DownloadManager::downloadImage(const QString &author, const QString &author
|
||||
|
||||
QUrl packImageUri
|
||||
(
|
||||
QStringLiteral("%1/%2/images/%3").arg
|
||||
QStringLiteral("file://%1/%2/images/%3").arg
|
||||
(
|
||||
QStandardPaths::writableLocation(QStandardPaths::TempLocation),
|
||||
id,
|
||||
@@ -80,7 +127,12 @@ auto DownloadManager::downloadImage(const QString &author, const QString &author
|
||||
);
|
||||
|
||||
QDir packDirectory(packUri.toLocalFile());
|
||||
packDirectory.mkpath(packUri.toLocalFile());
|
||||
|
||||
if(!packDirectory.mkpath(packUri.toLocalFile()))
|
||||
{
|
||||
throw file_exception(QStringLiteral("Could not create directory").toStdString());
|
||||
}
|
||||
|
||||
packDirectory.mkdir(QStringLiteral("images"));
|
||||
|
||||
QFile::rename(result.toLocalFile(), packImageUri.toLocalFile());
|
||||
@@ -96,15 +148,15 @@ auto DownloadManager::downloadImage(const QString &author, const QString &author
|
||||
if(!packFile.open(QFile::ReadWrite))
|
||||
{
|
||||
setError(QStringLiteral("File Error"), packFile.errorString());
|
||||
throw FileException(packFile.errorString());
|
||||
throw file_exception(packFile.errorString().toStdString());
|
||||
}
|
||||
|
||||
QByteArray data = metadata.json().toJson(QJsonDocument::Compact);
|
||||
QByteArray data = metadata.json().toJson(QJsonDocument::Indented);
|
||||
|
||||
if(packFile.write(data) != data.length())
|
||||
{
|
||||
setError(QStringLiteral("File Error"), packFile.errorString());
|
||||
throw FileException(packFile.errorString());
|
||||
throw file_exception(packFile.errorString().toStdString());
|
||||
}
|
||||
|
||||
packFile.close();
|
||||
@@ -113,10 +165,18 @@ auto DownloadManager::downloadImage(const QString &author, const QString &author
|
||||
}
|
||||
)
|
||||
.then
|
||||
(
|
||||
[this](QUrl result) -> QUrl
|
||||
{
|
||||
return install(result);
|
||||
}
|
||||
)
|
||||
.then
|
||||
(
|
||||
[this](QUrl result)
|
||||
{
|
||||
install(result);
|
||||
setLastInstalledFile(result.toLocalFile());
|
||||
setState(Complete);
|
||||
}
|
||||
)
|
||||
.onCanceled
|
||||
@@ -127,37 +187,66 @@ auto DownloadManager::downloadImage(const QString &author, const QString &author
|
||||
}
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this] (const std::filesystem::filesystem_error &e)
|
||||
{
|
||||
reset();
|
||||
setError(QStringLiteral("Network Exception %1").arg(QString::number(e.code().value())), e.what());
|
||||
}
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this] (const std::logic_error &e)
|
||||
{
|
||||
reset();
|
||||
setError(QStringLiteral("Logic Exception %1"), e.what());
|
||||
}
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this] (const std::exception &e)
|
||||
{
|
||||
reset();
|
||||
setError(QStringLiteral("Logic Exception %1"), e.what());
|
||||
}
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this] ()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
);
|
||||
setError(QStringLiteral("Unknown Exception"), QString());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto DownloadManager::downloadVideo(const QString &author, const QString &authorId, const QString &description, const QUrl &url) noexcept(false) -> void
|
||||
auto DownloadManager::downloadVideo(const QString &author, const QString &authorId, const QUrl &url) -> void
|
||||
{
|
||||
QFuture future = QtConcurrent::run
|
||||
(
|
||||
[this, url, author, authorId, description]()
|
||||
{
|
||||
QNetworkRequest request(url);
|
||||
QString id = QUuid::createUuidV7().toString(QUuid::WithoutBraces);
|
||||
|
||||
QFuture<QUrl> downloadUri = download(request, id);
|
||||
setState(Downloading);
|
||||
|
||||
downloadUri
|
||||
if(m_downloadFuture.isRunning())
|
||||
{
|
||||
m_downloadFuture.cancelChain();
|
||||
}
|
||||
|
||||
m_downloadFuture = download(request, id, Get);
|
||||
|
||||
m_downloadFuture
|
||||
.then
|
||||
(
|
||||
[this, author, description, id](QUrl result) -> QUrl
|
||||
[this, author, id](QUrl result) -> QUrl
|
||||
{
|
||||
setState(Installing);
|
||||
|
||||
ShaderPack metadata;
|
||||
metadata.setAuthor(author);
|
||||
metadata.setDescription(description);
|
||||
metadata.setName(QStringLiteral("Pexels Video (%1)").arg(id));
|
||||
metadata.setType(ShaderPack::Video);
|
||||
metadata.setName(QStringLiteral("Pexels Video (%1)").arg(id));
|
||||
metadata.setSource(QStringLiteral("./videos/%1").arg(result.fileName()));
|
||||
metadata.setId(id);
|
||||
|
||||
QUrl packUri
|
||||
(
|
||||
@@ -168,9 +257,9 @@ auto DownloadManager::downloadVideo(const QString &author, const QString &author
|
||||
)
|
||||
);
|
||||
|
||||
QUrl packImageUri
|
||||
QUrl packVideoUri
|
||||
(
|
||||
QStringLiteral("%1/%2/images/%3").arg
|
||||
QStringLiteral("%1/%2/videos/%3").arg
|
||||
(
|
||||
QStandardPaths::writableLocation(QStandardPaths::TempLocation),
|
||||
id,
|
||||
@@ -182,7 +271,7 @@ auto DownloadManager::downloadVideo(const QString &author, const QString &author
|
||||
packDirectory.mkpath(packUri.toLocalFile());
|
||||
packDirectory.mkdir(QStringLiteral("images"));
|
||||
|
||||
QFile::rename(result.toLocalFile(), packImageUri.toLocalFile());
|
||||
QFile::rename(result.toLocalFile(), packVideoUri.toLocalFile());
|
||||
|
||||
QFile packFile
|
||||
(
|
||||
@@ -195,7 +284,7 @@ auto DownloadManager::downloadVideo(const QString &author, const QString &author
|
||||
if(!packFile.open(QFile::ReadWrite))
|
||||
{
|
||||
setError(QStringLiteral("File Error"), packFile.errorString());
|
||||
throw FileException(packFile.errorString());
|
||||
throw file_exception(packFile.errorString().toStdString());
|
||||
}
|
||||
|
||||
QByteArray data = metadata.json().toJson(QJsonDocument::Compact);
|
||||
@@ -203,7 +292,7 @@ auto DownloadManager::downloadVideo(const QString &author, const QString &author
|
||||
if(packFile.write(data) != data.length())
|
||||
{
|
||||
setError(QStringLiteral("File Error"), packFile.errorString());
|
||||
throw FileException(packFile.errorString());
|
||||
throw file_exception(packFile.errorString().toStdString());
|
||||
}
|
||||
|
||||
packFile.close();
|
||||
@@ -212,10 +301,18 @@ auto DownloadManager::downloadVideo(const QString &author, const QString &author
|
||||
}
|
||||
)
|
||||
.then
|
||||
(
|
||||
[this](QUrl result) -> QUrl
|
||||
{
|
||||
return install(result);
|
||||
}
|
||||
)
|
||||
.then
|
||||
(
|
||||
[this](QUrl result)
|
||||
{
|
||||
install(result);
|
||||
setLastInstalledFile(result.toLocalFile());
|
||||
setState(Complete);
|
||||
}
|
||||
)
|
||||
.onCanceled
|
||||
@@ -226,22 +323,41 @@ auto DownloadManager::downloadVideo(const QString &author, const QString &author
|
||||
}
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this] (const std::filesystem::filesystem_error &e)
|
||||
{
|
||||
reset();
|
||||
setError(QStringLiteral("Network Exception %1").arg(QString::number(e.code().value())), e.what());
|
||||
}
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this] (const std::logic_error &e)
|
||||
{
|
||||
reset();
|
||||
setError(QStringLiteral("Logic Exception %1"), e.what());
|
||||
}
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this] (const std::exception &e)
|
||||
{
|
||||
reset();
|
||||
setError(QStringLiteral("Logic Exception %1"), e.what());
|
||||
}
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this] ()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
);
|
||||
setError(QStringLiteral("Unknown Exception"), QString());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto DownloadManager::downloadPack(const QString &id) noexcept(false) -> void
|
||||
auto DownloadManager::downloadPack(const QString &id) -> void
|
||||
{
|
||||
QFuture aether = QtConcurrent::run
|
||||
(
|
||||
[this, id]()
|
||||
{
|
||||
QUrl downloadUrl = QUrl
|
||||
(
|
||||
QStringLiteral("%1/%2/%3").arg
|
||||
@@ -254,39 +370,77 @@ auto DownloadManager::downloadPack(const QString &id) noexcept(false) -> void
|
||||
|
||||
QNetworkRequest request(downloadUrl);
|
||||
request.setRawHeader(QByteArray("uuid"), id.toUtf8());
|
||||
QString tempFile(QString("%1.tar.gz").arg(id));
|
||||
setState(Downloading);
|
||||
|
||||
QFuture<QUrl> downloadUri = download(request, id, Post);
|
||||
QFuture<QUrl> downloadUri = download(request, id, Post, tempFile);
|
||||
|
||||
downloadUri
|
||||
.then
|
||||
(
|
||||
[this](QUrl result) -> QUrl
|
||||
{
|
||||
return m_compiler->process(result).result();
|
||||
setState(Compiling);
|
||||
return m_compiler->build(result);
|
||||
}
|
||||
)
|
||||
.then
|
||||
(
|
||||
[this](QUrl result) -> QUrl
|
||||
{
|
||||
return install(result);
|
||||
}
|
||||
)
|
||||
.then
|
||||
(
|
||||
[this](QUrl result)
|
||||
{
|
||||
install(result);
|
||||
}
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this]()
|
||||
{
|
||||
reset();
|
||||
setError(m_compiler->errorTitle(), m_compiler->errorMessage());
|
||||
setLastInstalledFile(result.toLocalFile());
|
||||
setState(Complete);
|
||||
}
|
||||
)
|
||||
.onCanceled
|
||||
(
|
||||
[this]()
|
||||
[this, tempFile]()
|
||||
{
|
||||
QFile::remove(QStringLiteral("/tmp/") + tempFile);
|
||||
reset();
|
||||
}
|
||||
);
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this, tempFile] (const std::filesystem::filesystem_error &e)
|
||||
{
|
||||
QFile::remove(QStringLiteral("/tmp/") + tempFile);
|
||||
reset();
|
||||
setError(QStringLiteral("Network Exception %1").arg(QString::number(e.code().value())), e.what());
|
||||
}
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this, tempFile] (const shader::logic_error &e)
|
||||
{
|
||||
QFile::remove(QStringLiteral("/tmp/") + tempFile);
|
||||
reset();
|
||||
setError(e.what(), QString::fromStdString(e.details));
|
||||
}
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this, tempFile] (const std::exception &e)
|
||||
{
|
||||
QFile::remove(QStringLiteral("/tmp/") + tempFile);
|
||||
reset();
|
||||
setError(QStringLiteral("Exception %1"), e.what());
|
||||
}
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this, tempFile] ()
|
||||
{
|
||||
QFile::remove(QStringLiteral("/tmp/") + tempFile);
|
||||
reset();
|
||||
setError(QStringLiteral("Unknown Exception"), QString());
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -337,6 +491,8 @@ auto DownloadManager::setError(const QString &title, const QString &message) ->
|
||||
m_errorMessage = std::move(message);
|
||||
m_errorTitle = std::move(title);
|
||||
Q_EMIT errorChanged();
|
||||
|
||||
setState(Error);
|
||||
}
|
||||
|
||||
void DownloadManager::setCompilerOutput(const QString &compilerOutput)
|
||||
@@ -376,106 +532,63 @@ auto DownloadManager::setDownloadProgress(qreal progress) -> void
|
||||
|
||||
auto DownloadManager::install(const QUrl &uri) noexcept(false) -> QUrl
|
||||
{
|
||||
if(!uri.isLocalFile())
|
||||
{
|
||||
throw FileException(QStringLiteral("URI is not a local file"));
|
||||
}
|
||||
validateUri(uri);
|
||||
|
||||
QDir tempDirectory(uri.toLocalFile());
|
||||
|
||||
QString installLocation = QStringLiteral("%1/.local/share/komplex/packs/%2").arg
|
||||
QUrl installLocation = QStringLiteral("file://%1/.local/share/komplex/packs/%2").arg
|
||||
(
|
||||
QStandardPaths::writableLocation(QStandardPaths::HomeLocation),
|
||||
tempDirectory.dirName()
|
||||
);
|
||||
|
||||
QDir installDirectory(installLocation);
|
||||
QDir installDirectory(installLocation.toLocalFile());
|
||||
|
||||
if(installDirectory.exists())
|
||||
installDirectory.removeRecursively();
|
||||
|
||||
QProcess process;
|
||||
|
||||
QObject::connect
|
||||
(
|
||||
&process,
|
||||
&QProcess::readyReadStandardOutput,
|
||||
this,
|
||||
[this, &process]()
|
||||
{
|
||||
QByteArray processData = process.readAllStandardOutput();
|
||||
|
||||
if(!processData.isValidUtf8())
|
||||
{
|
||||
qWarning() << QStringLiteral("Process output not valid UTF8 data");
|
||||
return;
|
||||
}
|
||||
|
||||
setCompilerOutput(m_compilerOutput + processData);
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect
|
||||
(
|
||||
&process,
|
||||
&QProcess::readyReadStandardError,
|
||||
this,
|
||||
[this, &process]()
|
||||
{
|
||||
QByteArray processData = process.readAllStandardError();
|
||||
|
||||
if(!processData.isValidUtf8())
|
||||
{
|
||||
qWarning() << QStringLiteral("Process output not valid UTF8 data");
|
||||
return;
|
||||
}
|
||||
|
||||
setCompilerOutput(m_compilerOutput + processData);
|
||||
}
|
||||
);
|
||||
|
||||
QStringList arguments = QStringList
|
||||
{
|
||||
QStringLiteral("-R"),
|
||||
uri.toLocalFile(),
|
||||
installLocation
|
||||
installLocation.toLocalFile()
|
||||
};
|
||||
|
||||
process.start(QStringLiteral("cp"), arguments);
|
||||
m_moveProcess.start(QStringLiteral("mv"), arguments);
|
||||
|
||||
if(!process.waitForStarted(3000))
|
||||
if(!m_moveProcess.waitForStarted(3000))
|
||||
{
|
||||
qWarning() << QStringLiteral("Could not start copy process: %1").arg(process.readAllStandardError());
|
||||
throw FileException(QStringLiteral("Could not start install process"));
|
||||
qWarning() << QStringLiteral("Could not start copy process: %1").arg(m_moveProcess.readAllStandardError());
|
||||
throw file_exception(QStringLiteral("Could not start install process").toStdString());
|
||||
}
|
||||
|
||||
if(!process.waitForFinished())
|
||||
if(!m_moveProcess.waitForFinished())
|
||||
{
|
||||
qWarning() << QStringLiteral("Copy process took longer than expected (>30s)");
|
||||
throw FileException(QStringLiteral("Install process took longer than expected (>30s)"));
|
||||
throw file_exception(QStringLiteral("Install process took longer than expected (>30s)").toStdString());
|
||||
}
|
||||
|
||||
QUrl installUri(installLocation);
|
||||
installUri.setScheme(QStringLiteral("file://"));
|
||||
|
||||
return installUri;
|
||||
return installLocation;
|
||||
}
|
||||
|
||||
auto DownloadManager::download(const QNetworkRequest &request, const QString &id, RequestType type) -> QFuture<QUrl>
|
||||
auto DownloadManager::download(const QNetworkRequest &request, const QString &id, RequestType type, const QString &filename) -> QFuture<QUrl>
|
||||
{
|
||||
return QtConcurrent::run
|
||||
(
|
||||
[this, request, id]() -> QUrl
|
||||
[this, request, id, type, _filename = filename]() -> QUrl
|
||||
{
|
||||
QEventLoop loop;
|
||||
QString filename = id;
|
||||
QString filename = _filename;
|
||||
|
||||
if(filename.isEmpty())
|
||||
if(filename.isEmpty() && !request.url().fileName().isEmpty())
|
||||
{
|
||||
filename = QUuid::createUuidV7().toString();
|
||||
filename = request.url().fileName();
|
||||
}
|
||||
else if(filename.isEmpty())
|
||||
{
|
||||
filename = id;
|
||||
}
|
||||
|
||||
QUrl downloadUri = QStringLiteral("%1/%2").arg
|
||||
QUrl downloadUri = QStringLiteral("file://%1/%2").arg
|
||||
(
|
||||
QStandardPaths::writableLocation(QStandardPaths::TempLocation),
|
||||
filename
|
||||
@@ -485,7 +598,7 @@ auto DownloadManager::download(const QNetworkRequest &request, const QString &id
|
||||
|
||||
if(manager == nullptr)
|
||||
{
|
||||
throw NetworkException
|
||||
throw network_exception
|
||||
{
|
||||
QStringLiteral("Network Manager reference has already been deleted")
|
||||
};
|
||||
@@ -493,20 +606,23 @@ auto DownloadManager::download(const QNetworkRequest &request, const QString &id
|
||||
|
||||
QFile downloadFile(downloadUri.toLocalFile());
|
||||
|
||||
if(!downloadFile.open(QFile::ReadWrite))
|
||||
if(downloadFile.exists() && !downloadFile.remove())
|
||||
{
|
||||
throw FileException(QStringLiteral("Could not open temp file location"));
|
||||
throw file_exception(downloadFile.errorString().toStdString());
|
||||
}
|
||||
|
||||
QNetworkReply *reply = manager->post(request, nullptr);
|
||||
QNetworkReply *reply = nullptr;
|
||||
|
||||
QObject::connect
|
||||
(
|
||||
reply,
|
||||
&QNetworkReply::finished,
|
||||
&loop,
|
||||
&QEventLoop::quit
|
||||
);
|
||||
switch(type)
|
||||
{
|
||||
case Post:
|
||||
reply = manager->post(request, nullptr);
|
||||
break;
|
||||
default:
|
||||
case Get:
|
||||
reply = manager->get(request, nullptr);
|
||||
break;
|
||||
}
|
||||
|
||||
QObject::connect
|
||||
(
|
||||
@@ -517,7 +633,7 @@ auto DownloadManager::download(const QNetworkRequest &request, const QString &id
|
||||
{
|
||||
loop.quit();
|
||||
|
||||
throw NetworkException
|
||||
throw network_exception
|
||||
(
|
||||
QStringLiteral("Network Error %1").arg
|
||||
(
|
||||
@@ -545,40 +661,52 @@ auto DownloadManager::download(const QNetworkRequest &request, const QString &id
|
||||
reply,
|
||||
&QNetworkReply::readyRead,
|
||||
this,
|
||||
[this, &downloadFile, &reply]()
|
||||
[this, downloadUri]()
|
||||
{
|
||||
if(!downloadFile.isOpen())
|
||||
QMutexLocker locker(&m_fileMutex);
|
||||
QFile downloadFile(downloadUri.toLocalFile());
|
||||
|
||||
if(!downloadFile.open(QFile::ReadWrite | QFile::Append))
|
||||
{
|
||||
return;
|
||||
throw file_exception(QStringLiteral("Could not open temp file location").toStdString());
|
||||
}
|
||||
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
|
||||
|
||||
if(reply == nullptr)
|
||||
{
|
||||
throw std::system_error
|
||||
{
|
||||
std::error_code
|
||||
(
|
||||
ENOENT,
|
||||
std::system_category()
|
||||
),
|
||||
QStringLiteral("Reply is not a qobject").toStdString()
|
||||
};
|
||||
}
|
||||
|
||||
quint64 bytes = reply->bytesAvailable();
|
||||
quint64 bytesWritten = downloadFile.write(reply->read(bytes));
|
||||
QByteArray data = reply->readAll();
|
||||
quint64 bytesWritten = downloadFile.write(data/*bytes)*/);
|
||||
|
||||
if(bytesWritten != bytes)
|
||||
{
|
||||
throw FileException(QStringLiteral("Could not write temp file data"));
|
||||
}
|
||||
downloadFile.close();
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect
|
||||
(
|
||||
reply,
|
||||
&QNetworkReply::finished,
|
||||
&loop,
|
||||
&QEventLoop::quit
|
||||
);
|
||||
|
||||
if(!reply->isFinished())
|
||||
{
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
if(reply->bytesAvailable() > 0)
|
||||
{
|
||||
quint64 bytes = reply->bytesAvailable();
|
||||
quint64 bytesWritten = downloadFile.write(reply->read(bytes));
|
||||
|
||||
if(bytesWritten != bytes)
|
||||
{
|
||||
throw FileException(QStringLiteral("Could not write temp file data"));
|
||||
}
|
||||
}
|
||||
|
||||
downloadFile.close();
|
||||
manager.clear();
|
||||
|
||||
return downloadUri;
|
||||
@@ -586,29 +714,61 @@ auto DownloadManager::download(const QNetworkRequest &request, const QString &id
|
||||
);
|
||||
}
|
||||
|
||||
auto DownloadManager::validateUri(const QUrl &uri) noexcept(false) -> void
|
||||
{
|
||||
if(!uri.isValid() || !uri.isLocalFile())
|
||||
{
|
||||
throw std::filesystem::filesystem_error
|
||||
(
|
||||
QStringLiteral("URI %1 is not a local file uri").arg
|
||||
(
|
||||
uri.toString()
|
||||
).toStdString(),
|
||||
std::error_code
|
||||
(
|
||||
ENOENT,
|
||||
std::system_category()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
auto DownloadManager::readShaderToyEntry(const QUrl &uri) noexcept(false) -> ShaderToyEntry
|
||||
{
|
||||
if(!uri.isLocalFile())
|
||||
{
|
||||
throw FileException(QStringLiteral("URI is not a local file"));
|
||||
}
|
||||
validateUri(uri);
|
||||
|
||||
QDir packDir(uri.toLocalFile());
|
||||
|
||||
if(!packDir.exists(QStringLiteral("pack.json")))
|
||||
{
|
||||
throw FileException(QStringLiteral("Pack file not found"));
|
||||
throw std::filesystem::filesystem_error
|
||||
(
|
||||
QStringLiteral("Pack file not found").arg
|
||||
(
|
||||
uri.toString()
|
||||
).toStdString(),
|
||||
std::error_code
|
||||
(
|
||||
ENOENT,
|
||||
std::system_category()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
QFile packFile(packDir.absoluteFilePath(QStringLiteral("pack.json")));
|
||||
|
||||
if(!packFile.open(QFile::ReadOnly))
|
||||
{
|
||||
throw FileException
|
||||
throw std::filesystem::filesystem_error
|
||||
(
|
||||
QStringLiteral("Could not open pack file: %1").arg
|
||||
QStringLiteral("Could not open pack file").arg
|
||||
(
|
||||
packFile.errorString()
|
||||
uri.toString()
|
||||
).toStdString(),
|
||||
std::error_code
|
||||
(
|
||||
errno,
|
||||
std::system_category()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -620,11 +780,16 @@ auto DownloadManager::readShaderToyEntry(const QUrl &uri) noexcept(false) -> Sha
|
||||
|
||||
if(jsonError.error != QJsonParseError::NoError)
|
||||
{
|
||||
throw FileException
|
||||
throw std::filesystem::filesystem_error
|
||||
(
|
||||
QStringLiteral("Could parse pack file: %1").arg
|
||||
QStringLiteral("URI %1 is not a local file uri").arg
|
||||
(
|
||||
jsonError.errorString()
|
||||
uri.toString()
|
||||
).toStdString(),
|
||||
std::error_code
|
||||
(
|
||||
EIO,
|
||||
std::system_category()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -717,6 +882,19 @@ auto DownloadManager::readShaderToyEntry(const QUrl &uri) noexcept(false) -> Sha
|
||||
return std::move(entry);
|
||||
}
|
||||
|
||||
auto DownloadManager::lastInstalledFile() const -> const QString &
|
||||
{
|
||||
return m_lastInstalledFile;
|
||||
}
|
||||
|
||||
auto DownloadManager::setLastInstalledFile(const QString &lastInstalledFile) -> void
|
||||
{
|
||||
if (m_lastInstalledFile == lastInstalledFile)
|
||||
return;
|
||||
m_lastInstalledFile = lastInstalledFile;
|
||||
emit lastInstalledFileChanged();
|
||||
}
|
||||
|
||||
auto DownloadManager::setDownloadedBytes(qint64 downloadedBytes) -> void
|
||||
{
|
||||
if (m_downloadedBytes == downloadedBytes)
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2026 @DigitalArtifex
|
||||
* https://digitalartifex.dev - https://github.com/DigitalArtifex
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef DOWNLOADMANAGER_H
|
||||
#define DOWNLOADMANAGER_H
|
||||
|
||||
@@ -50,13 +68,13 @@ public:
|
||||
* out of it
|
||||
* @param url
|
||||
*/
|
||||
auto downloadImage
|
||||
Q_INVOKABLE auto downloadImage
|
||||
(
|
||||
const QString &author,
|
||||
const QString &authorId,
|
||||
const QString &description,
|
||||
const QUrl &url
|
||||
) noexcept(false) -> void;
|
||||
) -> void;
|
||||
|
||||
/**
|
||||
* @brief downloadVideo
|
||||
@@ -67,13 +85,12 @@ public:
|
||||
* @param description
|
||||
* @param url
|
||||
*/
|
||||
auto downloadVideo
|
||||
Q_INVOKABLE auto downloadVideo
|
||||
(
|
||||
const QString &author,
|
||||
const QString &authorId,
|
||||
const QString &description,
|
||||
const QUrl &url
|
||||
) noexcept(false) -> void;
|
||||
) -> void;
|
||||
|
||||
/**
|
||||
* @brief downloadPack
|
||||
@@ -82,13 +99,13 @@ public:
|
||||
* @param id
|
||||
* @return File info of the installed pack
|
||||
*/
|
||||
auto downloadPack(const QString &id) noexcept(false) -> void;
|
||||
Q_INVOKABLE auto downloadPack(const QString &id) -> void;
|
||||
|
||||
auto compilerOutput() const -> const QString & { return m_compilerOutput; }
|
||||
auto errorTitle() const -> const QString & { return m_errorTitle; }
|
||||
auto errorMessage() const -> const QString & { return m_errorMessage; }
|
||||
auto state() const -> State { return m_state; }
|
||||
auto reset() -> void;
|
||||
Q_INVOKABLE auto reset() -> void;
|
||||
auto downloadProgress() -> qreal { return m_downloadProgress; }
|
||||
auto compileProgress() -> qreal;
|
||||
auto compileSteps() -> qint64;
|
||||
@@ -100,6 +117,9 @@ public:
|
||||
auto downloadedBytes() const -> qint64 { return m_downloadedBytes; }
|
||||
auto setDownloadedBytes(qint64 downloadedBytes) -> void;
|
||||
|
||||
auto lastInstalledFile() const -> const QString &;
|
||||
auto setLastInstalledFile(const QString &lastInstalledFile) -> void;
|
||||
|
||||
protected:
|
||||
auto setError(const QString &title, const QString &message) -> void;
|
||||
auto setCompilerOutput(const QString &compilerOutput) -> void;
|
||||
@@ -118,11 +138,16 @@ signals:
|
||||
auto compileStepsChanged() -> void;
|
||||
auto compileStepsCompletedChanged() -> void;
|
||||
|
||||
auto lastInstalledFileChanged() -> void;
|
||||
|
||||
private:
|
||||
auto compile(const QUrl &uri) noexcept(false) -> QUrl;
|
||||
auto install(const QUrl &uri) noexcept(false) -> QUrl;
|
||||
auto download(const QNetworkRequest &request, const QString &id, RequestType type = Get) -> QFuture<QUrl>;
|
||||
auto download(const QNetworkRequest &request, const QString &id, RequestType type = Get, const QString &filename = QString()) -> QFuture<QUrl>;
|
||||
auto readShaderToyEntry(const QUrl &uri) noexcept(false) -> ShaderToyEntry;
|
||||
auto validateUri(const QUrl &uri) noexcept(false) -> void;
|
||||
|
||||
QString m_lastInstalledFile;
|
||||
|
||||
QString m_compilerOutput;
|
||||
QString m_errorTitle;
|
||||
@@ -135,9 +160,14 @@ private:
|
||||
State m_state;
|
||||
|
||||
QMutex m_downloadMutex;
|
||||
QMutex m_fileMutex;
|
||||
|
||||
PackCompiler *m_compiler = nullptr;
|
||||
|
||||
QProcess m_moveProcess;
|
||||
|
||||
QFuture<QUrl> m_downloadFuture;
|
||||
|
||||
Q_PROPERTY(QString compilerOutput READ compilerOutput WRITE setCompilerOutput NOTIFY compilerOutputChanged FINAL)
|
||||
Q_PROPERTY(QString errorTitle READ errorTitle NOTIFY errorChanged FINAL)
|
||||
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorChanged FINAL)
|
||||
@@ -148,6 +178,7 @@ private:
|
||||
Q_PROPERTY(qint64 downloadedBytes READ downloadedBytes WRITE setDownloadedBytes NOTIFY downloadedBytesChanged FINAL)
|
||||
Q_PROPERTY(qint64 compileSteps READ compileSteps NOTIFY compileStepsChanged FINAL)
|
||||
Q_PROPERTY(qint64 compileStepsCompleted READ compileStepsCompleted NOTIFY compileStepsCompletedChanged FINAL)
|
||||
Q_PROPERTY(QString lastInstalledFile READ lastInstalledFile WRITE setLastInstalledFile NOTIFY lastInstalledFileChanged FINAL)
|
||||
};
|
||||
Q_DECLARE_METATYPE(DownloadManager)
|
||||
|
||||
|
||||
@@ -5,49 +5,56 @@ FeaturedImagesModel::FeaturedImagesModel(QObject *parent) : QAbstractListModel{p
|
||||
m_paginator = new FeaturedImagesPaginator(this);
|
||||
PaginationNotifier *notifier = static_cast<PaginationNotifier*>(m_paginator);
|
||||
|
||||
QObject::connect(
|
||||
QObject::connect
|
||||
(
|
||||
notifier,
|
||||
&PaginationNotifier::resultsPerPageChanged,
|
||||
this,
|
||||
&FeaturedImagesModel::resultsPerPageChanged
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
QObject::connect
|
||||
(
|
||||
notifier,
|
||||
&PaginationNotifier::totalResultsChanged,
|
||||
this,
|
||||
&FeaturedImagesModel::totalResultsChanged
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
QObject::connect
|
||||
(
|
||||
notifier,
|
||||
&PaginationNotifier::totalPagesChanged,
|
||||
this,
|
||||
&FeaturedImagesModel::totalPagesChanged
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
QObject::connect
|
||||
(
|
||||
notifier,
|
||||
&PaginationNotifier::pageChanged,
|
||||
this,
|
||||
&FeaturedImagesModel::pageChanged
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
QObject::connect
|
||||
(
|
||||
notifier,
|
||||
&PaginationNotifier::pageChanged,
|
||||
this,
|
||||
&FeaturedImagesModel::resetDataModel
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
QObject::connect
|
||||
(
|
||||
notifier,
|
||||
&PaginationNotifier::fetchComplete,
|
||||
this,
|
||||
&FeaturedImagesModel::resetState
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
QObject::connect
|
||||
(
|
||||
notifier,
|
||||
&PaginationNotifier::fetching,
|
||||
this,
|
||||
@@ -172,6 +179,10 @@ auto FeaturedImagesModel::state() const -> FeaturedImagesModel::State
|
||||
|
||||
auto FeaturedImagesModel::setState(State state) -> void
|
||||
{
|
||||
QFuture<void> aether = QtConcurrent::run
|
||||
(
|
||||
[this, state]
|
||||
{
|
||||
if (m_state == state)
|
||||
{
|
||||
return;
|
||||
@@ -179,6 +190,8 @@ auto FeaturedImagesModel::setState(State state) -> void
|
||||
|
||||
m_state = state;
|
||||
emit stateChanged();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto FeaturedImagesModel::resetState() -> void
|
||||
@@ -245,7 +258,6 @@ auto FeaturedImagesModel::resultsPerPage() const -> qsizetype
|
||||
|
||||
auto FeaturedImagesModel::setResultsPerPage(qsizetype resultsPerPage) -> void
|
||||
{
|
||||
|
||||
if(m_paginator != nullptr)
|
||||
{
|
||||
QFuture<void> future = QtConcurrent::run
|
||||
@@ -344,3 +356,23 @@ auto FeaturedImagesModel::totalPages() const -> qsizetype
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto FeaturedImagesModel::hasNextPage() const -> bool
|
||||
{
|
||||
if(m_paginator == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_paginator->page() < m_paginator->totalPages();
|
||||
}
|
||||
|
||||
auto FeaturedImagesModel::hasPreviousPage() const -> bool
|
||||
{
|
||||
if(m_paginator == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_paginator->page() > 0;
|
||||
}
|
||||
|
||||
@@ -213,6 +213,20 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE auto previousPage() -> void;
|
||||
|
||||
/**
|
||||
* @brief hasNextPage
|
||||
* Calculates if there are more pages based on the current page calculation
|
||||
* @return
|
||||
*/
|
||||
auto hasNextPage() const -> bool;
|
||||
|
||||
/**
|
||||
* @brief hasPreviousPage
|
||||
* If page is greater than 1, returns true
|
||||
* @return
|
||||
*/
|
||||
auto hasPreviousPage() const -> bool;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief setErrorString
|
||||
@@ -248,7 +262,6 @@ signals:
|
||||
auto stateChanged() -> void;
|
||||
auto errorStringChanged() -> void;
|
||||
auto resultsPerPageChanged() -> void;
|
||||
|
||||
auto pageChanged() -> void;
|
||||
auto totalResultsChanged() -> void;
|
||||
auto totalPagesChanged() -> void;
|
||||
@@ -344,6 +357,8 @@ private:
|
||||
Q_PROPERTY(qsizetype resultsPerPage READ resultsPerPage WRITE setResultsPerPage NOTIFY resultsPerPageChanged FINAL)
|
||||
Q_PROPERTY(qsizetype totalResults READ totalResults NOTIFY totalResultsChanged FINAL)
|
||||
Q_PROPERTY(qsizetype totalPages READ totalPages NOTIFY totalPagesChanged FINAL)
|
||||
Q_PROPERTY(bool hasNextPage READ hasNextPage NOTIFY pageChanged FINAL)
|
||||
Q_PROPERTY(bool hasPreviousPage READ hasPreviousPage NOTIFY pageChanged FINAL)
|
||||
Q_PROPERTY(qsizetype page READ page NOTIFY pageChanged FINAL)
|
||||
};
|
||||
Q_DECLARE_METATYPE(FeaturedImagesModel)
|
||||
|
||||
@@ -311,6 +311,26 @@ auto FeaturedPacksModel::previousPage() -> void
|
||||
}
|
||||
}
|
||||
|
||||
auto FeaturedPacksModel::hasNextPage() const -> bool
|
||||
{
|
||||
if(m_paginator == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_paginator->page() < m_paginator->totalPages();
|
||||
}
|
||||
|
||||
auto FeaturedPacksModel::hasPreviousPage() const -> bool
|
||||
{
|
||||
if(m_paginator == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_paginator->page() > 0;
|
||||
}
|
||||
|
||||
qsizetype FeaturedPacksModel::page() const
|
||||
{
|
||||
if(m_paginator != nullptr)
|
||||
|
||||
@@ -208,6 +208,20 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE auto previousPage() -> void;
|
||||
|
||||
/**
|
||||
* @brief hasNextPage
|
||||
* Calculates if there are more pages based on the current page calculation
|
||||
* @return
|
||||
*/
|
||||
auto hasNextPage() const -> bool;
|
||||
|
||||
/**
|
||||
* @brief hasPreviousPage
|
||||
* If page is greater than 1, returns true
|
||||
* @return
|
||||
*/
|
||||
auto hasPreviousPage() const -> bool;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief setErrorString
|
||||
@@ -310,6 +324,8 @@ private:
|
||||
Q_PROPERTY(qsizetype resultsPerPage READ resultsPerPage WRITE setResultsPerPage NOTIFY resultsPerPageChanged FINAL)
|
||||
Q_PROPERTY(qsizetype totalResults READ totalResults NOTIFY totalResultsChanged FINAL)
|
||||
Q_PROPERTY(qsizetype totalPages READ totalPages NOTIFY totalPagesChanged FINAL)
|
||||
Q_PROPERTY(bool hasNextPage READ hasNextPage NOTIFY pageChanged FINAL)
|
||||
Q_PROPERTY(bool hasPreviousPage READ hasPreviousPage NOTIFY pageChanged FINAL)
|
||||
Q_PROPERTY(qsizetype page READ page NOTIFY pageChanged FINAL)
|
||||
};
|
||||
Q_DECLARE_METATYPE(FeaturedPacksModel)
|
||||
|
||||
@@ -160,6 +160,10 @@ auto FeaturedVideosModel::state() const -> FeaturedVideosModel::State
|
||||
|
||||
auto FeaturedVideosModel::setState(State state) -> void
|
||||
{
|
||||
QFuture<void> aether = QtConcurrent::run
|
||||
(
|
||||
[this, state]
|
||||
{
|
||||
if (m_state == state)
|
||||
{
|
||||
return;
|
||||
@@ -167,6 +171,8 @@ auto FeaturedVideosModel::setState(State state) -> void
|
||||
|
||||
m_state = state;
|
||||
emit stateChanged();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto FeaturedVideosModel::resetState() -> void
|
||||
@@ -205,6 +211,24 @@ auto FeaturedVideosModel::itemModel() const -> VideoItemModel*
|
||||
return m_itemModel;
|
||||
}
|
||||
|
||||
auto FeaturedVideosModel::windowSize() const -> qsizetype
|
||||
{
|
||||
if(m_paginator == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_paginator->windowSize();
|
||||
}
|
||||
|
||||
auto FeaturedVideosModel::setWindowSize(qsizetype size) -> void
|
||||
{
|
||||
if(m_paginator != nullptr)
|
||||
{
|
||||
m_paginator->setWindowSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
auto FeaturedVideosModel::roleNames() const -> QHash<int, QByteArray>
|
||||
{
|
||||
return m_dataRoles;
|
||||
@@ -347,3 +371,23 @@ auto FeaturedVideosModel::totalPages() const -> qsizetype
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto FeaturedVideosModel::hasNextPage() const -> bool
|
||||
{
|
||||
if(m_paginator == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_paginator->page() < m_paginator->totalPages();
|
||||
}
|
||||
|
||||
auto FeaturedVideosModel::hasPreviousPage() const -> bool
|
||||
{
|
||||
if(m_paginator == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_paginator->page() > 0;
|
||||
}
|
||||
|
||||
@@ -217,6 +217,34 @@ public:
|
||||
|
||||
VideoItemModel *itemModel() const;
|
||||
|
||||
/**
|
||||
* @brief windowSize
|
||||
* Paginator cache size
|
||||
* @return
|
||||
*/
|
||||
auto windowSize() const -> qsizetype;
|
||||
|
||||
/**
|
||||
* @brief setWindowSize
|
||||
* Set the paginator cache size
|
||||
* @param size
|
||||
*/
|
||||
auto setWindowSize(qsizetype size) -> void;
|
||||
|
||||
/**
|
||||
* @brief hasNextPage
|
||||
* Calculates if there are more pages based on the current page calculation
|
||||
* @return
|
||||
*/
|
||||
auto hasNextPage() const -> bool;
|
||||
|
||||
/**
|
||||
* @brief hasPreviousPage
|
||||
* If page is greater than 1, returns true
|
||||
* @return
|
||||
*/
|
||||
auto hasPreviousPage() const -> bool;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief setErrorString
|
||||
@@ -243,7 +271,7 @@ signals:
|
||||
auto stateChanged() -> void;
|
||||
auto errorStringChanged() -> void;
|
||||
auto resultsPerPageChanged() -> void;
|
||||
|
||||
auto windowSizeChanged() -> void;
|
||||
auto pageChanged() -> void;
|
||||
auto totalResultsChanged() -> void;
|
||||
auto totalPagesChanged() -> void;
|
||||
@@ -313,7 +341,10 @@ private:
|
||||
Q_PROPERTY(qsizetype totalResults READ totalResults NOTIFY totalResultsChanged FINAL)
|
||||
Q_PROPERTY(qsizetype totalPages READ totalPages NOTIFY totalPagesChanged FINAL)
|
||||
Q_PROPERTY(qsizetype page READ page NOTIFY pageChanged FINAL)
|
||||
Q_PROPERTY(bool hasNextPage READ hasNextPage NOTIFY pageChanged FINAL)
|
||||
Q_PROPERTY(bool hasPreviousPage READ hasPreviousPage NOTIFY pageChanged FINAL)
|
||||
Q_PROPERTY(VideoItemModel *itemModel READ itemModel CONSTANT FINAL)
|
||||
Q_PROPERTY(qsizetype windowSize READ windowSize WRITE setWindowSize NOTIFY windowSizeChanged FINAL)
|
||||
};
|
||||
Q_DECLARE_METATYPE(FeaturedVideosModel)
|
||||
|
||||
|
||||
@@ -159,6 +159,10 @@ auto NewestPacksModel::state() const -> NewestPacksModel::State
|
||||
|
||||
auto NewestPacksModel::setState(State state) -> void
|
||||
{
|
||||
QFuture<void> aether = QtConcurrent::run
|
||||
(
|
||||
[this, state]
|
||||
{
|
||||
if (m_state == state)
|
||||
{
|
||||
return;
|
||||
@@ -166,6 +170,8 @@ auto NewestPacksModel::setState(State state) -> void
|
||||
|
||||
m_state = state;
|
||||
emit stateChanged();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto NewestPacksModel::resetState() -> void
|
||||
@@ -224,7 +230,7 @@ auto NewestPacksModel::setResultsPerPage(qsizetype resultsPerPage) -> void
|
||||
{
|
||||
if(m_paginator != nullptr)
|
||||
{
|
||||
QFuture<void> future = QtConcurrent::run
|
||||
QFuture<void> aether = QtConcurrent::run
|
||||
(
|
||||
[this, resultsPerPage]
|
||||
{
|
||||
@@ -277,7 +283,7 @@ auto NewestPacksModel::nextPage() const -> void
|
||||
{
|
||||
if(m_paginator != nullptr)
|
||||
{
|
||||
QFuture<void> future = QtConcurrent::run
|
||||
QFuture<void> aether = QtConcurrent::run
|
||||
(
|
||||
[this]
|
||||
{
|
||||
@@ -291,7 +297,7 @@ auto NewestPacksModel::previousPage() const -> void
|
||||
{
|
||||
if(m_paginator != nullptr)
|
||||
{
|
||||
QFuture<void> future = QtConcurrent::run
|
||||
QFuture<void> aether = QtConcurrent::run
|
||||
(
|
||||
[this]
|
||||
{
|
||||
@@ -320,3 +326,24 @@ auto NewestPacksModel::totalPages() const -> qsizetype
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto NewestPacksModel::hasNextPage() const -> bool
|
||||
{
|
||||
if(m_paginator == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_paginator->page() < m_paginator->totalPages();
|
||||
|
||||
}
|
||||
|
||||
auto NewestPacksModel::hasPreviousPage() const -> bool
|
||||
{
|
||||
if(m_paginator == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_paginator->page() > 0;
|
||||
}
|
||||
|
||||
@@ -215,6 +215,20 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE auto previousPage() const -> void;
|
||||
|
||||
/**
|
||||
* @brief hasNextPage
|
||||
* Calculates if there are more pages based on the current page calculation
|
||||
* @return
|
||||
*/
|
||||
auto hasNextPage() const -> bool;
|
||||
|
||||
/**
|
||||
* @brief hasPreviousPage
|
||||
* If page is greater than 1, returns true
|
||||
* @return
|
||||
*/
|
||||
auto hasPreviousPage() const -> bool;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief setErrorString
|
||||
@@ -321,6 +335,8 @@ private:
|
||||
Q_PROPERTY(qsizetype totalResults READ totalResults NOTIFY totalResultsChanged FINAL)
|
||||
Q_PROPERTY(qsizetype totalPages READ totalPages NOTIFY totalPagesChanged FINAL)
|
||||
Q_PROPERTY(qsizetype page READ page NOTIFY pageChanged FINAL)
|
||||
Q_PROPERTY(bool hasNextPage READ hasNextPage NOTIFY pageChanged FINAL)
|
||||
Q_PROPERTY(bool hasPreviousPage READ hasPreviousPage NOTIFY pageChanged FINAL)
|
||||
};
|
||||
Q_DECLARE_METATYPE(NewestPacksModel)
|
||||
|
||||
|
||||
+416
-726
File diff suppressed because it is too large
Load Diff
+237
-93
@@ -1,3 +1,21 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2026 @DigitalArtifex
|
||||
* https://digitalartifex.dev - https://github.com/DigitalArtifex
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef PACKCOMPILER_H
|
||||
#define PACKCOMPILER_H
|
||||
|
||||
@@ -18,6 +36,8 @@
|
||||
#include <qobject.h>
|
||||
#include <qtmetamacros.h>
|
||||
#include <QPainter>
|
||||
#include <QtShaderTools>
|
||||
#include <QtShaderTools/rhi/qshaderbaker.h>
|
||||
#include <QtConcurrent/qtconcurrentrun.h>
|
||||
#include <qcontainerfwd.h>
|
||||
#include <qmargins.h>
|
||||
@@ -25,21 +45,42 @@
|
||||
#include <qtmetamacros.h>
|
||||
#include <qurl.h>
|
||||
#include "common/komplex_global.h"
|
||||
|
||||
#if __has_include("vulkan/vulkan_core.h")
|
||||
#define HAS_VULKAN
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#endif
|
||||
|
||||
#if __has_include("GL/gl.h")
|
||||
#define HAS_OPENGL
|
||||
#include <GL/gl.h>
|
||||
#endif
|
||||
|
||||
struct KOMPLEX_EXPORT BuildContext
|
||||
{
|
||||
QMap<QByteArray, QByteArray> commonData;
|
||||
QPromise<QUrl> promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The PackCompiler class
|
||||
* [1] Extract Pack (GZip)
|
||||
* [2] Verify directory is a pack and has shaders
|
||||
* [3] Load shader code
|
||||
* [4] Append Common and Global files, if they exist
|
||||
* [5] Add uniform and replace variable use
|
||||
* [6] Run C Preprocessor (cpp -P) to process macros
|
||||
* [7] Append version macro
|
||||
* [8] Compile with Qt Shader Baker
|
||||
* [3] Load Common shader code
|
||||
* [4] Walk the shader directory
|
||||
* [5] Load shader code
|
||||
* [6] Append type specific common code and global code
|
||||
* [7] Replace uniform variable use
|
||||
* [8] Add uniform header
|
||||
* [9] Add uniform footer
|
||||
* [10] Run preprocessor to process macros
|
||||
* [11] Append version macro
|
||||
* [12] Compile with QShaderBaker
|
||||
* [13] Save
|
||||
*/
|
||||
class KOMPLEX_EXPORT PackCompiler : public QObject
|
||||
class PackCompiler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
public:
|
||||
|
||||
enum State
|
||||
@@ -52,7 +93,6 @@ public:
|
||||
Q_ENUM(State)
|
||||
|
||||
explicit PackCompiler(QObject *parent = nullptr);
|
||||
~PackCompiler() override;
|
||||
|
||||
auto compilerOutput() const -> const QString & { return m_compilerOutput; }
|
||||
auto progress() -> qreal { return m_progress; }
|
||||
@@ -60,62 +100,11 @@ public:
|
||||
auto totalSteps() -> qint64 { return m_totalSteps; }
|
||||
auto errorTitle() const -> const QString & { return m_errorTitle; }
|
||||
auto errorMessage() const -> const QString & { return m_errorMessage; }
|
||||
auto reset() -> void;
|
||||
|
||||
auto process(const QUrl &uri) -> QFuture<QUrl>;
|
||||
|
||||
auto status() const -> const QString & { return m_status; }
|
||||
auto state() const -> State { return m_state; }
|
||||
auto reset() -> void;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief prepareShaders
|
||||
* Scans the pack's shader directory and sends each file to
|
||||
* prepareFile()
|
||||
* @param uri
|
||||
* Directory to prepare
|
||||
*/
|
||||
auto prepareShaders(const QUrl &uri) noexcept(false) -> void;
|
||||
|
||||
/**
|
||||
* @brief prepareFile
|
||||
* Prepares the individual file by appending the header, footer,
|
||||
* common files and replacing standard variable names
|
||||
* @param uri
|
||||
*/
|
||||
auto prepareFile(const QUrl &uri) noexcept(false) -> void;
|
||||
|
||||
/**
|
||||
* @brief validateDirectory
|
||||
*
|
||||
* @param uri
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
auto validateDirectory(const QUrl &uri) -> bool;
|
||||
|
||||
auto extract(const QUrl &sourceUri, const QUrl &destinationUri) noexcept(false) -> void;
|
||||
auto copyFile(const QUrl &sourceUri, const QUrl &destinationUri) noexcept(false) -> void;
|
||||
auto compile(const QUrl &uri) noexcept(false) -> void;
|
||||
auto preprocess(const QUrl &uri) noexcept(false) -> void;
|
||||
auto appendVersion(const QUrl &uri) noexcept(false) -> void;
|
||||
auto compileShader(const QUrl &uri) noexcept(false) -> void;
|
||||
auto loadCommonFragmentData(const QUrl &uri) -> QByteArray;
|
||||
auto loadCommonVertexData(const QUrl &uri) -> QByteArray;
|
||||
auto loadGlobalData(const QUrl &uri) -> QByteArray;
|
||||
|
||||
auto setError(const QString &title, const QString &message, const QUrl &uri = {}) -> void;
|
||||
auto setCompilerOutput(const QString &compilerOutput) -> void;
|
||||
auto setState(State state) -> void;
|
||||
auto setProgress(qreal progress) -> void;
|
||||
auto setTotalSteps(qint64 steps) -> void;
|
||||
auto setCurrentStep(qint64 step) -> void;
|
||||
|
||||
auto setStatus(const QString &status) -> void;
|
||||
|
||||
auto incrementCompileStep() -> void;
|
||||
|
||||
auto removeDirectory(const QUrl &uri) -> bool;
|
||||
auto build(const QUrl &uri) -> QUrl;
|
||||
|
||||
signals:
|
||||
auto compilerOutputChanged() -> void;
|
||||
@@ -126,15 +115,151 @@ signals:
|
||||
auto progressChanged() -> void;
|
||||
auto currentStepChanged() -> void;
|
||||
auto totalStepsChanged() -> void;
|
||||
|
||||
auto compileComplete(const QUrl &uri) -> void;
|
||||
|
||||
auto statusChanged() -> void;
|
||||
auto packReady(const QString &) -> void;
|
||||
auto errorStepChanged() -> void;
|
||||
auto errorRatingChanged() -> void;
|
||||
|
||||
private:
|
||||
//[1]
|
||||
/**
|
||||
* @brief extract
|
||||
* Extracts the packfile and deletes the source
|
||||
* @param uri of the tar.gz pack
|
||||
* @return uri of the extracted directory
|
||||
*/
|
||||
auto extract(const QUrl &uri) noexcept(false) -> const QUrl;
|
||||
|
||||
//[2]
|
||||
/**
|
||||
* @brief validatePack
|
||||
* Validates that the directory is a valid pack and has shaders to compile
|
||||
* @param uri of the directory provided by extract
|
||||
*/
|
||||
auto validatePack(const QUrl &uri) const noexcept(false) -> void;
|
||||
|
||||
//[3]
|
||||
/**
|
||||
* @brief loadCommonData
|
||||
* Loads the common and global shader code from the shader directory of the uri
|
||||
* @param uri
|
||||
*/
|
||||
auto loadCommonData(const QUrl &uri) -> QMap<QByteArray,QByteArray>;
|
||||
|
||||
//[4-5]
|
||||
/**
|
||||
* @brief processShaders
|
||||
* Walks the shader directory, loads the code and calls 6-13 for each
|
||||
* @param uri
|
||||
* @param context
|
||||
*/
|
||||
auto processShaders(const QUrl &uri, BuildContext *context) -> void;
|
||||
|
||||
//[6]
|
||||
/**
|
||||
* @brief appendCommonData
|
||||
* Appends the type specific common code to the shader data
|
||||
* @param data
|
||||
* Pointer to shader data
|
||||
* @param suffix
|
||||
* Shader file suffix
|
||||
* @param context
|
||||
* Pointer to build context object
|
||||
*/
|
||||
auto appendCommonData(QByteArray *data, const QByteArray &suffix, BuildContext *context) noexcept(true) -> void;
|
||||
|
||||
//[7]
|
||||
/**
|
||||
* @brief replaceUniformVariables
|
||||
* Replaces standard variables such as iFrame with their uniform buffer name
|
||||
* @param data
|
||||
* Pointer to the shader data
|
||||
*/
|
||||
auto replaceUniformVariables(QByteArray *data) noexcept(true) -> void;
|
||||
|
||||
//[8]
|
||||
/**
|
||||
* @brief appendUniformHeader
|
||||
* Adds header and uniform buffer to the shader data
|
||||
* @param data
|
||||
* Pointer to the shader data
|
||||
*/
|
||||
auto appendUniformHeader(QByteArray *data) noexcept(true) -> void;
|
||||
|
||||
//[9]
|
||||
/**
|
||||
* @brief appendUniformFooter
|
||||
* Adds the uniform footer to the shader data
|
||||
* @param data
|
||||
* Pointer to the shader data
|
||||
*/
|
||||
auto appendUniformFooter(QByteArray *data) noexcept(true) -> void;
|
||||
|
||||
//[10]
|
||||
/**
|
||||
* @brief preprocess
|
||||
* I have had issues with defines in code so passing it to a c/c++
|
||||
* preprocessor is required. I am now using clang for this as clang
|
||||
* is more modern and can use stdin
|
||||
* @param data
|
||||
*/
|
||||
auto preprocess(QByteArray *data) noexcept(false) -> void;
|
||||
|
||||
//[11]
|
||||
/**
|
||||
* @brief appendVersion
|
||||
* The version header confuses the preprocessor, so add it at the end
|
||||
* @param data
|
||||
*/
|
||||
auto appendVersion(QByteArray *data) noexcept(true) -> void;
|
||||
|
||||
//[12]
|
||||
/**
|
||||
* @brief compile
|
||||
* Compiles the shader data in memory and returns the shader object.
|
||||
* @param data
|
||||
* Shader data
|
||||
* @param filename
|
||||
* Filename of the shader
|
||||
* @param stage
|
||||
* Type of shader, based on suffix of original file
|
||||
* @return
|
||||
* Compiled shader object
|
||||
*/
|
||||
auto compile(const QByteArray &data, const QString &filename, const QShader::Stage stage) noexcept(false) -> QShader;
|
||||
|
||||
//[13]
|
||||
auto save(const QUrl &uri, const QShader &shader) noexcept(false) -> void;
|
||||
|
||||
//helpers
|
||||
auto validateUri(const QUrl &uri) const noexcept(false) -> void;
|
||||
auto run(const QString &command, const QStringList &arguments, const QByteArray &data = {}) noexcept(false) -> QByteArray;
|
||||
auto readFile(const QUrl &uri) noexcept(false) -> QByteArray;
|
||||
auto writeFile(const QUrl &uri, const QByteArray &data) noexcept(false) -> void;
|
||||
auto copyFile(const QUrl &sourceUri, const QUrl &destinationUri) noexcept(false) -> void;
|
||||
auto createDirectory(const QUrl &uri) noexcept(false) -> void;
|
||||
auto removeDirectory(const QUrl &uri) noexcept(false) -> void;
|
||||
auto getStageFromSuffix(const QString &suffix) const -> QShader::Stage;
|
||||
|
||||
auto setError(const QString &title, const QString &message, const QUrl &uri = {}) -> void;
|
||||
auto setCompilerOutput(const QString &compilerOutput) -> void;
|
||||
auto setState(State state) -> void;
|
||||
auto setProgress(qreal progress) -> void;
|
||||
auto setTotalSteps(qint64 steps) -> void;
|
||||
auto setCurrentStep(qint64 step) -> void;
|
||||
auto setStatus(const QString &status) -> void;
|
||||
auto incrementCompileStep() -> void;
|
||||
|
||||
#ifdef HAS_VULKAN
|
||||
auto vulkanVersion() const -> qint32;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_OPENGL
|
||||
auto openGlVersion() const -> qint32;
|
||||
#endif
|
||||
|
||||
//members
|
||||
static inline const QStringList m_updateVariables
|
||||
{
|
||||
QStringLiteral("iTime"),
|
||||
@@ -150,12 +275,51 @@ private:
|
||||
QStringLiteral("iChannelResolution")
|
||||
};
|
||||
|
||||
static inline const QStringList m_shaderNameFilters
|
||||
{
|
||||
QStringLiteral("*.frag"),
|
||||
QStringLiteral("*.vert"),
|
||||
QStringLiteral("*.tese"),
|
||||
QStringLiteral("*.tesc"),
|
||||
QStringLiteral("*.geom"),
|
||||
QStringLiteral("*.comp"),
|
||||
QStringLiteral("*.glsl")
|
||||
};
|
||||
|
||||
static inline const QMap<QString, QShader::Stage> m_shaderStages
|
||||
{
|
||||
{
|
||||
QStringLiteral("frag"),
|
||||
QShader::FragmentStage
|
||||
},
|
||||
{
|
||||
QStringLiteral("vert"),
|
||||
QShader::VertexStage
|
||||
},
|
||||
{
|
||||
QStringLiteral("tese"),
|
||||
QShader::TessellationEvaluationStage
|
||||
},
|
||||
{
|
||||
QStringLiteral("tesc"),
|
||||
QShader::TessellationControlStage
|
||||
},
|
||||
{
|
||||
QStringLiteral("geom"),
|
||||
QShader::GeometryStage
|
||||
},
|
||||
{
|
||||
QStringLiteral("comp"),
|
||||
QShader::ComputeStage
|
||||
}
|
||||
};
|
||||
|
||||
static inline const QByteArray m_version
|
||||
{
|
||||
R"(#version 450)"
|
||||
};
|
||||
|
||||
static inline const QString m_header
|
||||
static inline const QByteArray m_header
|
||||
{
|
||||
R"(layout(location = 0) in vec2 qt_TexCoord0;
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
@@ -184,15 +348,19 @@ layout(binding = 4) uniform sampler2D iChannel3;
|
||||
vec2 fragCoord = vec2(qt_TexCoord0.x, 1.0 - qt_TexCoord0.y) * ubuf.iResolution.xy;)"
|
||||
};
|
||||
|
||||
static inline const QString m_footer
|
||||
static inline const QByteArray m_footer
|
||||
{
|
||||
R"(void main() {
|
||||
R"(
|
||||
void main() {
|
||||
vec4 color = vec4(0.0);
|
||||
mainImage(color, fragCoord);
|
||||
fragColor = color;
|
||||
})"
|
||||
};
|
||||
|
||||
QMap<QByteArray, QByteArray> m_commonData;
|
||||
QMap<QByteArray, QShaderBaker> m_shaders;
|
||||
|
||||
QString m_compilerOutput;
|
||||
QString m_errorMessage;
|
||||
QString m_errorTitle;
|
||||
@@ -206,34 +374,10 @@ vec2 fragCoord = vec2(qt_TexCoord0.x, 1.0 - qt_TexCoord0.y) * ubuf.iResolution.x
|
||||
|
||||
QMutex m_downloadMutex;
|
||||
|
||||
QFuture<QUrl> m_buildFuture;
|
||||
|
||||
static inline QList<QRegularExpression> m_variableExpressions;
|
||||
|
||||
static inline const QRegularExpression m_commonFragmentExpression = QRegularExpression
|
||||
(
|
||||
QString("^common\\.frag$"),
|
||||
QRegularExpression::CaseInsensitiveOption
|
||||
);
|
||||
|
||||
static inline const QRegularExpression m_commonVertexExpression = QRegularExpression
|
||||
(
|
||||
QString("^common\\.vert$"),
|
||||
QRegularExpression::CaseInsensitiveOption
|
||||
);
|
||||
|
||||
static inline const QRegularExpression m_fragmentExpression = QRegularExpression
|
||||
(
|
||||
QString("^.{1,}\\.frag$"),
|
||||
QRegularExpression::CaseInsensitiveOption |
|
||||
QRegularExpression::DotMatchesEverythingOption
|
||||
);
|
||||
|
||||
static inline const QRegularExpression m_vertexExpression = QRegularExpression
|
||||
(
|
||||
QString("^.{1,}\\.vert$"),
|
||||
QRegularExpression::CaseInsensitiveOption |
|
||||
QRegularExpression::DotMatchesEverythingOption
|
||||
);
|
||||
|
||||
Q_PROPERTY(QString compilerOutput READ compilerOutput WRITE setCompilerOutput NOTIFY compilerOutputChanged FINAL)
|
||||
Q_PROPERTY(QString errorTitle READ errorTitle NOTIFY errorTitleChanged FINAL)
|
||||
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged FINAL)
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2026 @DigitalArtifex
|
||||
* https://digitalartifex.dev - https://github.com/DigitalArtifex
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef FEATUREDCUBEMAPSPAGINATOR_H
|
||||
#define FEATUREDCUBEMAPSPAGINATOR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QEventLoop>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "wallpaperpaginator.h"
|
||||
|
||||
class KOMPLEX_EXPORT FeaturedCubemapsPaginator : public WallpaperPaginator
|
||||
{
|
||||
public:
|
||||
explicit FeaturedCubemapsPaginator(QObject *parent = nullptr) : WallpaperPaginator(parent)
|
||||
{
|
||||
setUri(
|
||||
QString::fromUtf8(KOMPLEX_ENDPOINT_CUBEMAPS_FEATURED)
|
||||
);
|
||||
|
||||
controller()->setWindowSize(20);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // FEATUREDCUBEMAPSPAGINATOR_H
|
||||
@@ -36,11 +36,8 @@ class KOMPLEX_EXPORT FeaturedImagesPaginator : public ImagePaginator
|
||||
public:
|
||||
explicit FeaturedImagesPaginator(QObject *parent = nullptr) : ImagePaginator(parent)
|
||||
{
|
||||
setUri(
|
||||
QString::fromUtf8(KOMPLEX_ENDPOINT_IMAGES_FEATURED)
|
||||
);
|
||||
|
||||
controller()->setWindowSize(20);
|
||||
setUri(KOMPLEX_ENDPOINT_IMAGES_FEATURED);
|
||||
controller()->setWindowSize(32);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -36,10 +36,7 @@ class KOMPLEX_EXPORT FeaturedPacksPaginator : public WallpaperPaginator
|
||||
public:
|
||||
explicit FeaturedPacksPaginator(QObject *parent = nullptr) : WallpaperPaginator(parent)
|
||||
{
|
||||
setUri(
|
||||
QString::fromUtf8(KOMPLEX_ENDPOINT_PACKS_FEATURED)
|
||||
);
|
||||
|
||||
setUri(KOMPLEX_ENDPOINT_PACKS_FEATURED);
|
||||
controller()->setWindowSize(32);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2026 @DigitalArtifex
|
||||
* https://digitalartifex.dev - https://github.com/DigitalArtifex
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef FEATUREDSHADERSPAGINATOR_H
|
||||
#define FEATUREDSHADERSPAGINATOR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QEventLoop>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "wallpaperpaginator.h"
|
||||
|
||||
class KOMPLEX_EXPORT FeaturedShadersPaginator : public WallpaperPaginator
|
||||
{
|
||||
public:
|
||||
explicit FeaturedShadersPaginator(QObject *parent = nullptr) : WallpaperPaginator(parent)
|
||||
{
|
||||
setUri(
|
||||
QString::fromUtf8(KOMPLEX_ENDPOINT_SHADERS_FEATURED)
|
||||
);
|
||||
|
||||
controller()->setWindowSize(20);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // FEATUREDSHADERSPAGINATOR_H
|
||||
@@ -36,11 +36,8 @@ class KOMPLEX_EXPORT FeaturedVideosPaginator : public VideoPaginator
|
||||
public:
|
||||
explicit FeaturedVideosPaginator(QObject *parent = nullptr) : VideoPaginator(parent)
|
||||
{
|
||||
setUri(
|
||||
QString::fromUtf8(KOMPLEX_ENDPOINT_VIDEOS_FEATURED)
|
||||
);
|
||||
|
||||
controller()->setWindowSize(20);
|
||||
setUri(KOMPLEX_ENDPOINT_VIDEOS_FEATURED);
|
||||
controller()->setWindowSize(32);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/paginator.h"
|
||||
#include "common/imagecache.h"
|
||||
#include "common/coreservices.h"
|
||||
#include "paginator.h"
|
||||
|
||||
/**
|
||||
* @brief The ImagePaginator class
|
||||
|
||||
@@ -38,10 +38,7 @@ class KOMPLEX_EXPORT ImageSearchPaginator : public ImagePaginator
|
||||
public:
|
||||
explicit ImageSearchPaginator(QObject *parent = nullptr) : ImagePaginator(parent)
|
||||
{
|
||||
setUri(
|
||||
QString::fromUtf8(KOMPLEX_ENDPOINT_IMAGES_SEARCH)
|
||||
);
|
||||
|
||||
setUri(KOMPLEX_ENDPOINT_IMAGES_SEARCH);
|
||||
setQueryable(true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2026 @DigitalArtifex
|
||||
* https://digitalartifex.dev - https://github.com/DigitalArtifex
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef NEWESTCUBEMAPSPAGINATOR_H
|
||||
#define NEWESTCUBEMAPSPAGINATOR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QEventLoop>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "wallpaperpaginator.h"
|
||||
|
||||
class KOMPLEX_EXPORT NewestCubemapsPaginator : public WallpaperPaginator
|
||||
{
|
||||
public:
|
||||
explicit NewestCubemapsPaginator(QObject *parent = nullptr) : WallpaperPaginator(parent)
|
||||
{
|
||||
setUri(
|
||||
QString::fromUtf8(KOMPLEX_ENDPOINT_CUBEMAPS_NEWEST)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // NEWESTCUBEMAPSPAGINATOR_H
|
||||
@@ -36,10 +36,7 @@ class KOMPLEX_EXPORT NewestPacksPaginator : public WallpaperPaginator
|
||||
public:
|
||||
explicit NewestPacksPaginator(QObject *parent = nullptr) : WallpaperPaginator(parent)
|
||||
{
|
||||
setUri(
|
||||
QString::fromUtf8(KOMPLEX_ENDPOINT_PACKS_NEWEST)
|
||||
);
|
||||
|
||||
setUri(KOMPLEX_ENDPOINT_PACKS_NEWEST);
|
||||
controller()->setWindowSize(20);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2026 @DigitalArtifex
|
||||
* https://digitalartifex.dev - https://github.com/DigitalArtifex
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef NEWESTSHADERSPAGINATOR_H
|
||||
#define NEWESTSHADERSPAGINATOR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QEventLoop>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "wallpaperpaginator.h"
|
||||
|
||||
class KOMPLEX_EXPORT NewestShadersPaginator : public WallpaperPaginator
|
||||
{
|
||||
public:
|
||||
explicit NewestShadersPaginator(QObject *parent = nullptr) : WallpaperPaginator(parent)
|
||||
{
|
||||
setUri(
|
||||
QString::fromUtf8(KOMPLEX_ENDPOINT_SHADERS_NEWEST)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // NEWESTSHADERSPAGINATOR_H
|
||||
@@ -36,10 +36,7 @@ class KOMPLEX_EXPORT PackSearchPaginator : public WallpaperPaginator
|
||||
public:
|
||||
explicit PackSearchPaginator(QObject *parent = nullptr) : WallpaperPaginator(parent)
|
||||
{
|
||||
setUri(
|
||||
QString::fromUtf8(KOMPLEX_ENDPOINT_PACKS_SEARCH)
|
||||
);
|
||||
|
||||
setUri(KOMPLEX_ENDPOINT_PACKS_SEARCH);
|
||||
setQueryable(true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
|
||||
#include "komplex_global.h"
|
||||
#include "slidingcachecontroller.h"
|
||||
#include "common/komplex_global.h"
|
||||
#include "common/slidingcachecontroller.h"
|
||||
|
||||
/**
|
||||
* @brief The PaginationNotifier class
|
||||
@@ -120,6 +120,7 @@ public:
|
||||
* @brief resultsPerPage
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]]
|
||||
auto resultsPerPage() const -> qsizetype
|
||||
{
|
||||
return m_resultsPerPage;
|
||||
@@ -204,6 +205,7 @@ public:
|
||||
* @brief data
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]]
|
||||
auto data() const -> const QList<T>
|
||||
{
|
||||
if(boundaryCheck(m_resultsPerPage))
|
||||
@@ -219,6 +221,7 @@ public:
|
||||
* @brief count
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]]
|
||||
auto count() const -> qsizetype
|
||||
{
|
||||
if(m_offset == nullsize)
|
||||
@@ -240,6 +243,7 @@ public:
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]]
|
||||
auto at(qsizetype index) const -> const T&
|
||||
{
|
||||
return m_cacheController.at(m_offset + index);
|
||||
@@ -251,6 +255,7 @@ public:
|
||||
* The total number of results as reported by the controller
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]]
|
||||
auto totalResults() const -> qsizetype
|
||||
{
|
||||
return m_cacheController.totalCount();
|
||||
@@ -261,6 +266,7 @@ public:
|
||||
* Calculates the current page
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]]
|
||||
auto page() const -> qsizetype
|
||||
{
|
||||
return std::floor(
|
||||
@@ -273,6 +279,7 @@ public:
|
||||
* Calculates the total number of pages available
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]]
|
||||
auto totalPages() const -> qsizetype
|
||||
{
|
||||
return std::ceil(
|
||||
@@ -280,13 +287,29 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief setWindowSize
|
||||
* Sets the controller window size
|
||||
* @return
|
||||
*/
|
||||
auto setWindowSize(qsizetype size) -> void
|
||||
{
|
||||
m_cacheController.setWindowSize(size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto windowSize() const -> qsizetype
|
||||
{
|
||||
return m_cacheController.windowSize();
|
||||
}
|
||||
|
||||
auto queryable() const -> bool
|
||||
{
|
||||
return m_queryable;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
auto setQueryable(bool queryable) -> void
|
||||
{
|
||||
m_queryable = queryable;
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2026 @DigitalArtifex
|
||||
* https://digitalartifex.dev - https://github.com/DigitalArtifex
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef SEARCHCUBEMAPSPAGINATOR_H
|
||||
#define SEARCHCUBEMAPSPAGINATOR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QEventLoop>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "wallpaperpaginator.h"
|
||||
|
||||
class KOMPLEX_EXPORT SearchCubemapsPaginator : public WallpaperPaginator
|
||||
{
|
||||
public:
|
||||
explicit SearchCubemapsPaginator(QObject *parent = nullptr) : WallpaperPaginator(parent)
|
||||
{
|
||||
setUri(
|
||||
QString::fromUtf8(KOMPLEX_ENDPOINT_CUBEMAPS_SEARCH)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SEARCHCUBEMAPSPAGINATOR_H
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2026 @DigitalArtifex
|
||||
* https://digitalartifex.dev - https://github.com/DigitalArtifex
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef SEARCHSHADERSPAGINATOR_H
|
||||
#define SEARCHSHADERSPAGINATOR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QEventLoop>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "wallpaperpaginator.h"
|
||||
|
||||
class KOMPLEX_EXPORT SearchShadersPaginator : public WallpaperPaginator
|
||||
{
|
||||
public:
|
||||
explicit SearchShadersPaginator(QObject *parent = nullptr) : WallpaperPaginator(parent)
|
||||
{
|
||||
setUri(
|
||||
QString::fromUtf8(KOMPLEX_ENDPOINT_SHADERS_SEARCH)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SEARCHSHADERSPAGINATOR_H
|
||||
@@ -30,9 +30,9 @@
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/paginator.h"
|
||||
#include "common/videocache.h"
|
||||
#include "common/coreservices.h"
|
||||
#include "paginator.h"
|
||||
|
||||
/**
|
||||
* @brief The VideoPaginator class
|
||||
@@ -245,6 +245,8 @@ protected:
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
manager.clear();
|
||||
|
||||
LOG_ERROR_X(reply->error() != QNetworkReply::NoError,
|
||||
"NewestPacksPaginator::getNetworkReply",
|
||||
reply->errorString().toStdString().c_str(),
|
||||
|
||||
@@ -36,10 +36,7 @@ class KOMPLEX_EXPORT VideoSearchPaginator : public VideoPaginator
|
||||
public:
|
||||
explicit VideoSearchPaginator(QObject *parent = nullptr) : VideoPaginator(parent)
|
||||
{
|
||||
setUri(
|
||||
QString::fromUtf8(KOMPLEX_ENDPOINT_VIDEOS_SEARCH)
|
||||
);
|
||||
|
||||
setUri(KOMPLEX_ENDPOINT_VIDEOS_SEARCH);
|
||||
setQueryable(true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/paginator.h"
|
||||
#include "common/wallpapercache.h"
|
||||
#include "common/coreservices.h"
|
||||
#include "paginator.h"
|
||||
|
||||
/**
|
||||
* @brief The WallpaperPaginator class
|
||||
@@ -248,6 +248,8 @@ protected:
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
manager.clear();
|
||||
|
||||
LOG_ERROR_X(reply->error() != QNetworkReply::NoError,
|
||||
"NewestPacksPaginator::getNetworkReply",
|
||||
reply->errorString().toStdString().c_str(),
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
#include "usermodel.h"
|
||||
#include "common/userservices.h"
|
||||
#include <QtConcurrent/qtconcurrentrun.h>
|
||||
#include <qdatetime.h>
|
||||
#include <qfuture.h>
|
||||
#include <qobjectdefs.h>
|
||||
#include <qstringview.h>
|
||||
|
||||
UserModel::UserModel(QObject *parent)
|
||||
: QObject{parent}
|
||||
{
|
||||
m_stateResetTimer = new QTimer(this);
|
||||
m_stateResetTimer->setInterval(1000);
|
||||
m_stateResetTimer->setSingleShot(true);
|
||||
|
||||
QObject::connect
|
||||
(
|
||||
m_stateResetTimer,
|
||||
&QTimer::timeout,
|
||||
this,
|
||||
&UserModel::resetState
|
||||
);
|
||||
|
||||
setState(Busy);
|
||||
|
||||
QSharedPointer<UserServices> service = UserServices::instance();
|
||||
|
||||
if(service == nullptr)
|
||||
{
|
||||
setErrorString(QStringLiteral("UserServices object deleted"));
|
||||
setState(LoginFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
QFuture<QString> usernameFuture = service->defaultUsername();
|
||||
|
||||
usernameFuture
|
||||
.then
|
||||
(
|
||||
[this] (const QString username)
|
||||
{
|
||||
login(username);
|
||||
}
|
||||
)
|
||||
.onCanceled
|
||||
(
|
||||
[this] () -> void
|
||||
{
|
||||
reset();
|
||||
}
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this] () -> void
|
||||
{
|
||||
reset();
|
||||
}
|
||||
);
|
||||
|
||||
service.clear();
|
||||
}
|
||||
|
||||
UserModel::~UserModel()
|
||||
{
|
||||
if(m_stateResetTimer != nullptr)
|
||||
{
|
||||
m_stateResetTimer->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
auto UserModel::login() -> void
|
||||
{
|
||||
QFuture aether = QtConcurrent::run
|
||||
(
|
||||
[this] () -> void
|
||||
{
|
||||
QMutexLocker locker(&m_loginMutex);
|
||||
setState(LoggingIn);
|
||||
|
||||
QSharedPointer<UserServices> service = UserServices::instance();
|
||||
|
||||
if(service == nullptr)
|
||||
{
|
||||
setErrorString(QStringLiteral("UserServices object deleted"));
|
||||
setState(LoginFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
m_loginAttempt = service->login(username(), password().toUtf8());
|
||||
|
||||
m_loginAttempt
|
||||
.then
|
||||
(
|
||||
[this] (UserCredentials credentials) -> void
|
||||
{
|
||||
setState(LoggedIn);
|
||||
}
|
||||
)
|
||||
.onCanceled
|
||||
(
|
||||
[this] () -> void
|
||||
{
|
||||
setState(LoginFailure);
|
||||
}
|
||||
)
|
||||
.onFailed
|
||||
(
|
||||
[this] () -> void
|
||||
{
|
||||
setState(LoginCancelled);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto UserModel::login(const QString username) -> void
|
||||
{
|
||||
if(username == m_username)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_username = username;
|
||||
Q_EMIT usernameChanged();
|
||||
|
||||
QSharedPointer<UserServices> service = UserServices::instance();
|
||||
|
||||
if(service == nullptr)
|
||||
{
|
||||
setErrorString(QStringLiteral("UserServices object deleted"));
|
||||
setState(LoggedOut);
|
||||
return;
|
||||
}
|
||||
|
||||
QFuture<UserCredentials> tokenRead = service->readToken(username);
|
||||
|
||||
tokenRead
|
||||
.then
|
||||
(
|
||||
[this] (const UserCredentials credentials) -> void
|
||||
{
|
||||
if(QDateTime::currentDateTime() <= credentials.expiry)
|
||||
{
|
||||
setState(LoggedIn);
|
||||
return;
|
||||
}
|
||||
|
||||
QSharedPointer<UserServices> service = UserServices::instance();
|
||||
|
||||
if(service == nullptr)
|
||||
{
|
||||
setErrorString(QStringLiteral("UserServices object deleted"));
|
||||
setState(LoggedOut);
|
||||
return;
|
||||
}
|
||||
|
||||
QFuture<QByteArray> passwordRead = service->readPassword(credentials.username);
|
||||
|
||||
passwordRead
|
||||
.then
|
||||
(
|
||||
[this] (const QByteArray password)
|
||||
{
|
||||
if(password.isEmpty())
|
||||
{
|
||||
setState(LoggedOut);
|
||||
return;
|
||||
}
|
||||
|
||||
setPassword(password);
|
||||
}
|
||||
);
|
||||
|
||||
service.clear();
|
||||
}
|
||||
);
|
||||
|
||||
service.clear();
|
||||
}
|
||||
|
||||
auto UserModel::cancelLogin() -> void
|
||||
{
|
||||
QFuture aether = QtConcurrent::run
|
||||
(
|
||||
[this] () -> void
|
||||
{
|
||||
QMutexLocker locker(&m_loginMutex);
|
||||
|
||||
if(m_loginAttempt.isRunning())
|
||||
{
|
||||
m_loginAttempt.cancel();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto UserModel::reset() -> void
|
||||
{
|
||||
resetState();
|
||||
setUsername({});
|
||||
setPassword({});
|
||||
}
|
||||
|
||||
auto UserModel::setUsername(const QString &username) -> void
|
||||
{
|
||||
if(username == m_username)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_username = username;
|
||||
Q_EMIT usernameChanged();
|
||||
|
||||
if(!password().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
setState(Busy);
|
||||
|
||||
QSharedPointer<UserServices> service = UserServices::instance();
|
||||
|
||||
if(service == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QFuture<QByteArray> passwordRead = service->readPassword(username);
|
||||
passwordRead
|
||||
.then
|
||||
(
|
||||
[this] (const QByteArray password) -> void
|
||||
{
|
||||
setPassword(password);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto UserModel::setPassword(const QString &password) -> void
|
||||
{
|
||||
if(password == m_password)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_password = password;
|
||||
Q_EMIT passwordChanged();
|
||||
}
|
||||
|
||||
auto UserModel::resetState() -> void
|
||||
{
|
||||
setState(Idle);
|
||||
}
|
||||
|
||||
auto UserModel::setState(State state) -> void
|
||||
{
|
||||
if(state == m_state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case LoginFailure:
|
||||
case LoggedOut:
|
||||
case LoginCancelled:
|
||||
m_stateResetTimer->start();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_state = state;
|
||||
Q_EMIT stateChanged();
|
||||
}
|
||||
|
||||
auto UserModel::setErrorString(const QString &string) -> void
|
||||
{
|
||||
if(string == m_errorString)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_errorString = string;
|
||||
Q_EMIT errorStringChanged();
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#ifndef USERMODEL_H
|
||||
#define USERMODEL_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QMutexLocker>
|
||||
#include <qqmlintegration.h>
|
||||
#include "common/userservices.h"
|
||||
|
||||
class UserModel : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
public:
|
||||
|
||||
enum State
|
||||
{
|
||||
Idle,
|
||||
LoggingIn,
|
||||
LoggedIn,
|
||||
LoginFailure,
|
||||
LoginCancelled,
|
||||
LoggedOut,
|
||||
Busy
|
||||
};
|
||||
Q_ENUM(State)
|
||||
|
||||
explicit UserModel(QObject *parent = nullptr);
|
||||
~UserModel();
|
||||
|
||||
Q_INVOKABLE auto login() -> void;
|
||||
Q_INVOKABLE auto login(const QString username) -> void;
|
||||
Q_INVOKABLE auto cancelLogin() -> void;
|
||||
Q_INVOKABLE auto reset() -> void;
|
||||
|
||||
auto username() const -> const QString & { return m_username; }
|
||||
auto setUsername(const QString &username) -> void;
|
||||
|
||||
auto password() const -> const QString & { return m_password; }
|
||||
auto setPassword(const QString &password) -> void;
|
||||
|
||||
auto state() const -> State { return m_state; }
|
||||
auto errorString() const -> QString { return m_errorString; }
|
||||
|
||||
signals:
|
||||
auto loginSuccessful() -> void;
|
||||
auto loginFailed() -> void;
|
||||
auto stateChanged() -> void;
|
||||
auto usernameChanged() -> void;
|
||||
auto passwordChanged() -> void;
|
||||
auto errorStringChanged() -> void;
|
||||
|
||||
protected slots:
|
||||
auto resetState() -> void;
|
||||
|
||||
private:
|
||||
auto setState(State state) -> void;
|
||||
auto setErrorString(const QString &string) -> void;
|
||||
|
||||
State m_state = Idle;
|
||||
QString m_errorString;
|
||||
QString m_username;
|
||||
QString m_password;
|
||||
QTimer *m_stateResetTimer = nullptr;
|
||||
QMutex m_loginMutex;
|
||||
QFuture<UserCredentials> m_loginAttempt;
|
||||
|
||||
Q_PROPERTY(State state READ state NOTIFY stateChanged FINAL)
|
||||
Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged FINAL)
|
||||
Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged FINAL)
|
||||
Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged FINAL)
|
||||
};
|
||||
|
||||
#endif // USERMODEL_H
|
||||
@@ -0,0 +1,91 @@
|
||||
# Komplex AI Usage Policy (Draft)
|
||||
|
||||
| Applies To | Effective Date | Revision Date |
|
||||
| ------ | --- | --- |
|
||||
| Internal & Contributors | August 12, 2026 | August 12, 2026 |
|
||||
|
||||
This policy may be updated in the future. The updated date will be reflected on this file.
|
||||
|
||||
## Applies to
|
||||
This policy applies to contributors to Komplex and all associated repositories under the Komplex project umbrella.
|
||||
It outlines how artificial intelligence (AI) tools and systems may be used responsibly in the context of this project.
|
||||
|
||||
Our goal is to encourage innovation and productivity while protecting our users, contributors, community, and the integrity of our open source projects. We want to enable employees and contractors to understand what is, and is not, acceptable, and enable the wider community to understand and have trust in how we operate.
|
||||
|
||||
## Scope
|
||||
This policy applies to any use of AI tools that can materially influence repository outputs, including:
|
||||
- Generative AI (e.g., image, text and code assistants)
|
||||
- AI-assisted development tools (e.g., code completion and refactoring assistants)
|
||||
- Tools that can generate diffs, patches, or substantial edits
|
||||
- Tools that can draft or summarize issues, bug reports, or diagnostics
|
||||
- Tools that can generate translations files and reports
|
||||
- Agenic automation of project tasks
|
||||
|
||||
## Guiding principles
|
||||
1. Human responsibility: Humans remain accountable for decisions, code, and outputs.
|
||||
2. Transparency: If AI materially contributes to the output, disclose it clearly and concisely in pull requests, commit messages, or accompanying documentation where appropriate.
|
||||
3. Safety and quality: AI-generated suggestions must be reviewed and validated by humans before being adopted.
|
||||
4. Integrity and authorship: AI must not be used to bypass authorship, review, or verification requirements.
|
||||
5. Community and ecosystem trust: AI must not be used to mislead reviewers or circumvent project quality standards.
|
||||
|
||||
## Prohibited uses of AI
|
||||
AI tools may not be used for:
|
||||
- Agentic generation of code, patches, diffs, or substantial edits intended to become part of the repository.
|
||||
- Produce a complete file replacement, complete module, or end-to-end implementation of a feature.
|
||||
- Generate bug report details or where the bug report content would be primarily AI-authored.
|
||||
|
||||
### “Agentic” Automation
|
||||
You may not use AI tools that can take actions on your behalf without intentional invocation. For example browsing, running commands, editing files and repositories, or opening pull requests automatically.
|
||||
|
||||
### Misrepresentation and undisclosed material contribution
|
||||
Misrepresentation of AI-assisted work as human-authored where disclosure is required by this policy is subject to enforcement policies.
|
||||
|
||||
## Acceptable uses of AI
|
||||
AI tools may be used to:
|
||||
- Assist with writing or refactoring code, provided the resulting code changes are implemented, reviewed and verified by a human contributor.
|
||||
- Generating support documentation that is based on human-authored doxygen comments.
|
||||
- Help with debugging by reviewing code, generating hypotheses and proposing targeted experiments to identify root cause in existing code.
|
||||
- Translate a human-authored bug report into another language, or reformat a human-authored report for clarity.
|
||||
- Translate and generate application translation files (including strings and localized resources), subject to human review.
|
||||
- Generate images and icons when used only as placeholders, with the intention to replace them with project-approved assets.
|
||||
|
||||
All acceptable uses are subject to change.
|
||||
|
||||
### Bug Reports
|
||||
You must write the factual and investigative aspects of bug reports yourself.
|
||||
For reporting AI may be used only to:
|
||||
- Reformat or rewrite for clarity while preserving the original factual content
|
||||
- Help translate the human-authored report into a different structure, language, or style
|
||||
|
||||
### Implementation
|
||||
- You must implement changes yourself.
|
||||
- You must verify changes via tests, linting/type checks, and/or manual validation consistent with project standards.
|
||||
- Do not copy/paste AI-generated patches as the final solution.
|
||||
|
||||
### Media placeholders
|
||||
AI-generated images/icons may be used only as placeholders with the intention to replace them.
|
||||
|
||||
- Placeholder files must be clearly marked in the AI_ASSETS file.
|
||||
- Placeholders should be replaced when possible, but may require artist contribution and may not always be immediately feasible.
|
||||
|
||||
### Qt Translation Files
|
||||
- AI may be used to generate translation files.
|
||||
- AI generated translation files must be noted in AI_ASSETS
|
||||
|
||||
## Transparency and disclosure
|
||||
When disclosing the use of AI generated assets, you must disclose:
|
||||
- The file name and path relative to the project root
|
||||
- Which allowed category was used (e.g., translation file generation, bug report translation, placeholder media)
|
||||
- What tool/version was used for generation
|
||||
- Optionally can include a short message about the asset
|
||||
|
||||
## Enforcement
|
||||
Failure to comply with this policy may result in:
|
||||
|
||||
- Requesting changes to bring contributions back into compliance
|
||||
- Rejection of pull requests
|
||||
- Removal of contributor privileges where applicable
|
||||
- Escalation to maintainers for further action
|
||||
|
||||
## Policy values statement
|
||||
AI is a tool, not a substitute for responsibility. In Komplex, AI may be used to increase productivity and assist with debugging, review, translation, and placeholders, but humans must remain accountable for the correctness, safety, and authorship of repository outputs.
|
||||
@@ -34,7 +34,10 @@
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Most of the development of Komplex and it's associated apps will now happen here, instead of Github.
|
||||
<small>Note: Application style is based on your system theme. It will look different than it does in the screenshots. I am using a modified KVantum Glass, Blur and Neon Sunset Wallpaper Pack.</small>
|
||||
|
||||
## Development
|
||||
Most of the development of Komplex and applications under its project umbrella will now happen on https://git.digitalartifex.dev instead of Github.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -51,10 +54,11 @@ Most of the development of Komplex and it's associated apps will now happen here
|
||||
- Qt6 Shader Tools
|
||||
- CMake
|
||||
- clang
|
||||
- C Pre Processor (cpp)
|
||||
|
||||
#### Build
|
||||
```console
|
||||
$ git clone gitea@git.digitalartifex.dev:digitalartifex/komplex-hub.git
|
||||
$ git clone --recursive gitea@git.digitalartifex.dev:digitalartifex/komplex-hub.git
|
||||
$ mkdir build
|
||||
$ cmake -S ./ -B ./build/
|
||||
$ cmake --build ./build
|
||||
@@ -74,4 +78,110 @@ Content purchases will be processed through the website using Paypal as will art
|
||||
|
||||
## Status
|
||||
|
||||
This app is currently under development and is pre-alpha. The API is now being hosted on https://komplex.dev. However, there is no download functionality as of yet. BETA should be here within a few weeks.
|
||||
### Legend
|
||||
|
||||
| Status | Icon | Description |
|
||||
|-------|:-------:|--------|
|
||||
| Not started | ❌ | Either no work has been done yet or so little work that it is unusable in its current state.
|
||||
| In Progress | ✔ | *Some* work has been completed, but is not yet functional and/or feature complete
|
||||
| Complete | ✅ | Planned features completed
|
||||
|
||||
### API Status
|
||||
|
||||
| Feature | Status |
|
||||
|-------|:-------:|
|
||||
| Packs | ✔ | |
|
||||
| - Download Packs | ✅ | |
|
||||
| - Newest Packs | ✅ | |
|
||||
| - Featured Packs | ✔ | |
|
||||
| - Search Packs | ✅ | |
|
||||
| - Submit Packs | ✔ | |
|
||||
| - Thumbnails | ✅ | |
|
||||
| | | |
|
||||
| Cubemaps | ✔ | |
|
||||
| - Download Cubemaps | ✅ | |
|
||||
| - Newest Cubemaps | ✅ | |
|
||||
| - Featured Cubemaps | ✔ | |
|
||||
| - Search Cubemaps | ✅ | |
|
||||
| - Submit Cubemaps | ✔ | - |
|
||||
| | | |
|
||||
| Filter Shaders | ✔ | |
|
||||
| - Download Filter Shaders | ✅ | |
|
||||
| - Newest Filter Shaders | ✅ | |
|
||||
| - Featured Filter Shaders | ✔ | |
|
||||
| - Search Filter Shaders | ✅ | |
|
||||
| - Submit Filter Shaders | ✔ | - |
|
||||
| | | |
|
||||
| Images | ✅ | |
|
||||
| - Featured Images | ✅ | |
|
||||
| - Search Images | ✅ | |
|
||||
| | | |
|
||||
| Videos | ✅ | |
|
||||
| - Popular Videos | ✅ | |
|
||||
| - Search Videos | ✅ | |
|
||||
| | | |
|
||||
| Accounts | ✔ | |
|
||||
| - User Accounts | ✔ | |
|
||||
| - User Authentication | ✔ | |
|
||||
| - User Editing | ❌ | |
|
||||
| - User Configuration | ❌ | |
|
||||
| - User Media | ❌ | |
|
||||
| - Author Profiles | ❌ | |
|
||||
|
||||
### Media Hub Status
|
||||
|
||||
| Feature | Status |
|
||||
|-------|:-------:|
|
||||
| Spotlight/Homepage | ✔ | |
|
||||
| - Newest Packs | ✅ | |
|
||||
| - Featured Packs | ✔ | |
|
||||
| - Featured Images | ✅ | |
|
||||
| - Popular Videos | ✅ | |
|
||||
| - View More | ✅ | |
|
||||
| | | |
|
||||
| Media Search | ✅ | |
|
||||
| - Packs | ✅ | |
|
||||
| - Videos | ✅ | |
|
||||
| - Images | ✅ | |
|
||||
| | | |
|
||||
| Media Downloader | ✅ | |
|
||||
| - Packs | ✅ | |
|
||||
| - Videos | ✅ | |
|
||||
| - Images | ✅ | |
|
||||
| | | |
|
||||
| Detailed Views | ✅ | |
|
||||
| - Packs | ✅ | |
|
||||
| - Videos | ✅ | |
|
||||
| - Images | ✅ | |
|
||||
| | | |
|
||||
| App Management | ✔ | |
|
||||
| Installed Media Manager | ✔ | |
|
||||
| Settings Manager | ✅ | |
|
||||
| | | |
|
||||
| Account Management | ❌ | |
|
||||
| User Accounts | ❌ | |
|
||||
| - User Login | ❌ | |
|
||||
| - User Profile | ❌ | |
|
||||
| - Author Profile | ❌ | |
|
||||
| About Page | ✅ | |
|
||||
|
||||
### Pack Designer
|
||||
|
||||
| Feature | Status |
|
||||
|-------|:-------:|
|
||||
| All Design & Features | ❌ | |
|
||||
|
||||
### Site
|
||||
|
||||
| Feature | Status |
|
||||
|-------|:-------:|
|
||||
| Site Base | ✔ | |
|
||||
| Accounts | ✔ | |
|
||||
| Gallery | ❌ | |
|
||||
| Payment Gateway | ❌ | |
|
||||
| - Artist Payout | ❌ | |
|
||||
| - User Media Purchase | ❌ | |
|
||||
|
||||
This app is currently under development and is currently BETA1 and the new API is live. Please do expect bugs in pack compiling at this stage.
|
||||
|
||||
"Featured" Live wallpapers is currently a duplicated of "Newest". The featured endpoint will take manual curation, so this will take some time.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.3 MiB After Width: | Height: | Size: 2.0 MiB |
@@ -11,8 +11,8 @@ target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
KomplexHubModuleplugin
|
||||
KomplexHubContentplugin
|
||||
KomplexHubPluginplugin
|
||||
KomplexHubModule_Controlsplugin
|
||||
KomplexHubModule_Keroplugin
|
||||
KomplexHubControlsplugin
|
||||
KomplexHubKeroplugin
|
||||
Qt6::Quick
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
|
||||
Reference in New Issue
Block a user