punktdateien/kde/plasma/plasmoids/org.kde.plasma.dittomenu/contents/ui/ItemGridView.qml
2024-02-14 21:14:16 +01:00

485 lines
17 KiB
QML

/***************************************************************************
* Copyright (C) 2015 by Eike Hein <hein@kde.org> *
* *
* 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.core 2.0 as PlasmaCore
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
import QtQuick.Controls 2.12
FocusScope {
id: itemGrid
signal keyNavLeft
signal keyNavRight
signal keyNavUp
signal keyNavDown
signal itemActivated(int index, string actionId, string argument)
property bool dragEnabled: false
property bool dropEnabled: false
property bool showLabels: true
property alias usesPlasmaTheme: gridView.usesPlasmaTheme
property alias currentIndex: gridView.currentIndex
property alias currentItem: gridView.currentItem
property alias contentItem: gridView.contentItem
property alias count: gridView.count
property alias model: gridView.model
property alias cellWidth: gridView.cellWidth
property alias cellHeight: gridView.cellHeight
property alias iconSize: gridView.iconSize
property alias scrollBar: scrollArea
//<>property alias horizontalScrollBarPolicy: scrollArea.horizontalScrollBarPolicy
//<>property alias verticalScrollBarPolicy: scrollArea.verticalScrollBarPolicy
onDropEnabledChanged: {
if (!dropEnabled && "dropPlaceHolderIndex" in model) {
model.dropPlaceHolderIndex = -1;
}
}
onFocusChanged: {
if (!focus) {
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: actionMenu
onActionClicked: {
visualParent.actionTriggered(actionId, actionArgument);
}
}
DropArea {
id: dropArea
anchors.fill: parent
onDragMove: {
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);
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;
}
}
onDragLeave: {
if ("dropPlaceholderIndex" in itemGrid.model) {
itemGrid.model.dropPlaceholderIndex = -1;
gridView.currentIndex = -1;
}
}
onDrop: {
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: resetAnimationDurationTimer
interval: 120
repeat: false
onTriggered: {
gridView.animationDuration = interval - 20;
}
}
//PlasmaExtras.ScrollArea
Flickable{
id: scrollArea
clip: true
anchors.fill: parent
boundsBehavior: Flickable.StopAtBounds
interactive: false
focus: true
GridView {
id: gridView
width: itemGrid.width
height: itemGrid.height
signal itemContainsMouseChanged(bool containsMouse)
property bool usesPlasmaTheme: false
property int iconSize: PlasmaCore.Units.iconSizes.huge
property bool animating: false
property int animationDuration: itemGrid.dropEnabled ? resetAnimationDurationTimer.interval : 0
focus: true
snapMode: GridView.SnapToRow
ScrollBar.vertical: ScrollBar {
visible: true
active: true
policy: ScrollBar.AlwaysOn;
}
currentIndex: -1
move: Transition {
enabled: itemGrid.dropEnabled
SequentialAnimation {
PropertyAction { target: gridView; property: "animating"; value: true }
NumberAnimation {
duration: gridView.animationDuration
properties: "x, y"
easing.type: Easing.OutQuad
}
PropertyAction { target: gridView; property: "animating"; value: false }
}
}
moveDisplaced: Transition {
enabled: itemGrid.dropEnabled
SequentialAnimation {
PropertyAction { target: gridView; property: "animating"; value: true }
NumberAnimation {
duration: gridView.animationDuration
properties: "x, y"
easing.type: Easing.OutQuad
}
PropertyAction { target: gridView; property: "animating"; value: false }
}
}
keyNavigationWraps: false
boundsBehavior: Flickable.StopAtBounds
delegate: ItemGridDelegate {
showLabel: itemGrid.showLabels
}
highlight: Item {
property bool isDropPlaceHolder: "dropPlaceholderIndex" in itemGrid.model && itemGrid.currentIndex === itemGrid.model.dropPlaceholderIndex
PlasmaComponents.Highlight {
visible: gridView.currentItem && !isDropPlaceHolder
anchors.fill: parent
}
PlasmaCore.FrameSvgItem {
visible: gridView.currentItem && isDropPlaceHolder
anchors.fill: parent
imagePath: "widgets/viewitem"
prefix: "selected"
opacity: 0.5
PlasmaCore.IconItem {
anchors {
right: parent.right
rightMargin: parent.margins.right
bottom: parent.bottom
bottomMargin: parent.margins.bottom
}
width: PlasmaCore.Units.iconSizes.smallMedium
height: width
source: "list-add"
active: false
}
}
}
highlightFollowsCurrentItem: true
highlightMoveDuration: 0
onCurrentIndexChanged: {
if (currentIndex != -1) {
hoverArea.hoverEnabled = false
focus = true;
}
}
onCountChanged: {
animationDuration = 0;
resetAnimationDurationTimer.start();
}
onModelChanged: {
currentIndex = -1;
}
Keys.onLeftPressed: {
if (itemGrid.currentCol() !== 0) {
event.accepted = true;
moveCurrentIndexLeft();
} else {
itemGrid.keyNavLeft();
}
}
Keys.onRightPressed: {
var columns = Math.floor(width / cellWidth);
if (itemGrid.currentCol() !== columns - 1 && currentIndex != count -1) {
event.accepted = true;
moveCurrentIndexRight();
} else {
itemGrid.keyNavRight();
}
}
Keys.onUpPressed: {
if (itemGrid.currentRow() !== 0) {
event.accepted = true;
moveCurrentIndexUp();
positionViewAtIndex(currentIndex, GridView.Contain);
} else {
itemGrid.keyNavUp();
}
}
Keys.onDownPressed: {
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();
}
}
onItemContainsMouseChanged: {
if (!containsMouse) {
if (!actionMenu.opened) {
gridView.currentIndex = -1;
}
hoverArea.pressX = -1;
hoverArea.pressY = -1;
hoverArea.lastX = -1;
hoverArea.lastY = -1;
hoverArea.pressedItem = null;
hoverArea.hoverEnabled = true;
}
}
}
}
MouseArea {
id: hoverArea
//anchors.fill: parent
width: itemGrid.width - PlasmaCore.Units.largeSpacing
height: itemGrid.height
property int pressX: -1
property int pressY: -1
property int lastX: -1
property int lastY: -1
property Item pressedItem: null
acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled: true
function 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);
if (!item) {
gridView.currentIndex = -1;
pressedItem = null;
} else {
gridView.currentIndex = item.itemIndex;
itemGrid.focus = (itemGrid.currentIndex != -1)
}
return item;
}
onPressed: mouse => {
mouse.accepted = true;
updatePositionProperties(mouse.x, mouse.y);
pressX = mouse.x;
pressY = mouse.y;
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;
}
}
onReleased: mouse => {
mouse.accepted = true;
updatePositionProperties(mouse.x, mouse.y);
if (gridView.currentItem && gridView.currentItem == pressedItem) {
if ("trigger" in gridView.model) {
gridView.model.trigger(pressedItem.itemIndex, "", null);
root.toggle();
}
itemGrid.itemActivated(pressedItem.itemIndex, "", null);
} else if (!dragHelper.dragging && !pressedItem && mouse.button == Qt.LeftButton) {
root.toggle();
}
pressX = -1;
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.icon,
"text/x-plasmoidservicename", item.m.pluginName);
} else {
dragHelper.startDrag(kicker, item.url, item.icon);
}
kicker.dragSource = item;
pressX = -1;
pressY = -1;
}
}
}
}
}
}