Large update. Added Video, Image and Live item views. Added Live search

This commit is contained in:
Digital Artifex
2026-08-05 19:58:24 -04:00
parent 5b2500c841
commit e5627742d2
46 changed files with 3416 additions and 578 deletions
+3
View File
@@ -18,5 +18,8 @@ qt6_add_qml_module(KomplexHubModule_Controls
QML_FILES PaginatorGrid.qml
QML_FILES HorizontalPaginator.qml
QML_FILES ImageView.qml
QML_FILES
QML_FILES VideoView.qml
QML_FILES PackView.qml
)
+51 -43
View File
@@ -17,18 +17,13 @@ Item
property string thumbnail
property string portrait
property string landscape
property string small
property string original
property string medium
property string large
property string extraLarge
property string fullScreen
property string portraitSize
property string landscapeSize
property string smallSize
property string originalSize
property string mediumSize
property string largeSize
property string extraLargeSize
property string fullScreenSize
property string backgroundPortrait
property string backgroundLandscape
property int imageHeight
property int imageWidth
@@ -38,7 +33,27 @@ Item
Rectangle
{
anchors.fill: parent
color: palette.base.alpha(1)
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
{
@@ -50,20 +65,32 @@ Item
{
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop| Qt.AlignHCenter
Layout.topMargin: 32
/** Spacer **/
Item { Layout.fillWidth: true }
Image
Rectangle
{
width: imageWidth
height: imageHeight
transform: Image.PreserveAspectFit
source: rootItem.large
}
Layout.fillWidth: true
Layout.preferredHeight: width / 1.77777777778
Layout.maximumWidth: 800
Layout.alignment: Qt.AlignHCenter
color: palette.base.darker()
/** Spacer **/
Item { Layout.fillWidth: true }
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
@@ -91,7 +118,6 @@ Item
Layout.fillWidth: true
}
/** Spacer **/
Item
{
@@ -119,12 +145,9 @@ Item
{
id: downloadSelector
model:[
"Small: " + smallSize,
"Medium: " + mediumSize,
"Large: " + largeSize,
"Extra Large: " + extraLargeSize,
"Portrait: " + portraitSize,
"Landscape: " + landscapeSize
"Screen (" + fullScreenSize + ")",
"Portrait (" + portraitSize + ")",
"Landscape (" + landscapeSize + ")"
]
Layout.fillWidth: true
Layout.preferredHeight: 36
@@ -138,21 +161,6 @@ Item
}
}
// Text {
// property var texts: [
// small,
// medium,
// large,
// extraLarge,
// portrait,
// landscape
// ]
// text: texts[downloadSelector.currentIndex]
// elide: Text.ElideLeft
// Layout.fillWidth: true
// }
/** Spacer **/
Item { Layout.fillWidth: true; Layout.fillHeight: true }
+177
View File
@@ -0,0 +1,177 @@
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
}
}
}
}
}
+35 -12
View File
@@ -16,7 +16,7 @@ Item {
signal triggered
signal viewMoreTriggered
id: searchResultItemRoot
id: mainItem
width: 256
height: 256
clip: true
@@ -48,7 +48,7 @@ Item {
onReleased: () => {
if(!states.selected)
{
searchResultItemRoot.triggered()
mainItem.triggered()
}
}
}
@@ -68,12 +68,35 @@ Item {
color: palette.base.lighter(1.25)
Image
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
id: thumnailImage
source: searchResultItemRoot.thumbnail
onStatusChanged: () =>
{
// if(status === Image.Error)
// {
// if(retries <= 3)
// {
// ++retries
// let src = source
// source = ""
// source = src
// }
// }
}
}
Image
@@ -89,14 +112,14 @@ Item {
opacity: 0.5
source: "qrc:/images/icons/pexels-icon-filled-256.svg"
visible: searchResultItemRoot.pexels
visible: mainItem.pexels
}
}
Text
{
color: palette.text
text: searchResultItemRoot.title
text: mainItem.title
font.pixelSize: 16
font.bold: true
@@ -112,12 +135,12 @@ Item {
{
color: palette.text
font.pixelSize: 12
text: "By: " + searchResultItemRoot.author
text: "By: " + mainItem.author
leftPadding: 12
Layout.alignment: Qt.AlignLeft | Qt.AlignBottom
Layout.preferredWidth: 250
visible: !searchResultItemRoot.pexels
visible: !mainItem.pexels
}
Text
@@ -126,7 +149,7 @@ Item {
clip: true
color: palette.text
font.pixelSize: 12
text: searchResultItemRoot.description
text: mainItem.description
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
textFormat: Text.PlainText
elide: Text.ElideRight
@@ -153,7 +176,7 @@ Item {
opacity: 0
onTriggered: () => {
searchResultItemRoot.viewMoreTriggered()
mainItem.viewMoreTriggered()
}
}
@@ -166,7 +189,7 @@ Item {
id: states
property bool clicked: false
property bool hovered: false
property alias selected: searchResultItemRoot.selected
property alias selected: mainItem.selected
property bool idle: true
onIdleChanged: () =>
+1 -1
View File
@@ -25,7 +25,7 @@ Item {
component IconSettings: QtObject {
property int width: 16
property int height: 16
property string source: "images/icons/icons8-next.svg"
property string source: "qrc:/images/icons/icons8-next.svg"
}
component BorderSettings: QtObject {
+146 -113
View File
@@ -1,126 +1,159 @@
import QtQuick
import QtQuick.Shapes
import QtQuick.Effects
import QtQuick.Layouts
Item {
id: throbberRoot
Rectangle {
anchors.fill: parent
color: "transparent"
Item
onHeightChanged: () => {
if(height > width)
{
id: outerRing
anchors.fill: parent
Shape
{
anchors.fill: parent
asynchronous: true
ShapePath
{
property double progress: 0
property int dashSpaceLength: 15
property int dashLength: 1
id: outerRingPath
strokeColor: palette.accent.darker(1 + (progress - 0.25))
fillColor: "transparent"
strokeWidth: 3
strokeStyle: ShapePath.DashLine
startX: 0
startY: outerRing.height * 0.5
dashPattern: [
dashSpaceLength - (dashSpaceLength * progress),
dashSpaceLength * progress
]
PathArc
{
x: outerRing.width
y: outerRing.height * 0.5
radiusX: outerRing.width / 2
radiusY: outerRing.height / 2
useLargeArc: false
}
PathArc
{
x: 0
y: outerRing.height * 0.5
radiusX: outerRing.width / 2
radiusY: outerRing.height / 2
useLargeArc: false
}
}
}
transform: Rotation {
id: outerRingRotation
origin.x: outerRing.width / 2
origin.y: outerRing.height / 2
}
NumberAnimation {
property bool reverse: false
target: outerRingPath
property: "progress"
duration: 1600
//easing.type: Easing.InOutQuad
from: 0
to: 1
loops: 1
running: true
onFinished: () => {
if(reverse)
{
from = 0
to = 1
start()
}
else
{
from = 1
to = 0
start()
}
reverse = !reverse
}
}
NumberAnimation {
target: outerRingRotation
property: "angle"
duration: 16000
easing.type: Easing.InOutQuad
from: 0
to: (360 * 4)
loops: NumberAnimation.Infinite
running: true
}
layout.throbberSize = width - 24
}
else
{
layout.throbberSize = height - 24
}
}
MultiEffect {
source: outerRing
anchors.fill: outerRing
brightness: 0.4
saturation: 0.2
blurEnabled: true
blurMax: 64
blur: 1.0
onWidthChanged: () => {
if(height > width)
{
layout.throbberSize = width - 24
}
else
{
layout.throbberSize = height - 24
}
}
RowLayout
{
property int throbberSize
id: layout
anchors.fill: parent
Rectangle
{
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: parent.throbberSize
Layout.preferredWidth: parent.throbberSize
color: "transparent"
Item
{
id: outerRing
anchors.fill: parent
Shape
{
anchors.fill: parent
asynchronous: true
ShapePath
{
property double progress: 0
property int dashSpaceLength: 15
property int dashLength: 1
id: outerRingPath
strokeColor: palette.accent.darker(1 + (progress - 0.25))
fillColor: "transparent"
strokeWidth: 3
strokeStyle: ShapePath.DashLine
startX: 0
startY: outerRing.height * 0.5
dashPattern: [
dashSpaceLength - (dashSpaceLength * progress),
dashSpaceLength * progress
]
PathArc
{
x: outerRing.width
y: outerRing.height * 0.5
radiusX: outerRing.width / 2
radiusY: outerRing.height / 2
useLargeArc: false
}
PathArc
{
x: 0
y: outerRing.height * 0.5
radiusX: outerRing.width / 2
radiusY: outerRing.height / 2
useLargeArc: false
}
}
}
transform: Rotation {
id: outerRingRotation
origin.x: outerRing.width / 2
origin.y: outerRing.height / 2
}
NumberAnimation {
property bool reverse: false
target: outerRingPath
property: "progress"
duration: 1600
//easing.type: Easing.InOutQuad
from: 0
to: 1
loops: 1
running: true
onFinished: () => {
if(reverse)
{
from = 0
to = 1
start()
}
else
{
from = 1
to = 0
start()
}
reverse = !reverse
}
}
NumberAnimation {
target: outerRingRotation
property: "angle"
duration: 16000
easing.type: Easing.InOutQuad
from: 0
to: (360 * 4)
loops: NumberAnimation.Infinite
running: true
}
}
MultiEffect {
source: outerRing
anchors.fill: outerRing
brightness: 0.4
saturation: 0.2
blurEnabled: true
blurMax: 64
blur: 1.0
}
}
}
}
+191
View File
@@ -0,0 +1,191 @@
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
}
}
}
}
}