Lettore 0.4.0: rifatto senza WebView (Text rich text QML)
Il WebView di Sailfish non supporta lo scroll programmatico affidabile (window.scrollY stale, contentHeight/scrollPosition non scrivibili): 0.3.x non ha mai cambiato capitolo in modo affidabile. Ora i capitoli sono rich text Qt in un Text QML dentro un SilicaFlickable: contentY/contentHeight nativi, paginazione a tap deterministico, fine capitolo esatta. HTML->rich text in C++ (tag base, entità, fallback). Bookmark v2 invariato (offset caratteri stimato da frazione). Niente immagini inline (limite rich text).
This commit is contained in:
+154
-277
@@ -1,8 +1,13 @@
|
||||
import QtQuick 2.0
|
||||
import Sailfish.Silica 1.0
|
||||
import Sailfish.WebView 1.0
|
||||
import harbour.calibreweb 1.0
|
||||
|
||||
// Lettore senza WebView: il capitolo è renderizzato come rich text Qt in un
|
||||
// Text QML dentro un SilicaFlickable. Lo scroll (contentY/contentHeight) è
|
||||
// nativo QML e sempre affidabile — sul WebView di Sailfish lo scroll
|
||||
// programmatico e la lettura della posizione non funzionano (window.scrollY
|
||||
// stale, contentHeight/scrollPosition non scrivibili): con il WebView il
|
||||
// cambio capitolo non scattava mai.
|
||||
Page {
|
||||
id: page
|
||||
|
||||
@@ -12,13 +17,10 @@ Page {
|
||||
|
||||
property int currentChapter: 0
|
||||
property double chapterFraction: 0.0
|
||||
// posizione di lettura nel capitolo corrente, in caratteri di testo
|
||||
// (conteggio JS sui text node: stabile al variare di font/margini)
|
||||
// posizione di lettura nel capitolo corrente, stimata in caratteri di testo
|
||||
// dalla frazione di scroll (stabile col reflow del font)
|
||||
property int charOffset: 0
|
||||
// caratteri di testo per pagina (stimati dalla viewport corrente): usato
|
||||
// per il passo di pagina e per la label "pagina corrente/totale"
|
||||
property int charsPerPage: 1
|
||||
// offset da ripristinare al load del capitolo (-1 = nessuno)
|
||||
// offset da ripristinare al cambio capitolo (-1 = nessuno)
|
||||
property int restoreCharOffset: -1
|
||||
// ultimo offset assoluto salvato (evita scritture continue)
|
||||
property int lastSavedAbs: -1
|
||||
@@ -26,10 +28,15 @@ Page {
|
||||
property int pageIndex: 1
|
||||
property int pageCount: 1
|
||||
property bool ready: false
|
||||
property bool restoring: false
|
||||
property string errorMessage: ""
|
||||
// reflow: scala il font (il testo si ri-avvolge), non lo zoom grafico
|
||||
property double fontScale: 1.0
|
||||
// restore in sospeso: frazione da applicare quando il testo si ri-impagina
|
||||
property bool pendingRestore: false
|
||||
property double restoreFrac: 0.0
|
||||
// anteprima margini dal dialog (valori temporanei, -1 = usa le impostazioni)
|
||||
property int previewMarginL: -1
|
||||
property int previewMarginT: -1
|
||||
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
@@ -38,8 +45,9 @@ Page {
|
||||
}
|
||||
|
||||
SilicaFlickable {
|
||||
id: readerFlick
|
||||
anchors.fill: parent
|
||||
contentHeight: parent.height
|
||||
contentHeight: readerContent.height
|
||||
|
||||
// il PullDownMenu va DENTRO una vista Silica, non a livello di Page
|
||||
PullDownMenu {
|
||||
@@ -56,8 +64,9 @@ Page {
|
||||
previewHandler: page.previewMargins
|
||||
})
|
||||
dlg.accepted.connect(function() {
|
||||
// i valori sono già persistiti da onAccepted; rinfresca subito
|
||||
applyMarginsJS()
|
||||
// i valori sono già persistiti da onAccepted; esci dal preview
|
||||
page.previewMarginL = -1
|
||||
page.previewMarginT = -1
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -130,49 +139,43 @@ Page {
|
||||
}
|
||||
}
|
||||
|
||||
WebView {
|
||||
id: web
|
||||
anchors.fill: parent
|
||||
active: true
|
||||
onLoadedChanged: {
|
||||
if (!loaded)
|
||||
return
|
||||
loadWatchdog.stop()
|
||||
page.ready = true
|
||||
// sblocca lo scroll se il CSS del libro lo blocca, poi ripristina
|
||||
// la posizione per OFFSET DI CARATTERE (stabile con font/margini)
|
||||
web.runJavaScript(page.unlockScrollJs(), function() {
|
||||
if (page.restoreCharOffset >= 0) {
|
||||
var off = page.restoreCharOffset
|
||||
page.restoreCharOffset = -1
|
||||
if (off >= book.chapterTextLength(page.currentChapter)) {
|
||||
// oltre la fine = vai all'ultima pagina (usato da
|
||||
// prevPage per non saltare la coda del capitolo):
|
||||
// scrollTo oltre il limite porta in fondo al documento
|
||||
web.runJavaScript(
|
||||
"window.scrollTo(0,9999999);true",
|
||||
function() { readScrollPosition() })
|
||||
} else {
|
||||
web.runJavaScript(page.scrollToCharJs(off), function() {
|
||||
readScrollPosition()
|
||||
})
|
||||
}
|
||||
} else {
|
||||
readScrollPosition()
|
||||
}
|
||||
})
|
||||
page.restoring = false
|
||||
// contenuto del capitolo: rich text Qt in un Text QML. Il cambio di
|
||||
// currentChapter aggiorna il binding; il reflow (font/margini) è nativo.
|
||||
Item {
|
||||
id: readerContent
|
||||
width: parent.width
|
||||
height: Math.max(parent.height,
|
||||
textItem.height + 2 * (page.previewMarginT >= 0
|
||||
? page.previewMarginT
|
||||
: appSettings.readerMarginV))
|
||||
|
||||
Text {
|
||||
id: textItem
|
||||
x: page.previewMarginL >= 0 ? page.previewMarginL : appSettings.readerMargin
|
||||
y: page.previewMarginT >= 0 ? page.previewMarginT : appSettings.readerMarginV
|
||||
width: parent.width - 2 * (page.previewMarginL >= 0
|
||||
? page.previewMarginL
|
||||
: appSettings.readerMargin)
|
||||
text: book.chapterRichText(page.currentChapter)
|
||||
textFormat: Text.RichText
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
font.pixelSize: page.fontPx()
|
||||
color: Theme.primaryColor
|
||||
onContentHeightChanged: page.onTextReflowed()
|
||||
}
|
||||
}
|
||||
|
||||
// Paginazione a tap: niente scroll libero. La meta' destra va avanti di
|
||||
// una pagina, la sinistra indietro. Le fasce superiore e inferiore sono
|
||||
// lasciate ai menu (PullDown in alto, PushUp in basso).
|
||||
// preventStealing: i drag nella zona centrale NON scrollano (paginazione
|
||||
// a tap come richiesto), i pulley menu restano raggiungibili dai bordi.
|
||||
MouseArea {
|
||||
id: tapZone
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: Theme.itemSizeLarge
|
||||
anchors.bottomMargin: Theme.itemSizeLarge + Theme.paddingLarge
|
||||
preventStealing: true
|
||||
onClicked: {
|
||||
if (mouse.x > width / 2)
|
||||
page.nextPage()
|
||||
@@ -182,29 +185,11 @@ Page {
|
||||
}
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
Label {
|
||||
anchors.centerIn: parent
|
||||
running: !page.ready
|
||||
size: BusyIndicatorSize.Large
|
||||
}
|
||||
|
||||
// watchdog: se il capitolo non si carica, niente spinner infinito
|
||||
Timer {
|
||||
id: loadWatchdog
|
||||
interval: 15000
|
||||
onTriggered: {
|
||||
if (!web.loaded && !page.ready) {
|
||||
page.errorMessage = qsTr("The reader is not responding (WebView). The EPUB file may be damaged.")
|
||||
page.ready = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ritardo di apertura: evita la race tra onCompleted e l'init del WebView
|
||||
Timer {
|
||||
id: openTimer
|
||||
interval: 100
|
||||
onTriggered: openChapter()
|
||||
text: page.errorMessage
|
||||
color: Theme.errorColor
|
||||
visible: page.errorMessage.length > 0
|
||||
}
|
||||
|
||||
// chiusura libro differita (pop dalla MainPage, non dall'handler del ReaderPage)
|
||||
@@ -214,19 +199,23 @@ Page {
|
||||
onTriggered: pageStack.pop(null)
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.centerIn: parent
|
||||
text: page.errorMessage
|
||||
color: Theme.errorColor
|
||||
visible: page.errorMessage.length > 0
|
||||
// rete di sicurezza per il restore: se il reflow non ha ancora cambiato
|
||||
// contentHeight (capitoli con altezza identica), applica comunque
|
||||
Timer {
|
||||
id: restoreTimer
|
||||
interval: 80
|
||||
onTriggered: {
|
||||
if (page.pendingRestore) {
|
||||
page.pendingRestore = false
|
||||
applyRestore()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- controlli spostati nel PushUpMenu (trascina in alto) ----
|
||||
|
||||
// ---- timer di tracciamento ----
|
||||
// tracciamento periodico (aggiorna label e bookmark anche senza tap)
|
||||
Timer {
|
||||
id: trackTimer
|
||||
interval: 2000
|
||||
interval: 3000
|
||||
running: page.ready
|
||||
repeat: true
|
||||
onTriggered: readScrollPosition()
|
||||
@@ -235,110 +224,51 @@ Page {
|
||||
function overallProgress() {
|
||||
if (book.chapterCount <= 0)
|
||||
return 0
|
||||
return (page.currentChapter + page.chapterFraction) / book.chapterCount
|
||||
var total = book.chapterStartChar(book.chapterCount)
|
||||
if (total <= 0)
|
||||
return 0
|
||||
var start = book.chapterStartChar(page.currentChapter)
|
||||
return (start + page.charOffset) / total
|
||||
}
|
||||
|
||||
// JS che sblocca lo scroll del documento: rimuove overflow hidden / altezze
|
||||
// fisse che i CSS di alcuni EPUB impongono (sintomo: max==0, ogni tap salta
|
||||
// al capitolo successivo saltando tutto il testo)
|
||||
function unlockScrollJs() {
|
||||
return "(function(){"
|
||||
+ "var de=document.documentElement,b=document.body;"
|
||||
+ "if(!de||!b)return false;"
|
||||
+ "de.style.setProperty('overflow','visible','important');"
|
||||
+ "b.style.setProperty('overflow','visible','important');"
|
||||
+ "de.style.setProperty('height','auto','important');"
|
||||
+ "b.style.setProperty('height','auto','important');"
|
||||
+ "var after=Math.max(de.scrollHeight,b.scrollHeight);"
|
||||
+ "if(after<=window.innerHeight+1&&b.textContent.length>0){"
|
||||
+ "var all=b.querySelectorAll('div,section,article,main,td,li');"
|
||||
+ "for(var i=0;i<all.length;i++){var cs=getComputedStyle(all[i]);"
|
||||
+ "if(cs.overflowY==='hidden'||cs.overflowY==='scroll'){"
|
||||
+ "all[i].style.setProperty('overflow','visible','important');}}}"
|
||||
+ "return true;})()"
|
||||
// frazione di contenuto per un offset in caratteri (off >= len = fondo)
|
||||
function offsetToFrac(off, chapterIndex) {
|
||||
var len = book.chapterTextLength(chapterIndex)
|
||||
if (len <= 0)
|
||||
return 0
|
||||
if (off >= len)
|
||||
return 1.0
|
||||
return Math.max(0, Math.min(1, off / len))
|
||||
}
|
||||
|
||||
// JS che conta i caratteri di testo sopra la viewport (per il bookmark).
|
||||
// Lo scroll NON è gestito qui: pagina/frazione si leggono dalle proprietà
|
||||
// native del WebView (contentHeight/scrollPosition), sempre affidabili.
|
||||
function positionJs() {
|
||||
return "(function(){"
|
||||
+ "var b=document.body;"
|
||||
+ "var o=0;"
|
||||
+ "var it=document.createTreeWalker(b,NodeFilter.SHOW_TEXT);"
|
||||
+ "var n;"
|
||||
+ "while((n=it.nextNode())){"
|
||||
+ "var p=n.parentNode;"
|
||||
+ "if(!p||p.tagName==='SCRIPT'||p.tagName==='STYLE'||p.tagName==='NOSCRIPT')continue;"
|
||||
+ "var r=document.createRange();r.selectNodeContents(n);"
|
||||
+ "var rect=r.getBoundingClientRect();"
|
||||
+ "if(rect.bottom<=0){o+=n.data.length;continue;}"
|
||||
+ "if(rect.top>=0)break;"
|
||||
+ "var h=rect.bottom-rect.top;"
|
||||
+ "if(h>0)o+=Math.round(n.data.length*(0-rect.top)/h);"
|
||||
+ "break;}"
|
||||
+ "return o;})()"
|
||||
function applyRestore() {
|
||||
var max = Math.max(0, readerFlick.contentHeight - readerFlick.height)
|
||||
readerFlick.contentY = page.restoreFrac * max
|
||||
readScrollPosition()
|
||||
}
|
||||
|
||||
// JS che conta i caratteri di testo NELLA viewport corrente (per il passo
|
||||
// di pagina): stessa tecnica di positionJs, ma sulla fascia [0, innerHeight]
|
||||
function charsInViewportJs() {
|
||||
return "(function(){"
|
||||
+ "var b=document.body;"
|
||||
+ "var vh=window.innerHeight;"
|
||||
+ "var c=0;"
|
||||
+ "var it=document.createTreeWalker(b,NodeFilter.SHOW_TEXT);"
|
||||
+ "var n;"
|
||||
+ "while((n=it.nextNode())){"
|
||||
+ "var p=n.parentNode;"
|
||||
+ "if(!p||p.tagName==='SCRIPT'||p.tagName==='STYLE'||p.tagName==='NOSCRIPT')continue;"
|
||||
+ "var r=document.createRange();r.selectNodeContents(n);"
|
||||
+ "var rect=r.getBoundingClientRect();"
|
||||
+ "if(rect.bottom<=0||rect.top>=vh)continue;"
|
||||
+ "var top=Math.max(0,rect.top),bot=Math.min(vh,rect.bottom);"
|
||||
+ "var h=rect.bottom-rect.top;"
|
||||
+ "if(h>0)c+=Math.round(n.data.length*(bot-top)/h);"
|
||||
+ "}"
|
||||
+ "return c;})()"
|
||||
}
|
||||
|
||||
// JS che porta in cima alla viewport il carattere 'off' del capitolo.
|
||||
// Usa scrollBy RELATIVO (rect.top - 8): non legge mai window.scrollY,
|
||||
// che sul WebView Sailfish non riflette lo scroll reale (con scrollTo +
|
||||
// scrollY il tap oscillava di pochi pixel invece di avanzare).
|
||||
function scrollToCharJs(off) {
|
||||
return "(function(){"
|
||||
+ "var b=document.body,o=0;"
|
||||
+ "var it=document.createTreeWalker(b,NodeFilter.SHOW_TEXT);"
|
||||
+ "var n;"
|
||||
+ "while((n=it.nextNode())){"
|
||||
+ "var p=n.parentNode;"
|
||||
+ "if(!p||p.tagName==='SCRIPT'||p.tagName==='STYLE'||p.tagName==='NOSCRIPT')continue;"
|
||||
+ "if(o+n.data.length>=" + off + "){"
|
||||
+ "var r=document.createRange();r.setStart(n," + off + "-o);r.collapse(true);"
|
||||
+ "var rect=r.getBoundingClientRect();"
|
||||
+ "window.scrollBy(0,rect.top-8);"
|
||||
+ "return true;}"
|
||||
+ "o+=n.data.length;}"
|
||||
+ "return false;})()"
|
||||
// il testo si è ri-impaginato (cambio capitolo, font, margini):
|
||||
// applica la frazione di restore in sospeso
|
||||
function onTextReflowed() {
|
||||
if (page.pendingRestore) {
|
||||
page.pendingRestore = false
|
||||
applyRestore()
|
||||
}
|
||||
}
|
||||
|
||||
function readScrollPosition() {
|
||||
if (!web.loaded)
|
||||
if (!page.ready)
|
||||
return
|
||||
web.runJavaScript(page.positionJs(), function(o) {
|
||||
page.charOffset = o || 0
|
||||
// avanzamento per CARATTERI: stabile e indipendente dallo scroll reale
|
||||
var total = book.chapterStartChar(book.chapterCount)
|
||||
var start = book.chapterStartChar(page.currentChapter)
|
||||
if (total > 0)
|
||||
page.chapterFraction = (start + page.charOffset) / total
|
||||
var len = book.chapterTextLength(page.currentChapter)
|
||||
var pp = Math.max(1, page.charsPerPage)
|
||||
page.pageIndex = Math.floor(page.charOffset / pp) + 1
|
||||
page.pageCount = Math.max(1, Math.ceil(len / pp))
|
||||
maybeSave(false)
|
||||
})
|
||||
var vh = Math.max(1, readerFlick.height)
|
||||
var max = Math.max(0, readerFlick.contentHeight - vh)
|
||||
var y = Math.max(0, Math.min(max, readerFlick.contentY))
|
||||
var len = book.chapterTextLength(page.currentChapter)
|
||||
page.chapterFraction = max > 0 ? y / max : 0
|
||||
// posizione per caratteri (stima dalla frazione: stabile col reflow)
|
||||
page.charOffset = Math.round(page.chapterFraction * len)
|
||||
page.pageIndex = Math.floor(y / vh) + 1
|
||||
page.pageCount = Math.max(1, Math.ceil(max / vh) + 1)
|
||||
maybeSave(false)
|
||||
}
|
||||
|
||||
function maybeSave(force) {
|
||||
@@ -362,98 +292,73 @@ Page {
|
||||
maybeSave(true)
|
||||
page.chapterFraction = 0
|
||||
page.charOffset = 0
|
||||
page.restoreCharOffset = -1
|
||||
page.lastSavedAbs = -1
|
||||
}
|
||||
page.currentChapter = index
|
||||
loadChapter()
|
||||
// il binding del Text si aggiorna da solo; restore all'inizio del capitolo
|
||||
page.pendingRestore = true
|
||||
page.restoreFrac = page.restoreCharOffset >= 0
|
||||
? offsetToFrac(page.restoreCharOffset, page.currentChapter)
|
||||
: 0.0
|
||||
page.restoreCharOffset = -1
|
||||
restoreTimer.start()
|
||||
}
|
||||
|
||||
function changeFontScale(delta) {
|
||||
page.fontScale = Math.max(0.7, Math.min(2.5, page.fontScale + delta))
|
||||
// ricarica con la nuova dimensione, mantenendo la posizione di lettura:
|
||||
// l'offset in CARATTERI è stabile col reflow (a differenza dei px scrollati)
|
||||
page.restoreCharOffset = page.charOffset
|
||||
loadChapter()
|
||||
// il binding aggiorna il font; ripristina la posizione alla frazione
|
||||
// corrente (l'offset in caratteri è stabile col reflow)
|
||||
page.pendingRestore = true
|
||||
page.restoreFrac = page.chapterFraction
|
||||
restoreTimer.start()
|
||||
}
|
||||
|
||||
// pagina successiva: avanza di una "pagina di testo" (i caratteri visibili
|
||||
// nella viewport corrente). La fine del capitolo è determinata dal CONFRONTO
|
||||
// CON LA LUNGHEZZA IN CARATTERI (nota in C++), non dallo scroll reale del
|
||||
// WebView — quindi il passaggio al capitolo successivo è sempre affidabile.
|
||||
// pagina successiva: una viewport avanti (contentY nativo, affidabile);
|
||||
// a fine capitolo (o capitolo non scrollabile) passa al successivo
|
||||
function nextPage() {
|
||||
if (!web.loaded || !page.ready)
|
||||
if (!page.ready)
|
||||
return
|
||||
web.runJavaScript(page.positionJs(), function(o) {
|
||||
page.charOffset = o || 0
|
||||
web.runJavaScript(page.charsInViewportJs(), function(c) {
|
||||
page.charsPerPage = Math.max(1, Math.round(c || 1))
|
||||
var target = page.charOffset + page.charsPerPage
|
||||
if (target >= book.chapterTextLength(page.currentChapter)) {
|
||||
// fine capitolo: segna la coda come letta e passa avanti
|
||||
page.charOffset = Math.max(0, book.chapterTextLength(page.currentChapter) - 1)
|
||||
maybeSave(true)
|
||||
gotoChapter(page.currentChapter + 1)
|
||||
} else {
|
||||
web.runJavaScript(page.scrollToCharJs(target), function() {
|
||||
readScrollPosition()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
var vh = readerFlick.height
|
||||
var max = Math.max(0, readerFlick.contentHeight - vh)
|
||||
var y = readerFlick.contentY
|
||||
if (y >= max - 2) {
|
||||
// fine capitolo: segna la coda come letta e passa avanti
|
||||
page.charOffset = Math.max(0, book.chapterTextLength(page.currentChapter) - 1)
|
||||
maybeSave(true)
|
||||
gotoChapter(page.currentChapter + 1)
|
||||
} else {
|
||||
readerFlick.contentY = Math.min(max, y + vh)
|
||||
readScrollPosition()
|
||||
}
|
||||
}
|
||||
|
||||
// pagina precedente: torna indietro di una pagina di testo; dall'inizio del
|
||||
// capitolo va al capitolo prima SULL'ULTIMA PAGINA (non salta la coda)
|
||||
// pagina precedente: una viewport indietro; dall'inizio del capitolo va al
|
||||
// capitolo prima SULL'ULTIMA PAGINA (non salta la coda del capitolo)
|
||||
function prevPage() {
|
||||
if (!web.loaded || !page.ready)
|
||||
if (!page.ready)
|
||||
return
|
||||
web.runJavaScript(page.positionJs(), function(o) {
|
||||
page.charOffset = o || 0
|
||||
web.runJavaScript(page.charsInViewportJs(), function(c) {
|
||||
page.charsPerPage = Math.max(1, Math.round(c || 1))
|
||||
var target = page.charOffset - page.charsPerPage
|
||||
if (target <= 0) {
|
||||
if (page.currentChapter > 0) {
|
||||
maybeSave(true)
|
||||
page.currentChapter -= 1
|
||||
page.chapterFraction = 0
|
||||
page.charOffset = 0
|
||||
// sentinella "fine capitolo": va oltre la lunghezza, così
|
||||
// onLoadedChanged porta all'ultima pagina
|
||||
page.restoreCharOffset = book.chapterTextLength(page.currentChapter) + 1
|
||||
page.lastSavedAbs = -1
|
||||
loadChapter()
|
||||
}
|
||||
} else {
|
||||
web.runJavaScript(page.scrollToCharJs(target), function() {
|
||||
readScrollPosition()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
var y = readerFlick.contentY
|
||||
if (y > 1) {
|
||||
readerFlick.contentY = Math.max(0, y - readerFlick.height)
|
||||
readScrollPosition()
|
||||
} else if (page.currentChapter > 0) {
|
||||
maybeSave(true)
|
||||
page.currentChapter -= 1
|
||||
page.chapterFraction = 0
|
||||
page.charOffset = 0
|
||||
page.lastSavedAbs = -1
|
||||
// sentinella "fine capitolo": va oltre la lunghezza, così
|
||||
// offsetToFrac porta all'ultima pagina
|
||||
page.pendingRestore = true
|
||||
page.restoreFrac = 1.0
|
||||
restoreTimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
function fontPx() {
|
||||
return Math.round(16 * page.fontScale * 100) / 100
|
||||
}
|
||||
|
||||
// carica il capitolo corrente via loadHtml: lo stile di reflow (font-size
|
||||
// e margini) è iniettato nell'HTML dal parser, quindi niente JS post-load
|
||||
function loadChapter() {
|
||||
var html = book.chapterHtml(page.currentChapter, page.fontPx(),
|
||||
appSettings.readerMargin, appSettings.readerMarginV)
|
||||
if (html.length === 0) {
|
||||
page.errorMessage = qsTr("Chapter unreadable")
|
||||
page.ready = true
|
||||
return
|
||||
}
|
||||
page.ready = false
|
||||
page.restoring = true
|
||||
loadWatchdog.restart()
|
||||
web.loadHtml(html, book.chapterBaseUrl(page.currentChapter))
|
||||
}
|
||||
|
||||
function showChapterList() {
|
||||
var dialog = pageStack.push(Qt.resolvedUrl("ChapterListDialog.qml"),
|
||||
{ model: book.chapterList() })
|
||||
@@ -462,32 +367,14 @@ Page {
|
||||
})
|
||||
}
|
||||
|
||||
// JS che sovrascrive i margini sul documento aperto: stile inline con
|
||||
// 'important' batte lo stylesheet iniettato. Effetto immediato, niente reload.
|
||||
function marginJs(lr, tb) {
|
||||
return "var h=document.documentElement,b=document.body;"
|
||||
+ "h.style.setProperty('margin-left','" + lr + "px','important');"
|
||||
+ "h.style.setProperty('margin-right','" + lr + "px','important');"
|
||||
+ "b.style.setProperty('margin-left','" + lr + "px','important');"
|
||||
+ "b.style.setProperty('margin-right','" + lr + "px','important');"
|
||||
+ "b.style.setProperty('margin-top','" + tb + "px','important');"
|
||||
+ "b.style.setProperty('margin-bottom','" + tb + "px','important');"
|
||||
+ "true"
|
||||
}
|
||||
|
||||
// riapplica i margini salvati nelle impostazioni (senza ricaricare il libro)
|
||||
function applyMarginsJS() {
|
||||
if (!web.loaded || !page.ready)
|
||||
return
|
||||
web.runJavaScript(marginJs(appSettings.readerMargin, appSettings.readerMarginV),
|
||||
function() { })
|
||||
}
|
||||
|
||||
// anteprima live dal dialogo margini: valori non ancora persistiti
|
||||
function previewMargins(lr, tb) {
|
||||
if (!web.loaded || !page.ready)
|
||||
return
|
||||
web.runJavaScript(marginJs(lr, tb), function() { })
|
||||
page.previewMarginL = lr
|
||||
page.previewMarginT = tb
|
||||
// il reflow è immediato (binding); mantieni la frazione di lettura
|
||||
page.pendingRestore = true
|
||||
page.restoreFrac = page.chapterFraction
|
||||
restoreTimer.start()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
@@ -497,10 +384,6 @@ Page {
|
||||
page.ready = true
|
||||
return
|
||||
}
|
||||
openTimer.start()
|
||||
}
|
||||
|
||||
function openChapter() {
|
||||
// ripristina il segno locale: posizione per CARATTERE (v2),
|
||||
// le vecchie chiavi v1 vengono migrate da parseBookmarkKey
|
||||
var key = appSettings.bookmarkKey(page.bookId, page.bookFormat)
|
||||
@@ -513,22 +396,16 @@ Page {
|
||||
off = r.hasOwnProperty("charOffset") ? r.charOffset : -1
|
||||
}
|
||||
}
|
||||
page.ready = true
|
||||
page.currentChapter = ch
|
||||
page.chapterFraction = 0
|
||||
page.charOffset = 0
|
||||
page.restoreCharOffset = off
|
||||
page.lastSavedAbs = -1
|
||||
loadChapter()
|
||||
page.pendingRestore = true
|
||||
page.restoreFrac = off >= 0 ? offsetToFrac(off, ch) : 0.0
|
||||
restoreTimer.start()
|
||||
}
|
||||
|
||||
onStatusChanged: {
|
||||
if (status === PageStatus.Inactive) {
|
||||
maybeSave(true)
|
||||
} else if (status === PageStatus.Active) {
|
||||
// riapplica i margini se cambiati nelle impostazioni:
|
||||
// via JS sul documento aperto, senza ricaricare il capitolo
|
||||
if (page.ready && book.chapterCount() > 0)
|
||||
applyMarginsJS()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user