import QtQuick 2.0 import Sailfish.Silica 1.0 import Sailfish.WebView 1.0 import harbour.calibreweb 1.0 Page { id: page property string epubPath: "" property int bookId: 0 property string bookFormat: "epub" property int currentChapter: 0 property double chapterFraction: 0.0 property bool restoring: false property int saveCounter: 0 property int syncCounter: 0 property string errorMessage: "" allowedOrientations: Orientation.All EpubBook { id: book } SilicaFlickable { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.bottom: toolbar.top contentHeight: parent.height WebView { id: web anchors.fill: parent active: true onLoadedChanged: { if (!loaded) return if (page.restoring) page.restoring = false else restorePosition() page.ready = true } } } BusyIndicator { anchors.centerIn: parent running: !page.ready size: BusyIndicatorSize.Large } Label { anchors.centerIn: parent text: page.errorMessage color: Theme.errorColor visible: page.errorMessage.length > 0 } // ---- barra di controllo ---- Rectangle { id: toolbar anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom height: Theme.itemSizeMedium color: Theme.rgba(Theme.primaryColor, 0.08) IconButton { id: prevBtn anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter icon.source: "image://theme/icon-m-left" enabled: page.currentChapter > 0 onClicked: gotoChapter(page.currentChapter - 1) } Label { id: chapterLabel anchors.left: prevBtn.right anchors.right: nextBtn.left anchors.verticalCenter: parent.verticalCenter text: (book.chapterCount > 0) ? (page.currentChapter + 1) + "/" + book.chapterCount + " · " + book.chapterTitle(page.currentChapter) + " · " + Math.round(page.overallProgress() * 100) + "%" : "…" truncationMode: TruncationMode.Elide color: Theme.primaryColor horizontalAlignment: Text.AlignHCenter font.pixelSize: Theme.fontSizeSmall } IconButton { id: nextBtn anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter icon.source: "image://theme/icon-m-right" enabled: page.currentChapter < book.chapterCount - 1 onClicked: gotoChapter(page.currentChapter + 1) } ProgressBar { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom height: 6 value: page.overallProgress() minimumValue: 0 maximumValue: 1 } } PullDownMenu { MenuItem { text: "Indice capitoli" onClicked: showChapterList() } MenuItem { text: "Aggiorna posizione sul server" onClicked: syncNow() } } // ---- timer di tracciamento ---- Timer { id: trackTimer interval: 2000 running: page.ready repeat: true onTriggered: readScrollPosition() } function overallProgress() { if (book.chapterCount <= 0) return 0 return (page.currentChapter + page.chapterFraction) / book.chapterCount } function readScrollPosition() { if (!web.loaded) return web.runJavaScript( "window.scrollY / Math.max(1, document.documentElement.scrollHeight - window.innerHeight)", function(f) { var v = Math.max(0, Math.min(1, f)) if (Math.abs(v - page.chapterFraction) > 0.005) { page.chapterFraction = v maybeSave(true) } }) } function maybeSave(force) { if (bookId <= 0) return page.saveCounter++ // salva locale ogni ~10 s, sync server ogni ~30 s if (force || page.saveCounter % 5 === 0) { appSettings.setBookmarkKey(page.bookId, page.bookFormat, book.makeBookmarkKey(page.currentChapter, page.chapterFraction)) } if (force || page.syncCounter++ % 15 === 0) syncNow() } function syncNow() { if (bookId <= 0) return apiClient.postBookmark(page.bookId, page.bookFormat, book.makeBookmarkKey(page.currentChapter, page.chapterFraction)) } function gotoChapter(index) { if (index < 0 || index >= book.chapterCount) return if (index !== page.currentChapter) { maybeSave(true) page.chapterFraction = 0 } page.currentChapter = index page.restoring = true page.ready = false web.url = "file://" + book.chapterFilePath(index) } function restorePosition() { if (page.chapterFraction > 0.01) { web.runJavaScript( "window.scrollTo(0, " + page.chapterFraction + " * Math.max(1, document.documentElement.scrollHeight - window.innerHeight)); true", function() { }) } } function showChapterList() { var dialog = pageStack.push(Qt.resolvedUrl("ChapterListDialog.qml"), { model: book.chapterList() }) dialog.accepted.connect(function() { gotoChapter(dialog.selectedIndex) }) } Component.onCompleted: { page.ready = false if (!book.open(page.epubPath)) { page.errorMessage = "Impossibile aprire il libro EPUB" page.ready = true return } // ripristina il segno locale var key = appSettings.bookmarkKey(page.bookId, page.bookFormat) var ch = 0 var fr = 0.0 if (key.length > 0 && book.parseBookmarkKey(key, ch, fr)) { page.currentChapter = Math.max(0, Math.min(ch, book.chapterCount - 1)) page.chapterFraction = fr } web.url = "file://" + book.chapterFilePath(page.currentChapter) } onStatusChanged: { if (status === PageStatus.Inactive) maybeSave(true) } }