Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for "Opening photo from file muncher doesn't start gallery" #14

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
227 changes: 21 additions & 206 deletions qml/ImageContainer.qml
Expand Up @@ -45,213 +45,28 @@ Item {
width: imgController.imgContainerWidth
height: imgController.imgContainerHeight

function resetZoom() {
//resetting all variables related to pinch-to-zoom
img.scale = 1
flickImg.contentX = flickImg.contentY = 0
pinchImg.lastContentX = pinchImg.lastContentY = pinchImg.deltaX = pinchImg.deltaY = 0
pinchImg.lastScaleX = pinchImg.lastScaleY = 1
pinchImg.isZoomingOut = false
}

PinchArea {
id: pinchImg
anchors.fill: imgContainer

//Disable the pincharea if the listview is scrolling, to avoid problems
enabled: (!imgController.moving && !isVideo)
pinch.target: img
pinch.maximumScale: 5
pinch.dragAxis: Pinch.NoDrag

property real lastContentX: 0
property real lastContentY: 0
property real lastScaleX: 1
property real lastScaleY: 1
property real deltaX: 0
property real deltaY: 0
property bool initializedX: false
property bool initializedY: false
property bool isZoomingOut: false


function updateContentX() {

//Only calculate the correct ContentX if the image is wider than the screen, otherwise keep it centered (contentX = 0 in the else branch)
if (rect.width == imgController.width) {

//Anchors the image to the left
if (flickImg.contentX < 0){
deltaX = 0.0
lastContentX = 0.0
flickImg.contentX = 0.0
}
else {
//if the right end of the image is inside the screen area, lock it to the right and zoom out using right edge as an anchor
if ((flickImg.contentWidth - flickImg.contentX < parent.width) && isZoomingOut) {

//align to the right
flickImg.contentX -= parent.width - (flickImg.contentWidth - flickImg.contentX)

//Algo: set variable as if a new pinch starting from right edge were triggered
lastContentX = flickImg.contentX
deltaX = flickImg.contentX + parent.width
lastScaleX = img.scale
}

flickImg.contentX = (lastContentX + deltaX * ((img.scale / lastScaleX) - 1.0 ))
}
}
else {
flickImg.contentX = 0
}
ImageDisplay {
id: img
isVideo: imgContainer.isVideo
width: (fitsVertically) ? (imgController.height * imgRatio) : imgController.width
height: (fitsVertically) ? (imgController.height) : (imgController.width / imgRatio)
pinchEnabled: (!imgController.moving && !isVideo)
pageWidth: imgController.width
pageHeight: imgController.height

property int imgWidth: isVideo ? videoThumbnailSize : (info.available ? info.metaData.width : -1)
property int imgHeight: isVideo ? videoThumbnailSize : (info.available ? info.metaData.height : -1)
property real imgRatio: imgWidth / imgHeight
property bool fitsVertically: imgRatio < (imgContainer.width / imgContainer.height)

//DocumentGalleryItem automatically recognizes the rootType of the file
DocumentGalleryItem {
id: info
item: galleryModel.get(index).itemId
autoUpdate: true
properties: ["width", "height", "url"]
}

function updateContentY() {

//Only calculate the correct ContentY if the image is taller than the screen, otherwise keep it centered (contentY = 0 in the else branch)
if (rect.height == imgController.height) {

//Anchors the image to the top when zooming out
if (flickImg.contentY < 0) {
deltaY = 0.0
lastContentY = 0.0
flickImg.contentY = 0.0
}
else {
//if the bottom end of the image is inside the screen area, lock it to the bottom and zoom out using bottom edge as an anchor
if ((flickImg.contentHeight - flickImg.contentY < parent.height) && isZoomingOut) {
//align to the bottom
flickImg.contentY -= parent.height - (flickImg.contentHeight - flickImg.contentY)

//Algo: set variable as if a new pinch starting from bottom edge were triggered
lastContentY = flickImg.contentY
deltaY = flickImg.contentY + parent.height
lastScaleY = img.scale
}
flickImg.contentY = (lastContentY + deltaY * ((img.scale / lastScaleY) - 1.0 ))
}
}
else {
flickImg.contentY = 0
}
}


onPinchUpdated: {
//Am I zooming in or out?
if (pinch.scale > pinch.previousScale) isZoomingOut = false
else isZoomingOut = true

//Get updated "zoom center point" values when the image is completely zoomed out
if(img.scale == 1) {
//This is so that everytime you zoom out, the new zoom is started with updated values
initializedX = false
initializedY = false
}

//i.e. everytime the image is wider than the screen, it should actually be
// img.width == imgController.width, but this condition is rarely met because of numeric error
if (rect.width == imgController.width) {
if (!initializedX ) {
//If it has not already been set by the "if (height == parent.imgController.height)" branch, set the scale here
lastScaleX = img.scale

lastContentX = flickImg.contentX
deltaX = flickImg.contentX + pinch.center.x
initializedX = true;
}

}

if (rect.height == imgController.height) {
if (!initializedY) {
//If it has not already been set by the "if (width == imgController.width)", set the scale here
lastScaleY = img.scale

lastContentY = flickImg.contentY
deltaY = flickImg.contentY + pinch.center.y
initializedY = true;
}
}
// updateContentX and updateContentY are called after the scale on the target item updates bindings
}

onPinchFinished: {
lastContentX = flickImg.contentX
lastContentY = flickImg.contentY

initializedX = false
initializedY = false
}

}

Item {
id: rect
anchors.centerIn: parent

width: Math.min(img.width*img.scale, parent.width)
height: Math.min(img.height*img.scale, parent.height)

Flickable {
id: flickImg

anchors.fill: rect
transformOrigin: Item.TopLeft

contentWidth: img.width * img.scale
contentHeight: img.height * img.scale

onContentWidthChanged: {
pinchImg.updateContentX()
pinchImg.updateContentY()
}
onContentHeightChanged: {
pinchImg.updateContentX()
pinchImg.updateContentY()
}

Image {
id: img
width: (fitsVertically) ? (imgController.height * imgRatio) : imgController.width
height: (fitsVertically) ? (imgController.height) : (imgController.width / imgRatio)

property int imgWidth: isVideo ? videoThumbnailSize : (info.available ? info.metaData.width : -1)
property int imgHeight: isVideo ? videoThumbnailSize : (info.available ? info.metaData.height : -1)
property real imgRatio: imgWidth / imgHeight
property bool fitsVertically: imgRatio < (imgContainer.width / imgContainer.height)

//DocumentGalleryItem automatically recognizes the rootType of the file
DocumentGalleryItem {
id: info
item: galleryModel.get(index).itemId
autoUpdate: true
properties: ["width", "height", "url"]
}

transformOrigin: Item.TopLeft
asynchronous: true
source: isVideo ? "qrc:/images/DefaultVideoThumbnail.jpg" : galleryModel.get(index).url
sourceSize.width: 1200

//Disable ListView scrolling if you're zooming
onScaleChanged: {
if (scale != 1 && imgController.flickAreaEnabled == true) imgController.flickAreaEnabled = false
else if (scale == 1 && imgController.flickAreaEnabled == false) imgController.flickAreaEnabled = true
}

MouseArea {
anchors.fill: parent

onClicked: {
if (!isVideo) {
if (imgController.doubleClickTimer.running) resetZoom()
else imgController.doubleClickTimer.start()
}
}
}
}
}
source: isVideo ? "qrc:/images/DefaultVideoThumbnail.jpg" : galleryModel.get(index).url
}
}
138 changes: 138 additions & 0 deletions qml/ImageDisplay.qml
@@ -0,0 +1,138 @@
/*
* Copyright (C) 2012 Andrea Bernabei <and.bernabei@gmail.com>
*
* You may use this file under the terms of the 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 Nemo Mobile 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
* OWNER 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 1.1

Item {
id: imageItem
property bool isVideo: false
property bool pinchEnabled: true
property bool flickEnabled: pinchImg.totalScale == 1
property int pageWidth: 0
property int pageHeight: 0
property variant doubleClickTimer: timer
property string source: ""
property int doubleClickInterval: 350
anchors.centerIn: parent

function resetZoom() {
if(imageItem.width <= 0)
return
if(imageItem.width == imageItem.height) {
// @todo for some reason first image shown is with
// width == height causing wrong aspect ratio to be set.
// Figure out why. This still works. -vranki
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Edit: read your explanation from before. Sort of makes sense, but I'd really like to know what exactly is going on and how it could really be fixed.

console.log("resetZoom workaround")
return
}
flickImg.contentWidth = imageItem.width
flickImg.contentHeight = imageItem.height
flickImg.returnToBounds()
}
Item {
id: rect
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary Item; merge it with the Flickable.

anchors.centerIn: parent

width: Math.min(img.width*img.scale, parent.width)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is img.scale ever even set with this new pinch code?

height: Math.min(img.height*img.scale, parent.height)
Flickable {
id: flickImg

anchors.fill: rect
transformOrigin: Item.TopLeft
contentWidth: imageItem.width
contentHeight: imageItem.height

PinchArea {
id: pinchImg
width: Math.max(flickImg.contentWidth, flickImg.width)
height: Math.max(flickImg.contentHeight, flickImg.height)
property real totalScale: flickImg.contentWidth / imageItem.width
pinch.minimumScale: 1
pinch.maximumScale: 5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these even apply (they're meant to be used with pinch.target), they look wrong: wouldn't that mean you can't do less than 1x or more than 5x in a single pinch gesture, with no relation to the total scale factor?

property real initialWidth
property real initialHeight
onPinchStarted: {
initialWidth = flickImg.contentWidth
initialHeight = flickImg.contentHeight
}

onPinchUpdated: {
// adjust content pos due to drag
flickImg.contentX += pinch.previousCenter.x - pinch.center.x
flickImg.contentY += pinch.previousCenter.y - pinch.center.y

if(initialWidth * pinch.scale < imageItem.width) {
flickImg.contentWidth = imageItem.width
flickImg.contentHeight = imageItem.height
return
}
if(initialWidth * pinch.scale > imageItem.width*5) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also not going to limit the total scale factor (because it uses pinch.scale), and this will fail for images where height > width. Calculate the actual effective scale factor as it would be after resize, bound that, and apply it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wrong here, because initialWidth is the size at the start of the gesture, and aspect ratio is fixed. This will work as intended.

flickImg.contentWidth = imageItem.width*5
flickImg.contentHeight = imageItem.height*5
return
}
// resize content
flickImg.resizeContent(initialWidth * pinch.scale, initialHeight * pinch.scale, pinch.center)
}

onPinchFinished: {
// Move its content within bounds.
flickImg.returnToBounds()
}

Image {
source: imageItem.source
id: img
asynchronous: true
transformOrigin: Item.TopLeft
sourceSize.width: 1200
width: flickImg.contentWidth
height: flickImg.contentHeight

MouseArea {
anchors.fill: parent
onClicked: {
if (!isVideo) {
if (timer.running) resetZoom()
else timer.start()
}
}
}
}
}
}
}

Timer {
id: timer
interval: doubleClickInterval
}
}