import QtQuick import QtQuick.Controls import QtQuick.Dialogs import QtQuick.Layouts import com.github.digitalartifex.komplex as Komplex Item { property alias selectedFile: searchModel.lastSavedFile id: mainItem Komplex.CubemapSearchModel { id: searchModel } ColumnLayout { anchors.fill: parent RowLayout { Layout.fillHeight: false Layout.fillWidth: true Layout.margins: 6 TextField { Layout.preferredHeight: 32 Layout.fillWidth: true id: searchField placeholderText: "Search" onEditingFinished: mainItem.updateSearch() Keys.onPressed: (event) => { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { searchField.focus = false; // Unfocus the TextField event.accepted = true; // Prevent further propagation of the key event } } } Button { Layout.preferredHeight: 32 Layout.preferredWidth: 32 icon.name: "search-symbolic" onClicked: mainItem.updateSearch() } } Component { id: highlight Rectangle { width: view.cellWidth; height: view.cellHeight color: palette.highlight; 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 } } } } Rectangle { Layout.fillHeight: true Layout.fillWidth: true color: palette.base clip: true RowLayout { anchors.fill: parent GridView { // The standard size property int idealCellHeight: 300 property int idealCellWidth: 300 cellWidth: width / Math.floor(width / idealCellWidth) cellHeight: idealCellHeight id: view model: searchModel highlight: highlight highlightFollowsCurrentItem: false Layout.fillHeight: true Layout.fillWidth: true Layout.margins: 6 delegate: Column { id: entry leftPadding: Math.floor((width - thumbnailImage.width) / 2) topPadding: 10 rightPadding: Math.floor((width - thumbnailImage.width) / 2) bottomPadding: 10 width: view.cellWidth required property string id required property string name required property string description required property string thumbnail required property int index property int itemIndex: index Image { width: 280 height: 200 id: thumbnailImage source: parent.thumbnail//"https://api.artifex.services/v1/cubemaps/thumbnail/" + parent.id anchors.horizontalCenter: parent.horizontalCenter MouseArea { z: 9000 anchors.fill: parent onClicked: (mouse) => { view.currentIndex = entry.itemIndex } } Rectangle { color: palette.base anchors.fill: parent visible: thumbnailImage.status === Image.Loading RowLayout { anchors.fill: parent BusyIndicator { Layout.alignment: Qt.AlignCenter Layout.preferredHeight: 64 Layout.preferredWidth: 64 visible: running } } } Rectangle { color: palette.dark anchors.fill: parent visible: thumbnailImage.status === Image.Error Text { color: palette.text anchors.centerIn: parent text: qsTr("Error Loading Image") } } } Text { elide: Text.ElideRight topPadding: 4 bottomPadding: 2 text: "

" + name + "

" anchors.horizontalCenter: parent.horizontalCenter width: 280 color: palette.link font.bold: true } Text { leftPadding: 8 rightPadding: 8 text: qsTr(description) anchors.horizontalCenter: parent.horizontalCenter elide: Text.ElideRight width: 280 color: palette.text wrapMode: Text.WrapAtWordBoundaryOrAnywhere maximumLineCount: 2 font.italic: true } RowLayout { visible: parent.itemIndex === view.currentIndex width: 280 Button { Layout.topMargin: 4 Layout.alignment: Qt.AlignHCenter Layout.preferredHeight: 32 Layout.fillWidth: true text: "Download" icon.source: "./icons/download.svg" icon.name: "download-symbolic" onClicked: { progressDialog.id = entry.id progressDialog.description = entry.description progressDialog.name = entry.name progressDialog.thumbnail = entry.thumbnail progressDialog.open() searchModel.download(entry.id) } } } } populate: Transition { NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 1000 } } } } } RowLayout { Layout.margins: 6 Layout.fillWidth: true Button { text: "Previous" enabled: searchModel.previousPage !== "" onClicked: searchModel.back() } RowLayout { Layout.margins: 6 Layout.fillWidth: true Text { visible: searchModel.totalResults > 0 color: palette.text text: (searchModel.currentOffset + 1) + "-" + (searchModel.resultsPerPage + searchModel.currentOffset) + " of " + searchModel.totalResults } Text { color: palette.text Layout.fillWidth: true text: "Page " + (searchModel.currentOffset / searchModel.resultsPerPage) + " of " + Math.ceil(searchModel.totalResults / searchModel.resultsPerPage) } } Button { text: "Next" enabled: searchModel.nextPage !== "" onClicked: searchModel.next() } } } Rectangle { color: palette.base width: mainItem.width height: mainItem.height visible: searchModel.status === Komplex.PexelsImageSearchModel.Searching RowLayout { anchors.fill: parent BusyIndicator { Layout.alignment: Qt.AlignCenter Layout.preferredHeight: 128 Layout.preferredWidth: 128 visible: running } } } Dialog { property string name property string description property string id property string thumbnail modal: Qt.WindowModal width: 600 height: 420 anchors.centerIn: parent clip: true id: progressDialog ColumnLayout { anchors.fill: parent Image { Layout.fillHeight: true Layout.fillWidth: true fillMode: Image.PreserveAspectCrop source: progressDialog.thumbnail } Text { text: "Downloading Cubemap..." color: palette.text } ProgressBar { value: searchModel.downloadProgress Layout.fillWidth: true Layout.preferredHeight: 6 } } Connections { target: searchModel function onDownloadFinished() { progressDialog.close() } } } Dialog { width: 420 height: 105 id: warningDialog ColumnLayout { Text { id: header text: "Installation Error" font.pointSize: 14 color: palette.text } Text { id: informative text: searchModel.statusMessage font.pointSize: 10 color: palette.text } DialogButtonBox { Layout.alignment: Qt.AlignRight standardButtons: DialogButtonBox.Ok onAccepted: warningDialog.close() } } Connections { target: searchModel function onStatusChanged() { if(searchModel.status === Komplex.CubemapSearchModel.Error) { warningDialog.open(); progressDialog.close(); } } } } function updateSearch() { console.log(searchField.text) searchModel.query = searchField.text } }