Warning, /multimedia/audiotube/src/contents/ui/SearchWithDropdown.qml is written in an unsupported language. File is not indexed.

0001 // SPDX-FileCopyrightText: 2023 Mathis BrĂ¼chert <mbb@kaidan.im>
0002 // SPDX-FileCopyrightText: 2021 Jonah BrĂ¼chert <jbb@kaidan.im>
0003 //
0004 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 
0006 import QtQuick 2.1
0007 import QtQuick.Controls 2.12 as Controls
0008 import QtQuick.Layouts 1.3
0009 import org.kde.kirigami 2.14 as Kirigami
0010 import org.kde.ytmusic 1.0
0011 
0012 Item {
0013     function forceFocus(){searchField.forceActiveFocus()}
0014     function accepted(){searchField.accepted()}
0015     property alias text: searchField.text
0016 
0017     id: root
0018     Controls.TextField{
0019         id: dummy
0020         x:searchField.x
0021         y:searchField.y
0022         width: searchField.width
0023         readOnly: true
0024     }
0025     Kirigami.SearchField {
0026         id: searchField
0027         autoAccept: false
0028         width: root.width
0029         selectByMouse: true
0030         onFocusChanged: {
0031             Library.searches.filter = text
0032             if (wideScreen && focus)
0033                 popup.open()
0034         }
0035 
0036         onTextEdited: {
0037             Library.searches.filter = text
0038             if(completionList.count === 0) {
0039                 //no matching search -> message should display
0040                 Library.temporarySearch = ""
0041             }
0042             else {
0043                 Library.temporarySearch = text
0044             }
0045         }
0046 
0047         Keys.onPressed: {
0048             if(completionList.count > 0) {
0049                 if (event.key === Qt.Key_Down) {
0050                     if(completionList.selectedDelegate === -1 || completionList.selectedDelegate === completionList.count - 1) {
0051                         completionList.selectedDelegate = 0
0052                         mainScrollView.contentItem.contentY = 0
0053                     }
0054                     else {
0055                         ++completionList.selectedDelegate
0056                         if(!completionList.isSelectedDelegateVisible()) {
0057                             if(!recentsRepeater.visible) {
0058                                 mainScrollView.contentItem.contentY = (completionList.selectedDelegate + 1) * completionList.empiricDelegateHeight - mainScrollView.height
0059                             }
0060                             else {
0061                                 mainScrollView.contentItem.contentY = (completionList.selectedDelegate + 1) * completionList.empiricDelegateHeight + recentsRepeater.height + mainScrollViewLayout.spacing - mainScrollView.height
0062                             }
0063                         }
0064                     }
0065                     event.accepted = true
0066                 }
0067                 else if(event.key === Qt.Key_Up) {
0068                     if(completionList.selectedDelegate === -1 || completionList.selectedDelegate === 0) {
0069                         completionList.selectedDelegate = completionList.count - 1
0070                         mainScrollView.contentItem.contentY = mainScrollView.contentHeight - mainScrollView.height
0071                     }
0072                     else {
0073                         --completionList.selectedDelegate
0074                         if(!completionList.isSelectedDelegateVisible()) {
0075                             if(!recentsRepeater.visible) {
0076                                 mainScrollView.contentItem.contentY = completionList.empiricDelegateHeight * completionList.selectedDelegate
0077                             }
0078                             else {
0079                                 mainScrollView.contentItem.contentY = completionList.empiricDelegateHeight * completionList.selectedDelegate + recentsRepeater.height + mainScrollViewLayout.spacing
0080                             }
0081                         }
0082                     }
0083                     event.accepted = true
0084                 }
0085                 if(event.accepted) {
0086                     text = completionList.itemAt(completionList.selectedDelegate).text
0087                 }
0088                 else {
0089                     completionList.selectedDelegate = -1
0090                     mainScrollView.contentItem.contentY = 0
0091                 }
0092             }
0093         }
0094 
0095         onAccepted: {
0096             popup.close()
0097             completionList.selectedDelegate = -1
0098             Library.temporarySearch = ""
0099 
0100             while (pageStack.depth > 0) {
0101                 pageStack.pop()
0102             }
0103 
0104             pageStack.layers.clear()
0105 
0106             if (text) {
0107                 Library.addSearch(text)
0108                 pageStack.push("qrc:/SearchPage.qml", {
0109                             "searchQuery": text,
0110                             objectName: "searchPage"
0111                                })
0112             } else {
0113                 wideScreen
0114                         ? pageStack.push("qrc:/LibraryPage.qml")
0115                         : pageStack.push("qrc:/SearchHistoryPage.qml")
0116             }
0117             searchField.focus = false
0118 
0119         }
0120     }
0121     Controls.Popup {
0122         id: popup
0123         property int expansion: 5
0124         property int shadowSize: 15
0125         onAboutToShow:{
0126             searchField.parent = fieldContainer
0127             onOpened: searchField.background.visible = false
0128             playOpenHeight.running = true
0129             playOpenWidth.running = true
0130             playOpenX.running = true
0131             playOpenY.running = true
0132             playOpenRadius.running = true
0133 
0134         }
0135         onAboutToHide:{
0136             searchField.parent = root
0137             onOpened: searchField.background.visible = true
0138             searchField.focus = false
0139             playCloseHeight.running = true
0140             playCloseWidth.running = true
0141             playCloseX.running = true
0142             playCloseY.running = true
0143             playCloseRadius.running = true
0144 
0145         }
0146 
0147         x: -(popup.shadowSize+popup.expansion)
0148         y: -(popup.shadowSize+popup.expansion)
0149 
0150         rightPadding:popup.shadowSize+1
0151         leftPadding:popup.shadowSize+1
0152         bottomPadding:popup.shadowSize
0153         rightInset: popup.shadowSize
0154         leftInset: popup.shadowSize
0155         bottomInset: popup.shadowSize
0156         leftMargin:-popup.shadowSize
0157 
0158         background: Kirigami.ShadowedRectangle{
0159             id: bg
0160             Kirigami.Theme.inherit: false
0161             Kirigami.Theme.colorSet: Kirigami.Theme.View
0162             color: Kirigami.Theme.backgroundColor
0163             radius: popup.expansion+2
0164             shadow.size: popup.shadowSize
0165             shadow.yOffset: popup.expansion
0166             shadow.color: Qt.rgba(0, 0, 0, 0.2)
0167 
0168             border.width: 1
0169             border.color: Kirigami.ColorUtils.linearInterpolation(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.3);
0170             NumberAnimation on radius{
0171                 id: playOpenRadius
0172                 easing.type: Easing.OutCubic
0173                 running: false
0174                 from: 2
0175                 duration: 100
0176                 to: popup.expansion+2
0177             }
0178             NumberAnimation on radius{
0179                 id: playCloseRadius
0180                 easing.type: Easing.OutCubic
0181                 running: false
0182                 from: popup.expansion+2
0183                 duration: 100
0184                 to: 2
0185             }
0186 
0187         }
0188         width: searchField.width + 2*(popup.shadowSize+popup.expansion)
0189         height: completionList
0190                 ? (Math.min(content.implicitHeight, Kirigami.Units.gridUnit * 20))+2*(popup.shadowSize+popup.expansion)
0191                 : (Kirigami.Units.gridUnit * 20)+2*(popup.shadowSize+popup.expansion)
0192 
0193         NumberAnimation on height{
0194             id: playOpenHeight
0195             easing.type: Easing.OutCubic
0196             running: false
0197             from: searchField.height
0198             duration: 200
0199             to: completionList
0200                 ? (Library.searches.filter && recentsRepeater.count > 0 //can't use recentsRepeater.visible directly, because it always returns false at this stage
0201                     ? (Math.min(fieldContainer.height+ (completionList.count) * (completionList.delegateHeight + completionList.delegatePadding) + Kirigami.Separator.implicitHeight + recentsRepeater.implicitHeight, Kirigami.Units.gridUnit * 20))+2*(popup.shadowSize+popup.expansion)
0202                     : (completionList.count === 0
0203                         ?(Math.min(fieldContainer.height+ noMatchingSearchLabel.height + 2*noMatchingSearchLabel.Layout.margins, Kirigami.Units.gridUnit * 20))+2*(popup.shadowSize+popup.expansion)
0204                         :(Math.min(fieldContainer.height+ (completionList.count) * (completionList.delegateHeight + 2.725*completionList.delegatePadding) + 2*mainScrollViewLayout.spacing, Kirigami.Units.gridUnit * 20))+2*(popup.shadowSize+popup.expansion)
0205                     )
0206                 )
0207                 : (Kirigami.Units.gridUnit * 20)+2*(popup.shadowSize+popup.expansion)
0208         }
0209         NumberAnimation on width{
0210             id: playOpenWidth
0211             easing.type: Easing.OutCubic
0212             running: false
0213             from: searchField.width
0214             duration: 100
0215             to: searchField.width +2*(popup.shadowSize+popup.expansion)
0216         }
0217         NumberAnimation on x{
0218             id: playOpenX
0219             easing.type: Easing.OutCubic
0220             running: false
0221             from: 0
0222             duration: 100
0223             to: -(popup.shadowSize+popup.expansion)
0224         }
0225         NumberAnimation on y{
0226             id: playOpenY
0227             easing.type: Easing.OutCubic
0228             running: false
0229             from: 0
0230             duration: 100
0231             to: -popup.expansion
0232         }
0233 
0234 
0235 
0236         NumberAnimation on height{
0237             id: playCloseHeight
0238             easing.type: Easing.OutCubic
0239             running: false
0240             from: completionList
0241                   ? (Math.min(content.implicitHeight, Kirigami.Units.gridUnit * 20))+2*(popup.shadowSize+popup.expansion)
0242                   : (Kirigami.Units.gridUnit * 20)+2*(popup.shadowSize+popup.expansion)
0243             duration: 100
0244             to: searchField.height
0245         }
0246         NumberAnimation on width{
0247             id: playCloseWidth
0248             easing.type: Easing.OutCubic
0249             running: false
0250             from: searchField.width + 2*(popup.shadowSize+popup.expansion)
0251             duration: 100
0252             to: searchField.width + 2*(popup.shadowSize)
0253         }
0254         NumberAnimation on x{
0255             id: playCloseX
0256             easing.type: Easing.OutCubic
0257             running: false
0258             from: -(popup.shadowSize+popup.expansion)
0259             duration: 100
0260             to: -popup.shadowSize
0261         }
0262         NumberAnimation on y{
0263             id: playCloseY
0264             easing.type: Easing.OutCubic
0265             running: false
0266             from: - popup.expansion
0267             duration: 100
0268             to: -0
0269         }
0270 
0271         property alias fieldContainer: fieldContainer
0272 
0273         contentItem: ColumnLayout{
0274             id: content
0275             width: parent.width
0276             Controls.Control{
0277                 Layout.fillHeight: true
0278                 Layout.fillWidth: true
0279                 Controls.Control{
0280                     x: popup.expansion
0281                     y:-popup.expansion
0282                     id: fieldContainer
0283                     height:searchField.height
0284                 }
0285                 implicitHeight: fieldContainer.height
0286             }
0287 
0288             Controls.ScrollView {
0289                 id: mainScrollView
0290                 implicitHeight: mainScrollViewLayout.implicitHeight
0291 
0292                 clip: true
0293 
0294                 Layout.fillHeight: true
0295                 Layout.fillWidth: true
0296 
0297                 Keys.enabled: false
0298                 contentWidth: availableWidth
0299 
0300                 ColumnLayout {
0301                     id: mainScrollViewLayout
0302 
0303                     width: mainScrollView.contentWidth
0304 
0305                     HorizontalCoverView {
0306                         id: recentsRepeater
0307                         Controls.ScrollBar.horizontal.policy: Controls.ScrollBar.AlwaysOff
0308 
0309                         contentHeight: 120
0310                         itemSpacing: 0
0311 
0312                         Layout.fillWidth: true
0313 
0314                         visible: Library.searches.filter && recentsRepeater.count > 0 //if changed, adjust playOpenHeight
0315 
0316                         model: LocalSearchModel {
0317                             searchQuery: Library.searches.filter
0318                         }
0319 
0320                         delegate: searchAlbum
0321                     }
0322                     Kirigami.Separator {
0323                         visible: recentsRepeater.visible
0324                         Layout.fillWidth: true
0325                         implicitWidth: popup.width
0326                     }
0327                     RowLayout{
0328                         id: noMatchingSearchLabel
0329 
0330                         Layout.margins: 10
0331                         visible: completionList.count == 0
0332                         Kirigami.Icon {
0333                             source: "documentinfo"
0334                             implicitHeight: Kirigami.Units.gridUnit
0335                             implicitWidth: Kirigami.Units.gridUnit
0336                             color: Kirigami.Theme.disabledTextColor
0337                         }
0338                         Controls.Label {
0339                             text: i18n("No matching previous searches")
0340                             Layout.fillWidth: true
0341                             color: Kirigami.Theme.disabledTextColor
0342 
0343                         }
0344                     }
0345                     Repeater {
0346                         Layout.fillWidth: true
0347 
0348                         property int selectedDelegate: -1
0349                         property int delegateHeight: Kirigami.Units.gridUnit
0350                         property int delegatePadding: Kirigami.Units.smallSpacing
0351                         property int empiricDelegateHeight: recentsRepeater.visible ? (mainScrollView.contentHeight - recentsRepeater.height) / count : (mainScrollView.contentHeight + mainScrollViewLayout.spacing) / (count)
0352 
0353                         id: completionList
0354                         model: Library.searches
0355 
0356                         function isSelectedDelegateVisible() {
0357                             if(!recentsRepeater.visible) {
0358                                 return selectedDelegate * empiricDelegateHeight > mainScrollView.contentItem.contentY && (selectedDelegate + 1) * empiricDelegateHeight < mainScrollView.contentItem.contentY + mainScrollView.height
0359                             }
0360                             else {
0361                                     return selectedDelegate * empiricDelegateHeight + recentsRepeater.height > mainScrollView.contentItem.contentY && (selectedDelegate + 1) * empiricDelegateHeight + recentsRepeater.height < mainScrollView.contentItem.contentY + mainScrollView.height
0362                             }
0363                         }
0364 
0365                         delegate: Controls.ItemDelegate {
0366                             required property string searchQuery
0367                             required property int index
0368 
0369                             id: completionDelegate
0370                             highlighted: focus || (completionList.selectedDelegate == index)
0371                             Kirigami.Theme.colorSet: Kirigami.Theme.View
0372                             Kirigami.Theme.inherit: false
0373                             Layout.fillWidth: true
0374                             height: completionList.delegateHeight
0375                             text: searchQuery
0376                             padding: completionList.delegatePadding
0377                             spacing: 0
0378 
0379                             contentItem: RowLayout {
0380                                 Kirigami.Icon {
0381                                     source: "search"
0382                                     implicitHeight: completionList.delegateHeight
0383                                     implicitWidth: completionList.delegateHeight
0384                                     color: Kirigami.Theme.disabledTextColor
0385                                 }
0386                                 Controls.Label{
0387                                     text: searchQuery
0388                                     Layout.fillWidth: true
0389                                     elide: Text.ElideRight
0390                                 }
0391                                 Controls.ToolButton {
0392                                     icon.name: "list-remove"
0393                                     text: i18n("remove from search history")
0394                                     display: Controls.AbstractButton.IconOnly
0395                                     onClicked: {
0396                                         Library.removeSearch(completionDelegate.searchQuery)
0397                                     }
0398                                     implicitHeight: completionList.delegateHeight
0399                                     visible: Library.temporarySearch == "" || index != 0
0400                                 }
0401 
0402                             }
0403                             onClicked: {
0404                                 searchField.text = searchQuery
0405                                 searchField.accepted()
0406                             }
0407                         }
0408                     }
0409                 }
0410             }
0411         }
0412     }
0413 }