From 6d32ed6bce058572dcae398d2b6aec4f7a471117 Mon Sep 17 00:00:00 2001 From: Digital Artifex <7929434+DigitalArtifex@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:02:17 -0400 Subject: [PATCH] Simplified view code --- KomplexHubContent/pages/HomePage.qml | 562 +++++++----------- KomplexHubContent/pages/ImageSearchPage.qml | 14 +- KomplexHubContent/pages/LiveSearchPage.qml | 18 +- KomplexHubContent/pages/VideoSearchPage.qml | 14 +- KomplexHubModule/Controls/CMakeLists.txt | 1 + .../Controls/HorizontalPaginator.qml | 169 ++++++ KomplexHubModule/Controls/PaginatorGrid.qml | 33 +- .../Controls/SearchResultItem.qml | 13 +- 8 files changed, 452 insertions(+), 372 deletions(-) create mode 100644 KomplexHubModule/Controls/HorizontalPaginator.qml diff --git a/KomplexHubContent/pages/HomePage.qml b/KomplexHubContent/pages/HomePage.qml index 2f45953..3f3fdec 100644 --- a/KomplexHubContent/pages/HomePage.qml +++ b/KomplexHubContent/pages/HomePage.qml @@ -8,66 +8,33 @@ import KomplexHub.Controls import KomplexHub.Kero import KomplexHubPlugin -Item { +Item +{ readonly property bool searchable: false property int resultsPerRow: (homePageRoot.width - (64 + Constants.largeMargin)) / (resultWidth + Constants.largeMargin) + 1 property int resultWidth: 256 - property int resultHeight: 256 + property int resultHeight: 245 + + clip: true id: homePageRoot NewestPacksModel { - id: newestPacksModel + id: packsModel resultsPerPage: homePageRoot.resultsPerRow - - onStateChanged: () => - { - if(state == NewestPacksModel.Loading) - { - console.log("Newest packs loading") - } - else if(state == NewestPacksModel.Idle) - { - console.log("Newest packs finished") - } - } } FeaturedImagesModel { - id: featuredImagesModel + id: imagesModel resultsPerPage: homePageRoot.resultsPerRow - - onStateChanged: () => - { - if(state == FeaturedImagesModel.Loading) - { - console.log("Featured Images loading") - } - else if(state == FeaturedImagesModel.Idle) - { - console.log("Featured Images finished") - } - } } FeaturedVideosModel { - id: featuredVideosModel + id: videosModel resultsPerPage: homePageRoot.resultsPerRow - - onStateChanged: () => - { - if(state == FeaturedVideosModel.Loading) - { - console.log("Featured Videos loading") - } - else if(state == FeaturedVideosModel.Idle) - { - console.log("Featured Videos finished") - } - } } Rectangle @@ -75,329 +42,174 @@ Item { anchors.fill: parent color: palette.base - ScrollView + ColumnLayout { - id: newPacksView + id: resultsLayout anchors.fill: parent - anchors.margins: Constants.largeMargin + anchors.margins: Constants.mediumMargin - ColumnLayout + Text { - width: newPacksView.width + Layout.alignment: Qt.AlignTop + Layout.preferredHeight: 50 - ColumnLayout + color: palette.text + font.pixelSize: Constants.h2Font.pixelSize + font.bold: true + text: qsTr("Newest Wallpaper Packs") + verticalAlignment: Qt.AlignVCenter + } + + HorizontalPaginator + { + id: packsPaginator + Layout.fillWidth: true + Layout.preferredHeight: 245 + Layout.alignment: Qt.AlignTop + + model: packsModel + delegate: ItemDelegate { - width: parent.width - height: 420 + width: resultWidth + height: resultHeight - Text + 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 { - color: palette.text - font.pixelSize: Constants.h2Font.pixelSize - font.bold: true - text: qsTr("Newest Wallpaper Packs") + anchors.fill: parent + title: parent.name + author: parent.author + description: parent.description + thumbnail: parent.thumbnail + uuid: parent.uuid + + selected: parent.highlighted } - RowLayout + MouseArea { - Layout.fillWidth: true - Layout.preferredHeight: 256 - - Rectangle - { - Layout.fillWidth: true - Layout.fillHeight: true - - color: "transparent" - - clip: true - Row - { - clip: true - spacing: Constants.mediumMargin - - anchors.top: parent.top - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.right: newPacksMoreButton.left - anchors.leftMargin: Constants.largeMargin - anchors.rightMargin: Constants.largeMargin - - Repeater - { - model: newestPacksModel - - delegate: Item { - 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 - - SearchResultItem - { - anchors.fill: parent - title: parent.name - author: parent.author - description: parent.description - thumbnail: parent.thumbnail - uuid: parent.uuid - } - } - } - } - - Rectangle - { - id: newestGradientMap - anchors.top: parent.top - anchors.right: newPacksMoreButton.left - height: resultHeight - width: resultWidth / 4 - - gradient: Gradient - { - orientation: Gradient.Horizontal - GradientStop { position: 0.0; color: "transparent" } - GradientStop { position: 1.0; color: palette.light } - } - opacity: 0.5 - } - - SquareButton - { - id: newPacksMoreButton - anchors.top: parent.top - anchors.right: parent.right - height: resultHeight - - width: 64 - - icon.source: "qrc:/images/icons/icons8-forward.svg" - icon.height: 24 - icon.width: 24 - } - } - } - } - - Item { - height: Constants.largeMargin - } - - ColumnLayout - { - width: parent.width - height: 420 - - Text - { - color: palette.text - font.pixelSize: Constants.h2Font.pixelSize - font.bold: true - text: qsTr("Featured Images") - } - - RowLayout - { - Layout.fillWidth: true - Layout.preferredHeight: 256 - - Rectangle - { - Layout.fillWidth: true - Layout.fillHeight: true - - color: "transparent" - - clip: true - Row - { - clip: true - spacing: Constants.mediumMargin - - anchors.top: parent.top - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.right: featuredImagesMoreButton.left - anchors.leftMargin: Constants.largeMargin - anchors.rightMargin: Constants.largeMargin - - Repeater - { - model: featuredImagesModel - - delegate: Item - { - width: resultWidth - height: resultHeight - - required property string author - required property string description - required property string uuid - required property string thumbnail - required property string authorId - - SearchResultItem - { - anchors.fill: parent - title: parent.author - description: parent.description - thumbnail: parent.thumbnail - uuid: parent.uuid - - pexels: true - } - } - } - } - - Rectangle - { - anchors.top: parent.top - anchors.right: featuredImagesMoreButton.left - height: resultHeight - width: resultWidth / 4 - - gradient: Gradient - { - orientation: Gradient.Horizontal - GradientStop { position: 0.0; color: "transparent" } - GradientStop { position: 1.0; color: palette.light } - } - opacity: 0.5 - } - - SquareButton - { - id: featuredImagesMoreButton - anchors.top: parent.top - anchors.right: parent.right - height: resultHeight - - width: 64 - - icon.source: "qrc:/images/icons/icons8-forward.svg" - icon.height: 24 - icon.width: 24 - } - } - } - } - - Item { - height: Constants.largeMargin - } - - ColumnLayout - { - width: parent.width - height: 420 - - Text - { - color: palette.text - font.pixelSize: Constants.h2Font.pixelSize - font.bold: true - text: qsTr("Featured Videos") - } - - RowLayout - { - Layout.fillWidth: true - Layout.preferredHeight: 256 - - Rectangle - { - Layout.fillWidth: true - Layout.fillHeight: true - - color: "transparent" - - clip: true - Row - { - clip: true - spacing: Constants.mediumMargin - - anchors.top: parent.top - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.right: featuredVideosMoreButton.left - anchors.leftMargin: Constants.largeMargin - anchors.rightMargin: Constants.largeMargin - - Repeater - { - model: featuredVideosModel - - delegate: Item { - width: resultWidth - height: resultHeight - - required property string uuid - required property string author - required property string authorId - required property string authorUrl - required property string thumbnail - - SearchResultItem - { - anchors.fill: parent - title: parent.author - thumbnail: parent.thumbnail - uuid: parent.uuid - pexels: true - } - } - } - } - - Rectangle - { - anchors.top: parent.top - anchors.right: featuredVideosMoreButton.left - height: resultHeight - width: resultWidth / 4 - - gradient: Gradient - { - orientation: Gradient.Horizontal - GradientStop { position: 0.0; color: "transparent" } - GradientStop { position: 1.0; color: palette.light } - } - opacity: 0.5 - } - - SquareButton - { - id: featuredVideosMoreButton - anchors.top: parent.top - anchors.right: parent.right - height: resultHeight - - width: 64 - - icon.source: "qrc:/images/icons/icons8-forward.svg" - icon.height: 24 - icon.width: 24 - } - } + anchors.fill: parent + onClicked: () => packsPaginator.currentIndex = index } } } + + Text + { + Layout.alignment: Qt.AlignTop + Layout.preferredHeight: 50 + + color: palette.text + font.pixelSize: Constants.h2Font.pixelSize + font.bold: true + text: qsTr("Featured Images") + verticalAlignment: Qt.AlignVCenter + } + + HorizontalPaginator + { + id: imagesPaginator + Layout.fillWidth: true + Layout.preferredHeight: 245 + Layout.alignment: Qt.AlignTop + + model: imagesModel + delegate: ItemDelegate + { + width: resultWidth + height: resultHeight + + required property string author + required property string description + required property string uuid + required property string thumbnail + required property string authorId + + SearchResultItem + { + anchors.fill: parent + title: parent.author + description: parent.description + thumbnail: parent.thumbnail + uuid: parent.uuid + pexels: true + selected: parent.highlighted + } + + MouseArea + { + anchors.fill: parent + onClicked: () => imagesPaginator.currentIndex = index + } + } + } + + Text + { + Layout.alignment: Qt.AlignTop + Layout.preferredHeight: 50 + + color: palette.text + font.pixelSize: Constants.h2Font.pixelSize + font.bold: true + text: qsTr("Popular Videos") + verticalAlignment: Qt.AlignVCenter + } + + HorizontalPaginator + { + id: videosPaginator + Layout.fillWidth: true + Layout.preferredHeight: 245 + Layout.alignment: Qt.AlignTop + + model: videosModel + delegate: ItemDelegate + { + width: resultWidth + height: resultHeight + + required property string uuid + required property string author + required property string authorId + required property string authorUrl + required property string thumbnail + + SearchResultItem + { + anchors.fill: parent + title: parent.author + thumbnail: parent.thumbnail + uuid: parent.uuid + pexels: true + selected: parent.highlighted + } + + MouseArea + { + anchors.fill: parent + onClicked: () => videosPaginator.currentIndex = index + } + } + } + + /* spacer */ + Item { Layout.fillHeight: true } } } Component.onCompleted: () => { - featuredImagesModel.nextPage() - featuredVideosModel.nextPage() - newestPacksModel.nextPage() + imagesModel.nextPage() + videosModel.nextPage() + packsModel.nextPage() } KeroLoadingAnimation @@ -405,26 +217,66 @@ Item { id: loadingAnimation anchors.fill: parent - OpacityAnimator on opacity { - duration: 150 - } - visible: opacity > 0 - opacity: 0 + opacity: 1 } states: [ State { name: "loading" - when: newestPacksModel.state == NewestPacksModel.Loading || - featuredImagesModel.state == FeaturedImagesModel.Loading || - featuredVideosModel.state == FeaturedVideosModel.Loading; + when: packsModel.state === NewestPacksModel.Loading || + imagesModel.state === FeaturedImagesModel.Loading || + videosModel.state === FeaturedVideosModel.Loading; - PropertyChanges { + PropertyChanges + { target: loadingAnimation opacity: 1 } - } + PropertyChanges + { + target: resultsLayout + opacity: 0 + } + }, + State { + name: "idle" + when: packsModel.state !== NewestPacksModel.Loading && + imagesModel.state !== FeaturedImagesModel.Loading && + videosModel.state !== FeaturedVideosModel.Loading; + + PropertyChanges + { + target: loadingAnimation + opacity: 0 + } + + PropertyChanges + { + target: resultsLayout + opacity: 1 + } + } + ] + + transitions: [ + Transition + { + from: "loading" + to: "idle" + NumberAnimation + { + target: loadingOverlay + duration: 250 + property: "opacity"; + } + NumberAnimation + { + target: resultsLayout + duration: 250 + property: "opacity"; + } + } ] } diff --git a/KomplexHubContent/pages/ImageSearchPage.qml b/KomplexHubContent/pages/ImageSearchPage.qml index 31aa640..0529277 100644 --- a/KomplexHubContent/pages/ImageSearchPage.qml +++ b/KomplexHubContent/pages/ImageSearchPage.qml @@ -28,15 +28,19 @@ Item model: searchModel loading: model.state === ImageSearchModel.Loading - delegate: Item + delegate: ItemDelegate { + id: resultDelegate + width: 256 height: 245 + 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 { @@ -45,6 +49,14 @@ Item description: parent.description thumbnail: parent.thumbnail uuid: parent.uuid + + selected: resultDelegate.highlighted + } + + MouseArea + { + anchors.fill: parent + onClicked: paginator.currentIndex = index } } } diff --git a/KomplexHubContent/pages/LiveSearchPage.qml b/KomplexHubContent/pages/LiveSearchPage.qml index 8b08a14..17a3949 100644 --- a/KomplexHubContent/pages/LiveSearchPage.qml +++ b/KomplexHubContent/pages/LiveSearchPage.qml @@ -12,7 +12,7 @@ Item property string query readonly property bool searchable: paginator.searchable - VideoSearchModel + PackSearchModel { id: searchModel query: rootItem.query @@ -24,12 +24,16 @@ Item id: paginator anchors.fill: parent model: searchModel - loading: model.state === VideoSearchModel.Loading + loading: model.state === PackSearchModel.Loading - delegate: Item + delegate: ItemDelegate { + id: resultDelegate + width: 256 height: 245 + + required property string name required property string author required property string description required property string uuid @@ -43,6 +47,14 @@ Item description: parent.description thumbnail: parent.thumbnail uuid: parent.uuid + + selected: resultDelegate.highlighted + } + + MouseArea + { + anchors.fill: parent + onClicked: paginator.currentIndex = index } } } diff --git a/KomplexHubContent/pages/VideoSearchPage.qml b/KomplexHubContent/pages/VideoSearchPage.qml index 56683b0..83ef5b7 100644 --- a/KomplexHubContent/pages/VideoSearchPage.qml +++ b/KomplexHubContent/pages/VideoSearchPage.qml @@ -28,15 +28,19 @@ Item model: searchModel loading: model.state === VideoSearchModel.Loading - delegate: Item + delegate: ItemDelegate { + id: resultDelegate + width: 256 height: 245 + 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 { @@ -45,6 +49,14 @@ Item description: parent.description thumbnail: parent.thumbnail uuid: parent.uuid + + selected: resultDelegate.highlighted + } + + MouseArea + { + anchors.fill: parent + onClicked: paginator.currentIndex = index } } } diff --git a/KomplexHubModule/Controls/CMakeLists.txt b/KomplexHubModule/Controls/CMakeLists.txt index ae31ee7..f0d08c0 100644 --- a/KomplexHubModule/Controls/CMakeLists.txt +++ b/KomplexHubModule/Controls/CMakeLists.txt @@ -16,5 +16,6 @@ qt6_add_qml_module(KomplexHubModule_Controls "SearchResultList.qml" QML_FILES PaginationBox.qml QML_FILES PaginatorGrid.qml + QML_FILES HorizontalPaginator.qml ) diff --git a/KomplexHubModule/Controls/HorizontalPaginator.qml b/KomplexHubModule/Controls/HorizontalPaginator.qml new file mode 100644 index 0000000..a1a51ee --- /dev/null +++ b/KomplexHubModule/Controls/HorizontalPaginator.qml @@ -0,0 +1,169 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import KomplexHub +import KomplexHub.Controls +import KomplexHub.Kero +import KomplexHubPlugin + +Item +{ + readonly property bool searchable: false + property int resultsPerRow: (resultsContainer.width - (Constants.largeMargin * 2)) / (resultWidth + Constants.mediumMargin) + property int rows: ((resultsContainer.height - (Constants.largeMargin * 2)) / (resultHeight + Constants.mediumMargin)) + property int resultsPerPage: resultsPerRow * rows + property int emptyWidth: (resultsContainer.width - (Constants.largeMargin * 2)) - (resultsPerRow * (resultWidth + Constants.mediumMargin)) + property int resultWidth: 256 + property int resultHeight: 245 + property bool loading: false + + property alias currentItem: resultsView.currentItem + property alias currentIndex: resultsView.currentIndex + + property var model + + property Component delegate + + id: rootItem + + Item + { + id: resultsContainer + anchors.fill: parent + + RowLayout + { + anchors.fill: parent + + Component + { + id: highlight + Rectangle + { + width: 256 + 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 } } + } + } + + ListView + { + id: resultsView + Layout.fillHeight: true + Layout.fillWidth: true + + spacing: Constants.mediumMargin + + orientation: ListView.Horizontal + model: rootItem.model + + delegate: rootItem.delegate + highlight: Rectangle { color: palette.accent; radius: 5 } + highlightFollowsCurrentItem: true + focus: true + + add: Transition + { + NumberAnimation + { + properties: "x" + from: resultsContainer.width + duration: 250 + } + NumberAnimation + { + properties: "opacity,scale" + to: 1.0 + duration: 500 + } + } + + move: Transition + { + NumberAnimation + { + properties: "x,y" + duration: 250 + } + } + + displaced: Transition + { + NumberAnimation + { + properties: "x,y" + duration: 250 + } + } + + populate: Transition + { + NumberAnimation + { + properties: "opacity,scale" + to: 1.0 + duration: 500 + } + } + + remove: Transition + { + NumberAnimation + { + properties: "x" + to: 0 + duration: 250 + } + NumberAnimation + { + properties: "opacity" + to: 0 + duration: 250 + } + } + } + + Item + { + Layout.fillHeight: true + Layout.preferredWidth: 128 + Rectangle + { + id: gradientMap + anchors.top: parent.top + anchors.right: moreButton.left + height: resultHeight + width: resultWidth / 4 + + gradient: Gradient + { + orientation: Gradient.Horizontal + GradientStop { position: 0.0; color: "transparent" } + GradientStop { position: 1.0; color: palette.light } + } + opacity: 0.5 + } + + SquareButton + { + id: moreButton + anchors.top: parent.top + anchors.right: parent.right + height: resultHeight + + width: 64 + + icon.source: "qrc:/images/icons/icons8-forward.svg" + icon.height: 24 + icon.width: 24 + } + } + } + } +} diff --git a/KomplexHubModule/Controls/PaginatorGrid.qml b/KomplexHubModule/Controls/PaginatorGrid.qml index ab00155..e5a434a 100644 --- a/KomplexHubModule/Controls/PaginatorGrid.qml +++ b/KomplexHubModule/Controls/PaginatorGrid.qml @@ -19,6 +19,9 @@ Item property int resultHeight: 245 property bool loading: false + property alias currentItem: resultsView.currentItem + property alias currentIndex: resultsView.currentIndex + property var model property Component delegate @@ -78,6 +81,22 @@ Item Layout.maximumWidth: emptyWidth / 2 } + Component + { + id: highlight + Rectangle + { + width: view.cellWidth + height: view.cellHeight + color: palette.accent + radius: 5 + x: view.currentItem.x + y: view.currentItem.y + Behavior on x { SpringAnimation { spring: 3; damping: 0.2 } } + Behavior on y { SpringAnimation { spring: 3; damping: 0.2 } } + } + } + GridView { id: resultsView @@ -86,10 +105,12 @@ Item cellWidth: 256 + Constants.mediumMargin cellHeight: 245 + Constants.mediumMargin - focus: true model: rootItem.model delegate: rootItem.delegate + highlight: Rectangle { color: palette.accent; radius: 5 } + highlightFollowsCurrentItem: true + focus: true add: Transition { @@ -116,6 +137,15 @@ Item } } + displaced: Transition + { + NumberAnimation + { + properties: "x,y" + duration: 250 + } + } + populate: Transition { NumberAnimation @@ -146,6 +176,7 @@ Item PaginationBox { + id: buttonBoxLayout height: 35 anchors.left: parent.left diff --git a/KomplexHubModule/Controls/SearchResultItem.qml b/KomplexHubModule/Controls/SearchResultItem.qml index 4238bd2..c1dfad4 100644 --- a/KomplexHubModule/Controls/SearchResultItem.qml +++ b/KomplexHubModule/Controls/SearchResultItem.qml @@ -119,7 +119,8 @@ Item { } } - MouseArea { + MouseArea + { property string lastState id: mouseArea @@ -155,16 +156,6 @@ Item { } } - onSelectedChanged: () => { - if(selected) { - searchResultItemRoot.triggered() - searchResultItemRoot.state = "selected" - } - else { - searchResultItemRoot.state = "" - } - } - states: [ State { name: "hovered"