Compare commits

..

2 Commits

Author SHA1 Message Date
Digital Artifex 8a9417a5e5 Added featured images model, paginator and cache 2026-06-17 14:52:15 -04:00
Digital Artifex ac71b74ed0 Added thumbnail image 2026-06-17 14:50:55 -04:00
10 changed files with 813 additions and 77 deletions
+8 -8
View File
@@ -120,7 +120,6 @@ Item {
delegate: Item {
width: 256
height: 256
required property string name
required property string author
required property string description
required property string uuid
@@ -130,8 +129,8 @@ Item {
SearchResultItem
{
anchors.fill: parent
title: parent.name
author: parent.author
title: parent.author
author: qsTr("Pexels Images")
description: parent.description
thumbnail: parent.thumbnail
uuid: parent.uuid
@@ -176,17 +175,18 @@ Item {
delegate: Item {
width: 256
height: 256
required property string name
required property string author
required property string description
required property string uuid
required property string thumbnail
required property string author
required property string authorId
required property string authorUrl
required property string description
required property string thumbnail
required property string original
SearchResultItem
{
anchors.fill: parent
title: parent.name
title: "Pexels Photo"
author: parent.author
description: parent.description
thumbnail: parent.thumbnail
+20 -5
View File
@@ -7,7 +7,6 @@ Item {
property string description: "This is a description of the search result item"
property string title: "Wallpaper Title"
property string thumbnail: ""
property string url: ""
property string uuid: ""
property bool selected: false
@@ -18,7 +17,8 @@ Item {
height: 256
clip: true
Rectangle {
Rectangle
{
id: searchResultItemContainer
anchors.fill: parent
@@ -26,10 +26,12 @@ Item {
color: palette.alternateBase.lighter(1.25)
ColumnLayout {
ColumnLayout
{
anchors.fill: parent
Rectangle {
Rectangle
{
Layout.preferredWidth: 250
Layout.preferredHeight: 141
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
@@ -38,6 +40,14 @@ Item {
border.color: palette.alternateBase.lighter(1.75)
color: palette.base.lighter(1.25)
Image
{
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
id: thumnailImage
source: searchResultItemRoot.thumbnail
}
}
Text {
@@ -63,14 +73,19 @@ Item {
}
Text {
clip: true
color: palette.text
font.pixelSize: 12
text: searchResultItemRoot.description
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
textFormat: Text.PlainText
Layout.maximumHeight: 32
Layout.preferredHeight: 32
elide: Text.ElideRight
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
Layout.preferredWidth: 236
Layout.maximumWidth: 236
Layout.bottomMargin: 3
}
}
+2
View File
@@ -46,6 +46,8 @@ qt_add_qml_module(
SOURCES featuredvideosmodel.h
SOURCES featuredvideosmodel.cpp
SOURCES paginators/featuredvideospaginator.h
SOURCES common/imagecache.h
SOURCES paginators/imagepaginator.h
)
+337
View File
@@ -0,0 +1,337 @@
{
"page": 1,
"per_page": 15,
"photos": [
{
"alt": "A solitary church atop a hill in Iceland set against a dramatic cloudy sky in black and white.",
"avg_color": "#777777",
"height": 4000,
"id": 37953511,
"liked": false,
"photographer": "Alexis B",
"photographer_id": 1699196,
"photographer_url": "https://www.pexels.com/@alexis-b-1699196",
"src": {
"landscape": "https://images.pexels.com/photos/37953511/pexels-photo-37953511.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=627&w=1200",
"large": "https://images.pexels.com/photos/37953511/pexels-photo-37953511.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
"large2x": "https://images.pexels.com/photos/37953511/pexels-photo-37953511.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"medium": "https://images.pexels.com/photos/37953511/pexels-photo-37953511.jpeg?auto=compress&cs=tinysrgb&h=350",
"original": "https://images.pexels.com/photos/37953511/pexels-photo-37953511.jpeg",
"portrait": "https://images.pexels.com/photos/37953511/pexels-photo-37953511.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=1200&w=800",
"small": "https://images.pexels.com/photos/37953511/pexels-photo-37953511.jpeg?auto=compress&cs=tinysrgb&h=130",
"tiny": "https://images.pexels.com/photos/37953511/pexels-photo-37953511.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280"
},
"url": "https://www.pexels.com/photo/isolated-church-on-icelandic-hill-in-black-and-white-37953511/",
"width": 6000
},
{
"alt": "A stylish wedding couple poses in a historic Istanbul location, embodying romance and elegance.",
"avg_color": "#242424",
"height": 6304,
"id": 38109250,
"liked": false,
"photographer": "Mina Fotoğraf",
"photographer_id": 2162259056,
"photographer_url": "https://www.pexels.com/@mina-fotograf-2162259056",
"src": {
"landscape": "https://images.pexels.com/photos/38109250/pexels-photo-38109250.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=627&w=1200",
"large": "https://images.pexels.com/photos/38109250/pexels-photo-38109250.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
"large2x": "https://images.pexels.com/photos/38109250/pexels-photo-38109250.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"medium": "https://images.pexels.com/photos/38109250/pexels-photo-38109250.jpeg?auto=compress&cs=tinysrgb&h=350",
"original": "https://images.pexels.com/photos/38109250/pexels-photo-38109250.jpeg",
"portrait": "https://images.pexels.com/photos/38109250/pexels-photo-38109250.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=1200&w=800",
"small": "https://images.pexels.com/photos/38109250/pexels-photo-38109250.jpeg?auto=compress&cs=tinysrgb&h=130",
"tiny": "https://images.pexels.com/photos/38109250/pexels-photo-38109250.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280"
},
"url": "https://www.pexels.com/photo/elegant-wedding-couple-in-historic-istanbul-venue-38109250/",
"width": 4203
},
{
"alt": "Black and white cityscape capturing two people walking along a quaint European street at twilight.",
"avg_color": "#434343",
"height": 7728,
"id": 37644555,
"liked": false,
"photographer": "Alec Doualetas",
"photographer_id": 61739,
"photographer_url": "https://www.pexels.com/@alecdoua",
"src": {
"landscape": "https://images.pexels.com/photos/37644555/pexels-photo-37644555.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=627&w=1200",
"large": "https://images.pexels.com/photos/37644555/pexels-photo-37644555.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
"large2x": "https://images.pexels.com/photos/37644555/pexels-photo-37644555.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"medium": "https://images.pexels.com/photos/37644555/pexels-photo-37644555.jpeg?auto=compress&cs=tinysrgb&h=350",
"original": "https://images.pexels.com/photos/37644555/pexels-photo-37644555.jpeg",
"portrait": "https://images.pexels.com/photos/37644555/pexels-photo-37644555.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=1200&w=800",
"small": "https://images.pexels.com/photos/37644555/pexels-photo-37644555.jpeg?auto=compress&cs=tinysrgb&h=130",
"tiny": "https://images.pexels.com/photos/37644555/pexels-photo-37644555.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280"
},
"url": "https://www.pexels.com/photo/charming-urban-street-view-with-evening-stroll-37644555/",
"width": 5152
},
{
"alt": "Monochrome portrait of a young man in a denim jacket seated indoors.",
"avg_color": "#626262",
"height": 6000,
"id": 38110448,
"liked": false,
"photographer": "Portraits By PB",
"photographer_id": 2162091442,
"photographer_url": "https://www.pexels.com/@pbportraits",
"src": {
"landscape": "https://images.pexels.com/photos/38110448/pexels-photo-38110448.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=627&w=1200",
"large": "https://images.pexels.com/photos/38110448/pexels-photo-38110448.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
"large2x": "https://images.pexels.com/photos/38110448/pexels-photo-38110448.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"medium": "https://images.pexels.com/photos/38110448/pexels-photo-38110448.jpeg?auto=compress&cs=tinysrgb&h=350",
"original": "https://images.pexels.com/photos/38110448/pexels-photo-38110448.jpeg",
"portrait": "https://images.pexels.com/photos/38110448/pexels-photo-38110448.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=1200&w=800",
"small": "https://images.pexels.com/photos/38110448/pexels-photo-38110448.jpeg?auto=compress&cs=tinysrgb&h=130",
"tiny": "https://images.pexels.com/photos/38110448/pexels-photo-38110448.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280"
},
"url": "https://www.pexels.com/photo/black-and-white-portrait-of-young-man-in-denim-38110448/",
"width": 4000
},
{
"alt": "A dynamic black and white photo capturing reflections on modern building windows.",
"avg_color": "#6B6B6B",
"height": 3840,
"id": 37488903,
"liked": false,
"photographer": "wal_ 172619",
"photographer_id": 2156618639,
"photographer_url": "https://www.pexels.com/@wal_-172619-2156618639",
"src": {
"landscape": "https://images.pexels.com/photos/37488903/pexels-photo-37488903.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=627&w=1200",
"large": "https://images.pexels.com/photos/37488903/pexels-photo-37488903.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
"large2x": "https://images.pexels.com/photos/37488903/pexels-photo-37488903.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"medium": "https://images.pexels.com/photos/37488903/pexels-photo-37488903.jpeg?auto=compress&cs=tinysrgb&h=350",
"original": "https://images.pexels.com/photos/37488903/pexels-photo-37488903.jpeg",
"portrait": "https://images.pexels.com/photos/37488903/pexels-photo-37488903.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=1200&w=800",
"small": "https://images.pexels.com/photos/37488903/pexels-photo-37488903.jpeg?auto=compress&cs=tinysrgb&h=130",
"tiny": "https://images.pexels.com/photos/37488903/pexels-photo-37488903.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280"
},
"url": "https://www.pexels.com/photo/abstract-black-and-white-modern-building-reflection-37488903/",
"width": 5760
},
{
"alt": "Black and white photo of cyclists racing under a bridge, casting shadows on a concrete wall.",
"avg_color": "#666666",
"height": 3648,
"id": 37730682,
"liked": false,
"photographer": "Emeric Laperriere",
"photographer_id": 3009913,
"photographer_url": "https://www.pexels.com/@emeric07",
"src": {
"landscape": "https://images.pexels.com/photos/37730682/pexels-photo-37730682.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=627&w=1200",
"large": "https://images.pexels.com/photos/37730682/pexels-photo-37730682.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
"large2x": "https://images.pexels.com/photos/37730682/pexels-photo-37730682.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"medium": "https://images.pexels.com/photos/37730682/pexels-photo-37730682.jpeg?auto=compress&cs=tinysrgb&h=350",
"original": "https://images.pexels.com/photos/37730682/pexels-photo-37730682.jpeg",
"portrait": "https://images.pexels.com/photos/37730682/pexels-photo-37730682.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=1200&w=800",
"small": "https://images.pexels.com/photos/37730682/pexels-photo-37730682.jpeg?auto=compress&cs=tinysrgb&h=130",
"tiny": "https://images.pexels.com/photos/37730682/pexels-photo-37730682.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280"
},
"url": "https://www.pexels.com/photo/cyclists-racing-under-urban-bridge-in-shadows-37730682/",
"width": 5472
},
{
"alt": "Two hikers standing atop a grassy hill in Scotland, captured in striking black and white.",
"avg_color": "#9C9C9C",
"height": 4530,
"id": 33777882,
"liked": false,
"photographer": "Phil Desforges",
"photographer_id": 1226422,
"photographer_url": "https://www.pexels.com/@storybyphil",
"src": {
"landscape": "https://images.pexels.com/photos/33777882/pexels-photo-33777882.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=627&w=1200",
"large": "https://images.pexels.com/photos/33777882/pexels-photo-33777882.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
"large2x": "https://images.pexels.com/photos/33777882/pexels-photo-33777882.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"medium": "https://images.pexels.com/photos/33777882/pexels-photo-33777882.jpeg?auto=compress&cs=tinysrgb&h=350",
"original": "https://images.pexels.com/photos/33777882/pexels-photo-33777882.jpeg",
"portrait": "https://images.pexels.com/photos/33777882/pexels-photo-33777882.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=1200&w=800",
"small": "https://images.pexels.com/photos/33777882/pexels-photo-33777882.jpeg?auto=compress&cs=tinysrgb&h=130",
"tiny": "https://images.pexels.com/photos/33777882/pexels-photo-33777882.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280"
},
"url": "https://www.pexels.com/photo/stunning-black-and-white-hiking-scene-in-scotland-33777882/",
"width": 3624
},
{
"alt": "Silhouettes of two children playing indoors by a large window with a view of a ship in İstanbul.",
"avg_color": "#A4A4A4",
"height": 4209,
"id": 28319243,
"liked": false,
"photographer": "İrem Dur",
"photographer_id": 1175044181,
"photographer_url": "https://www.pexels.com/@i-rem-dur-1175044181",
"src": {
"landscape": "https://images.pexels.com/photos/28319243/pexels-photo-28319243.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=627&w=1200",
"large": "https://images.pexels.com/photos/28319243/pexels-photo-28319243.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
"large2x": "https://images.pexels.com/photos/28319243/pexels-photo-28319243.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"medium": "https://images.pexels.com/photos/28319243/pexels-photo-28319243.jpeg?auto=compress&cs=tinysrgb&h=350",
"original": "https://images.pexels.com/photos/28319243/pexels-photo-28319243.jpeg",
"portrait": "https://images.pexels.com/photos/28319243/pexels-photo-28319243.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=1200&w=800",
"small": "https://images.pexels.com/photos/28319243/pexels-photo-28319243.jpeg?auto=compress&cs=tinysrgb&h=130",
"tiny": "https://images.pexels.com/photos/28319243/pexels-photo-28319243.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280"
},
"url": "https://www.pexels.com/photo/two-people-are-walking-through-a-glass-door-28319243/",
"width": 6313
},
{
"alt": "Delicate cherry blossoms captured in black and white, showcasing their elegant beauty in springtime.",
"avg_color": "#797979",
"height": 3089,
"id": 27661694,
"liked": false,
"photographer": "Anna Holodna",
"photographer_id": 237271098,
"photographer_url": "https://www.pexels.com/@holodna",
"src": {
"landscape": "https://images.pexels.com/photos/27661694/pexels-photo-27661694.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=627&w=1200",
"large": "https://images.pexels.com/photos/27661694/pexels-photo-27661694.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
"large2x": "https://images.pexels.com/photos/27661694/pexels-photo-27661694.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"medium": "https://images.pexels.com/photos/27661694/pexels-photo-27661694.jpeg?auto=compress&cs=tinysrgb&h=350",
"original": "https://images.pexels.com/photos/27661694/pexels-photo-27661694.jpeg",
"portrait": "https://images.pexels.com/photos/27661694/pexels-photo-27661694.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=1200&w=800",
"small": "https://images.pexels.com/photos/27661694/pexels-photo-27661694.jpeg?auto=compress&cs=tinysrgb&h=130",
"tiny": "https://images.pexels.com/photos/27661694/pexels-photo-27661694.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280"
},
"url": "https://www.pexels.com/photo/black-and-white-photograph-of-flowers-in-bloom-27661694/",
"width": 2048
},
{
"alt": "Textured sand dunes in Monahans, Texas captured in striking black and white.",
"avg_color": "#8E8E8E",
"height": 6336,
"id": 37152401,
"liked": false,
"photographer": "Phil Evenden",
"photographer_id": 69038984,
"photographer_url": "https://www.pexels.com/@philevenphotos",
"src": {
"landscape": "https://images.pexels.com/photos/37152401/pexels-photo-37152401.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=627&w=1200",
"large": "https://images.pexels.com/photos/37152401/pexels-photo-37152401.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
"large2x": "https://images.pexels.com/photos/37152401/pexels-photo-37152401.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"medium": "https://images.pexels.com/photos/37152401/pexels-photo-37152401.jpeg?auto=compress&cs=tinysrgb&h=350",
"original": "https://images.pexels.com/photos/37152401/pexels-photo-37152401.jpeg",
"portrait": "https://images.pexels.com/photos/37152401/pexels-photo-37152401.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=1200&w=800",
"small": "https://images.pexels.com/photos/37152401/pexels-photo-37152401.jpeg?auto=compress&cs=tinysrgb&h=130",
"tiny": "https://images.pexels.com/photos/37152401/pexels-photo-37152401.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280"
},
"url": "https://www.pexels.com/photo/monahans-sandhills-ripples-in-black-and-white-37152401/",
"width": 9504
},
{
"alt": "Black and white photo of a bicycle leaning against a brick building in Copenhagen.",
"avg_color": "#707070",
"height": 3918,
"id": 20643887,
"liked": false,
"photographer": "Mihai Vlasceanu",
"photographer_id": 602123,
"photographer_url": "https://www.pexels.com/@vlasceanu",
"src": {
"landscape": "https://images.pexels.com/photos/20643887/pexels-photo-20643887.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=627&w=1200",
"large": "https://images.pexels.com/photos/20643887/pexels-photo-20643887.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
"large2x": "https://images.pexels.com/photos/20643887/pexels-photo-20643887.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"medium": "https://images.pexels.com/photos/20643887/pexels-photo-20643887.jpeg?auto=compress&cs=tinysrgb&h=350",
"original": "https://images.pexels.com/photos/20643887/pexels-photo-20643887.jpeg",
"portrait": "https://images.pexels.com/photos/20643887/pexels-photo-20643887.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=1200&w=800",
"small": "https://images.pexels.com/photos/20643887/pexels-photo-20643887.jpeg?auto=compress&cs=tinysrgb&h=130",
"tiny": "https://images.pexels.com/photos/20643887/pexels-photo-20643887.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280"
},
"url": "https://www.pexels.com/photo/bicycle-near-building-wall-in-black-and-white-20643887/",
"width": 5602
},
{
"alt": "A contemplative moment captured at a park in Honolulu with silhouetted trees and a bench.",
"avg_color": "#36332E",
"height": 5762,
"id": 37134995,
"liked": false,
"photographer": "Roy Serafin",
"photographer_id": 2157973447,
"photographer_url": "https://www.pexels.com/@roythephotographer",
"src": {
"landscape": "https://images.pexels.com/photos/37134995/pexels-photo-37134995.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=627&w=1200",
"large": "https://images.pexels.com/photos/37134995/pexels-photo-37134995.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
"large2x": "https://images.pexels.com/photos/37134995/pexels-photo-37134995.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"medium": "https://images.pexels.com/photos/37134995/pexels-photo-37134995.jpeg?auto=compress&cs=tinysrgb&h=350",
"original": "https://images.pexels.com/photos/37134995/pexels-photo-37134995.jpeg",
"portrait": "https://images.pexels.com/photos/37134995/pexels-photo-37134995.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=1200&w=800",
"small": "https://images.pexels.com/photos/37134995/pexels-photo-37134995.jpeg?auto=compress&cs=tinysrgb&h=130",
"tiny": "https://images.pexels.com/photos/37134995/pexels-photo-37134995.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280"
},
"url": "https://www.pexels.com/photo/serene-silhouette-in-honolulu-park-37134995/",
"width": 3839
},
{
"alt": "Panoramic view of rugged cliffs and ocean in La Gomera, showcasing dramatic landscapes.",
"avg_color": "#6E7C7F",
"height": 6000,
"id": 34145802,
"liked": false,
"photographer": "Liisbet Luup",
"photographer_id": 121486327,
"photographer_url": "https://www.pexels.com/@liisbet-luup-121486327",
"src": {
"landscape": "https://images.pexels.com/photos/34145802/pexels-photo-34145802.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=627&w=1200",
"large": "https://images.pexels.com/photos/34145802/pexels-photo-34145802.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
"large2x": "https://images.pexels.com/photos/34145802/pexels-photo-34145802.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"medium": "https://images.pexels.com/photos/34145802/pexels-photo-34145802.jpeg?auto=compress&cs=tinysrgb&h=350",
"original": "https://images.pexels.com/photos/34145802/pexels-photo-34145802.jpeg",
"portrait": "https://images.pexels.com/photos/34145802/pexels-photo-34145802.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=1200&w=800",
"small": "https://images.pexels.com/photos/34145802/pexels-photo-34145802.jpeg?auto=compress&cs=tinysrgb&h=130",
"tiny": "https://images.pexels.com/photos/34145802/pexels-photo-34145802.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280"
},
"url": "https://www.pexels.com/photo/dramatic-cliffs-and-ocean-view-canary-islands-34145802/",
"width": 4000
},
{
"alt": "Lush pine trees in a tranquil forest setting, capturing the beauty of Finnish nature.",
"avg_color": "#6B645A",
"height": 3637,
"id": 38101841,
"liked": false,
"photographer": "Alice Tran",
"photographer_id": 2162267306,
"photographer_url": "https://www.pexels.com/@alice-tran-2162267306",
"src": {
"landscape": "https://images.pexels.com/photos/38101841/pexels-photo-38101841.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=627&w=1200",
"large": "https://images.pexels.com/photos/38101841/pexels-photo-38101841.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
"large2x": "https://images.pexels.com/photos/38101841/pexels-photo-38101841.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"medium": "https://images.pexels.com/photos/38101841/pexels-photo-38101841.jpeg?auto=compress&cs=tinysrgb&h=350",
"original": "https://images.pexels.com/photos/38101841/pexels-photo-38101841.jpeg",
"portrait": "https://images.pexels.com/photos/38101841/pexels-photo-38101841.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=1200&w=800",
"small": "https://images.pexels.com/photos/38101841/pexels-photo-38101841.jpeg?auto=compress&cs=tinysrgb&h=130",
"tiny": "https://images.pexels.com/photos/38101841/pexels-photo-38101841.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280"
},
"url": "https://www.pexels.com/photo/serene-pine-forest-in-finnish-wilderness-38101841/",
"width": 2433
},
{
"alt": "A scenic aerial view of boats anchored in a tranquil blue bay surrounded by lush hills.",
"avg_color": "#3B6571",
"height": 7008,
"id": 33802711,
"liked": false,
"photographer": "Sueda Dilli",
"photographer_id": 143459271,
"photographer_url": "https://www.pexels.com/@suedadilli",
"src": {
"landscape": "https://images.pexels.com/photos/33802711/pexels-photo-33802711.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=627&w=1200",
"large": "https://images.pexels.com/photos/33802711/pexels-photo-33802711.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
"large2x": "https://images.pexels.com/photos/33802711/pexels-photo-33802711.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"medium": "https://images.pexels.com/photos/33802711/pexels-photo-33802711.jpeg?auto=compress&cs=tinysrgb&h=350",
"original": "https://images.pexels.com/photos/33802711/pexels-photo-33802711.jpeg",
"portrait": "https://images.pexels.com/photos/33802711/pexels-photo-33802711.jpeg?auto=compress&cs=tinysrgb&fit=crop&h=1200&w=800",
"small": "https://images.pexels.com/photos/33802711/pexels-photo-33802711.jpeg?auto=compress&cs=tinysrgb&h=130",
"tiny": "https://images.pexels.com/photos/33802711/pexels-photo-33802711.jpeg?auto=compress&cs=tinysrgb&dpr=1&fit=crop&h=200&w=280"
},
"url": "https://www.pexels.com/photo/serene-aerial-view-of-boats-in-a-blue-bay-33802711/",
"width": 4672
}
],
"total_results": 64520
}
+48
View File
@@ -0,0 +1,48 @@
#ifndef IMAGECACHE_H
#define IMAGECACHE_H
#include "komplex_global.h"
#include <QObject>
#include <QMap>
#include <QString>
struct KOMPLEX_EXPORT ImageCache
{
QString altText;
QByteArray averageColor;
qsizetype height;
qsizetype width;
qint64 id;
bool liked; //probably not needed
QString photographer;
QString photographerUrl;
qint64 photographerId;
QMap<QString, QString> sources;
QString url;
auto operator == (const ImageCache &other) const -> bool
{
return (
other.altText == altText &&
other.averageColor == averageColor &&
other.height == height &&
other.width == width &&
other.id == id &&
other.liked == liked &&
other.photographer == photographer &&
other.photographerUrl == photographerUrl &&
other.photographerId == photographerId &&
other.sources == sources &&
other.url == url
);
}
auto operator != (const ImageCache &other) const -> bool
{
return !(other == *this);
}
};
Q_DECLARE_METATYPE(ImageCache)
#endif // IMAGECACHE_H
+31 -25
View File
@@ -59,44 +59,50 @@ auto FeaturedImagesModel::data(const QModelIndex &index, int role) const -> QVar
return {};
}
WallpaperCache dataPoint = m_data.at(index.row());
ImageCache dataPoint = m_data.at(index.row());
QVariant data;
switch(static_cast<DataRole>(role))
{
case UuidRole:
data = dataPoint.uuid;
data = dataPoint.id;
break;
case AuthorRole:
data = dataPoint.author;
break;
case DescriptionRole:
data = dataPoint.description;
break;
case NameRole:
data = dataPoint.name;
break;
case ThumbnailRole:
data = dataPoint.thumbnail;
break;
case CreatedDateRole:
data = dataPoint.createdDate;
data = dataPoint.photographer;
break;
case AuthorIdRole:
data = dataPoint.authorId;
data = dataPoint.photographerId;
break;
case PriceRole:
data = dataPoint.price;
case AuthorUrlRole:
data = dataPoint.photographerUrl;
break;
case CurrencyRole:
data = dataPoint.currency;
case DescriptionRole:
data = dataPoint.altText;
break;
case DownloadCountRole:
data = dataPoint.downloadCount;
case ThumbnailRole:
data = dataPoint.sources.value(QString::fromUtf8("tiny"));
break;
case TypeRole:
data = dataPoint.type;
case PortraitUrlRole:
data = dataPoint.sources.value(QString::fromUtf8("portrait"));
break;
case LandscapeUrlRole:
data = dataPoint.sources.value(QString::fromUtf8("landscape"));
break;
case SmallUrlRole:
data = dataPoint.sources.value(QString::fromUtf8("small"));
break;
case OriginalUrlRole:
data = dataPoint.sources.value(QString::fromUtf8("original"));
break;
case MediumUrlRole:
data = dataPoint.sources.value(QString::fromUtf8("medium"));
break;
case LargeUrlRole:
data = dataPoint.sources.value(QString::fromUtf8("large"));
break;
case ExtraLargeUrlRole:
data = dataPoint.sources.value(QString::fromUtf8("large2x"));
break;
}
@@ -160,7 +166,7 @@ auto FeaturedImagesModel::resetDataModel() -> void
beginInsertRows(QModelIndex(), 0, m_paginator->count() - 1);
auto dataset = m_paginator->data();
for(const WallpaperCache& data : std::as_const(dataset))
for(const ImageCache& data : std::as_const(dataset))
{
m_data.append(data);
}
+31 -21
View File
@@ -65,14 +65,16 @@ public:
UuidRole = Qt::UserRole + 1,
AuthorRole,
AuthorIdRole,
AuthorUrlRole,
DescriptionRole,
NameRole,
ThumbnailRole,
CreatedDateRole,
PriceRole,
CurrencyRole,
DownloadCountRole,
TypeRole
PortraitUrlRole,
LandscapeUrlRole,
SmallUrlRole,
OriginalUrlRole,
MediumUrlRole,
LargeUrlRole,
ExtraLargeUrlRole
};
Q_ENUM(DataRole)
@@ -251,36 +253,44 @@ private:
QByteArray("authorId")
},
{
static_cast<int>(DescriptionRole),
QByteArray("description")
static_cast<int>(AuthorUrlRole),
QByteArray("authorUrl")
},
{
static_cast<int>(NameRole),
QByteArray("name")
static_cast<int>(DescriptionRole),
QByteArray("description")
},
{
static_cast<int>(ThumbnailRole),
QByteArray("thumbnail")
},
{
static_cast<int>(CreatedDateRole),
QByteArray("createdDate")
static_cast<int>(PortraitUrlRole),
QByteArray("portrait")
},
{
static_cast<int>(PriceRole),
QByteArray("price")
static_cast<int>(LandscapeUrlRole),
QByteArray("landscape")
},
{
static_cast<int>(CurrencyRole),
QByteArray("currency")
static_cast<int>(SmallUrlRole),
QByteArray("small")
},
{
static_cast<int>(DownloadCountRole),
QByteArray("downloadCount")
static_cast<int>(OriginalUrlRole),
QByteArray("original")
},
{
static_cast<int>(TypeRole),
QByteArray("type")
static_cast<int>(MediumUrlRole),
QByteArray("medium")
},
{
static_cast<int>(LargeUrlRole),
QByteArray("large")
},
{
static_cast<int>(ExtraLargeUrlRole),
QByteArray("extraLarge")
}
};
State m_state = Idle;
@@ -289,7 +299,7 @@ private:
FeaturedImagesPaginator *m_paginator = nullptr;
mutable QList<WallpaperCache> m_data;
mutable QList<ImageCache> m_data;
Q_PROPERTY(State state READ state WRITE setState RESET resetState NOTIFY stateChanged FINAL)
Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged FINAL)
@@ -29,12 +29,12 @@
#include <QJsonArray>
#include <QJsonObject>
#include "wallpaperpaginator.h"
#include "imagepaginator.h"
class KOMPLEX_EXPORT FeaturedImagesPaginator : public WallpaperPaginator
class KOMPLEX_EXPORT FeaturedImagesPaginator : public ImagePaginator
{
public:
explicit FeaturedImagesPaginator(QObject *parent = nullptr) : WallpaperPaginator(parent)
explicit FeaturedImagesPaginator(QObject *parent = nullptr) : ImagePaginator(parent)
{
setUri(
QString::fromUtf8(KOMPLEX_ENDPOINT_IMAGES_FEATURED)
@@ -0,0 +1,317 @@
/*
* 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 IMAGEPAGINATOR_H
#define IMAGEPAGINATOR_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QEventLoop>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QJsonArray>
#include <QJsonObject>
#include "common/logging.h"
#include "common/paginator.h"
#include "common/imagecache.h"
#include "common/coreservices.h"
/**
* @brief The ImagePaginator class
* Most of the media endpoints for the Komplex API have the same data structure so
* child classes can make use of this generic controller where possible
*/
class KOMPLEX_EXPORT ImagePaginator : public Paginator<ImageCache>
{
Q_OBJECT
public:
explicit ImagePaginator(QObject *parent = nullptr) : Paginator<ImageCache>(parent)
{
SlidingCacheController<ImageCache> *controller = this->controller();
controller->setFetch(
std::bind(&ImagePaginator::fetch, this, std::placeholders::_1, std::placeholders::_2)
);
}
/**
* @brief uri
* URI of the API Endpoint. This is set by the child class
* @return
*/
[[nodiscard]]
auto uri() const -> QString
{
return m_uri;
}
/**
* @brief query
* Current query string, if the API Endpoint supports queries
* @return
*/
[[nodiscard]]
auto query() const -> QString
{
return m_query;
}
/**
* @brief setQuery
* Sets the new query string and updates the controller
* @param query
*/
auto setQuery(const QString &query) -> void
{
if(query == m_query)
{
return;
}
m_query = query;
Q_EMIT queryChanged();
reset();
setOffset(0);
}
signals:
/**
* @brief uriChanged
*/
auto uriChanged() -> void;
/**
* @brief queryChanged
*/
auto queryChanged() -> void;
protected:
/**
* @brief fetch
* Used to fetch results from the API endpoint
* for the CacheController. It is intended to be passed
* to the Cache Controller not meant to be called directly.
* @param offset Offset index to start the results from
* @param limit Number of results to attempt to fetch
* @return FetchResult<ImageCache>
*/
[[nodiscard]]
auto fetch(
qsizetype offset,
qsizetype limit
) const -> FetchResult<ImageCache>
{
LOG_ERROR_X(m_uri.isEmpty(),
"ImagePaginator::fetch",
"Uri not set",
{}
);
QUrl url(
QString::fromUtf8("%1/%2/%3").arg(
KOMPLEX_API_HOST,
KOMPLEX_API_VERSION,
m_uri
)
);
QNetworkRequest request(url);
request.setHeaders(
QHttpHeaders::fromMultiMap(
{
{
QByteArray("query"),
m_query.toUtf8()
},
{
QByteArray("offset"),
QString::number(offset).toUtf8()
},
{
QByteArray("limit"),
QString::number(limit).toUtf8()
},
{
QByteArray("session_token"),
CoreServices::sessionToken()
}
}
)
);
QNetworkReply *reply = getNetworkReply(request);
if(reply == nullptr)
{
return {};
}
QJsonDocument document = getDocument(reply);
QJsonObject rootObject = document.object();
const QJsonArray resultsArray = rootObject.value(QString::fromUtf8("photos")).toArray();
QList<ImageCache> fetchedResults;
for(const auto result : resultsArray)
{
if(!result.isObject())
{
continue;
}
QJsonObject resultObject = result.toObject();
const QVariantMap sources = resultObject.value(QString::fromUtf8("src")).toObject().toVariantMap();
QMap<QString, QString> sourceMap;
QMapIterator<QString,QVariant> sourceIterator(sources);
while(sourceIterator.hasNext())
{
sourceIterator.next();
sourceMap.insert(sourceIterator.key(), sourceIterator.value().toString());
}
fetchedResults.append(
{
resultObject.value(QString::fromUtf8("alt")).toString(),
resultObject.value(QString::fromUtf8("avg_color")).toString().toUtf8(),
resultObject.value(QString::fromUtf8("height")).toInteger(),
resultObject.value(QString::fromUtf8("width")).toInteger(),
resultObject.value(QString::fromUtf8("id")).toInteger(),
resultObject.value(QString::fromUtf8("liked")).toBool(),
resultObject.value(QString::fromUtf8("photographer")).toString(),
resultObject.value(QString::fromUtf8("photographer_url")).toString(),
resultObject.value(QString::fromUtf8("photographer_id")).toInteger(),
sourceMap,
resultObject.value(QString::fromUtf8("url")).toString()
}
);
}
FetchResult<ImageCache> result
{
static_cast<qsizetype>(rootObject.value(QString::fromUtf8("total_results")).toInteger()),
fetchedResults.count(),
std::move(fetchedResults)
};
return std::move(result);
}
/**
* @brief getNetworkReply
* Processes the request and blocks until completion.
* @param request
* @return
*/
[[nodiscard]]
auto getNetworkReply(const QNetworkRequest &request) const -> QNetworkReply*
{
QEventLoop loop;
QWeakPointer<QNetworkAccessManager> managerReference = CoreServices::networkAccessManager();
QSharedPointer<QNetworkAccessManager> manager = managerReference.toStrongRef();
LOG_ERROR_X(!manager,
"NewestPacksPaginator::getNetworkReply",
"Network manager reference has already been deleted",
nullptr
);
QNetworkReply *reply = manager->post(request, nullptr);
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
QObject::connect(reply, &QNetworkReply::errorOccurred, &loop,
[&loop](QNetworkReply::NetworkError error) -> void
{
loop.quit();
}
);
if(!reply->isFinished())
{
loop.exec();
}
LOG_ERROR_X(reply->error() != QNetworkReply::NoError,
"NewestPacksPaginator::getNetworkReply",
reply->errorString().toStdString().c_str(),
nullptr
);
return reply;
}
/**
* @brief getDocument
* Extracts the JSON document from the server reply
* @param reply
* @return
*/
[[nodiscard]]
auto getDocument(QNetworkReply *reply) const -> QJsonDocument
{
QByteArray data = reply->readAll();
QJsonParseError documentError;
QJsonDocument document = QJsonDocument::fromJson(data, &documentError);
LOG_ERROR_X(documentError.error != QJsonParseError::NoError,
"NewestPacksPaginator::getDocument",
documentError.errorString().toStdString().c_str(),
{}
);
return document;
}
/**
* @brief setUri
* Sets the API Endpoint URI to fetch from
* @param uri
*/
auto setUri(const QString &uri) -> void
{
if(uri == m_uri)
{
return;
}
m_uri = uri;
Q_EMIT uriChanged();
}
private:
/**
* @brief m_uri
* API Endpoint URI
*/
QString m_uri;
/**
* @brief m_query
* The query string used in fetch operations
*/
QString m_query;
};
#endif // IMAGEPAGINATOR_H
@@ -138,19 +138,19 @@ protected:
QHttpHeaders::fromMultiMap(
{
{
QByteArray("Query"),
QByteArray("query"),
m_query.toUtf8()
},
{
QByteArray("Offset"),
QByteArray("offset"),
QString::number(offset).toUtf8()
},
{
QByteArray("Limit"),
QByteArray("limit"),
QString::number(limit).toUtf8()
},
{
QByteArray("SessionToken"),
QByteArray("session_token"),
CoreServices::sessionToken()
}
}
@@ -266,8 +266,9 @@ protected:
[[nodiscard]]
auto getDocument(QNetworkReply *reply) const -> QJsonDocument
{
QByteArray data = reply->readAll();
QJsonParseError documentError;
QJsonDocument document = QJsonDocument::fromJson(reply->readAll(), &documentError);
QJsonDocument document = QJsonDocument::fromJson(data, &documentError);
LOG_ERROR_X(documentError.error != QJsonParseError::NoError,
"NewestPacksPaginator::getDocument",