[app] Support for internal hyperlinks
This commit is contained in:
parent
cd3194e1a2
commit
b2d01710e8
8 changed files with 303 additions and 21 deletions
|
@ -160,6 +160,7 @@ SilicaFlickable {
|
|||
titleVisible: _currentState.title
|
||||
pageNumberVisible: _currentState.page
|
||||
title: bookModel.title
|
||||
onJumpToPage: bookView.jumpTo(page)
|
||||
onPageClicked: {
|
||||
root.pageClicked(index)
|
||||
globalSettings.pageDetails = (globalSettings.pageDetails+ 1) % _visibilityStates.length
|
||||
|
|
|
@ -52,6 +52,7 @@ Item {
|
|||
signal pageClicked()
|
||||
signal imagePressed(var url, var rect)
|
||||
signal browserLinkPressed(var url)
|
||||
signal jumpToPage(var page)
|
||||
|
||||
PageWidget {
|
||||
id: widget
|
||||
|
@ -60,6 +61,8 @@ Item {
|
|||
model: bookModel
|
||||
onBrowserLinkPressed: view.browserLinkPressed(url)
|
||||
onImagePressed: view.imagePressed(url, rect)
|
||||
onActiveTouch: pressImage.animate(x, y)
|
||||
onJumpToPage: view.jumpToPage(page)
|
||||
}
|
||||
|
||||
BooksTitleLabel {
|
||||
|
@ -86,6 +89,54 @@ Item {
|
|||
Behavior on opacity {}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: pressImage
|
||||
source: globalSettings.invertColors ? "images/press-invert.svg" : "images/press.svg"
|
||||
visible: opacity > 0
|
||||
opacity: 0
|
||||
readonly property int maxsize: Math.max(view.width, view.height)
|
||||
ParallelAnimation {
|
||||
id: pressAnimation
|
||||
NumberAnimation {
|
||||
target: pressImage
|
||||
easing.type: Easing.InOutQuad
|
||||
properties: "width,height"
|
||||
from: pressImage.sourceSize.width
|
||||
to: pressImage.maxsize
|
||||
}
|
||||
NumberAnimation {
|
||||
id: pressAnimationX
|
||||
target: pressImage
|
||||
easing.type: Easing.InOutQuad
|
||||
properties: "x"
|
||||
}
|
||||
NumberAnimation {
|
||||
id: pressAnimationY
|
||||
target: pressImage
|
||||
easing.type: Easing.InOutQuad
|
||||
properties: "y"
|
||||
}
|
||||
NumberAnimation {
|
||||
target: pressImage
|
||||
easing.type: Easing.InOutQuad
|
||||
properties: "opacity"
|
||||
from: 0.5
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
function animate(x0, y0) {
|
||||
pressAnimation.stop();
|
||||
opacity = 0
|
||||
width = pressImage.sourceSize.width
|
||||
height = pressImage.sourceSize.height
|
||||
pressAnimationX.from = x = x0 - Math.round(width/2)
|
||||
pressAnimationY.from = y = y0 - Math.round(height/2)
|
||||
pressAnimationX.to = x0 - maxsize/2
|
||||
pressAnimationY.to = y0 - maxsize/2
|
||||
pressAnimation.start()
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
|
@ -104,6 +155,7 @@ Item {
|
|||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: view.pageClicked()
|
||||
onPressed: widget.handlePress(mouseX, mouseY)
|
||||
onPressAndHold: widget.handleLongPress(mouseX, mouseY)
|
||||
}
|
||||
}
|
||||
|
|
88
app/qml/images/press-invert.svg
Normal file
88
app/qml/images/press-invert.svg
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100"
|
||||
height="100"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="press-invert.svg">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient3771">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3773" />
|
||||
<stop
|
||||
id="stop3781"
|
||||
offset="0.5"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3779"
|
||||
offset="0.69732136"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3775" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3771"
|
||||
id="radialGradient3777"
|
||||
cx="51.42857"
|
||||
cy="53.57143"
|
||||
fx="51.42857"
|
||||
fy="53.57143"
|
||||
r="30"
|
||||
gradientTransform="matrix(1,0,0,1.0119048,0,-0.63775609)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter3803">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="1.5089022"
|
||||
id="feGaussianBlur3805" />
|
||||
</filter>
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-952.36218)">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="fill:url(#radialGradient3777);fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:6;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;opacity:0.50000000000000000;filter:url(#filter3803)"
|
||||
id="path2985"
|
||||
sodipodi:cx="51.42857"
|
||||
sodipodi:cy="53.57143"
|
||||
sodipodi:rx="30"
|
||||
sodipodi:ry="30.357143"
|
||||
d="m 81.42857,53.57143 a 30,30.357143 0 1 1 -60,0 30,30.357143 0 1 1 60,0 z"
|
||||
transform="matrix(1.6666667,0,0,1.6470588,-35.714283,914.12689)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
88
app/qml/images/press.svg
Normal file
88
app/qml/images/press.svg
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100"
|
||||
height="100"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="press">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient3771">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3773" />
|
||||
<stop
|
||||
id="stop3781"
|
||||
offset="0.5"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3779"
|
||||
offset="0.7"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3775" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3771"
|
||||
id="radialGradient3777"
|
||||
cx="51.42857"
|
||||
cy="53.57143"
|
||||
fx="51.42857"
|
||||
fy="53.57143"
|
||||
r="30"
|
||||
gradientTransform="matrix(1,0,0,1.0119048,0,-0.63775609)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter3803">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="1.5089022"
|
||||
id="feGaussianBlur3805" />
|
||||
</filter>
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-952.36218)">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="fill:url(#radialGradient3777);fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:6;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;opacity:0.50000000000000000;filter:url(#filter3803)"
|
||||
id="path2985"
|
||||
sodipodi:cx="51.42857"
|
||||
sodipodi:cy="53.57143"
|
||||
sodipodi:rx="30"
|
||||
sodipodi:ry="30.357143"
|
||||
d="m 81.42857,53.57143 a 30,30.357143 0 1 1 -60,0 30,30.357143 0 1 1 60,0 z"
|
||||
transform="matrix(1.6666667,0,0,1.6470588,-35.714283,914.12689)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -350,6 +350,17 @@ BooksPos BooksBookModel::pageMark(int aPage) const
|
|||
return BooksPos();
|
||||
}
|
||||
|
||||
int BooksBookModel::linkToPage(const std::string& aLink) const
|
||||
{
|
||||
if (iData && !iData->iBookModel.isNull()) {
|
||||
BookModel::Label label = iData->iBookModel->label(aLink);
|
||||
if (label.ParagraphNumber >= 0) {
|
||||
return iData->pickPage(BooksPos(label.ParagraphNumber, 0, 0));
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
shared_ptr<BookModel> BooksBookModel::bookModel() const
|
||||
{
|
||||
return iData ? iData->iBookModel : NULL;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Jolla Ltd.
|
||||
* Copyright (C) 2015-2016 Jolla Ltd.
|
||||
* Contact: Slava Monich <slava.monich@jolla.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
|
@ -125,6 +125,7 @@ public:
|
|||
shared_ptr<ZLTextModel> bookTextModel() const;
|
||||
shared_ptr<ZLTextModel> contentsModel() const;
|
||||
shared_ptr<ZLTextStyle> textStyle() const { return iTextStyle; }
|
||||
int linkToPage(const std::string& aLink) const;
|
||||
int fontSizeAdjust() const;
|
||||
|
||||
// QAbstractListModel
|
||||
|
|
|
@ -157,12 +157,12 @@ void BooksPageWidget::RenderTask::performTask()
|
|||
}
|
||||
|
||||
// ==========================================================================
|
||||
// BooksPageWidget::LongPressTask
|
||||
// BooksPageWidget::PressTask
|
||||
// ==========================================================================
|
||||
|
||||
class BooksPageWidget::LongPressTask : public BooksTask {
|
||||
class BooksPageWidget::PressTask : public BooksTask {
|
||||
public:
|
||||
LongPressTask(shared_ptr<BooksPageWidget::Data> aData, int aX, int aY) :
|
||||
PressTask(shared_ptr<BooksPageWidget::Data> aData, int aX, int aY) :
|
||||
iData(aData), iX(aX), iY(aY), iKind(REGULAR) {}
|
||||
|
||||
void performTask();
|
||||
|
@ -179,7 +179,7 @@ public:
|
|||
shared_ptr<ZLImageData> iImageData;
|
||||
};
|
||||
|
||||
void BooksPageWidget::LongPressTask::performTask()
|
||||
void BooksPageWidget::PressTask::performTask()
|
||||
{
|
||||
if (!isCanceled()) {
|
||||
const BooksTextView& view = *iData->iView;
|
||||
|
@ -222,7 +222,7 @@ void BooksPageWidget::LongPressTask::performTask()
|
|||
iKind = link.kind();
|
||||
iLink = link.label();
|
||||
iLinkType = link.hyperlinkType();
|
||||
HDEBUG("link" << iLink.c_str());
|
||||
HDEBUG("link" << kind << iLink.c_str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -261,6 +261,7 @@ BooksPageWidget::BooksPageWidget(QQuickItem* aParent) :
|
|||
iSettings(NULL),
|
||||
iResetTask(NULL),
|
||||
iRenderTask(NULL),
|
||||
iPressTask(NULL),
|
||||
iLongPressTask(NULL),
|
||||
iEmpty(false),
|
||||
iPage(-1)
|
||||
|
@ -279,6 +280,7 @@ BooksPageWidget::~BooksPageWidget()
|
|||
HDEBUG("page" << iPage);
|
||||
if (iResetTask) iResetTask->release(this);
|
||||
if (iRenderTask) iRenderTask->release(this);
|
||||
if (iPressTask) iPressTask->release(this);
|
||||
if (iLongPressTask) iLongPressTask->release(this);
|
||||
}
|
||||
|
||||
|
@ -559,23 +561,49 @@ void BooksPageWidget::onRenderTaskDone()
|
|||
update();
|
||||
}
|
||||
|
||||
void BooksPageWidget::onPressTaskDone()
|
||||
{
|
||||
HASSERT(sender() == iPressTask);
|
||||
HDEBUG(iPressTask->iKind);
|
||||
|
||||
PressTask* task = iPressTask;
|
||||
iPressTask = NULL;
|
||||
|
||||
if (task->iKind != REGULAR) {
|
||||
Q_EMIT activeTouch(task->iX, task->iY);
|
||||
}
|
||||
|
||||
task->release(this);
|
||||
}
|
||||
|
||||
void BooksPageWidget::onLongPressTaskDone()
|
||||
{
|
||||
HASSERT(sender() == iLongPressTask);
|
||||
HDEBUG(iLongPressTask->iKind);
|
||||
|
||||
if (iLongPressTask->iKind == EXTERNAL_HYPERLINK) {
|
||||
PressTask* task = iLongPressTask;
|
||||
iLongPressTask = NULL;
|
||||
|
||||
if (task->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));
|
||||
if (ZLStringUtil::stringStartsWith(task->iLink, HTTP) ||
|
||||
ZLStringUtil::stringStartsWith(task->iLink, HTTPS)) {
|
||||
QString url(QString::fromStdString(task->iLink));
|
||||
Q_EMIT browserLinkPressed(url);
|
||||
}
|
||||
} else if (iLongPressTask->iKind == IMAGE) {
|
||||
} else if (task->iKind == INTERNAL_HYPERLINK) {
|
||||
if (iModel) {
|
||||
int page = iModel->linkToPage(task->iLink);
|
||||
if (page >= 0) {
|
||||
HDEBUG("link to page" << page);
|
||||
Q_EMIT jumpToPage(page);
|
||||
}
|
||||
}
|
||||
} else if (task->iKind == IMAGE) {
|
||||
// Make sure that the book path is mixed into the image id to handle
|
||||
// the case of different books having images with identical ids
|
||||
QString id = QString::fromStdString(iLongPressTask->iImageId);
|
||||
QString id = QString::fromStdString(task->iImageId);
|
||||
QString path;
|
||||
if (iModel) {
|
||||
BooksBook* book = iModel->book();
|
||||
|
@ -592,13 +620,11 @@ void BooksPageWidget::onLongPressTaskDone()
|
|||
}
|
||||
static const QString IMAGE_URL("image://%1/%2");
|
||||
QString url = IMAGE_URL.arg(BooksImageProvider::PROVIDER_ID, id);
|
||||
BooksImageProvider::instance()->addImage(iModel, id,
|
||||
iLongPressTask->iImageData);
|
||||
Q_EMIT imagePressed(url, iLongPressTask->iRect);
|
||||
BooksImageProvider::instance()->addImage(iModel, id, task->iImageData);
|
||||
Q_EMIT imagePressed(url, task->iRect);
|
||||
}
|
||||
|
||||
iLongPressTask->release(this);
|
||||
iLongPressTask = NULL;
|
||||
task->release(this);
|
||||
}
|
||||
|
||||
void BooksPageWidget::updateSize()
|
||||
|
@ -640,7 +666,17 @@ void BooksPageWidget::handleLongPress(int aX, int aY)
|
|||
HDEBUG(aX << aY);
|
||||
if (!iResetTask && !iRenderTask && !iData.isNull()) {
|
||||
if (iLongPressTask) iLongPressTask->release(this);
|
||||
iLongPressTask = new LongPressTask(iData, aX, aY);
|
||||
iLongPressTask = new PressTask(iData, aX, aY);
|
||||
iTaskQueue->submit(iLongPressTask, this, SLOT(onLongPressTaskDone()));
|
||||
}
|
||||
}
|
||||
|
||||
void BooksPageWidget::handlePress(int aX, int aY)
|
||||
{
|
||||
HDEBUG(aX << aY);
|
||||
if (!iResetTask && !iRenderTask && !iData.isNull()) {
|
||||
if (iPressTask) iPressTask->release(this);
|
||||
iPressTask = new PressTask(iData, aX, aY);
|
||||
iTaskQueue->submit(iPressTask, this, SLOT(onPressTaskDone()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ public:
|
|||
BooksMargins margins() const { return iMargins; }
|
||||
|
||||
Q_INVOKABLE void handleLongPress(int aX, int aY);
|
||||
Q_INVOKABLE void handlePress(int aX, int aY);
|
||||
|
||||
Q_SIGNALS:
|
||||
void loadingChanged();
|
||||
|
@ -102,6 +103,8 @@ Q_SIGNALS:
|
|||
void bottomMarginChanged();
|
||||
void browserLinkPressed(QString url);
|
||||
void imagePressed(QString url, QRect rect);
|
||||
void activeTouch(int x, int y);
|
||||
void jumpToPage(int page);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onWidthChanged();
|
||||
|
@ -115,6 +118,7 @@ private Q_SLOTS:
|
|||
void onInvertColorsChanged();
|
||||
void onResetTaskDone();
|
||||
void onRenderTaskDone();
|
||||
void onPressTaskDone();
|
||||
void onLongPressTaskDone();
|
||||
|
||||
private:
|
||||
|
@ -128,7 +132,7 @@ private:
|
|||
private:
|
||||
class ResetTask;
|
||||
class RenderTask;
|
||||
class LongPressTask;
|
||||
class PressTask;
|
||||
|
||||
shared_ptr<BooksTaskQueue> iTaskQueue;
|
||||
shared_ptr<ZLTextStyle> iTextStyle;
|
||||
|
@ -141,7 +145,8 @@ private:
|
|||
shared_ptr<QImage> iImage;
|
||||
ResetTask* iResetTask;
|
||||
RenderTask* iRenderTask;
|
||||
LongPressTask* iLongPressTask;
|
||||
PressTask* iPressTask;
|
||||
PressTask* iLongPressTask;
|
||||
bool iEmpty;
|
||||
int iPage;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue