From 4c918861d0710bfe35b510c607d44242a8835789 Mon Sep 17 00:00:00 2001 From: Slava Monich Date: Sat, 8 Oct 2016 19:57:54 +0300 Subject: [PATCH] [app] Zoom images on long tap --- app/app.pro | 2 + app/qml/BooksBookView.qml | 22 +++++- app/qml/BooksImageView.qml | 73 +++++++++++++++++++ app/qml/BooksPageView.qml | 11 +-- app/src/BooksImageProvider.cpp | 127 +++++++++++++++++++++++++++++++++ app/src/BooksImageProvider.h | 68 ++++++++++++++++++ app/src/BooksPageWidget.cpp | 96 ++++++++++++++++--------- app/src/BooksPageWidget.h | 5 +- app/src/ZLibrary.cpp | 3 + 9 files changed, 365 insertions(+), 42 deletions(-) create mode 100644 app/qml/BooksImageView.qml create mode 100644 app/src/BooksImageProvider.cpp create mode 100644 app/src/BooksImageProvider.h diff --git a/app/app.pro b/app/app.pro index acc603d..f9433b6 100644 --- a/app/app.pro +++ b/app/app.pro @@ -105,6 +105,7 @@ SOURCES += \ src/BooksCoverWidget.cpp \ src/BooksDialogManager.cpp \ src/BooksHints.cpp \ + src/BooksImageProvider.cpp \ src/BooksImportModel.cpp \ src/BooksListWatcher.cpp \ src/BooksLoadingProperty.cpp \ @@ -144,6 +145,7 @@ HEADERS += \ src/BooksDefs.h \ src/BooksDialogManager.h \ src/BooksHints.h \ + src/BooksImageProvider.h \ src/BooksImportModel.h \ src/BooksItem.h \ src/BooksListWatcher.h \ diff --git a/app/qml/BooksBookView.qml b/app/qml/BooksBookView.qml index 357fa76..0384991 100644 --- a/app/qml/BooksBookView.qml +++ b/app/qml/BooksBookView.qml @@ -13,8 +13,8 @@ * 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 Jolla Ltd nor the names of its contributors - may be used to endorse or promote products derived from this software + * Neither the name of 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" @@ -63,9 +63,10 @@ SilicaFlickable { qsTrId("harbour-books-book-view-applying_smaller_fonts") ] - interactive: !linkMenu || !linkMenu.visible + interactive: !linkMenu || !linkMenu.visible || !imageView || !imageView.visible property var linkMenu + property var imageView Component { id: linkMenuComponent @@ -73,6 +74,12 @@ SilicaFlickable { } } + Component { + id: imageViewComponent + BooksImageView { + } + } + PullDownMenu { MenuItem { //% "Back to library" @@ -156,6 +163,15 @@ SilicaFlickable { root.pageClicked(index) globalSettings.pageDetails = (globalSettings.pageDetails+ 1) % _visibilityStates.length } + onImagePressed: { + if (_currentPage == index) { + if (!imageView) { + imageView = imageViewComponent.createObject(root) + } + imageView.source = url + imageView.show() + } + } onBrowserLinkPressed: { if (_currentPage == index) { if (!linkMenu) { diff --git a/app/qml/BooksImageView.qml b/app/qml/BooksImageView.qml new file mode 100644 index 0000000..8cbf499 --- /dev/null +++ b/app/qml/BooksImageView.qml @@ -0,0 +1,73 @@ +/* + Copyright (C) 2016 Jolla Ltd. + Contact: Slava Monich + + 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 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 + +Rectangle { + id: root + visible: opacity > 0.0 + opacity: 0.0 + width: parent.width + height: parent.height + color: Theme.rgba(Theme.highlightDimmerColor, 0.9) + + property alias source: image.source + readonly property real maxImageWidth: width - 2*Theme.horizontalPageMargin + readonly property real maxImageHeight: height - 2*Theme.paddingLarge + + Behavior on opacity { FadeAnimation {} } + + Image { + id: image + anchors.centerIn: parent + smooth: true + readonly property bool landscape: sourceSize.width * parent.height > sourceSize.height * parent.width + width: landscape ? maxImageWidth : (maxImageHeight * sourceSize.width / sourceSize.height) + height: landscape ? (maxImageWidth * sourceSize.height / sourceSize.width) : maxImageHeight + } + + MouseArea { + anchors.fill: parent + onPressed: { + root.hide() + } + } + + function show() { + opacity = 1.0 + } + + function hide() { + opacity = 0.0 + } +} diff --git a/app/qml/BooksPageView.qml b/app/qml/BooksPageView.qml index fce780a..52148d0 100644 --- a/app/qml/BooksPageView.qml +++ b/app/qml/BooksPageView.qml @@ -1,5 +1,5 @@ /* - Copyright (C) 2015 Jolla Ltd. + Copyright (C) 2015-2016 Jolla Ltd. Contact: Slava Monich You may use this file under the terms of BSD license as follows: @@ -7,14 +7,15 @@ 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. + * Neither the name of 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 @@ -49,6 +50,7 @@ Item { property bool pageNumberVisible signal pageClicked() + signal imagePressed(var url, var rect) signal browserLinkPressed(var url) PageWidget { @@ -57,6 +59,7 @@ Item { settings: globalSettings model: bookModel onBrowserLinkPressed: view.browserLinkPressed(url) + onImagePressed: view.imagePressed(url, rect) } BooksTitleLabel { diff --git a/app/src/BooksImageProvider.cpp b/app/src/BooksImageProvider.cpp new file mode 100644 index 0000000..0477e66 --- /dev/null +++ b/app/src/BooksImageProvider.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2016 Jolla Ltd. + * Contact: Slava Monich + * + * 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 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 + * 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. + */ + +#include "BooksImageProvider.h" +#include "image/ZLQtImageManager.h" +#include "HarbourDebug.h" + +const QString BooksImageProvider::PROVIDER_ID("bookImage"); +BooksImageProvider* BooksImageProvider::gInstance = NULL; + +BooksImageProvider* +BooksImageProvider::instance() +{ + if (!gInstance) { + new BooksImageProvider; + } + return gInstance; +} + +BooksImageProvider::BooksImageProvider( + QObject* aParent) : + QObject(aParent), + QQuickImageProvider(QQuickImageProvider::Image) +{ + gInstance = this; +} + +BooksImageProvider::~BooksImageProvider() +{ + if (gInstance == this) { + gInstance = NULL; + } +} + +void +BooksImageProvider::addImage( + QObject* aOwner, + QString aId, + shared_ptr aData) +{ + if (aOwner && !aId.isEmpty() && !aData.isNull()) { + QMutexLocker locker(&iMutex); + if (!iImageMap.contains(aId)) { + if (!iOwnerMap.contains(aOwner)) { + connect(aOwner, SIGNAL(destroyed(QObject*)), + SLOT(releaseImages(QObject*))); + } else { + QStringList ids = iOwnerMap.value(aOwner); + ids.append(aId); + iOwnerMap.insert(aOwner, ids); + } + } + iImageMap.insert(aId, aData); + } +} + +void +BooksImageProvider::releaseImages( + QObject* aOwner) +{ + QMutexLocker locker(&iMutex); + const QStringList ids = iOwnerMap.take(aOwner); + const int n = ids.count(); + for (int i=0; i ptr = iImageMap.value(aId); + if (!ptr.isNull()) { + HDEBUG(aId); + const ZLQtImageData& data = (const ZLQtImageData&)*ptr; + const QImage* image = data.image(); + HASSERT(image); + if (image) { + if (aRequestedSize.isEmpty() || image->size() == aRequestedSize) { + *aSize = image->size(); + return *image; + } else { + *aSize = aRequestedSize; + return image->scaled(aRequestedSize, Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + } + } + } else { + HWARN("No such image:" << aId); + } + return QImage(); +} diff --git a/app/src/BooksImageProvider.h b/app/src/BooksImageProvider.h new file mode 100644 index 0000000..ee77fde --- /dev/null +++ b/app/src/BooksImageProvider.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016 Jolla Ltd. + * Contact: Slava Monich + * + * 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 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 + * 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. + */ + +#ifndef BOOKS_IMAGE_PROVIDER_H +#define BOOKS_IMAGE_PROVIDER_H + +#include "ZLImageManager.h" + +#include +#include +#include + +class BooksImageProvider : public QObject, public QQuickImageProvider +{ + Q_OBJECT + +public: + static const QString PROVIDER_ID; + + static BooksImageProvider* instance(); + BooksImageProvider(QObject* aParent = NULL); + virtual ~BooksImageProvider(); + + void addImage(QObject* aOwner, QString aId, shared_ptr aData); + virtual QImage requestImage(const QString& aId, QSize* aSize, + const QSize& aRequestedSize); + +public Q_SLOTS: + void releaseImages(QObject* aOwner); + +private: + QMutex iMutex; + QHash > iImageMap; + QHash iOwnerMap; + static BooksImageProvider* gInstance; +}; + +#endif // BOOKS_IMAGE_PROVIDER_H diff --git a/app/src/BooksPageWidget.cpp b/app/src/BooksPageWidget.cpp index 7b469a0..fac78d9 100644 --- a/app/src/BooksPageWidget.cpp +++ b/app/src/BooksPageWidget.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Jolla Ltd. + * Copyright (C) 2015-2016 Jolla Ltd. * Contact: Slava Monich * * You may use this file under the terms of the BSD license as follows: @@ -14,7 +14,7 @@ * 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 + * * Neither the name of 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. * @@ -31,6 +31,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "BooksImageProvider.h" #include "BooksPageWidget.h" #include "BooksTextStyle.h" #include "BooksDefs.h" @@ -162,7 +163,7 @@ void BooksPageWidget::RenderTask::performTask() class BooksPageWidget::LongPressTask : public BooksTask { public: LongPressTask(shared_ptr aData, int aX, int aY) : - iData(aData), iX(aX), iY(aY) {} + iData(aData), iX(aX), iY(aY), iKind(REGULAR) {} void performTask(); @@ -170,9 +171,12 @@ public: shared_ptr iData; int iX; int iY; + QRect iRect; ZLTextKind iKind; - std::string iLinkText; + std::string iLink; std::string iLinkType; + std::string iImageId; + shared_ptr iImageData; }; void BooksPageWidget::LongPressTask::performTask() @@ -180,27 +184,47 @@ void BooksPageWidget::LongPressTask::performTask() if (!isCanceled()) { const ZLTextArea& area = iData->iView->textArea(); const ZLTextElementRectangle* rect = area.elementByCoordinates(iX, iY); - if (rect && !isCanceled() && - ((rect->Kind == ZLTextElement::WORD_ELEMENT) || - (rect->Kind == ZLTextElement::IMAGE_ELEMENT))) { - ZLTextWordCursor cursor = area.startCursor(); - cursor.moveToParagraph(rect->ParagraphIndex); - cursor.moveToParagraphStart(); - for (int i=0; iElementIndex && !isCanceled(); i++) { - const ZLTextElement& element = cursor.element(); - if (element.kind() == ZLTextElement::CONTROL_ELEMENT) { - const ZLTextControlEntry& controlEntry = - ((const ZLTextControlElement&)element).entry(); - if (controlEntry.isHyperlink()) { - const ZLTextHyperlinkControlEntry& hyperLinkEntry = - ((const ZLTextHyperlinkControlEntry&)controlEntry); - iKind = hyperLinkEntry.kind(); - iLinkText = hyperLinkEntry.label(); - iLinkType = hyperLinkEntry.hyperlinkType(); - return; + if (rect && !isCanceled()) { + iRect.setLeft(rect->XStart); + iRect.setRight(rect->XEnd); + iRect.setTop(rect->YStart); + iRect.setBottom(rect->YEnd); + if (rect->Kind == ZLTextElement::WORD_ELEMENT) { + ZLTextWordCursor cursor = area.startCursor(); + cursor.moveToParagraph(rect->ParagraphIndex); + cursor.moveToParagraphStart(); + for (int i=0; iElementIndex && !isCanceled(); i++) { + const ZLTextElement& element = cursor.element(); + if (element.kind() == ZLTextElement::CONTROL_ELEMENT) { + const ZLTextControlEntry& controlEntry = + ((const ZLTextControlElement&)element).entry(); + if (controlEntry.isHyperlink()) { + const ZLTextHyperlinkControlEntry& hyperLinkEntry = + ((const ZLTextHyperlinkControlEntry&)controlEntry); + iKind = hyperLinkEntry.kind(); + iLink = hyperLinkEntry.label(); + iLinkType = hyperLinkEntry.hyperlinkType(); + HDEBUG("link" << iLink.c_str()); + return; + } } + cursor.nextWord(); + } + } else if (rect->Kind == ZLTextElement::IMAGE_ELEMENT) { + ZLTextWordCursor cursor = area.startCursor(); + cursor.moveToParagraph(rect->ParagraphIndex); + cursor.moveTo(rect->ElementIndex, 0); + const ZLTextElement& element = cursor.element(); + HASSERT(element.kind() == ZLTextElement::IMAGE_ELEMENT); + if (element.kind() == ZLTextElement::IMAGE_ELEMENT) { + const ZLTextImageElement& imageElement = + (const ZLTextImageElement&)element; + iKind = IMAGE; + iImageId = imageElement.id(); + iImageData = imageElement.image(); + HDEBUG("image element" << iImageId.c_str() << + iImageData->width() << iImageData->height()); } - cursor.nextWord(); } } } @@ -519,18 +543,24 @@ void BooksPageWidget::onRenderTaskDone() void BooksPageWidget::onLongPressTaskDone() { - static const std::string HTTP("http://"); - static const std::string HTTPS("https://"); - HASSERT(sender() == iLongPressTask); - HDEBUG(iLongPressTask->iKind << - iLongPressTask->iLinkType.c_str() << - iLongPressTask->iLinkText.c_str()); + HDEBUG(iLongPressTask->iKind); - if (iLongPressTask->iKind == EXTERNAL_HYPERLINK && - (ZLStringUtil::stringStartsWith(iLongPressTask->iLinkText, HTTP) || - ZLStringUtil::stringStartsWith(iLongPressTask->iLinkText, HTTPS))) { - Q_EMIT browserLinkPressed(QString::fromStdString(iLongPressTask->iLinkText)); + if (iLongPressTask->iKind == EXTERNAL_HYPERLINK) { + static const std::string HTTP("http://"); + static const std::string HTTPS("https://"); + if (ZLStringUtil::stringStartsWith(iLongPressTask->iLink, HTTP) || + ZLStringUtil::stringStartsWith(iLongPressTask->iLink, HTTPS)) { + QString url(QString::fromStdString(iLongPressTask->iLink)); + Q_EMIT browserLinkPressed(url); + } + } else if (iLongPressTask->iKind == IMAGE) { + static const QString PREFIX("image://"); + QString id(QString::fromStdString(iLongPressTask->iImageId)); + QString url = PREFIX + BooksImageProvider::PROVIDER_ID + "/" + id; + BooksImageProvider::instance()->addImage(iModel, id, + iLongPressTask->iImageData); + Q_EMIT imagePressed(url, iLongPressTask->iRect); } iLongPressTask->release(this); diff --git a/app/src/BooksPageWidget.h b/app/src/BooksPageWidget.h index de8460b..76ba090 100644 --- a/app/src/BooksPageWidget.h +++ b/app/src/BooksPageWidget.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Jolla Ltd. + * Copyright (C) 2015-2016 Jolla Ltd. * Contact: Slava Monich * * You may use this file under the terms of the BSD license as follows: @@ -14,7 +14,7 @@ * 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 + * * Neither the name of 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. * @@ -101,6 +101,7 @@ Q_SIGNALS: void topMarginChanged(); void bottomMarginChanged(); void browserLinkPressed(QString url); + void imagePressed(QString url, QRect rect); private Q_SLOTS: void onWidthChanged(); diff --git a/app/src/ZLibrary.cpp b/app/src/ZLibrary.cpp index 8ab433c..62e5d94 100644 --- a/app/src/ZLibrary.cpp +++ b/app/src/ZLibrary.cpp @@ -35,6 +35,7 @@ #include "BooksStorage.h" #include "BooksPaintContext.h" #include "BooksDialogManager.h" +#include "BooksImageProvider.h" #include "HarbourDebug.h" @@ -191,6 +192,8 @@ void ZLibrary::run(ZLApplication* aApp) QQuickView* view = SailfishApp::createView(); QQmlContext* root = view->rootContext(); + root->engine()->addImageProvider(BooksImageProvider::PROVIDER_ID, + new BooksImageProvider(root)); root->setContextProperty("PointsPerInch", booksPPI); root->setContextProperty("MaximumHintCount", 1); root->setContextProperty("BooksSettingsMenu",