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 }