/*SPDX-FileCopyrightText: 2015 Eike Hein <hein@kde.org>SPDX-License-Identifier: GPL-2.0-or-later*/pragma ComponentBehavior: Boundimport QtQuickimport org.kde.kirigami as Kirigamiimport org.kde.ksvg as KSvgimport org.kde.plasma.components as PlasmaComponentsimport org.kde.plasma.extras as PlasmaExtrasFocusScope {id: itemGridsignal keyNavLeftsignal keyNavRightsignal keyNavUpsignal keyNavDownsignal interactionConcluded()property bool dragEnabled: trueproperty bool dropEnabled: falseproperty bool showLabels: trueproperty bool hoverEnabled: trueproperty var view: gridViewproperty alias currentIndex: gridView.currentIndexproperty alias currentItem: gridView.currentItemproperty alias contentItem: gridView.contentItemproperty alias count: gridView.countproperty alias model: gridView.modelproperty alias cellWidth: gridView.cellWidthproperty alias cellHeight: gridView.cellHeightproperty alias iconSize: gridView.iconSizeproperty var horizontalScrollBarPolicy: PlasmaComponents.ScrollBar.AlwaysOffproperty var verticalScrollBarPolicy: PlasmaComponents.ScrollBar.AsNeededonDropEnabledChanged: {if (!dropEnabled && "dropPlaceHolderIndex" in model) {model.dropPlaceHolderIndex = -1;}}onFocusChanged: {if (!focus && !root.keyEventProxy.activeFocus) {currentIndex = -1;}}function currentRow() {if (currentIndex === -1) {return -1;}return Math.floor(currentIndex / Math.floor(width / itemGrid.cellWidth));}function currentCol() {if (currentIndex === -1) {return -1;}return currentIndex - (currentRow() * Math.floor(width / itemGrid.cellWidth));}function lastRow() {var columns = Math.floor(width / itemGrid.cellWidth);return Math.ceil(count / columns) - 1;}function tryActivate(row, col) {if (count) {var columns = Math.floor(width / itemGrid.cellWidth);var rows = Math.ceil(count / columns);row = Math.min(row, rows - 1);col = Math.min(col, columns - 1);currentIndex = Math.min(row ? ((Math.max(1, row) * columns) + col): col,count - 1);focus = true;}}function forceLayout() {gridView.forceLayout();}ActionMenu {id: actionMenuonActionClicked: (actionId, actionArgument) => {visualParent.actionTriggered(actionId, actionArgument);}}DropArea {id: dropAreaanchors.fill: parentonPositionChanged: event => {if (!itemGrid.dropEnabled || gridView.animating || !kicker.dragSource) {return;}var x = Math.max(0, event.x - (width % itemGrid.cellWidth));var cPos = mapToItem(gridView.contentItem, x, event.y);var item = gridView.itemAt(cPos.x, cPos.y) as ItemGridDelegate;if (item) {if (kicker.dragSource.parent === gridView.contentItem) {if (item !== kicker.dragSource) {item.GridView.view.model.moveRow(dragSource.itemIndex, item.itemIndex);}} else if (kicker.dragSource.GridView.view.model.favoritesModel === itemGrid.model&& !itemGrid.model.isFavorite(kicker.dragSource.favoriteId)) {var hasPlaceholder = (itemGrid.model.dropPlaceholderIndex !== -1);itemGrid.model.dropPlaceholderIndex = item.itemIndex;if (!hasPlaceholder) {gridView.currentIndex = (item.itemIndex - 1);}}} else if (kicker.dragSource.parent !== gridView.contentItem&& kicker.dragSource.GridView.view.model.favoritesModel === itemGrid.model&& !itemGrid.model.isFavorite(kicker.dragSource.favoriteId)) {var hasPlaceholder = (itemGrid.model.dropPlaceholderIndex !== -1);itemGrid.model.dropPlaceholderIndex = hasPlaceholder ? itemGrid.model.count - 1 : itemGrid.model.count;if (!hasPlaceholder) {gridView.currentIndex = (itemGrid.model.count - 1);}} else {itemGrid.model.dropPlaceholderIndex = -1;gridView.currentIndex = -1;}}onExited: {if ("dropPlaceholderIndex" in itemGrid.model) {itemGrid.model.dropPlaceholderIndex = -1;gridView.currentIndex = -1;}}onDropped: drop => {if (kicker.dragSource && kicker.dragSource.parent !== gridView.contentItem && kicker.dragSource.GridView.view.model.favoritesModel === itemGrid.model) {itemGrid.model.addFavorite(kicker.dragSource.favoriteId, itemGrid.model.dropPlaceholderIndex);gridView.currentIndex = -1;}}Timer {id: resetAnimationDurationTimerinterval: 120repeat: falseonTriggered: {gridView.animationDuration = interval - 20;}}PlasmaComponents.ScrollView {id: scrollAreaanchors.fill: parentfocus: truePlasmaComponents.ScrollBar.horizontal.policy: itemGrid.horizontalScrollBarPolicyGridView {id: gridViewsignal itemContainsMouseChanged(bool containsMouse)property int iconSize: Kirigami.Units.iconSizes.hugeproperty bool animating: falseproperty int animationDuration: itemGrid.dropEnabled ? resetAnimationDurationTimer.interval : 0focus: trueclip: height < contentHeight + topMargin + bottomMargincurrentIndex: -1move: Transition {enabled: itemGrid.dropEnabledSequentialAnimation {PropertyAction { target: gridView; property: "animating"; value: true }NumberAnimation {duration: gridView.animationDurationproperties: "x, y"easing.type: Easing.OutQuad}PropertyAction { target: gridView; property: "animating"; value: false }}}moveDisplaced: Transition {enabled: itemGrid.dropEnabledSequentialAnimation {PropertyAction { target: gridView; property: "animating"; value: true }NumberAnimation {duration: gridView.animationDurationproperties: "x, y"easing.type: Easing.OutQuad}PropertyAction { target: gridView; property: "animating"; value: false }}}keyNavigationWraps: falseboundsBehavior: Flickable.StopAtBoundsdelegate: ItemGridDelegate {showLabel: itemGrid.showLabelsiconSize: gridView.iconSizeonInteractionConcluded: itemGrid.interactionConcluded()hoverEnabled: itemGrid.hoverEnabledonHoveredChanged: {if (hovered) {gridView.currentIndex = index} else {if (!actionMenu.opened) {gridView.currentIndex = -1;}hoverArea.pressX = -1;hoverArea.pressY = -1;hoverArea.lastX = -1;hoverArea.lastY = -1;hoverArea.pressedItem = null;hoverArea.hoverEnabled = itemGrid.hoverEnabled;}}}highlight: Item {id: highlightItemproperty bool isDropPlaceHolder: "dropPlaceholderIndex" in itemGrid.model && itemGrid.currentIndex === itemGrid.model.dropPlaceholderIndexPlasmaExtras.Highlight {visible: gridView.currentItem && !highlightItem.isDropPlaceHolderhovered: truepressed: hoverArea.pressedanchors.fill: parent}KSvg.FrameSvgItem {visible: gridView.currentItem && highlightItem.isDropPlaceHolderanchors.fill: parentimagePath: "widgets/viewitem"prefix: "selected"opacity: 0.5Kirigami.Icon {anchors {right: parent.rightrightMargin: parent.margins.rightbottom: parent.bottombottomMargin: parent.margins.bottom}width: Kirigami.Units.iconSizes.smallMediumheight: widthsource: "list-add"active: false}}}highlightFollowsCurrentItem: truehighlightMoveDuration: 0onCurrentIndexChanged: {if (currentIndex !== -1) {hoverArea.hoverEnabled = falsefocus = true;}}onCountChanged: {animationDuration = 0;resetAnimationDurationTimer.start();}onModelChanged: {currentIndex = -1;}function handleLeftRightArrow(event: KeyEvent) : void {let backArrowKey = (event.key === Qt.Key_Left && Application.layoutDirection === Qt.LeftToRight) ||(event.key === Qt.Key_Right && Application.layoutDirection === Qt.RightToLeft)let forwardArrowKey = (event.key === Qt.Key_Right && Application.layoutDirection === Qt.LeftToRight) ||(event.key === Qt.Key_Left && Application.layoutDirection === Qt.RightToLeft)if (backArrowKey) {if (itemGrid.currentCol() !== 0) {// GridView move..() already handles RtL(event.key === Qt.Key_Left) ? moveCurrentIndexLeft() : moveCurrentIndexRight();} else {itemGrid.keyNavLeft();}} else if (forwardArrowKey) {var columns = Math.floor(width / cellWidth);if (itemGrid.currentCol() !== columns - 1 && currentIndex !== count -1) {// GridView move..() already handles RtL(event.key === Qt.Key_Left) ? moveCurrentIndexLeft() : moveCurrentIndexRight()} else {itemGrid.keyNavRight();}}}Keys.onLeftPressed: event => handleLeftRightArrow(event)Keys.onRightPressed: event => handleLeftRightArrow(event)Keys.onEnterPressed: Keys.returnPressed()Keys.onReturnPressed: {if (gridView.model.trigger) {gridView.model.trigger(currentIndex, "", null)itemGrid.interactionConcluded()}}Keys.onUpPressed: event => {if (itemGrid.currentRow() !== 0) {event.accepted = true;moveCurrentIndexUp();positionViewAtIndex(currentIndex, GridView.Contain);} else {itemGrid.keyNavUp();}}Keys.onDownPressed: event => {if (itemGrid.currentRow() < itemGrid.lastRow()) {// Fix moveCurrentIndexDown()'s lack of proper spatial nav down// into partial columns.event.accepted = true;var columns = Math.floor(width / cellWidth);var newIndex = currentIndex + columns;currentIndex = Math.min(newIndex, count - 1);positionViewAtIndex(currentIndex, GridView.Contain);} else {itemGrid.keyNavDown();}}}}MouseArea {id: hoverAreaanchors.fill: parentanchors.rightMargin: scrollArea.effectiveScrollBarWidthproperty int pressX: -1property int pressY: -1property int lastX: -1property int lastY: -1property ItemGridDelegate pressedItem: nullacceptedButtons: Qt.LeftButton | Qt.RightButtonhoverEnabled: itemGrid.hoverEnabledfunction updatePositionProperties(x, y) {// Prevent hover event synthesis in QQuickWindow interfering// with keyboard navigation by ignoring repeated events with// identical coordinates. As the work done here would be re-// dundant in any case, these are safe to ignore.if (lastX === x && lastY === y) {return;}lastX = x;lastY = y;var cPos = mapToItem(gridView.contentItem, x, y);var item = gridView.itemAt(cPos.x, cPos.y) as ItemGridDelegate;if (!item) {gridView.currentIndex = -1;pressedItem = null;} else {itemGrid.focus = (item.itemIndex !== -1)gridView.currentIndex = item.itemIndex;}return item;}onPressed: mouse => {mouse.accepted = true;updatePositionProperties(mouse.x, mouse.y);pressX = mouse.x;pressY = mouse.y;const currentDelegate = gridView.currentItem as ItemGridDelegateif (mouse.button === Qt.RightButton) {if (currentDelegate) {if (currentDelegate.hasActionList) {var mapped = mapToItem(currentDelegate, mouse.x, mouse.y);currentDelegate.openActionMenu(mapped.x, mapped.y);}} else {var mapped = mapToItem(rootItem, mouse.x, mouse.y);contextMenu.open(mapped.x, mapped.y);}} else {pressedItem = currentDelegate;}}onReleased: mouse => {mouse.accepted = true;updatePositionProperties(mouse.x, mouse.y);if (!dragHelper.dragging) {if (pressedItem) {if ("trigger" in gridView.model) {gridView.model.trigger(pressedItem.itemIndex, "", null);itemGrid.interactionConcluded()}} else if (mouse.button === Qt.LeftButton) {itemGrid.interactionConcluded()}}pressX = pressY = -1;pressedItem = null;}onPositionChanged: mouse => {var item = pressedItem ? pressedItem : updatePositionProperties(mouse.x, mouse.y);if (gridView.currentIndex !== -1) {if (itemGrid.dragEnabled && pressX !== -1 && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) {if ("pluginName" in item.m) {dragHelper.startDrag(kicker, item.url, item.decoration,"text/x-plasmoidservicename", item.model.pluginName);} else {dragHelper.startDrag(kicker, item.url, item.decoration);}kicker.dragSource = item;pressX = -1;pressY = -1;}}}}}}