Warning, /plasma/plasma-desktop/applets/kickoff/package/contents/ui/KickoffListView.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 SPDX-FileCopyrightText: 2011 Martin Gräßlin <mgraesslin@kde.org> 0003 SPDX-FileCopyrightText: 2012 Gregor Taetzner <gregor@freenet.de> 0004 SPDX-FileCopyrightText: 2015-2018 Eike Hein <hein@kde.org> 0005 SPDX-FileCopyrightText: 2021 Mikel Johnson <mikel5764@gmail.com> 0006 SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 import QtQuick 2.15 0011 import QtQml 2.15 0012 0013 import org.kde.plasma.plasmoid 2.0 0014 import org.kde.plasma.components 3.0 as PC3 0015 import org.kde.plasma.extras as PlasmaExtras 0016 0017 import org.kde.ksvg 1.0 as KSvg 0018 import org.kde.kirigami 2.20 as Kirigami 0019 0020 // ScrollView makes it difficult to control implicit size using the contentItem. 0021 // Using EmptyPage instead. 0022 EmptyPage { 0023 id: root 0024 property alias model: view.model 0025 property alias count: view.count 0026 property alias currentIndex: view.currentIndex 0027 property alias currentItem: view.currentItem 0028 property alias delegate: view.delegate 0029 property alias section: view.section 0030 property alias highlight: view.highlight 0031 property alias view: view 0032 0033 property bool mainContentView: false 0034 property bool hasSectionView: false 0035 0036 /** 0037 * Request showing the section view 0038 */ 0039 signal showSectionViewRequested(string sectionName) 0040 0041 clip: view.height < view.contentHeight 0042 0043 header: MouseArea { 0044 implicitHeight: KickoffSingleton.listItemMetrics.margins.top 0045 hoverEnabled: true 0046 onEntered: { 0047 if (containsMouse) { 0048 let targetIndex = view.indexAt(mouseX + view.contentX, view.contentY) 0049 if (targetIndex >= 0) { 0050 view.currentIndex = targetIndex 0051 view.forceActiveFocus(Qt.MouseFocusReason) 0052 } 0053 } 0054 } 0055 } 0056 0057 footer: MouseArea { 0058 implicitHeight: KickoffSingleton.listItemMetrics.margins.bottom 0059 hoverEnabled: true 0060 onEntered: { 0061 if (containsMouse) { 0062 let targetIndex = view.indexAt(mouseX + view.contentX, view.height + view.contentY - 1) 0063 if (targetIndex >= 0) { 0064 view.currentIndex = targetIndex 0065 view.forceActiveFocus(Qt.MouseFocusReason) 0066 } 0067 } 0068 } 0069 } 0070 0071 implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 0072 contentWidth, // exclude padding to avoid scrollbars automatically affecting implicitWidth 0073 implicitHeaderWidth2, 0074 implicitFooterWidth2) 0075 0076 leftPadding: verticalScrollBar.visible && root.mirrored ? verticalScrollBar.implicitWidth : 0 0077 rightPadding: verticalScrollBar.visible && !root.mirrored ? verticalScrollBar.implicitWidth : 0 0078 0079 contentItem: ListView { 0080 id: view 0081 0082 readonly property real availableWidth: width - leftMargin - rightMargin 0083 readonly property real availableHeight: height - topMargin - bottomMargin 0084 property bool movedWithKeyboard: false 0085 property bool movedWithWheel: false 0086 0087 Accessible.role: Accessible.List 0088 0089 implicitWidth: { 0090 let totalMargins = leftMargin + rightMargin 0091 if (mainContentView) { 0092 if (kickoff.mayHaveGridWithScrollBar) { 0093 totalMargins += verticalScrollBar.implicitWidth 0094 } 0095 return KickoffSingleton.gridCellSize * kickoff.minimumGridRowCount + totalMargins 0096 } 0097 return contentWidth + totalMargins 0098 } 0099 implicitHeight: { 0100 // use grid cells to determine size 0101 let h = KickoffSingleton.gridCellSize * kickoff.minimumGridRowCount 0102 // If no grids are used, use the number of items that would fit in the grid height 0103 if (Plasmoid.configuration.favoritesDisplay !== 0 && Plasmoid.configuration.applicationsDisplay !== 0) { 0104 h = Math.floor(h / kickoff.listDelegateHeight) * kickoff.listDelegateHeight 0105 } 0106 return h + topMargin + bottomMargin 0107 } 0108 0109 leftMargin: kickoff.backgroundMetrics.leftPadding 0110 rightMargin: kickoff.backgroundMetrics.rightPadding 0111 0112 currentIndex: count > 0 ? 0 : -1 0113 focus: true 0114 interactive: height < contentHeight 0115 pixelAligned: true 0116 reuseItems: true 0117 boundsBehavior: Flickable.StopAtBounds 0118 // default keyboard navigation doesn't allow focus reasons to be used 0119 // and eats up/down key events when at the beginning or end of the list. 0120 keyNavigationEnabled: false 0121 keyNavigationWraps: false 0122 0123 // This is actually needed. The highlight will animate from thin to wide otherwise. 0124 highlightResizeDuration: 0 0125 highlightMoveDuration: 0 0126 highlight: PlasmaExtras.Highlight { 0127 // The default Z value for delegates is 1. The default Z value for the section delegate is 2. 0128 // The highlight gets a value of 3 while the drag is active and then goes back to the default value of 0. 0129 z: root.currentItem && root.currentItem.Drag.active ? 0130 3 : 0 0131 pressed: view.currentItem && view.currentItem.isPressed && !view.currentItem.isCategoryListItem 0132 active: view.activeFocus 0133 || (kickoff.contentArea === root 0134 && kickoff.searchField.activeFocus) 0135 } 0136 0137 delegate: KickoffListDelegate { 0138 width: view.availableWidth 0139 } 0140 0141 section { 0142 property: "group" 0143 criteria: ViewSection.FullString 0144 delegate: PC3.AbstractButton { 0145 width: view.availableWidth 0146 height: KickoffSingleton.compactListDelegateHeight 0147 0148 PC3.Label { 0149 id: contentLabel 0150 anchors.left: parent.left 0151 width: section.length === 1 0152 ? KickoffSingleton.compactListDelegateContentHeight + leftPadding + rightPadding 0153 : parent.width 0154 height: parent.height 0155 leftPadding: view.effectiveLayoutDirection === Qt.LeftToRight 0156 ? KickoffSingleton.listItemMetrics.margins.left : 0 0157 rightPadding: view.effectiveLayoutDirection === Qt.RightToLeft 0158 ? KickoffSingleton.listItemMetrics.margins.right : 0 0159 horizontalAlignment: section.length === 1 ? Text.AlignHCenter : Text.AlignLeft 0160 verticalAlignment: Text.AlignVCenter 0161 maximumLineCount: 1 0162 elide: Text.ElideRight 0163 font.pixelSize: KickoffSingleton.compactListDelegateContentHeight 0164 enabled: hoverHandler.hovered 0165 text: section.length === 1 ? section.toUpperCase() : section 0166 textFormat: Text.PlainText 0167 } 0168 0169 HoverHandler { 0170 id: hoverHandler 0171 enabled: root.hasSectionView 0172 cursorShape: enabled ? Qt.PointingHandCursor : undefined 0173 } 0174 0175 onClicked: root.showSectionViewRequested(contentLabel.text) 0176 } 0177 } 0178 0179 move: normalTransition 0180 moveDisplaced: normalTransition 0181 0182 Transition { 0183 id: normalTransition 0184 NumberAnimation { 0185 duration: Kirigami.Units.shortDuration 0186 properties: "x, y" 0187 easing.type: Easing.OutCubic 0188 } 0189 } 0190 0191 PC3.ScrollBar.vertical: PC3.ScrollBar { 0192 id: verticalScrollBar 0193 parent: root 0194 z: 2 0195 height: root.height 0196 anchors.right: parent.right 0197 } 0198 0199 Kirigami.WheelHandler { 0200 target: view 0201 filterMouseEvents: true 0202 // `20 * Qt.styleHints.wheelScrollLines` is the default speed. 0203 horizontalStepSize: 20 * Qt.styleHints.wheelScrollLines 0204 verticalStepSize: 20 * Qt.styleHints.wheelScrollLines 0205 0206 onWheel: wheel => { 0207 view.movedWithWheel = true 0208 view.movedWithKeyboard = false 0209 movedWithWheelTimer.restart() 0210 } 0211 } 0212 0213 Connections { 0214 target: kickoff 0215 function onExpandedChanged() { 0216 if (kickoff.expanded) { 0217 view.currentIndex = 0 0218 view.positionViewAtBeginning() 0219 } 0220 } 0221 } 0222 0223 // Used to block hover events temporarily after using keyboard navigation. 0224 // If you have one hand on the touch pad or mouse and another hand on the keyboard, 0225 // it's easy to accidentally reset the highlight/focus position to the mouse position. 0226 Timer { 0227 id: movedWithKeyboardTimer 0228 interval: 200 0229 onTriggered: view.movedWithKeyboard = false 0230 } 0231 0232 Timer { 0233 id: movedWithWheelTimer 0234 interval: 200 0235 onTriggered: view.movedWithWheel = false 0236 } 0237 0238 function focusCurrentItem(event, focusReason) { 0239 currentItem.forceActiveFocus(focusReason) 0240 event.accepted = true 0241 } 0242 0243 Keys.onMenuPressed: event => { 0244 if (currentItem !== null) { 0245 currentItem.forceActiveFocus(Qt.ShortcutFocusReason) 0246 currentItem.openActionMenu() 0247 } 0248 } 0249 Keys.onPressed: event => { 0250 let targetX = currentItem ? currentItem.x : contentX 0251 let targetY = currentItem ? currentItem.y : contentY 0252 let targetIndex = currentIndex 0253 let atFirst = currentIndex === 0 0254 let atLast = currentIndex === count - 1 0255 if (count > 1) { 0256 switch (event.key) { 0257 case Qt.Key_Up: if (!atFirst) { 0258 decrementCurrentIndex() 0259 0260 if (currentItem.isSeparator) { 0261 decrementCurrentIndex() 0262 } 0263 0264 focusCurrentItem(event, Qt.BacktabFocusReason) 0265 } break 0266 case Qt.Key_K: if (!atFirst && event.modifiers & Qt.ControlModifier) { 0267 decrementCurrentIndex() 0268 focusCurrentItem(event, Qt.BacktabFocusReason) 0269 } break 0270 case Qt.Key_Down: if (!atLast) { 0271 incrementCurrentIndex() 0272 0273 if (currentItem.isSeparator) { 0274 incrementCurrentIndex() 0275 } 0276 0277 focusCurrentItem(event, Qt.TabFocusReason) 0278 } break 0279 case Qt.Key_J: if (!atLast && event.modifiers & Qt.ControlModifier) { 0280 incrementCurrentIndex() 0281 focusCurrentItem(event, Qt.TabFocusReason) 0282 } break 0283 case Qt.Key_Home: if (!atFirst) { 0284 currentIndex = 0 0285 focusCurrentItem(event, Qt.BacktabFocusReason) 0286 } break 0287 case Qt.Key_End: if (!atLast) { 0288 currentIndex = count - 1 0289 focusCurrentItem(event, Qt.TabFocusReason) 0290 } break 0291 case Qt.Key_PageUp: if (!atFirst) { 0292 targetY = targetY - height + 1 0293 targetIndex = indexAt(targetX, targetY) 0294 // TODO: Find a more efficient, but accurate way to do this 0295 while (targetIndex === -1) { 0296 targetY += 1 0297 targetIndex = indexAt(targetX, targetY) 0298 } 0299 currentIndex = Math.max(targetIndex, 0) 0300 focusCurrentItem(event, Qt.BacktabFocusReason) 0301 } break 0302 case Qt.Key_PageDown: if (!atLast) { 0303 targetY = targetY + height - 1 0304 targetIndex = indexAt(targetX, targetY) 0305 // TODO: Find a more efficient, but accurate way to do this 0306 while (targetIndex === -1) { 0307 targetY -= 1 0308 targetIndex = indexAt(targetX, targetY) 0309 } 0310 currentIndex = Math.min(targetIndex, count - 1) 0311 focusCurrentItem(event, Qt.TabFocusReason) 0312 } break 0313 } 0314 } 0315 movedWithKeyboard = event.accepted 0316 if (movedWithKeyboard) { 0317 movedWithKeyboardTimer.restart() 0318 } 0319 } 0320 } 0321 }