Warning, /plasma/plasma-desktop/applets/kicker/package/contents/ui/ItemGridView.qml is written in an unsupported language. File is not indexed.
0001 /*
0002 SPDX-FileCopyrightText: 2015 Eike Hein <hein@kde.org>
0003
0004 SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006
0007 import QtQuick 2.15
0008
0009 import org.kde.kquickcontrolsaddons 2.0
0010 import org.kde.ksvg 1.0 as KSvg
0011 import org.kde.plasma.components as PlasmaComponents
0012 import org.kde.plasma.extras 2.0 as PlasmaExtras
0013 import org.kde.kirigami 2.20 as Kirigami
0014
0015 FocusScope {
0016 id: itemGrid
0017
0018 signal keyNavLeft
0019 signal keyNavRight
0020 signal keyNavUp
0021 signal keyNavDown
0022
0023 signal itemActivated(int index, string actionId, string argument)
0024
0025 property bool dragEnabled: true
0026 property bool dropEnabled: false
0027 property bool showLabels: true
0028
0029 property alias currentIndex: gridView.currentIndex
0030 property alias currentItem: gridView.currentItem
0031 property alias contentItem: gridView.contentItem
0032 property alias count: gridView.count
0033 property alias model: gridView.model
0034
0035 property alias cellWidth: gridView.cellWidth
0036 property alias cellHeight: gridView.cellHeight
0037 property alias iconSize: gridView.iconSize
0038
0039 property var horizontalScrollBarPolicy: PlasmaComponents.ScrollBar.AlwaysOff
0040 property var verticalScrollBarPolicy: PlasmaComponents.ScrollBar.AsNeeded
0041
0042 onDropEnabledChanged: {
0043 if (!dropEnabled && "dropPlaceHolderIndex" in model) {
0044 model.dropPlaceHolderIndex = -1;
0045 }
0046 }
0047
0048 onFocusChanged: {
0049 if (!focus && !root.keyEventProxy.activeFocus) {
0050 currentIndex = -1;
0051 }
0052 }
0053
0054 function currentRow() {
0055 if (currentIndex === -1) {
0056 return -1;
0057 }
0058
0059 return Math.floor(currentIndex / Math.floor(width / itemGrid.cellWidth));
0060 }
0061
0062 function currentCol() {
0063 if (currentIndex === -1) {
0064 return -1;
0065 }
0066
0067 return currentIndex - (currentRow() * Math.floor(width / itemGrid.cellWidth));
0068 }
0069
0070 function lastRow() {
0071 var columns = Math.floor(width / itemGrid.cellWidth);
0072 return Math.ceil(count / columns) - 1;
0073 }
0074
0075 function tryActivate(row, col) {
0076 if (count) {
0077 var columns = Math.floor(width / itemGrid.cellWidth);
0078 var rows = Math.ceil(count / columns);
0079 row = Math.min(row, rows - 1);
0080 col = Math.min(col, columns - 1);
0081 currentIndex = Math.min(row ? ((Math.max(1, row) * columns) + col)
0082 : col,
0083 count - 1);
0084
0085 focus = true;
0086 }
0087 }
0088
0089 function forceLayout() {
0090 gridView.forceLayout();
0091 }
0092
0093 ActionMenu {
0094 id: actionMenu
0095
0096 onActionClicked: {
0097 visualParent.actionTriggered(actionId, actionArgument);
0098 }
0099 }
0100
0101 DropArea {
0102 id: dropArea
0103
0104 anchors.fill: parent
0105
0106 onPositionChanged: event => {
0107 if (!itemGrid.dropEnabled || gridView.animating || !kicker.dragSource) {
0108 return;
0109 }
0110
0111 var x = Math.max(0, event.x - (width % itemGrid.cellWidth));
0112 var cPos = mapToItem(gridView.contentItem, x, event.y);
0113 var item = gridView.itemAt(cPos.x, cPos.y);
0114
0115 if (item) {
0116 if (kicker.dragSource.parent === gridView.contentItem) {
0117 if (item !== kicker.dragSource) {
0118 item.GridView.view.model.moveRow(dragSource.itemIndex, item.itemIndex);
0119 }
0120 } else if (kicker.dragSource.GridView.view.model.favoritesModel === itemGrid.model
0121 && !itemGrid.model.isFavorite(kicker.dragSource.favoriteId)) {
0122 var hasPlaceholder = (itemGrid.model.dropPlaceholderIndex !== -1);
0123
0124 itemGrid.model.dropPlaceholderIndex = item.itemIndex;
0125
0126 if (!hasPlaceholder) {
0127 gridView.currentIndex = (item.itemIndex - 1);
0128 }
0129 }
0130 } else if (kicker.dragSource.parent !== gridView.contentItem
0131 && kicker.dragSource.GridView.view.model.favoritesModel === itemGrid.model
0132 && !itemGrid.model.isFavorite(kicker.dragSource.favoriteId)) {
0133 var hasPlaceholder = (itemGrid.model.dropPlaceholderIndex !== -1);
0134
0135 itemGrid.model.dropPlaceholderIndex = hasPlaceholder ? itemGrid.model.count - 1 : itemGrid.model.count;
0136
0137 if (!hasPlaceholder) {
0138 gridView.currentIndex = (itemGrid.model.count - 1);
0139 }
0140 } else {
0141 itemGrid.model.dropPlaceholderIndex = -1;
0142 gridView.currentIndex = -1;
0143 }
0144 }
0145
0146 onExited: {
0147 if ("dropPlaceholderIndex" in itemGrid.model) {
0148 itemGrid.model.dropPlaceholderIndex = -1;
0149 gridView.currentIndex = -1;
0150 }
0151 }
0152
0153 onDropped: {
0154 if (kicker.dragSource && kicker.dragSource.parent !== gridView.contentItem && kicker.dragSource.GridView.view.model.favoritesModel === itemGrid.model) {
0155 itemGrid.model.addFavorite(kicker.dragSource.favoriteId, itemGrid.model.dropPlaceholderIndex);
0156 gridView.currentIndex = -1;
0157 }
0158 }
0159
0160 Timer {
0161 id: resetAnimationDurationTimer
0162
0163 interval: 120
0164 repeat: false
0165
0166 onTriggered: {
0167 gridView.animationDuration = interval - 20;
0168 }
0169 }
0170
0171 PlasmaComponents.ScrollView {
0172 id: scrollArea
0173
0174 anchors.fill: parent
0175
0176 focus: true
0177
0178 PlasmaComponents.ScrollBar.horizontal.policy: itemGrid.horizontalScrollBarPolicy
0179
0180 GridView {
0181 id: gridView
0182
0183 signal itemContainsMouseChanged(bool containsMouse)
0184
0185 property int iconSize: Kirigami.Units.iconSizes.huge
0186
0187 property bool animating: false
0188 property int animationDuration: itemGrid.dropEnabled ? resetAnimationDurationTimer.interval : 0
0189
0190 focus: true
0191
0192 currentIndex: -1
0193
0194 move: Transition {
0195 enabled: itemGrid.dropEnabled
0196
0197 SequentialAnimation {
0198 PropertyAction { target: gridView; property: "animating"; value: true }
0199
0200 NumberAnimation {
0201 duration: gridView.animationDuration
0202 properties: "x, y"
0203 easing.type: Easing.OutQuad
0204 }
0205
0206 PropertyAction { target: gridView; property: "animating"; value: false }
0207 }
0208 }
0209
0210 moveDisplaced: Transition {
0211 enabled: itemGrid.dropEnabled
0212
0213 SequentialAnimation {
0214 PropertyAction { target: gridView; property: "animating"; value: true }
0215
0216 NumberAnimation {
0217 duration: gridView.animationDuration
0218 properties: "x, y"
0219 easing.type: Easing.OutQuad
0220 }
0221
0222 PropertyAction { target: gridView; property: "animating"; value: false }
0223 }
0224 }
0225
0226 keyNavigationWraps: false
0227 boundsBehavior: Flickable.StopAtBounds
0228
0229 delegate: ItemGridDelegate {
0230 showLabel: itemGrid.showLabels
0231 }
0232
0233 highlight: Item {
0234 property bool isDropPlaceHolder: "dropPlaceholderIndex" in itemGrid.model && itemGrid.currentIndex === itemGrid.model.dropPlaceholderIndex
0235
0236 PlasmaExtras.Highlight {
0237 visible: gridView.currentItem && !isDropPlaceHolder
0238 hovered: true
0239 pressed: hoverArea.pressed
0240
0241 anchors.fill: parent
0242 }
0243
0244 KSvg.FrameSvgItem {
0245 visible: gridView.currentItem && isDropPlaceHolder
0246
0247 anchors.fill: parent
0248
0249 imagePath: "widgets/viewitem"
0250 prefix: "selected"
0251
0252 opacity: 0.5
0253
0254 Kirigami.Icon {
0255 anchors {
0256 right: parent.right
0257 rightMargin: parent.margins.right
0258 bottom: parent.bottom
0259 bottomMargin: parent.margins.bottom
0260 }
0261
0262 width: Kirigami.Units.iconSizes.smallMedium
0263 height: width
0264
0265 source: "list-add"
0266 active: false
0267 }
0268 }
0269 }
0270
0271 highlightFollowsCurrentItem: true
0272 highlightMoveDuration: 0
0273
0274 onCurrentIndexChanged: {
0275 if (currentIndex !== -1) {
0276 hoverArea.hoverEnabled = false
0277 focus = true;
0278 }
0279 }
0280
0281 onCountChanged: {
0282 animationDuration = 0;
0283 resetAnimationDurationTimer.start();
0284 }
0285
0286 onModelChanged: {
0287 currentIndex = -1;
0288 }
0289
0290 Keys.onLeftPressed: event => {
0291 if (itemGrid.currentCol() !== 0) {
0292 event.accepted = true;
0293 moveCurrentIndexLeft();
0294 } else {
0295 itemGrid.keyNavLeft();
0296 }
0297 }
0298
0299 Keys.onRightPressed: event => {
0300 var columns = Math.floor(width / cellWidth);
0301
0302 if (itemGrid.currentCol() !== columns - 1 && currentIndex !== count -1) {
0303 event.accepted = true;
0304 moveCurrentIndexRight();
0305 } else {
0306 itemGrid.keyNavRight();
0307 }
0308 }
0309
0310 Keys.onUpPressed: event => {
0311 if (itemGrid.currentRow() !== 0) {
0312 event.accepted = true;
0313 moveCurrentIndexUp();
0314 positionViewAtIndex(currentIndex, GridView.Contain);
0315 } else {
0316 itemGrid.keyNavUp();
0317 }
0318 }
0319
0320 Keys.onDownPressed: event => {
0321 if (itemGrid.currentRow() < itemGrid.lastRow()) {
0322 // Fix moveCurrentIndexDown()'s lack of proper spatial nav down
0323 // into partial columns.
0324 event.accepted = true;
0325 var columns = Math.floor(width / cellWidth);
0326 var newIndex = currentIndex + columns;
0327 currentIndex = Math.min(newIndex, count - 1);
0328 positionViewAtIndex(currentIndex, GridView.Contain);
0329 } else {
0330 itemGrid.keyNavDown();
0331 }
0332 }
0333
0334 onItemContainsMouseChanged: containsMouse => {
0335 if (!containsMouse) {
0336 if (!actionMenu.opened) {
0337 gridView.currentIndex = -1;
0338 }
0339
0340 hoverArea.pressX = -1;
0341 hoverArea.pressY = -1;
0342 hoverArea.lastX = -1;
0343 hoverArea.lastY = -1;
0344 hoverArea.pressedItem = null;
0345 hoverArea.hoverEnabled = true;
0346 }
0347 }
0348 }
0349 }
0350
0351 MouseArea {
0352 id: hoverArea
0353
0354 anchors.fill: parent
0355
0356 property int pressX: -1
0357 property int pressY: -1
0358 property int lastX: -1
0359 property int lastY: -1
0360 property Item pressedItem: null
0361
0362 acceptedButtons: Qt.LeftButton | Qt.RightButton
0363
0364 hoverEnabled: true
0365
0366 function updatePositionProperties(x, y) {
0367 // Prevent hover event synthesis in QQuickWindow interfering
0368 // with keyboard navigation by ignoring repeated events with
0369 // identical coordinates. As the work done here would be re-
0370 // dundant in any case, these are safe to ignore.
0371 if (lastX === x && lastY === y) {
0372 return;
0373 }
0374
0375 lastX = x;
0376 lastY = y;
0377
0378 var cPos = mapToItem(gridView.contentItem, x, y);
0379 var item = gridView.itemAt(cPos.x, cPos.y);
0380
0381 if (!item) {
0382 gridView.currentIndex = -1;
0383 pressedItem = null;
0384 } else {
0385 itemGrid.focus = (item.itemIndex !== -1)
0386 gridView.currentIndex = item.itemIndex;
0387 }
0388
0389 return item;
0390 }
0391
0392 onPressed: mouse => {
0393 mouse.accepted = true;
0394
0395 updatePositionProperties(mouse.x, mouse.y);
0396
0397 pressX = mouse.x;
0398 pressY = mouse.y;
0399
0400 if (mouse.button === Qt.RightButton) {
0401 if (gridView.currentItem) {
0402 if (gridView.currentItem.hasActionList) {
0403 var mapped = mapToItem(gridView.currentItem, mouse.x, mouse.y);
0404 gridView.currentItem.openActionMenu(mapped.x, mapped.y);
0405 }
0406 } else {
0407 var mapped = mapToItem(rootItem, mouse.x, mouse.y);
0408 contextMenu.open(mapped.x, mapped.y);
0409 }
0410 } else {
0411 pressedItem = gridView.currentItem;
0412 }
0413 }
0414
0415 onReleased: mouse => {
0416 mouse.accepted = true;
0417 updatePositionProperties(mouse.x, mouse.y);
0418
0419 if (!dragHelper.dragging) {
0420 if (pressedItem) {
0421 if ("trigger" in gridView.model) {
0422 gridView.model.trigger(pressedItem.itemIndex, "", null);
0423 root.toggle();
0424 }
0425
0426 itemGrid.itemActivated(pressedItem.itemIndex, "", null);
0427 } else if (mouse.button === Qt.LeftButton) {
0428 root.toggle();
0429 }
0430 }
0431
0432 pressX = pressY = -1;
0433 pressedItem = null;
0434 }
0435
0436 onPositionChanged: mouse => {
0437 var item = pressedItem? pressedItem : updatePositionProperties(mouse.x, mouse.y);
0438
0439 if (gridView.currentIndex !== -1) {
0440 if (itemGrid.dragEnabled && pressX !== -1 && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) {
0441 if ("pluginName" in item.m) {
0442 dragHelper.startDrag(kicker, item.url, item.icon,
0443 "text/x-plasmoidservicename", item.m.pluginName);
0444 } else {
0445 dragHelper.startDrag(kicker, item.url, item.icon);
0446 }
0447
0448 kicker.dragSource = item;
0449
0450 pressX = -1;
0451 pressY = -1;
0452 }
0453 }
0454 }
0455 }
0456 }
0457 }