diff --git a/package/contents/ui/ItemGridDelegate.qml b/package/contents/ui/ItemGridDelegate.qml index 605d11c..17d52bc 100644 --- a/package/contents/ui/ItemGridDelegate.qml +++ b/package/contents/ui/ItemGridDelegate.qml @@ -1,145 +1,115 @@ /*************************************************************************** * Copyright (C) 2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ import QtQuick 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.kquickcontrolsaddons 2.0 import "../code/tools.js" as Tools -MouseArea { +Item { id: item width: GridView.view.cellWidth height: width - signal actionTriggered(string actionId, variant actionArgument) - signal aboutToShowActionMenu(variant actionMenu) - property bool showLabel: true property int itemIndex: model.index property url url: model.url != undefined ? model.url : "" property bool pressed: false property bool hasActionList: ((model.favoriteId != null) || (("hasActionList" in model) && (model.hasActionList == true))) property Item view: GridView.view - property Item menu: actionMenu Accessible.role: Accessible.MenuItem Accessible.name: model.display - acceptedButtons: Qt.LeftButton | Qt.RightButton - - onPressed: { - if (mouse.buttons & Qt.RightButton) { - if (hasActionList) { - openActionMenu(item, mouse.x, mouse.y); - } - } else { - pressed = true; - } - } - - onReleased: { - if (pressed && GridView.view.currentItem == item) { - GridView.view.model.trigger(index, "", null); - - if ("toggle" in root) { - root.toggle(); - } else { - root.visible = false; - } - } - - pressed = false; - } - - onAboutToShowActionMenu: { + function openActionMenu(x, y) { var actionList = hasActionList ? model.actionList : []; Tools.fillActionMenu(actionMenu, actionList, GridView.view.model.favoritesModel, model.favoriteId); + actionMenu.visualParent = item; + actionMenu.open(x, y); } - onActionTriggered: { - Tools.triggerAction(GridView.view.model, model.index, actionId, actionArgument); - } + function actionTriggered() { + var close = Tools.triggerAction(GridView.view.model, model.index, actionId, actionArgument); - function openActionMenu(visualParent, x, y) { - aboutToShowActionMenu(actionMenu); - actionMenu.visualParent = visualParent; - actionMenu.open(x, y); + if (close) { + root.toggle(); + } } PlasmaCore.IconItem { id: icon y: showLabel ? (2 * highlightItemSvg.margins.top) : 0 anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: showLabel ? undefined : parent.verticalCenter width: iconSize height: width animated: false usesPlasmaTheme: view.usesPlasmaTheme source: model.decoration } PlasmaComponents.Label { id: label visible: showLabel anchors { top: icon.bottom topMargin: units.smallSpacing left: parent.left leftMargin: highlightItemSvg.margins.left right: parent.right rightMargin: highlightItemSvg.margins.right } horizontalAlignment: Text.AlignHCenter elide: Text.ElideRight wrapMode: Text.NoWrap text: model.display } Keys.onPressed: { if (event.key == Qt.Key_Menu && hasActionList) { event.accepted = true; openActionMenu(item); } else if ((event.key == Qt.Key_Enter || event.key == Qt.Key_Return)) { event.accepted = true; GridView.view.model.trigger(index, "", null); if ("toggle" in root) { root.toggle(); } else { root.visible = false; } } } } diff --git a/package/contents/ui/ItemGridView.qml b/package/contents/ui/ItemGridView.qml index ab1f8a4..2b65a27 100644 --- a/package/contents/ui/ItemGridView.qml +++ b/package/contents/ui/ItemGridView.qml @@ -1,340 +1,395 @@ /*************************************************************************** * Copyright (C) 2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ import QtQuick 2.4 import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kquickcontrolsaddons 2.0 import org.kde.draganddrop 2.0 FocusScope { id: itemGrid signal keyNavLeft signal keyNavRight signal keyNavUp signal keyNavDown property bool dragEnabled: false property bool showLabels: true property alias usesPlasmaTheme: gridView.usesPlasmaTheme property int iconSize: root.iconSize - property int pressX: -1 - property int pressY: -1 - property alias currentIndex: gridView.currentIndex property alias currentItem: gridView.currentItem property alias contentItem: gridView.contentItem property alias count: gridView.count property alias flow: gridView.flow property alias snapMode: gridView.snapMode property alias model: gridView.model property alias cellWidth: gridView.cellWidth property alias cellHeight: gridView.cellHeight property alias horizontalScrollBarPolicy: scrollArea.horizontalScrollBarPolicy property alias verticalScrollBarPolicy: scrollArea.verticalScrollBarPolicy onFocusChanged: { if (!focus) { currentIndex = -1; } } function currentRow() { if (currentIndex == -1) { return -1; } return Math.floor(currentIndex / Math.floor(width / cellWidth)); } function currentCol() { if (currentIndex == -1) { return -1; } return currentIndex - (currentRow() * Math.floor(width / cellWidth)); } function lastRow() { var columns = Math.floor(width / cellWidth); return Math.ceil(count / columns) - 1; } function tryActivate(row, col) { if (count) { var columns = Math.floor(width / 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); gridView.forceActiveFocus(); } } function forceLayout() { gridView.forceLayout(); } + ActionMenu { + id: actionMenu + + onActionClicked: { + visualParent.actionTriggered(actionId, actionArgument); + } + } + DropArea { id: dropArea anchors.fill: parent onDragMove: { if (!dragEnabled || gridView.animating) { return; } var cPos = mapToItem(gridView.contentItem, event.x, event.y); var item = gridView.itemAt(cPos.x, cPos.y); if (item && item != kicker.dragSource && kicker.dragSource && kicker.dragSource.parent == gridView.contentItem) { item.GridView.view.model.moveRow(dragSource.itemIndex, item.itemIndex); } } - MouseEventListener { - anchors.fill: parent + Timer { + id: resetAnimationDurationTimer - hoverEnabled: true + interval: 80 + repeat: false - onPressed: { - pressX = mouse.x; - pressY = mouse.y; + onTriggered: { + gridView.animationDuration = interval - 20; } + } - onPressAndHold: { - if (!dragEnabled) { - pressX = -1; - pressY = -1; - return; - } - - var cPos = mapToItem(gridView.contentItem, mouse.x, mouse.y); - var item = gridView.itemAt(cPos.x, cPos.y); + PlasmaExtras.ScrollArea { + id: scrollArea - if (!item) { - return; - } + anchors.fill: parent - if (!dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) { - kicker.dragSource = item; - dragHelper.startDrag(kicker, item.url); - } + focus: true - pressX = -1; - pressY = -1; - } + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - onReleased: { - pressX = -1; - pressY = -1; - } + GridView { + id: gridView - onClicked: { - var cPos = mapToItem(gridView.contentItem, mouse.x, mouse.y); - var item = gridView.itemAt(cPos.x, cPos.y); + property bool usesPlasmaTheme: false - if (!item && "toggle" in root) { - root.toggle(); - } - } + property bool animating: false + property int animationDuration: dragEnabled ? resetAnimationDurationTimer.interval : 0 - onPositionChanged: { - var cPos = mapToItem(gridView.contentItem, mouse.x, mouse.y); - var item = gridView.itemAt(cPos.x, cPos.y); + focus: true - if (!item) { - gridView.currentIndex = -1; - } else { - gridView.currentIndex = item.itemIndex; - itemGrid.focus = (currentIndex != -1) - } - } + currentIndex: -1 - onContainsMouseChanged: { - if (!containsMouse && (!currentItem || !currentItem.menu.opened)) { - gridView.currentIndex = -1; - pressX = -1; - pressY = -1; - } - } + move: Transition { + enabled: itemGrid.dragEnabled - Timer { - id: resetAnimationDurationTimer + SequentialAnimation { + PropertyAction { target: gridView; property: "animating"; value: true } - interval: 80 - repeat: false + NumberAnimation { + duration: gridView.animationDuration + properties: "x, y" + easing.type: Easing.OutQuad + } - onTriggered: { - gridView.animationDuration = interval - 20; + PropertyAction { target: gridView; property: "animating"; value: false } + } } - } - PlasmaExtras.ScrollArea { - id: scrollArea + moveDisplaced: Transition { + enabled: itemGrid.dragEnabled - anchors.fill: parent + SequentialAnimation { + PropertyAction { target: gridView; property: "animating"; value: true } - focus: true - - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + NumberAnimation { + duration: gridView.animationDuration + properties: "x, y" + easing.type: Easing.OutQuad + } - GridView { - id: gridView + PropertyAction { target: gridView; property: "animating"; value: false } + } + } - property bool usesPlasmaTheme: false + keyNavigationWraps: false + boundsBehavior: Flickable.StopAtBounds - property bool animating: false - property int animationDuration: dragEnabled ? resetAnimationDurationTimer.interval : 0 + delegate: ItemGridDelegate { + showLabel: showLabels + } - focus: true + highlight: PlasmaComponents.Highlight {} + highlightFollowsCurrentItem: true + highlightMoveDuration: 0 - currentIndex: -1 + onCountChanged: { + animationDuration = 0; + resetAnimationDurationTimer.start(); + } - move: Transition { - enabled: itemGrid.dragEnabled + onModelChanged: { + currentIndex = -1; + } - SequentialAnimation { - PropertyAction { target: gridView; property: "animating"; value: true } + Keys.onLeftPressed: { + if (currentIndex == -1) { + currentIndex = 0; + return; + } - NumberAnimation { - duration: gridView.animationDuration - properties: "x, y" - easing.type: Easing.OutQuad - } + if (currentCol() != 0) { + event.accepted = true; + moveCurrentIndexLeft(); + } else { + itemGrid.keyNavLeft(); + } + } - PropertyAction { target: gridView; property: "animating"; value: false } - } + Keys.onRightPressed: { + if (currentIndex == -1) { + currentIndex = 0; + return; } - moveDisplaced: Transition { - enabled: itemGrid.dragEnabled + var columns = Math.floor(width / cellWidth); - SequentialAnimation { - PropertyAction { target: gridView; property: "animating"; value: true } + if (currentCol() != columns - 1 && currentIndex != count - 1) { + event.accepted = true; + moveCurrentIndexRight(); + } else { + itemGrid.keyNavRight(); + } + } - NumberAnimation { - duration: gridView.animationDuration - properties: "x, y" - easing.type: Easing.OutQuad - } + Keys.onUpPressed: { + if (currentIndex == -1) { + currentIndex = 0; + return; + } - PropertyAction { target: gridView; property: "animating"; value: false } - } + if (currentRow() != 0) { + event.accepted = true; + moveCurrentIndexUp(); + positionViewAtIndex(currentIndex, GridView.Contain); + } else { + itemGrid.keyNavUp(); } + } - keyNavigationWraps: false - boundsBehavior: Flickable.StopAtBounds + Keys.onDownPressed: { + if (currentIndex == -1) { + currentIndex = 0; + return; + } - delegate: ItemGridDelegate { - showLabel: showLabels + if (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(); } + } + } + } - highlight: PlasmaComponents.Highlight {} - highlightFollowsCurrentItem: true - highlightMoveDuration: 0 + MouseArea { + anchors.fill: parent - onCountChanged: { - animationDuration = 0; - resetAnimationDurationTimer.start(); - } + property int pressX: -1 + property int pressY: -1 + property int lastX: -1 + property int lastY: -1 + property Item pressedItem: null - onModelChanged: { - currentIndex = -1; - } + acceptedButtons: Qt.LeftButton | Qt.RightButton - Keys.onLeftPressed: { - if (currentIndex == -1) { - currentIndex = 0; - return; - } + hoverEnabled: true - if (currentCol() != 0) { - event.accepted = true; - moveCurrentIndexLeft(); - } else { - itemGrid.keyNavLeft(); + onPressed: { + pressX = mouse.x; + pressY = mouse.y; + + mouse.accepted = true; + + if (mouse.button == Qt.RightButton) { + if (gridView.currentItem) { + if (gridView.currentItem.hasActionList) { + var mapped = mapToItem(gridView.currentItem, mouse.x, mouse.y); + gridView.currentItem.openActionMenu(mapped.x, mapped.y); } + } else { + var mapped = mapToItem(rootItem, mouse.x, mouse.y); + contextMenu.open(mapped.x, mapped.y); } + } else { + pressedItem = gridView.currentItem; + } + } - Keys.onRightPressed: { - if (currentIndex == -1) { - currentIndex = 0; - return; - } + onReleased: { + mouse.accepted = true; - var columns = Math.floor(width / cellWidth); + if (gridView.currentItem && gridView.currentItem == pressedItem) { + if ("trigger" in gridView.model) { + gridView.model.trigger(pressedItem.itemIndex, "", null); - if (currentCol() != columns - 1 && currentIndex != count - 1) { - event.accepted = true; - moveCurrentIndexRight(); + if ("toggle" in root) { + root.toggle(); } else { - itemGrid.keyNavRight(); + root.visible = false; } } + } else if (!pressedItem && mouse.button == Qt.LeftButton) { + if ("toggle" in root) { + root.toggle(); + } else { + root.visible = false; + } + } - Keys.onUpPressed: { - if (currentIndex == -1) { - currentIndex = 0; - return; - } + pressX = -1; + pressY = -1; + pressedItem = null; + } - if (currentRow() != 0) { - event.accepted = true; - moveCurrentIndexUp(); - positionViewAtIndex(currentIndex, GridView.Contain); - } else { - itemGrid.keyNavUp(); - } - } + onPressAndHold: { + if (!dragEnabled) { + pressX = -1; + pressY = -1; + return; + } - Keys.onDownPressed: { - if (currentIndex == -1) { - currentIndex = 0; - return; - } + var cPos = mapToItem(gridView.contentItem, mouse.x, mouse.y); + var item = gridView.itemAt(cPos.x, cPos.y); - if (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(); - } - } + if (!item) { + return; + } + + if (!dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) { + kicker.dragSource = item; + dragHelper.startDrag(kicker, item.url); + } + + pressX = -1; + pressY = -1; + pressedItem = null; + } + + onPositionChanged: { + // 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 (mouse.x == lastX && mouse.y == lastY) { + return; + } + + lastX = mouse.x; + lastY = mouse.y; + + var cPos = mapToItem(gridView.contentItem, mouse.x, mouse.y); + var item = gridView.itemAt(cPos.x, cPos.y); + + if (!item) { + gridView.currentIndex = -1; + pressedItem = null; + } else { + gridView.currentIndex = item.itemIndex; + itemGrid.focus = (currentIndex != -1) + } + } + + onContainsMouseChanged: { + if (!containsMouse && (!currentItem || !currentItem.menu.opened)) { + gridView.currentIndex = -1; + pressX = -1; + pressY = -1; + pressedItem = null; } } } } }