ubuntu-touch-coreapps-reviewers team mailing list archive
-
ubuntu-touch-coreapps-reviewers team
-
Mailing list archive
-
Message #04253
[Merge] lp:~verzegnassi-stefano/ubuntu-docviewer-app/reboot-lok-zoom into lp:ubuntu-docviewer-app/reboot
Stefano Verzegnassi has proposed merging lp:~verzegnassi-stefano/ubuntu-docviewer-app/reboot-lok-zoom into lp:ubuntu-docviewer-app/reboot.
Commit message:
Added zooming support (two modes: automatic and manual)
Requested reviews:
Ubuntu Document Viewer Developers (ubuntu-docviewer-dev)
For more details, see:
https://code.launchpad.net/~verzegnassi-stefano/ubuntu-docviewer-app/reboot-lok-zoom/+merge/267982
Added zooming support (two modes: automatic and manual)
For who will review this MP: please pay attention on invalidation/removal of tiles, which is probably the most "critical" piece of code added with this MP.
UI Design:
TBD, I hope to get some feedback from the UX team.
A bottom panel has been added in the loView page, and it automatically disappear following the behaviour of the page header. This seems a good concept on desktop/tablet, but it may require some change when running on a phone.
The code of the ZoomSelector (QML component) does not look good, I know, but it's anyway good for prototyping. Since that component could even be replaced in future (we don't know yet), I decided not to improve it.
--
Your team Ubuntu Document Viewer Developers is requested to review the proposed merge of lp:~verzegnassi-stefano/ubuntu-docviewer-app/reboot-lok-zoom into lp:ubuntu-docviewer-app/reboot.
=== modified file 'src/app/main.cpp'
--- src/app/main.cpp 2015-03-03 16:49:48 +0000
+++ src/app/main.cpp 2015-08-13 18:21:53 +0000
@@ -19,6 +19,7 @@
// Uncomment if you need to use QML analyzer
// #define QT_QML_DEBUG
+// #include <QtQuick>
#include "docviewer-application.h"
#include <QDebug>
=== added file 'src/app/qml/loView/BottomPanel.qml'
--- src/app/qml/loView/BottomPanel.qml 1970-01-01 00:00:00 +0000
+++ src/app/qml/loView/BottomPanel.qml 2015-08-13 18:21:53 +0000
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import Ubuntu.Components.ListItems 1.0 as ListItems
+
+Panel {
+ id: bottomPanel
+ anchors {
+ left: parent.left
+ bottom: parent.bottom
+ right: parent.right
+ }
+
+ readonly property bool shouldBeVisible: loPage.header.y > -1
+ onShouldBeVisibleChanged: {
+ if (shouldBeVisible)
+ bottomPanel.open()
+ else
+ bottomPanel.close()
+ }
+
+ height: units.gu(6)
+
+ Rectangle {
+ anchors.fill: parent
+ color: "white"
+
+ ListItems.ThinDivider {
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ }
+ }
+ }
+}
=== modified file 'src/app/qml/loView/LOView.qml'
--- src/app/qml/loView/LOView.qml 2015-07-04 16:00:33 +0000
+++ src/app/qml/loView/LOView.qml 2015-08-13 18:21:53 +0000
@@ -27,7 +27,7 @@
// Disable header auto-hide.
// TODO: Show/hide header if a user taps the page
- flickable: null
+ flickable: loView
// TRANSLATORS: the first argument (%1) refers to the page currently shown on the screen,
// while the second one (%2) refers to the total pages count.
@@ -72,6 +72,29 @@
}*/
}
+ BottomPanel {
+ id: bottomPanel
+
+ // TODO: if we'll still be using a bottom panel, when we'll switch to
+ // UITK1.3, we could use the ActionBar component.
+ Row {
+ anchors.fill: parent
+ layoutDirection: Qt.RightToLeft
+
+ PanelButton {
+ iconName: "zoom-out"
+ onClicked: loView.zoomFactor -= 0.1
+ }
+
+ PanelButton {
+ iconName: "zoom-in"
+ onClicked: loView.zoomFactor += 0.1
+ }
+
+ ZoomSelector {}
+ }
+ }
+
// *** HEADER ***
state: "default"
states: LOViewDefaultHeader {
=== modified file 'src/app/qml/loView/LOViewDefaultHeader.qml'
--- src/app/qml/loView/LOViewDefaultHeader.qml 2015-06-26 14:56:23 +0000
+++ src/app/qml/loView/LOViewDefaultHeader.qml 2015-08-13 18:21:53 +0000
@@ -90,7 +90,6 @@
Action {
iconName: "search"
// onTriggered: pageMain.state = "search"
- //Disable it until we provide search in Poppler plugin.
enabled: false
},
@@ -98,7 +97,7 @@
objectName:"gotopage"
iconName: "browser-tabs"
text: i18n.tr("Go to page...")
- onTriggered: PopupUtils.open(Qt.resolvedUrl("LOViewGotoDialog.qml"), targetPage)
+ enabled: false
},
Action {
=== removed file 'src/app/qml/loView/LOViewDelegate.qml'
--- src/app/qml/loView/LOViewDelegate.qml 2015-06-24 12:04:16 +0000
+++ src/app/qml/loView/LOViewDelegate.qml 1970-01-01 00:00:00 +0000
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2013-2015 Canonical, Ltd.
- *
- * 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; version 3.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-import QtQuick 2.3
-import Ubuntu.Components 1.1
-
-Rectangle {
- id: loPage
-
- property int index: model.index
- property bool _previewFetched: false
-
- property alias status: pageImg.status
-
- width: parent.width
- height: width * (model.height / model.width)
- color: "#E6E6E6"
-
- // Preview page rendering. Used as placeholder while zooming the page.
- // We generate the low resolution preview from the texture of the PDF page,
- // so that we can keep page rendering as fast as possible.
- ShaderEffectSource {
- id: previewImg
- anchors.fill: parent
-
- // We cannot change its opacity or visibility, otherwise the texture will be refreshed,
- // even if live is false.
- live: false
- textureSize: Qt.size(256, 256 * (model.height / model.width))
- }
-
- Image {
- id: pageImg
- anchors.fill: parent
-
- source: "image://libreoffice/page/" + index;
- sourceSize.width: loPage.width
-
- onStatusChanged: {
- // This is supposed to run the first time PdfViewDelegate gets the page rendering.
- if (!_previewFetched) {
- if (status == Image.Ready) {
- previewImg.sourceItem = pageImg
- // Re-assign sourceItem property, so the texture is not updated when Image status changes.
- previewImg.sourceItem = loPage
- }
- }
- }
-
- // Request a new page rendering. The order, which pages are requested with, depends on the distance from the currentPage
- Timer {
- id: _zoomTimer
- interval: {
- var diff = Math.abs(loView.currentPageIndex - model.index)
- var prov = loDocument.providersNumber * 0.5
-
- if (diff < prov)
- return 0
- else
- return (diff - prov) * 10
- }
-
- onTriggered: {
- pageImg.sourceSize.width = loPage.width;
- }
- }
- }
-
- // Page rendering depends on the width of PdfViewDelegate.
- // Because of this, we have multiple callings to ImageProvider while zooming.
- // Just avoid it.
- Connections {
- target: pinchy
-
- onPinchStarted: _zoomTimer.stop();
- onPinchUpdated: {
- // This ensures that page image is not reloaded when the maximumScale or minimumScale has already been reached.
- if ( !(_zoomHelper.scale >= 2.5 && pinch.scale > 1.0) && !(_zoomHelper.scale <= 1.0 && pinch.scale < 1.0) )
- pageImg.sourceSize.width = 0;
- }
- onPinchFinished: _zoomTimer.restart();
- }
-}
=== removed file 'src/app/qml/loView/LOViewGotoDialog.qml'
--- src/app/qml/loView/LOViewGotoDialog.qml 2015-06-24 12:04:16 +0000
+++ src/app/qml/loView/LOViewGotoDialog.qml 1970-01-01 00:00:00 +0000
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2014-2015 Canonical, Ltd.
- *
- * 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; version 3.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-import QtQuick 2.3
-import Ubuntu.Components 1.1
-import Ubuntu.Components.Popups 1.0
-
-Dialog {
- id: goToPageDialog
- objectName:"LOViewGotoDialog"
-
- title: i18n.tr("Go to page")
- text: i18n.tr("Choose a page between 1 and %1").arg(loView.count)
-
- TextField {
- id: goToPageTextField
- objectName:"goToPageTextField"
-
- width: parent.width
-
- hasClearButton: true
- inputMethodHints: Qt.ImhFormattedNumbersOnly
- validator: IntValidator{ bottom: 1; top: loView.count }
-
- Keys.onReturnPressed: goToPage()
- Component.onCompleted: forceActiveFocus()
- }
-
- Button {
- objectName:"GOButton"
- text: i18n.tr("GO!")
- color: UbuntuColors.green
-
- enabled: goToPageTextField.acceptableInput
- onClicked: goToPage()
- }
-
- Button {
- text: i18n.tr("Cancel")
- onClicked: PopupUtils.close(goToPageDialog)
- }
-
- function goToPage() {
- loView.positionAtIndex((goToPageTextField.text - 1))
- PopupUtils.close(goToPageDialog)
- }
-}
=== added file 'src/app/qml/loView/PanelButton.qml'
--- src/app/qml/loView/PanelButton.qml 1970-01-01 00:00:00 +0000
+++ src/app/qml/loView/PanelButton.qml 2015-08-13 18:21:53 +0000
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+
+AbstractButton {
+ width: units.gu(4); height: parent.height
+
+ property alias iconName: icon.name
+ property alias iconSource: icon.source
+
+ Icon {
+ id: icon
+ anchors.centerIn: parent
+ width: units.gu(2.5); height: width
+ }
+}
=== added file 'src/app/qml/loView/ZoomSelector.qml'
--- src/app/qml/loView/ZoomSelector.qml 1970-01-01 00:00:00 +0000
+++ src/app/qml/loView/ZoomSelector.qml 2015-08-13 18:21:53 +0000
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import Ubuntu.Components.Popups 1.0
+import DocumentViewer.LibreOffice 1.0 as LibreOffice
+
+TextField {
+ id: textField
+ anchors.verticalCenter: parent.verticalCenter
+ width: units.gu(24)
+
+ property var values: ["auto", 0.5, 0.7, 0.85, 1.0, 1.25, 1.5, 1.75, 2.0, 3.0, 4.0]
+ property var labels: [
+ i18n.tr("Automatic (Fit width)"),
+ "50%", "70%", "85%", "100%", "125%",
+ "150%", "175%","200%", "300%", "400%"
+ ]
+
+ property bool expanded: false
+
+ hasClearButton: true
+ inputMethodHints: Qt.ImhFormattedNumbersOnly
+ validator: IntValidator{ bottom: 50; top: 400 }
+
+ onAccepted: {
+ loView.zoomFactor = parseInt(text) / 100
+ focus = false
+ }
+
+ secondaryItem: AbstractButton {
+ visible: !textField.highlighted
+ id: listButton
+ height: parent.height
+ width: visible ? height : 0
+
+ Rectangle {
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ height: parent.height - units.gu(1)
+
+ width: units.dp(1)
+ color: "Grey"
+ opacity: 0.5
+ }
+
+ Icon {
+ id: _upArrow
+
+ width: units.gu(2)
+ height: width
+ anchors.centerIn: parent
+
+ name: "go-down"
+ color: "Grey"
+ rotation: textField.expanded ? 180 : 0
+
+ Behavior on rotation {
+ UbuntuNumberAnimation {}
+ }
+ }
+
+ onClicked: {
+ textField.expanded = true
+ PopupUtils.open(zoomSelectorDialog, listButton)
+ }
+ }
+
+ onHighlightedChanged: {
+ if (highlighted) {
+ text = parseInt(loView.zoomFactor * 100)
+ } else text = ""
+ }
+
+ Label {
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ anchors.leftMargin: units.gu(2)
+ visible: !textField.highlighted
+ text: loView.zoomMode == LibreOffice.View.FitToWidth ? i18n.tr("Automatic (%1%)").arg(parseInt(loView.zoomFactor*100))
+ : i18n.tr("Zoom: %1%").arg(parseInt(loView.zoomFactor*100))
+ }
+
+ Component {
+ id: zoomSelectorDialog
+ Popover {
+ id: zoomSelectorDialogue
+ autoClose: false
+ contentHeight: units.gu(24)
+ contentWidth: units.gu(24)
+ Component.onDestruction: textField.expanded = false
+
+ // We don't use 'autoClose' property, since we want to propagate
+ // mouse/touch events to other items (e.g. when zoomSelectorDialogue
+ // is visible, and user taps the zoom+ button on its right, we want
+ // the zoom button to receive the event).
+ InverseMouseArea {
+ anchors.fill: parent
+ propagateComposedEvents: true
+
+ onPressed: {
+ mouse.accepted = false
+ PopupUtils.close(zoomSelectorDialogue)
+ }
+ }
+
+ OptionSelector {
+ expanded: true
+ width: parent.width
+ containerHeight: units.gu(24)
+ model: textField.labels
+ selectedIndex: {
+ if (loView.zoomMode == LibreOffice.View.FitToWidth)
+ return 0
+
+ for (var i=0; i<textField.values.length; i++) {
+ if (values[i] == loView.zoomFactor)
+ return i
+ }
+ return -1
+ }
+
+ onSelectedIndexChanged: {
+ if (selectedIndex == 0) {
+ loView.adjustZoomToWidth();
+ return;
+ }
+
+ loView.zoomFactor = textField.values[selectedIndex]
+ PopupUtils.close(zoomSelectorDialogue)
+ }
+ }
+ }
+ }
+}
=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/LOViewer.qml'
--- src/plugin/libreofficetoolkit-qml-plugin/LOViewer.qml 2015-07-26 17:33:51 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/LOViewer.qml 2015-08-13 18:21:53 +0000
@@ -23,12 +23,22 @@
property alias document: view.document
property alias zoomFactor: view.zoomFactor
property alias cacheBuffer: view.cacheBuffer
-
- contentHeight: view.height * view.zoomFactor
- contentWidth: view.width * view.zoomFactor
+ property alias zoomMode: view.zoomMode
+
+ function adjustZoomToWidth()
+ {
+ view.adjustZoomToWidth();
+ }
+
+ // zoomFactor is not used here to set contentSize, since it's all managed
+ // internally, in the LibreOffice.View component.
+ contentHeight: view.height
+ contentWidth: view.width
boundsBehavior: Flickable.StopAtBounds
+ Component.onCompleted: adjustZoomToWidth()
+
LibreOffice.View {
id: view
=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/lodocument.cpp'
--- src/plugin/libreofficetoolkit-qml-plugin/lodocument.cpp 2015-07-23 01:05:20 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/lodocument.cpp 2015-08-13 18:21:53 +0000
@@ -102,15 +102,15 @@
// Paint a tile, with size=canvasSize, of the part of the document defined by
// the rect tileSize.
-QImage LODocument::paintTile(QSize canvasSize, QRect tileSize)
+QImage LODocument::paintTile(QSize canvasSize, QRect tileSize, qreal zoom)
{
QImage result = QImage(canvasSize.width(), canvasSize.height(), QImage::Format_RGB32);
m_document->paintTile(result.bits(),
canvasSize.width(), canvasSize.height(),
- Twips::convertPixelsToTwips(tileSize.x()),
- Twips::convertPixelsToTwips(tileSize.y()),
- Twips::convertPixelsToTwips(tileSize.width()),
- Twips::convertPixelsToTwips(tileSize.height()));
+ Twips::convertPixelsToTwips(tileSize.x(), zoom),
+ Twips::convertPixelsToTwips(tileSize.y(), zoom),
+ Twips::convertPixelsToTwips(tileSize.width(), zoom),
+ Twips::convertPixelsToTwips(tileSize.height(), zoom));
return result.rgbSwapped();
}
=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/lodocument.h'
--- src/plugin/libreofficetoolkit-qml-plugin/lodocument.h 2015-07-23 01:05:20 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/lodocument.h 2015-08-13 18:21:53 +0000
@@ -52,7 +52,7 @@
DocumentType documentType() const;
QSize documentSize() const;
- QImage paintTile(QSize canvasSize, QRect tileSize);
+ QImage paintTile(QSize canvasSize, QRect tileSize, qreal zoom = 1.0);
Q_INVOKABLE bool saveAs(QString url, QString format, QString filterOptions);
=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/loview.cpp'
--- src/plugin/libreofficetoolkit-qml-plugin/loview.cpp 2015-07-26 17:33:51 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/loview.cpp 2015-08-13 18:21:53 +0000
@@ -28,6 +28,13 @@
// TODO: Use a QQuickItem and implement painting through
// updatePaintNode(QSGNode * oldNode, UpdatePaintNodeData * data)
+static qreal zoomValueToFitWidth;
+
+static qreal getZoomToFitWidth(const qreal &width, int documentWidth)
+{
+ return qreal(width / Twips::convertTwipsToPixels(documentWidth, 1.0));
+}
+
LOView::LOView(QQuickItem *parent)
: QQuickPaintedItem(parent)
, m_parentFlickable(nullptr)
@@ -38,6 +45,11 @@
, m_bufferArea(0, 0, 0, 0)
{
Q_UNUSED(parent)
+ m_updateTimer.setSingleShot(true);
+
+ // FIXME: TESTING
+ setOpaquePainting(true);
+ setFillColor(Qt::white);
connect(this, SIGNAL(documentChanged()), this, SLOT(updateViewSize()));
connect(this, SIGNAL(zoomFactorChanged()), this, SLOT(updateViewSize()));
@@ -100,22 +112,38 @@
Q_EMIT documentChanged();
}
-// Not used yet.
qreal LOView::zoomFactor() const
{
return m_zoomFactor;
}
-// Not used yet.
-void LOView::setZoomFactor(qreal zoom)
+void LOView::setZoomFactor(const qreal zoom)
{
if (m_zoomFactor == zoom)
return;
m_zoomFactor = zoom;
+
+ if (this->zoomFactor() != zoomValueToFitWidth)
+ this->setZoomMode(LOView::Manual);
+
Q_EMIT zoomFactorChanged();
}
+LOView::ZoomMode LOView::zoomMode() const
+{
+ return m_zoomMode;
+}
+
+void LOView::setZoomMode(const ZoomMode zoomMode)
+{
+ if (m_zoomMode == zoomMode)
+ return;
+
+ m_zoomMode = zoomMode;
+ Q_EMIT zoomModeChanged();
+}
+
int LOView::cacheBuffer() const
{
return m_cacheBuffer;
@@ -130,6 +158,37 @@
Q_EMIT cacheBufferChanged();
}
+void LOView::adjustZoomToWidth()
+{
+ this->setZoomMode(LOView::FitToWidth);
+
+ zoomValueToFitWidth = getZoomToFitWidth(parentFlickable()->width(),
+ document()->documentSize().width());
+
+ this->setZoomFactor(zoomValueToFitWidth);
+ qDebug() << "Adjust zoom to width - value:" << zoomValueToFitWidth;
+}
+
+bool LOView::updateZoomIfAutomatic()
+{
+ // This function is only used in LOView::updateVisibleRect()
+ // It returns a bool, so that we can stop the execution of that function,
+ // which will be triggered again when we'll automatically update the zoom value.
+ if (this->zoomMode() == LOView::FitToWidth) {
+ zoomValueToFitWidth = getZoomToFitWidth(parentFlickable()->width(),
+ document()->documentSize().width());
+
+ if (this->zoomFactor() != zoomValueToFitWidth) {
+ this->setZoomFactor(zoomValueToFitWidth);
+
+ qDebug() << "Adjust automatic zoom to width - value:" << zoomValueToFitWidth;
+ return true;
+ }
+ }
+
+ return false;
+}
+
// Update the size of LOView, according to the size of the loaded document.
void LOView::updateViewSize()
{
@@ -139,8 +198,8 @@
QSize docSize = m_document->documentSize();
// FIXME: Area may become too large, resulting in a black texture.
- this->setWidth(Twips::convertTwipsToPixels(docSize.width()) * m_zoomFactor);
- this->setHeight(Twips::convertTwipsToPixels(docSize.height()) * m_zoomFactor);
+ this->setWidth(Twips::convertTwipsToPixels(docSize.width(), m_zoomFactor));
+ this->setHeight(Twips::convertTwipsToPixels(docSize.height(), m_zoomFactor));
// TODO: Consider to use connections to widthChanged and heightChanged
this->updateVisibleRect();
@@ -154,6 +213,30 @@
if (!m_parentFlickable)
return;
+ // Changes in parentFlickable width/height trigger directly LOView::updateVisibleRect(),
+ // since they don't imply a change in the zoom factor - i.e. LOView::updateViewSize().
+ // Anyway, this class also handle an automatic zoom when the parentFlickable has been
+ // resized, so we need to take care of it.
+ // updateZoomIfAutomatic() returns a bool, which is true when the zoomFactor is
+ // set to a new value.
+ // If that happens, stop the execution of this function, since the change of
+ // zoomFactor will trigger the updateViewSize() function, which triggers this
+ // function again.
+ if (this->updateZoomIfAutomatic())
+ return;
+
+ // Check if current tiles have a different zoom value
+ if (!m_tiles.isEmpty()) {
+ TileItem* tile = m_tiles.first();
+
+ if (tile->zoomFactor() != this->zoomFactor()) {
+ m_tiles.clear();
+#ifdef DEBUG_VERBOSE
+ qDebug() << "Zoom value of tiles is different than the current zoom value. Erasing cache...";
+#endif
+ }
+ }
+
// Update information about the visible area
m_visibleArea.setRect(m_parentFlickable->property("contentX").toInt(),
m_parentFlickable->property("contentY").toInt(),
@@ -194,7 +277,7 @@
int visiblesToWidth = qCeil(qreal(m_visibleArea.right()) / TILE_SIZE);
int visiblesToHeight = qCeil(qreal(m_visibleArea.bottom()) / TILE_SIZE);
- // Get indexes for tiles in the visible area
+ // Get indexes for tiles in the buffer area
int bufferFromWidth = int(m_bufferArea.left() / TILE_SIZE);
int bufferFromHeight = int(m_bufferArea.top() / TILE_SIZE);
int bufferToWidth = qCeil(qreal(m_bufferArea.right()) / TILE_SIZE);
@@ -223,7 +306,7 @@
qDebug() << "Creating tile indexed as" << index;
#endif
- auto tile = new TileItem(rect, m_document);
+ auto tile = new TileItem(rect, m_document, m_zoomFactor);
connect(tile, SIGNAL(textureChanged()), this, SLOT(update()));
tile->requestTexture();
@@ -232,7 +315,7 @@
}
#ifdef DEBUG_VERBOSE
else {
- qDebug() << "tile" << x << "x" << y << "already exists";
+ qDebug() << "tile" << index << "already exists";
}
#endif
}
=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/loview.h'
--- src/plugin/libreofficetoolkit-qml-plugin/loview.h 2015-07-26 17:33:51 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/loview.h 2015-08-13 18:21:53 +0000
@@ -26,17 +26,23 @@
class LOView : public QQuickPaintedItem
{
Q_OBJECT
+ Q_ENUMS(ZoomMode)
Q_PROPERTY(QQuickItem* parentFlickable READ parentFlickable WRITE setParentFlickable NOTIFY parentFlickableChanged)
Q_PROPERTY(LODocument* document READ document WRITE setDocument NOTIFY documentChanged)
- // TODO: Implement zoom!
Q_PROPERTY(qreal zoomFactor READ zoomFactor WRITE setZoomFactor NOTIFY zoomFactorChanged)
+ Q_PROPERTY(ZoomMode zoomMode READ zoomMode NOTIFY zoomModeChanged)
Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged)
public:
LOView(QQuickItem *parent = 0);
~LOView();
+ enum ZoomMode {
+ FitToWidth,
+ Manual
+ };
+
void paint(QPainter *painter);
QQuickItem* parentFlickable() const;
@@ -46,15 +52,20 @@
void setDocument(LODocument* doc);
qreal zoomFactor() const;
- void setZoomFactor(qreal zoom);
+ void setZoomFactor(const qreal zoom);
+
+ ZoomMode zoomMode() const;
int cacheBuffer() const;
void setCacheBuffer(int cacheBuffer);
+ Q_INVOKABLE void adjustZoomToWidth();
+
Q_SIGNALS:
void parentFlickableChanged();
void documentChanged();
void zoomFactorChanged();
+ void zoomModeChanged();
void cacheBufferChanged();
private Q_SLOTS:
@@ -67,6 +78,7 @@
LODocument* m_document;
qreal m_zoomFactor;
+ ZoomMode m_zoomMode;
int m_cacheBuffer;
QRect m_visibleArea;
@@ -76,6 +88,9 @@
QMap<int, TileItem*> m_tiles;
+ void setZoomMode(const ZoomMode zoomMode);
+ bool updateZoomIfAutomatic();
+
void generateTiles(int x1, int y1, int x2, int y2, int tilesPerWidth);
void createTile(int index, QRect rect);
};
=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/tileitem.cpp'
--- src/plugin/libreofficetoolkit-qml-plugin/tileitem.cpp 2015-07-22 16:44:39 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/tileitem.cpp 2015-08-13 18:21:53 +0000
@@ -21,6 +21,7 @@
#include "config.h"
#include <QThreadPool>
+#include <QMutex>
#include <QDebug>
#ifdef DEBUG_TILE_BENCHMARK
@@ -31,17 +32,21 @@
* TileItem class *
******************/
-TileItem::TileItem(const QRect &area, LODocument *document)
- : m_painted(false)
+TileItem::TileItem(const QRect &area, LODocument *document, const qreal &zoom)
+ : m_zoomFactor(1.0)
+ , m_painted(false)
, m_document(nullptr)
{
this->setArea(area);
+ this->setZoomFactor(zoom);
this->setDocument(document);
}
// Destructor
TileItem::~TileItem()
{
+ delete m_task;
+
this->releaseTexture();
}
@@ -64,6 +69,20 @@
return m_texture;
}
+qreal TileItem::zoomFactor() const
+{
+ return m_zoomFactor;
+}
+
+void TileItem::setZoomFactor(const qreal &zoom)
+{
+ if (m_zoomFactor == zoom)
+ return;
+
+ m_zoomFactor = zoom;
+ Q_EMIT zoomFactorChanged();
+}
+
bool TileItem::isPainted() const
{
return m_painted;
@@ -94,11 +113,11 @@
void TileItem::requestTexture()
{
- auto task = new RenderTask(this->area(), this->document());
- connect(task, SIGNAL(renderCompleted(QImage)), this, SLOT(updateTexture(QImage)));
+ m_task = new RenderTask(this->area(), this->document(), this->zoomFactor());
+ connect(m_task, SIGNAL(renderCompleted(QImage)), this, SLOT(updateTexture(QImage)), Qt::QueuedConnection);
- task->setAutoDelete(true);
- QThreadPool::globalInstance()->start(task);
+ m_task->setAutoDelete(true);
+ QThreadPool::globalInstance()->start(m_task);
}
// Free memory used by the texture
@@ -111,6 +130,14 @@
Q_EMIT textureChanged();
}
+void TileItem::cancelRenderTask()
+{
+ if (!m_task)
+ return;
+
+ QMetaObject::invokeMethod(m_task, "cancel", Qt::QueuedConnection);
+}
+
// This is a slot, connect to renderCompleted() signal from RenderTask class.
void TileItem::updateTexture(QImage t)
{
@@ -122,13 +149,14 @@
* RenderTask class *
********************/
-RenderTask::RenderTask(const QRect &area, LODocument* document)
+RenderTask::RenderTask(const QRect &area, LODocument* document, const qreal &zoom)
+ : m_aborted(false)
{
this->setArea(area);
+ this->setZoomFactor(zoom);
this->setDocument(document);
}
-
QRect RenderTask::area() const
{
return m_area;
@@ -143,6 +171,20 @@
Q_EMIT areaChanged();
}
+qreal RenderTask::zoomFactor() const
+{
+ return m_zoomFactor;
+}
+
+void RenderTask::setZoomFactor(const qreal &zoom)
+{
+ if (m_zoomFactor == zoom)
+ return;
+
+ m_zoomFactor = zoom;
+ Q_EMIT zoomFactorChanged();
+}
+
LODocument* RenderTask::document() const
{
return m_document;
@@ -160,13 +202,24 @@
// Render the texture for this tile.
void RenderTask::run()
{
+ QMutex mutex;
+ mutex.lock();
+
+ if (m_aborted) {
+ mutex.unlock();
+ return;
+ }
+
+ mutex.unlock();
+
#ifdef DEBUG_TILE_BENCHMARK
QElapsedTimer renderTimer;
renderTimer.start();
#endif
QImage render = this->document()->paintTile(this->area().size(),
- this->area());
+ this->area(),
+ this->zoomFactor());
Q_EMIT renderCompleted(render);
=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/tileitem.h'
--- src/plugin/libreofficetoolkit-qml-plugin/tileitem.h 2015-07-13 23:49:43 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/tileitem.h 2015-08-13 18:21:53 +0000
@@ -24,12 +24,44 @@
class LODocument;
+class RenderTask : public QObject, public QRunnable
+{
+ Q_OBJECT
+
+public:
+ RenderTask(const QRect &area, LODocument* document, const qreal &zoom);
+
+ QRect area() const;
+ void setArea(const QRect &area);
+
+ qreal zoomFactor() const;
+ void setZoomFactor(const qreal &zoom);
+
+ LODocument* document() const;
+ void setDocument(LODocument* document);
+
+ void run();
+ void cancel() { m_aborted = true; }
+
+Q_SIGNALS:
+ void areaChanged();
+ void zoomFactorChanged();
+ void documentChanged();
+ void renderCompleted(QImage t);
+
+private:
+ QRect m_area;
+ qreal m_zoomFactor;
+ LODocument* m_document;
+ bool m_aborted;
+};
+
class TileItem : public QObject
{
Q_OBJECT
public:
- TileItem(const QRect &area, LODocument* document);
+ TileItem(const QRect &area, LODocument* document, const qreal &zoom);
~TileItem();
QRect area() const;
@@ -37,6 +69,9 @@
QImage texture() const;
+ qreal zoomFactor() const;
+ void setZoomFactor(const qreal &zoom);
+
bool isPainted() const;
void setPainted(bool isPainted);
@@ -47,9 +82,13 @@
void requestTexture();
void releaseTexture();
+ // TODO: Do we want to make it "markAsInvalid()", so we can request a new Runnable? If so, we don't need to return them...
+ void cancelRenderTask();
+
Q_SIGNALS:
void areaChanged();
void textureChanged();
+ void zoomFactorChanged();
void isPaintedChanged();
void documentChanged();
@@ -59,34 +98,11 @@
private:
QRect m_area;
QImage m_texture;
+ qreal m_zoomFactor;
bool m_painted;
LODocument* m_document;
-};
-
-class RenderTask : public QObject, public QRunnable
-{
- Q_OBJECT
-
-public:
- RenderTask(const QRect &area, LODocument* document);
-
- QRect area() const;
- void setArea(const QRect &area);
-
- LODocument* document() const;
- void setDocument(LODocument* document);
-
- void run();
-
-Q_SIGNALS:
- void areaChanged();
- void documentChanged();
- void renderCompleted(QImage t);
-
-private:
- QRect m_area;
- LODocument* m_document;
+ RenderTask* m_task;
};
#endif // TILEITEM_H
Follow ups