harbour-books/app/qml/BooksDragArea.qml
Slava Monich 08c0904a84 [app] Fixed the problem with the initial focus
If the first press isn't ignored by the drag area then the first flick
won't work. The problem has something to do with this drag area as
everything works fine if the drag area is removed. Couldn't figure out
what exactly the problem was and what's wrong with the initial focus,
but this hack makes things work.
2015-12-01 19:10:31 +02:00

285 lines
12 KiB
QML

/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
MouseArea {
id: root
parent: dragInProgress ? dragParent : gridView
anchors.fill: parent
propagateComposedEvents: true
signal deleteItemAt(var index)
signal dropItem(var mouseX, var mouseY)
property var dragParent
property var gridView
property int draggedItemIndex: -1
property int pressedItemIndex: -1
property int pressedDeleteItemIndex: -1
property int lastPressedItemScalingIndex: -1
property int lastReleasedItemIndex: -1
property int lastReleasedDeleteItemIndex: -1
property bool pressedItemScaling: (pressedItemIndex >= 0 && lastPressedItemScalingIndex === pressedItemIndex)
property bool dragPositionChanged
property bool dragCloseToTheLeftEdge
property bool dragCloseToTheRightEdge
property real dragLastX
property real dragLastY
property bool _ignorePress: true
onDraggedItemIndexChanged: {
draggedItem = (draggedItemIndex >= 0) ? shelf.get(draggedItemIndex) : null
}
function itemX(index) { return shelfView.cellWidth * (index % shelfView._cellsPerRow) - gridView.contentX }
function itemY(index) { return shelfView.cellHeight * Math.floor(index / shelfView._cellsPerRow) - gridView.contentY }
onCanceled: {
stopDrag()
}
onClicked: {
var index
if (shelfView.editMode) {
if (!dragInProgress || !dragPositionChanged) {
index = gridView.indexAt(mouseX + gridView.contentX, mouseY + currentShelfView.contentY)
if (index >= 0 &&
index === lastReleasedDeleteItemIndex &&
dragItem.withinDeleteButton(mouseX - itemX(index), mouseY - itemY(index))) {
lastReleasedDeleteItemIndex = -1
root.deleteItemAt(index)
dragScrollAnimation.stop()
} else if (shelfView.shelf.deleteRequested(index)) {
shelfView.shelf.setDeleteRequested(index, false);
} else {
shelfView.stopEditing()
}
}
} else {
index = gridView.indexAt(mouseX + gridView.contentX, mouseY + currentShelfView.contentY)
if (index >= 0) {
if (index === lastReleasedItemIndex) {
var item = shelf.get(index);
if (item.accessible) {
if (item.book) {
shelfView.openBook(item.book)
} else if (item.shelf) {
var path = shelfView.shelf.relativePath
shelfView.shelf.relativePath = path ? (path + "/" + item.shelf.name) : item.shelf.name
}
}
}
} else if (mouseY + gridView.contentY < 0) {
// Let the header item handle it
mouse.accepted = false
}
}
resetPressState()
dragScrollAnimation.stop()
}
onPressed: {
var index = gridView.indexAt(mouseX + gridView.contentX, mouseY + currentShelfView.contentY)
lastReleasedItemIndex = -1
lastReleasedDeleteItemIndex = -1
pressedItemIndex = index
if (pressedDeleteItemIndex < 0 &&
dragItem.withinDeleteButton(mouseX - itemX(index), mouseY - itemY(index))) {
pressedDeleteItemIndex = index
} else {
pressedDeleteItemIndex = -1
}
if (mouseY + gridView.contentY < 0) {
// Let the header item handle it
mouse.accepted = false
} else if (_ignorePress) {
// If the first press isn't ignored here then the first flick
// won't work. The problem has something to do with this drag
// area as everything works fine if the drag area is removed.
// Couldn't figure out what exactly the problem was and what's
// wrong with the initial focus, but this hack makes things work.
_ignorePress = false
mouse.accepted = false
}
}
onReleased: {
stopDrag(mouseX, mouseY)
if (mouseY + gridView.contentY < 0) {
// Let the header item handle it
mouse.accepted = false
}
}
onPressAndHold: {
if (!shelfView.editMode) {
shelfView.startEditing()
}
}
onPositionChanged: {
if (shelfView.editMode) {
if (!pressedItemScaling && !dragInProgress && pressedDeleteItemIndex < 0) {
startDrag(mouseX, mouseY)
} else {
doDrag(mouseX, mouseY)
}
}
}
function startDrag(x, y) {
var index = gridView.indexAt(x + gridView.contentX, y + currentShelfView.contentY)
if (index >= 0) {
var item = shelf.get(index)
if (item.accessible) {
var delta = gridView.mapToItem(dragParent,0,0)
console.log(shelf.path, "dragging", item.name)
dragLastX = x + delta.x
dragLastY = y + delta.y
dragItem.moveAnimationEnabled = false
dragItem.shelfIndex = shelfView.shelfIndex
dragItem.dropShelfIndex = -1
dragItem.dropItemIndex = -1
dragItem.x = itemX(index) + delta.x
dragItem.y = itemY(index) + delta.y
dragCloseToTheLeftEdge = (x < horizontalScrollThreshold)
dragCloseToTheRightEdge = (x > (gridView.width - horizontalScrollThreshold))
dragPositionChanged = false
draggedItemIndex = index
pressedItemIndex = -1
dragItem.visible = true
} else {
console.log(item.name, "is not draggable")
}
}
}
function doDrag(x, y) {
if (dragInProgress && !dragItem.moving) {
var dx = x - dragLastX
var dy = y - dragLastY
if (dx !== 0 || dy !== 0) {
dragPositionChanged = true
dragItem.x += dx
dragItem.y += dy
dragLastX = x
dragLastY = y
var newIndex = currentShelfView.indexAt(x + gridView.contentX, Math.max(y + currentShelfView.contentY, 0))
if (newIndex < 0) newIndex = currentShelf.count - 1
if (newIndex >= 0) {
if (currentShelf.hasDummyItem) {
currentShelf.dummyItemIndex = newIndex
} else {
currentShelf.move(draggedItemIndex, newIndex)
draggedItemIndex = newIndex
}
}
if (y < verticalScrollThreshold && currentShelfView.contentY > 0) {
if (!dragScrollAnimation.running) {
dragScrollAnimation.from = currentShelfView.contentY
dragScrollAnimation.to = 0
dragScrollAnimation.duration = currentShelfView.contentY*1000/gridView.height
dragScrollAnimation.start()
}
} else if (y > (gridView.height - verticalScrollThreshold) && (currentShelfView.contentY < maxContentY)) {
if (!dragScrollAnimation.running) {
dragScrollAnimation.from = currentShelfView.contentY
dragScrollAnimation.to = maxContentY
dragScrollAnimation.duration = (maxContentY - currentShelfView.contentY)*1000/gridView.height
dragScrollAnimation.start()
}
} else {
if (dragScrollAnimation.running) {
dragScrollAnimation.stop()
}
if (x < horizontalScrollThreshold) {
if (!dragCloseToTheLeftEdge) {
dragCloseToTheLeftEdge = true
shelfView.scrollLeft()
}
} else {
dragCloseToTheLeftEdge = false
}
if (x > (gridView.width - horizontalScrollThreshold)) {
if (!dragCloseToTheRightEdge) {
dragCloseToTheRightEdge = true
shelfView.scrollRight()
}
} else {
dragCloseToTheRightEdge = false
}
}
}
}
}
function stopDrag(x, y) {
lastReleasedItemIndex = pressedItemIndex
lastReleasedDeleteItemIndex = pressedDeleteItemIndex
if (draggedItemIndex >= 0) {
if (x !== undefined && y !== undefined) doDrag(x, y)
var delta = gridView.mapToItem(dragParent,0,0)
dragItem.moveAnimationEnabled = true
dragItem.x = itemX(draggedItemIndex) + delta.x
dragItem.y = itemY(draggedItemIndex) + delta.y
// Allow the listener to modify dragItem coordinates
if (x !== undefined && y !== undefined) root.dropItem(x,y)
dragItem.dragged = false
// Normally we would finish things up once all the
// animations have completed
if (!dragItem.animating) finishDrag()
}
pressedItemIndex = -1
dragScrollAnimation.stop()
}
function finishDrag() {
console.log(shelf.path, "done with drag animation")
draggedItemIndex = -1
dragItem.dropShelfIndex = -1
dragItem.dropItemIndex = -1
dragItem.moveAnimationEnabled = false
dragItem.visible = false
}
function resetPressState() {
pressedItemIndex = -1
pressedDeleteItemIndex = -1
lastReleasedItemIndex = -1
lastReleasedDeleteItemIndex = -1
dragCloseToTheLeftEdge = false
dragCloseToTheRightEdge = false
}
NumberAnimation {
id: dragScrollAnimation
target: currentShelfView
property: "contentY"
easing.type: Easing.InOutQuad
}
}